// 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
Problem Statement
01
The standard pattern is to build first and check later. Security settings get added after the fact, documentation lags, and by the time an auditor shows up the team is scrambling to prove the infrastructure was ever in the right state.
This module builds compliance into the resource definition. Every Cloud Storage bucket it provisions is compliant from the first apply. A consumer cannot misconfigure their way out of it, and the evidence is generated automatically at apply time.
Architecture Decision
02
// hard-coded controls
No Optional Security Settings
Encryption, public access prevention, uniform IAM, versioning, and retention are fixed. Consumers pass in the bucket name and retention floor. That is the extent of their influence over the security configuration.
// plan-time validation
Fail Before Any API Call
Terraform validation blocks reject misconfiguration before any API call goes out. A prod bucket with less than 365 days retention fails at plan time with a readable error and exits. No partial creates, no cloud spend.
// 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, without a console login.
// 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.
Controls Enforced
03
| 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 |
How It Works
04
hard-coded resources
Controls Are the Infrastructure
Encryption, public access block, uniform IAM, and versioning are fixed resource attributes. 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.
Module Structure
05
tools/terraform/
modules/compliant-gcs-bucket/ # the reusable module
main.tf # bucket + KMS resources
variables.tf # inputs with validation blocks
outputs.tf # compliance_attestation output
primitives/compliant-gcs/ # dev consumer example
evidence/lab-2-4/ # generated attestation artifacts
consumers/negative-test/ # proves validation gate fires
Summary
06
6
controls enforced
0
optional toggles
1
negative test gate
0
screenshots needed