TerraformIII · Installing and Versioning TerraformProduction Terraform
Verifying the Installation
What you'll learn
- Run the canonical post-install checks: terraform version, autocomplete, provider resolution
- Distinguish a healthy install from one that is technically present but operationally broken
- Set up a CI gate that fails the build when the Terraform binary is wrong
- Define an audit cadence that detects drift between hosts over time
- Recognise the symptoms of a partially-installed or tampered Terraform
Prerequisites
None — start here.
Verified against Terraform CLI 1.9.x · OpenTofu 1.7.x · HCL 2.0 · bpg/proxmox provider 0.66+ · hashicorp/local provider 2.5+ · hashicorp/null provider 3.2+ · hashicorp/random provider 3.6+ · hashicorp/http provider 3.4+ · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · 2026-08-13
A binary being present in $PATH is not a verified install. It is
the first check of many. A production install is verified when we
have evidence that the binary is the version we expect, that it
can resolve the providers the configuration requires, that it can
reach the state backend, and that the same evidence can be
produced by a CI runner on every commit. This lesson is the
checklist.
The four checks
A verified install is the conjunction of four independent checks.
+--------------------+ +--------------------+ +--------------------+ +--------------------+
| Binary check | | Autocomplete check | | Provider check | | Backend reachability|
| - terraform version| | - autocomplete | | - terraform init | | - curl / API call |
| - hash | | - shell hooks | | - provider mirrors | | - IAM role / token |
+--------------------+ +--------------------+ +--------------------+ +--------------------+
Run them in this order. A failure in an earlier check usually makes later checks meaningless.
1. The binary check
# READ-ONLY
terraform version
Terraform v1.9.8
on linux_amd64
What to confirm:
- The version string matches the team’s pin. A 1.9.8 binary is fine when the team is on 1.9.8. A 1.10.0 binary in a 1.9.x fleet is a rollout failure.
- The architecture line is correct.
linux_amd64is the production shape on Ubuntu 24.04 and Debian 12. Adarwin_arm64on a Linux server is the wrong binary. - The provider line, if present, lists the cached providers. This is informational; it is the next check that confirms the providers actually resolve.
For the standalone binary, a second check is the hash:
# READ-ONLY
sha256sum $(which terraform)
Compare against the upstream SHA256SUMS. A mismatch is an
integrity incident.
2. The autocomplete check
# CONFIGURATION: install autocomplete for the current shell
terraform -install-autocomplete
You can now run terraform -install-autocomplete to install autocomplete
for a different shell, or to output the autocomplete script to stdout.
What this does: writes a completion script to the user’s bash or
zsh configuration. It is idempotent. A fresh host returns the
above message and adds the completion script to ~/.bashrc.
What to confirm:
- The exit code is zero. A non-zero exit means the install
could not write the completion file (typically a permissions
issue on
~/.bashrc). - The completion is in effect. Open a new shell and type
terraform pfollowed by the Tab key. The completion should offerplan,providers,push.
If the team’s workstations use zsh, run terraform -install-autocomplete once for each shell the team uses. The
command supports bash and zsh out of the box.
3. The provider resolution check
A binary that runs is not enough. The CLI must be able to resolve the providers the configuration uses. The check is a minimal configuration and a dry init.
# READ-ONLY: minimal working directory
mkdir -p /tmp/tf-verify && cd /tmp/tf-verify
# /tmp/tf-verify/main.tf
terraform {
required_providers {
null = {
source = "hashicorp/null"
version = "~> 3.0"
}
}
}
resource "null_resource" "smoke" {
count = 1
}
# READ-ONLY: confirm the CLI resolves the provider
terraform init -backend=false
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/null...
- Installing hashicorp/null v3.2.3...
- Installed hashicorp/null v3.2.3 (unauthenticated)
Terraform has been successfully initialized!
What to confirm:
- The init succeeds. A failure here is the most common post-install problem and almost always points at network or mirror configuration.
- The provider version matches the lock file. If the team’s
configuration locks
nullat3.2.2and the install resolves3.2.3, the install is healthy but the working directory is not. - The
(unauthenticated)warning is acceptable in this context. It refers to the registry’s signature; in production the mirror should be authenticated, but for a verification smoke test against the public registry, the warning is normal.
Clean up:
# CONFIGURATION: remove the smoke working directory
rm -rf /tmp/tf-verify
4. The state-backend reachability check
The CLI on a host can be perfectly installed and still fail every apply because the backend is unreachable. The check depends on the backend.
S3 backend. The CLI uses the AWS SDK’s credentials chain. The reachability check is whether the SDK can resolve the bucket.
# READ-ONLY
aws s3 ls s3://acme-tf-state-eu-west-1/
2025-09-12 14:02:18 prod
2025-09-12 14:02:18 staging
Self-hosted terraform serve.
# READ-ONLY
curl -fsS -H "Authorization: Bearer $TF_TOKEN" \
https://tf-state.internal.example.com:9080/_health
{"status":"ok"}
Terraform Cloud.
# READ-ONLY: confirm the saved API token is valid
terraform login
A non-zero exit or an authentication error is the failure to debug, not the install.
The CI gate
The post-install checks are necessary but not sufficient. The team also needs a CI gate that fails the build if the binary is wrong, so a host cannot merge a configuration change while running a different Terraform than the rest of the team.
# .github/workflows/tf-verify.yml (illustrative)
name: tf-verify
on: [pull_request]
jobs:
verify:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
# CONFIGURATION: install the pinned Terraform
- name: Install Terraform 1.9.8
run: |
wget -q https://releases.hashicorp.com/terraform/1.9.8/terraform_1.9.8_linux_amd64.zip
wget -q https://releases.hashicorp.com/terraform/1.9.8/terraform_1.9.8_SHA256SUMS
sha256sum -c <(grep linux_amd64 terraform_1.9.8_SHA256SUMS) < terraform_1.9.8_linux_amd64.zip
sudo unzip -o terraform_1.9.8_linux_amd64.zip -d /usr/local/bin
# READ-ONLY: verify
- name: Verify
run: |
terraform version
terraform -install-autocomplete
terraform init -backend=false
terraform validate
terraform plan -input=false
The four commands in the Verify step are the CI gate. A failure
on any of them fails the build. The exact commands depend on the
backend and the configuration; the principle does not.
The audit cadence
The four checks run at install time. They do not run again automatically. Over weeks and months, a host can drift: an operator upgrades Terraform locally, a CI runner is rebuilt with the wrong image, a new bastion is provisioned with a different mirror. The audit cadence catches the drift.
Daily. The CI gate runs on every pull request. If a host is on the wrong version, it cannot merge a plan.
Weekly. A scheduled job runs terraform version on every host
the team owns and reports the result to a central dashboard. The
hosts whose version is wrong are the hosts to fix.
Quarterly. A live rollback rehearsal on a non-production host. Pick a host, downgrade to the previous known-good version, run the smoke test, then upgrade back. The point is to confirm that the rollback actually works and that the previous version is retained.
Per release. When HashiCorp ships a new minor, or when the team decides to adopt a new OpenTofu release, the upgrade is a deliberate event: read the changelog, run the upgrade on staging, confirm the dev plan is empty, then roll the team forward.
Production failure modes
1. Binary present but wrong version. Symptom: terraform version returns 1.10.0 on a host the team believes is on 1.9.8.
Recovery: downgrade using the standard install method; re-run the
audit job to confirm.
2. Autocomplete install fails silently. Symptom: a new
engineer cannot get completion working; the bashrc is owned by
root because the engineer was provisioned as root. Recovery: fix
the home-directory ownership, re-run terraform -install-autocomplete.
3. Provider resolution fails after a private-mirror cutover.
Symptom: terraform init fails with “provider not available in
the mirror”; the CLI does not fall back to the upstream registry
because the direct { exclude } line excludes everything. Recovery:
either populate the mirror with the missing provider, or remove
the exclude clause for that provider, depending on the team’s
policy.
4. CI gate green but production apply fails. Symptom: the CI
plan succeeded; the production apply failed on terraform init
because the production host’s ~/.terraformrc points at a mirror
that does not exist. Recovery: the CI gate must run with the same
client configuration as the production hosts. Bake the
terraformrc into the CI image.
5. The audit job is ignored. Symptom: the weekly version report shows five hosts on the wrong version for a month; nobody reads the report. Recovery: page on a sustained version mismatch. The audit is only useful if someone acts on it.
6. The smoke test does not match production. Symptom: the smoke test runs against the dev backend; production is on a different backend with different IAM. A host that passes the smoke test fails production. Recovery: the smoke test is a subset, not the whole. The CI plan against staging is the closer match; the production apply is the only true test.
Security and performance
- Hash verification is part of the binary check. The
sha256sumagainst the upstreamSHA256SUMSis the integrity boundary for the standalone binary. The apt repository verifies GPG. Either is fine; the rule is “verify before trust”. - Provider cache. Once the providers are cached,
initis near-instant. The cache lives in~/.terraform.d/plugin-cacheby default; on a CI runner, point it at a path that survives between jobs. - Reachability checks are not free. A
curlto a state API on every CI run is cheap; aterraform planagainst a real backend on every CI run is expensive. The CI gate should distinguish between the smoke test (every PR) and the full plan (merge to main).
Production guidance
- Run the four checks on every new host. Bake them into the bootstrap script; do not rely on the engineer remembering.
- Make the CI gate fail loud. A non-zero exit on any check fails the build. The configuration is not safe to merge if the host cannot verify the install.
- Audit weekly. A scheduled job that reports every host’s version. Page on a sustained mismatch.
- Document the rollback. The previous version is retained; the downgrade command is in the runbook; the rehearsal is quarterly.
What comes next
The next lesson covers the team-wide rollout procedure that ties
the install method, the version pin, the ~/.terraformrc, and
the smoke test into a single change applied to every host the team
owns.
Verification
Run the four checks against your own host.
# READ-ONLY
terraform version
which terraform
terraform -install-autocomplete
# READ-ONLY: provider resolution
mkdir -p /tmp/tf-verify && cd /tmp/tf-verify
cat > main.tf <<'EOF'
terraform {
required_providers {
null = {
source = "hashicorp/null"
version = "~> 3.0"
}
}
}
resource "null_resource" "smoke" { count = 1 }
EOF
terraform init -backend=false
terraform validate
cd /tmp && rm -rf /tmp/tf-verify
# READ-ONLY: backend reachability (example for self-hosted)
curl -fsS -H "Authorization: Bearer $TF_TOKEN" \
https://tf-state.internal.example.com:9080/_health
A clean output of every command is the verification.
Knowledge check · 7 questions
Q1. What is the FIRST post-install verification?
Q2. What does terraform -install-autocomplete do?
Q3. A terraform version that returns the expected version string is sufficient verification for a production install.
Q4. Which reachability check applies to a self-hosted terraform serve backend?
Q5. Which of the following are part of a healthy post-install verification? (Select all that apply.)
Q6. The CI gate runs terraform version and terraform validate. The production apply fails on terraform init. What is the most likely gap?
Q7. How often should the team audit Terraform version drift across hosts?
Passing score: 75%. Answers are checked in this browser.