terraform destroy #70
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # MOSIP Infrastructure Destruction Workflow | |
| # | |
| # This workflow supports both remote (S3/Azure Storage/GCS) and local backend configurations for destruction | |
| # | |
| # GPG Encryption for Local Backend: | |
| # - When BACKEND_TYPE is 'local', Terraform state files are automatically encrypted using GPG | |
| # - Required GitHub Secret: GPG_PASSPHRASE (used for encrypting/decrypting state files) | |
| # - Encrypted files: {cloud}-{component}-{branch}-terraform.tfstate.gpg, {cloud}-{component}-{branch}-terraform.tfstate.backup.gpg | |
| # - On destruction, encrypted files are automatically decrypted before Terraform operations | |
| # - State files are cleaned up after successful destruction | |
| # | |
| # Security Features: | |
| # - State files never stored unencrypted in repository when using local backend | |
| # - AES256 encryption with compression | |
| # - Automatic cleanup of unencrypted files after encryption | |
| # | |
| name: terraform destroy | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| CLOUD_PROVIDER: | |
| description: 'Choose the cloud provider (aws, azure, gcp)' | |
| required: true | |
| type: choice | |
| options: | |
| - aws | |
| - azure | |
| - gcp | |
| TERRAFORM_COMPONENT: | |
| description: 'Choose component to destroy (WARNING: base-infra destruction removes foundational resources!)' | |
| required: true | |
| type: choice | |
| options: | |
| - infra | |
| - base-infra | |
| - observ-infra | |
| default: infra | |
| BACKEND_TYPE: | |
| description: 'Choose Terraform backend type (must match the one used during creation)' | |
| required: true | |
| type: choice | |
| options: | |
| - local | |
| - remote | |
| default: local | |
| REMOTE_BACKEND_CONFIG: | |
| description: 'Remote backend config (format: aws:bucket_base_name:region OR azure:rg_name:storage_account:container OR gcp:bucket_name). For AWS, bucket will be created as bucket_base_name-BRANCH_NAME' | |
| required: false | |
| type: string | |
| ENABLE_STATE_LOCKING: | |
| description: 'Enable state locking cleanup (must match the setting used during creation)' | |
| required: false | |
| type: boolean | |
| default: true | |
| SSH_PRIVATE_KEY: | |
| description: 'The GitHub secret containing the private key of the SSH key named in the preceding input variable `SSH_PRIVATE_KEY` is used for SSH login purposes on nginx node.' | |
| required: true | |
| TERRAFORM_DESTROY: | |
| description: 'Terraform destroy - CONFIRM DESTRUCTION' | |
| required: false | |
| type: boolean | |
| default: false | |
| env: | |
| # TF_LOG_PATH: ./temp/terraform.log | |
| ## TRACE, DEBUG, INFO, WARN or ERROR | |
| # TF_LOG: TRACE | |
| # Repository-level secrets for cloud credentials | |
| AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| TF_VAR_ssh_private_key: ${{ secrets[inputs.SSH_PRIVATE_KEY] }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| jobs: | |
| terraform-destroy: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 # 2 hours maximum runtime protection | |
| # Use dynamic environment based on current branch name | |
| environment: ${{ github.ref_name }} | |
| defaults: | |
| run: | |
| shell: bash | |
| # We work in the cloud-specific implementation directory | |
| working-directory: 'terraform/implementations/${{ inputs.CLOUD_PROVIDER }}/${{ inputs.TERRAFORM_COMPONENT }}' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Check for required implementation directory | |
| run: | | |
| if [ ! -d "." ]; then | |
| echo "Directory 'terraform/implementations/${{ inputs.CLOUD_PROVIDER }}/${{ inputs.TERRAFORM_COMPONENT }}' does not exist." | |
| echo "Available providers: aws, azure, gcp" | |
| echo "Available components: base-infra, infra" | |
| exit 1 | |
| fi | |
| # Check if tfvars file exists | |
| if [ ! -f "${{ inputs.CLOUD_PROVIDER }}.tfvars" ]; then | |
| echo "Configuration file '${{ inputs.CLOUD_PROVIDER }}.tfvars' not found in current directory." | |
| exit 1 | |
| fi | |
| - name: Check Cloud Storage for Remote State (All Providers) | |
| run: | | |
| if [ "${{ inputs.BACKEND_TYPE }}" = "remote" ]; then | |
| echo "Checking cloud storage for remote Terraform state..." | |
| echo "Provider: ${{ inputs.CLOUD_PROVIDER }}" | |
| echo "Component: ${{ inputs.TERRAFORM_COMPONENT }}" | |
| echo "Branch: ${{ github.ref_name }}" | |
| # Parse remote configuration to check if storage exists | |
| BACKEND_CONFIG="${{ inputs.REMOTE_BACKEND_CONFIG }}" | |
| if [ -n "$BACKEND_CONFIG" ]; then | |
| IFS=':' read -ra CONFIG_PARTS <<< "$BACKEND_CONFIG" | |
| PROVIDER_TYPE="${CONFIG_PARTS[0]}" | |
| case "$PROVIDER_TYPE" in | |
| aws) | |
| BUCKET_BASE_NAME="${CONFIG_PARTS[1]}" | |
| REGION="${CONFIG_PARTS[2]:-us-east-1}" | |
| DYNAMIC_BUCKET_NAME="${BUCKET_BASE_NAME}-${{ github.ref_name }}" | |
| echo "Checking AWS S3 bucket: $DYNAMIC_BUCKET_NAME" | |
| if aws s3api head-bucket --bucket "$DYNAMIC_BUCKET_NAME" 2>/dev/null; then | |
| echo "AWS S3 bucket found - ready for destroy" | |
| else | |
| echo "WARNING: AWS S3 bucket not found - may already be destroyed" | |
| fi | |
| ;; | |
| azure) | |
| RESOURCE_GROUP="${CONFIG_PARTS[1]}" | |
| STORAGE_ACCOUNT="${CONFIG_PARTS[2]}" | |
| CONTAINER="${CONFIG_PARTS[3]}" | |
| echo "Checking Azure Storage: RG=$RESOURCE_GROUP, Account=$STORAGE_ACCOUNT, Container=$CONTAINER" | |
| echo "INFO: Azure storage existence check requires Azure CLI - assuming exists for destroy" | |
| ;; | |
| gcp) | |
| BUCKET_NAME="${CONFIG_PARTS[1]}" | |
| echo "Checking GCP Cloud Storage bucket: $BUCKET_NAME" | |
| echo "INFO: GCP storage existence check requires gcloud CLI - assuming exists for destroy" | |
| ;; | |
| *) | |
| echo "WARNING: Unknown provider type: $PROVIDER_TYPE" | |
| ;; | |
| esac | |
| else | |
| echo "WARNING: No remote backend config provided" | |
| fi | |
| echo "Cloud storage check completed" | |
| else | |
| echo "Skipping cloud storage check (using local backend)" | |
| fi | |
| - name: Configure Terraform Backend for State Management | |
| run: | | |
| # Use backend configuration script for cleaner code | |
| ../../../../.github/scripts/configure-backend.sh \ | |
| --type "${{ inputs.BACKEND_TYPE }}" \ | |
| --provider "${{ inputs.CLOUD_PROVIDER }}" \ | |
| --component "${{ inputs.TERRAFORM_COMPONENT }}" \ | |
| --branch "${{ github.ref_name }}" \ | |
| --remote-config "${{ inputs.REMOTE_BACKEND_CONFIG }}" \ | |
| ${{ inputs.ENABLE_STATE_LOCKING == true && '--enable-locking' || '' }} | |
| - name: Skip SSH Host key verification | |
| run: | | |
| mkdir -p ~/.ssh | |
| echo -e "Host *\n StrictHostKeyChecking no" > ~/.ssh/config | |
| - name: Setup ufw firewall | |
| run: | | |
| sudo ufw enable | |
| sudo ufw allow ssh | |
| sudo ufw allow 443/tcp | |
| sudo ufw allow 51820/udp | |
| sudo ufw status | |
| if: "${{ inputs.TERRAFORM_COMPONENT != 'base-infra' }}" | |
| - name: Install WireGuard | |
| run: sudo apt-get install -y wireguard | |
| if: "${{ inputs.TERRAFORM_COMPONENT != 'base-infra' }}" | |
| - name: Configure WireGuard | |
| run: | | |
| # WG_CONFIG comes from environment-level secrets (dynamic based on branch name) | |
| if [ -z "${{ secrets.TF_WG_CONFIG }}" ]; then | |
| echo "❌ ERROR: TF_WG_CONFIG secret is not configured for environment '${{ github.ref_name }}'" | |
| echo "Please configure the WireGuard configuration secret for this branch/environment" | |
| exit 1 | |
| fi | |
| # Debug: Check if secret is available (without exposing content) | |
| echo "🔍 Checking TF_WG_CONFIG secret..." | |
| if [ ${#TF_WG_CONFIG} -eq 0 ]; then | |
| echo "❌ ERROR: TF_WG_CONFIG environment variable is empty" | |
| echo "Secret may contain only whitespace or special characters" | |
| exit 1 | |
| fi | |
| echo "✅ Secret length: ${#TF_WG_CONFIG} characters" | |
| # Create config file with proper handling of multiline content | |
| echo "Creating /etc/wireguard directory with proper permissions..." | |
| sudo mkdir -p /etc/wireguard | |
| sudo chmod 755 /etc/wireguard | |
| sudo chown root:root /etc/wireguard | |
| echo "Directory created. Checking permissions..." | |
| sudo ls -la /etc/ | grep wireguard || echo "WireGuard directory not visible" | |
| # Debug: Check secret format without exposing sensitive data | |
| echo "Secret contains '[Interface]': $(printf '%s' "$TF_WG_CONFIG" | grep -c '\[Interface\]')" | |
| echo "Secret contains '[Peer]': $(printf '%s' "$TF_WG_CONFIG" | grep -c '\[Peer\]')" | |
| # Write to temp file with explicit newline handling | |
| printf '%s' "$TF_WG_CONFIG" > /tmp/wg0.conf.tmp | |
| # Check temp file was created successfully | |
| if [ ! -f /tmp/wg0.conf.tmp ]; then | |
| echo "❌ ERROR: Failed to create temporary config file" | |
| exit 1 | |
| fi | |
| echo "Temp file size: $(wc -c < /tmp/wg0.conf.tmp) bytes" | |
| echo "Temp file lines: $(wc -l < /tmp/wg0.conf.tmp)" | |
| # Use a more direct approach - write directly with sudo | |
| echo "Writing config directly with sudo..." | |
| sudo tee /etc/wireguard/wg0.conf < /tmp/wg0.conf.tmp > /dev/null | |
| # Clean up temp file | |
| rm -f /tmp/wg0.conf.tmp | |
| # Verify the file was created and check its size | |
| if sudo test -f /etc/wireguard/wg0.conf; then | |
| echo "✅ Config file created successfully" | |
| echo "Final config file size: $(sudo wc -c < /etc/wireguard/wg0.conf) bytes" | |
| else | |
| echo "❌ ERROR: Config file still doesn't exist" | |
| sudo ls -la /etc/wireguard/ || echo "Cannot list /etc/wireguard directory" | |
| exit 1 | |
| fi | |
| # Set proper permissions | |
| sudo chmod 600 /etc/wireguard/wg0.conf | |
| sudo chown root:root /etc/wireguard/wg0.conf | |
| echo "Final file permissions: $(sudo ls -la /etc/wireguard/wg0.conf)" | |
| # Verify config file is not empty and has expected content | |
| if ! sudo test -s /etc/wireguard/wg0.conf; then | |
| echo "❌ ERROR: WireGuard config file is empty after writing!" | |
| echo "File size: $(sudo wc -c < /etc/wireguard/wg0.conf) bytes" | |
| exit 1 | |
| fi | |
| # Basic validation that it looks like a WireGuard config | |
| if ! sudo grep -q "\[Interface\]" /etc/wireguard/wg0.conf; then | |
| echo "❌ ERROR: WireGuard config doesn't contain [Interface] section" | |
| echo "Please check that TF_WG_CONFIG contains a valid WireGuard configuration" | |
| echo "Config file size: $(sudo wc -c < /etc/wireguard/wg0.conf) bytes" | |
| exit 1 | |
| fi | |
| echo "✅ WireGuard configuration applied successfully" | |
| echo "Config file size: $(sudo wc -c < /etc/wireguard/wg0.conf) bytes" | |
| env: | |
| TF_WG_CONFIG: ${{ secrets.TF_WG_CONFIG }} | |
| if: "${{ inputs.TERRAFORM_COMPONENT != 'base-infra' }}" | |
| - name: Start WireGuard | |
| run: | | |
| sudo chmod 600 /etc/wireguard/wg0.conf | |
| sudo chmod 700 /etc/wireguard/ | |
| sudo chmod 644 /lib/systemd/system/wg-quick@.service | |
| sudo systemctl daemon-reload | |
| echo "Starting WireGuard..." | |
| if ! sudo wg-quick up wg0; then | |
| echo "❌ ERROR: Failed to start WireGuard VPN" | |
| echo "Config file contents (first 5 lines):" | |
| head -5 /etc/wireguard/wg0.conf | |
| exit 1 | |
| fi | |
| echo "✅ WireGuard started successfully" | |
| sudo wg show wg0 | |
| if: "${{ inputs.TERRAFORM_COMPONENT != 'base-infra' }}" | |
| - name: Setup Terraform with specified version on the runner | |
| uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_version: v1.8.5 | |
| - name: Clean Terraform Environment (Prevent Stale Issues) | |
| run: | | |
| echo "=== Cleaning Terraform Environment ===" | |
| # Remove any existing .terraform directory to prevent version conflicts | |
| if [ -d ".terraform" ]; then | |
| echo "🧹 Removing existing .terraform directory..." | |
| rm -rf .terraform | |
| echo "✅ Cleaned .terraform directory" | |
| fi | |
| # Remove any existing .terraform.lock.hcl to allow fresh provider resolution | |
| if [ -f ".terraform.lock.hcl" ]; then | |
| echo "🧹 Removing existing .terraform.lock.hcl..." | |
| rm -f .terraform.lock.hcl | |
| echo "✅ Cleaned .terraform.lock.hcl" | |
| fi | |
| # Remove any temporary Terraform files that might cause conflicts | |
| echo "🧹 Cleaning temporary Terraform files..." | |
| rm -f terraform_*.sh 2>/dev/null || true | |
| rm -f .terraform.tfstate.lock.info 2>/dev/null || true | |
| rm -f terraform.log 2>/dev/null || true | |
| rm -f crash.log 2>/dev/null || true | |
| # Show what we're starting with | |
| echo "✅ Terraform environment cleaned" | |
| echo "Files in directory after cleanup:" | |
| ls -la | head -10 | |
| - name: Setup GPG for State File Encryption | |
| run: | | |
| ../../../../.github/scripts/setup-gpg.sh \ | |
| --backend-type "${{ inputs.BACKEND_TYPE }}" \ | |
| --passphrase "${{ secrets.GPG_PASSPHRASE }}" | |
| - name: Decrypt Existing State Files | |
| run: | | |
| ../../../../.github/scripts/decrypt-state.sh \ | |
| --backend-type "${{ inputs.BACKEND_TYPE }}" \ | |
| --passphrase "${{ secrets.GPG_PASSPHRASE }}" | |
| - name: Display Destruction Warning | |
| run: | | |
| echo "DESTRUCTIVE OPERATION: ${{ inputs.CLOUD_PROVIDER }} (${{ inputs.TERRAFORM_COMPONENT }})" | |
| echo "Working directory: $(pwd)" | |
| echo "Available files:" | |
| ls -la | |
| if [ "${{ inputs.TERRAFORM_COMPONENT }}" = "base-infra" ]; then | |
| echo "CRITICAL WARNING: base-infra destruction will remove foundational resources!" | |
| echo " This includes VPCs, subnets, routing tables, security groups, etc." | |
| echo " This is typically a ONE-TIME setup and should rarely be destroyed." | |
| echo " Make sure you understand the impact before proceeding." | |
| else | |
| echo "INFO: infra destruction - removes MOSIP application infrastructure" | |
| echo " This is safe to destroy and recreate as needed." | |
| fi | |
| if [ "${{ inputs.TERRAFORM_DESTROY }}" != "true" ]; then | |
| echo "ERROR: TERRAFORM_DESTROY is not set to true - skipping actual destruction" | |
| else | |
| echo "TERRAFORM_DESTROY confirmed - proceeding with destruction" | |
| fi | |
| - name: Terraform Init with State Validation | |
| run: | | |
| echo "=== Terraform Init with Enhanced State Validation ===" | |
| # Generate the expected state file name with custom pattern | |
| expected_state_file="${{ inputs.CLOUD_PROVIDER }}-${{ inputs.TERRAFORM_COMPONENT }}-${{ github.ref_name }}-terraform.tfstate" | |
| echo "🔍 Looking for state file: $expected_state_file" | |
| # First, verify that any existing state is valid | |
| if [ -f "$expected_state_file" ]; then | |
| echo "🔍 Found existing state file, validating..." | |
| # Basic state file validation | |
| if terraform show > /dev/null 2>&1; then | |
| echo "✅ Existing state is valid" | |
| # Show what resources exist before destroy | |
| echo "📋 Resources in current state:" | |
| terraform state list | head -10 || echo "Unable to list resources" | |
| else | |
| echo "⚠️ WARNING: Existing state may be corrupted or incompatible" | |
| echo "📋 State file size: $(wc -c < "$expected_state_file") bytes" | |
| echo "📋 State file age: $(stat -c %Y "$expected_state_file")" | |
| # For destroy operations, we need to be more careful with bad state | |
| echo "🚨 Bad state detected during destroy - manual intervention may be needed" | |
| fi | |
| else | |
| echo "ℹ️ No existing state file found ($expected_state_file) - nothing to destroy" | |
| echo "This could mean:" | |
| echo " 1. No infrastructure was previously deployed" | |
| echo " 2. State file naming pattern has changed" | |
| echo " 3. Different branch/component combination was used" | |
| echo "Available files in directory:" | |
| ls -la *.tfstate* 2>/dev/null || echo "No state files found" | |
| fi | |
| # Initialize with clean environment | |
| echo "🚀 Running terraform init..." | |
| if ! terraform init; then | |
| echo "❌ Initial init failed, trying with -upgrade to resolve provider conflicts..." | |
| if ! terraform init -upgrade; then | |
| echo "❌ Init with upgrade failed, trying with -reconfigure for backend changes..." | |
| terraform init -reconfigure -upgrade | |
| fi | |
| fi | |
| # Verify initialization was successful | |
| if [ -d ".terraform" ] && [ -f ".terraform.lock.hcl" ]; then | |
| echo "✅ Terraform initialization successful" | |
| echo "📋 Provider versions:" | |
| terraform version | |
| echo "📋 Workspace: $(terraform workspace show)" | |
| else | |
| echo "❌ Terraform initialization verification failed" | |
| exit 1 | |
| fi | |
| - name: Terraform refresh | |
| run: terraform refresh -var-file="${{ inputs.CLOUD_PROVIDER }}.tfvars" -no-color | |
| if: "${{ inputs.TERRAFORM_DESTROY == true }}" | |
| - name: Terraform Destroy Plan | |
| id: destroy-plan | |
| run: | | |
| echo "Showing what will be destroyed..." | |
| terraform plan -destroy -var-file="${{ inputs.CLOUD_PROVIDER }}.tfvars" -no-color | |
| if: "${{ inputs.TERRAFORM_DESTROY == true }}" | |
| continue-on-error: true | |
| - name: Terraform Destroy | |
| id: destroy | |
| run: | | |
| echo "Starting Terraform destroy..." | |
| # Standard terraform destroy for all components | |
| echo "Running terraform destroy for ${{ inputs.TERRAFORM_COMPONENT }}..." | |
| terraform destroy -var-file="${{ inputs.CLOUD_PROVIDER }}.tfvars" -no-color -auto-approve | |
| echo "✅ Terraform destroy completed successfully" | |
| if: "${{ inputs.TERRAFORM_DESTROY == true }}" | |
| continue-on-error: true | |
| - name: Emergency State Save on Cancellation | |
| run: | | |
| echo "=== EMERGENCY STATE SAVE ===" | |
| echo "Workflow cancellation detected - attempting emergency state save..." | |
| # Only for local backend | |
| if [ "${{ inputs.BACKEND_TYPE }}" = "local" ]; then | |
| # Quick encrypt and commit whatever state we have | |
| ../../../../.github/scripts/encrypt-state.sh \ | |
| --backend-type "local" \ | |
| --passphrase "${{ secrets.GPG_PASSPHRASE }}" \ | |
| --operation "destroy" \ | |
| --destroy-success "false" || true | |
| # Emergency commit | |
| git add -f *.tfstate* *.gpg tf-plan* backend.tf 2>/dev/null || true | |
| # Explicitly exclude .terraform directory and its contents | |
| git rm --cached -r .terraform 2>/dev/null || true | |
| git rm --cached .terraform.lock.hcl 2>/dev/null || true | |
| if ! git diff --cached --quiet; then | |
| git config --global user.email ${{ github.actor }}@users.noreply.github.com | |
| git config --global user.name ${{ github.actor }} | |
| git commit -m "EMERGENCY: Destroy state save before cancellation [skip ci]" || true | |
| git push || true | |
| echo "✅ Emergency state saved" | |
| else | |
| echo "⚠️ No state changes to save" | |
| fi | |
| fi | |
| if: cancelled() | |
| continue-on-error: true | |
| - name: Clean up state files and artifacts | |
| run: | | |
| echo "Cleaning up state files and artifacts after successful destruction..." | |
| # Use the encryption script to handle cleanup logic | |
| ../../../../.github/scripts/encrypt-state.sh \ | |
| --backend-type "${{ inputs.BACKEND_TYPE }}" \ | |
| --passphrase "${{ secrets.GPG_PASSPHRASE }}" \ | |
| --operation "destroy" \ | |
| --destroy-success "true" | |
| rm -f backend.tf | |
| echo "✅ Cleanup completed" | |
| if: "${{ inputs.TERRAFORM_DESTROY == true && steps.destroy.outcome == 'success' }}" | |
| - name: Cleanup State Locking Resources | |
| run: | | |
| echo "Cleaning up state locking resources..." | |
| ../../../../.github/scripts/cleanup-state-locking.sh \ | |
| --provider "${{ inputs.CLOUD_PROVIDER }}" \ | |
| --config "${{ inputs.REMOTE_BACKEND_CONFIG }}" \ | |
| --branch "${{ github.ref_name }}" \ | |
| --component "${{ inputs.TERRAFORM_COMPONENT }}" \ | |
| ${{ inputs.ENABLE_STATE_LOCKING == true && '--enable-locking' || '' }} | |
| if: "${{ inputs.TERRAFORM_DESTROY == true && steps.destroy.outcome == 'success' && inputs.BACKEND_TYPE == 'remote' }}" | |
| - name: Add the Terraform state changes | |
| run: | | |
| echo "Current working directory: $(pwd)" | |
| echo "Files in current directory:" | |
| ls -la | |
| echo "Checking for Terraform files to commit..." | |
| # Handle GPG encryption for local backend if state files exist | |
| if [ "${{ steps.destroy.outcome }}" != "success" ]; then | |
| ../../../../.github/scripts/encrypt-state.sh \ | |
| --backend-type "${{ inputs.BACKEND_TYPE }}" \ | |
| --passphrase "${{ secrets.GPG_PASSPHRASE }}" \ | |
| --operation "destroy" \ | |
| --destroy-success "false" | |
| fi | |
| # Force add Terraform files even if they're in .gitignore - EXCLUDE .terraform | |
| git add -f *.tfstate* 2>/dev/null || echo "No .tfstate files found" | |
| git add -f *.gpg 2>/dev/null || echo "No .gpg files found" | |
| git add -f backend.tf 2>/dev/null || echo "No backend.tf file found" | |
| git add -f *.tf 2>/dev/null || echo "No .tf files to add" | |
| # Explicitly exclude .terraform directory and its contents from being committed | |
| git rm --cached -r .terraform 2>/dev/null || true | |
| git rm --cached .terraform.lock.hcl 2>/dev/null || true | |
| echo "✅ Excluded .terraform directory from commit" | |
| # Add other Terraform-related files but avoid .terraform directory | |
| git add -A | |
| git reset .terraform 2>/dev/null || true # Remove .terraform from staged changes | |
| git reset .terraform.lock.hcl 2>/dev/null || true # Remove lock file from staged changes | |
| echo "Git status after adding files:" | |
| git status | |
| # Check if there are any changes to commit | |
| if git diff --cached --quiet; then | |
| echo "No changes to commit - this is normal for remote backends or when state is managed remotely" | |
| echo "Terraform destroy completed successfully" | |
| else | |
| echo "Committing Terraform changes..." | |
| git config --global user.email ${{ github.actor }}@users.noreply.github.com | |
| git config --global user.name ${{ github.actor }} | |
| # Customize commit message based on backend type and destroy outcome | |
| if [ "${{ inputs.BACKEND_TYPE }}" = "local" ]; then | |
| if [ "${{ steps.destroy.outcome }}" = "success" ]; then | |
| commit_msg="Terraform destroy cleanup GitHub Actions - ${{ inputs.CLOUD_PROVIDER }}-${{ inputs.TERRAFORM_COMPONENT }}" | |
| else | |
| commit_msg="Terraform destroy (encrypted state) GitHub Actions - ${{ inputs.CLOUD_PROVIDER }}-${{ inputs.TERRAFORM_COMPONENT }}" | |
| fi | |
| else | |
| commit_msg="Terraform destroy GitHub Actions - ${{ inputs.CLOUD_PROVIDER }}-${{ inputs.TERRAFORM_COMPONENT }}" | |
| fi | |
| git commit -s -am "$commit_msg" | |
| git push | |
| echo "Changes committed and pushed successfully" | |
| fi | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| if: "${{ inputs.TERRAFORM_DESTROY == true }}" | |
| - name: Terraform destroy status | |
| if: ${{ steps.destroy.outcome == 'failure' }} | |
| run: | | |
| echo "ERROR: Terraform destroy failed" | |
| echo "Check the logs above for detailed error information" | |
| exit 1 | |
| - name: Terraform destroy success | |
| if: ${{ steps.destroy.outcome == 'success' }} | |
| run: | | |
| echo "SUCCESS: Terraform destroy completed successfully" | |
| echo "All ${{ inputs.CLOUD_PROVIDER }}-${{ inputs.TERRAFORM_COMPONENT }} resources have been destroyed" | |
| - uses: 8398a7/action-slack@v3 | |
| with: | |
| status: ${{ job.status }} | |
| fields: repo,message,author,commit,workflow,job # selectable (default: repo,message) | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required | |
| if: "${{ github.event_name != 'pull_request' && failure() }}" # Pick up events even if the job fails or is canceled. |