Catching Up: Labs 2.5 - 4.3, and Passing the CGE-AUD Auditor Exam
Summary
My last post left off at Lab 2.4. Since then, I've completed: Labs 2.5, 3.3, 3.4, and 4.3, plus the GRC Auditor exam. This is a quick summary with the main takeaway per lab.
Code for these labs is in my CGE-P Capstone repo.
The GRC Auditor exam was also launched in the meantime, recommended as a prerequisite to the CGE-P exam. My engineering background helped me knock it out in two days. Grateful for quick wins!
Lab 2.5: IaC as Compliance Evidence (AWS)
This lab turns a Terraform plan into signed, retained evidence rather than just a deployment artifact. The module, evidence-vault, is an S3 bucket with Object Lock in GOVERNANCE mode. A capture script (scripts/capture-evidence.sh) runs against a target workspace (in my case, Lab 2.3's compliant-s3) and writes a receipt into the vault.
| Step | What it proves |
|---|---|
Capture evidence against compliant-s3 | The plan output itself becomes the evidence artifact |
Verify RetainUntilDate under GOVERNANCE mode | Retention is enforced, not just configured |
Attempt delete-object on the locked object | Confirms the lock actually blocks deletion with AccessDenied as expected |
Labs 3.3 & 3.4: Writing Compliance Policies in Rego, GCP to AWS
Lab 3.3 writes the policy library. Lab 3.4 proves it doesn't have to be tied to one cloud provider.
Lab 3.3: Writing Compliance Policies in Rego (GCP)
Three Rego policies, three NIST controls, checked against a GCP Terraform plan:
| Control | File | Enforces |
|---|---|---|
| SC-28 | sc28_encryption.rego | Every google_storage_bucket has a populated encryption { default_kms_key_name } block |
| AC-3 | ac3_no_public.rego | Buckets have uniform_bucket_level_access = true and public_access_prevention = "enforced"; firewalls don't expose 22 or 3389 to 0.0.0.0/0 |
| CM-6 | cm6_required_tags.rego | Every taggable resource carries the four required labels: project, environment, managed_by, compliance_scope |
Lab 3.4: Integrating PaC with Terraform via Conftest (AWS)
This was my first exposure to Conftest, Rego, and Open Policy Agent.
This lab does two things: runs the Lab 3.3 GCP policies against an AWS plan (they pass with zero coverage since there were no GCP resource types to check), and adds AWS-typed variants of the same three controls into the library. The point is that a control ID is portable across cloud providers while a Rego rule hardcoded to google_storage_bucket is not.
| Control | GCP file (3.3) | AWS file (3.4) |
|---|---|---|
| SC-28 | sc28_encryption.rego | sc28_encryption_aws.rego |
| AC-3 | ac3_no_public.rego | ac3_no_public_aws.rego |
| CM-6 | cm6_required_tags.rego | cm6_required_tags_aws.rego |
Lab 4.3: Building a GRC Evidence Pipeline (AWS + GitHub Actions)
Lab 4.3 wires the Lab 3.4 Conftest gate into GitHub Actions. Every PR now runs terraform plan, Conftest, and tfsec, and uploads a named evidence artifact regardless of pass or fail. The workflow file itself is the CM-3/CM-6/CA-2/RA-5/AU-9 audit evidence.
Setting up IAM Identity Center
This was my first time setting up IAM Identity Center (SSO) on my personal AWS account. Up to this point, I've been working on the lab in CloudShell to avoid saving keys on my laptop. This has caused some headaches because AWS CloudShell only allows for 1 GB of persistent storage per AWS Region. I kept getting "No space left on device" errors on the AWS labs because every terraform init re-downloads its own full copy. Claude helped me find the Terraform plugin-cache and delete old files to make room for each new AWS lab, but it felt like I was just putting off the inevitable of setting up IAM Identity Center.
With OIDC, my sessions are short-lived and expire, which means occasionally re-authenticating, but nothing durable to leak and no forgetting about keys on my computer.
The guide's reference oidc/main.tf trusts the role to repo:OWNER/REPO:*, so basically any branch, any event. In my repo, I tightened up both the subject claim and the permissions granted.
Since my repo has no remote backend configured, my state stays local. There's no diff for testing drift against real infrastructure. The plan always shows a full "create," not a diff against what's actually live.
With identity and OIDC sorted, here's the gate itself in action:
The two-PR demonstration
Green: PR #6, merged. All checks passing.
Red: branch
lab-4-3-red, opened, not merged. Conftest failed with:[SC-28] aws_s3_bucket.primary: aws_s3_bucket has no matching aws_s3_bucket_server_side_encryption_configuration. Remediation: add one referencing this bucket.
That message contains the control ID, resource, and remediation all in one line. The whole point of the gate is that this failure surfaces on the PR, before merge, instead of turning up as a finding in a later audit.
Takeaway
Every gotcha I came across in these labs traced back to two things: my AWS CloudShell setup and that the guides are written for a flat, single-purpose repo. My repo has real structure, which means every path, working directory, and relative reference in the lab guides needed debugging (e.g. TF_WORKING_DIR pointed three levels deep so every ../ in the guide's Conftest/tfsec steps needed to become ../../../).