Pulumi Resources & Providers

Medium 25 min read

Resource Basics

Why Resources & Providers Matter

The Problem: Cloud infrastructure spans hundreds of services across multiple providers, each with different APIs and configuration options.

The Solution: Pulumi resources provide a unified programming model where every cloud service is a typed object with inputs, outputs, and lifecycle management.

Real Impact: With 100+ providers and thousands of resource types, you can manage your entire cloud estate from a single codebase.

Real-World Analogy

Think of resources and providers like a hardware store:

  • Provider = A specific brand/manufacturer (DeWalt, Bosch, Makita)
  • Resource = A specific tool from that brand (drill, saw, sander)
  • Resource Args = The specifications you choose (voltage, speed, size)
  • Resource Options = How you want the tool handled (warranty, delivery, protection)
  • Outputs = What you get back (serial number, actual specs, receipt)

The Resource Model

Custom Resources

Map directly to a single cloud resource like an S3 bucket, EC2 instance, or Azure SQL database.

Component Resources

Logical groupings of multiple resources that form a higher-level abstraction like a VPC with subnets.

Provider Resources

Configure how Pulumi communicates with a cloud provider, including credentials and regions.

Stack Resources

The root resource that represents the entire deployment, automatically created for every stack.

Provider Configuration

Provider to Resources Hierarchy
AWS Provider aws.s3.Bucket aws.ec2.Instance aws.rds.Instance aws.lambda.Function my-app-bucket web-server app-database api-handler Each resource type creates actual cloud infrastructure

Configuring Providers

provider-config.ts
import * as aws from "@pulumi/aws";

// Default provider (uses stack config for region)
const bucket = new aws.s3.Bucket("default-bucket");

// Explicit provider for a different region
const westProvider = new aws.Provider("west", {
    region: "us-west-2",
});

// Use the explicit provider
const westBucket = new aws.s3.Bucket("west-bucket", {}, {
    provider: westProvider,
});

// Multi-cloud: Azure provider
import * as azure from "@pulumi/azure-native";

const resourceGroup = new azure.resources.ResourceGroup("rg", {
    location: "eastus",
});

Creating Resources

creating-resources.ts
import * as aws from "@pulumi/aws";

// Resource name is the first argument (logical name)
// Resource args is the second argument (configuration)
const vpc = new aws.ec2.Vpc("main-vpc", {
    cidrBlock: "10.0.0.0/16",
    enableDnsHostnames: true,
    enableDnsSupport: true,
    tags: { Name: "main-vpc" },
});

// Resources can reference other resources
const subnet = new aws.ec2.Subnet("app-subnet", {
    vpcId: vpc.id,           // Output from vpc
    cidrBlock: "10.0.1.0/24",
    availabilityZone: "us-east-1a",
    tags: { Name: "app-subnet" },
});

// Export important values
export const vpcId = vpc.id;
export const subnetId = subnet.id;

Resource Options

resource-options.ts
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// protect: Prevent accidental deletion
const db = new aws.rds.Instance("prod-db", {
    engine: "postgres",
    instanceClass: "db.r5.large",
    allocatedStorage: 100,
}, { protect: true });

// dependsOn: Explicit dependency ordering
const app = new aws.ecs.Service("app", {
    /* ... args ... */
}, { dependsOn: [db] });

// ignoreChanges: Ignore specific property changes
const server = new aws.ec2.Instance("server", {
    ami: "ami-0abcdef1234567890",
    instanceType: "t3.micro",
}, { ignoreChanges: ["ami"] });

// aliases: Rename without recreating
const bucket = new aws.s3.Bucket("new-name", {}, {
    aliases: [{ name: "old-name" }],
});

Common Pitfall

Problem: Renaming a resource causes Pulumi to delete and recreate it, potentially causing data loss.

Solution: Use the aliases resource option to tell Pulumi that a resource was renamed. This preserves the existing cloud resource.

Custom vs Component Resources

Feature Custom Resource Component Resource
Maps to Single cloud resource Group of resources
Provider Managed by a provider plugin Written in your program
CRUD Provider handles create/read/update/delete Delegates to child resources
Example aws.s3.Bucket, aws.ec2.Instance VpcWithSubnets, AppCluster
Use Case Individual infrastructure pieces Reusable infrastructure patterns

When to Use Component Resources

  • Encapsulation: Group related resources (VPC + subnets + route tables)
  • Reusability: Create patterns used across multiple stacks or projects
  • Abstraction: Hide complexity behind a simple interface
  • Organization: Keep the resource tree clean and navigable in the Pulumi console

Quick Reference

Resource Options Reference

Option Description Example
provider Explicit provider for the resource { provider: westProvider }
dependsOn Explicit dependencies { dependsOn: [vpc, subnet] }
protect Prevent accidental deletion { protect: true }
ignoreChanges Ignore property drift { ignoreChanges: ["tags"] }
aliases Previous names for migration { aliases: [{ name: "old" }] }
parent Parent component resource { parent: this }
deleteBeforeReplace Delete old before creating new { deleteBeforeReplace: true }