So with about a year of Terraform under my belt I discovered Pulumi. Now with a lot of both Terraform & Pulumi experience under my belt I thought it would be a good time to share my thoughts on how they stack up. For those of you who don't already know what Pulumi it is another IaC(Infrastructure as Code) solution that is much newer than Terraform only launching v1 in September 2019. Its main difference is that it leverages your preferred programing language to write out the definition of your infrastructure. Today it supports Python, Typescript, .NET, and Go. I cannot really speak for all of them here, but I can highly recommend Typescript over JavaScript for this. Typescript is extremely well suited for infrastructure as code. It makes it so you can easily navigate massive classes with intellisense to find the one property on the specific single resource you’re searching for. Not to mention the bugs you will eliminate with your linter before they exist. I suspect Go and Python could accomplish this too, but vanilla JavaScript just is not strict enough for this type of program. Now let’s dive into my comparisons. This is a little high level but hopefully it will give you some insight into some details that took me months of working with Pulumi to fully understand. I have notably left out Pulumi's SaaS offering as I have not used it. Open source only for me.
Where Its Better
No HCL
This is definitely subjective to the type of user but I HATE HCL (HashiCorp Configuration Language). At first glance HCL is a declarative language but only in spirit because HashiCorp decided to mix in all kinds of functions and scripting features in there rather than leveraging something proper for the extra heavy lifting. This creates a language that really has no good current IDE or linter for, so you basically must just jump back and forth from the code to the console to work out every typo and mistake when it doesn't compile. The best one is the VSCode extension, but it always seems behind the updates it will not run on any of my larger projects.
Real Programing Languages
With HCL out of the picture your tooling and development environments really open up. Python and Typescript have massive community support. You never have to type a thing without fulling linting and autocomplete. No more jumping back and forth between the docs and your code because every property has the same description in on the property right in your editor that you'll find in the online docs. Optional vs required properties are well defined and in the case of Typescript the interfaces even handle the conflicts between properties of a resource. For example, the linter won't be satisfied if you define both the networkInterface & privateIp on an EC2 instance. Furthermore, you will not be dealing with a handful of shorthand terraform functions to modify and resource variables. Instead, you have all the tools the language and any of your favorite libraries provide.
Automation API
Two words....GAME CHANGER.
Want to build your own command line tool around Pulumi to satisfy your workflow? What about controlling reusable stacks from your applications API? You can get/modify the config of a whole existing deployment and then run it all from a rest API. Use the Automation API to deploy new infrastructure right when a user requests it. All while still being tracked and maintained in the same Pulumi code base and state history. Even take it back offline to your workstation if you need to. Maybe use it to write a program that controls your dev environment. I plan on really getting into this one I want to see how far it can go.
Kubernetes
I am no expert at provisioning Kubernetes but I have now done it in Terraform and Pulumi now. Just trust me DON'T use Terraform its a bad idea. Your much better off just using your own raw yaml/Helm and using a bash script to apply. Trying to convert the specs to HCL is mind-numbing. There is no real easy way and its just a big waste of time. Pulumi’s k8s support on the other hand is MUCH better. You can write it out directly in your language of choice, which is not bad in typescript. Also, if you have complete deployments in yaml or Helm lying around its pretty strait forward to load the yaml with python or typescript and include it that way so you’re not wasting time converting perfectly functional deployments. There are some drawbacks I have encountered. Pulumi currently does not support Helm hooks & you will have to get used to passing Pulumi specific annotations to fine tune how Pulumi checks the health of resources before saying complete.
Custom Resources
When it comes to custom resources Pulumi is a very clear winner because you can still create resources in go just like you would for Terraform. You would use the Terraform SDK and then write type definitions for it. Who needs all that work though when you can just hack it out in your own preferred programing language with what Pulumi calls Dynamic Providers. I am going to be posting some practical examples on this as I found Pulumi's lacking but for now check out the link if you’re interested.
Resource Variables
This ties back to using a real programming language vs HCL but I wanted to point it out specifically. If you are using a lot of modules with Terraform, then you have loads of variable defined all over the place all referring to the same piece of data. The names can become inconsistent not to mention you have to write it out twice, once when calling the module and once in the modules tfvars file, every time you use it in a module. Anyone who has a large Terraform project knows what I am talking about. For me Pulumi is a breath of fresh air here. If you group your resource variables into large classes, it ends up being a couple large objects you pass around that contains everything. And most importantly there is only 1 instance of there is no module layer that severs the link to the original information. No more outputting every excruciating detail you need from a Terraform module. Now it’s just a matter of digging through your class structure to find what you need because it’s all at your fingertips.
Config Variables
In Terraform the variables of the root project are only accessable in the root files and not any of the used modules. As stated above this means you have to pass them all over the place and duplicate all this content. In Pulumi the config variables are stored at the in a big yaml file at the root of the project and accessible anywhere in your code no matter how many layers deep you go into modules. Secrets as also natively supported with a lot of different options for secret providers.
Preview
At initial inspection Terraforms preview looks great. When you’re first creating a couple resources it is detailed and easy to read. This however starts to break down when your project grows too large. A seemingly small change of adding a server can really blow up when everything that it requires access to needs a new rule in its security group. They can get so big it runs right off your console. At that point just a quick review of what your changes are touching become exhausting. Pulumi addresses this issue directly with a preview that only shows a high-level summary. You can always ask for more details and get an equivalent of the Terraform output but on a big apply its great to be able to quickly see if anything you didn’t expect to is getting modified. To further organize the preview Pulumi allows you to assign a parent to resources. I highly recommend you do this. It does not have any effect on the actual infrastructure, but it helps your deployment stay organized.
Previewing update (dev): Type Name Plan pulumi:pulumi:Stack parent + ├─ awsx:x:ec2:Vpc vpc create + │ ├─ awsx:x:ec2:Subnet public-1 create + │ └─ awsx:x:ec2:Subnet public-2 create
Replace Before Destroy
Has anyone else modified a resource, applied Terraform, and then had the resource fail to create after it already destroyed the original? Now you have a bug to might not know how to fix so you either have to figure it out or roll back. All while your original resource is gone. Pulumi instead does not do this. It creates the new replacement before it destroys the original. There is definitely some complexity to this especially to the naming and they explain all that here. For resources where this approach does not work you can always just set the "deleteBeforeReplace" property on the resource and it will behave like Terraform. Best of both worlds.
Where Its Similar
Cloud Provider Support
You don't need to worry about Pulumi being behind Terraform for the major cloud providers. Pulumi leverages the Terraform providers under the hood so it has updates almost immediately after they are available to Terraform.
Workflow, State Storage & Management
If your coming from Terraform all of this is going to feel very familiar. terraform apply = pulumi up. terraform destroy = pulumi destroy. Imports, moves, and deletes in the state are handled a little differently but nothing notable just slightly different ways of doing it. The state historical snapshots are stored automatically with the diff too which is nice for Pulumi. Automated change log maybe?? Terraform just tells you to turn on s3 versioning for that :/
Pulumi Stacks & Terraform Workspaces
These are pretty interchangeable features if you use them. Pulumi gets the edge slightly because it works better without that associated SaaS offering than Terraform allowing you to have a different configuration file for each stack.
Where Its Worse
Not as Mature
Pulumi is much younger than terraform and I can definitely say the core engine is a little buggier. It doesn't perform so great at times and is generally less polished than Terraform. Nothing that has ever made me consider going back to Terraform but keep it in mind. The project is extremely active though and patches are released all the time so this will get better with time.
No Open Source State Locking
Its in the title. Pulumi only currently supports state locking with their SaaS solution. If this is an issue for you maybe wait until they do or like me plan on using the Automation API to build a cli wrapper that does the locking. Hmmm That sounds like a pretty good idea for a blog post.
Much Steeper Learning Curve
Milage will vary on this depending on your previous experience. if you have extensive programming experience in one of the supported languages it will be much easier for you. If you do not it will definitely take some time to get up to speed. Pulumi makes use of all the tools available to it. In my opinion it is elegantly designed. The whole program runs to create the definition of the infrastructure you’re trying to create. This also means though that all your code runs without any of the properties actually being defined. They accomplish this by wrapping literally EVERYTHING in Promises and I am not going to lie its confusing until you get a hang of it. For example if you want to manipulate the raw value of a property at runtime you have to write what is basically a callback function using a '.apply'.
let cidrBlock = instance.privateIp.apply((privateIp) =>; { return '${privateIp}/32'; });
pulumi.interpolate is basically shorthand for this id all your doing is concatenating a string
let cidrBlock = pulumi.interpolate'${instance.privateIp}/32';
Now "cidrBlock" is of type "Pulumi.Output\<string>" which is basically a promise that won't send a value to the resource you use it in until it can be calculated. This is also how the dependencies work and how the engine knows how to create one thing before another. If you don't have much programming experience maybe take some free classes and do some self-learning before jumping right in. I also think no matter who you are that Terraform experience helps too. The main providers are identical and the whole workflow is similar as well. If your seasoned with Terraform and have some experience with one of the supported languages, you should feel right at home with Pulumi.
Smaller Community (For Now) & Third-Party Provider Support
Currently Pulumis community is significantly smaller than the giant community that Terraform boasts. With that said it is growing quickly and appears plenty active enough to have staying power. All of this does mean that there are less providers outside of the main cloud providers. They are getting added quickly because they just leverage the terraform ones, but they do need all of the type interfaces defined before they can be used in Pulumi.
Conclusion
Jeez sorry that got long!! I hope this helps everyone understand why Pulumi is well worth a try especially if your already comfortable with the IaC model. If you would call yourself a programmer can regularly write services in one of the supported languages I can't imagine you won't enjoy the power Pulumi can offer just by using real languages. If you are not really a programmer but use Terraform or anything similar, I encourage you to take the plunge you will eventually be able to create automation you just simply can't do in terraform. I really think Pulumi is designed extremely well. They took all the best parts of Terraform and then just replaced crappy parts.... HCL....ahem. Please keep in mind this is all just my opinion so if yours is different I would love to discuss below. With this out of the way I plan to get more technical with some real-life examples and gotchas that could help you with your own journey to automate infrastructure.
Pulumi vs Terraform