The IAM policy

The IAM policy Kulshan runs with.

The composed policy contains 160 unique read-only actions and zero create, update, modify, or delete actions. Verify the hash, inspect the JSON, and decide whether the access boundary fits your environment.

version
0.4.2 (released 2026-07-20)
total actions
160
aws services
33
sha256
6533e48168c2a4ce8e96f383f42534a9f05575ad459f1e5b9f86dcbac632678f
file
Kulshan-readonly.json (composed policy, 7.1 KB)
source
Generated from per-pack policies in Kulshan/iam/per-check/. Composed at release time, attested by hash.

What the policy allows.

Plain-language summary. Every line below maps to read-only AWS APIs. The full action list is in the JSON file above.

spend
Cost Explorer queries (cost and usage, anomaly history, RI/SP coverage and recommendations, forecast). Aggregate cost data only. ce:Get*
compute
EC2 instances, EBS volumes and snapshots, security groups, route tables, NAT gateways, Auto Scaling groups, Lambda function configuration. ec2:Describe*, autoscaling:Describe*, lambda:List*, lambda:GetFunction*
containers
ECS clusters and services, EKS clusters, ECR repositories, image scan findings. Configuration only.
storage
S3 bucket inventory, encryption, versioning, public-access settings, lifecycle policies, replication. Bucket configuration only, no object data.
database
RDS clusters and instances, parameter groups, snapshots, DynamoDB tables, ElastiCache clusters. Configuration only.
network
VPC topology, subnets, route tables, peering, transit gateways, ELB / ALB / NLB, Route 53 hosted zones. Configuration only.
identity
IAM users, roles, groups, policies (their configuration, not their secrets), Access Analyzer findings, organization structure, caller identity. iam:Get*, iam:List*, access-analyzer:Get*, access-analyzer:List*, organizations:Describe*, sts:GetCallerIdentity
audit log
CloudTrail trail configuration and integrity validation status, AWS Config recorders and rules, X-Ray traces. Whether logging is on, not the log contents.
monitoring
CloudWatch metrics, alarms, dashboards, log-group existence and retention. Log-group names and retention, not the log lines.
encryption
KMS key configuration (rotation, policy, grants). No Decrypt. No GenerateDataKey. No cryptographic operations.
backup
AWS Backup vaults, plans, recovery points, protected resources. Configuration and inventory only.
threat data
GuardDuty detector configuration and findings.
tags
Resource Groups Tagging API queries to compute tag coverage and ownership gaps.
capacity
Service Quotas (current usage and limits), CloudFormation stack drift detection status.
delivery
SNS topic configuration. Used by the observability pack to map alarm-to-channel wiring.

What it explicitly does NOT allow.

Verbs the policy never grants. If you find a Kulshan code path that calls one of these, that is a bug, and it should be reported to [email protected].

  • No Put, Create, Update, Modify, Delete, or Terminate on any service.
  • No iam:CreateAccessKey, iam:UpdateLoginProfile, iam:CreateUser, or any IAM mutation.
  • No sts:AssumeRole in the policy itself. Kulshan uses your existing credential chain; assuming a role is your decision, not the policy's.
  • No s3:GetObject. The bucket configuration is readable; the bucket contents are not.
  • No kms:Decrypt, kms:GenerateDataKey, kms:Encrypt, or any cryptographic operation.
  • No ec2:RunInstances, ec2:TerminateInstances, ec2:CreateTags, or any compute-lifecycle action.
  • No rds:Modify*, lambda:UpdateFunctionCode, secretsmanager:GetSecretValue, or any other resource modification or secret access.

Verify it yourself.

Download the JSON, hash it locally, and compare to the SHA256 above. If the two match, the file you have is the file we ship.

$ shasum -a 256 Kulshan-readonly.json 6533e48168c2a4ce8e96f383f42534a9f05575ad459f1e5b9f86dcbac632678f Kulshan-readonly.json

If the hash does not match, something is wrong. Email [email protected].

Per-pack policies.

Each pack ships its own minimum-viable policy. Attach only the packs you intend to run. Per-pack totals do not add up to 160 because some read-only actions are reused across multiple checks. The composed policy de-duplicates shared actions into 160 unique IAM actions.

Pack
Actions
Services
Download
age
16
7
core
2
2
cost
15
3
dr
29
10
drift
12
6
limit
19
10
pulse
23
14
security
81
14
sweep
24
9
tag
33
11
topo
14
2

Trust Ledger.

Corrections, disclosures, and integrity events. Newest first.

2026-07-20 // v0.4.2

Three S3 entries in our published IAM policy used AWS API operation names instead of IAM action names. They granted nothing. More importantly, the scanner could treat the resulting authorization failure as a clean result, meaning a user applying our published least-privilege policy could receive a false clean for an affected S3 check.

We corrected the IAM action names and changed the scanner so a failed evaluation is reported as "could not check," with the denied action named. A check is now marked clean only when the required evidence was successfully retrieved and evaluated.

fixed version
0.4.2
affected versions
All versions prior to 0.4.2 (exact first-affected version could not be determined)
previous policy sha256
3c9e7673091705aa70be9990e81ee5625bc876e2a4d68748e3761d4233decc7b
corrected policy sha256
6533e48168c2a4ce8e96f383f42534a9f05575ad459f1e5b9f86dcbac632678f
incorrect actions
s3:GetBucketEncryption, s3:GetBucketLifecycleConfiguration, s3:GetBucketReplication
correct actions
s3:GetEncryptionConfiguration, s3:GetLifecycleConfiguration, s3:GetReplicationConfiguration
corrected policy

Reuse

Reuse freely under CC BY 4.0. Credit: MissionFinOps, missionfinops.com.

This applies to the policy file (kulshan-readonly.json and the per-pack files in kulshan/iam/per-check/) only. The CC BY 4.0 license on the policy file is in addition to the Apache 2.0 license that covers the rest of the Kulshan codebase.

If your team or tool adopts this policy as a baseline, no permission needed. A link back is appreciated, not required.