Skip to content
CSY302 Week 09 Advanced

Week Content

Cloud & Infrastructure Security

Track your progress through this week's content

Opening Framing

Infrastructure as Code (IaC) transforms cloud infrastructure from manually configured resources into version-controlled, reviewable, and repeatable definitions. This shift fundamentally changes security: instead of auditing running infrastructure, you can analyze the code that creates it. Misconfigurations can be caught before deployment, security policies can be enforced through automated checks, and consistent security baselines can be guaranteed across all environments.

But IaC also introduces new risks. A single misconfigured template can create hundreds of insecure resources. Secrets accidentally committed to repositories are exposed in version history forever. And the speed of IaC deployment means insecure infrastructure can be created faster than ever. Security must be built into the IaC pipeline, not bolted on afterward.

This week covers IaC security fundamentals, static analysis tools, policy as code with OPA and Sentinel, secrets management in IaC, and secure CI/CD pipelines for infrastructure deployment. You'll learn to shift security left and catch issues before they reach production.

Key insight: IaC enables "security as code"—automated, consistent, and reviewable security controls.

1) Infrastructure as Code Fundamentals

Understanding IaC tools and their security implications is essential for securing cloud infrastructure:

IaC Tools Landscape:

DECLARATIVE vs IMPERATIVE:
┌─────────────────────────────────────────────────────────────┐
│ DECLARATIVE (What you want):                                │
│ - Terraform, CloudFormation, Pulumi                         │
│ - Define desired end state                                  │
│ - Tool figures out how to get there                         │
│ - Easier to audit and review                                │
│                                                             │
│ IMPERATIVE (How to do it):                                  │
│ - AWS CDK, Ansible, Scripts                                 │
│ - Define steps to execute                                   │
│ - More flexibility, more complexity                         │
│ - Harder to predict final state                             │
└─────────────────────────────────────────────────────────────┘

MAJOR IaC TOOLS:
┌─────────────────────────────────────────────────────────────┐
│ Terraform:                                                  │
│ - Multi-cloud support                                       │
│ - HCL (HashiCorp Configuration Language)                    │
│ - State file tracks resources                               │
│ - Large provider ecosystem                                  │
│                                                             │
│ AWS CloudFormation:                                         │
│ - AWS-native                                                │
│ - YAML/JSON templates                                       │
│ - Integrated with AWS services                              │
│ - Stack-based resource management                           │
│                                                             │
│ AWS CDK:                                                    │
│ - Infrastructure in programming languages                   │
│ - TypeScript, Python, Java, etc.                            │
│ - Synthesizes to CloudFormation                             │
│ - Higher-level constructs                                   │
│                                                             │
│ Pulumi:                                                     │
│ - Multi-cloud                                               │
│ - Real programming languages                                │
│ - State management options                                  │
└─────────────────────────────────────────────────────────────┘

IaC Security Benefits and Risks:

IaC Security Implications:

SECURITY BENEFITS:
┌─────────────────────────────────────────────────────────────┐
│ ✓ Version Control                                           │
│   - Track all infrastructure changes                        │
│   - Audit trail of who changed what                         │
│   - Rollback capability                                     │
│                                                             │
│ ✓ Code Review                                               │
│   - Security review before deployment                       │
│   - Multiple eyes on changes                                │
│   - Catch misconfigurations early                           │
│                                                             │
│ ✓ Consistency                                               │
│   - Same code = same infrastructure                         │
│   - Eliminate configuration drift                           │
│   - Reproducible environments                               │
│                                                             │
│ ✓ Automated Testing                                         │
│   - Static analysis in CI/CD                                │
│   - Policy enforcement                                      │
│   - Compliance checking                                     │
│                                                             │
│ ✓ Documentation                                             │
│   - Code IS documentation                                   │
│   - Always current                                          │
│   - Searchable and reviewable                               │
└─────────────────────────────────────────────────────────────┘

SECURITY RISKS:
┌─────────────────────────────────────────────────────────────┐
│ ✗ Secrets in Code                                           │
│   - Credentials committed to repositories                   │
│   - Visible in version history forever                      │
│   - Exposed in state files                                  │
│                                                             │
│ ✗ Overprivileged Deployment                                 │
│   - CI/CD needs broad permissions                           │
│   - Compromised pipeline = infrastructure compromise        │
│                                                             │
│ ✗ State File Exposure                                       │
│   - Terraform state contains sensitive data                 │
│   - Must be secured and encrypted                           │
│                                                             │
│ ✗ Rapid Misconfiguration Deployment                         │
│   - Bad template = many bad resources fast                  │
│   - Automation amplifies mistakes                           │
│                                                             │
│ ✗ Third-Party Module Risks                                  │
│   - Community modules may be insecure                       │
│   - Supply chain attacks possible                           │
└─────────────────────────────────────────────────────────────┘

