Choice 06. By Nicolas Dao

Pulumi or Terraform?

We're putting our infrastructure in code. Pulumi or Terraform, and what if we already run one of them?

Pulumi for new work: real code, state in your own S3 bucket, and every Terraform provider through its bridge, so it costs no coverage. Terraform when the client's team already runs it well; I keep it. Often both: Pulumi already runs on Terraform's providers, and new pieces go in Pulumi next to a client's existing Terraform, each resource with one owner.

When to pick which

Pick Pulumi when

  • New work with no Terraform history: the same providers, in a real language.
  • Infrastructure with real logic: resources generated per tenant, shared components with options.
  • One codebase across vendors: AWS, Cloudflare, GitHub, Tailscale, Neon, Resend, PostHog, OpenRouter, Stripe.

Pick Terraform when

  • The client's team already runs Terraform.
  • A platform team has working modules, policies and review habits in HCL: rewriting infrastructure that works is a waste of time.
  • OpenTofu rather than Terraform when an open-source licence matters to the client, or you want state encryption built in.

Both, when

  • Pulumi already uses Terraform's providers: anything with a Terraform or OpenTofu provider is provisionable from Pulumi code.
  • At a client that runs Terraform, it keeps what it owns and new pieces go in Pulumi, which reads outputs straight from Terraform state.
  • Never two owners for one resource. When a module is worth moving, convert it and import its resources into Pulumi, then Terraform lets it go.

Questions that change the answer

  • Does the team already run Terraform, and run it well? Then I keep it and add Pulumi only for new pieces.

  • Does the infrastructure have logic: per-tenant resources, shared components with options? count, for_each and dynamic blocks get painful fast.

  • Does an open-source licence matter to the client? Terraform is under the Business Source License; OpenTofu is MPL, under the CNCF.

  • Who runs the applies: you on a small stack, or CI on every merge? It decides whether GitHub Actions is in the picture.

The short version

Pulumi Terraform / OpenTofu CloudFormation
Verdict My default Use when it fits Never
Written in Real code HCL configuration YAML or JSON templates
Logic Loops and conditions in the language count, for_each, dynamic blocks Fn::If, Fn::Sub, Conditions
Reach Every Terraform or OpenTofu provider, bridged Every provider AWS only
State Your own S3 bucket, no Pulumi Cloud fees S3 with use_lockfile, or HCP Terraform Held by AWS
Secrets in state Encrypted with KMS Plaintext, sensitive values included; OpenTofu can encrypt No state file
Licence Open source Terraform BSL since August 2023; OpenTofu MPL, CNCF since April 2025 AWS service
Price Free with an S3 backend Both CLIs free; HCP Terraform free up to 500 resources Free for AWS resource types

Price doesn't decide this one: all three are free to run the way I run them. The language, the reach and the state decide it.

Why Pulumi is my default

Every piece of infrastructure I build is defined in Pulumi, with state in S3, and I've had no unsolved pain with it. Terraform is config files; Pulumi is real code. When infrastructure has logic, a loop is a loop and a shared component is a function with options.

The part people miss is that Pulumi can use any Terraform provider. One command, pulumi package add terraform-provider, generates an SDK from it. So choosing Pulumi costs me no coverage, only HCL. That's what lets me say "if I can't provision it, it loses points" and still cover nearly the whole market. Pulumi everywhere shows one app across six providers.

What it asks of you: pin community providers, which are often one person's project (Neon, Resend, Clerk). Use @pulumi/aws-native next to the classic provider for new AWS services that land there first. Build Cloudflare Workers with Wrangler and deploy the output with Pulumi, with one owner per Worker.

When I keep Terraform

Terraform is fine, and its providers power half of Pulumi's ecosystem. I tried it and prefer Pulumi, but when a client already runs it, I keep it. Rewriting working infrastructure to suit my taste is the kind of consultant waste I criticise. A team that's happy and fluent in Terraform doesn't need me to move it.

Terraform or OpenTofu? Both work, and the licence change doesn't stop a client using Terraform. For a new HCL codebase I'd lean OpenTofu: an open-source licence under the CNCF, and state encryption built in. Pick one per codebase, pin its version in CI and on every laptop, and never run both binaries on the same state.

Its traps: state holds every secret in plaintext, so it goes in an encrypted S3 bucket with tight access. The S3 lockfile that replaces DynamoDB locking is off by default: set use_lockfile = true. And commit .terraform.lock.hcl, with an exact version on every module, or modules upgrade themselves on the next init.

