'Github Actions revert/destroy terraform AWS infrastructure created by Terraform Plan

I have setup terraform for AWS infrastructure but was not able to set a workflow where I can trigger destroying of the infrastructure created using Terraform Plan/Apply.

Can a github action be triggered manually without code being pushed or pull request being created?

I don't want to register a workspace on hashicorp and want to run the pipelines on Github Actions itself.

There is this resource for destroying infrastructure but it only works on Pull request close.



Solution 1:[1]

Use workflow_dispath https://docs.github.com/en/actions/reference/events-that-trigger-workflows#workflow_dispatch

You can then manually run it from the web GUI.

Solution 2:[2]

I created another workflow that could be triggered manually and would use the state file to destroy the existing infrastructure

To do this, I required the state file to be stored. I stored the file on AWS S3.

Note: you can use terraform Backend to maintain versions of state file.

I haven't done this because I was not able to configure it for different environments and variables are not allowed.

Below are the changes in the Terraform create Infrastructure job:

- name: AWS Plan Copy
        id: copyfrom
        run: aws s3 cp s3://your-bucket/yourapp-${{ env.ENVIRONMENT }}.tfstate yourapp-${{ env.ENVIRONMENT }}.tfstate
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ${{ secrets.REGION }}
        continue-on-error: true // incase infra does not exist

- name: Terraform Plan
        id: plan
        if: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }} // Plan creation is required both on pull_request, push
        run: terraform plan 
        continue-on-error: true

- name: Terraform Apply
        id: apply
        if: github.event_name == 'push'
        run: terraform apply -auto-approve
      
      - name: AWS Plan Copy
        if: github.event_name == 'push' && steps.apply.outcome == 'success'
        run: aws s3 cp terraform.tfstate s3://your-bucket/yourapp-${{ env.ENVIRONMENT }}.tfstate
        id: copy
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ${{ secrets.REGION }}

Destroy Workflow:

name: Manually triggered workflow
on:
  workflow_dispatch:
    inputs:
      env:
        description: 'Environment'
        required: true
        default: 'dev'

jobs:
  destroy:
    name: "Destroy AWS"
    runs-on: ubuntu-latest
    steps:
        - name: AWS Plan Copy
          run: aws s3 cp s3://your-bucket/yourapp-${{ github.event.inputs.env }}.tfstate terraform.tfstate
          id: copy
          env:
            AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
            AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
            AWS_DEFAULT_REGION: ${{ secrets.REGION }}

        - name: Show Destroy plan
          run: terraform plan -destroy
          continue-on-error: true

        - name: Destroy resources jobs
          id: destroy
          run: terraform destroy -auto-approve

        - name: Delete plan file
          if: steps.destroy.outcome == 'success'
          run: aws s3 rm s3://your-bucket/yourapp-${{ github.event.inputs.env }}.tfstate
          env:
            AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
            AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
            AWS_DEFAULT_REGION: ${{ secrets.REGION }}

Solution 3:[3]

Here is how to create a workflow that could triggered manually both Terraform apply and destroy using inputs in workflow. Backends are responsible for storing state. So, we'll store the state file in S3 bucket.

To do this, First step is to create an S3 bucket.

Create a new file backend.tf and past the below code in it.

terraform {
  backend "s3" {
    bucket = "mybucket"
    key    = "path/to/my/key"
    region = "us-east-1"
  }
}

This assumes we have a bucket created called mybucket. The Terraform state is written to the key path/to/my/key.

Now create a workflow file.

name: "Terraform"

on:
  workflow_dispatch:
    inputs:
      # Working directory input from user.
      resource:
        type: choice
        description: Choose the resource
        options:
        - name_of_dir1
        - name_of_dir2
      # Terraform action you want to perform
      action:
        description: 'Terraform Action to Perform'
        type: choice
        options:
        - Terraform_apply
        - Terraform_destroy

jobs:
  terraform_apply:
    name: "Terraform_apply"
    if: ${{ github.event.inputs.action == 'Terraform_apply' }}
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ${{ github.event.inputs.resource }}
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v1

      - name: Terraform Init
        id: init
        run: terraform init
        env:
            AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
            AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
            AWS_DEFAULT_REGION: ${{ secrets.REGION }}
      
      - name: Terraform Validate
        id: validate
        run: terraform validate -no-color

      - name: Terraform Apply
        id: apply
        run: terraform apply -auto-approve -var-file=variables.tfvars
        env:
            AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
            AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
            AWS_DEFAULT_REGION: ${{ secrets.REGION }}
  
  terraform_destroy:
    name: "Terraform_destroy"
    if: ${{ github.event.inputs.action == 'Terraform_destroy' }}
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ${{ github.event.inputs.resource }}
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v1

      - name: Terraform Init
        id: init
        run: terraform init
        env:
            AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
            AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
            AWS_DEFAULT_REGION: ${{ secrets.REGION }}
      
      - name: Terraform Destroy
        id: destroy
        working-directory: ${{ github.event.inputs.resource }}
        run: terraform destroy -auto-approve -var-file=variables.tfvars
        env:
            AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
            AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
            AWS_DEFAULT_REGION: ${{ secrets.REGION }}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 baynezy
Solution 2
Solution 3 Kvyas