Continuous Deployment Recipe using GitHub Actions, Checkov and Terraform Cloud(CLI)

7 minute read

"Buy Me A Coffee"

In my last article I was working with a CI/CD pipeline using Azure Pipelines. This time I wanted to use the same approach but using GitHub Actions which lately is the new trend and also it is known that Microsoft is investing a bit more around this product. I must say I was impressed on how fast I was a able to test it out.

I wanted to Test-Drive this model by using the Terraform Cloud CLI-driven Run Workflow instead of the API-driven Run Workflow for a change. Also, I have introduced some Static Code analysis using Checkov. Checkov is a static code analysis tool for scanning infrastructure as code (IaC) files for misconfigurations that may lead to security or compliance problems, so I was able to plug that in into my workflow as it has a Github Action too. drawing

Environment preparation

What do I need to get started?

Terraform Cloud

You need a Terraform Cloud Instance Free plan up to 5 users and you can leverage the Private Module registry and Remote State Storage.

  1. Create a workspace using the CLI workflow and assign the required variables to authenticate to the Azure Platform image-center

  2. Generate a token that will be used by GitHub to authenticate to TFC and trigger the workspace for the Infrastructure as Code to be deployed image-center

GitHub Repository

Create a new Github Repo.

  1. Go to your GitHub profile then settings then developer settings then personal access tokens
    • Generate a GitHub token. This is to be used for commenting the PR’s for review image-center
  2. Go to Settings then Secrets
    • Create a new secret named TFC_TOKEN and paste the TFC token value generated.
    • Create a secret called AZURE_CREDENTIALS. This is for the Web App deployment. Make sure you set the JSON format properly as you may get some errors. It should look like this.
          "clientId": "<GUID>",
          "clientSecret": "<GUID>",
          "subscriptionId": "<GUID>",
          "tenantId": "<GUID>",

🤜Looks like we got everything required regarding Authentication & Authorization🤛 image-center

Important Always keep security in mind and use credentials with less privileged access.

My repo is organized like this to make simple

Web App Repo
        |       |_terraform.yml
        |       |_
        |       |_
        |       |_
        |       |_
        |       |_
        |_ src
            |_ Application Source Code

Deployment Process

I have created a workflow with three GitHub Actions which actually were already available for its consumptionn in the GitHub Actions Marketplace one for the infrastructure Deployment, one for Static Code Analysis and one for the Web App code deployment. A difference to my article using AZDO, this time I am creating a Pull Request to do some validation in my terraform files prior applying the configuration.

  1. After code has been committed a Pull Request will be created, consequently a validation process will kick off. All results will be directly available in the Pull Request instead of opening the GitHub Action or the Terraform Cloud workspace. However, on the time I played with I could not get Checkov’s output to be advertised in the PR comments. I think if using the commands directly and installing the binary it will be more flexible ಥ_ಥ.
    • Terraform format: checks whether the configuration has been properly formatted
    • Terraform Validate: validates the configuration used in the GitHub action workflow.
    • Terraform plan: generates a Terraform plan in the Terraform Cloud workspace
    • Static Security Code Analysis: Potential compliance/misconfiguration image-center

Static Security Code Analysis with Checkov

Interestingly enough, and after a few rounds I had to set this variable soft_fail: true as it does not seem to be a way to select the policies that are important for your environment OOB. However, I found there are more policies for compliance than tfsec for my specific case. The policies as you can see below are good. However, some of them could potentially be false positives and/or not required depending on your environment. Still this is great tool and I think I will be exploring deeper into this project.

    - name: Run Checkov action
      id: checkov
      uses: bridgecrewio/checkov-action@master
        directory: terraform/
        soft_fail: true


  1. When merging the PR, it will trigger the deployment of the Infrastructure and the Web App deployment (I know I didn’t do any validation on the web app code ☜(゚ヮ゚☜))… and the final result is a deployed Web App in about ~10 mins. image-center

GitHub Workflow

