Waste scans
What we scan on each cloud
Detailed catalog of every waste check SpendPilot runs for AWS, Azure, and GCP — by service, how it works, and whether Approve & delete is available.
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 into approve-then-delete and confirms.
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 Cloud accounts 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, or Approve & delete when remediable + 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.
21 checks · 3 remediable
Org / Other
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Spend anomaly anomaly | Yesterday’s spend ≥ ~25% above a recent daily baseline | Cost Explorer daily UnblendedCost vs ~7-day baseline | Medium | No |
RIs & Savings Plans
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Low RI / Savings Plans coverage commitment_coverage | Low Reserved Instance hour coverage and/or Savings Plans coverage | Cost Explorer reservation and Savings Plans coverage metrics | Medium | No |
EBS Storage
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Unattached EBS volume unattached_ebs | EBS volumes in available (not attached) state | EC2 DescribeVolumes filtered to available | High | Yes* |
Old EBS snapshots old_snapshots | Owned snapshots older than 90 days | EC2 DescribeSnapshots (owner=self) with age filter | Medium | Yes* |
gp2 → gp3 opportunity gp2_to_gp3 | gp2 volumes that can move to gp3 in place | EC2 volume type inventory + regional list-price delta | Medium | No |
Unused old AMIs old_ami | Owned AMIs >90 days not used by running/stopped instances | EC2 DescribeImages + instance AMI references | Medium | No |
EC2 Compute
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Idle EC2 instance idle_ec2 | Running instances with very low average CPU over 14 days | CloudWatch CPUUtilization + EC2 DescribeInstances | Medium | No |
Compute Optimizer rightsizing compute_optimizer | Overprovisioned EC2 / EBS recommendations from AWS | AWS Compute Optimizer APIs (when enabled in the account) | Medium | No |
Idle EKS node group idle_eks | EKS node groups with low CPU — scale-down candidate | EKS + CloudWatch CPU on node group instances | Medium | No |
Oversized Auto Scaling group oversized_asg | ASGs with low average CPU over 14 days | Auto Scaling + CloudWatch CPU | Medium | No |
EC2 Other
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Idle Elastic IP idle_eip | EIPs not associated with an instance or ENI | EC2 DescribeAddresses | High | Yes* |
Idle NAT Gateway idle_nat | NAT Gateways with ~0 ActiveConnectionCount over 14 days | EC2 + CloudWatch ActiveConnectionCount | Medium | No |
Unused network interface unused_eni | Available ENIs (excluding AWS-managed interfaces) | EC2 DescribeNetworkInterfaces (status=available) | Medium | No |
RDS
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Oversized RDS oversized_rds | Provisioned RDS with low CPU (skips Aurora Serverless) | RDS DescribeDBInstances + CloudWatch CPU | Medium | No |
Load Balancing
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Unused load balancer unused_elb | ALB/NLB with no targets, or ALB with ~0 requests over 14 days | ELBv2 target health + CloudWatch RequestCount | Medium | No |
ElastiCache
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Idle ElastiCache idle_elasticache | Cache nodes with low CPU over 14 days | ElastiCache + CloudWatch CPUUtilization | Medium | No |
VPC / Endpoints
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Unused VPC interface endpoint unused_vpc_endpoint | Interface endpoints with near-zero ActiveConnections | EC2 VPC endpoints + CloudWatch (gateway endpoints skipped) | Medium | No |
S3
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Orphaned S3 multipart uploads s3_multipart | Incomplete multipart uploads older than 7 days | S3 ListMultipartUploads across buckets | High | No |
OpenSearch
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Idle OpenSearch domain idle_opensearch | Domains with low CPU over 14 days | OpenSearch + CloudWatch CPU | Medium | No |
MSK Kafka
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Idle MSK cluster idle_msk | Clusters with near-zero BytesInPerSec over 14 days | MSK + CloudWatch BytesInPerSec | Medium | No |
API Gateway
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Unused API Gateway unused_apigateway | REST/HTTP stages with near-zero traffic over 14 days | API Gateway + CloudWatch request metrics | Medium | No |
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.
12 checks · 2 remediable
Org / Other
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Spend anomaly anomaly | Yesterday’s spend spike vs ~7-day Cost Management baseline | Cost Management daily ActualCost query | Medium | No |
Virtual Machines
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Idle virtual machine idle_vm | Running VMs with very low average CPU over 14 days | Resource Graph VMs + Azure Monitor Percentage CPU (capped lookups) | Medium | No |
Storage
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Unattached managed disk unattached_disk | Managed disks in Unattached state | Resource Graph microsoft.compute/disks | High | Yes* |
Old disk snapshots old_disk_snapshot | Managed disk snapshots older than 90 days | Resource Graph microsoft.compute/snapshots | Medium | No |
Networking
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Unused public IP unused_public_ip | Public IPs not associated with NIC / LB / NAT / etc. | Resource Graph microsoft.network/publicipaddresses | High | Yes* |
Unused load balancer unused_lb | Load balancers with no backend pools or rules | Resource Graph microsoft.network/loadbalancers | Medium | No |
Unused network interface unused_nic | NICs not attached to a VM | Resource Graph microsoft.network/networkinterfaces | Medium | No |
Azure SQL
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Oversized Azure SQL oversized_sql | Databases on Premium / high-capacity SKUs (rightsizing candidates) | Resource Graph SQL databases filtered by sku tier/capacity | Medium | No |
Kubernetes (AKS)
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Idle AKS cluster idle_aks | AKS clusters with zero agent nodes (empty / stopped pools) | Resource Graph microsoft.containerservice/managedclusters | Medium | No |
API Management
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Unused API Management unused_apim | APIM with no APIs, or leftover Developer SKU gateways | Resource Graph APIM services joined to APIs; SKU check | Mixed | No |
Cosmos DB
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Empty Cosmos DB account idle_cosmos | Cosmos accounts with no databases or containers | Resource Graph database accounts left-joined to child resources | High | No |
Monitor & Logs
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
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 | No |
Google Cloud
Uses Compute Engine, Cloud SQL, and BigQuery billing export APIs. Idle GKE is reserved in the product model but not scanned yet.
Scope Project-scoped. Compute/network/disk checks respect selected scan regions (or all enabled). Anomaly needs BigQuery billing export configured on the account.
9 checks · 2 remediable
Cloud Monitoring metrics for low-CPU running VMs are not scanned yet — idle_gce today flags stopped/terminated VMs only.
Org / Other
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Spend anomaly anomaly | Yesterday’s GCP spend spike vs recent baseline | BigQuery billing export (dataset + table on the GCP account) | Medium | No |
Compute Engine
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Stopped / terminated GCE VM idle_gce | VMs that are stopped or terminated but still hold disks & IPs | Compute Engine instances.list filtered by status | Medium | No |
Old custom machine images old_gcp_image | Project custom images older than 90 days | Compute Engine global images with age filter | Medium | No |
Cloud Storage / Disk
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Unattached persistent disk unattached_pd | Persistent disks with no attached users | Compute Engine disks.list (users empty) | High | Yes* |
Old disk snapshots old_disk_snapshot | Global disk snapshots older than 90 days | Compute Engine snapshots.list with age filter | Medium | No |
Networking
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Unused static external IP unused_static_ip | External static IPs in RESERVED state | Compute Engine addresses.list (status=RESERVED) | High | Yes* |
Unused load balancer / target pool unused_gcp_lb | Forwarding rules with no backend/target, or empty legacy target pools | Compute Engine aggregated forwardingRules + targetPools | Mixed | No |
Cloud SQL / AlloyDB
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Stopped Cloud SQL oversized_cloudsql | Cloud SQL instances left in STOPPED state (still billable) | Cloud SQL Admin instances.list filtered by state | High | No |
Kubernetes (GKE)
| Check | Detects | How | Confidence | Delete |
|---|---|---|---|---|
Idle GKE cluster idle_gke | GKE clusters with zero nodes (empty / scaled to zero) | Container API projects.locations.clusters.list | Mixed | No |
Approve & delete (optional)
Only the checks marked Yes*can be deleted from SpendPilot. You must enable remediation on the cloud account and an engineer must confirm Approve & delete. Everything else is fix manually, then mark fixed / verified.
AWS
Delete EBS volume
unattached_ebs
Release Elastic IP
idle_eip
Delete EBS snapshot
old_snapshots
Azure
Delete managed disk
unattached_disk
Delete public IP
unused_public_ip
GCP
Delete persistent disk
unattached_pd
Release static IP
unused_static_ip
IAM / role setup for read-only and optional delete: Cloud access setup →
What scans do not do
- Purchase Reserved Instances or Savings Plans
- Automatically rightsize or stop idle compute (manual workflow)
- Change Monitor retention or APIM SKUs without your action
- Run delete without account opt-in and engineer confirmation