user-icon Matthias Müller
10. December 2021
timer-icon 5 min

Why you should consider Pulumi over Terraform for your next project (Pt.1)

Having used Pulumi the last 2 years after switching from Terraform I want to share some of my experience by comparing both infrastructure as code tools and explain why I prefer Pulumi in most situations. In general, I have successfully provisioned infrastructure with both tools, but from my experience Pulumi makes it easier for me. In this blog post series I want to explain why this is the case.

HCL vs. GPLs

Let’s start with the main difference first, their language support. While Hashicorp developed a new domain-specific language (DSL) called HCL (HashiCorp configuration language) for Terraform, Pulumi supports various general-purpose languages (GPL) like Typescript, Python or Go. This fact plays a major part in how and when you should use a specific tool in my opinion. Regarding the language support there are two points I want to highlight: flexibility and maturity.

Flexibility

As I said before, Hashicorp developed a new DSL for Terraform. It’s specifically designed for describing infrastructure in a declarative and human-friendly way. Pulumi on the other hand supports multiple GPLs like Python or Go, that are designed to be used for various different tasks.

Although they look totally different at first glance, Pulumi and Terraform are both using a similar logical process and model for managing infrastructure. You first describe the state of your preferred infrastructure as code. Executing the code will result in a logical model of your infrastructure, which is almost completely independent of the language you used. The tools then take care of applying this model to the real world. 

The aim of both tools in the end is to have a clear declarative model on how your infrastructure should look like, but the language you use is different.

To better demonstrate that let’s have a look at some examples.

Let’s start with a simple setup to have a quick summary of some key features of both tools:


There are various keywords in Terraform like resource, which can describe one or more infrastructure objects, such as loadbalancers, virtual machines or databases. output can be used to expose values and variable defines input parameters to allow different configurations for the same codebase.

What I want to highlight here is the simplicity and efficiency of HCL. There is almost no boilerplate code and everything is well readable.

Next let’s have a look at the same infrastructure but written with Pulumi:


As you can see the configuration of the resources are almost the same in both tools. The main differences are language specific features like objects, variables or import of libraries.

I find both code snippets to be well readable but Terraform contains a bit less boilerplate code. For simple infrastructure without any logic I think both tools do a similar good job.

Since that’s not always the case, let’s have a look at something more complex next:


Implementing a conditional resource isn’t really well readable in Terraform. The easiest way is to use the count attribute, which wasn’t designed to be used like that. There are other alternatives based on your use case but they are usually way more complex than this.

Hashicorp discovered that the users of Terraform want more flexibility, which is why they added some new language features over the last few years mirroring classical features of GPLs, e.g. for_each to map a list into a list of resources.

On the other side you have Pulumi, which offers those features simply by supporting GPLs:


Using a simple if clause makes it much easier to implement this logic and better readable than the Terraform code in my opinion.

Another example for limitations in Terraform are optional attributes in objects. This is something all GPLs support, while it’s only an experimental feature in Terraform since 0.14.x. Not using the experimental feature means you have to define every attribute even if it’s null.

As I showed above Terraform really does a good job when having clear and simple infrastructure but reaches some limits as the complexity grows. They are working on it and added some new features over the last years, but it’s still not on the same level as Pulumi. That’s one of the reasons why I would recommend Pulumi in general, because it can handle both complex and simple infrastructure very well. After switching to Pulumi I also had to spent less time trying to find a way of implementing logic in my infrastructure code.

Maturity

The other point regarding language support I want to highlight is maturity. When I think of HCL, I immediately think of all the bugs I encountered while using Terraform. Here are two examples for that:

On the other side you have Pulumi with its mature GPLs, where you don’t have to worry as much about any of this.

I’m not saying Pulumi doesn’t have its own bugs as well, but I never experienced them so often and with that much impact as with Terraform.

Basic things like lists and objects not working can be quite challenging and time-consuming to work around.

Another evidence regarding missing maturity is the flexibility, which I already covered before. Terraform is still not mature enough to handle all the various use cases requested by the community very well.

Provider support

Migrating from Terraform to Pulumi was made a lot easier thanks to all the Terraform providers that are also available for Pulumi. Thanks to the pulumi-terraform-brige library many Terraform providers have been migrated to Pulumi. Since I could use the same provider again, it was pretty easy to describe the same infrastructure I had in Terraform with Pulumi.

Additionally to the providers migrated from Terraform you also have Providers unique to Pulumi like the Kubernetes provider, which I think is a lot better than its counterpart in Terraform. The Pulumi Kubernetes provider is directly built on top of the Kubernetes OpenAPI spec, which makes the resources schematically totally identical to the real Kubernetes resources and latest releases are available very fast. The Terraform Kubernetes provider on the other side uses his own implementation, which always needs additional time and work until new API versions are supported.

On the other side I also have to mention that bug fixes can take a bit longer, because they often need to be fixed in the Terraform provider first, before being migrated to Pulumi. That never really affected me since not staying on the latest version for a few weeks was never an issue. 

Another thing to mention are high level modules managed by the Pulumi team. A good example for that is the AWSX package, which contains a lot of wrappers around many core AWS ‘raw’ resources to make them easier and more convenient to use.

 

This has been part 1 of my blog post series. I hope you enjoyed it so far. In the next parts I will cover other topics like testability, migration between both tools, setup/maintenance cost and state management.

Comment article