100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Cloud Security — AWS, Azure & GCP
65 minintermediate

Implement Guardrails with Terraform and Policy-as-Code

This exercise turns Lesson 32's design document into working infrastructure as code. You will express the account guardrails, IAM boundaries, and network baseline as Terraform, and encode the non-negotiable rules as policy-as-code so they are enforced automatically rather than relying on every squad remembering them. This is the same shift from design to durable, self-enforcing control you practised with SCPs, organisation policies, and Kubernetes admission control throughout the course.

Analogy🏏Cricket
✈️ Think of it like travel: A modern airline does not inspect an aircraft once and trust it forever; a maintenance system continuously tracks every component, flags any part drifting out of tolerance, and surfaces the issue before the plane flies. Just as that system inventories every part and checks each against a standard so nothing is missed, CSPM inventories every cloud resource and checks each against best practice. Just as catching a worn part on the ground is far cheaper than discovering it aloft, catching a misconfiguration before an attacker does is far cheaper than after. This reveals why continuous automated checking is the natural defence against a constant risk.

The Exercise

Step 1 — Encode the Guardrails as Organisation Policy

Start with the non-negotiable rules from the design: no public storage anywhere in the organisation, mandatory logging that cannot be disabled, and a restriction to PayNest's approved region for data residency. Express these as an SCP or organisation policy applied at the root, so they bind every current and future account automatically, exactly mirroring the AWS SCP and GCP organisation policy patterns from Modules 2 and 3.

Analogy🏏Cricket
⚽ Think of it like sports: Encoding the non-negotiable rules as a root-level org policy is like the laws of football being enforced by the referee for every player on the pitch, not left to each footballer's goodwill — no public storage, no disabling logs, no out-of-region resources apply to every account automatically, current and future. Just as no player can opt out of the offside law mid-match, no squad can opt out of a guardrail applied at the organisation root. This reveals the power of policy-as-code at the top: the rule binds everyone by default rather than depending on each team to remember it.
json
# Step 1 -- root-level guardrails (bind to EVERY account automatically).

# SCP: deny public S3 access org-wide (no account can override this)
{
  "Effect": "Deny",
  "Action": ["s3:PutBucketPublicAccessBlock", "s3:PutBucketPolicy"],
  "Resource": "*",
  "Condition": {"Bool": {"s3:PublicAccessBlockConfiguration": "false"}}
}

# SCP: deny disabling CloudTrail anywhere in the org
{
  "Effect": "Deny",
  "Action": ["cloudtrail:StopLogging", "cloudtrail:DeleteTrail"],
  "Resource": "*"
}

# SCP: restrict all resource creation to the approved region
{
  "Effect": "Deny",
  "NotAction": ["iam:*", "organizations:*", "route53:*"],
  "Resource": "*",
  "Condition": {"StringNotEquals": {"aws:RequestedRegion": "ap-south-1"}}
}

Step 2 — Implement Per-Squad IAM in Terraform

Translate the IAM table from Lesson 32 into Terraform, giving each squad's engineers a role scoped to their own account only, and each application a dedicated service role scoped to its exact task, following the least-privilege pattern from Module 2. Write this as a reusable module so onboarding a fourth squad later means calling the module with new parameters, not writing new IAM logic from scratch — directly serving the squad-independence goal without creating per-squad maintenance burden for the single security engineer.

Analogy🏏Cricket
🍳 Think of it like cooking: A reusable IAM module is like a tested master recipe a kitchen scales to any number of covers — onboarding a fourth squad means following the same proven recipe with new quantities, not inventing a dish from scratch under pressure. Just as a standard recipe lets one head chef keep quality consistent across a busy service, the module lets one part-time engineer keep IAM consistent across many squads. This reveals why reusability is not merely tidy code but a direct answer to the brief's staffing limit: it stretches one engineer's scarce time across every team without new risk each time.
hcl
# Step 2 -- a reusable Terraform module for per-squad IAM.

module "squad_iam" {
  source      = "./modules/squad-role"
  squad_name  = "payments"
  account_id  = var.payments_dev_account_id
  permissions = ["s3:GetObject", "s3:PutObject", "dynamodb:*"]
  # scoped to THIS squad's resources only -- module enforces the pattern
}

module "squad_iam_merchant" {
  source      = "./modules/squad-role"
  squad_name  = "merchant"
  account_id  = var.merchant_dev_account_id
  permissions = ["s3:GetObject", "sqs:SendMessage"]
}