COMMON IaC MISCONFIGURATIONS:
┌─────────────────────────────────────────────────────────────┐
│ - S3 buckets with public access                             │
│ - Security groups with 0.0.0.0/0                            │
│ - Unencrypted storage (EBS, RDS, S3)                        │
│ - Overprivileged IAM roles                                  │
│ - Disabled logging                                          │
│ - Missing tags                                              │
│ - Default VPC usage                                         │
│ - Hardcoded secrets                                         │
│ - Missing resource limits                                   │
│ - Exposed management ports                                  │
└─────────────────────────────────────────────────────────────┘

Key insight: IaC transforms security from reactive (fixing running infrastructure) to proactive (preventing bad deployments).

2) IaC Static Analysis Tools

Static analysis tools scan IaC templates for security issues before deployment:

IaC Scanning Tools:

CHECKOV (Open Source):
┌─────────────────────────────────────────────────────────────┐
│ Supports: Terraform, CloudFormation, Kubernetes, ARM, CDK   │
│                                                             │
│ Installation:                                               │
│ pip install checkov                                         │
│                                                             │
│ Usage:                                                      │
│ checkov -d .                        # Scan directory        │
│ checkov -f main.tf                  # Scan file             │
│ checkov --framework terraform       # Specific framework    │
│ checkov --check CKV_AWS_1          # Run specific check     │
│ checkov --skip-check CKV_AWS_2     # Skip check             │
│                                                             │
│ Output:                                                     │
│ Passed checks: 45                                           │
│ Failed checks: 3                                            │
│ Skipped checks: 2                                           │
│                                                             │
│ Check: CKV_AWS_19: "Ensure S3 bucket has encryption"        │
│   FAILED for resource: aws_s3_bucket.data                   │
│   File: /main.tf:15-20                                      │
└─────────────────────────────────────────────────────────────┘

TFSEC (Terraform Security):
┌─────────────────────────────────────────────────────────────┐
│ Installation:                                               │
│ brew install tfsec                                          │
│                                                             │
│ Usage:                                                       │
│ tfsec .                             # Scan directory        │
│ tfsec --format json                 # JSON output           │
│ tfsec --minimum-severity HIGH       # Filter by severity    │
│                                                             │
│ Output:                                                     │
│ Result #1 HIGH S3 bucket has encryption disabled            │
│ ─────────────────────────────────────────────────────────   │
│   main.tf:15-20                                             │
│   15 │ resource "aws_s3_bucket" "data" {                    │
│   16 │   bucket = "my-data-bucket"                          │
│   17 │ }                                                    │
│                                                             │
│ Impact: The bucket objects could be read if accessed        │
│ Resolution: Enable server-side encryption                   │
└─────────────────────────────────────────────────────────────┘

TRIVY (Multi-Purpose):
┌─────────────────────────────────────────────────────────────┐
│ Supports: Terraform, CloudFormation, Kubernetes, Dockerfile │
│                                                             │
│ Usage:                                                      │
│ trivy config .                      # Scan IaC              │
│ trivy config --severity HIGH,CRITICAL .                     │
│                                                             │
│ Also scans:                                                 │
│ - Container images                                          │
│ - Filesystems                                               │
│ - Git repositories                                          │
└─────────────────────────────────────────────────────────────┘

AWS CLOUDFORMATION GUARD:
┌─────────────────────────────────────────────────────────────┐
│ Policy-as-code for CloudFormation                           │
│                                                             │
│ Installation:                                               │
│ cargo install cfn-guard                                     │
│                                                             │
│ Usage:                                                      │
│ cfn-guard validate -d template.yaml -r rules.guard          │
│                                                             │
│ Example Rule (rules.guard):                                 │
│ let s3_buckets = Resources.*[Type == 'AWS::S3::Bucket']     │
│                                                             │
│ rule s3_bucket_encryption when %s3_buckets !empty {         │
│   %s3_buckets.Properties.BucketEncryption exists            │
│   %s3_buckets.Properties.BucketEncryption.                  │
│     ServerSideEncryptionConfiguration[*].                   │
│     ServerSideEncryptionByDefault.SSEAlgorithm in           │
│     ['AES256', 'aws:kms']                                   │
│ }                                                           │
└─────────────────────────────────────────────────────────────┘

CI/CD Integration:

CI/CD Pipeline Integration:

