Trying Pulumi

2025/06/13

I finally described my cloud infrastructure in a declarative configuration. It’s a modest setup: a DNS zone, this blog, and a couple of VM instances. Terraform, which I use at work, was my initial choice. I quickly imported the existing resources, but the resulting code didn’t spark joy. It felt too verbose and lacked expressiveness. HCL still relies on for_each and count for even basic flow control.

Looking for alternatives, I chose Pulumi because it supports configuration in general-purpose programming languages like Typescript, Go, or Java. Lisp dialects weren’t supported, so I settled on Typescript. Emacs with LSP support provided a great developer experience, offering contextual autocomplete and easy access to comprehensive documentation.

For secrets management, Pulumi offers ESC, which requires account registration and is dependent on their cloud. Since I don’t need RBAC or anything complex, I replaced it with SOPS. A small helper script (direnv/lib/sops.sh) injects the secrets into the shell environment when I cd into the pulumi directory.

Pulumi is a clear winner for me. Writing infrastructure code in a proper language is liberating, and I appreciate how pulumi stack neatly organizes my infrastructure view.

Current stack is dev:
    Managed by thinkpad
    Last updated: 59 minutes ago (2025-06-13 07:42:44.338221653 +0200 CEST)
    Pulumi version used: v3.175.0
Current stack resources (40):
    TYPE                                                            NAME
    pulumi:pulumi:Stack                                             terraform-dev
    ├─ components:index:Cloudflare                                  cloudflare
    │  ├─ cloudflare:index/zone:Zone                                sarg.org.ru
    │  │  ├─ cloudflare:index/dnsRecord:DnsRecord                   MX 2
    │  │  ├─ cloudflare:index/dnsRecord:DnsRecord                   CNAME mail
    │  │  ├─ cloudflare:index/dnsRecord:DnsRecord                   MX 3
    │  │  ├─ cloudflare:index/dnsRecord:DnsRecord                   TXT dmarc
    │  │  ├─ cloudflare:index/dnsRecord:DnsRecord                   MX 1
    │  │  ├─ cloudflare:index/dnsRecord:DnsRecord                   TXT spf
    │  │  ├─ cloudflare:index/dnsRecord:DnsRecord                   MX 0
    │  │  └─ cloudflare:index/dnsRecord:DnsRecord                   TXT dkim
    │  ├─ cloudflare:index/workersScript:WorkersScript              blog
    │  ├─ cloudflare:index/workersScript:WorkersScript              tgbot
    │  ├─ cloudflare:index/workersCustomDomain:WorkersCustomDomain  blog
    │  └─ cloudflare:index/workersCustomDomain:WorkersCustomDomain  tgbot
    ├─ components:index:Google                                      google
    │  ├─ gcp:organizations/project:Project                         default
    │  ├─ gcp:projects/service:Service                              services-apikeys.googleapis.com
    │  ├─ gcp:projects/service:Service                              services-serviceusage.googleapis.com
    │  ├─ gcp:projects/service:Service                              services-generativelanguage.googleapis.com
    │  ├─ gcp:projects/service:Service                              services-cloudresourcemanager.googleapis.com
    │  └─ gcp:projects/apiKey:ApiKey                                gptel
    ├─ components:index:Selectel                                    selectel
    │  ├─ openstack:networking/network:Network                      private
    │  ├─ openstack:compute/keypair:Keypair                         ssh_key
    │  ├─ openstack:networking/subnet:Subnet                        private
    │  ├─ openstack:networking/router:Router                        router
    │  ├─ openstack:networking/port:Port                            vpn
    │  └─ openstack:networking/routerInterface:RouterInterface      interface
    ├─ components:index:Hetzner                                     hetzner
    │  ├─ hcloud:index/sshKey:SshKey                                ssh_key
    │  └─ hcloud:index/primaryIp:PrimaryIp                          vpn_ipv6
    ├─ pulumi:providers:telegram                                    telegram
    ├─ components:index:Telegram                                    telegram
    │  ├─ telegram:index/botCommands:BotCommands                    diogenisbot
    │  └─ telegram:index/botWebhook:BotWebhook                      diogenisbot
    ├─ pulumi:providers:openstack                                   default_5_1_0
    ├─ pulumi:providers:hcloud                                      default_1_23_0
    ├─ pulumi:providers:cloudflare                                  default_6_2_1
    └─ pulumi:providers:gcp                                         default_8_33_0

Current stack outputs (0):
    No output values currently in this stack

Use `pulumi stack select` to change stack; `pulumi stack ls` lists known ones