d1aa5daf2b
---ci--- project: atelier phase: 5 milestone: v0.2 status: complete requirements: covered: [ATELIER-36, ATELIER-37, ATELIER-38, ATELIER-39, ATELIER-40, ATELIER-41, ATELIER-42, ATELIER-43, ATELIER-44, ATELIER-45, ATELIER-46, ATELIER-47, ATELIER-48, ATELIER-49, ATELIER-50, ATELIER-51, ATELIER-52, ATELIER-53, ATELIER-54, ATELIER-55, ATELIER-56, ATELIER-57, ATELIER-58, ATELIER-59] partial: [] ---/ci---
4.1 KiB
4.1 KiB
Bad Example: Unlocked Local State
An IaC configuration that violates Atelier's Infrastructure as Code principles. Each violation is cited.
The Code
# main.tf — committed to the repo
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
# no version pin
}
}
}
provider "aws" {
region = "us-east-1"
access_key = "AKIAIOSFODNN7EXAMPLE" # committed
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" # committed
}
resource "aws_s3_bucket" "logs" {
bucket = "app-logs-prod"
}
resource "aws_db_instance" "primary" {
identifier = "app-prod"
username = "admin"
password = "hunter2" # committed, in plaintext, in state
allocated_storage = 20
engine = "postgres"
instance_class = "db.t3.micro"
skip_final_snapshot = true
}
The team runs terraform apply from their laptops. The state file terraform.tfstate is committed alongside main.tf "so everyone is on the same page."
What Makes It Bad
Local State in a Shared Environment (IaC P8 Remote State with Locking)
- State is
terraform.tfstateon each laptop. Two team members runterraform applysimultaneously; the second to finish silently overwrites the first's changes. There is no lock. - The state file is committed to the repo. It contains the DB password in plaintext. It is a secret-bearing artifact in version control.
- Fix: use a remote backend with locking (S3 + DynamoDB, GCS, etc.). Never commit state. See
domains/infrastructure-as-code/state.md.
Hardcoded Secrets (IaC P10 Secrets Never in Code)
access_keyandsecret_keyare committed inmain.tf. The DBpasswordis committed and also written to state in plaintext.- The secrets are now in the git history. Rotating them is not optional; the history must be scrubbed or the credentials rotated and the old ones revoked.
- Fix: credentials from environment, a secrets manager, or a
datasource (aws_secretsmanager_secret_version). Mark sensitive attributessensitive = true. Seedomains/security/secrets.md.
Unpinned Provider (IaC P5 Version Everything)
- The
awsprovider has noversion. The nextterraform initpulls whatever is latest — a different provider version can change resource behavior with no review. - Fix: pin
version = "~> 5.0". Commit the lock file (.terraform.lock.hcl). Seedomains/infrastructure-as-code/terraform.md.
Manual Drift, No Plan Review (IaC P4 Plan Before Apply, P9 Drift is Recoverable)
- The team applies from laptops with no
planreview. When the DB password is wrong, someone SSHes in and changes it manually — drift thatplanwill later report as a surprise. - Manual changes to managed resources are an incident, not a shortcut. Each one is a future
plandiff that no one can explain. - Fix: run
terraform planin CI; review the diff;applyfrom CI on merge. Treat every drift report as an incident to investigate. Seedomains/infrastructure-as-code/state.md(Drift and Reconciliation).
No Module Composition (IaC P6 Modules Compose)
- The S3 bucket and DB instance are inline. When the team needs a second bucket, they copy-paste the block and rename it. The two copies drift over time.
- Fix: a versioned module for each reusable pattern. The difference is a variable, not a copy. See
domains/infrastructure-as-code/modules.md(the module-vs-copy boundary).
The Cascade
The violations compound. Unlocked local state lets two apply runs race. Committed secrets mean the race loser's changes — and the secrets — are in the repo. Manual drift hides the corruption until a plan surfaces a diff no one can explain. The unpinned provider means that diff might be the provider's fault, not the team's, and no one can tell which.
Cross-Domain Links
domains/security/secrets.md— secret hygiene is non-tradeable; this example violates it in three places.domains/security/supply-chain.md— committed credentials in git are a supply-chain incident.domains/devops/P6 Configuration as Code— config in the repo is correct; committed state and secrets is the violation.