Read-only. Always.
Here's the proof.

CostPatrol connects via a CloudFormation-deployed IAM role with zero write permissions. We observe and report. We never modify your infrastructure. Below is every permission we request and why.

Three steps. Zero credentials stored.

01

You deploy a CloudFormation template

One-click deployment in your AWS account. Creates a read-only IAM role with an external ID unique to your account. No agents, no long-term credentials.

Takes under 2 minutes
02

CostPatrol assumes the role temporarily

1-hour STS session tokens. No stored credentials. External ID prevents confused deputy attacks.

MaxSessionDuration: 3600 seconds
03

We scan and report. That's it.

Read-only API calls to describe and list resources. Results packaged as savings actions and delivered to Slack.

No state changes. Ever.

Every permission we request

These are the exact IAM actions in our CloudFormation template. Nothing more. Nothing hidden.

Service Permission Why We Need It
Cost Explorer ce:GetCostAndUsage Pull daily cost data, RI and Savings Plans coverage, purchase recommendations, and cost-allocation tag inventory
ce:GetReservationUtilization
ce:GetReservationCoverage
ce:GetReservationPurchaseRecommendation
ce:GetSavingsPlansUtilization
ce:GetSavingsPlansCoverage
ce:GetSavingsPlansPurchaseRecommendation
ce:ListCostAllocationTags
CloudWatch cloudwatch:GetMetricData Measure CPU, memory, throughput; inventory alarms and metric streams to detect idle and oversized resources
cloudwatch:GetMetricStatistics
cloudwatch:ListMetrics
cloudwatch:DescribeAlarms
cloudwatch:ListMetricStreams
cloudwatch:GetMetricStream
CloudWatch Logs logs:DescribeLogGroups Audit log retention; run Logs Insights queries for NAT and Lambda cost analysis
logs:StartQuery
logs:GetQueryResults
EC2 ec2:DescribeInstances Inventory instances, volumes, NAT gateways, Elastic IPs, snapshots, AMIs, VPC/transit gateway/VPN topology, and route tables for waste detection
ec2:DescribeVolumes
ec2:DescribeFastSnapshotRestores
ec2:DescribeNatGateways
ec2:DescribeVpcEndpoints
ec2:DescribeVpcs
ec2:DescribeAddresses
ec2:DescribeSnapshots
ec2:DescribeImages
ec2:DescribeTransitGateways
ec2:DescribeTransitGatewayAttachments
ec2:DescribeVpnConnections
ec2:DescribePrefixLists
ec2:DescribeFlowLogs
ec2:DescribeRouteTables
ec2:DescribeSubnets
ec2:DescribeNetworkInterfaces
ec2:DescribeLaunchTemplateVersions
Lambda lambda:ListFunctions Check architecture (ARM64 vs x86), memory allocation, runtime, and provisioned concurrency settings. We do not request lambda:GetFunction, which would expose your functions' environment variables.
lambda:ListProvisionedConcurrencyConfigs
RDS rds:DescribeDBInstances Find idle databases, cluster sprawl, snapshot waste, reservation utilization, and previous-generation instance types. Also covers DocumentDB and Neptune, which read through the same RDS API.
rds:DescribeDBSnapshots
rds:DescribeDBClusterSnapshots
rds:DescribeDBClusters
rds:DescribeReservedDBInstances
rds:DescribeOrderableDBInstanceOptions
rds:ListTagsForResource
DynamoDB dynamodb:ListTables Audit capacity mode, TTL settings, backup config, tags, and usage patterns
dynamodb:DescribeTable
dynamodb:DescribeTimeToLive
dynamodb:DescribeContinuousBackups
dynamodb:ListTagsOfResource
S3 s3:ListAllMyBuckets Check lifecycle rules, encryption config, intelligent-tiering, incomplete multipart uploads, and storage class optimization. s3:ListBucket reads object KEYS to tell an empty bucket from a used one. It never reads object contents, which would need s3:GetObject.
s3:ListBucket
s3:GetBucketLocation
s3:GetBucketVersioning
s3:GetLifecycleConfiguration
s3:ListBucketMultipartUploads
s3:GetEncryptionConfiguration
s3:GetIntelligentTieringConfiguration
ELB elasticloadbalancing:DescribeLoadBalancers Find idle or unused load balancers and target groups
elasticloadbalancing:DescribeTargetGroups
elasticloadbalancing:DescribeTargetHealth
ECS ecs:ListClusters Inventory ECS services, task definitions, and Fargate configuration for ARM64 and ephemeral storage analysis
ecs:ListServices
ecs:DescribeServices
ecs:DescribeTaskDefinition
SageMaker sagemaker:ListNotebookInstances Find idle notebook instances
ElastiCache elasticache:DescribeCacheClusters Audit cache clusters for idle nodes and Graviton/Valkey migration opportunities
API Gateway apigateway:GET Read REST API metadata to check stages and integrations
Backup backup:ListBackupVaults Audit backup vaults, plans, and cross-region replication for non-production cost optimization
backup:ListBackupPlans
backup:GetBackupPlan
backup:ListTags
Budgets budgets:ViewBudget Verify cost guardrails exist and have alert notifications configured. ViewBudget is the only read action AWS Budgets defines.
CloudFront cloudfront:ListDistributions Inventory distributions for price class and origin optimization
CloudTrail cloudtrail:DescribeTrails Find duplicate trails and unnecessarily verbose data-event logging that inflate S3 + CloudTrail costs
cloudtrail:GetTrailStatus
cloudtrail:GetEventSelectors
Compute Optimizer compute-optimizer:GetEC2InstanceRecommendations Cross-check AWS's own rightsizing recommendations against scanner findings
AWS Config config:DescribeConfigurationRecorders Detect Config recorders running in regions with no real resources (common waste pattern)
config:DescribeConfigurationRecorderStatus
config:GetDiscoveredResourceCounts
Application Auto Scaling application-autoscaling:DescribeScalableTargets Read DynamoDB and ECS scaling targets and policies to detect over-provisioning
application-autoscaling:DescribeScalingPolicies
ECR ecr:DescribeRepositories Find repositories without lifecycle policies and inventory stored image data
ecr:GetLifecyclePolicy
ecr:DescribeImages
EFS elasticfilesystem:DescribeFileSystems Detect file systems with no lifecycle policy and over-provisioned throughput mode
elasticfilesystem:DescribeLifecycleConfiguration
EKS eks:ListClusters Inventory clusters, node groups, Fargate profiles and enabled capabilities for capacity right-sizing
eks:DescribeCluster
eks:ListNodegroups
eks:DescribeNodegroup
eks:ListFargateProfiles
eks:ListCapabilities
EMR elasticmapreduce:ListClusters Detect long-running clusters with idle instance groups
elasticmapreduce:ListInstanceGroups
Glue glue:GetDevEndpoints Find idle dev endpoints and analyze job DPU/run patterns for waste
glue:GetJobs
glue:GetJobRuns
Kafka (MSK) kafka:ListClustersV2 Inventory MSK clusters for sizing analysis
Keyspaces cassandra:Select Inventory keyspaces and tables for capacity-mode analysis. Amazon Keyspaces authorizes under the cassandra: prefix; there is no keyspaces: prefix in IAM.
Kinesis kinesis:ListStreams Inventory streams for shard-count and idle-stream analysis
kinesis:DescribeStreamSummary
KMS kms:ListKeys Detect S3 buckets using KMS encryption without bucket keys (a known cost driver)
kms:DescribeKey
kms:ListGrants
MemoryDB memorydb:DescribeClusters Find clusters where reserved-node coverage is missing
memorydb:DescribeReservedNodes
OpenSearch es:ListDomainNames Inventory domains and reservation utilization for right-sizing
es:DescribeDomain
es:DescribeReservedInstances
Redshift redshift:DescribeClusters Inventory provisioned clusters and Serverless workgroups
redshift-serverless:ListWorkgroups
Route 53 route53:ListHostedZones Detect empty zones and CNAME records that should be Alias records (free)
route53:GetHostedZone
route53:ListResourceRecordSets
Step Functions states:ListStateMachines Inventory state machines for execution-cost analysis
states:DescribeStateMachine
Timestream timestream:ListDatabases Inventory databases and tables for retention analysis. DescribeEndpoints is required because the Timestream SDK resolves a cell-specific endpoint before every call.
timestream:ListTables
timestream:DescribeEndpoints
Secrets Manager secretsmanager:ListSecrets Count stored secrets to flag ones that could live in SSM Parameter Store for free. ListSecrets returns names and metadata only. Reading a secret's value would need secretsmanager:GetSecretValue, which we do not request.
Transfer Family transfer:ListServers Detect idle SFTP/FTPS servers
Organizations organizations:DescribeOrganization Detect org membership and enumerate member accounts for multi-account discovery
organizations:ListAccounts
IAM iam:SimulatePrincipalPolicy Let CostPatrol check this role against what its own rules need, so it can tell you which checks a missing permission is blocking instead of silently reporting nothing. Scoped to the CostPatrolReadOnly role itself. It evaluates policy documents and calls none of the services it names.

