Terraform Execution¶
Policy¶
Terraform is part of Vayzo CI/CD. Routine staging infrastructure reconciliation is automatic;
production infrastructure reconciliation is tied to the release approval gate. Shared/platform
changes use an explicit Jenkins ACTION=Infrastructure plan/approval/apply flow.
The knowledge portal build itself never changes infrastructure; Jenkins is the controlled executor.
Safety model¶
| Operation | State access | Infrastructure change | CI behavior |
|---|---|---|---|
terraform fmt -check |
No | No | Every PR/build |
terraform init -backend=false + validate |
No | No | Every PR/build |
terraform plan |
Yes | No | Staging release, production release, infrastructure action |
saved-plan terraform apply |
Yes | Yes | Automatic staging; after approval for production/infrastructure action |
terraform destroy |
Yes | Destructive | Not part of Jenkins delivery pipeline |
Runner¶
Use scripts/terraform-ci.sh; it executes Terraform in the official
hashicorp/terraform:1.9 Docker image.
Credential-free static validation¶
This runs:
- recursive
terraform fmt -check -diff; terraform init -backend=false;terraform validate;
for platform, staging, production, and shared.
No R2 state or provider credentials are contacted.
Real plan and apply¶
A Jenkins Secret-file credential is mounted as TFVARS_FILE. Cloudflare R2 Terraform-state
credentials are injected as AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY.
plan writes a binary plan under artifacts/terraform/ plus a text report. apply refuses to run
unless that saved plan exists in the current Jenkins workspace. This ensures the applied change is
the same change Jenkins planned.
Environment behavior¶
Staging¶
Every deploy from release/* and every release tag performs staging plan/apply automatically before
Kubernetes Secret synchronization and Helm deployment.
This is intentionally idempotent: a no-change plan simply reconciles to the existing state.
Production¶
A release tag performs the production plan only after staging has passed. Jenkins archives the plan, presents the single release approval, then applies the saved production plan and continues with Kubernetes deployment.
Platform/shared¶
These roots can affect more than one application environment or the CI host itself. Use:
Jenkins validates and plans, asks for the infrastructure approval, then applies the saved plan. The same mode is also available for deliberate staging/production infrastructure-only changes.
State backend¶
State remains in Cloudflare R2 through Terraform's S3-compatible backend. Each root has its own state key. Staging/production read platform state but do not write it.
The R2 state bucket is a bootstrap dependency and remains a one-time setup concern. Its access keys live only in Jenkins Credentials/developer secret stores.
Concurrency¶
The Jenkinsfile uses disableConcurrentBuilds(), preventing two Vayzo Jenkins executions from
applying the same environment simultaneously. Terraform state is not committed to Git.
R2 does not provide DynamoDB-style Terraform state locking, so operators must not run a competing
manual terraform apply while Jenkins is reconciling the same root.
Secrets¶
Do not commit:
Sensitive environment variables are supplied through Jenkins Secret-file credentials or
TF_VAR_* variables. Terraform plan artifacts are retained only in the private Jenkins build
context required for review/evidence.
Destroy¶
terraform destroy is deliberately absent from the delivery pipeline. Destruction requires a
separate, deliberate recovery/decommissioning procedure with backup confirmation.