Infrastructure as code on AWS means defining resources, VPCs, load balancers, databases, IAM roles, in a file that is versioned and reviewed like application code, then applying it with a tool instead of clicking through the console. The main tools are CloudFormation, AWS’s native YAML or JSON format, CDK, which compiles code in a real language into CloudFormation, Terraform and its fork OpenTofu, which are cloud-agnostic and use HCL, and Pulumi, which uses general-purpose languages with no separate templating format. The choice comes down to team shape more than technical merit: a single-cloud AWS shop with no existing Terraform knowledge fits CloudFormation or CDK, a team that already runs Terraform elsewhere keeps using it, and a team of three or four people building one application often gets more value from a platform that removes most of this layer entirely.
Every AWS IaC explainer online reaches the same conclusion, that infrastructure as code beats clicking through the console, and stops there. That part is not in dispute. What is missing from most of them is which tool for which team, what a state file actually costs to operate correctly, and the honest fact that a small application does not always need any of this.
Table of contents
- What infrastructure as code means, concretely
- CloudFormation: native, and verbose
- CDK: infrastructure as actual code
- Terraform and OpenTofu: cloud-agnostic, and the most common default
- Pulumi: general-purpose languages, no separate format
- State and drift: the operational tax every tool shares
- A minimal IaC layout for a small application
- The honest alternative
- How this fits the rest of the stack
- FAQ
What infrastructure as code means, concretely
Instead of creating an S3 bucket, a VPC, and a database by clicking through the AWS console, IaC defines them as text: a bucket resource with a name and a versioning setting, a VPC with subnets and route tables, a database with an engine and an instance size. That file gets committed to Git, reviewed in a pull request like any code change, and applied by a tool that reconciles the described state with what actually exists in the account.
The value is repeatability and review. A console change has no diff by default; an IaC change does.
CloudFormation: native, and verbose
CloudFormation is AWS’s own format, YAML or JSON, with no separate installation since it runs entirely as an AWS service. It integrates tightly with IAM permissions and every other AWS service, and it is the most verbose of the options for anything beyond a handful of resources, which is the trade a native format for the underlying API tends to make.
It fits a team entirely on AWS, that does not want a third-party tool in the deploy path, and is comfortable with YAML at real application size.
CDK: infrastructure as actual code
CDK lets the same resources be defined in TypeScript, Python, or a few other languages, with loops, conditionals, and reusable constructs, and it compiles down to a CloudFormation template at deploy time. For a team of developers who already think in one of those languages, CDK reads more like the rest of the codebase than a CloudFormation YAML file does.
It still runs through CloudFormation underneath, so CloudFormation’s deployment model and its occasional stuck-stack failure modes apply either way.
Terraform and OpenTofu: cloud-agnostic, and the most common default
Terraform, and OpenTofu after the 2023 license change forked it under an open license, uses HCL, a purpose-built declarative language, and supports every major cloud provider through the same workflow. That matters for a team on AWS today that might add another provider later, or one already running infrastructure on more than one cloud.
Terraform’s plan-then-apply workflow, showing exactly what will change before it changes, is the feature most teams cite as the reason they picked it over CloudFormation directly.
Pulumi: general-purpose languages, no separate format
Pulumi is closer to CDK than Terraform in spirit, general-purpose languages instead of a templating format, but it targets multiple clouds rather than compiling to one provider’s native format. It suits a team that wants CDK’s ergonomics without being tied to AWS specifically.
State and drift: the operational tax every tool shares
Every one of these tools keeps a state file, or an equivalent, tracking what it believes exists. Two people applying changes from their own laptops without a shared, locked state store will eventually corrupt that state or overwrite each other’s changes; a remote backend with locking, an S3 bucket with DynamoDB locking for Terraform, or CloudFormation’s own managed state, is not optional once more than one person touches infrastructure.
Drift, someone changing a resource by hand in the console, is the other constant cost. Every tool eventually needs a drift-detection or reconciliation step, and every team that skips it finds out about drift the hard way, during an apply that tries to revert someone’s manual fix.
A minimal IaC layout for a small application
For a small team’s first real IaC setup, regardless of tool: one directory per environment, a shared module for anything genuinely identical between them, a remote state backend with locking from day one, and CI running plan on every pull request with apply gated behind review. Resist defining more than the application needs; a VPC, a database, and the compute layer is often the entire list for a small service.
The honest alternative
A three or four person team building one application, rather than operating a platform for others, is often choosing between IaC tooling to manage a VPC, a load balancer, an autoscaling group, and a managed database, or a deploy platform where most of that is already handled: the network, the certificate, the scaling policy, and the database’s private networking exist without a template describing them.
That is not a universal answer. A team with compliance requirements naming specific AWS services, or existing infrastructure already under Terraform, has real reasons to stay there. A team whose entire IaC file describes one VPC, one service, and one database, repeated with small variations per environment, is often paying the maintenance cost of a tool to describe something a platform already provides.
How this fits the rest of the stack
For the shape of infrastructure most small teams are actually describing, a service, a database, environment variables, and a domain, RunxBuild provides the underlying network, scaling, and database provisioning without a state file to lock or a drift check to run: push the repository, pick a plan, connect a managed Postgres or MySQL instance on private networking, and let autoscaling handle the floor-to-ceiling range a small IaC setup would otherwise script by hand. The RunxBuild hosting calculator prices that against a specific vCPU and memory target rather than an AWS bill nobody has committed to yet.
Useful related references:
- AWS CodeDeploy: What a Deployment Controller Does, and When You Need One
- Terraform Deployment: Where the Line Between Infra and App Sits
- Cloud Infrastructure Provider: How to Choose Without a Vendor Grid
- Services on RunxBuild
FAQ
What is IaC in AWS?
Defining AWS resources, such as a VPC, an S3 bucket, or a database instance, as versioned text files applied by a tool, rather than creating them by clicking through the AWS console. CloudFormation, CDK, Terraform, OpenTofu, and Pulumi are the common tools; they differ in language and provider scope, not in the underlying idea.
CloudFormation vs Terraform, which should I use?
CloudFormation if the team is entirely on AWS, wants no third-party tool in the deploy path, and is comfortable with YAML at real application size. Terraform if the team might use another cloud provider, already knows HCL, or values its plan-then-apply diff workflow. Neither is objectively better; the choice follows team background and provider scope.
Is CDK better than CloudFormation?
CDK compiles to CloudFormation, so it inherits the same deployment behaviour underneath. CDK is worth choosing when the team wants to define infrastructure in TypeScript, Python, or another general-purpose language instead of YAML, with real loops and reusable constructs; it is not a different deployment engine.
Is IaC worth it for a small project on AWS?
Worth it once more than one person touches infrastructure, or once staging and production need to stay in sync. For a solo developer, the state file and locking setup IaC requires can cost more time than the manual changes it replaces, at least until the project grows.
What is drift in infrastructure as code?
A mismatch between what the IaC tool’s state file believes exists and what is actually running, usually caused by a manual change made in the AWS console outside the tool. Every IaC tool needs a periodic drift check or reconciliation step, because an apply that encounters unexpected drift can revert a manual fix without warning.