Skip to content

Multiple environments and roots

One Action invocation builds one Terraform root with one ordered set of var files. Staging and production planned from the same root with different tfvars are two builds. So are two separate roots. Each gets its own diagram and its own pull request comment.

Steps in a job run one after another, so two environments as two steps means two full init + plan + scan cycles, back to back. Legs of a matrix are separate jobs and run at the same time.

.github/workflows/infragram.yml
jobs:
diagram:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- environment: staging
varFile: environments/staging.tfvars
- environment: production
varFile: environments/production.tfvars
steps:
- uses: actions/checkout@v7
- uses: hashicorp/setup-terraform@v4
with:
terraform_wrapper: false
- uses: actions/setup-go@v7
with:
go-version: stable
# … cloud credentials …
- uses: Tekchbila-Studios/infragram-action@v1
with:
working-directory: infrastructure
environment: ${{ matrix.environment }}
var-files: |
environments/common.tfvars
${{ matrix.varFile }}

For separate roots, put the directory in the matrix too:

matrix:
include:
- environment: network
directory: stacks/network
- environment: platform
directory: stacks/platform
# …
with:
working-directory: ${{ matrix.directory }}
environment: ${{ matrix.environment }}

A newer build replaces an older one on the same pull request, but only within the same environment. Two builds that both leave environment unset count as the same build, so the second replaces the first and the pull request ends up with one diagram instead of two.

Environment names can be up to 40 characters: letters, digits, spaces, ., _ and -, starting with a letter or digit. Any other value is ignored, and the build counts as the unnamed default environment.

Without it, a plan that fails in one environment cancels the other legs, including ones that were about to produce a diagram.

Keep concurrency at the workflow level:

concurrency:
group: infragram-${{ github.event.pull_request.number }}
cancel-in-progress: true

If you move it onto the job, every matrix leg lands in the same group, and cancel-in-progress makes the legs cancel each other. A job-level group has to include the matrix value in its name, for example infragram-${{ github.event.pull_request.number }}-${{ matrix.environment }}.

Matrix legs post their bundles within seconds of each other and can hit your account’s concurrent build limit. This is expected. The Action waits and retries for up to five minutes and logs this notice:

This account already has the maximum number of builds rendering. Retrying in 30s.

It doesn’t drop the build. By that point the plan and the scan are done, and failing over a few seconds of contention would waste them.

var-files is read in order, and later files override earlier ones. Paths are relative to working-directory, because that is where Terraform runs. Don’t list terraform.tfvars or *.auto.tfvars, which Terraform loads on its own.

To override individual variables, use vars, one key=value per line:

vars: |
region=eu-west-1
cluster_name=platform blue

Values are read line by line, so spaces need no quoting.

Terraform refuses to plan a configuration whose count or for_each depends on a value known only after apply, such as an IPAM-allocated CIDR or the ID of a resource that doesn’t exist yet. There is no plan to diagram, and retrying can’t produce one.

The Action treats this one case as a warning and exits successfully, so one such root doesn’t turn a whole matrix red. Any other plan error still fails the step. See Troubleshooting.