# Adding a 4th squad later = one more module call, not new IAM logic.
# This is what makes the design maintainable by one part-time engineer.
Analogy🏏Cricket
📷 Think of it like photography: A reusable IAM module is like a saved camera preset a studio applies to every new shoot — the exposure, white balance, and colour profile are dialled in once and reused, so each fresh session starts correct instead of being reconfigured by hand. Just as the preset scales a photographer's careful setup across countless shoots, the module scales the security engineer's careful IAM design across countless squads. This reveals why reusability directly answers the brief's staffing constraint: proven configuration applied repeatably is how one person supports many teams safely.

Step 3 — Gate the Pipeline with Checkov

Wire Checkov into the CI pipeline that deploys this Terraform, exactly as practised in Module 5's exercise, so any future change to these guardrails or squad modules is scanned before it can deploy. This closes the loop between design, implementation, and durability: even a future engineer unfamiliar with this capstone's reasoning cannot accidentally merge a misconfiguration past this gate, because the pipeline itself enforces the standard.

Analogy🏏Cricket
🎮 Think of it like gaming: Wiring Checkov into the pipeline is like a game's anti-cheat scanning every match before it counts — no matter who queues up or how careless they are, a banned move is caught and blocked before it can affect play. Any future change to the guardrails is scanned before it can deploy, so even an engineer who never read this capstone cannot merge a misconfiguration past the gate. Just as anti-cheat protects the game without trusting each player's honesty, the CI gate protects the infrastructure without trusting each engineer's memory. This reveals the point of automated gating: the standard enforces itself.
yaml
# Step 3 -- CI gate on the capstone's own infrastructure repo.

stages:
  - name: iac-security-scan
    run: checkov -d ./infrastructure --compact --soft-fail=false
  - name: terraform-plan
    run: terraform plan
  - name: terraform-apply
    run: terraform apply
    when: manual_approval_after_plan

# Any future PR that reintroduces a public bucket, an open port, or a
# wildcard policy is blocked here -- automatically, permanently.
Analogy🏏Cricket
🎵 Think of it like music: This CI gate is like the mandatory soundcheck before every single show — run automatically regardless of which crew is on that night or how experienced they are, catching a dead mic or a wrong level before the audience ever hears it. Just as the soundcheck protects the performance even as touring crews change city to city, the pipeline gate protects infrastructure quality even as engineers join and leave PayNest. This reveals the real value of encoding standards as automated gates: the standard survives staff turnover, which a purely written policy never reliably does.

Pro Tip

Comment every guardrail and module in your Terraform with the specific business goal or SOC 2 requirement it satisfies, exactly as your Lesson 32 design document did in prose. A future auditor, or a course grader, should be able to read the code itself and understand not just what it does but why it exists. This turns your infrastructure into its own documentation, reducing the maintenance burden the brief is explicitly worried about.

Warning: Do not implement every guardrail in strict enforce mode on the first deployment. As practised in the Module 4 Kubernetes exercise, roll out new SCPs and policies in a monitoring or audit mode first, observe what they would have blocked against PayNest's actual existing usage, adjust for any legitimate traffic you missed, and only then switch to enforcing. Deploying strict guardrails blind against a real, already-running environment risks breaking production on day one of your capstone's own rollout.

Deliverable for This Lesson

Produce the Terraform implementing the root-level guardrails, the reusable per-squad IAM module, and the CI pipeline configuration gating future changes with Checkov — the working code realisation of Lesson 32's design. This becomes the second artefact in your final portfolio, and it should be ready as the foundation for the monitoring and alerting layer you will add in the next lesson.

Analogy🏏Cricket
🎬 Think of it like movies: This design document is like the shooting script and storyboards locked before a film goes into production — the account diagram, the IAM table, and the logging plan are the scenes every later department will build from, annotated so the reason behind each shot is unmistakable. Just as a crew shoots from the locked script rather than reinventing scenes on set, Lesson 33's Terraform builds from these decisions rather than reopening them. This reveals why precision here matters: a clear, unambiguous design lets the implementation phase execute instead of deliberate.
  • Implementation turns a design document into durable, self-enforcing infrastructure, closing the gap between an intention on paper and a rule that actually holds.
  • Non-negotiable rules — no public storage, mandatory logging, region restriction — belong as root-level SCPs or organisation policies binding every account automatically.
  • Writing per-squad IAM as a reusable Terraform module lets onboarding a new squad mean calling the module, not writing new logic — directly serving maintainability.
  • Gating the pipeline with Checkov ensures future changes are scanned automatically, so the standard survives staff turnover rather than depending on memory.
  • Commenting code with the business goal or requirement it satisfies turns infrastructure into its own documentation, reducing the ongoing maintenance burden.
  • Roll new guardrails out in audit or monitoring mode before enforcing, since deploying strict policy blind against a real running environment can break production immediately.
Lesson 33 of 35
0% complete