The Deny is what actually enforces this

The table above is what the role asks for. What stops it doing anything else is a second statement in the same template: an explicit Deny on every action not named Get, Describe, List or BatchGet. An explicit Deny wins in IAM whatever the rest of the policy says, so nothing named Create, Put, Update, Modify or Delete can reach this role, whatever the Allow list asks for.

That is a strong default. It is not a proof, and you should have the limit from us rather than find it yourself. The Deny matches NAMES, and AWS does not name its write actions consistently. glue:GetStorage is a write, and AWS classifies it as permission management too. redshift:GetClusterCredentials mints database credentials. Both are a Get, so a rail built on read verbs would let either one past.

So the proof is not the Deny. AWS publishes a Service Reference that records, for every action in every service, whether that action writes. We keep a dated copy of it in the repo, and a test fails the build if any action this role requests is classified as a write, as permission management, or as tagging. Every action in the table above is clear on all three, against a copy captured on 2026-08-20, which the build re-checks on every change we ship and which we recapture at least every 180 days.

5 of the actions above are reads AWS did not name that way, so that Deny would block them too. The template excepts each one by name, with the reason next to it. This is the whole of the role's reach beyond the read verbs.

  • apigateway:GET API Gateway v1 authorizes reads by HTTP verb, not by operation name. IAM matching is case sensitive, so GET is not Get.
  • budgets:ViewBudget The only read action AWS Budgets defines. There is no budgets:DescribeBudgets.
  • cassandra:Select Amazon Keyspaces authorizes under the cassandra prefix, and Select is its read verb.
  • iam:SimulatePrincipalPolicy Evaluates this role's own policy documents so CostPatrol can tell you which checks a missing permission is blocking. It calls none of the services it names.
  • logs:StartQuery Starts a CloudWatch Logs Insights query. It reads log data and writes nothing.

