CloudBurn Rules

EC2 Rules

CloudBurn cost optimization rules for AWS EC2.

These rules identify EC2 instances using outdated types, idle or oversized instances, unassociated Elastic IPs, unused VPC endpoints, expiring Reserved Instances, long-running instances that may benefit from a generational refresh, instances with unnecessary detailed monitoring, and idle NAT gateways.

Rule IDScan TypeName
CLDBRN-AWS-EC2-1Discovery and IaCEC2 Instance Type Not Preferred
CLDBRN-AWS-EC2-2IaCS3 Interface VPC Endpoint Used
CLDBRN-AWS-EC2-3Discovery and IaCElastic IP Address Unassociated
CLDBRN-AWS-EC2-4DiscoveryVPC Interface Endpoint Inactive
CLDBRN-AWS-EC2-5DiscoveryEC2 Instance Low Utilization
CLDBRN-AWS-EC2-6Discovery and IaCEC2 Instance Without Graviton
CLDBRN-AWS-EC2-7DiscoveryEC2 Reserved Instance Expiring
CLDBRN-AWS-EC2-8Discovery and IaCEC2 Instance Large Size
CLDBRN-AWS-EC2-9DiscoveryEC2 Instance Long Running
CLDBRN-AWS-EC2-10IaCEC2 Instance Detailed Monitoring Enabled
CLDBRN-AWS-EC2-11DiscoveryNAT Gateway Idle

CLDBRN-AWS-EC2-1

EC2 Instance Type Not Preferred

Scan type: Discovery and IaC

What it checks

Flags EC2 instances not using the curated set of preferred instance families. The preferred families cover current-generation Graviton4, AMD, and Intel variants across compute, general purpose, memory, and burstable categories — the types offering the best price/performance on EC2 today.

Why it matters

Older instance families (c1-c7, m1-m7, r3-r7, t1-t3a) are less efficient, often more expensive, and in some cases on a deprecation path. Current-generation Graviton4 instances (m8g, c8g, r8g) provide 30-40% better performance per dollar than the previous generation at the same or lower price.

What triggers a finding

The instance family is classified as non-preferred. The full non-preferred list:

  • Compute: c1, c3, c4, c5, c5a, c5ad, c5d, c5n, c6a, c6g, c6gd, c6gn, c6i, c6id, c6in, c7a, c7g, c7gd, c7gn, c7i, c7i-flex, c7in
  • General purpose: m1, m2, m3, m4, m5, m5a, m5ad, m5d, m5dn, m5n, m5zn, m6a, m6g, m6gd, m6i, m6id, m6idn, m6in, m7a, m7d, m7g, m7gd, m7i, m7i-flex, m7in
  • Memory: r3, r4, r5, r5a, r5ad, r5b, r5d, r5dn, r5n, r6a, r6g, r6gd, r6i, r6id, r6idn, r6in, r7a, r7g, r7gd, r7i, r7iz
  • Burstable: t1, t2, t3, t3a

The preferred families are: c8a, c8g, c8gb, c8gd, c8gn, c8i, c8id, c8i-flex, m8a, m8azn, m8g, m8gb, m8gd, m8gn, m8i, m8id, m8i-flex, r8a, r8g, r8gb, r8gd, r8gn, r8i, r8id, r8i-flex, and t4g.

Instance families not in either list are treated as unclassified and skipped.

How to remediate

Migrate to the equivalent preferred family:

  • c* family → c8g (e.g., c6i.largec8g.large)
  • m* family → m8g (e.g., m6i.xlargem8g.xlarge)
  • r* family → r8g (e.g., r6i.2xlarger8g.2xlarge)
  • t2/t3/t3at4g

Test your workload with the new instance type in a non-production environment first, particularly if migrating from x86 to Graviton (arm64).

IaC resources checked

IaC ToolResource Type
Terraformaws_instance
CloudFormationAWS::EC2::Instance

CLDBRN-AWS-EC2-2

S3 Interface VPC Endpoint Used

Scan type: IaC

What it checks

Flags VPC endpoints for S3 that are configured as Interface type. S3 has a free Gateway endpoint option that provides the same access within a VPC without any hourly or data-processing charges.

Why it matters

A VPC Interface endpoint for S3 costs approximately $0.01/hour per AZ (~$7/month per AZ) plus $0.01/GB processed. A three-AZ deployment costs $21+/month just in endpoint hours. The S3 Gateway endpoint is free and functionally equivalent for most use cases — it does not traverse the internet and does not require a NAT gateway.

What triggers a finding

A VPC endpoint resource has a serviceName ending in .s3 AND a VpcEndpointType (or type) of Interface.

How to remediate

Replace the S3 Interface endpoint with a Gateway endpoint:

# Replace this:
resource "aws_vpc_endpoint" "s3_interface" {
  vpc_id            = aws_vpc.main.id
  service_name      = "com.amazonaws.us-east-1.s3"
  vpc_endpoint_type = "Interface"
}

