All Detectors

Complete reference for all 40+ cost optimization and security posture checks in StackSage. Every check runs entirely on your machine or in CI — no data leaves your environment.

40+ Checks

Cost optimization and security posture, in one scan

Privacy First

Runs on your machine or in CI — data never leaves your environment

CloudWatch-Backed

Uses real CloudWatch metrics — not just inventory snapshots

💻Compute

EC2 Idle Instances

High

Instances with <5% average CPU utilization over 14 days. Likely candidates for downsizing or termination.

idle_ec2$50–500/mo per instance

EC2 Generation Upgrade

Medium

Old-generation instance types (t2→t3, m4→m5/m6i, c4→c5/c6i, r4→r5/r6i). Newer generations are 5–15% cheaper with better performance.

ec2_generation_upgrade$10–100/mo per instance

Stopped EC2 Instances

Medium

EC2 instances in stopped state for >14 days. Stopped instances still incur EBS storage costs and block Elastic IP allocations.

stopped_ec2$5–50/mo per instance

Lambda Memory Overprovisioning

Medium

Lambda functions using <30% of their timeout duration while configured with >256 MB memory. Reducing memory allocation saves ~20% in compute costs.

overprovisioned_lambda$10–100/mo per function

Lambda Graviton Migration (arm64)

Low

x86_64 Lambda functions that can migrate to arm64 (Graviton2) for ~20% compute savings with no code changes in most runtimes.

lambda_graviton_migration$1–50/mo per function

EC2 → Serverless Migration Opportunities

Medium

Always-on small EC2 instances (<2 vCPU, <4 GB RAM) with sustained low CPU — candidates for Lambda + API Gateway migration.

architecture_opportunity$5–200/mo per instance

📦Containers

ECS (EC2) → Fargate Migration

Low

ECS services on the EC2 launch type with low task utilization. Moving to Fargate eliminates idle EC2 capacity costs.

ecs_to_fargate$5–200/mo per service

Fargate Spot Opportunities

Low

Fargate services running on standard capacity in environments tagged dev/test/staging. Fargate Spot cuts costs up to 70% for fault-tolerant workloads.

fargate_spot_opportunity$5–200/mo per service

💾Storage

EBS Unattached Volumes

High

Volumes not attached to any running instance for >7 days. Likely forgotten after instance termination.

unattached_ebs$10–200/mo per volume

EBS Overprovisioned Performance

Medium

io1/io2 or gp3 volumes with <20% IOPS or throughput utilization over 14 days. Lowering provisioned IOPS reduces cost without affecting performance.

ebs_overprovisioned_performance$20–500/mo per volume

GP2 to GP3 Migration

Medium

gp2 volumes that can be migrated to gp3 for a straight 20% savings at equivalent baseline IOPS, with the option to provision more IOPS/throughput separately.

gp2_to_gp3_migration$5–100/mo per volume

Old EBS Snapshots

Low

Snapshots older than 90 days with no associated volume. Often leftover from deleted instances or manual backup workflows.

old_snapshot$5–50/mo per snapshot

EBS Snapshot Consolidation

Low

Multiple recent snapshots of the same volume in a short window — suggests redundant manual backups that could be replaced with a lifecycle policy.

snapshot_consolidation$5–30/mo per volume

🗄️Database

RDS Underutilized (CPU + Storage)

High

RDS instances with <10% average CPU and >70% free storage over 14 days. Suggests the instance is significantly oversized and can be downsized.

underutilized_rds$50–500/mo per instance

RDS Low Connections

Medium

RDS instances averaging <10 connections over 14 days — may indicate an orphaned or forgotten database.

rds_low_connections$50–500/mo per instance

DynamoDB Unused Tables

Medium

Tables with zero consumed read/write capacity for 30+ days. Likely test or abandoned tables still incurring provisioned capacity costs.

dynamodb_unused_table$5–200/mo per table

RDS → Aurora Serverless v2 Candidates

Medium

Spiky RDS workloads (high peak CPU, low average CPU) that may benefit from Aurora Serverless v2's auto-scaling capacity units rather than a fixed instance size.

rds_to_aurora_serverless_v2$5–200/mo per instance

Caching

ElastiCache Idle Clusters

Medium

Redis/Memcached clusters with <5% cache hit rate and <2 active connections over 14 days. Likely unused or orphaned.

elasticache_idle_cluster$20–300/mo per cluster

🌐Network

NAT Gateway Advisory

Low

All active NAT gateways are reported as a cost-awareness advisory. NAT gateways incur a fixed hourly charge (~$32/mo) plus per-GB data processing fees.

nat_gatewayVaries by data volume

