Things I Wish I Knew Earlier About Pulumi

At the time of writing, Pulumi is still kind of a new kid on the block.

Most people have used some flavor of infrastructure as code (IaC) tooling. Terraform as the most common cross-cloud solution, or a cloud-focused one like specific tooling around CloudFormation (like troposphere, or CDK).

Pulumi’s main selling point vs Terraform is, that you can code it in a language you already know - TypeScript and Python for example, instead of Terraform’s HCL.

I found that value proposition intriguing enough to give Pulumi a serious try the last few months. While doing so, I stumbled upon a few details you won’t find clearly stated in the docs, in basic-focused articles or in talks about it (at least to my knowledge).

Here are some of the things I have learned, with the hope that it will help to make your mid-term Pulumi experience smoother. The examples below focus on TypeScript, but the points themselves are language-independent.

Building A Broad Understanding In The Beginning

This is something I wish I had done sooner.

The Pulumi artchitecture docs are pretty good for a broad overview. If you haven’t taken the time to read through them, it’s a worthwhile use of your time.

This could be a personal bias. I like top-down approaches to learning about things quite a lot.

Understanding terms and concepts can go a long way to help avoid confusion.

If you want a guided all-around tour with a few practical hints, the best video resource I found is this YouTube playlist.

Terraform Modules? Nope. Use Importable Packages.

Terraform has a cool way of making parts of your infrastructure code into reusable blocks. Just write a Terraform module, and reference it from another part of your codebase. No hassle. Here’s how it looks like (taken from their docs):

module "consul" {
  source = "github.com/hashicorp/example"

In Pulumi, you have to do approach things differently. Either you have importable code in your infra repository, or you have to publish a (private) package to your npm registry of choice (in case of TypeScript).

Component Resources

You might want to be using Component Resources if you are building reusable or complexity-encapsulating components.

Of course, you can just use a Python/TypeScript function to create Pulumi Resources. They won’t be grouped together in a Component Resource resource, which feels less clean and tidy.

Here’s the gotcha though: if you start with functions and choose to transition towards Component Resources later on, you’ll have to have those resources recreated, or to specify aliases.

Note: thanks a lot to /u/mdaffin for pointing to the alias solution!

The transition is not impossible, but it feels like a hassle. I think, I would choose to encapsulate resources as early as possible if they feel like a groupable and abstractable-away building block in the future.

// either
function creteSomeResources() {
    // resources go here    

// or
class SomeComponent extends pulumi.ComponentResource {
    // resources go here...

Say No To Default Providers - Eventually

Well, or at least consider being explicit with them. If your setup is simple, there’s no need to do this.

Providers in this context, are the settings through which Pulumi interacts with your cloud(s) of choice. Think stuff like the default region.

Once you start to juggle different regions and similar details, you’ll want to switch to specifying explicit providers, instead of relying on the default ones.

There’s a recent setting which you can add to your Pulumi configs to disable one, or all default providers:

- aws
# or
pulumi:disable-default-providers: [“*”]

From then on, Pulumi will start complaining if you’re not passing explicit providers. Being explicit prevents nasty gotchas down the line when working with more complex setups.

In TypeScript, you’d need to add a [provider argument]() your resources:

const bucket = new aws.s3.Bucket("name", {
    // params left out
}, { provider: yourExplicitProvider });

Keep ‘Depends On’ In Mind

Most of the time, Pulumi will figure out what Resources need to be created or deleted first. It looks at what outputs are being passed, and builds a dependency tree.

Sometimes, it doesn’t completely work out though. If you have parts which rely on each other, but it’s not completely obvious to Pulumi, you will run into flakiness and weird errors.

In that case, it can really help to add a few explicit dependencies between those problematic resources! This will make sure that the foundation and the house will be built/destroyed in the right order.

const bucket = new aws.s3.Bucket("name", {
    // params left out
}, { dependsOn: [otherResource]] });

Using Terraform Providers Can Be A Hassle

Pulumi has been using Terraform providers under the hood in the beginning, but has pulled up with some native providers not long ago.

The native providers are more up-to date than something like CloudFormation is for AWS when it comes to recently released features sometimes!

Note: how does it compare to Terraform providers? I’d assume they tend to lag behind in comparison, but I digress…

However, if you want to get fancier and make use of an existing Terraform provider, which nobody else has built a bridge into Pulumi yet, you’ll have to do it yourself. I haven’t tried it myself, but this seems like a slight downside you should be aware of, if you want to make use of the Terraform ecosystem.

Especially if you’re using recent products which offer a Terraform provider, there might not be a low-hassle way to use it in Pulumi.

I haven’t tried the bridging process myself. Maybe it’s fairly straightforward? Seems like it has potential for being busywork and maintenance overhead though.


  • /u/ChapterIllustrious81 pointed out, that Terraform refreshes the state by default, while Pulumi doesn’t. An important detail to know if you’re used to the refreshes.

In Conclusion

That’s about all I have learned from my first few months with a non-trivial Pulumi use case.

I hope that you can use those pieces of information to structure your setup in a more robust manner and avoid unnecessary export/import iterations.

My own search for focused & somewhat advanced resources around best-practices and working with Pulumi haven’t been fruitful unfortunately. Neither the-right-level-of-advanced books, nor courses or useful conference talks have come up. If you know of some, I’d be very curious to hear of them!