Infrastructure pipeline¶
The terraform-modules project has its own .gitlab-ci.yml. Unlike application pipelines, it does not consume cicd-templates: it defines hidden OpenTofu templates and concrete jobs per layer. Provider selection changes the working directory and variables while retaining the same sequence.
Input controls¶
| Variable | Snapshot default | Behavior |
|---|---|---|
CLOUD_PROVIDER |
aws |
Validated against aws and gcp |
ENVIRONMENT |
pro |
State and resource_group prefix; the comment lists dev/pre/pro, but the script does not validate them |
PIPELINE_ACTION |
destroy |
Validated against create and destroy |
STACK |
Set by each job | network, kubernetes, database or platform |
TF_IN_AUTOMATION |
true |
Automation mode |
TF_INPUT |
false |
No interactive prompts |
Select the operation explicitly
The published configuration selects destruction plans by default. Applying those plans still requires manual actions. To create an environment, specify PIPELINE_ACTION=create, the chosen provider and environment, after reviewing resource isolation in state.
When launching a pipeline through the GitLab UI, creation settings are:
| Field | AWS | GCP |
|---|---|---|
| Execution branch for the current code | main |
main |
PIPELINE_ACTION |
create |
create |
CLOUD_PROVIDER |
aws |
gcp |
ENVIRONMENT |
Previously prepared environment name | Previously prepared environment name |
No environment value is selected automatically here: using dev in an account that already contains the fixed names associated with another state can cause collisions or incorrect resource ownership.
Common job preparation¶
.tofu_base uses the 4.7.0-opentofu1.12.1-alpine OpenTofu image, clears its entrypoint and installs bash, curl, jq and aws-cli. Even GCP jobs run this shared installation.
It then validates selector/action/directory, calculates TF_ROOT and state names, enters the root, exports HTTP backend and remote-state references, and maps DB, Agent, region and project variables. For GCP it writes the service-account JSON to a workspace file. See the variable contract.
Creation sequence¶
Each layer completes validation, planning and application before the next begins:
| Layer | Jobs, in order | Prerequisite |
|---|---|---|
| Network | validate_network, plan_network, apply_network |
No previous layer |
| Kubernetes | validate_kubernetes, plan_kubernetes, apply_kubernetes |
apply_network |
| Database | validate_database, plan_database, apply_database |
apply_kubernetes |
| Platform | validate_platform, plan_platform, apply_platform |
apply_database |
Validation runs tofu init -reconfigure, tofu fmt -check -recursive and tofu validate. The recursive option runs after entering a particular root, so it does not validate all eight repository roots at once. Validation also does not guarantee cloud permissions, quotas, cost or full deployment feasibility.
Planning runs tofu plan -out=plan.cache and exports JSON with tofu show -json. Files are copied to the workspace root as <stack>.plan.cache and <stack>.plan.json. Apply retrieves the artifact through needs, reinitializes the backend and applies that saved plan, without silently recalculating it.
Apply jobs are manual, have allow_failure: false, and are included only when the branch is literally main. This creates an explicit review point for each layer. A delayed approval should verify that the plan remains current and artifacts have not expired.
Destruction sequence¶
Each layer has plan_destroy_<stack> and apply_destroy_<stack> jobs. The first runs tofu plan -destroy; the second manually applies the saved destroy.cache. Order is platform → database → Kubernetes → network.
There is also force_unlock_gcp_database_private_service_access. Despite its name, the script runs tofu state rm for the Private Service Access connection. It does not release a lock. It is restricted to GCP/destroy/main, uses the fixed dev-gcp-database resource group and does not make the manual job optional with allow_failure: true. Its effect on overall pipeline status therefore also needs review; do not run it merely to clear a blocked pipeline status. The exceptional procedure is explained in teardown.
Artifacts and reports¶
Binary and JSON plans are kept for one week. Apply operationally depends on them: if approval waits longer than retention, regenerate and review the plan. The inspected configuration has no explicit artifacts:access control.
The YAML declares artifacts:reports:terraform, but submits full tofu show -json output. The documented GitLab integration expects an action summary with create, update and delete. An artifact type in YAML does not establish that the merge-request widget works correctly. Summary conversion and access restrictions are recommended improvements to the infrastructure repository.
Branches, merge requests and concurrency¶
Creation validation/planning jobs are included on any branch, while apply jobs exist only on main. However, validate_kubernetes has a needs dependency on apply_network, and later layers follow the same pattern. Static inspection therefore indicates that creation on another branch may produce an invalid graph with a missing required job. Check this using CI Lint before presenting the pipeline as suitable infrastructure validation for every merge request.
Resource groups constrain concurrency per environment/provider/layer; the backend adds its own locking. Neither serializes the full planning-to-manual-approval cycle. Coordinate runs for each environment and regenerate plans after any state change to avoid stale plans.
These observations describe the snapshot and do not modify pipelines in the original repositories. The documentation pipeline supplied in this ZIP is independent and does not provision or destroy infrastructure.
Available historical evidence¶
Public pipeline 2604650152, dated 16 June 2026, records successful destruction jobs for all four layers and the Private Service Access job; it includes two plan_destroy_database executions. It belongs to an earlier commit than the reviewed snapshot. It is historical teardown evidence, not proof of running clusters today or successful execution of the current HEAD.
Sources¶
- Complete pipeline, commit ae1502b0.
- State and credentials, variables, teardown.
- GitLab: merge-request plan reports.
- Thesis §7.2.7, printed pages 45–49 (PDF 51–55), figures 8 and 10: infrastructure execution and states. Defaults, branch rules and the recovery job come from the source code.