Mastering the Core 20% of Pulumi: A Platform Engineer's Guide

G'day, cloud enthusiasts! If you're diving into the world of Infrastructure as Code (IaC), Pulumi offers a refreshing approach by letting you use familiar programming languages rather than domain-specific ones. Today, I'll focus on the vital 20% of Pulumi concepts that will handle 80% of your day-to-day infrastructure needs.
1. Resources: The Building Blocks
At its core, Pulumi revolves around resources - the cloud components you're provisioning. Whether it's an AWS S3 bucket, an Azure App Service, or a GCP Compute instance, understanding how to declare and configure resources is fundamental:
import * as azure from "@pulumi/azure";
// Create an Azure Resource Group
const resourceGroup = new azure.core.ResourceGroup("my-resource-group", {
location: "Australia East",
});
The pattern is consistent: you import a provider package, then instantiate resources with a logical name (used by Pulumi) and a configuration object.
2. Stacks: Environment Management Made Simple
Stacks are Pulumi's way of managing different environments (dev, staging, production) or deployment targets. They're isolated instances of your infrastructure that share the same code but can have different configurations:
# Create and select different stacks
pulumi stack init dev
pulumi stack init prod
pulumi stack select dev
Each stack maintains its own state file and configuration values, making it easy to promote changes through your environments while keeping configuration separate.
3. Configuration Management
Speaking of configuration, Pulumi offers a robust system for managing environment-specific settings:
// Import the configuration module
import * as pulumi from "@pulumi/pulumi";
const config = new pulumi.Config();
// Get configuration values with defaults
const dbSize = config.getNumber("dbSize") || 20;
const environmentName = config.require("environmentName"); // Will error if not set
Set these values using the CLI:
pulumi config set dbSize 50 --stack prod
pulumi config set environmentName production --stack prod --secret
The --secret
flag ensures sensitive data is encrypted. Simple, yet powerful.
4. Outputs and Inputs: Handling Dependencies
Pulumi's dependency management is driven by its Output system, which handles asynchronous resource creation and dependencies between resources:
// Create a resource that depends on another
const storageAccount = new azure.storage.Account("storage", {
resourceGroupName: resourceGroup.name, // This creates a dependency
accountReplicationType: "LRS",
accountTier: "Standard",
location: resourceGroup.location,
});
By referencing resourceGroup.name
(an Output), Pulumi automatically knows that the storage account depends on the resource group and orders operations accordingly.
5. Component Resources: Reusable Infrastructure Patterns
When you need to group related resources together, Component Resources are your friend:
import * as pulumi from "@pulumi/pulumi";
import * as azure from "@pulumi/azure";
class WebService extends pulumi.ComponentResource {
public readonly endpoint: pulumi.Output<string>;
constructor(name: string, args: any, opts?: pulumi.ComponentResourceOptions) {
super("pkg:index:WebService", name, {}, opts);
const appService = new azure.appservice.AppService(`${name}-app`, {
// configuration here
}, { parent: this });
this.endpoint = appService.defaultSiteHostname;
this.registerOutputs({
endpoint: this.endpoint,
});
}
}
// Use it in your code
const service = new WebService("my-service", {
// args here
});
export const endpoint = service.endpoint;
Component Resources help you encapsulate and reuse infrastructure patterns across projects.
6. Pulumi State Management
Pulumi stores state (the mapping between your code and deployed resources) in the Pulumi Service by default, though you can configure it to use alternatives like Azure Blob Storage or AWS S3:
# Configure state backend (example for Azure)
pulumi login azblob://pulumi-state
Understanding how Pulumi tracks state is crucial for collaborative work and troubleshooting.
7. Using Existing Resources with Import
Sometimes you need to bring existing infrastructure under Pulumi management:
// Import an existing resource
const existingResourceGroup = azure.core.ResourceGroup.get("existing-rg", "/subscriptions/sub-id/resourceGroups/existing-name");
This lets you gradually adopt Pulumi without rebuilding everything from scratch.
Conclusion
These seven concepts represent the critical 20% of Pulumi knowledge that will serve you in the vast majority of infrastructure scenarios. By mastering resources, stacks, configuration, outputs, component resources, state management, and imports, you'll have a solid foundation for building sophisticated infrastructure with less cognitive overhead.
As you grow more comfortable with these concepts, you can explore advanced features like Policy as Code, automated testing, and custom providers. But start here, and you'll be productive with Pulumi faster than you might expect.
Happy coding, and may your infrastructure be ever idempotent!
The author is a Senior Platform Engineer working across GCP and Azure, specializing in Infrastructure as Code solutions.
Subscribe to my newsletter
Read articles from Joby directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
