Collaboration and Automation¶
The last domain (around 15%) tests your ability to wire Terraform into VCS workflows, CI/CD pipelines, and organization-scale self-service patterns.
VCS Integration¶
HCP Terraform integrates natively with:
- GitHub (Cloud and Enterprise Server)
- GitLab (Cloud and Self-Managed)
- Bitbucket (Cloud and Data Center)
- Azure DevOps (Cloud and Server)
OAuth vs App¶
- GitHub App (preferred): granular per-repo permissions, no personal token
- OAuth: user-tied, broader permissions, works with older deployments
In HCP: Settings > Providers > add VCS provider. The resulting oauth_token_id is what workspaces reference.
Working Directory and Trigger Patterns¶
A monorepo often holds multiple Terraform modules. Configure per workspace:
- Working directory: relative path where
terraform initruns (e.g.,environments/prod) - VCS branch: which branch triggers runs (default: repo default branch)
- Auto-apply: whether to skip the human approval step
- Trigger patterns: glob patterns; only changes to matching files queue a run
environments/prod/**
modules/vpc/**
Without trigger patterns, any push to the configured branch queues a run for every workspace attached to that repo. With trigger patterns, runs queue only when matching files change.
Speculative Plans on Pull Requests¶
When a PR is opened against the tracked branch, HCP Terraform auto-runs a speculative plan. The plan output posts as a PR comment or status check. Plan succeeds or fails; no apply is ever offered. Merges to the trunk branch then trigger a real run (plan + approve + apply).
GitHub Actions Integration¶
For non-HCP setups or bespoke pipelines:
name: Terraform
on:
pull_request:
push:
branches: [main]
jobs:
terraform:
runs-on: ubuntu-latest
permissions:
id-token: write # for OIDC to AWS
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.9.8
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
- run: terraform fmt -check -recursive
- run: terraform init
- run: terraform validate
- name: Plan
if: github.event_name == 'pull_request'
run: terraform plan -out=tfplan -input=false
- name: Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve -input=false
The setup-terraform action installs the CLI and, with a token, configures terraform login for HCP Terraform. When using the cloud{} block, plan/apply actually execute remotely; the Actions runner just orchestrates.
GitLab CI Integration¶
stages: [validate, plan, apply]
variables:
TF_ROOT: ${CI_PROJECT_DIR}/environments/prod
default:
image: hashicorp/terraform:1.9.8
before_script:
- cd ${TF_ROOT}
- terraform init
validate:
stage: validate
script:
- terraform fmt -check -recursive
- terraform validate
plan:
stage: plan
script:
- terraform plan -out=$CI_PROJECT_DIR/tfplan
artifacts:
paths: [tfplan]
apply:
stage: apply
script:
- terraform apply -auto-approve tfplan
dependencies: [plan]
when: manual
only: [main]
GitLab's terraform managed state backend uses the http backend protocol.
API-Driven Workflow¶
For fully bespoke pipelines (e.g., Jenkins, Buildkite), use the HCP Terraform API directly.
Flow:
- Create a configuration version:
POST /workspaces/:id/configuration-versions - Upload a tarball of your config:
PUTto returned upload URL - Queue a run:
POST /runsreferencing the workspace and configuration version - Poll
GET /runs/:iduntil status isplanned_and_finishedorcost_estimated - If auto-apply is off,
POST /runs/:id/actions/applyafter approval
Useful Python and Go clients exist. HashiCorp's go-tfe library is the canonical Go client.
Self-Service Workspace Provisioning¶
Pattern: a "meta-workspace" manages other workspaces via the tfe provider.
resource "tfe_project" "team_a" {
name = "team-a"
organization = "my-org"
}
resource "tfe_workspace" "team_a_dev" {
name = "team-a-dev"
organization = "my-org"
project_id = tfe_project.team_a.id
vcs_repo {
identifier = "my-org/team-a-infra"
branch = "main"
oauth_token_id = var.oauth_token
}
execution_mode = "remote"
auto_apply = false
}
resource "tfe_team_project_access" "team_a_write" {
access = "write"
team_id = tfe_team.team_a.id
project_id = tfe_project.team_a.id
}
resource "tfe_variable_set" "team_a_aws" {
name = "team-a-aws-oidc"
}
resource "tfe_project_variable_set" "team_a" {
project_id = tfe_project.team_a.id
variable_set_id = tfe_variable_set.team_a_aws.id
}
New team? Add one project block and submit a PR. Merge triggers workspace provisioning.
Drift Detection Strategies¶
- HCP Terraform health assessments: scheduled
plan -refresh-only. Surfaces drift in UI and notifications. Best for ongoing passive monitoring. - Scheduled applies: cron-like run triggers that auto-remediate. Use cautiously; if drift reflects a legitimate emergency change, auto-apply undoes it.
- Third-party tools:
driftctl,terraformerfor bulk detection. Useful during onboarding. - AWS Config / Azure Policy: detect cloud-side changes outside Terraform and notify.
A robust pattern combines health assessments (for daily awareness) with Config rules (for real-time alerts on critical resources).
Module Versioning in Consumers¶
Consumer repos should pin:
module "vpc" {
source = "app.terraform.io/my-org/vpc/aws"
version = "~> 2.1"
}
On new major releases, maintainers communicate via:
- CHANGELOG.md in the module repo
- Announcement in team channel or org-wide readme
- Optionally, a dependabot-like bot PRs version bumps to consumers
Speculative plans on PRs catch most breakage before merge.
Run Tasks for Scanner Integration¶
Run tasks hit a webhook at a run phase with a signed HMAC. The handler evaluates and POSTs back a verdict.
Common integrations:
- Checkov, tfsec, Trivy: IaC security scanning
- Infracost: cost diff reporting
- Bridgecrew, Wiz, Snyk: commercial security platforms
- Custom webhooks: for org-specific policies
Attach at post-plan phase to evaluate the plan JSON before apply.
Observability¶
- HCP Terraform audit logs (Plus tier) stream via HTTP to SIEMs
- Workspace notifications into Slack or webhook for run lifecycle
- Prometheus exporter for HCP Terraform metrics (community)
Track:
- Apply success rate per workspace
- Mean plan duration
- Number of policy failures
- Frequency of manual overrides
Branching Strategies for Terraform¶
Trunk-based with environments as workspaces: single branch (main), multiple workspaces pointing to different directories or var files. Best for most orgs.
Branch-per-environment: main is prod, staging branch is staging, etc. Fragile and promotes drift between branches. Avoid unless required by policy.
GitOps (ArgoCD-style): PR merge to an env-specific branch triggers apply. Works but adds complexity.
Anti-Patterns¶
- Committing
.terraform/directory (should be gitignored) - Committing
terraform.tfstateto git - Sharing backend credentials across teams (use per-team roles)
- Configuring providers inside reusable modules (keep providers in root)
- One workspace per resource (over-decomposition)
- One workspace for everything (no blast-radius isolation)
Exam-Ready Checklist¶
- Can configure VCS-driven workspace with working dir and trigger patterns
- Can build a GitHub Actions pipeline using
setup-terraform - Can provision workspaces via
tfeprovider - Understand speculative plans on PRs
- Can integrate a run task end-to-end
- Know drift detection options and tradeoffs