Pulumi is an infrastructure-as-code platform that uses general-purpose programming languages (Python, TypeScript, Go) to define cloud infrastructure. Unlike Terraform's HCL, Pulumi's state files contain not just resource references but full resource outputs — including database connection strings, generated passwords, TLS private keys, and API tokens — stored in the state backend (S3, Azure Blob, or Pulumi Cloud). Anyone with read access to the S3 bucket hosting Pulumi state can extract all infrastructure credentials without any Pulumi authentication; Pulumi's pulumi stack output --show-secrets command decrypts secrets for any stack member; Pulumi.*.yaml config files committed to repositories may contain encrypted secrets whose encryption key (stored in the stack) can be used to decrypt them; and Pulumi's Automation API allows programmatic stack manipulation from within application code — a compromised application can call stack.up() to modify infrastructure. This guide covers systematic Pulumi security assessment.
# Pulumi state is stored in the backend (S3, Azure Blob, GCS, Pulumi Cloud)
# S3 backend: s3://your-bucket/org/project/stack/.pulumi/stacks/
# List Pulumi state files in S3 backend
aws s3 ls s3://pulumi-state-bucket/ --recursive 2>/dev/null | \
grep "\.json$" | head -20
# Returns: .pulumi/stacks/production.json, .pulumi/stacks/staging.json etc.
# Download and examine a state file (state files are JSON, may contain plaintext secrets)
aws s3 cp s3://pulumi-state-bucket/.pulumi/stacks/production.json /tmp/state.json 2>/dev/null && \
python3 -c "
import json
with open('/tmp/state.json') as f:
state = json.load(f)
resources = state.get('checkpoint', {}).get('latest', {}).get('resources', [])
print(f'Resources: {len(resources)}')
for r in resources:
outputs = r.get('outputs', {})
for k, v in outputs.items():
if isinstance(v, str) and len(v) > 8:
if any(x in k.lower() for x in ['password', 'key', 'secret', 'token', 'cert', 'connection']):
print(f' {r[\"urn\"].split(\"::\")[-1]} .{k} = {v[:50]}...')
" 2>/dev/null | head -30
# Pulumi stack outputs can be marked as secret but any stack member can read them
# via: pulumi stack output --show-secrets
# List stack outputs (shows placeholders for secrets without --show-secrets)
pulumi stack output 2>/dev/null
# Reveal all secrets (requires stack access — tests excessive stack membership)
pulumi stack output --show-secrets 2>/dev/null | head -20
# Outputs database_url, api_key, tls_private_key etc. in plaintext
# Via Pulumi REST API (Pulumi Cloud backend)
PULUMI_ACCESS_TOKEN=$(cat ~/.pulumi/credentials.json 2>/dev/null | \
python3 -c "import json,sys; print(json.load(sys.stdin).get('accessTokens',{}).get('https://api.pulumi.com',''))" 2>/dev/null)
# List all stacks in org
curl -s "https://api.pulumi.com/api/stacks" \
-H "Authorization: token ${PULUMI_ACCESS_TOKEN}" 2>/dev/null | \
python3 -c "
import json,sys
data = json.load(sys.stdin)
stacks = data.get('stacks', [])
print(f'Stacks: {len(stacks)}')
for s in stacks:
print(f\" {s.get('orgName','?')}/{s.get('projectName','?')}/{s.get('stackName','?')}\")
" 2>/dev/null
Pulumi.*.yaml files with encrypted secrets to public repositories — Pulumi's default passphrase encryption is brute-forceablepulumi stack output --show-secrets calls — enable Pulumi Audit Logs (Pulumi Cloud) to track secret access| Security Test | Method | Risk |
|---|---|---|
| State file in S3 contains plaintext credentials | aws s3 cp state.json — parse outputs for password/key/token fields | Critical |
| Stack output secrets readable by all members | pulumi stack output --show-secrets — decrypts all secret outputs for any stack member | High |
| Pulumi.*.yaml committed with encrypted secrets | git log --all --grep=secret Pulumi*.yaml — find secrets in git history | High |
| Provider credentials in environment not rotated | Review CI/CD env vars — static AWS/Azure keys used as Pulumi provider creds | High |
| Automation API allows stack modifications from app code | Review app code for AutoStack/LocalWorkspace calls — can modify infra from compromised app | High |
| PULUMI_ACCESS_TOKEN in ~/.pulumi/credentials.json | Read credentials file on developer machines — token scoped to all org stacks | Medium |
Ironimo tests Pulumi IaC deployments for state file credential exposure in S3 backends, stack output secrets readable by all team members, Pulumi.*.yaml files with encrypted secrets committed to repositories, provider credentials stored as static environment variables, Automation API enabling infrastructure modification from compromised application code, and PULUMI_ACCESS_TOKEN scope allowing org-wide stack access from developer machines.
Start free scan