# With this:
resource "aws_vpc_endpoint" "s3_gateway" {
  vpc_id            = aws_vpc.main.id
  service_name      = "com.amazonaws.us-east-1.s3"
  vpc_endpoint_type = "Gateway"
  route_table_ids   = [aws_route_table.main.id]
}

IaC resources checked

IaC ToolResource Type
Terraformaws_vpc_endpoint
CloudFormationAWS::EC2::VPCEndpoint

CLDBRN-AWS-EC2-3

Elastic IP Address Unassociated

Scan type: Discovery and IaC

What it checks

Flags Elastic IP addresses not associated with any resource. Unassociated EIPs incur a charge that was introduced by AWS in February 2024 to encourage cleanup of unused addresses.

Why it matters

As of February 2024, AWS charges $0.005/hour (~$3.65/month) for every Elastic IP that is not associated with a running instance. An account with 10 unassociated EIPs pays $36.50/month with no benefit.

What triggers a finding

The EIP has no associationId, instanceId, or networkInterfaceId.

How to remediate

Release unassociated EIPs:

aws ec2 release-address --allocation-id eipalloc-0abc1234def56789

If you need to preserve the IP address for future use, note that AWS does not guarantee that a released IP will be available to re-allocate. Plan accordingly before releasing.

IaC resources checked

IaC ToolResource Type
Terraformaws_eip
Terraformaws_eip_association
CloudFormationAWS::EC2::EIP
CloudFormationAWS::EC2::EIPAssociation

CLDBRN-AWS-EC2-4

VPC Interface Endpoint Inactive

Scan type: Discovery

What it checks

Flags VPC Interface endpoints that processed zero bytes in the past 30 days. An endpoint with no traffic is not serving any workload but continues to incur hourly charges for each Availability Zone it is provisioned in.

Why it matters

Each VPC Interface endpoint AZ costs approximately $0.01/hour (~$7/month per AZ). An endpoint active across 3 AZs costs ~$21/month. An endpoint that has processed no traffic in 30 days is almost certainly unused.

What triggers a finding

bytesProcessedLast30Days is 0.

How to remediate

Delete the inactive VPC endpoint:

aws ec2 delete-vpc-endpoints --vpc-endpoint-ids vpce-0abc1234def56789

Before deleting, verify no application is using the endpoint by checking security group rules and DNS resolution for the endpoint service name. An endpoint can show zero bytes if traffic recently stopped but the endpoint is still referenced.


CLDBRN-AWS-EC2-5

EC2 Instance Low Utilization

Scan type: Discovery

What it checks

Flags EC2 instances with consistently low CPU and network utilization. Instances running at very low utilization are likely oversized for their workload or are no longer actively used.

Why it matters

An oversized EC2 instance pays for compute capacity that is not being used. A m7i.4xlarge idling at 2% CPU costs ~$550/month and could potentially be replaced by a m7i.large at ~$70/month — an 87% reduction for the same workload.

What triggers a finding

lowUtilizationDays is 4 or more out of the past 14 days. A day is counted as low-utilization when both CPU and network metrics are below their respective thresholds.

How to remediate

  • Rightsize the instance to a smaller type that matches actual utilization
  • If the instance is a dev/test server, schedule it to stop outside working hours
  • If the instance hosts a workload that would fit on Lambda or containers, consider migrating to reduce the minimum footprint
  • Use AWS Compute Optimizer recommendations as a starting point for rightsizing decisions

CLDBRN-AWS-EC2-6

EC2 Instance Without Graviton

Scan type: Discovery and IaC

What it checks

Flags EC2 instances not using the arm64 architecture when a Graviton equivalent exists for the instance family. This rule covers a broader set of instances than CLDBRN-AWS-EC2-1, specifically targeting the Graviton migration opportunity regardless of generation.

Why it matters

Graviton instances (arm64) typically offer 20-40% better price/performance than equivalent x86 instance types. Moving to Graviton is often the single highest-impact optimization available for compute-heavy workloads.

What triggers a finding

The instance has a known architecture that is not arm64 AND the instance family is in the Graviton review set (families that have a direct Graviton equivalent). Instances with no reported architecture are skipped.

How to remediate

  1. Identify the Graviton equivalent for your instance family (e.g., m6im6g, c6ic6g, or migrate all the way to m8g/c8g)
  2. Verify your software stack runs on arm64 — check language runtime support, native libraries, and container image architectures
  3. Test in a non-production environment, then migrate production using a stop-change-start or blue/green approach

IaC resources checked

IaC ToolResource Type
Terraformaws_instance
CloudFormationAWS::EC2::Instance

CLDBRN-AWS-EC2-7

EC2 Reserved Instance Expiring

Scan type: Discovery

What it checks

Flags active EC2 Reserved Instances that expire within 60 days. An expiring RI that covers a running instance will revert to On-Demand pricing on expiration, potentially causing a significant cost increase.

