Pulumi audit logs don’t actually record changes; they record intent expressed through Pulumi commands.

Let’s watch Pulumi in action. Imagine we have a simple Pulumi program in Python that provisions an AWS S3 bucket.

import pulumi
import pulumi_aws as aws

bucket = aws.s3.Bucket("my-app-bucket")

pulumi.export("bucket_name", bucket.id)

When you run pulumi up in your terminal, this is what’s happening under the hood, and what the audit logs reflect:

  1. pulumi login: You authenticate with the Pulumi Service. This establishes your identity for subsequent operations.
  2. pulumi preview: Pulumi reads your code, consults the current state of your infrastructure (stored in the Pulumi Service), and determines what changes would be made. This is a dry run.
  3. pulumi up: This is the actual deployment command. Pulumi again reads your code and the current state. It compares them to generate an update plan. If you confirm the plan, Pulumi then sends instructions to the relevant cloud provider (AWS, Azure, GCP, etc.) to create, update, or delete resources.
  4. pulumi destroy: Similar to up, but the plan is to remove all resources managed by this Pulumi stack.

The Pulumi Service, where your state is stored and where you access audit logs, is recording these events. An event might be: "User 'alice@example.com' ran pulumi up on stack 'my-project/dev'". It’s not a diff of the bucket’s properties before and after, but rather the action taken by a user.

The Mental Model: Intent and State

Pulumi operates on a declarative model. You tell Pulumi what you want your infrastructure to look like (your code), and Pulumi figures out how to get there.

  • Your Code (Desired State): This is your Pulumi program. It defines the resources and their properties.
  • Pulumi State (Current State): This is a snapshot of your infrastructure as managed by Pulumi, stored in the Pulumi Service. It tracks which resources Pulumi is responsible for and their current configuration.
  • Cloud Provider API (Source of Truth): This is the actual state of your infrastructure as understood by AWS, Azure, GCP, etc.

When you run pulumi up, Pulumi compares your code (desired state) against the Pulumi state (current state). If there are differences, it generates a plan to update the cloud provider’s resources to match your desired state. The audit log captures the initiation of these operations.

The Levers You Control

  • pulumi login: Controls your identity and access to the Pulumi Service.
  • Stack Name: pulumi stack select <stack-name> or pulumi stack init <stack-name>. This is crucial for isolating environments (dev, staging, prod). Each stack has its own state.
  • pulumi up / pulumi preview / pulumi destroy: These are the primary commands that trigger actions and subsequent log entries.
  • RBAC (Role-Based Access Control): Within the Pulumi Service, you can define users, teams, and their permissions on specific stacks. This determines who can run which commands.
  • Service Backend: You can choose to self-host your Pulumi state (e.g., using S3 or Azure Blob Storage) or use the SaaS Pulumi Service. Audit logging capabilities vary between these. The SaaS service provides a richer audit log experience.

What Most People Miss: The "Preview" Nuance

While pulumi up is the command that makes changes, pulumi preview generates an identical plan. The audit log will show both a preview event and an update event if you subsequently run pulumi up. This might seem redundant, but it’s vital for understanding the workflow. The preview event confirms that a user planned the change, and the update event confirms they executed it. It’s a two-step verification process that helps prevent accidental deployments.

The next concept you’ll likely encounter is how to integrate these audit logs with external security information and event management (SIEM) systems for more comprehensive compliance monitoring.

Want structured learning?

Take the full Pulumi course →