NAT Gateway Idle

High

NAT gateways processing <1 GB/day over 14 days — likely serving no active traffic and can be deleted.

idle_nat_gateway$30–45/mo per NAT

NAT Gateway Egress Cost

Medium

Active NAT gateways with quantified data-processing costs (CloudWatch BytesOut + BytesIn, extrapolated to 30 days at $0.045/GB). Cross-references missing S3 and DynamoDB VPC endpoints in the same VPC as the primary remediation path.

nat_gateway_egress_costVaries — $0.045/GB eliminated by VPC endpoints

Missing S3 VPC Endpoint

Low

No S3 gateway VPC endpoint detected. Private instances accessing S3 through NAT incur unnecessary data processing charges. Gateway endpoints are free.

missing_s3_vpc_endpoint$5–50/mo

Missing DynamoDB VPC Endpoint

Low

No DynamoDB Gateway VPC endpoint detected. Lambda and ECS workloads in private subnets hitting DynamoDB through NAT incur $0.045/GB data-processing charges. Gateway endpoints are free and eliminate this cost entirely.

missing_dynamodb_vpc_endpoint$5–100/mo

Elastic IP Unattached

Low

Elastic IPs not associated with any running instance. AWS charges $0.005/hr (~$3.60/mo) per idle EIP.

unused_eip$3–5/mo per EIP

Load Balancer Idle

Medium

Classic ELBs, ALBs, and NLBs with <10 requests/day over 14 days. Likely leftover from old deployments.

idle_elb$20–30/mo per LB

Load Balancer Empty Target Groups

High

ALB/NLB load balancers where all target groups have zero healthy targets for >7 days — the load balancer is running but serving no traffic.

lb_empty_target_groups$15–40/mo per load balancer

🌍Content Delivery & DNS

CloudFront Unused Distributions

Low

Distributions receiving <100 total requests over the last 30 days. Often forgotten CDN endpoints from old deployments.

cloudfront_unused_distribution$1–10/mo per distribution

Route 53 Unused Hosted Zones

Medium

Hosted zones with zero DNS queries over the last 90 days. AWS charges $0.50/mo per zone plus per-query fees.

route53_unused_hosted_zone$0.50–50/mo per zone

📋CloudWatch Logs

Log Group Retention Not Set

Medium

CloudWatch Log Groups with 'Never Expire' retention that are storing significant data. Setting a 30/90/365-day retention policy typically saves ~60% of log storage costs.

cloudwatch_logs_retentionVaries by log volume

Log Group: No Metric Filters

Low/Medium

Log groups storing ≥1 GB with zero metric filters defined — stored but never analysed. This is the strongest signal that retention can be reduced aggressively with low operational risk. Pairs with the retention finding to confirm it is safe to act. Reports $0 savings to avoid double-counting with the retention detector.

cloudwatch_logs_no_metric_filtersCorroborating signal — see Log Group Retention finding

🪣S3

S3 Lifecycle Suggestions

Low

S3 buckets with no lifecycle configuration. Adding tiering rules (Standard → Infrequent Access → Glacier) reduces storage costs for buckets with older objects.

s3_lifecycle_suggestionVaries by bucket size

S3 Access Logging Disabled

Low

Buckets without server access logging enabled. Access logs are the prerequisite for confident lifecycle and Intelligent-Tiering decisions — without them, you cannot know object access frequency and any optimisation recommendation is a guess.

s3_access_logging_disabledEnabler — unlocks confident lifecycle and IT decisions

S3 Intelligent-Tiering Not Configured

Low

Buckets with no S3 Intelligent-Tiering configuration. IT automatically moves objects between Frequent and Infrequent Access tiers — no lifecycle rules to manage. Typical savings: 40–60% on infrequently accessed data. Enable access logging first to validate access patterns.

s3_intelligent_tiering_not_configuredVaries — typically 40–60% on IA-eligible data

🛡️Cost Guardrails

AWS Budgets Not Configured

Medium

No AWS Budgets found in the account. Budgets alert you when spend approaches or exceeds a threshold, preventing surprise bills.

cost_guardrail_missing_budgetsPrevents cost overruns

Cost Anomaly Detection Not Enabled

Medium

No AWS Cost Anomaly Detection monitors configured. Anomaly detection uses ML to alert on unexpected spend spikes within hours of them occurring.

cost_guardrail_missing_anomaly_detectionPrevents cost overruns

🔒Security & Posture

Security posture checks run automatically alongside cost checks when a live AWS session is available. They are privacy-first: only aggregate counts and boolean flags are recorded — never raw resource lists or policy documents.

IAM Root MFA & Access Keys

Critical

