---
title: "Define every resource in code · Agent Wranglers"
url: https://agentwranglers.se/playbook/infrastructure/infrastructure-as-code/define-infrastructure-in-code
updated: 2026-10-20
licence: "All rights reserved"
---

# Define every resource in code

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Required
- Chapter: [Infrastructure as code](https://agentwranglers.se/playbook/infrastructure/infrastructure-as-code)
- Rule: 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: 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](https://agentwranglers.se/playbook/infrastructure/infrastructure-as-code/import-existing-resources). A sandbox account for short experiments is the only exception, and it is wiped on a schedule.
- 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.

Example:

```hcl
resource "aws_s3_bucket" "invoices" {
  bucket = "example-shop-invoices"
}
```

```ts
// Pulumi in TypeScript: the same bucket
import * as aws from '@pulumi/aws';

export const invoices = new aws.s3.Bucket('invoices', { bucket: 'example-shop-invoices' });
```

```python
# Pulumi in Python: the same bucket
import pulumi_aws as aws

invoices = aws.s3.Bucket("invoices", bucket="example-shop-invoices")
```

```sh
# 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'
```

**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`.

- Next: [Keep state remote with locking and encryption](https://agentwranglers.se/playbook/infrastructure/infrastructure-as-code/remote-state)
- [Infrastructure as code](https://agentwranglers.se/playbook/infrastructure/infrastructure-as-code)
