tadata
Back to home

Infrastructure as Code Patterns: Tools, State & Multi-Account Design

#infrastructure#iac#devops#terraform#cloud

Infrastructure as Code (IaC) is the practice of managing infrastructure through declarative or imperative definitions stored in version control. In 2026, the question is no longer whether to use IaC but which tool, pattern, and organizational model fits your context.

Tool Comparison

DimensionTerraform / OpenTofuPulumiAWS CDKAzure BicepCrossplane
LanguageHCLTypeScript, Python, Go, C#TypeScript, Python, GoBicep DSLYAML (K8s CRDs)
ParadigmDeclarativeImperative + DeclarativeImperative → CloudFormationDeclarativeDeclarative (GitOps)
Multi-cloudExcellent (1000+ providers)Good (growing)AWS onlyAzure onlyGood (providers)
State managementRemote backend (S3, GCS, TFC)Pulumi Cloud or self-managedCloudFormation stackAzure Resource Manageretcd (K8s cluster)
Learning curveMedium (HCL is unique)Low (familiar languages)MediumLow (Azure devs)High (K8s required)
TestingTerratest, tftestNative unit testsCDK AssertionsBicep linterK8s testing tools
License (2026)BSL (TF) / MPL (OpenTofu)Apache 2.0Apache 2.0MITApache 2.0
Best forMulti-cloud, large teamsDev-heavy teamsAWS-native shopsAzure-native shopsK8s-centric platforms
Community sizeVery largeGrowingLarge (AWS)Medium (Azure)Medium

State Management Comparison

StrategyProsConsBest For
Local stateSimple, no setupNo collaboration, no lockingLearning, prototyping
Remote backend (S3+DynamoDB)Locking, team access, versionedSelf-managed, no UIAWS-centric teams
Terraform Cloud / HCPUI, run history, RBAC, VCS integrationCost, vendor lock-inEnterprise teams
Pulumi CloudIntegrated with Pulumi, secrets mgmtTied to Pulumi ecosystemPulumi users
GitOps (Crossplane)K8s-native, drift detectionRequires K8s clusterPlatform engineering teams

Critical rule: State files contain secrets. Encrypt at rest, restrict access, never commit to git.

Module Design Patterns

IaC Module Patterns
├── Composition Pattern
│   ├── Root module calls child modules
│   ├── Each module = one logical resource group
│   └── Example: network module + compute module + database module
│
├── Wrapper Pattern
│   ├── Thin wrapper around a provider resource
│   ├── Enforces org standards (tags, naming, encryption)
│   └── Example: "aws-s3-bucket" module that always enables versioning
│
├── Scaffold / Template Pattern
│   ├── Full environment template
│   ├── Parameterized for dev/staging/prod
│   └── Example: "environment" module creating VPC + EKS + RDS
│
└── Library Pattern
    ├── Shared modules in a central registry
    ├── Versioned, tested, documented
    └── Example: Terraform private registry or Git tags

Testing Strategy

Test LevelWhat It ValidatesToolSpeedWhen to Run
Static analysisSyntax, best practices, securitytflint, checkov, tfsecSecondsEvery commit (pre-commit)
Unit testsModule logic, variable validationtftest, Pulumi unit testsSecondsEvery PR
Contract testsOutputs match expected schemaCustom assertionsSecondsEvery PR
Integration testsReal infra deploys and worksTerratest, kitchen-terraformMinutesNightly or per PR (staging)
E2E / Smoke testsFull stack is functionalApplication-level testsMinutesPost-deploy
Drift detectionActual state matches desiredterraform plan (scheduled)MinutesDaily (cron)

Multi-Account Architecture

┌─────────────────────────────────────────────────────────────┐
│                     AWS Organization                         │
│                                                              │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │  Management  │  │  Security    │  │  Shared      │      │
│  │  Account     │  │  Account     │  │  Services    │      │
│  │              │  │              │  │              │      │
│  │ - Billing    │  │ - GuardDuty  │  │ - Networking │      │
│  │ - Org mgmt   │  │ - Config     │  │ - DNS        │      │
│  │ - SSO        │  │ - CloudTrail │  │ - CI/CD      │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
│                                                              │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │  Dev         │  │  Staging     │  │  Production  │      │
│  │  Account     │  │  Account     │  │  Account     │      │
│  │              │  │              │  │              │      │
│  │ - Workloads  │  │ - Workloads  │  │ - Workloads  │      │
│  │ - Sandbox    │  │ - Pre-prod   │  │ - Live       │      │
│  │ - Relaxed    │  │ - Prod-like  │  │ - Strict     │      │
│  │   policies   │  │   policies   │  │   policies   │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
│                                                              │
│  IaC Strategy:                                               │
│  - Shared modules in central repo (versioned)                │
│  - Per-account state files (isolated)                        │
│  - CI/CD deploys to dev → staging → prod                     │
│  - Drift detection runs daily per account                    │
└─────────────────────────────────────────────────────────────┘

Key Principles

Treat modules like libraries. Version them, test them, document their interfaces. Breaking changes in a shared module should follow semantic versioning.

State isolation per environment. Never share a state file between dev and prod. A bad plan in dev should never risk production resources.

Plan before apply, always. Every change goes through plan in CI. apply only happens after human review or automated policy checks (OPA/Sentinel).

Resources