Pulumi’s Azure provider lets you manage Azure resources using familiar programming languages, but the real magic is how it treats infrastructure as code in a way that feels surprisingly dynamic.
Let’s watch it in action. Imagine we want to spin up a new Azure App Service. We’ll use TypeScript:
import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";
const resourceGroupName = "my-dynamic-app-rg";
const appServiceName = "my-dynamic-app-svc";
// Create an Azure Resource Group
const resourceGroup = new azure_native.resources.ResourceGroup(resourceGroupName, {
resourceGroupName: resourceGroupName,
});
// Define the App Service Plan
const appServicePlan = new azure_native.web.AppServicePlan(`${appServiceName}-plan`, {
resourceGroupName: resourceGroup.name,
kind: "Linux",
sku: {
name: "B1", // Basic tier, 1 CPU, 1.75 GB RAM
tier: "Basic",
},
});
// Define the App Service
const appService = new azure_native.web.WebApp(appServiceName, {
resourceGroupName: resourceGroup.name,
serverFamily: "Node", // Specify the runtime stack (e.g., Node, Python, .NET)
siteName: appServiceName,
appServicePlanId: appServicePlan.id, // Link to the App Service Plan
httpsOnly: true, // Enforce HTTPS
});
// Export the default hostname of the App Service
export const defaultHostName = appService.defaultHostName;
export const defaultPrimaryKey = appService.defaultPrimaryKey;
When you run pulumi up with this code, Pulumi doesn’t just send a static template to Azure. It evaluates your TypeScript code, determines the desired state of your infrastructure, and then orchestrates the API calls to Azure to make it happen. It figures out dependencies automatically: it knows the WebApp needs the AppServicePlan, and the AppServicePlan needs the ResourceGroup.
The core problem Pulumi solves is the drift between your intent and your actual infrastructure. Manual deployments, ad-hoc scripts, or even declarative YAML files can become outdated, leading to inconsistencies. Pulumi’s approach anchors your infrastructure to a living, breathing codebase. You can use version control, run tests, and refactor your infrastructure just like any other application.
Internally, Pulumi maintains a "state file" which is a record of the resources it manages and their current properties. When you run pulumi up, it compares the desired state (from your code) with the current state (from the state file and Azure). It then calculates the minimal set of changes required to reach the desired state, creating, updating, or deleting resources as needed. This diffing and reconciliation process is what makes pulumi up so powerful and safe.
You control the infrastructure through the properties you define in your code. For the WebApp, you can specify the runtime, sku for the AppServicePlan, whether httpsOnly is true, and even advanced networking configurations. Pulumi’s Azure provider exposes a vast array of Azure resources and their configurable properties, mirroring the Azure API but in a type-safe, programmatic way.
The most surprising aspect is how Pulumi handles secrets. You can declare sensitive values directly in your code, like database passwords or API keys, and Pulumi will encrypt them in the state file and manage their lifecycle. When you need to access these secrets, Pulumi retrieves them securely, often making them available as outputs from your stack without ever exposing them in plain text in your source code or the default state file.
The next logical step is to integrate this dynamic App Service with a database, perhaps an Azure SQL Database, and manage its credentials securely through Pulumi.