GITHUB ACTIONS EXAMPLE:
name: IaC Security Scan
on:
  pull_request:
    paths:
      - 'terraform/**'
      - 'cloudformation/**'

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Run Checkov
        uses: bridgecrewio/checkov-action@master
        with:
          directory: terraform/
          framework: terraform
          soft_fail: false
          output_format: sarif
          output_file_path: checkov-results.sarif
      
      - name: Upload SARIF results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: checkov-results.sarif
      
      - name: Run tfsec
        uses: aquasecurity/tfsec-action@v1.0.0
        with:
          soft_fail: false
          
      - name: Run Trivy
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'config'
          scan-ref: 'terraform/'
          severity: 'HIGH,CRITICAL'
          exit-code: '1'

GITLAB CI EXAMPLE:
stages:
  - validate
  - security
  - plan
  - apply

security-scan:
  stage: security
  image: bridgecrew/checkov:latest
  script:
    - checkov -d terraform/ --output junitxml > checkov-results.xml
  artifacts:
    reports:
      junit: checkov-results.xml
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

terraform-plan:
  stage: plan
  needs: [security-scan]
  script:
    - terraform init
    - terraform plan -out=tfplan
  artifacts:
    paths:
      - tfplan

FAIL ON CRITICAL FINDINGS:
┌─────────────────────────────────────────────────────────────┐
│ Policy: Block PR merge if critical findings                 │
│                                                             │
│ checkov --hard-fail-on HIGH,CRITICAL                        │
│ tfsec --minimum-severity HIGH --soft-fail=false             │
│ trivy config --exit-code 1 --severity HIGH,CRITICAL         │
│                                                             │
│ This prevents insecure code from reaching main branch       │
└─────────────────────────────────────────────────────────────┘

Key insight: Scan on every PR. Finding a misconfiguration in code review is infinitely better than finding it in production.

3) Policy as Code

Policy as Code enables defining and enforcing security policies programmatically:

Policy as Code Concepts:

WHY POLICY AS CODE:
┌─────────────────────────────────────────────────────────────┐
│ Traditional Policy:                                         │
│ - Written in documents                                      │
│ - Manually enforced                                         │
│ - Inconsistently applied                                    │
│ - Hard to verify compliance                                 │
│                                                             │
│ Policy as Code:                                             │
│ - Written in code                                           │
│ - Automatically enforced                                    │
│ - Consistently applied                                      │
│ - Compliance verified automatically                         │
│                                                             │
│ Example Policy:                                             │
│ "All S3 buckets must be encrypted"                          │
│                                                             │
│ Document: Security team reviews each bucket manually        │
│ Code: Every deployment automatically checked and blocked    │
└─────────────────────────────────────────────────────────────┘

OPEN POLICY AGENT (OPA):
┌─────────────────────────────────────────────────────────────┐
│ General-purpose policy engine                               │
│ Uses Rego policy language                                   │
│ Works with Terraform, Kubernetes, APIs                      │
│                                                             │
│ Installation:                                               │
│ brew install opa                                            │
│                                                             │
│ Rego Policy Example:                                        │
│ package terraform.s3                                        │
│                                                             │
│ # Deny S3 buckets without encryption                        │
│ deny[msg] {                                                 │
│   resource := input.resource_changes[_]                     │
│   resource.type == "aws_s3_bucket"                          │
│   not bucket_encrypted(resource)                            │
│   msg := sprintf(                                           │
│     "S3 bucket %s must have encryption enabled",            │
│     [resource.name]                                         │
│   )                                                         │
│ }                                                           │
│                                                             │
│ bucket_encrypted(resource) {                                │
│   resource.change.after.server_side_encryption_configuration│
│ }                                                           │
│                                                             │
│ # Deny public S3 buckets                                    │
│ deny[msg] {                                                 │
│   resource := input.resource_changes[_]                     │
│   resource.type == "aws_s3_bucket_public_access_block"      │
│   resource.change.after.block_public_acls == false          │
│   msg := sprintf(                                           │
│     "S3 bucket %s must block public ACLs",                  │
│     [resource.name]                                         │
│   )                                                         │
│ }                                                           │
└─────────────────────────────────────────────────────────────┘

