Skip to content

Terraform CI

Terraform CI #2273

Workflow file for this run

name: "Terraform CI"
on:
pull_request:
merge_group:
types: [checks_requested]
push:
branches: [main]
env:
GO_VERSION: "1.21"
jobs:
setup:
name: "Setup and build matrix"
runs-on: ubuntu-24.04-arm
outputs:
validation-matrix: ${{ steps.build-matrix.outputs.validation-matrix }}
has-terraform-changes: ${{ steps.check-changes.outputs.has-changes }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Grab the last 75 commits. Later on we need to a git diff, and it's
# very likely that two commits involved will be inside the last 75
fetch-depth: 75
- name: Initialise mise
uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1
with:
github_token: ${{ secrets.MISE_PAT }} # As this is a private repo, we can get API rate limit issues. Use a PAT to avoid this. (The token needs no special permissions.)
install_args: go:github.com/hashicorp/terraform-config-inspect
- name: Check for Terraform changes
id: check-changes
env:
GIT_FROM: ${{github.event.pull_request.base.sha}}
GIT_TO: ${{github.event.pull_request.head.sha}}
run: |
set -e -u -o pipefail
if [[ $(git diff "${GIT_FROM}"..."${GIT_TO}" --name-only -- "*.tf" "*.tf.json" | wc -l) -eq 0 ]]; then
echo "No Terraform files have changed."
echo "has-changes=false" >> "$GITHUB_OUTPUT"
else
echo "Terraform files have changed."
echo "has-changes=true" >> "$GITHUB_OUTPUT"
fi
- name: Build validation matrix
id: build-matrix
if: steps.check-changes.outputs.has-changes == 'true'
env:
GIT_FROM: ${{github.event.pull_request.base.sha}}
GIT_TO: ${{github.event.pull_request.head.sha}}
run: |
set -e -u -o pipefail
# Get modified directories
git diff "${GIT_FROM}"..."${GIT_TO}" --name-only -- "*.tf" "*.tf.json" \
| xargs dirname \
| sort \
| uniq \
> modified-tf-dirs.txt
# Find all roots
all_roots=$(find infra/deployments -type d \
-mindepth 1 -maxdepth 2 \
-not -path "*/tfvars" \
-not -path "infra/deployments/forms" \
-not -path "infra/deployments/deploy" \
-not -path "infra/deployments/integration"
)
# Build dependency graph and determine which roots need validation
validation_dirs=()
for start in ${all_roots}; do
echo "Inspecting ${start}"
stack=("${start}")
visited=()
while [[ "${#stack[@]}" -gt 0 ]]; do
path="${stack[0]}"
stack=("${stack[@]:1}") # Shift the front element off
# shellcheck disable=SC2199
# shellcheck disable=SC2076
if [[ "${visited[@]}" =~ "${path}" ]]; then
# Skip because it's already been visited
continue
fi
visited+=("${path}")
declare -a new_paths
readarray -t new_paths < <(terraform-config-inspect --json "${path}" \
| jq -r '.module_calls | to_entries | .[] | .value.source' \
| sort \
| uniq \
| xargs -I{} readlink -f "${path}/{}" \
| xargs -I{} realpath --relative-to "$(pwd)" "{}" \
| sort
)
if [[ "${#new_paths[@]}" -gt 0 ]]; then
stack+=( "${stack[@]}" "${new_paths[@]}" )
fi
done
printf '%s\n' "${visited[@]}" | sort | uniq > "${start}/directories.txt"
matching_lines="$(comm -1 -2 modified-tf-dirs.txt "${start}/directories.txt")"
if [[ -n "$matching_lines" ]]; then
echo "✓ ${start} requires validation"
validation_dirs+=("${start}")
else
echo "✗ ${start} does not require validation"
fi
done
# Convert to JSON array for matrix
if [[ "${#validation_dirs[@]}" -gt 0 ]]; then
validation_matrix=$(printf '%s\n' "${validation_dirs[@]}" | jq -Rnc '[inputs]')
echo "validation-matrix=${validation_matrix}" >> "$GITHUB_OUTPUT"
echo "Matrix: ${validation_matrix}"
else
echo "validation-matrix=[]" >> "$GITHUB_OUTPUT"
echo "No directories require validation"
fi
terraform-fmt:
name: "Terraform format check"
needs: setup
if: needs.setup.outputs.has-terraform-changes == 'true'
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Initialise mise
uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1
with:
github_token: ${{ secrets.MISE_PAT }}
install_args: terraform
- name: Check Terraform style
run: |
terraform fmt -write=false -diff=true -list=true -recursive -check
terraform-validate:
name: "Terraform validate"
runs-on: ubuntu-24.04-arm
needs: setup
if: needs.setup.outputs.has-terraform-changes == 'true' && needs.setup.outputs.validation-matrix != '[]'
strategy:
fail-fast: false
matrix:
directory: ${{ fromJSON(needs.setup.outputs.validation-matrix) }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Initialise mise
uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1
with:
github_token: ${{ secrets.MISE_PAT }}
install_args: terraform
- name: Validate ${{ matrix.directory }}
env:
TF_PLUGIN_CACHE_DIR: "${{ runner.temp }}/terraform_cache"
run: |
set -e -u -o pipefail
mkdir -p "$TF_PLUGIN_CACHE_DIR"
echo "Validating ${{ matrix.directory }}"
terraform -chdir="${{ matrix.directory }}" init -backend=false
terraform -chdir="${{ matrix.directory }}" validate
tflint:
name: "tflint"
needs: setup
if: needs.setup.outputs.has-terraform-changes == 'true'
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Initialise mise
uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1
with:
github_token: ${{ secrets.MISE_PAT }}
install_args: tflint
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
name: Cache tflint plugin dir
with:
path: ~/.tflint.d/plugins
key: tflint-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('.tflint.hcl') }}
restore-keys: |
tflint-${{ runner.arch }}-${{ runner.os }}-
- name: Init tflint
run: tflint --init
env:
# https://github.com/terraform-linters/tflint/blob/master/docs/user-guide/plugins.md#avoiding-rate-limiting
GITHUB_TOKEN: ${{ github.token }}
- name: Run tflint
run: |
make tflint
checkov:
name: "Checkov"
needs: setup
if: needs.setup.outputs.has-terraform-changes == 'true'
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Initialise mise
uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1
with:
github_token: ${{ secrets.MISE_PAT }}
install_args: checkov
- name: Run Checkov against Terraform
run: |
checkov -d infra/ --external-checks-dir infra/checkov/ --framework terraform --quiet --skip-download
report:
# Required, for 'required PR status checks' on GitHub.
name: "Run terraform tests"
runs-on: ubuntu-24.04-arm
needs: [terraform-fmt, terraform-validate, tflint, checkov]
if: ${{ always() && !failure() && !cancelled() }}
steps:
- name: No checks failed
run: echo "No terraform issues found."