I have used Terraform professionally and in hobby things every now and then for couple of years now (most recently OpenTofu). I have tolerated it due to the ecosystem (as mentioned in an earlier blog post), but I have never particularly liked it. Why?

The reasons are pretty much the same as why I am not a fan of Helm charts either.

DSLs are not expressive enough, nor powerful enough

Making something ‘human friendly’ (read: huge pile of YAML for devops people) is overrated. The cost of doing that is that automatically validating and formatting it becomes tricky, and the expressed things are mostly too inaccurately defined (‘sure, this is a string, but you are supposed to enter an URL here’). The tooling usually does not help much either, as while programming languages have widespread support in editors, DSLs most of the time do not. Custom configuration languages are not usually much better - being limited by design is not great, nor is it great for integrating with ‘other’ things which use real programming languages.

Another problem is repetition; there are reuse concepts within the DSLs (e.g. Terraform modules, Helm sub-charts) but they are frequently somehow weirdly limited (Helm chart cannot currently refer to multiple copies of a sub-chart with different parameters;) or just downright frustrating to use (Go templating within YAML leads to hilarity when you need to care about how much whitespace you either insert, remove, or accidentally introduce).

Infra as (real) Code, or Pulumi

There has been infrastructure provisioning tools in the past too which have involved real programming languages. The downside with them has been usually the lack of ecosystem support.

Pulumi solves this beautifully by reusing Terraform providers. The outcome is that you can program your infra in a real programming language (with proper reuse constructs and validation), and leverage big ecosystem of modules to deal with the diverse infrastructure ecosystem out there. This allows you to also integrate also with other things using programming language of your choice.

Another cool aspect of Pulumi is that it actually supports converting Terraform configuration files to Pulumi code using pulumi convert CLI. This produced in my experience ‘mostly’ (~90%) correct code, which with some tuning and lot of restructuring was quite nice. The problem with the structure is that 1:1 rewrite of Terraform to programming language is quite ugly and you really want to e.g. split shared functionality to functions, and possibly also introduce more data structures (and use less code).

Language choice in Pulumi

Pulumi officially supports (at least) 5 languages:

  • TypeScript
  • Python
  • Go
  • C#
  • Java

Unfortunately they are not quite equal in terms of support. A lot of the documentation notes only examples in TypeScript and-or Python, and the rest of the languages are left to ‘please use Pulumi AI to get the example’.

I am personally not a huge fan of TypeScript so that was out by default. I do not have much of a C# or Java codebase either, so I evaluated Go and Python, converting a small Terraform project to both, and comparing what it looked like.

Go version was quite a bit more verbose than Python one, but Python one was broken number of times and Go one just worked out of the box once compiler was happy. Due to that, I chose to use Go for all of my Pulumi use for now. This led to number of issues:

Go and Pulumi - not quite ideal match

A lot of search engine hits are polluted with ‘Pulumi AI’ results and they are unfortunately not the best. Here are couple of issues and annoyances I encountered during the last couple of months (and I may have forgotten some):

  • A lot of search hits are about Helm chart v3; v4 is much better (dated information)
  • A lot of search hits are actually wrong Pulumi AI generated examples (e.g. pulumi.Map <> Go map being used directly; this does not work)
  • The data structure handling in Pulumi AI examples and somewhere elsewhere is quite ugly; explicit type conversion is needed to convert Output type to arbitrary Input subtypes every now and then, for example.
  • Go’s error handling makes it quite verbose
  • Lack of brief lambdas or equivalent makes code quite verbose (and unfortunately many examples refer to TypeScript lazy evaluation constructs for which there is no Go equivalent)

Other Pulumi warts

Monetization

Monetisation scheme of Pulumi is somewhat frustrating; While Pulumi Cloud is nice enough service, if you use it only for state storage, it is quite expensive for what you get. Without it, you are in for some pain as some things are not documented that well.

When not using Pulumi Cloud, the performance of S3 backend is apparently quite bad as it is accessed serially. However, an issue to address this has been left unaddressed for years, presumably for fiscal reasons.

Lack of documentation

For example, the StackReference, when doing self-hosted (‘free’) Pulumi does not have any documentation I could discover how it actually works. Ultimately it boils down to stack reference having hardcoded organization organisation, and then you can actually form the reference using format organization/<project>/<stack>, for example in my home infrastructure I refer to the dev version of my fw stack as organization/fw/dev.

Similarly when setting up AWS setup from scratch this week, I wound up with lots of examples and Pulumi AI generated code which did not work, or quite do what I wanted. This is probably somewhat due to underlying Terraform provider having lacking documentation, but the outcome is still somewhat aggravating. It took me couple of hours to ultimately write less than 200 lines of quite verbose Go infrastructure code. I guess it gets better eventually, although the modules’ own documentation is somewhat lacking so it feels like a lot of work right now.

Am I content with Pulumi?

So far, yes. It seems to be the best option readily available and i am using it both in my hobby projects as well as in my current startup. I reserve my right to change my opinion going forward though.

There will be another post which describes what I actually do with Pulumi sooner or later (I already wrote it, but this got too long so I split this to two parts)