PS Product SecurityKnowledge Base

Terraform and IaC Security Assessment Pack

Intro: These tasks are intentionally practical. The question is rarely โ€œwhat does this resource do?โ€ The question is โ€œwhat trust assumption did the author just make, and where does it leak?โ€

Task 1 - Terraform backend has no clear locking or encryption story

terraform {
  backend "s3" {
    bucket = "team-terraform-state"
    key    = "prod/network/terraform.tfstate"
    region = "us-east-1"
  }
}

Prompt

What is missing? Why does it matter operationally and from a security perspective?

Reveal the worked answer

Missing elements to discuss

  • no explicit locking story;
  • no mention of bucket hardening or access policy;
  • no state sensitivity discussion;
  • no clear KMS / encryption description;
  • no segregation of environments or ownership.

Better answer

A strong candidate says the backend block is only part of the story. You must also review the bucket policy, encryption, locking mechanism, access model, and environment separation.

Task 2 - Security group allows broad access because CIDR is passed from a shared variable

variable "admin_cidrs" {
  type = list(string)
  default = ["0.0.0.0/0"]
}

resource "aws_security_group_rule" "ssh_ingress" {
  type              = "ingress"
  from_port         = 22
  to_port           = 22
  protocol          = "tcp"
  cidr_blocks       = var.admin_cidrs
  security_group_id = aws_security_group.app.id
}

Prompt

What would you fix in code and in process?

Reveal the worked answer

Code fix direction

  • remove insecure defaults;
  • require explicit caller-provided ranges;
  • consider replacing SSH with Session Manager or JIT access patterns.

Process fix direction

  • add policy checks for 0.0.0.0/0 on admin ports;
  • block merge on critical exposure;
  • require justification for exceptions.

Task 3 - Module source is unpinned and fetched from Git on every run

module "vpc" {
  source = "git::https://github.com/example-org/terraform-vpc.git"
  name   = "prod-vpc"
}

Prompt

Why is this weak from a supply-chain perspective? Show a safer pattern.

Reveal the worked answer

Why weak

Without a pinned ref, the module content can drift unexpectedly. Review and execution no longer mean the same thing over time.

Safer pattern

module "vpc" {
  source = "git::https://github.com/example-org/terraform-vpc.git?ref=v1.4.2"
  name   = "prod-vpc"
}

Better still

Use an internal reviewed module distribution path and deliberate update workflow.

Task 4 - Ansible task leaks secrets into logs and shell history

- name: configure application secret
  shell: |
    echo "APP_SECRET={{ app_secret }}" >> /etc/app.env
  become: true

Prompt

This is mixed Terraform/IaC review territory. Explain the issue and propose a safer Ansible pattern.

Reveal the worked answer

What is wrong

  • shell is unnecessary for a simple file update;
  • secrets may appear in logs, task output, or process inspection;
  • idempotence is poor.

Safer Ansible pattern

- name: configure application secret
  copy:
    dest: /etc/app.env
    content: "APP_SECRET={{ app_secret }}\n"
    owner: root
    group: root
    mode: "0600"
  no_log: true
  become: true

Strong answer also mentions

Prefer a secret manager or injected runtime identity if the environment supports it.

Task 5 - terraform apply is triggered from merge requests with live cloud credentials

Scenario

The pipeline runs terraform plan and terraform apply in the same MR workflow against a real staging account, using long-lived cloud keys in CI variables.

Prompt

What are the issues and how would you redesign the workflow?

Reveal the worked answer

Issues

  • unreviewed or insufficiently reviewed infra mutation from MR context;
  • long-lived credentials in CI;
  • likely plan/apply trust collapse;
  • higher blast radius if a malicious MR lands on a trusted runner.

Better redesign

  • run lint, validate, and policy checks on every MR;
  • run plan in a controlled review stage;
  • require explicit promotion or protected-branch/tag context for apply;
  • replace long-lived cloud keys with OIDC/federated identity where possible.