Gjorgji Jovanovski Gjorgji Jovanovski

Working with Mid-Job Manual Approvals in GitHub Workflows.

In modern software development workflows, automation is key to achieving efficiency and reliability. CI/CD pipelines enable teams to automatically build, test, and deploy their applications. However, not all task’s can be automated, especially when it comes to critical steps such as deploying infrastructure changes. When working with Terraform in an automated manner, we often want to implement some type of “Manual Validation” step between our Plan and Apply stage.

One of my favorite task’s in Azure DevOps is the ManualValidation@0 task allowing the user to put the pipeline on hold for a certain period of time in order for them to be able to review the Plan output. Once the validation is complete, the changes can be approved or denied, depending on the review outcome.

Here is a simple example:

jobs:
- job: terraform_plan
  # Terraform Plan configuration
- job: manual_approval
  displayName: User validation
  dependsOn: terraform_plan
  condition: succeeded('terraform_plan')
  timeoutInMinutes: 360
  pool: server
  steps:
    - task: ManualValidation@0
      displayName: 'User validation'
      timeoutInMinutes: 60
      inputs:
        notifyUsers: # email input
        instructions: 'Please review the Terraform Output. Evaluate the changes and resume if all is good!'
        onTimeout: 'reject'
- deployment: terraform_apply
  dependsOn:
  - terraform_plan
  - manual_approval
  # Terraform Apply configuration

At the time of writing this article, GitHub does not support a manual validation step in the middle of a job. In order to implement such a solution in GitHub, you can use the trstringer/manual-approval action. This action allows you to add a manual approval step where you see fit. You can configure the number of minimum approvals required, specify the issue title and body for the approval request, and even include additional approved or denied words. This way, you can ensure that the Terraform plan is validated before proceeding with the Terraform apply step.

Here is a simple example:

jobs:
  terraform-deploy:
    name: Terraform Resource Deployment
    runs-on: ubuntu-latest
    steps:
      # Prerequisites
      ## Checkout code ##
      ## Set Environment Specific Variables ##
      ## Login to Azure ##
      ## Set subscription ##

      # Terraform Init
      - name: Initialize Terraform
        run: |
          terraform init

      # Terraform Plan
      - name: Plan Terraform
        run: |
          terraform plan -out=tfplan.out

      # Terraform Apply
      ## Manual Approval ##
      - uses: trstringer/manual-approval@v1
        name: Manual Approval
        timeout-minutes: 60
        with:
          secret: ${{ github.token }}
          approvers: # your github username or team name
          minimum-approvals: 1
          issue-title: "Please validate the terraform plan"
          issue-body: "Please check the output of the terraform plan and approve or deny"
          exclude-workflow-initiator-as-approver: false
          additional-approved-words: 'yes'
          additional-denied-words: 'no'

      ## Terraform Apply ##
      - name: Apply Terraform
        run: |
          terraform apply -auto-approve tfplan.out

Some remarks are that the custom action creates issues in our GitHub repository, that we then approve. This is not the cleanest solution, but for now if you are trying to achieve mid job manual approvals I have not found a better workaround.

I hope that GitHub will add this as a native feature in the future since it adds a lot of control over our workflows!