Checks that root MFA is enabled and root access keys do not exist. Root account compromise is catastrophic — these two checks are non-negotiable baselines.

iam_root_mfa_disabled / iam_root_access_keys_presentSecurity posture

IAM Password Policy

Medium

Verifies a password policy exists and meets baseline requirements: minimum length ≥14, complexity enabled, reuse prevention ≥24, max age ≤90 days.

iam_password_policy_missing / iam_password_policy_weakSecurity posture

IAM Access Key Hygiene

Medium

Detects IAM access keys that are active and older than 90 days (should be rotated) or that have never been used (should be deleted).

iam_access_key_hygieneSecurity posture

Security Groups Open to Internet

High

Identifies security groups with inbound rules allowing 0.0.0.0/0 or ::/0 on sensitive ports (SSH 22, RDP 3389, database ports, Redis 6379, etc.).

sg_open_to_worldSecurity posture

RDS Security Basics

High

Checks each RDS instance for three issues: publicly accessible flag enabled, storage encryption disabled, and backup retention period below 7 days.

rds_publicly_accessible / rds_storage_not_encrypted / rds_backup_retention_lowSecurity posture

S3 Block Public Access

High

Verifies all four S3 account-level Block Public Access settings are enabled, and checks individual bucket policy status for any buckets that appear publicly accessible.

s3_account_public_access_block_disabled / s3_buckets_publicSecurity posture

S3 Default Encryption

Medium

Identifies S3 buckets missing a default encryption configuration (SSE-S3 or SSE-KMS). Older buckets created before AWS made encryption the default may still be missing it.

s3_bucket_default_encryption_missingSecurity posture

CloudTrail Audit Logging

High

Confirms a CloudTrail trail exists, is configured as multi-region, and is actively logging. Without CloudTrail there is no audit record of AWS API calls.

cloudtrail_not_configuredSecurity posture

EBS Encryption by Default

Medium

Checks that the EBS encryption-by-default setting is enabled in the scanned region, ensuring all new volumes and snapshots are automatically encrypted with KMS.

ebs_encryption_by_default_disabledSecurity posture

AWS Config

Medium

Verifies AWS Config is enabled and its recorder is actively recording. Config provides a continuous record of resource configuration changes and is foundational for compliance.

aws_config_not_enabled / aws_config_not_recordingSecurity posture

Amazon GuardDuty

Medium

Checks GuardDuty is enabled in all scanned regions. GuardDuty provides continuous threat detection using ML on CloudTrail logs, VPC Flow Logs, and DNS query logs.

guardduty_not_enabledSecurity posture

CloudWatch Alarms Presence

Low

Flags accounts with no CloudWatch alarms found in any scanned region. Alarms on billing thresholds, error rates, and CPU are basic operational hygiene.

cloudwatch_alarms_missingSecurity posture

AWS Security Hub

Low

Checks Security Hub is enabled in scanned regions. Security Hub aggregates findings from GuardDuty, Inspector, Macie, and third-party tools into a single dashboard.

securityhub_not_enabledSecurity posture

IAM Access Analyzer External Access

High

If IAM Access Analyzer is enabled, reports the count of active external access findings (resources shared outside the account). Only the count is recorded — no policy details.

iam_access_analyzer_external_accessSecurity posture

Incident Response Readiness

Medium

Meta-check: if CloudTrail, GuardDuty, AWS Config, or Security Hub findings are detected above, this finding summarises the combined incident response gap.

incident_response_readiness_lowSecurity posture

🏷️Tagging Compliance

The tagging check is opt-in and must be explicitly enabled in stacksage.yml:

detectors:
  check_tagging_compliance: true

Untagged Resources

Low

EC2 instances, EBS volumes (≥50 GB), and RDS instances with zero user-defined tags. Detects complete absence of tags rather than checking for specific required keys — less opinionated and universally applicable.

untagged_resourceOpt-in

Disabling Detectors

You can suppress specific finding types using the exclusions.detectors key in stacksage.yml. The list accepts the finding type strings shown in each card above:

exclusions:
  detectors:
    - ec2_generation_upgrade    # skip generation upgrade suggestions
    - gp2_to_gp3_migration      # skip gp2→gp3 migration hints
    - nat_gateway               # skip NAT advisory findings

Detector Confidence Levels

Each finding includes a confidence score (0.0–1.0) indicating estimated accuracy:

  • 0.90–1.00: Very high — deterministic checks (e.g., unattached EBS, unused EIP)
  • 0.75–0.89: High — metric-based with clear signal (e.g., low CloudWatch utilization)
  • 0.60–0.74: Medium — heuristic or indirect evidence; verify before acting

Related Topics