Pulumi encrypts your secrets not by default, but by using a backend that supports encryption.
Let’s see Pulumi secrets in action. Imagine you have a Pulumi.yaml file and a Pulumi.dev.yaml file.
Pulumi.yaml:
name: my-app
runtime: nodejs
description: A simple Node.js Pulumi app
Pulumi.dev.yaml:
config:
my-app:databaseUrl: "supersecretpassword123"
When you run pulumi up, Pulumi needs to access databaseUrl. If your backend is configured for encryption (like the default Pulumi Service backend or an encrypted S3 bucket), Pulumi will encrypt supersecretpassword123 before storing it.
Here’s how you’d typically interact with it in your code:
import * as pulumi from "@pulumi/pulumi";
const config = new pulumi.Config();
const databaseUrl = config.require("databaseUrl");
// Use databaseUrl to connect to your database
console.log(`Connecting to database with URL: ${databaseUrl}`);
If you run pulumi config set --secret my-app:databaseUrl "anothersecret" from your CLI, Pulumi automatically encrypts this value.
The magic happens in the state backend. Pulumi doesn’t encrypt secrets in your code or in your Git repository. It encrypts them in the state file that Pulumi manages. This state file is the single source of truth for your deployed infrastructure.
When you run pulumi up, Pulumi reads the configuration, resolves any secret values (decrypting them if necessary using the appropriate key), and then uses those values to provision your cloud resources. The encrypted secret is then stored back in the state file.
What problem does this solve?
The primary problem Pulumi Secrets solves is preventing sensitive information like API keys, database passwords, and private certificates from being exposed in plain text in your Pulumi state file, which is often stored in version control or a cloud object store. This drastically improves the security posture of your infrastructure as code.
How does it work internally?
Pulumi uses a symmetric encryption scheme. When you mark a configuration value as a secret (either via pulumi config set --secret or by setting it in a config file that’s encrypted), Pulumi encrypts the value using a randomly generated key. This random key is then itself encrypted using a master key.
The master key’s location depends on your backend:
- Pulumi Service: The master key is managed and encrypted by the Pulumi Service. You authenticate with Pulumi Service, and it handles decrypting the random key, which then allows Pulumi to decrypt your secret value.
- Self-Managed Backends (e.g., S3, GCS, Azure Blob Storage): If you’re using a self-managed backend that supports encryption (like an S3 bucket with server-side encryption enabled), Pulumi can leverage that. Alternatively, you can use Pulumi’s local secret encryption, where the master key is stored locally in an encrypted file (often protected by a passphrase you set).
When you run pulumi up, Pulumi:
- Reads the configuration, identifying secret values.
- Retrieves the encrypted random key for each secret.
- Decrypts the random key using the master key (obtained from the Pulumi Service or local keyring).
- Uses the decrypted random key to decrypt the actual secret value.
- Uses the decrypted secret value to provision resources.
- If the secret value was updated, it re-encrypts it with the random key and then re-encrypts the random key with the master key, storing it back in the state.
The most surprising true thing about Pulumi Secrets is that the encryption mechanism is tied to the state backend and how you authenticate, not just a flag on the config value itself. While pulumi config set --secret marks a value, the actual encryption and decryption process relies on the infrastructure Pulumi is using to store its state.
This means if you switch your backend from the Pulumi Service to an S3 bucket without encryption, or vice-versa, you might have issues decrypting existing secrets or need to re-encrypt them. Pulumi tries to be smart about this, but understanding the backend’s role is crucial.
Consider the case where you’re using a self-managed backend and chose local secret encryption. If you lose the file containing your encrypted master key (and forget your passphrase), all your secrets are irrecoverable. Pulumi cannot help you get them back. This is a critical point of failure for secrets management if not handled with care. It emphasizes that Pulumi Secrets is a convenience and security feature for IaC, not a full-fledged secrets manager for all your application secrets, though it can be used for that too.
The next logical step after managing secrets is understanding how to manage different environments with Pulumi.