Skip to main content

AWS Provider

Deep dive into scanning AWS resources with Cloud Cost Analyzer.

Supported Services

CCA analyzes the following AWS services:

ServiceResource TypesOptimization Rules
EC2Instances, Reserved InstancesRightsizing, idle detection, RI coverage
EBSVolumes, SnapshotsUnattached volumes, old snapshots, GP2 to GP3
RDSInstances, Aurora clustersRightsizing, idle, RI coverage, storage
S3BucketsLifecycle policies, storage class optimization
LambdaFunctionsMemory optimization, unused functions
ELBALB, NLB, ClassicIdle load balancers, cross-AZ traffic
ElastiCacheClustersNode sizing, RI coverage
NAT GatewayGatewaysIdle gateways, excessive data transfer
ECS/EKSClusters, ServicesRightsizing, Fargate Spot opportunities

Authentication

CCA uses the standard AWS credential chain:

  1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
  2. Shared credentials file (~/.aws/credentials)
  3. IAM role (EC2, ECS, Lambda)
  4. AWS SSO / IAM Identity Center

Environment Variables

export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
export AWS_REGION=us-east-1

cca scan --provider aws

Named Profiles

CCA uses the standard AWS credential chain: select a profile with the AWS_PROFILE environment variable (there is no --profile flag):

# For a single command
AWS_PROFILE=production cca scan --provider aws

# Or export it for the session
export AWS_PROFILE=production
cca scan --provider aws

IAM Role (EC2/ECS/Lambda)

No configuration needed. CCA automatically retrieves credentials from the instance metadata service.

AWS SSO

# Login via SSO
aws sso login --profile my-sso-profile

# Run scan with the SSO profile
AWS_PROFILE=my-sso-profile cca scan --provider aws

Assume Role

# Assume a role first
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/CostAnalyzerRole \
--role-session-name cca-scan

# Or use a profile with role_arn configured
# ~/.aws/config
[profile cross-account]
role_arn = arn:aws:iam::123456789012:role/CostAnalyzerRole
source_profile = default

AWS_PROFILE=cross-account cca scan --provider aws

Provisioning access

cca setup --provider aws provisions the read-only access a scan needs. Who runs it depends on your environment.

Self-service (you have cloud-admin rights)

cca setup --provider aws --output-template   # preview the exact read-only policy (changes nothing)
cca setup --provider aws --deploy # create the role + policy via CloudFormation

--deploy requires permission to deploy CloudFormation and create IAM resources (iam:CreateRole, iam:CreatePolicy, iam:AttachRolePolicy, and the CAPABILITY_NAMED_IAM acknowledgement). It uses your current AWS session, so authenticate first (corporate accounts are usually SSO), and select the profile via AWS_PROFILE (there is no --profile flag):

aws sso login --profile my-profile
AWS_PROFILE=my-profile cca setup --provider aws --deploy

--deploy always targets --region (default us-east-1), which overrides the profile's region; pass --region to place the stack elsewhere. Re-running is safe: CloudFormation applies only the diff (idempotent).

Restricted (you cannot create IAM roles)

Most enterprises lock this down. Do not fight --deploy; hand off the policy instead:

cca setup --provider aws --output-template -f cca-role.yaml

Give cca-role.yaml to your cloud/platform team (a ticket, or a PR into their Terraform/CloudFormation repo). They deploy it once; you then consume the result:

  • read-only credentials, or
  • an assumable role ARN (add it to ~/.aws/config, see Assume Role), or
  • OIDC federation for CI, with no long-lived secrets (see the GitHub Actions guide).

For many accounts at once, provision the role across the org with CloudFormation StackSets (see Multi-Account Scanning).

Required IAM Permissions

CCA requires read-only access to analyze resources. Here's a minimal IAM policy:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CCAReadAccess",
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"rds:Describe*",
"rds:ListTagsForResource",
"s3:GetBucketLocation",
"s3:GetBucketTagging",
"s3:GetLifecycleConfiguration",
"s3:GetBucketVersioning",
"s3:ListAllMyBuckets",
"elasticloadbalancing:Describe*",
"lambda:List*",
"lambda:GetFunction",
"lambda:GetFunctionConfiguration",
"elasticache:Describe*",
"ecs:Describe*",
"ecs:List*",
"eks:Describe*",
"eks:List*",
"cloudwatch:GetMetricStatistics",
"cloudwatch:ListMetrics",
"ce:GetCostAndUsage",
"ce:GetReservationUtilization",
"ce:GetSavingsPlansCoverage",
"pricing:GetProducts",
"organizations:DescribeOrganization",
"tag:GetResources"
],
"Resource": "*"
}
]
}

CloudFormation Template

Deploy the IAM role via CloudFormation:

AWSTemplateFormatVersion: '2010-09-09'
Description: IAM role for Cloud Cost Analyzer