What we CANNOT do

The explicit Deny described above blocks every action not named Get, Describe, List or BatchGet, and it beats any Allow. Every action the role does hold is verified non-write against AWS's own Service Reference. The following operations are impossible with our permissions.

  • Create, modify, or delete any AWS resource
  • Access S3 object contents or log data contents
  • Modify IAM roles, policies, or permissions
  • Access secrets, parameters, or credentials in your account
  • Make any API call that changes state

Data protection

Every layer of our stack is designed with defense in depth. Here is how we protect the data we collect.

Encryption in transit

All traffic encrypted with TLS 1.2+ (TLS 1.3 preferred). No plaintext connections accepted.

Encryption at rest

Data at rest encrypted with AES-256. DynamoDB server-side encryption enabled on all tables.

Multi-tenant isolation

Composite DynamoDB keys ensure strict tenant isolation. No customer can access another customer's data.

Security headers

HSTS headers enforced. CORS restricted to costpatrol.io. Content Security Policy applied.

WAF protection

AWS WAF active with rate limiting, SQL injection, and XSS protection rules.

Short-lived credentials

1-hour STS session tokens only. No long-term AWS credentials stored anywhere in our infrastructure.

Standards and certifications

SOC 2 Type II

In progress — target Month 12

Working toward SOC 2 Type II certification covering security, availability, and confidentiality trust service criteria.

GDPR

Data processing agreement available

Data processing agreement available on request. We process only infrastructure metadata, not personal data.

Data retention

Configurable per account

Retention is configurable. All data deleted on account disconnection. No data held after offboarding.

Audit logging

2-year retention, immutable storage

All access and operations logged with 2-year retention in immutable storage for forensic analysis.

See exactly what we deploy. Then start your free scan.

Review the CloudFormation template yourself. Every permission is documented above. Read-only access, deployed in your account, under your control.