USING OPA WITH TERRAFORM:
┌─────────────────────────────────────────────────────────────┐
│ # Generate Terraform plan as JSON                           │
│ terraform plan -out=tfplan                                  │
│ terraform show -json tfplan > tfplan.json                   │
│                                                             │
│ # Evaluate against policies                                 │
│ opa eval --input tfplan.json \                              │
│   --data policies/ \                                        │
│   "data.terraform.deny"                                     │
│                                                             │
│ # Conftest (OPA wrapper for config files)                   │
│ conftest test tfplan.json --policy policies/                │
│                                                             │
│ FAIL - tfplan.json - S3 bucket data must have encryption    │
│ FAIL - tfplan.json - Security group allows 0.0.0.0/0        │
│                                                             │
│ 2 tests, 0 passed, 2 warnings, 2 failures                   │
└─────────────────────────────────────────────────────────────┘

HashiCorp Sentinel:

HashiCorp Sentinel (Terraform Cloud/Enterprise):

SENTINEL POLICY EXAMPLE:
┌─────────────────────────────────────────────────────────────┐
│ import "tfplan/v2" as tfplan                                │
│                                                             │
│ # Get all S3 buckets                                        │
│ s3_buckets = filter tfplan.resource_changes as _, rc {      │
│   rc.type is "aws_s3_bucket" and                            │
│   rc.mode is "managed" and                                  │
│   (rc.change.actions contains "create" or                   │
│    rc.change.actions contains "update")                     │
│ }                                                           │
│                                                             │
│ # Check encryption                                          │
│ bucket_encryption = rule {                                  │
│   all s3_buckets as _, bucket {                             │
│     bucket.change.after.server_side_encryption_configuration│
│       is not null                                           │
│   }                                                         │
│ }                                                           │
│                                                             │
│ # Check versioning                                          │
│ bucket_versioning = rule {                                  │
│   all s3_buckets as _, bucket {                             │
│     bucket.change.after.versioning[0].enabled is true       │
│   }                                                         │
│ }                                                           │
│                                                             │
│ # Main rule                                                 │
│ main = rule {                                               │
│   bucket_encryption and bucket_versioning                   │
│ }                                                           │
└─────────────────────────────────────────────────────────────┘

POLICY ENFORCEMENT LEVELS:
┌─────────────────────────────────────────────────────────────┐
│ Advisory:                                                   │
│ - Logged but not enforced                                   │
│ - For informational policies                                │
│                                                             │
│ Soft Mandatory:                                             │
│ - Can be overridden by authorized users                     │
│ - For policies with exceptions                              │
│                                                             │
│ Hard Mandatory:                                             │
│ - Cannot be overridden                                      │
│ - For critical security policies                            │
│ - Blocks non-compliant deployments                          │
└─────────────────────────────────────────────────────────────┘

AWS SERVICE CONTROL POLICIES (SCPs):
┌─────────────────────────────────────────────────────────────┐
│ Organization-level guardrails                               │
│ Enforced regardless of IaC tool                             │
│                                                             │
│ Example: Deny unencrypted S3                                │
│ {                                                           │
│   "Version": "2012-10-17",                                  │
│   "Statement": [{                                           │
│     "Effect": "Deny",                                       │
│     "Action": "s3:PutObject",                               │
│     "Resource": "*",                                        │
│     "Condition": {                                          │
│       "StringNotEquals": {                                  │
│         "s3:x-amz-server-side-encryption": "AES256"         │
│       }                                                     │
│     }                                                       │
│   }]                                                        │
│ }                                                           │
│                                                             │
│ Note: SCPs are a backstop, not a replacement for IaC checks │
└─────────────────────────────────────────────────────────────┘

Key insight: Policies as code enable consistent enforcement across all deployments without human intervention.

4) Secrets Management in IaC

Secrets in IaC require special handling to prevent exposure:

Secrets in IaC Risks:

COMMON MISTAKES:
┌─────────────────────────────────────────────────────────────┐
│ BAD: Hardcoded secrets in Terraform                         │
│                                                             │
│ resource "aws_db_instance" "default" {                      │
│   engine               = "mysql"                            │
│   username             = "admin"                            │
│   password             = "SuperSecret123!"  # EXPOSED!      │
│ }                                                           │
│                                                             │
│ Problems:                                                   │
│ - Visible in version control                                │
│ - Visible in Terraform state                                │
│ - Visible in CI/CD logs                                     │
│ - Cannot be rotated without code change                     │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ BAD: Secrets in environment variables                       │
│                                                             │
│ export TF_VAR_db_password="SuperSecret123!"                 │
│ terraform apply                                             │
│                                                             │
│ Problems:                                                   │
│ - Still in shell history                                    │
│ - May be logged in CI/CD                                    │
│ - Still ends up in state file                               │
└─────────────────────────────────────────────────────────────┘

