A leaked Pulumi access token is rarely just a Pulumi problem: depending on its role it can read stack state, decrypt stack secrets and open ESC environments that hand out live cloud credentials. Pulumi Cloud sits between your infrastructure code and your AWS, Azure, Google Cloud and Kubernetes accounts, so its tokens are a short path to the estate it manages. This guide covers what each Pulumi credential type reaches, the audit log events that show misuse, the revoke and rotate steps from Pulumi's own documentation, and how to remove static tokens from CI altogether with OIDC token exchange. It is for platform engineers, cloud security teams and responders handling a secret scanning alert for Pulumi.
Key takeaways
- Stack read access means secret access. Pulumi documents that for Pulumi Cloud stacks the decryption keys are obtained automatically for anyone with read access to the stack, so a Reader-level token can decrypt stack secrets.
- ESC turns one token into many credentials. Opening an ESC environment can resolve secrets from external stores and mint short-lived AWS, Azure, Google Cloud or GitHub credentials, so environment permissions are the real blast radius.
- Deletion is immediate. Deleting an organization or team token revokes it at once and later requests fail as unauthorised; expired tokens cannot be reactivated.
- The audit log names the damage. Events such as
secret-decrypted,secrets-batch-decrypted,stack-exported,environment-openandenvironment-decryptedshow what a token actually touched, but audit logs need the Pro or Enterprise edition. - The best rotation is no static token. OIDC token exchange gives CI a Pulumi token that defaults to two hours, and an organization expiry policy can cap every remaining token.
How identity and access work in Pulumi Cloud
People sign in to Pulumi Cloud through the web console or pulumi login. Automation authenticates with access tokens, normally supplied to the CLI through the PULUMI_ACCESS_TOKEN environment variable, or with short-lived tokens obtained through OIDC token exchange. Every token is evaluated against role-based access control: roles combine permission sets with entity access rules over stacks, ESC environments and Insights accounts.
| Credential | Acts as | What it reaches | Lifetime | How to revoke |
|---|---|---|---|---|
| Personal access token | The user who created it | The user's permissions across all of their organizations | Up to two years, or no expiry unless an org policy forbids it | User menu, Personal access tokens, Delete token |
| Organization access token | The organization (machine identity) | Whatever its assigned RBAC role permits; default member role if none was chosen | Up to two years, or as capped by policy | Settings, Access Management, Access Tokens (needs a user credential) |
| Team access token | A team | The combined roles of the team plus the organization's default Member permissions | As above | Teams, team name, Access Tokens |
| Legacy admin organization token | The organization | Everything an org admin can do except creating or deleting other organization tokens | As created | Delete and replace with a role-scoped token |
| OIDC-exchanged token | Org, team, personal or deployment runner, set by issuer policy | The role the policy grants; no admin scope unless requested | Default 2 hours, issuer maximum 25 hours by default | Remove or tighten the issuer's allow policy |
| Stack passphrase or KMS key | Not a login: the stack's secrets provider | Decrypts secret config and state for DIY backends | Until the provider is changed | pulumi stack change-secrets-provider and rotate the underlying secrets |
Two points change the response. First, personal tokens carry every organization the user belongs to, so one developer's leaked token may cross business units. Second, team tokens inherit the organization's default Member permissions, which Pulumi warns can give broader read access than intended. A team token that looks narrow can still read, and therefore decrypt, many stacks.
Admin roles and privileged access
- Organization admins: only users can create or delete organization tokens. Machine tokens get a 403 even with the Admin role, which stops a leaked machine token from minting new organization tokens, but team tokens can be created by machine tokens with sufficient permission.
- Stack creators: a machine identity that creates a stack receives an automatic Stack Admin grant on it. A leaked token able to create stacks therefore gains full rights over whatever it creates.
- Deployment settings: Pulumi Deployments run as a role recorded in each stack's deployment settings, and the organization has a default role for deployments. Changes appear as
deployment-settings-role-updatedandorganization-deployment-default-role-updated. - ESC environment rights: environments are a separate entity type in RBAC. Treat the right to open a production environment as privileged access, because opening it resolves its providers and returns their values.
- Custom roles over built-ins: Pulumi recommends assigning a custom role to new automation instead of the built-in Admin or Member roles, granting only the scopes the job needs. The legacy admin organization token type maps to the built-in Admin role and carries a warning in Pulumi's documentation.
Apply the same discipline you use for cloud administrators: few org admins, SSO and MFA for people, and no CI job that runs with an admin token by default. See the CI/CD Pipeline Identity Security Guide.
Integrations, API tokens and service accounts
Pulumi tokens sit wherever infrastructure code runs. During an incident, check each of these:
- CI/CD variables:
PULUMI_ACCESS_TOKENin GitHub Actions, GitLab CI, Jenkins or Azure Pipelines secrets, and in reusable workflow inputs. A compromised third-party action can dump runner memory, as the tj-actions/changed-files compromise did with cloud keys, GitHub tokens and npm tokens. - Developer machines: the CLI stores the token used at login in the user's Pulumi credentials file, and the same token also authorises the
pulumi apicommand. - Stack configuration files:
Pulumi.<stack>.yamlholds ciphertext undersecure:keys. Pulumi documents that committing these files is safe because decryption needs the stack's key. The risk is plain-text values that were never marked secret, and resource IDs, which are always stored in plain text. - DIY backends: stacks in S3, Azure Blob or Google Cloud Storage with the
passphraseprovider depend on the passphrase. If both the state bucket and the passphrase leak, every secret in that state is readable. - Service accounts: Pulumi Cloud records service account changes, and deleting a service account revokes its access tokens and permissions (event
service-deleted). - ESC consumers: applications and pipelines that call
esc openor use the environment in a stack. Each consumer's token should open only the environments it needs.
Leak response: revoke and rotate
Follow the general Leaked Credential Response Playbook, with these Pulumi steps.
- Identify the token. Match the leaked value's name, creator and location to the token lists for the user, the organization and each team. Record the role it holds, its expiry and every organization it reaches (for personal tokens, all of the user's organizations).
- Delete it. For a personal token use the three-dot menu on its row and choose Delete token. For organization and team tokens delete from Access Tokens; Pulumi states revocation is immediate and later requests fail as unauthorised. Deleted token names stay reserved permanently, so create the replacement under a new name.
- Issue the replacement on a better footing. For CI, register the CI platform as an OIDC issuer with an allow policy that checks
audandsub, and usepulumi login --oidc-token <token> --oidc-org <org>instead of a new static token. Where a static token is unavoidable, use an organization or team token with a custom role and an expiry. - Assume stack secrets are exposed. If the token had read access to any stack, treat every
secure:value in those stacks as disclosed. Rotate the underlying database passwords, API keys and cloud credentials at their source, then update the stack configuration. - Assume ESC-issued credentials are exposed. If the token could open environments, review
environment-open,environment-read-open,environment-decryptedandenvironment-oidc-token-issuedfor the exposure window. Rotate static secrets held in those environments and check the target clouds for activity by the roles ESC assumes. - Check for persistence. Look for
team-token-created,organization-token-created,member-added,member-role-changed,custom-role-updated,oidc-issuer-registeredandauthorization-policy-updatedentries you cannot attribute. A new OIDC issuer or loosened trust policy is a quiet way to keep access after the token is gone. - For a DIY backend passphrase leak, change the stack to a KMS-backed provider with
pulumi stack change-secrets-provider, rotate every secret that the old passphrase protected, and restrict access to the state bucket. - Close the source. Purge the token from repository history, logs and tickets and resolve the scanner alert with what was rotated.
Rotating the Pulumi token alone is not enough when secrets were decryptable. The Codecov breach shows the pattern: one stolen key in a CI path led to harvesting of the secrets that the pipeline environment carried.
Logging and detection
Pulumi Cloud audit logs are available on the Pro and Enterprise editions, viewable by organization admins under Settings, Audit Logs. Entries are immutable and carry a UNIX timestamp, the acting user, the event and the source IP. ESC has no separate log: environment operations appear in the same organization audit log. Export on demand from the console, the pulumi org audit-log CLI or the REST API (JSON, CSV or CEF), or continuously to Amazon S3 or Microsoft Sentinel on Enterprise.
| Audit log event | What it records | Why it matters after a leak |
|---|---|---|
secret-decrypted, secrets-batch-decrypted | Stack secret values decrypted | Shows which stacks' secrets the token read; a batch from a new IP is a strong signal |
stack-exported | Stack state exported | Full state may contain plain-text outputs and resource IDs |
environment-open, environment-read-open | ESC environment opened and values read | Dynamic cloud credentials or synced secrets were issued |
environment-decrypted | Environment definition read with secrets decrypted | Static secrets in the definition are exposed |
environment-oidc-token-issued | Environment minted an OIDC token for an external service | Follow up in the target service's own logs |
environment-unauthorized-open | Attempt to open an environment without read permission | Probing with a token that lacks rights |
team-token-created, organization-token-created | New machine tokens | Possible persistence |
oidc-issuer-registered, authorization-policy-updated | Trust changes for token exchange | Possible persistence through federation |
Good measures: number of static tokens with no expiry (target zero once a policy is set), share of CI pipelines using OIDC exchange, number of tokens holding the Admin role, count of secrets-batch-decrypted events from IPs outside known runner ranges, and time from scanner alert to token deletion. Correlate with cloud logs for the roles ESC and Deployments assume. See the Secrets Access Logging and Detection guide.
Choosing the credential for each job
| Job | Preferred credential | Acceptable fallback | Avoid |
|---|---|---|---|
| CI on GitHub Actions, GitLab CI or Kubernetes | OIDC-exchanged team or organization token, scoped by issuer policy | Team token with custom role and 30 to 90 day expiry | Personal token of a pipeline maintainer |
| Pulumi Deployments | Deployment settings role with only the stacks it manages | Organization default deployment role, kept narrow | Admin role as the default for all deployments |
| Developer laptop | Interactive pulumi login with SSO | Personal token with short expiry | Shared organization token pasted into shell profiles |
| Application reading config at runtime | ESC environment opened by a token scoped to that environment | Cloud-native secrets manager fed by ESC sync | Stack outputs read with a broad token |
Small teams on a single organization can usually reach "no static tokens in CI" in a sprint: register the CI issuer, add one allow policy per repository, and delete the old token. Larger estates with many teams should first set the default member role and a generous expiry cap (for example 365 days) so nothing breaks, then tighten the cap in steps while moving pipelines to OIDC, using the 403 error message that names the policy to find stragglers. Roll back by raising the cap, never by removing the policy.
Hardening baseline
| Control | Pulumi setting | Effect |
|---|---|---|
| Maximum token expiry | Organization policy, 1 to 3,650 days (0 removes it) | Non-compliant and non-expiring tokens are rejected with 403 on every request |
| OIDC exchange for CI | Register issuer, allow policies on aud and sub, deny policies where needed | No stored Pulumi token; deny overrides allow |
| Scoped machine tokens | Organization or team token with a custom role | Limits stacks and environments a leak reaches |
| Narrow default member role | Organization default role | Team tokens and tokens with no role inherit it |
| KMS secrets provider for DIY backends | awskms, azurekeyvault, gcpkms or hashivault | Decryption needs cloud key access, which is logged and revocable |
| Secrets in ESC, not stack config | ESC environments with rotators and external store sync | Central access control and audit for shared secrets |
| Audit export | Continuous export to S3 or Sentinel | Evidence survives and feeds detection |
Framework mapping
| Concern | MITRE ATT&CK | NIST SP 800-53 Rev 5 | NIST CSF 2.0 |
|---|---|---|---|
| Token in code, CI variable or config | T1552.001 Credentials In Files | IA-5 | PR.AA-01 |
| Stolen token used against Pulumi Cloud | T1550.001 Application Access Token | AC-6 | PR.AA-05 |
| Cloud credentials obtained through ESC | T1078.004 Cloud Accounts | AC-2 | DE.CM-03 |
| New token or issuer for persistence | T1098.001 Additional Cloud Credentials | AC-2, AU-6 | DE.CM-03 |
| Containment and rotation | n/a | IR-4 | RS.MI-01 |
Common misconfigurations
- Personal tokens in CI. The pipeline acts with a human's full permissions across every organization, and the token breaks or lingers when the person leaves.
- Treating Reader as harmless. Stack read access includes automatic decryption of stack secrets in Pulumi Cloud.
- One broad ESC environment for everything. A single environment importing production cloud credentials into every consumer means any leaked consumer token yields production access.
- Legacy admin organization tokens left in place. They carry admin rights, and many were created before custom roles existed.
- Passphrase in the same place as the state. A
PULUMI_CONFIG_PASSPHRASEstored as a CI variable next to state bucket credentials gives one leak everything. - Tokens with no expiry or owner. The Home Depot token exposure shows a leaked token staying valid for more than a year; an expiry policy limits that window.
Practitioner checklist
- Inventory personal, organization and team tokens with role, owner and expiry, and delete unused ones.
- Set an organization maximum token expiry and fix the pipelines that fail it.
- Move CI to OIDC token exchange with allow policies on
audandsub. - Replace legacy admin organization tokens and built-in Admin roles on automation with custom roles.
- Review the organization default member role, because team tokens inherit it.
- Split ESC environments by sensitivity and restrict who can open production ones.
- Move DIY backends off the passphrase provider to a KMS-backed provider.
- Export audit logs continuously and alert on decrypt batches, new tokens and issuer changes.
- Keep a runbook that rotates downstream secrets, not just the Pulumi token.
Standards and references
- Pulumi: Access tokens
- Pulumi: OIDC token exchange
- Pulumi: Secrets handling
- Pulumi: Audit logs
- Pulumi: Audit log event catalogue (documentation source)
- Pulumi: ESC documentation
- MITRE ATT&CK: T1550.001 Application Access Token
- NIST: SP 800-53 Rev 5 Security and Privacy Controls
Related NHI Mgmt Group resources: Leaked Credential Response Playbook · CI/CD Pipeline Identity Security Guide · Dynamic Secrets and Ephemeral Credentials Guide · Secrets Rotation Runbook Template