Add Terraform caching and destroy workflow
Some checks failed
Code Quality & Security Scan / TFLint (push) Has been cancelled
Code Quality & Security Scan / Tfsec Security Scan (push) Has been cancelled
Code Quality & Security Scan / Checkov Security Scan (push) Has been cancelled
Code Quality & Security Scan / SonarQube Trigger (push) Has been cancelled
Code Quality & Security Scan / Terraform Init (push) Has been cancelled
Code Quality & Security Scan / Terraform Apply (push) Has been cancelled
Code Quality & Security Scan / Terraform Destroy (push) Has been cancelled

Optimizations:
- Added Terraform provider caching to terraform-init job
- Apply job now reuses cached .terraform directory
- Cache persists across workflow runs (keyed by .terraform.lock.hcl)
- Significantly faster init times on subsequent runs

New terraform-destroy job:
- Only triggered on pull requests with 'destroy' label
- Requires manual approval via 'destroy-approval' environment
- Self-contained with fresh init (no cache for safety)
- Clear warnings and authorization verification
- Three-step process: verify → plan → execute

Security features:
- Destroy only runs on labeled pull requests
- Requires environment protection approval
- Fresh terraform init without cache for verification
- Detailed logging of who/what/when/where
- Cannot be triggered on direct push to master

Usage:
1. Create pull request with proposed destroy changes
2. Add 'destroy' label to the PR
3. Approve via Gitea environment protection
4. Review destroy plan in logs
5. Approve destroy-approval environment to execute

Benefits:
- Faster apply workflow (cached providers)
- Safe destroy process with multiple safeguards
- Clear audit trail for destructive operations
- Self-contained destroy for maximum safety
This commit is contained in:
Patrick de Ruiter 2025-11-02 12:33:44 +01:00
parent 7f9945461f
commit 2af3ccd989
Signed by: pderuiter
GPG Key ID: 5EBA7F21CF583321

View File

@ -92,6 +92,16 @@ jobs:
with:
terraform_version: latest
- name: Cache Terraform Providers
uses: actions/cache@v3
with:
path: |
.terraform
.terraform.lock.hcl
key: terraform-${{ hashFiles('**/.terraform.lock.hcl') }}
restore-keys: |
terraform-
- name: Terraform Init
env:
AWS_ACCESS_KEY_ID: ${{ secrets.MINIO_ACCESS_KEY }}
@ -168,6 +178,16 @@ jobs:
with:
terraform_version: latest
- name: Restore Terraform Cache
uses: actions/cache@v3
with:
path: |
.terraform
.terraform.lock.hcl
key: terraform-${{ hashFiles('**/.terraform.lock.hcl') }}
restore-keys: |
terraform-
- name: Install AWS CLI
run: |
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
@ -220,3 +240,94 @@ jobs:
TF_VAR_environment: ${{ secrets.ENVIRONMENT }}
VAULT_ADDR: ${{ secrets.VAULT_ADDR }}
run: terraform apply -auto-approve tfplan
terraform-destroy:
name: Terraform Destroy
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'destroy')
environment:
name: destroy-approval
steps:
- name: Verify Destroy Authorization
run: |
echo "⚠️ CRITICAL: INFRASTRUCTURE DESTRUCTION REQUESTED"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "PR: ${{ github.event.pull_request.html_url }}"
echo "Requested by: ${{ github.actor }}"
echo "Repository: ${{ github.repository }}"
echo "Branch: ${{ github.head_ref }}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "This action will PERMANENTLY DESTROY all infrastructure"
echo "managed by this Terraform configuration."
echo ""
echo "Waiting for manual approval via environment protection rules..."
- name: Checking out
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: latest
- name: Terraform Init (Fresh - No Cache)
env:
AWS_ACCESS_KEY_ID: ${{ secrets.MINIO_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.MINIO_SECRET_KEY }}
TF_BACKEND_ENDPOINT: ${{ secrets.MINIO_ENDPOINT }}
TF_BACKEND_BUCKET: ${{ secrets.MINIO_BUCKET }}
TF_BACKEND_KEY: ${{ secrets.MINIO_STATE_KEY }}
TF_BACKEND_REGION: "main"
TF_VAR_role_id: ${{ secrets.VAULT_ROLE_ID }}
TF_VAR_secret_id: ${{ secrets.VAULT_SECRET_ID }}
VAULT_ADDR: ${{ secrets.VAULT_ADDR }}
run: |
echo "Performing fresh terraform init (no cache for safety)..."
terraform init \
-backend-config="endpoints={s3=\"${TF_BACKEND_ENDPOINT}\"}" \
-backend-config="bucket=${TF_BACKEND_BUCKET}" \
-backend-config="key=${TF_BACKEND_KEY}" \
-backend-config="region=${TF_BACKEND_REGION}" \
-backend-config="skip_credentials_validation=true" \
-backend-config="skip_metadata_api_check=true" \
-backend-config="skip_requesting_account_id=true" \
-backend-config="skip_region_validation=true" \
-backend-config="use_path_style=true"
- name: Terraform Destroy Plan
env:
AWS_ACCESS_KEY_ID: ${{ secrets.MINIO_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.MINIO_SECRET_KEY }}
TF_VAR_role_id: ${{ secrets.VAULT_ROLE_ID }}
TF_VAR_secret_id: ${{ secrets.VAULT_SECRET_ID }}
TF_VAR_datacenter: ${{ secrets.VSPHERE_DATACENTER }}
TF_VAR_cluster_name: ${{ secrets.VSPHERE_CLUSTER }}
TF_VAR_environment: ${{ secrets.ENVIRONMENT }}
VAULT_ADDR: ${{ secrets.VAULT_ADDR }}
run: |
echo "Generating destroy plan..."
terraform plan -destroy -out=destroy.tfplan
echo ""
echo "Destroy plan generated. Review the plan above carefully."
- name: Terraform Destroy Execute
env:
AWS_ACCESS_KEY_ID: ${{ secrets.MINIO_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.MINIO_SECRET_KEY }}
TF_VAR_role_id: ${{ secrets.VAULT_ROLE_ID }}
TF_VAR_secret_id: ${{ secrets.VAULT_SECRET_ID }}
TF_VAR_datacenter: ${{ secrets.VSPHERE_DATACENTER }}
TF_VAR_cluster_name: ${{ secrets.VSPHERE_CLUSTER }}
TF_VAR_environment: ${{ secrets.ENVIRONMENT }}
VAULT_ADDR: ${{ secrets.VAULT_ADDR }}
run: |
echo "🔥 DESTROYING INFRASTRUCTURE..."
echo "This cannot be undone!"
echo ""
terraform apply -auto-approve destroy.tfplan
echo ""
echo "✅ Infrastructure has been destroyed"
echo "State file updated in MinIO: ${{ secrets.MINIO_STATE_KEY }}"