The GitHub workflow in this case is distributed in one job with different multiple steps that will run based on the branch and github event. image-center

Infrastructure as Code Deployment Stage

    name: 'Terraform'
    runs-on: ubuntu-latest

    # Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest
        shell: bash
        working-directory: terraform

    # Checkout the repository to the GitHub Actions runner
    - name: Checkout
      uses: actions/checkout@v2

    # Install the latest version of Terraform CLI and configure the Terraform CLI configuration file with a Terraform Cloud user API token
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v1
        cli_config_credentials_token: $

    # Checks that all Terraform configuration files adhere to a canonical format
    - name: Terraform Format
      id: fmt
      run: terraform fmt -check

    # Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
    - name: Terraform Init
      id: init
      run: terraform init

    - name: Terraform Validate
      id: validate
      run: terraform validate -no-color

    # Generates an execution plan for Terraform
    - name: Terraform Plan
      id: plan
      if: github.event_name == 'pull_request'
      run: terraform plan -no-color
      continue-on-error: true

    - uses: actions/github-script@v4.1.0
      if: github.event_name == 'pull_request'
          PLAN: "terraform\n$"
          github-token: $
          script: |
            const output = `#### Terraform Format and Style 🖌\`$\`
            #### Terraform Initialization ⚙️\`$\`
            #### Terraform Validation 🤖\`$\`
            #### Terraform Plan 📖\`$\`
            #### Static Security Analysis 🕵️‍♀️\`$\`
            <details><summary>Show Plan</summary>



            *Pusher: @$, Action: \`$\`*`;
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: output

    - name: Terraform Plan Status
      if: steps.plan.outcome == 'failure'
      run: exit 1

      # On push to main, build or change infrastructure according to Terraform configuration files
      # Note: It is recommended to set up a required "strict" status check in your repository for "Terraform Cloud". See the documentation on "strict" required status checks for more information:
    - name: Terraform Apply
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: terraform apply -auto-approve   

Infrastructure as Code deployment in Action

The below is a quick demo of the Github Actions and Terraform Cloud interaction triggered via CLI to successfully deploy the Infrastructure required with minimum effort


Web App Deployment

The below describes the steps required to build and deploy the code in the Azure Web App, This is basd on the github action OOB.

    - uses: azure/login@v1
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        creds: $
    - name: Setup Node $
      uses: actions/setup-node@v1
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        node-version: $
    - name: 'npm install, build, and test'
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: |
          npm install
          npm run build
      # deploy web app using Azure credentials
    - uses: azure/webapps-deploy@v2
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        app-name: $
        package: $

      # Azure logout 
    - name: logout
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: |
          az logout


🤔My idea in this article was to share my first experience with GitHub Actions “translating” what I had for Azure DevOps and see the similarities and new opportunities that it may open. I really like that I was able to do all of this with not much effort, as there are many actions available in the Github Actions marketplace. I can see many new projects are invested in GitHub Actions to provide their product integrations. I also liked the speed to get GitHub runner compared to a Microsoft Hosted agent in AZDO

In regards to Checkov, I would love to have more flexibility on the policies as it may be hard to get one size to fit all. Still I have to dig a bit more but it can definitely a good tool to use for static code analysis. Terraform Sentinel can help with this, you still have to author all the policies and that could be tedious, especially if golang is not your cup of tea ☕. However, you have the flexibility. Checkov is Python based which for me can be easier to digest 👨‍💻,, so what is your preference??

Overall the experience was great with some small challenges but the documentation is good to get your head around it. Especially with the action gives you quite a good percentage of the heavy lifting 🦸‍♂️

I do hope this helps someone and that you find it informative,, so please let me know as constructive feedback is always important🕵️‍♂️,, That’s it for now,,, Hasta la vista🐱‍🏍!!!

🚴‍♂️ If you enjoyed this blog, you can empower me with some caffeine to continue working in new content 🚴‍♂️.

"Buy Me A Coffee"