SECRETS IN STATE FILES:
┌─────────────────────────────────────────────────────────────┐
│ Terraform state contains ALL resource attributes            │
│ Including sensitive values in plaintext!                    │
│                                                             │
│ "resources": [{                                             │
│   "type": "aws_db_instance",                                │
│   "instances": [{                                           │
│     "attributes": {                                         │
│       "password": "SuperSecret123!"  # In state!            │
│     }                                                       │
│   }]                                                        │
│ }]                                                          │
│                                                             │
│ Must protect state file:                                    │
│ - Encrypt at rest                                           │
│ - Restrict access                                           │
│ - Use remote backend (not local file)                       │
└─────────────────────────────────────────────────────────────┘

Secure Secrets Handling:

Secure Patterns:

PATTERN 1: Reference Secrets Manager
┌─────────────────────────────────────────────────────────────┐
│ # Secret created outside Terraform (or in separate state)   │
│                                                             │
│ data "aws_secretsmanager_secret_version" "db_password" {    │
│   secret_id = "prod/database/password"                      │
│ }                                                           │
│                                                             │
│ resource "aws_db_instance" "default" {                      │
│   engine   = "mysql"                                        │
│   username = "admin"                                        │
│   password = data.aws_secretsmanager_secret_version.        │
│              db_password.secret_string                      │
│ }                                                           │
│                                                             │
│ Benefits:                                                   │
│ - Secret not in code                                        │
│ - Secret still in state (limitation)                        │
│ - Centralized secret management                             │
│ - Rotation possible                                         │
└─────────────────────────────────────────────────────────────┘

PATTERN 2: Generate Random Secrets
┌─────────────────────────────────────────────────────────────┐
│ resource "random_password" "db_password" {                  │
│   length  = 32                                              │
│   special = true                                            │
│ }                                                           │
│                                                             │
│ resource "aws_secretsmanager_secret" "db_password" {        │
│   name = "prod/database/password"                           │
│ }                                                           │
│                                                             │
│ resource "aws_secretsmanager_secret_version" "db_password" {│
│   secret_id     = aws_secretsmanager_secret.db_password.id  │
│   secret_string = random_password.db_password.result        │
│ }                                                           │
│                                                             │
│ resource "aws_db_instance" "default" {                      │
│   password = random_password.db_password.result             │
│ }                                                           │
│                                                             │
│ Benefits:                                                   │
│ - No secret in code                                         │
│ - Unique per environment                                    │
│ - Stored in Secrets Manager for apps                        │
│ - Still in state (limitation)                               │
└─────────────────────────────────────────────────────────────┘

PATTERN 3: Use SOPS for Encrypted Files
┌─────────────────────────────────────────────────────────────┐
│ # secrets.yaml (encrypted with SOPS)                        │
│ db_password: ENC[AES256_GCM,data:...,type:str]              │
│                                                             │
│ # In Terraform                                              │
│ data "sops_file" "secrets" {                                │
│   source_file = "secrets.yaml"                              │
│ }                                                           │
│                                                             │
│ resource "aws_db_instance" "default" {                      │
│   password = data.sops_file.secrets.data["db_password"]     │
│ }                                                           │
│                                                             │
│ Benefits:                                                   │
│ - Encrypted in repository                                   │
│ - Key managed separately (KMS)                              │
│ - Can be version controlled                                 │
└─────────────────────────────────────────────────────────────┘

TERRAFORM STATE PROTECTION:
┌─────────────────────────────────────────────────────────────┐
│ # S3 backend with encryption                                │
│ terraform {                                                 │
│   backend "s3" {                                            │
│     bucket         = "my-terraform-state"                   │
│     key            = "prod/terraform.tfstate"               │
│     region         = "us-east-1"                            │
│     encrypt        = true                                   │
│     kms_key_id     = "alias/terraform-state"                │
│     dynamodb_table = "terraform-locks"                      │
│   }                                                         │
│ }                                                           │
│                                                             │
│ S3 Bucket Policy:                                           │
│ - Deny unencrypted uploads                                  │
│ - Restrict to specific IAM roles                            │
│ - Enable versioning for recovery                            │
│ - Enable access logging                                     │
└─────────────────────────────────────────────────────────────┘

Secret Scanning:

Pre-Commit Secret Scanning:

GIT-SECRETS:
┌─────────────────────────────────────────────────────────────┐
│ # Install                                                   │
│ brew install git-secrets                                    │
│                                                             │
│ # Configure for AWS patterns                                │
│ git secrets --register-aws                                  │
│                                                             │
│ # Install hook                                              │
│ git secrets --install                                       │
│                                                             │
│ # Scan existing repo                                        │
│ git secrets --scan                                          │
│                                                             │
│ Detects:                                                    │
│ - AWS access key IDs (AKIA...)                              │
│ - AWS secret access keys                                    │
│ - Custom patterns you define                                │
└─────────────────────────────────────────────────────────────┘

