Skip to content

CLZ-370 - GitHub Actions for Terraform

GitHub Actions for Terraform architecture
Service or workflow illustration. The lesson defines the exact resource settings. Open at full size.

Goal

Separate credential-free pull-request validation from a manually triggered Azure OIDC plan, with immutable action pins, least permissions, timeouts, concurrency, and no apply step. The Terraform target is one tagged resource group, but neither example creates it.

CLZ-370 learning sequence
Arrows show the learning sequence, not service traffic. CI examples. Open at full size.

Prerequisites

  • Complete CLZ-120 and understand saved-plan review.
  • A GitHub repository is needed only if you enable the examples.
  • For OIDC, an Azure application/federated credential and GitHub environment approval must be configured by an authorized owner.

Architecture

CLZ-370 — Two inactive workflow examples separate credential-free PR source checks from an owner-configured manual OIDC plan behind an environment gate. The resource group is a proposal only; neither example includes an apply operation.
Lesson-specific teaching diagram. Read the arrow legend and the accompanying architecture explanation. CI examples. On narrow screens, scroll the diagram horizontally. Open at full size.

The validation example runs checkout, Terraform setup, backend-free initialization, formatting, and validation on pull requests without Azure credentials. The separate manual workflow requests an OIDC token, signs in through azure/login, and plans one tagged resource group in the protected azure-lab-plan environment. It never applies that plan.

Resources And Cost

Relative cost: None when the examples are used as written. The workflows create no Azure resource. GitHub-hosted runner minutes depend on repository plan and usage; manually applying the lesson outside the examples would create the otherwise no-charge resource group.

Component Purpose and principal cost driver
Credential-free workflow example Pull-request validation with read-only repository permission.
Manual OIDC plan example workflow_dispatch, id-token: write, environment gate, and no apply.
Pinned third-party actions Full commit SHAs for immutable checkout, Terraform setup, and Azure login behavior.
Plan-only resource-group target Gives the OIDC plan one small Azure change to evaluate; it is not applied.

Inputs

Input Required substitution or decision
AZURE_CLIENT_ID GitHub environment/repository variable for the federated application's client ID.
AZURE_TENANT_ID Variable for the Entra tenant ID.
AZURE_SUBSCRIPTION_ID Variable for the exact plan subscription; never select by display name.
azure-lab-plan Create this GitHub environment and add required reviewers before enabling the OIDC plan.

Keep real subscription IDs, CIDRs, endpoint names, and secrets in untracked local files or protected repository settings.

Deploy

Review the plan and lesson-specific gates before deliberate execution. Local-only and read-only lessons create no Azure resources.
Review the plan and lesson-specific gates before deliberate execution. Local-only and read-only lessons create no Azure resources. Open at full size.

Run from the repository root in PowerShell 7:

Set-Location CLZ-370-github-actions-terraform
terraform init -backend=false -lockfile=readonly
terraform fmt -check
terraform validate
actionlint .github/workflows/terraform-validate.yml.example
actionlint .github/workflows/terraform-plan-oidc.yml.example
# From the repository root, copy only the example you intend to enable:
Set-Location ..
Copy-Item CLZ-370-github-actions-terraform/.github/workflows/terraform-validate.yml.example .github/workflows/lesson-terraform-validate.yml

Enabling the credential-free validation example is safe after path review. Do not enable the OIDC example until the environment, federated identity, variables, scope, and approval policy exist.

Validate

actionlint CLZ-370-github-actions-terraform/.github/workflows/terraform-validate.yml.example
actionlint CLZ-370-github-actions-terraform/.github/workflows/terraform-plan-oidc.yml.example
Select-String -Path CLZ-370-github-actions-terraform/.github/workflows/*.example -Pattern 'terraform apply|client-secret'
Select-String -Path CLZ-370-github-actions-terraform/.github/workflows/terraform-plan-oidc.yml.example -Pattern 'terraform plan'

Expected result:

  • Both example workflows pass actionlint.
  • The search returns no apply step or client secret.
  • Credential-free local/PR checks stop at formatting and validation. The authenticated OIDC workflow contains one plan step for the tagged resource-group target and remains plan-only.

Troubleshooting

Gather evidence, repair the cause, and rerun verification.
Gather evidence, repair the cause, and rerun verification. Open at full size.
Symptom Resolution
GitHub reports an invalid workflow after copying Keep the .yml extension, preserve indentation, and confirm referenced working directories exist from repository root.
OIDC login fails Match issuer/subject/audience in the federated credential and verify all three GitHub variables.
Plan is forbidden Grant only the required role at the intended scope and confirm environment approval selected the correct subscription.

Cleanup

Use the lesson state and cleanup instructions. Preserve remote-state dependencies until consumers are removed.
Use the lesson state and cleanup instructions. Preserve remote-state dependencies until consumers are removed. Open at full size.
Remove-Item .github/workflows/lesson-terraform-validate.yml -ErrorAction SilentlyContinue
Remove-Item tfplan -ErrorAction SilentlyContinue
terraform state list

There are no Azure resources to destroy. Remove test workflow copies or federated credentials separately if you created them.

Production Delta

  • Pin actions to reviewed full commit SHAs, use Dependabot/manual review for updates, and minimize GITHUB_TOKEN plus Azure permissions.
  • Require environment reviewers for cloud plans/applies, separate identities and workflows, protect branches, retain sanitized artifacts briefly, and never persist plan secrets.
  • Keep apply as a separately approved operation; this lesson intentionally provides none.

Exercises

  1. Enable the credential-free example on a branch and observe one passing and one intentionally failing format check.
  2. Draft the exact OIDC subject for the azure-lab-plan environment and have an authorized reviewer verify its scope.

← CLZ-360 - Azure Monitor and Log Analytics | Curriculum | CLZ-380 - Azure SQL Private Access →