Cloud waste scans
What we scan on each cloud
Detailed catalog of every waste check SpendPilot runs for AWS, Azure, and GCP. Each check is tagged Approve & delete, Approve & stop, Approve & change, or Manual fix. OpenAI, Anthropic, and Gemini checks live under AI waste scans.
What a waste scan does
A waste scan inventories cloud resources and billing signals, then opens savings actions for likely idle or oversized spend. Scans are read-only by default — they never change your cloud until an engineer opts in and confirms Approve & delete or Approve & change.
Findings land on Actions (and roll into Can save /mo on Services). After you fix waste, mark verified so Savings reflects confirmed monthly savings.
1. Connect
Link AWS / Azure / GCP under Connections with read access.
2. Sync costs
Pull Cost Explorer / Cost Management / billing export for MTD spend.
3. Scan waste
Run per account. Findings become open actions with estimated $/mo.
4. Act
Approve → fix manually, Approve & delete, or Approve & change when opted in.
Run scans from your dashboard (log in). Review findings on Actions and service rollups on Services.
Amazon Web Services
Uses Cost Explorer, CloudWatch, EC2/RDS/ELB APIs, and Compute Optimizer where available.
Scope Account-level checks once (anomaly, commitments). All other scanners run per selected waste-scan region.
Org / Other
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Spend anomaly anomaly | Yesterday’s spend ≥ ~25% above a recent daily baseline | Cost Explorer daily UnblendedCost vs ~7-day baseline → Manual fix (Cost Explorer / Anomaly Detection; Mark fixed after investigation) | Medium | Manual fix |
RIs & Savings Plans
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Low RI / Savings Plans coverage commitment_coverage | Low Reserved Instance hour coverage and/or Savings Plans coverage | Cost Explorer GetReservationCoverage + GetSavingsPlansCoverage → Manual fix only (FinOps decides in Console; SpendPilot never purchases RIs or Savings Plans) | Medium | Manual fix |
EBS Storage
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Unattached EBS volume unattached_ebs | EBS volumes in available (not attached) state | EC2 DescribeVolumes filtered to available → Approve & delete. Decreasing volume size is not supported by AWS (ModifyVolume cannot shrink) — delete or snapshot+recreate manually if needed. | High | Approve & delete |
Old EBS snapshots old_snapshots | Owned snapshots older than 90 days | EC2 DescribeSnapshots (owner=self) with age filter | Medium | Approve & delete |
gp2 → gp3 opportunity gp2_to_gp3 | gp2 volumes that can move to gp3 in place | EC2 volume type inventory + regional list-price delta | High | Approve & change |
Unused old AMIs old_ami | Owned AMIs >90 days not used by running/stopped instances | EC2 DescribeImages + instance AMI references | Medium | Approve & delete |
EC2 Compute
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle EC2 instance idle_ec2 | Running instances with very low average CPU over 14 days | CloudWatch CPUUtilization + EC2 DescribeInstances | Medium | Approve & stop |
Compute Optimizer rightsizing compute_optimizer | Overprovisioned EC2 / EBS recommendations from AWS | AWS Compute Optimizer APIs — EC2 with recommended instance type → Approve & change; EBS CO findings are manual (no in-place shrink) | Medium | Approve & change |
Idle EKS node group idle_eks | EKS node groups with low CPU — scale-down candidate | EKS + CloudWatch CPU on node group instances | Medium | Manual fix |
Oversized Auto Scaling group oversized_asg | ASGs with low average CPU over 14 days | Auto Scaling + CloudWatch CPU → Approve & change (lower desired/max within safe bounds) | Medium | Approve & change |
EC2 Other
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle Elastic IP idle_eip | EIPs not associated with an instance or ENI | EC2 DescribeAddresses | High | Approve & delete |
Idle NAT Gateway idle_nat | NAT Gateways with ~0 ActiveConnectionCount over 14 days | EC2 + CloudWatch ActiveConnectionCount + route tables → Approve & delete when connections are zero and no routes still target the NAT (typed DELETE; high outage risk) | Medium | Approve & delete |
Unused network interface unused_eni | Available ENIs (excluding AWS-managed interfaces) | EC2 DescribeNetworkInterfaces (status=available) | Medium | Approve & delete |
RDS
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Oversized RDS oversized_rds | Provisioned RDS with low CPU (skips Aurora Serverless) | RDS DescribeDBInstances + CloudWatch CPU → Approve & change (one-step smaller class when mapped) | Medium | Approve & change |
Load Balancing
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Unused load balancer unused_elb | ALB/NLB with no targets, or ALB with ~0 requests over 14 days | ELBv2 target health + CloudWatch RequestCount → Approve & delete when zero registered targets only | Medium | Approve & delete |
ElastiCache
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle ElastiCache idle_elasticache | Cache nodes with low CPU over 14 days | ElastiCache + CloudWatch CPUUtilization | Medium | Manual fix |
VPC / Endpoints
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Unused VPC interface endpoint unused_vpc_endpoint | Interface endpoints with near-zero ActiveConnections | EC2 VPC endpoints + CloudWatch → Approve & delete when ActiveConnections are zero (typed DELETE; PrivateLink outage risk; gateway endpoints skipped) | Medium | Approve & delete |
S3
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Orphaned S3 multipart uploads s3_multipart | Incomplete multipart uploads older than 7 days | S3 ListMultipartUploads across buckets | High | Approve & delete |
OpenSearch
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle OpenSearch domain idle_opensearch | Domains with low CPU over 14 days | OpenSearch + CloudWatch CPU | Medium | Manual fix |
MSK Kafka
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle MSK cluster idle_msk | Clusters with near-zero BytesInPerSec over 14 days | MSK + CloudWatch BytesInPerSec | Medium | Manual fix |
API Gateway
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Unused API Gateway unused_apigateway | REST/HTTP stages with near-zero traffic over 14 days | API Gateway + CloudWatch request metrics | Medium | Manual fix |
SageMaker
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle SageMaker endpoint idle_sagemaker_endpoint | InService endpoint with ~0 Invocations over ~14 days | CloudWatch Invocations → Manual fix (delete endpoint in Console) | Medium | Manual fix |
Bedrock
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle Bedrock provisioned throughput idle_bedrock_throughput | InService provisioned throughput with low Invocations | ListProvisionedModelThroughputs + CloudWatch → Manual fix (delete/downsize) | Medium | Manual fix |
Microsoft Azure
Combines resource inventory (Resource Graph) with optional Monitor metrics for idle VMs and Cost Management for anomalies.
Scope Subscription-scoped via Azure Resource Graph, ARM, and Cost Management. One pass covers the whole subscription.
Org / Other
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Spend anomaly anomaly | Yesterday’s spend spike vs ~7-day Cost Management baseline | Cost Management daily ActualCost → Manual fix (Cost analysis by service; Mark fixed after investigation) | Medium | Manual fix |
Reservations & Savings Plans
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Low Reservation / Savings Plan coverage commitment_coverage | Low share of Reservation + Savings Plan spend vs on-demand (PricingModel, ~30 days) | Cost Management Query by PricingModel → Manual fix only (FinOps; SpendPilot never purchases Reservations or Savings Plans) | Medium | Manual fix |
Virtual Machines
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle virtual machine idle_vm | Running VMs with very low average CPU over 14 days | Resource Graph VMs + Azure Monitor Percentage CPU (capped lookups) → Approve & change when smaller VM size is mapped; Approve & deallocate also available | Medium | Approve & stopApprove & change |
Virtual Machine Scale Sets
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle virtual machine scale set idle_vmss | VMSS with very low average Percentage CPU over 14 days | Resource Graph virtualMachineScaleSets + Azure Monitor → Approve & change when smaller SKU is mapped (capacity unchanged; typed CHANGE; rolling upgrade risk) | Medium | Approve & change |
Storage
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Unattached managed disk unattached_disk | Managed disks in Unattached state | Resource Graph microsoft.compute/disks → Approve & delete; Approve & change when Premium/Ultra → Standard tier map exists | High | Approve & deleteApprove & change |
Old disk snapshots old_disk_snapshot | Managed disk snapshots older than 90 days | Resource Graph microsoft.compute/snapshots | Medium | Approve & delete |
Networking
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Unused public IP unused_public_ip | Public IPs not associated with NIC / LB / NAT / etc. | Resource Graph microsoft.network/publicipaddresses | High | Approve & delete |
Unused load balancer unused_lb | Load balancers with no backend pools or no load-balancing rules (scan). Approve & delete only when both backends and rules are empty at click. | Resource Graph microsoft.network/loadbalancers → Approve & delete (re-check empty backends + rules) | Medium | Approve & delete |
Unused network interface unused_nic | NICs not attached to a VM | Resource Graph microsoft.network/networkinterfaces | Medium | Approve & delete |
Azure Bastion host unused_bastion | Bastion hosts (always-on jump hosts — review before delete) | Resource Graph microsoft.network/bastionhosts | Medium | Manual fix |
Azure SQL
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Oversized Azure SQL oversized_sql | Databases on Premium / high-capacity SKUs (rightsizing candidates) | Resource Graph SQL databases filtered by sku tier/capacity → Approve & change one-step SKU downgrade when mapped | Medium | Approve & change |
Kubernetes (AKS)
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle AKS cluster idle_aks | AKS clusters with zero agent nodes (empty / stopped pools) | Resource Graph microsoft.containerservice/managedclusters | Medium | Manual fix |
API Management
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Unused API Management unused_apim | APIM with no APIs, or leftover Developer SKU gateways | Resource Graph APIM services joined to APIs; SKU check | Mixed | Manual fix |
Cosmos DB
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Empty Cosmos DB account idle_cosmos | Cosmos accounts with no databases or containers | Resource Graph database accounts left-joined to child resources | High | Manual fix |
Azure Cache for Redis
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle Azure Cache for Redis idle_azure_redis | Redis caches with average percentProcessorTime below ~5% over 14 days | Resource Graph microsoft.cache/redis + Monitor percentProcessorTime → Manual fix (scale/delete in Portal) | Medium | Manual fix |
Monitor & Logs
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Long Monitor & Logs retention idle_log_analytics | Log Analytics workspaces or Application Insights with retention ≥ 90 days | Resource Graph Operational Insights workspaces + Insights components | Medium | Manual fix |
App Service
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Empty App Service plan empty_app_service_plan | App Service plans with zero sites (non-Free / Shared SKUs) | Resource Graph microsoft.web/serverfarms (numberOfSites == 0) → Approve & delete or Approve & change (scale SKU down one step when mapped) | High | Approve & deleteApprove & change |
Google Cloud
Uses Compute Engine, Cloud Monitoring, Cloud SQL, and BigQuery billing export APIs.
Scope Project-scoped. Compute/network/disk checks respect selected scan regions (or all enabled). Anomaly needs BigQuery billing export configured on the account.
GCS Approve & change updates the bucket default storage class only — existing objects stay on the prior class until you add lifecycle rules or rewrite.
Org / Other
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Spend anomaly anomaly | Yesterday’s GCP spend spike vs recent baseline | BigQuery billing export → Manual fix (Billing reports by service/label; Mark fixed after investigation) | Medium | Manual fix |
Committed use (CUD)
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Low Committed Use Discount coverage commitment_coverage | Low CUD credits vs compute-like usage cost in BigQuery billing export (~30 days) | Billing export credits.type (COMMITTED_USE*) → Manual fix only (FinOps; SpendPilot never purchases CUDs; SUD is automatic/informational) | Medium | Manual fix |
Compute Engine
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle / stopped Compute Engine VM idle_gce | Stopped/terminated VMs still holding disks & IPs, plus RUNNING VMs with very low average CPU over 14 days | Compute Engine instances.list + Cloud Monitoring CPU utilization (capped lookups) → Approve & change for RUNNING low-CPU VMs when smaller machine type is mapped; Approve & stop also available. Stopped/terminated VMs remain delete-only. | Mixed | Approve & stopApprove & change |
Old custom machine images old_gcp_image | Project custom images older than 90 days | Compute Engine global images with age filter | Medium | Approve & delete |
Cloud Storage / Disk
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Unattached persistent disk unattached_pd | Persistent disks with no attached users | Compute Engine disks.list (users empty) → Approve & delete; Approve & change when pd-ssd/pd-extreme → cheaper type map exists | High | Approve & deleteApprove & change |
GCS default storage class gcs_storage_class | Buckets whose default storage class can step cooler (STANDARD → NEARLINE, NEARLINE → COLDLINE) | Cloud Storage buckets.list → Approve & change default storageClass (typed CHANGE; new objects only — existing objects need lifecycle/rewrite) | Medium | Approve & change |
Old disk snapshots old_disk_snapshot | Global disk snapshots older than 90 days | Compute Engine snapshots.list with age filter | Medium | Approve & delete |
Networking
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Unused static external IP unused_static_ip | External static IPs in RESERVED state | Compute Engine addresses.list (status=RESERVED) | High | Approve & delete |
Idle Cloud NAT idle_cloud_nat | Cloud NAT gateways with little or no byte traffic over 14 days (or sparse metrics) | Compute Engine aggregated routers (nats[]) + Cloud Monitoring NAT bytes | Medium | Manual fix |
Unused load balancer / target pool unused_gcp_lb | Forwarding rules with no backend/target, or empty legacy target pools | Compute Engine aggregated forwardingRules + targetPools → Approve & delete after preflight | Mixed | Approve & delete |
Cloud SQL / AlloyDB
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Stopped Cloud SQL oversized_cloudsql | Cloud SQL instances left in STOPPED state (still billable) | Cloud SQL Admin instances.list filtered by state → Approve & delete when STOPPED/SUSPENDED | High | Approve & delete |
Kubernetes (GKE)
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle GKE cluster idle_gke | GKE clusters with zero nodes (empty / scaled to zero) | Container API projects.locations.clusters.list | Mixed | Manual fix |
Vertex AI
| Check | Detects | How | Confidence | Fix in SpendPilot |
|---|---|---|---|---|
Idle Vertex AI endpoint idle_vertex_endpoint | Deployed endpoint with near-zero prediction traffic | Vertex Endpoints API / metrics → Manual fix (undeploy/delete) | Medium | Manual fix |
What scans do not do
- Purchase Reserved Instances, Savings Plans, Reservations, or CUDs (always Manual fix — never auto-buy)
- Terminate running VMs, delete live Cloud SQL, or act without account opt-in and engineer confirmation
- Delete Log Analytics workspaces, or change Monitor retention / APIM without your Manual fix
- Shrink EBS volumes in place (AWS cannot decrease size via ModifyVolume)
Fix matrix (delete / stop / change / manual)
Every waste check in one table. Opt-in on the cloud account is required for Approve & delete, Approve & stop, and Approve & change. Manual fix is detect-only — you change it in the cloud console.
| Cloud | Check | Service | Fix in SpendPilot |
|---|---|---|---|
| aws | Spend anomaly | Org / Other | Manual fix |
| aws | Low RI / Savings Plans coverage | RIs & Savings Plans | Manual fix |
| aws | Unattached EBS volume | EBS Storage | Approve & delete |
| aws | Old EBS snapshots | EBS Storage | Approve & delete |
| aws | gp2 → gp3 opportunity | EBS Storage | Approve & change |
| aws | Unused old AMIs | EBS Storage | Approve & delete |
| aws | Idle EC2 instance | EC2 Compute | Approve & stop |
| aws | Compute Optimizer rightsizing | EC2 Compute | Approve & change |
| aws | Idle EKS node group | EC2 Compute | Manual fix |
| aws | Oversized Auto Scaling group | EC2 Compute | Approve & change |
| aws | Idle Elastic IP | EC2 Other | Approve & delete |
| aws | Idle NAT Gateway | EC2 Other | Approve & delete |
| aws | Unused network interface | EC2 Other | Approve & delete |
| aws | Oversized RDS | RDS | Approve & change |
| aws | Unused load balancer | Load Balancing | Approve & delete |
| aws | Idle ElastiCache | ElastiCache | Manual fix |
| aws | Unused VPC interface endpoint | VPC / Endpoints | Approve & delete |
| aws | Orphaned S3 multipart uploads | S3 | Approve & delete |
| aws | Idle OpenSearch domain | OpenSearch | Manual fix |
| aws | Idle MSK cluster | MSK Kafka | Manual fix |
| aws | Unused API Gateway | API Gateway | Manual fix |
| aws | Idle SageMaker endpoint | SageMaker | Manual fix |
| aws | Idle Bedrock provisioned throughput | Bedrock | Manual fix |
| azure | Spend anomaly | Org / Other | Manual fix |
| azure | Low Reservation / Savings Plan coverage | Reservations & Savings Plans | Manual fix |
| azure | Idle virtual machine | Virtual Machines | Approve & stopApprove & change |
| azure | Idle virtual machine scale set | Virtual Machine Scale Sets | Approve & change |
| azure | Unattached managed disk | Storage | Approve & deleteApprove & change |
| azure | Old disk snapshots | Storage | Approve & delete |
| azure | Unused public IP | Networking | Approve & delete |
| azure | Unused load balancer | Networking | Approve & delete |
| azure | Unused network interface | Networking | Approve & delete |
| azure | Oversized Azure SQL | Azure SQL | Approve & change |
| azure | Idle AKS cluster | Kubernetes (AKS) | Manual fix |
| azure | Unused API Management | API Management | Manual fix |
| azure | Empty Cosmos DB account | Cosmos DB | Manual fix |
| azure | Idle Azure Cache for Redis | Azure Cache for Redis | Manual fix |
| azure | Long Monitor & Logs retention | Monitor & Logs | Manual fix |
| azure | Empty App Service plan | App Service | Approve & deleteApprove & change |
| azure | Azure Bastion host | Networking | Manual fix |
| gcp | Spend anomaly | Org / Other | Manual fix |
| gcp | Low Committed Use Discount coverage | Committed use (CUD) | Manual fix |
| gcp | Idle / stopped Compute Engine VM | Compute Engine | Approve & stopApprove & change |
| gcp | Old custom machine images | Compute Engine | Approve & delete |
| gcp | Unattached persistent disk | Cloud Storage / Disk | Approve & deleteApprove & change |
| gcp | GCS default storage class | Cloud Storage / Disk | Approve & change |
| gcp | Old disk snapshots | Cloud Storage / Disk | Approve & delete |
| gcp | Unused static external IP | Networking | Approve & delete |
| gcp | Idle Cloud NAT | Networking | Manual fix |
| gcp | Unused load balancer / target pool | Networking | Approve & delete |
| gcp | Stopped Cloud SQL | Cloud SQL / AlloyDB | Approve & delete |
| gcp | Idle GKE cluster | Kubernetes (GKE) | Manual fix |
| gcp | Idle Vertex AI endpoint | Vertex AI | Manual fix |
Shipped Approve & delete
- AWS: unattached_ebs, idle_eip, old_snapshots, old_ami, unused_eni, s3_multipart, unused_elb, idle_nat, unused_vpc_endpoint
- Azure: unattached_disk, unused_public_ip, old_disk_snapshot, unused_nic, empty_app_service_plan, unused_lb
- GCP: unattached_pd, unused_static_ip, old_disk_snapshot, old_gcp_image, unused_gcp_lb, oversized_cloudsql
Shipped Approve & stop
- AWS: idle_ec2 (Stop EC2 instance)
- Azure: idle_vm (Deallocate Azure VM)
- GCP: idle_gce (Stop GCE instance (low-CPU running only))
Shipped Approve & change
- AWS: gp2_to_gp3, compute_optimizer, oversized_rds, oversized_asg
- Azure: idle_vm, unattached_disk, empty_app_service_plan, oversized_sql, idle_vmss
- GCP: idle_gce, unattached_pd, gcs_storage_class