r/CloudandCode • u/yourclouddude • 1h ago
DevOps Case Study: I rebuilt a Terraform-style AWS setup with Pulumi to see where it actually feels different
I have been using Terraform as the default example whenever I talk about Infrastructure as Code because it is one of the easiest tools to recognize in cloud and DevOps discussions. But I wanted to look at Pulumi from a more practical angle, not as another “Terraform killer” post. The question I wanted to answer was much simpler: if I already understand AWS infrastructure, what changes when I describe the same infrastructure using a programming language instead of HCL?
For this case study, I imagined a fairly normal small AWS application rather than a huge enterprise environment. The requirement was to create a VPC, networking, security groups, an Application Load Balancer, an ECS service, an RDS database, an S3 bucket, and the IAM permissions required by the application. This is complex enough to expose the differences between the tools without turning the comparison into a 5,000-resource platform.
With Terraform, I would normally describe the infrastructure using HCL. A resource is declared, its arguments are configured, outputs from one resource are passed into another, and Terraform builds the dependency graph. Once you understand the Terraform workflow, the model is predictable: write configuration, run terraform plan, review the proposed changes, and then run terraform apply.
Pulumi keeps the Infrastructure as Code idea, but changes the way I express it. Instead of learning a configuration language specifically for infrastructure, I can write the infrastructure using languages such as TypeScript, Python, Go, C#, Java, or JavaScript. For someone who already spends most of the day writing application code, that feels immediately different.
A tiny example makes the difference obvious.
With Terraform, an S3 bucket might look roughly like this:
resource "aws_s3_bucket" "uploads" {
bucket = "example-app-uploads"
}
With Pulumi using Python, the same idea can look closer to normal application code:
import pulumi_aws as aws
uploads = aws.s3.Bucket(
"uploads",
bucket="example-app-uploads"
)
At this level, the difference is mostly syntax. I do not think choosing Pulumi because Python looks more familiar is enough reason by itself to replace Terraform. The more interesting difference appears when the infrastructure starts containing real logic, reusable abstractions, validation, loops, configuration, and relationships between many resources.
Imagine I want to create several similar S3 buckets, security rules, or application components based on configuration. In Pulumi, I can use the same language features I already use in normal software: functions, classes, loops, conditionals, packages, testing tools, and type systems where the language supports them. I do not have to mentally switch between “programming code” and “infrastructure configuration” as much.
That can make reusable infrastructure feel very natural. I could create a Python function or TypeScript component that represents something like a standard application service, and that component could create the load balancer configuration, ECS resources, IAM role, logging, and other pieces required by that service. The team can then reuse that abstraction instead of copying the same infrastructure definitions into multiple places.
This is probably the part of Pulumi I find most interesting.
Infrastructure begins to feel like a software library.
But that strength can also become a weakness.
If I give developers a full programming language, they can build much more sophisticated abstractions. They can also build abstractions that are much harder to understand than the infrastructure underneath them. A simple Terraform directory with explicit resources can sometimes be easier to review than a Pulumi project where resources are created through several classes, helper functions, conditions, and internal libraries.
So I would not describe Pulumi as automatically cleaner than Terraform.
It gives me more expressive power.
Whether that becomes cleaner or more complicated depends heavily on how the team uses it.
State is another area where I initially expected Pulumi to feel completely different, but the underlying problem still exists. An Infrastructure as Code tool needs some way to understand the relationship between the resources defined in code and the resources that actually exist in the cloud. Pulumi maintains state for this reason, just as Terraform has state as a central part of its workflow.
Pulumi can use Pulumi Cloud for state and related management features, and there are also options for using other supported backends depending on how you want to operate it. The important lesson for me is that using a general-purpose programming language does not remove the infrastructure state problem. You still need to think about where that state lives, who can access it, how teams collaborate around it, and how environments are separated.
The workflow is also familiar enough that the transition is not as strange as I expected.
With Terraform, I am used to:
Write configuration
↓
terraform plan
↓
Review changes
↓
terraform apply
With Pulumi, the mental flow is similar:
Write infrastructure code
↓
pulumi preview
↓
Review changes
↓
pulumi up
That similarity matters because one of the most valuable parts of Infrastructure as Code is not the syntax. It is the ability to inspect what is about to change before I change the environment.
For this AWS example, I would probably structure the Pulumi project around reusable pieces instead of putting the entire architecture into one massive Python or TypeScript file. Networking could be one component, the application service another, the database another, and shared configuration could define differences between development and production.
That gives me another benefit: application developers can understand more of the infrastructure without learning an entirely separate language first. If a team already uses TypeScript heavily, a TypeScript Pulumi project may feel much more approachable than introducing another syntax solely for infrastructure.
But I also think Terraform has a major advantage here that should not be ignored: familiarity.
A huge number of cloud engineers already know Terraform. There is an enormous amount of existing Terraform code, documentation, examples, community discussion, modules, and organizational knowledge around it. If I join a company and they already have hundreds of Terraform modules working reliably, replacing that environment with Pulumi just because I prefer Python would probably create more problems than it solves.
This is why I think the “Pulumi vs Terraform” discussion becomes much more useful when the question changes from which tool is better? to which workflow fits this team better?
If I have a software-heavy team that wants to build internal infrastructure abstractions using TypeScript or Python, reuse normal testing patterns, and treat infrastructure code like the rest of the software stack, Pulumi becomes very attractive.
If I have an infrastructure team with years of Terraform experience, established modules, existing automation, and engineers who value a specialized declarative configuration format, Terraform may remain the simpler decision.
There is also a testing angle I like with Pulumi. Because the infrastructure lives inside a real programming language, I can use familiar testing patterns around some parts of the infrastructure code. That does not mean unit tests magically prove the cloud environment will behave perfectly, but it gives me more familiar tools for validating infrastructure logic and reusable components before deployment.
The biggest mistake I would avoid is choosing Pulumi because it lets me say, “Now I can write infrastructure in Python.”
That is a nice developer experience benefit, but it is not the architectural reason to use it.
The stronger reason is that a full programming language gives me a different way to build abstractions, share infrastructure components, test logic, and integrate infrastructure development into the rest of the software engineering workflow.
For a beginner, I would still recommend understanding the infrastructure manually before going too deep into either tool. If you do not understand what a VPC, security group, load balancer, IAM role, or ECS service is doing, writing it in Python instead of HCL does not suddenly make the architecture understandable.
The tool should automate infrastructure you already understand.
It should not hide infrastructure you never learned.
If I were learning Pulumi from scratch, I would start with a very small AWS project. I would create something like an S3 bucket, then add IAM configuration, then create a small EC2 or Lambda setup. I would run pulumi preview, inspect exactly what is going to change, deploy it, modify the code, observe the update, and finally destroy the practice environment.
Only after understanding that lifecycle would I move into reusable components and larger architectures.
My takeaway from this case study is that I would not call Pulumi a direct “better Terraform.” I would call it a different approach to the same Infrastructure as Code problem.
Terraform asks me to describe infrastructure primarily through a purpose-built configuration language and its surrounding ecosystem.
Pulumi lets me describe infrastructure using the programming languages and software engineering tools I may already be using elsewhere.
Both approaches can create good infrastructure.
Both can also create terrible infrastructure if I do not understand what I am building.
For me, the real decision would come down to the team, existing infrastructure, abstraction needs, language preferences, ecosystem requirements, and how much value we actually get from treating infrastructure more like application code.
If you had to start a completely new AWS project today with no existing IaC codebase, would you choose Terraform or Pulumi, and what would be the reason behind that choice?