Pulumi Best Practices
Best practices that every Pulumi developer should consider following in their Pulumi apps.
Stack Configuration
Pulumi provides first-class configuration management for your stacks. Use the commands available under the parent command pulumi config
to set, read, and remove config properties. If you several config properties it is sometimes helpful to create a config structure in your codebase that exports all the properties in your stack config file. This is better than having config.get()
or config.require()
calls peppered all throughout your code. Just remember to use the --secret
flag when dealing with secret stack config.
Avoid Direct Changes
This one's a little obvious but I'll state (heh) this anyway. Since Pulumi is tracking your infrastructure state you should avoid changing resources directly in your cloud provider's console. I am stressing "should" because there are times when you need to or it is the quickest way to handle an incident but you should always bring those changes into Pulumi's management as soon as possible. This can be done using the refresh
command. Some might even prefer running the refresh
command as a cron job in their CI or perhaps always refresh the state before applying any updates by passing the --refresh (alias: -r)
flag. For example, pulumi up -r=true
.
Failure to refresh your Pulumi stack's state with the cloud provider will result in a "drift"; a difference in the state of a resource between what Pulumi knows and what it actually is. The longer you leave it that way the greater the chance for issues to arise when you next go to update the resource using Pulumi, especially when you don't update the stack regularly because of any reason. A genuine reason is that the core resources (databases, VPCs etc.) in a stack may not change regularly and therefore months may go on before you need to update them.
Use CI
One of the first things to do when you are ready to create your first non-local/dev stack is to setup CI to update your stack. The Console provides you with indicators to know if you are running updates from your local machine or from a CI service. There are a handful of guides to help you setup Pulumi in your favorite CI.
Component Resources
A ComponentResource
allows you to encapsulate many resources under a parent resource. For example, you could create a component that allows you to deploy a container to Google Cloud's Cloud Run service and setup a basic monitoring dashboard for it. It's one of the key features of Pulumi that allows your infrastructure to evolve seamlessly.
It is recommended that component names use the format <package>:<module>:<type>
. This allows child resources (those marked with parent: this
in their resource options)
to appear as child resources in the CLI output as well; a visual cue that is helpful to know the order of resources.
CLI Version
Always use the latest version of the CLI. There are rare circumstances when you want to disable update prompts. However, in CI you could turn off update checks by setting the environment variable PULUMI_SKIP_UPDATE_CHECK
to true
. Just make sure that whatever mechanism you are using to install the CLI always installs the latest version. Again, unless you have a good reason to pin the CLI version.
The Pulumi team is constantly updating the CLI with a ton of goodness. It is true that by using the latest version you might end up taking in a breaking change that might break your CI or worse your infrastructure. But those occurrences are rare and the CLI has very good test coverage for many core functionalities. By pinning to a specific version you might end up creating a future upgrade task for yourself which you may never be able to get to and before you know it, you are a whole major version behind. So you should seriously consider using the latest version always.
Comments
Comments! This, too, may seem obvious at first but it is important to add helpful comments to your infrastructure code as much as possible. Give yourself context on why that firewall rule is needed or why you need to create a resource in a specific way. With infrastructure changes, it is very important for team members (and even the future you) to understand the reason behind decisions you make with your infrastructure. The worst case with a misunderstood change can result in resources being destroyed which is a nice segue for the next topic.
Protect Resources
Always protect your sensitive resources from native deletion protection offered on certain resources by the cloud provider. In addition to that, you should also consider protecting sensitive resources with the protect
resource option.
Resource Creation Inside Futures
By now you more than likely would have come across the .apply()
method and may have even used it in your own Pulumi project. There are situations where you are tempted to create a resource inside an .apply()
. That is almost always an anti-pattern. There are few, very few genuine reasons for breaking this rule. Almost every resource's properties accepts a Pulumi future as input which means you don't need to call .apply()
on a property in order to pass the unwrapped value to another resource. Pulumi will do it for you automatically. It is one of the reasons why you don't have to write special code to await the construction of a resource before the next one can be created using an output property from the first resource. This dependency is implicitly tracked by Pulumi.
If you come across situations where you are seeing that it is not possible to pass an output property as-is, it is likely an oversight in the SDK that should support accepting a Pulumi future (i.e. pulumi.Output
and pulumi.Input
) and you should try to submit an issue to the relevant Pulumi provider repo to have it updated. But in the meantime, your workaround is to