GITLEAKS:
┌─────────────────────────────────────────────────────────────┐
│ # Scan repository                                           │
│ gitleaks detect --source .                                  │
│                                                             │
│ # Scan in CI/CD                                             │
│ gitleaks detect --source . --exit-code 1                    │
│                                                             │
│ Detects:                                                    │
│ - AWS, GCP, Azure credentials                               │
│ - API keys                                                  │
│ - Private keys                                              │
│ - Passwords                                                 │
│ - Generic secrets                                           │
└─────────────────────────────────────────────────────────────┘

GITHUB SECRET SCANNING:
┌─────────────────────────────────────────────────────────────┐
│ Automatic scanning for:                                     │
│ - Supported secret types (100+)                             │
│ - Push protection (blocks commits)                          │
│ - Alerts on detected secrets                                │
│                                                             │
│ Enable in repository settings:                              │
│ Settings > Security > Secret scanning                       │
└─────────────────────────────────────────────────────────────┘

Key insight: Treat IaC state files as sensitive as the secrets they contain. Encrypt, restrict access, and audit.

5) Secure IaC Pipelines

The CI/CD pipeline for IaC requires its own security controls:

Secure Pipeline Design:

PIPELINE STAGES:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐      │
│  │  Lint   │──►│  Scan   │──►│  Plan   │──►│ Approve │      │
│  │         │   │Security │   │         │   │         │      │
│  └─────────┘   └─────────┘   └─────────┘   └────┬────┘      │
│                                                 │           │
│                     ┌───────────────────────────┘           │
│                     ▼                                       │
│               ┌─────────┐   ┌─────────┐                     │
│               │  Apply  │──►│  Verify │                     │
│               │         │   │         │                     │
│               └─────────┘   └─────────┘                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

STAGE DETAILS:
┌─────────────────────────────────────────────────────────────┐
│ 1. LINT:                                                    │
│    - terraform fmt -check                                   │
│    - terraform validate                                     │
│    - tflint                                                 │
│                                                             │
│ 2. SECURITY SCAN:                                           │
│    - checkov / tfsec / trivy                                │
│    - Secret scanning                                        │
│    - Policy evaluation (OPA/Sentinel)                       │
│    - FAIL on critical findings                              │
│                                                             │
│ 3. PLAN:                                                    │
│    - terraform plan                                         │
│    - Review changes                                         │
│    - Cost estimation                                        │
│    - Drift detection                                        │
│                                                             │
│ 4. APPROVE:                                                 │
│    - Manual approval for production                         │
│    - Review plan output                                     │
│    - Require multiple approvers                             │
│                                                             │
│ 5. APPLY:                                                   │
│    - terraform apply                                        │
│    - Only from main branch                                  │
│    - Audit logging                                          │
│                                                             │
│ 6. VERIFY:                                                  │
│    - Post-deployment validation                             │
│    - Compliance check                                       │
│    - Functional testing                                     │
└─────────────────────────────────────────────────────────────┘

Pipeline Security Controls:

Pipeline Security:

CREDENTIAL MANAGEMENT:
┌─────────────────────────────────────────────────────────────┐
│ GitHub Actions with OIDC (no stored credentials):           │
│                                                             │
│ jobs:                                                       │
│   deploy:                                                   │
│     permissions:                                            │
│       id-token: write                                       │
│       contents: read                                        │
│     steps:                                                  │
│       - uses: aws-actions/configure-aws-credentials@v2      │
│         with:                                               │
│           role-to-assume: arn:aws:iam::123456789:role/...   │
│           aws-region: us-east-1                             │
│                                                             │
│ Benefits:                                                   │
│ - No long-lived credentials                                 │
│ - Automatic rotation                                        │
│ - Auditable access                                          │
└─────────────────────────────────────────────────────────────┘

LEAST PRIVILEGE FOR PIPELINE:
┌─────────────────────────────────────────────────────────────┐
│ Plan Stage IAM Role:                                        │
│ - Read-only access to state                                 │
│ - Read-only access to resources                             │
│ - No create/update/delete                                   │
│                                                             │
│ Apply Stage IAM Role:                                       │
│ - Full access to managed resources                          │
│ - Write access to state                                     │
│ - Scoped to specific resource types                         │
│ - No IAM admin permissions                                  │
│                                                             │
│ Separate roles for:                                         │
│ - Different environments (dev, staging, prod)               │
│ - Different resource types                                  │
│ - Plan vs Apply stages                                      │
└─────────────────────────────────────────────────────────────┘

