Skip to main content

🛠️ Examples of Secure Structure and Practices in IaC Projects

🌟 Objective​

Present concrete, reusable and auditable examples of how to structure and operate Infrastructure as Code (IaC) projects in a secure, coherent way aligned with the practices prescribed in this chapter.

This file is illustrative and operational in nature: it does not introduce new requirements, but demonstrates how to apply the existing ones correctly.


📁 Recommended repository structure​

iac/
├── modules/
│ └── networking/
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ └── README.md
├── envs/
│ ├── dev/
│ │ ├── main.tf
│ │ └── backend.tf
│ ├── staging/
│ └── prod/
├── policies/
│ ├── opa/
│ └── conftest/
├── .github/workflows/
│ └── validate.yaml
├── .pre-commit-config.yaml
├── LICENSE
└── README.md

Security properties ensured:

  • Physical and logical separation of environments;
  • Controlled and reusable modularisation;
  • Native integration of policy-as-code;
  • Direct support for traceability and auditing.

🏷️ Example of mandatory tagging​

tags = {
Environment = var.environment
Owner = var.owner
Application = var.application
Criticality = var.criticality
ManagedBy = "Terraform"
}
  • Tags are applied by default to all resources;
  • The absence of any mandatory tag must result in a blocking failure via OPA/Rego.

🔐 Example of permission control (least privilege)​

resource "aws_iam_role" "example" {
name = "app-deploy-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})

managed_policy_arns = [aws_iam_policy.least_privilege.arn]
}

Security notes:

  • No broad policy (*) is permitted by default;
  • Permissions are explicitly attached and validated in policy-as-code;
  • Mandatory review for any change to IAM.

🔄 Example of secure use of external modules​

module "vpc" {
source = "git::https://github.com/org/vpc-module.git?ref=v1.2.3"

cidr_block = "10.0.0.0/16"
environment = var.environment
}

Good practices demonstrated:

  • Explicit reference to an immutable tag (ref=vX.Y.Z);
  • Verifiable origin;
  • Compatible with digest and attestation validation in the pipeline.

💪 Example of a CI workflow for IaC validation​

name: Validate IaC

on:
pull_request:
paths:
- 'iac/**'

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Terraform Format
run: terraform fmt -check -recursive

- name: Terraform Validate
run: terraform validate

- name: TFLint
uses: terraform-linters/setup-tflint@v1

- name: tfsec
run: tfsec ./iac

- name: Checkov
run: checkov -d ./iac

Guarantees provided by the pipeline:

  • Syntactic and structural validation;
  • Early detection of misconfigurations;
  • Automatic blocking of non-conformities;
  • Auditable evidence per PR/MR.

🧷 Pre-commit hooks template​

repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.66.1
hooks:
- id: terraform_fmt
- id: terraform_validate
- id: terraform_tflint
- id: terraform_tfsec

Objective:

  • Shorten the feedback loop;
  • Eliminate trivial errors before CI;
  • Normalise quality across contributors.

🔗 Link to requirements​

Applied exampleRelated requirements
Modular repository structureIAC-002, IAC-005, REQ-004
Mandatory taggingIAC-002, IAC-008, REQ-006
Use of versioned modulesIAC-004, REQ-007
CI workflow with scannersIAC-003, REQ-005, REQ-006
Pre-commit hooksIAC-003, REQ-006

✅ Direct benefits​

  • Accelerates the consistent adoption of secure IaC;
  • Reduces variation between projects and teams;
  • Facilitates auditing and technical onboarding;
  • Turns security into an operational default.

🔗 These examples are aligned with the practices of SSDF (PW.6), SLSA (Build L2) and OWASP SAMM (AA2.1, CM1.1), serving as operational reference material for technical teams.