🛠️ 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 example | Related requirements |
|---|---|
| Modular repository structure | IAC-002, IAC-005, REQ-004 |
| Mandatory tagging | IAC-002, IAC-008, REQ-006 |
| Use of versioned modules | IAC-004, REQ-007 |
| CI workflow with scanners | IAC-003, REQ-005, REQ-006 |
| Pre-commit hooks | IAC-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.