When it's both

Side by side, not a rewrite, and every resource has one owner. The client's Terraform keeps what it owns, new work starts in Pulumi on the same providers, and Pulumi reads Terraform's outputs from its state, so nobody copies values by hand. When a module is worth moving, pulumi convert --from terraform translates the HCL (Pulumi expects 90–95% to convert cleanly and marks the rest), and pulumi import --from terraform adopts the live resources without recreating them.

Either way, the pipeline is the same. With GitHub Actions, every pull request runs a preview (pulumi preview posted as a comment, or a plan), every merge applies, and a concurrency group per stack stops two applies fighting over one lock. On a small stack I still read the preview and run pulumi up myself.

Why not CloudFormation

CloudFormation is Never. It does real things well: AWS holds the state, rollback is automatic, StackSets reach every account. But templates are configuration pretending to be code, they hit hard limits, and they stop at AWS's edge while every client also has Cloudflare, Neon, GitHub or Tailscale. The CDK fixes the format with real code, but it compiles to CloudFormation and inherits the same engine, limits and stuck rollbacks.

When I meet it at a client, I don't rip it out in a week. A vendor's template gets deployed from Pulumi as one resource (aws.cloudformation.Stack). Old stacks get imported into Pulumi one at a time, then deleted with their resources retained.

Sources, checked on 25 October 2026: use any Terraform provider, Terraform S3 backend, sensitive data in Terraform, OpenTofu state encryption, migrating from Terraform to Pulumi, CloudFormation quotas, migrating from CloudFormation to Pulumi, Pulumi with GitHub Actions.

More choices

  1. 01 Lambda, Web Functions or Workers? Where should this piece of logic run?
  2. 02 Lambda, Fargate or EC2? My Lambda is busy all day, or its reserved concurrency keeps going up. When is it time to move to a container, and where is the line?
  3. 03 SQS, EventBridge or SNS? Something happens in my app and other code must act on it. Do I put it in a queue, on an event bus, or on a topic?
  4. 04 SQS retries or Durable Functions? A job calls something flaky (a third-party API, an LLM, a slow Postgres). How do I retry it reliably, and when do I reach for Lambda durable functions instead of tuning SQS?
  5. 05 pgvector or S3 Vectors? Where should my embeddings live?
  6. 07 Cloudflare or S3 and CloudFront for a website? Where should a website, SPA or PWA live, Cloudflare static hosting or S3 behind CloudFront, and where do its heavy files go?
  7. 08 CloudWatch and Firehose, side by side How do I keep chatty logs without a CloudWatch-sized bill?
  8. 09 Athena or DuckDB? How should I query my data lake, and what changed when AWS bought DuckDB's company?
  9. 10 Neon or Aurora? I need Postgres for an app on AWS. When is Neon the right call, and when does it have to be Aurora?
  10. 11 Better Auth or Cognito? Which sign-in should my app use, and what do I actually get for free?
  11. 12 Why I ended up on AWS Which cloud should my company run on, and is AWS just the one you know?
  12. 13 Better Auth, run yourself or managed by Neon? My app is on Neon. Should I run Better Auth myself, or switch on Neon Auth, which is Better Auth managed by Neon?
  13. 14 Stripe or PayPal Should we take payments with Stripe or PayPal, and when is PayPal worth the pain?
  14. 15 Tailscale or Cloudflare Tunnel? I need to reach a private box or VPC, or put a service from it online. Is that Tailscale or Cloudflare Tunnel?
  15. 16 Resend or SES? Our app sends email, and maybe a newsletter. Resend, or Amazon SES in our own AWS account?
  16. 17 WhatsApp or Telegram Our bot should reach users in a messaging app. WhatsApp or Telegram, and why did yours get blocked?
  17. 18 Which model, at what price Claude, GPT or an open model? Which is the cheapest model that's good enough, tier by tier?
  18. 19 Function URL or API Gateway? My Lambda needs an HTTPS endpoint. When is a function URL enough, and when do I start on API Gateway from day one?

Want this stack in your AWS account?

Cloudless Labs is a Sydney consultancy. We migrate on-premises systems to AWS, build serverless apps on this stack, and coach teams to ship with Claude Code and Codex.

Contact Us

For general inquiries, please contact us at:

For e-learning capabilities, please contact: