Infrastructure as code Required
Define every resource in code
Create and change every cloud resource, firewall rule and DNS record through code in version control. Never make a lasting change in a web console.
How to do it
Pick one tool per project, such as OpenTofu, Terraform, Pulumi or CloudFormation. Give people read-only console access to staging and production, so a hand-made change cannot happen. Bring resources made by hand under code with import. A sandbox account for short experiments is the only exception, and it is wiped on a schedule.
Example
resource "aws_s3_bucket" "invoices" {
bucket = "example-shop-invoices"
}
// Pulumi in TypeScript: the same bucket
import * as aws from '@pulumi/aws';
export const invoices = new aws.s3.Bucket('invoices', { bucket: 'example-shop-invoices' });
# Pulumi in Python: the same bucket
import pulumi_aws as aws
invoices = aws.s3.Bucket("invoices", bucket="example-shop-invoices")
# Fails when anyone but the deploy role made a write call in this region in the last 30 days.
# Run it in every region you use. lookup-events reads at most 90 days of management events.
! aws cloudtrail lookup-events \
--start-time "$(date -u -d '-30 days' +%FT%TZ)" \
--lookup-attributes AttributeKey=ReadOnly,AttributeValue=false \
--query 'Events[].CloudTrailEvent' --output json \
| jq -r '.[] | fromjson | select(.userIdentity.type != "AWSService")
| .userIdentity.sessionContext.sessionIssuer.userName // .userIdentity.arn' \
| sort -u | grep -vx 'deploy-production'
How to check
The cloud audit log for production, such as AWS CloudTrail, shows no write calls in the last 30 days from anyone but the deploy role. A plan on the main branch shows no changes.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers infrastructure playbook: https://agentwranglers.se/playbook/infrastructure/infrastructure-as-code/define-infrastructure-in-code.