Infrastructure as Code

By Davy Rogers

IaC makes your infrastructure reviewable. It also makes your misconfigurations repeatable.

Infrastructure as Code turns your servers, networks, and permissions into version-controlled files. That's a genuine security win: infrastructure becomes reviewable, auditable, and repeatable, the same way application code is. But the repeatability cuts both ways. A misconfiguration written into a Terraform module isn't a one-off mistake on one server, it's a mistake you now deploy reliably, everywhere, every time. So the goal is to catch the bad config in the file, before it becomes thirty identical bad configs in production.

The misconfigurations to know on sight

A few patterns account for a large share of real cloud incidents. Learn to spot them in a diff.

A public storage bucket, usually one line away from secure:

# VULNERABLE
acl = "public-read"

# SECURE
resource "aws_s3_bucket_public_access_block" "data" {
  block_public_acls   = true
  block_public_policy = true
}

A security group open to the world, when it only needed to be open to the internal network:

# VULNERABLE
cidr_blocks = ["0.0.0.0/0"]

# SECURE
cidr_blocks = ["10.0.0.0/8"]

An IAM policy with wildcards, which grants far more than anything actually needs:

// VULNERABLE
{"Action": "*", "Resource": "*"}

// SECURE
{"Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::my-bucket/*"}

Scan it like code

Because it's code, you can test it before it's applied. Run a scanner in CI and block the merge on a failure:

checkov -d .
tfsec .

For the rules a generic scanner doesn't cover, write your own as policy-as-code with OPA or Sentinel, so your organisation's specific requirements are enforced automatically rather than remembered.

Protect the state file

The Terraform state file is sensitive in its own right. It contains resource IDs and outputs, which can include secrets. Never commit it. Keep it in a remote backend with encryption, and restrict who can read it.

Watch for drift

The moment someone makes a change by hand in the cloud console, your code and your reality diverge. That's drift, and it's dangerous because your files now lie about what's actually deployed. terraform plan shows you the difference; automate that check so drift is detected in minutes, not discovered during an incident.

Bake good defaults into modules

The most durable fix is to make the secure version the easy version. Encapsulate your hardened patterns into modules so teams get them by default:

module "secure_bucket" {
  source = "./modules/secure-s3-bucket"
}

Now "do the secure thing" is one line, and nobody has to remember the seven settings that make a bucket safe. Scan in CI, protect your state, detect drift, and push the right defaults down into modules so they're hard to get wrong.