Why it matters

RIs provide up to 72% discount compared to On-Demand. When an RI expires, the covered instance immediately starts billing at the full On-Demand rate. For a production instance, this can mean hundreds of dollars per month in unexpected charges if the expiration is not handled proactively.

What triggers a finding

The RI state is active AND the endTime is within 60 days from today.

How to remediate

Review each expiring RI and decide:

  • Renew: Purchase a new RI for the same instance type and term
  • Exchange: Use RI Convertible exchanges to change to a different instance type before expiration if your instance type needs have changed
  • Switch to Savings Plans: Compute Savings Plans are more flexible than RIs and often a better long-term strategy
  • Let expire: If the instance will be decommissioned before the RI expires, no action needed

CLDBRN-AWS-EC2-8

EC2 Instance Large Size

Scan type: Discovery and IaC

What it checks

Flags EC2 instances sized 2xlarge or larger, including metal instances. Large instances are significantly more expensive than smaller ones and should be reviewed regularly to confirm the size is justified.

Why it matters

Large instances can cost 4–64x as much as smaller equivalents. A m7i.16xlarge costs ~$2,400/month vs ~$150/month for a m7i.large. While large sizes are sometimes necessary, they are also commonly over-provisioned — sized for a peak load that never materializes or left over from an early sizing decision that was never revisited.

What triggers a finding

Instance size is any Nxlarge where N >= 2 (e.g. 2xlarge, 4xlarge, 8xlarge, 16xlarge) or any metal variant. The check is regex-based, so it catches all current and future large size suffixes.

How to remediate

  • Review CloudWatch CPU, memory (from CloudWatch Agent), and network metrics for the past 30 days
  • If utilization is consistently low, downsize to the next smaller type
  • Consider Savings Plans if the instance will remain large — Compute Savings Plans apply to any size or family

IaC resources checked

IaC ToolResource Type
Terraformaws_instance
CloudFormationAWS::EC2::Instance

CLDBRN-AWS-EC2-9

EC2 Instance Long Running

Scan type: Discovery

What it checks

Flags EC2 instances that have been running continuously for 180 or more days. Long-running instances may be using outdated instance types, missing the opportunity for newer-generation cost improvements, or running workloads that could be better served by auto-scaling or managed services.

Why it matters

EC2 instance generations improve approximately every 2 years. An instance running for 6+ months on a previous generation is likely paying more than necessary compared to a current-generation equivalent. Long-running instances are also less likely to be rightsized since they were provisioned once and never revisited.

What triggers a finding

Instance state is running AND launchTime is 180 or more days ago.

How to remediate

Use this finding as a prompt for a regular instance review:

  1. Check whether the instance type is still the best fit (instance family, size, generation)
  2. Review utilization to confirm the size is appropriate
  3. Check whether a Reserved Instance or Savings Plan covers this instance
  4. If the instance runs a stateless workload, consider replacing it with an auto-scaling group so instances refresh naturally on AMI or type changes

CLDBRN-AWS-EC2-10

EC2 Instance Detailed Monitoring Enabled

Scan type: IaC

What it checks

Flags EC2 instances that explicitly enable detailed monitoring. Detailed monitoring sends metrics to CloudWatch at 1-minute intervals instead of the default 5 minutes, adding CloudWatch cost.

Why it matters

Basic monitoring (5-minute intervals) is free. Detailed monitoring adds $3.50/month per instance for the first 10 metrics. For fleets of hundreds of instances, this adds up. Unless you need 1-minute granularity for autoscaling responsiveness or alerting precision, basic monitoring is sufficient.

What triggers a finding

detailedMonitoringEnabled is true.

How to remediate

Remove or set monitoring = false in your Terraform config, or remove Monitoring.Enabled: true in your CloudFormation template. Only enable detailed monitoring on instances where 1-minute metric granularity is genuinely required.

IaC resources checked

IaC ToolResource Type
Terraformaws_instance
CloudFormationAWS::EC2::Instance

CLDBRN-AWS-EC2-11

NAT Gateway Idle

Scan type: Discovery

What it checks

Flags available NAT gateways whose inbound and outbound traffic both stay at zero for 7 consecutive days.

Why it matters

NAT gateways charge $0.045/hour (~$32/month) just to exist, plus $0.045/GB for data processed. An idle NAT gateway costs $32/month with no data flowing through it. Idle gateways typically remain after VPC refactoring, subnet consolidation, or when the private resources they served have been decommissioned.

What triggers a finding

state is available AND bytesInFromDestinationLast7Days is 0 AND bytesOutToDestinationLast7Days is 0.

How to remediate

Verify no private subnet route tables still reference the NAT gateway. If the gateway is genuinely unused, delete it and its associated Elastic IP allocation.

aws ec2 delete-nat-gateway --nat-gateway-id nat-0abc123

See Also