Resources:
CCARole:
Type: AWS::IAM::Role
Properties:
RoleName: CloudCostAnalyzerRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/ReadOnlyAccess
Policies:
- PolicyName: CostExplorerAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ce:GetCostAndUsage
- ce:GetReservationUtilization
- ce:GetSavingsPlansCoverage
- pricing:GetProducts
Resource: '*'

Outputs:
RoleArn:
Value: !GetAtt CCARole.Arn
Description: ARN of the CCA role

Scanning Options

Region Selection

# Scan the default region (us-east-1)
cca scan --provider aws

# Scan a specific region
cca scan --provider aws --regions us-west-2

# Scan multiple regions (comma-separated)
cca scan --provider aws --regions us-east-1,us-west-2,eu-west-1

Filtering

# Limit to specific service categories (comma-separated):
# compute, storage, database, networking, serverless, analytics,
# containers, ai, security, monitoring, cdn, other
cca scan --provider aws --services compute,storage,database

# Filter resources by tag

# Combine filters

To filter findings by severity, re-render a saved dump with cca report --severity ….

AWS-Specific Rules

EC2 Rightsizing

Identifies instances with low CPU/memory utilization:

Rule ID: ec2-rightsizing
Severity: High
Triggers when:
- Average CPU < 10% over 14 days
- Or max CPU < 40% over 14 days

Example finding:

{
"rule_id": "ec2-rightsizing",
"resource_id": "i-0abc123def456",
"title": "Instance oversized",
"recommendation": "Consider downsizing from m5.2xlarge to m5.large",
"estimated_monthly_savings": 210.00,
"metadata": {
"current_type": "m5.2xlarge",
"recommended_type": "m5.large",
"avg_cpu_percent": 8.5,
"max_cpu_percent": 25.0
}
}

EBS Optimization

Unattached Volumes:

Rule ID: ebs-unattached
Triggers when: Volume has no attachment for 7+ days

GP2 to GP3 Migration:

Rule ID: ebs-gp2-to-gp3
Triggers when: Volume is GP2 type
Note: GP3 is 20% cheaper than GP2 for most workloads

Old Snapshots:

Rule ID: ebs-old-snapshots
Triggers when: Snapshot is older than 90 days with no recent access

RDS Optimization

Idle Instances:

Rule ID: rds-idle
Triggers when: Zero connections for 7+ days

Storage Over-provisioned:

Rule ID: rds-storage-overprovisioned
Triggers when: Storage utilization < 20% for 30+ days

S3 Optimization

Missing Lifecycle Policies:

Rule ID: s3-no-lifecycle
Triggers when: Bucket has no lifecycle rules configured

Intelligent Tiering:

Rule ID: s3-intelligent-tiering
Triggers when: Bucket has mixed access patterns

Reserved Instance Coverage

Rule ID: ec2-reserved-opportunity
Triggers when:
- Instance running 24/7 for 30+ days
- No RI coverage for this instance type
- Estimated savings > $50/month

Multi-Account Scanning

AWS Organizations

Scan all accounts in an organization:

# List all accounts
aws organizations list-accounts --query 'Accounts[*].Id' --output text

# Create a role in each account (use StackSets)
# Then scan each account
for account_id in 111111111111 222222222222 333333333333; do
AWS_PROFILE="account-${account_id}" cca scan \
--provider aws \
--mode managed
done

Cross-Account Role

Configure cross-account access in ~/.aws/config:

[profile account-111111111111]
role_arn = arn:aws:iam::111111111111:role/CCARole
source_profile = default

[profile account-222222222222]
role_arn = arn:aws:iam::222222222222:role/CCARole
source_profile = default

Troubleshooting

First: run cca doctor

Before digging in, run the preflight check: it verifies credentials, IAM permissions, and connectivity in one pass and reports exactly what's wrong:

cca doctor --provider aws

If a scan ran but isn't showing up in the dashboard, doctor surfaces the likely cause (invalid API key, expired credentials, a missing read permission, or an unreachable service URL).

"Access Denied" Errors

Check that the IAM policy includes all required permissions:

# Test permissions
aws ec2 describe-instances --dry-run
aws rds describe-db-instances
aws s3 ls

"Region not enabled"

Some regions require opt-in. Enable them in the AWS Console:

# List enabled regions
aws ec2 describe-regions --query 'Regions[*].RegionName'

Slow Scans

For large accounts, scan regions in parallel:

# Using xargs
echo "us-east-1 us-west-2 eu-west-1" | xargs -n1 -P3 -I{} \
cca scan --provider aws --regions {}

Cost Explorer Data Unavailable

Cost Explorer must be enabled in the AWS Console:

  1. Go to AWS Cost Management > Cost Explorer
  2. Click Enable Cost Explorer
  3. Wait 24 hours for data to populate

cca setup --deploy fails to create the role

--deploy needs rights to deploy CloudFormation and create IAM resources (CAPABILITY_NAMED_IAM). An AccessDenied on iam:CreateRole or CloudFormation means you lack provisioning rights, use the hand-off path in Provisioning access. If the session is expired, run aws sso login (or aws configure) first.

Next Steps