Continuous Deployment Recipe using GitHub Actions, Checkov and Terraform Cloud(CLI)
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.
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
.
-
Create a workspace using the CLI workflow and assign the required variables to authenticate to the Azure Platform
-
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
GitHub Repository
Create a new Github Repo.
- Go to your GitHub profile then
settings
thendeveloper settings
thenpersonal access tokens
- Generate a GitHub token. This is to be used for commenting the PR’s for review
- Go to
Settings
thenSecrets
- 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 theJSON
format properly as you may get some errors. It should look like this.{ "clientId": "<GUID>", "clientSecret": "<GUID>", "subscriptionId": "<GUID>", "tenantId": "<GUID>", }
- Create a new secret named
🤜Looks like we got everything required regarding Authentication & Authorization🤛
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
|
|_.github\workflows
| |_terraform.yml
|
|_Terraform
| |_ main.tf
| |_ variables.tf
| |_ outputs.tf
| |_ backend.tf
| |_ provider.tf
|
|_ 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.
- 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
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
with:
directory: terraform/
soft_fail: true
- 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.
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.
Infrastructure as Code Deployment Stage
jobs
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
# Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest
defaults:
run:
shell: bash
working-directory: terraform
steps:
# 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
with:
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'
env:
PLAN: "terraform\n$"
with:
github-token: $
script: |
const output = `#### Terraform Format and Style 🖌\`$\`
#### Terraform Initialization ⚙️\`$\`
#### Terraform Validation 🤖\`$\`
#### Terraform Plan 📖\`$\`
#### Static Security Analysis 🕵️♀️\`$\`
<details><summary>Show Plan</summary>
\`\`\`\n
${process.env.PLAN}
\`\`\`
</details>
*Pusher: @$, Action: \`$\`*`;
github.issues.createComment({
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: https://help.github.com/en/github/administering-a-repository/types-of-required-status-checks
- 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'
with:
creds: $
- name: Setup Node $
uses: actions/setup-node@v1
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
with:
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'
with:
app-name: $
package: $
# Azure logout
- name: logout
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: |
az logout
Summary
🤔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 🚴♂️.
Comments