BRANCH PROTECTION:
┌─────────────────────────────────────────────────────────────┐
│ GitHub Branch Protection Rules:                             │
│                                                             │
│ ✓ Require pull request reviews                              │
│ ✓ Require status checks to pass                             │
│   - security-scan                                           │
│   - terraform-plan                                          │
│ ✓ Require branches to be up to date                         │
│ ✓ Require signed commits                                    │
│ ✓ Do not allow bypassing settings                           │
│                                                             │
│ Only allow terraform apply from main branch                 │
│ Only allow merges after security scan passes                │
└─────────────────────────────────────────────────────────────┘

COMPLETE GITHUB ACTIONS EXAMPLE:
name: Terraform
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  id-token: write
  contents: read
  pull-requests: write

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Secret Scan
        uses: gitleaks/gitleaks-action@v2
        
      - name: Checkov Scan
        uses: bridgecrewio/checkov-action@master
        with:
          directory: terraform/
          soft_fail: false
          
      - name: tfsec Scan  
        uses: aquasecurity/tfsec-action@v1.0.0

  plan:
    needs: security
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: ${{ secrets.AWS_PLAN_ROLE }}
          aws-region: us-east-1
          
      - uses: hashicorp/setup-terraform@v2
      
      - name: Terraform Init
        run: terraform init
        
      - name: Terraform Plan
        run: terraform plan -out=tfplan
        
      - name: Post Plan to PR
        uses: actions/github-script@v6
        if: github.event_name == 'pull_request'
        with:
          script: |
            // Post plan output as PR comment

  apply:
    needs: plan
    if: github.ref == 'refs/heads/main'
    environment: production  # Requires approval
    runs-on: ubuntu-latest
    steps:
      - uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: ${{ secrets.AWS_APPLY_ROLE }}
          
      - name: Terraform Apply
        run: terraform apply -auto-approve tfplan

Key insight: The pipeline itself is a security boundary. Protect it as carefully as production infrastructure.

Real-World Context

Case Study: Codecov Supply Chain Attack

In 2021, attackers compromised Codecov's bash uploader script, which was used in thousands of CI/CD pipelines. The malicious script exfiltrated environment variables—including cloud credentials—from customer pipelines. Organizations using long-lived AWS credentials in their CI/CD were compromised. Those using OIDC-based authentication (short-lived credentials) were largely unaffected because credentials weren't stored in environment variables. This incident highlighted the importance of minimizing credentials in pipelines and using federated authentication.

Case Study: Terraform State Exposure

A developer accidentally committed their terraform.tfstate file to a public GitHub repository. The state file contained database passwords, API keys, and other sensitive values in plaintext. Even after removing the file, it remained in Git history. The organization had to rotate all credentials and implement state file encryption, remote backends, and .gitignore enforcement. Prevention would have been far cheaper than remediation.

IaC Security Checklist:

IaC Security Checklist:

CODE SECURITY:
□ No hardcoded secrets
□ Secrets from Secrets Manager / Vault
□ Variables marked sensitive
□ No default passwords
□ Secret scanning in pre-commit hooks

STATIC ANALYSIS:
□ Checkov/tfsec/trivy in CI/CD
□ Fail on HIGH/CRITICAL findings
□ Policy as code (OPA/Sentinel)
□ Regular rule updates

STATE FILE SECURITY:
□ Remote backend (S3, Terraform Cloud)
□ State file encryption (KMS)
□ State file access restricted
□ State file versioning enabled
□ No local state files committed

PIPELINE SECURITY:
□ OIDC authentication (no stored credentials)
□ Least privilege IAM roles
□ Separate plan/apply roles
□ Branch protection enabled
□ Required status checks
□ Manual approval for production
□ Audit logging enabled

MODULES:
□ Pin module versions
□ Use trusted sources
□ Review module code
□ Scan module dependencies

DRIFT DETECTION:
□ Regular drift checks
□ Alert on manual changes
□ Enforce IaC-only changes

IaC security is about preventing misconfigurations at the source. Every security check in the pipeline is a vulnerability prevented in production.

Guided Lab: Secure IaC Pipeline

In this lab, you'll build a secure Terraform pipeline with scanning and policy enforcement.

Lab Environment:

  • GitHub repository
  • AWS account with OIDC configured
  • Terraform installed locally
  • Checkov and tfsec installed

