<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Janita Madramootoo - GCP</title>
    <subtitle>Janita Madramootoo is a software architect based in Melbourne, FL.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://janita.me/tags/gcp/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://janita.me"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-07-15T00:00:00+00:00</updated>
    <id>https://janita.me/tags/gcp/atom.xml</id>
    <entry xml:lang="en">
        <title>Building a Compliant GCP Storage Module with Terraform (CGE-P Lab 2.4)</title>
        <published>2026-07-15T00:00:00+00:00</published>
        <updated>2026-07-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Janita
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://janita.me/blog/terraform-compliant-gcp-storage-module/"/>
        <id>https://janita.me/blog/terraform-compliant-gcp-storage-module/</id>
        
        <content type="html" xml:base="https://janita.me/blog/terraform-compliant-gcp-storage-module/">&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;Continuing through the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cert.grcengclub.com&#x2F;&quot;&gt;Certified GRC Engineer - Practitioner (CGE-P)&lt;&#x2F;a&gt; course. Lab 2.3 built a single compliant S3 bucket on AWS. Lab 2.4 moves to GCP and shifts from writing one bucket to writing a module that deploys buckets, with compliance sitting inside the module where a consumer can&#x27;t reach it.&lt;&#x2F;p&gt;
&lt;p&gt;Code for this lab is in my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;JanitaM&#x2F;CGE-P_Capstone&quot;&gt;CGE-P Capstone repo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lab-2-4-terraform-modules-for-compliance-gcp&quot;&gt;Lab 2.4: Terraform Modules for Compliance (GCP)&lt;&#x2F;h2&gt;
&lt;p&gt;The module is called &lt;code&gt;compliant-gcs-bucket&lt;&#x2F;code&gt;. It is a reusable building block that creates a GCS bucket pre-wired with NIST SP 800-53 controls, plus its own Cloud KMS keyring and key for encryption. Two consumers, &lt;code&gt;dev&lt;&#x2F;code&gt; and &lt;code&gt;prod&lt;&#x2F;code&gt;, call the same module with different environment and retention values. Both get the same security posture either way.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CDD6F4); background-color: light-dark(#EFF1F5, #1E1E2E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;terraform&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── modules&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│   └── compliant-gcs-bucket&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       ├── main.tf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       ├── variables.tf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       ├── outputs.tf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       └── README.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└── primitives&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    └── compliant-gcs&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ├── main.tf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        └── variables.tf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The module has three files:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;main.tf&lt;&#x2F;code&gt; hardcodes anything compliance-relevant. A consumer cannot turn it off.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;variables.tf&lt;&#x2F;code&gt; exposes only what business actually changes: project, environment, retention, naming.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;outputs.tf&lt;&#x2F;code&gt; returns evidence, including a computed &lt;code&gt;compliance_attestation&lt;&#x2F;code&gt; map.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The shift from Lab 2.3 is that compliance stops being something you remember to add per-resource, and becomes something the module forces on every consumer by construction.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Code block&lt;&#x2F;th&gt;&lt;th&gt;NIST Control&lt;&#x2F;th&gt;&lt;th&gt;What it does&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;google_kms_key_ring&lt;&#x2F;code&gt; + &lt;code&gt;google_kms_crypto_key&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;SC-12&lt;&#x2F;td&gt;&lt;td&gt;The module owns its own encryption key instead of relying on Google-managed encryption&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;encryption { default_kms_key_name = ... }&lt;&#x2F;code&gt; on the bucket&lt;&#x2F;td&gt;&lt;td&gt;SC-13 &#x2F; SC-28&lt;&#x2F;td&gt;&lt;td&gt;Customer-managed encryption key (CMEK) applied at rest&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;versioning&lt;&#x2F;code&gt; + &lt;code&gt;retention_policy&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;AU-11&lt;&#x2F;td&gt;&lt;td&gt;Object versions preserved, retention period enforced per environment&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;uniform_bucket_level_access&lt;&#x2F;code&gt; + &lt;code&gt;public_access_prevention = &quot;enforced&quot;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;AC-3&lt;&#x2F;td&gt;&lt;td&gt;IAM-only access, no ACLs, no accidental public exposure&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;locals.required_labels&lt;&#x2F;code&gt; merged into &lt;code&gt;labels&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;CM-6&lt;&#x2F;td&gt;&lt;td&gt;Compliance labels forced onto every bucket; a consumer can add labels but not remove the required four&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;gotchas-i-hit&quot;&gt;Gotchas I Hit&lt;&#x2F;h2&gt;
&lt;p&gt;Unlike Lab 2.3, the issue I ran into wasn&#x27;t a compliance gap in the design, it was drift in the documentation. If a compliance module&#x27;s own supporting docs can drift from its code, what does that imply about trusting a vendor module&#x27;s documentation without reading the &lt;code&gt;.tf&lt;&#x2F;code&gt; files directly? A vendor module&#x27;s README makes compliance claims too (&quot;this enforces encryption at rest&quot;). The lesson here is: don&#x27;t treat a README as proof of what a module does. Read the resource blocks yourself before you rely on the claim.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
