// Project Walkthrough · 03
Compliant GCS Bucket — NIST 800-171
A reusable Terraform module for GCP Cloud Storage that hard-codes six NIST 800-171 controls into every bucket it provisions, with plan-time validation that rejects non-compliant configuration before a single resource reaches GCP.
terraform
gcp
nist 800-171
cloud storage · kms · cmek · iam
Most organizations treat compliance as a checklist filled out after infrastructure is already built. Security settings get bolted on after the fact, documentation lags, and auditors end up reviewing a system that looks compliant on paper but was never engineered to be.
This module inverts that model. The controls are the infrastructure. Every Cloud Storage bucket provisioned by this module is compliant by construction — a consumer cannot misconfigure their way out of it, and the evidence is generated automatically at apply time.
// hard-coded controls
No Optional Security Settings
Encryption, public access prevention, uniform IAM, versioning, and retention are non-negotiable. Consumers configure bucket names and retention floors — they cannot toggle off a control.
// plan-time validation
Fail Before Any API Call
Terraform validation blocks catch misconfiguration at plan time. A prod bucket with less than 365 days retention fails with a human-readable error before anything reaches GCP.
// attestation output
Machine-Readable Evidence
Post-apply, a structured output lists every enforced control with its value and mapped control ID. An auditor reads the Terraform state directly — no console login required.
// separate kms keyring
Customer-Managed Keys (SC-12)
Each bucket gets its own KMS keyring. GCP holds no root key material. Key lifecycle is fully under the customer's control with 90-day automatic rotation enforced at the module level.
| Control ID | Control Name | How It's Enforced |
| SC-12 | Cryptographic Key Management | Separate KMS keyring per bucket, customer-managed, 90-day auto-rotation |
| SC-13 | Cryptographic Protection | CMEK encryption enforced at the API level — no bucket without it |
| SC-28 | Protection of Information at Rest | AES-256 via CMEK, public access prevention block always enabled |
| AC-3 | Access Enforcement | Uniform bucket-level IAM only — no legacy ACLs, no public access |
| CM-6 | Configuration Settings | Object versioning enabled, all settings non-parameterized |
| AU-11 | Audit Record Retention | Configurable floor with plan-time validation — prod requires 365+ days |
hard-coded resources
Controls Are the Infrastructure
Encryption, public access block, uniform IAM, and versioning are fixed resource attributes — not variables. A consumer calling the module cannot override them.
validation blocks
Plan-Time Rejection
Terraform validation blocks check the retention floor against the environment type before any API call. A prod deployment with less than 365 days retention fails at plan time with a clear error — not a runtime failure after resources are partially created.
negative test consumer
Proof the Gate Fires
consumers/negative-test/ contains a deliberately misconfigured consumer. The bucket is named should-never-exist because it never reaches GCP — the validation gate fires and Terraform exits before any resource is created.
compliance_attestation
Automatic Evidence at Apply Time
After a successful apply, the compliance_attestation output lists every enforced control as machine-readable structured data — control ID, value, and enforcement method.
tools/terraform/
modules/compliant-gcs-bucket/
main.tf
variables.tf
outputs.tf
primitives/compliant-gcs/
evidence/lab-2-4/
consumers/negative-test/