Exercise Steps:

  1. Create Terraform configuration with intentional issues
  2. Run local security scans and review findings
  3. Fix security issues in Terraform code
  4. Configure S3 backend with encryption
  5. Create GitHub Actions workflow with security stages
  6. Configure OIDC authentication for AWS
  7. Add OPA/Conftest policy checks
  8. Enable branch protection
  9. Test pipeline blocks insecure code

Reflection Questions:

  • How does shifting security left change the development workflow?
  • What security issues can't be caught by static analysis?
  • How would you handle exceptions to security policies?

Week Outcome Check

By the end of this week, you should be able to:

  • Explain the security benefits and risks of Infrastructure as Code
  • Use Checkov, tfsec, and Trivy for IaC security scanning
  • Write OPA/Rego policies for Terraform validation
  • Implement secure secrets management in IaC
  • Configure secure Terraform state backend
  • Design secure CI/CD pipelines for IaC deployment
  • Configure OIDC authentication for pipeline AWS access
  • Implement branch protection and approval workflows

🎯 Hands-On Labs (Free & Essential)

Secure infrastructure-as-code with static analysis, policy enforcement, and secrets scanning.

📝 TryHackMe: Terraform Security

What you'll do: Analyze Terraform code for misconfigurations—find exposed secrets, overpermissive policies, and hardening opportunities.
Why it matters: IaC misconfigurations multiply at deployment—catch them before production.
Time estimate: 2-3 hours

Start Terraform Security Lab →

🔍 Open Source: Checkov & tfsec Practice

What you'll do: Run Checkov and tfsec against sample IaC code—analyze findings, fix vulnerabilities, integrate into CI/CD.
Why it matters: Automated scanning prevents 80% of common IaC misconfigurations.
Time estimate: 2-3 hours

Open Checkov Repository →

🛡️ HackTheBox: CI/CD Pipeline Security

What you'll do: Exploit and secure CI/CD pipelines—secrets exposure, supply chain attacks, and policy enforcement (free modules).
Why it matters: Compromised pipelines deploy compromised infrastructure at scale.
Time estimate: 3-4 hours

Open HTB CI/CD Module →

💡 Lab Strategy: Never commit secrets to IaC—use secret managers and environment variables. Git history never forgets.

Resources

Lab

Complete the following lab exercises to practice IaC security.

Part 1: Security Scanning (LO8)

Scan Terraform code: (a) create Terraform with intentional misconfigurations, (b) scan with Checkov and tfsec, (c) analyze findings, (d) remediate issues, (e) verify scan passes.

Deliverable: Before/after Terraform code with scan results showing remediation.

Part 2: Policy as Code (LO8)

Implement OPA policies: (a) write Rego policy for S3 encryption, (b) write policy for security group restrictions, (c) test policies against Terraform plan, (d) integrate with Conftest.

Deliverable: Rego policy files with test results showing policy enforcement.

Part 3: Secrets Management (LO8)

Implement secure secrets: (a) configure Secrets Manager integration, (b) use random_password for generated secrets, (c) configure SOPS for encrypted files, (d) verify no plaintext secrets in state.

Deliverable: Terraform code showing secure secrets patterns with documentation.

Part 4: State Security (LO8)

Secure state management: (a) configure S3 backend with encryption, (b) set up DynamoDB locking, (c) configure bucket policy, (d) enable access logging.

Deliverable: Backend configuration with S3 bucket security settings documented.

Part 5: Pipeline Security (LO8)

Build secure pipeline: (a) create GitHub Actions workflow, (b) configure OIDC authentication, (c) add security scanning stages, (d) configure branch protection, (e) test pipeline blocks insecure code.

Deliverable: GitHub Actions workflow file and evidence of security gate enforcement.

Checkpoint Questions

  1. What are the security benefits of Infrastructure as Code compared to manual configuration?
  2. Compare Checkov and tfsec. What types of issues does each tool detect?
  3. What is Policy as Code? How does OPA/Rego differ from static analysis tools?
  4. Why is Terraform state sensitive? What protections should be applied to state files?
  5. Describe three patterns for managing secrets in Terraform without hardcoding them.
  6. Why is OIDC preferred over stored credentials for CI/CD AWS authentication?

Week 09 Quiz

Test your understanding of Infrastructure as Code usage, tools, and security.

Format: 10 multiple-choice questions. Passing score: 70%. Time: Untimed.

Take Quiz

Weekly Reflection

Infrastructure as Code transforms security from manual auditing to automated validation. This week explored building security into the infrastructure development lifecycle.

Reflect on the following in 200-300 words:

A strong reflection demonstrates understanding of IaC as an enabler of security automation while acknowledging the new risks it introduces.

Verified Resources & Videos

← Previous: Week 08 Next: Week 10 →