Pulumi Configuration & Secrets

Medium 20 min read

Configuration Basics

Why Configuration & Secrets Matter

The Problem: Infrastructure code needs different values per environment (instance sizes, regions, database passwords), and secrets must never be stored in plain text in version control.

The Solution: Pulumi provides a built-in configuration system with per-stack values and automatic secret encryption, so you can safely store all config in Git.

Real Impact: Teams can manage hundreds of configuration values across environments without risking secret exposure or config drift.

Real-World Analogy

Think of Pulumi configuration like a hotel key card system:

  • Config Values = Room preferences (floor, view, bed type) - different per guest
  • Secrets = The key card itself - encrypted and unique per guest
  • Stack Config File = The reservation record with all preferences
  • Secret Provider = The key card encoder machine
  • Config Class = The front desk that reads your reservation

How Config Works

Per-Stack Values

Each stack has its own Pulumi.<stack>.yaml file with environment-specific values like regions and instance sizes.

Namespaced Keys

Config keys are namespaced by project name (e.g., myproject:dbSize) to avoid conflicts between packages.

Encrypted Secrets

Secret values are automatically encrypted in the config file, safe to commit to version control.

Typed Access

The Config class provides typed getters (getString, getNumber, getBoolean) with required/optional variants.

Setting Config Values

config-cli.sh
# Set a plain config value
pulumi config set instanceType t3.micro

# Set a config value for a specific provider
pulumi config set aws:region us-east-1

# Set a secret value (automatically encrypted)
pulumi config set --secret dbPassword S3cur3P@ssw0rd!

# Set a config value on a specific stack
pulumi config set instanceType t3.large --stack prod

# View all config (secrets shown as [secret])
pulumi config

# View config including secret values
pulumi config --show-secrets

Structured Configuration

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

const config = new pulumi.Config();

// Simple values
const instanceType = config.require("instanceType");
const minSize = config.requireNumber("minSize");
const enableMonitoring = config.getBoolean("enableMonitoring") ?? true;

// Structured config (objects)
interface DatabaseConfig {
    engine: string;
    instanceClass: string;
    allocatedStorage: number;
}

const dbConfig = config.requireObject<DatabaseConfig>("database");

// Access provider-specific config
const awsConfig = new pulumi.Config("aws");
const region = awsConfig.require("region");

Managing Secrets

Configuration & Secrets Flow
CLI pulumi config set Encrypted in Pulumi.dev.yaml Plain text in Pulumi.dev.yaml decrypt Runtime config.requireSecret() Output <T> --secret flag Secrets encrypted Config plain text
secrets.ts
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const config = new pulumi.Config();

// Read a secret value (returns Output<string>)
const dbPassword = config.requireSecret("dbPassword");

// Use secrets in resources - Pulumi tracks secrecy
const db = new aws.rds.Instance("app-db", {
    engine: "postgres",
    instanceClass: "db.t3.micro",
    allocatedStorage: 20,
    username: "admin",
    password: dbPassword,  // Secret output - encrypted in state
});

// Programmatically mark a value as secret
const secretValue = pulumi.secret("my-sensitive-data");

// Secret outputs are never shown in logs or CLI output
export const connectionString = pulumi.secret(
    pulumi.interpolate`postgres://admin:${dbPassword}@${db.endpoint}/mydb`
);

Common Pitfall

Problem: Using config.require() instead of config.requireSecret() for sensitive values causes them to appear in plain text in stack state and logs.

Solution: Always use the --secret flag when setting sensitive config values, and use requireSecret() / getSecret() to read them. This ensures end-to-end encryption.

Secret Providers

Available Secret Providers

  • Pulumi Cloud (default): Managed encryption through Pulumi's service
  • AWS KMS: Use your own AWS KMS key for encryption
  • Azure Key Vault: Encrypt with Azure Key Vault keys
  • GCP KMS: Use Google Cloud KMS for encryption
  • Passphrase: Local encryption with a user-provided passphrase
secret-providers.sh
# Use AWS KMS for secret encryption
pulumi stack init prod --secrets-provider="awskms://alias/pulumi-secrets?region=us-east-1"

# Use passphrase-based encryption
pulumi stack init dev --secrets-provider="passphrase"

# Change secrets provider for existing stack
pulumi stack change-secrets-provider "awskms://alias/my-key"

Quick Reference

Configuration Commands & Methods

Command / Method Description Example
pulumi config set Set a config value pulumi config set key value
pulumi config set --secret Set an encrypted secret pulumi config set --secret apiKey abc123
config.require() Get required string config config.require("instanceType")
config.requireSecret() Get required secret as Output config.requireSecret("dbPassword")
config.getNumber() Get optional number config config.getNumber("port") ?? 3000
config.requireObject() Get required structured config config.requireObject<DbConfig>("db")
pulumi.secret() Mark a value as secret pulumi.secret(computedValue)