EC2 Rules
These rules identify EC2 instances using outdated types, idle or oversized instances, unassociated Elastic IPs, unused VPC endpoints, expiring Reserved Instances, and long-running instances that may benefit from a generational refresh.
| Rule ID | Scan Type | Name |
|---|---|---|
| CLDBRN-AWS-EC2-1 | Discovery and IaC | EC2 Instance Type Not Preferred |
| CLDBRN-AWS-EC2-2 | IaC | S3 Interface VPC Endpoint Used |
| CLDBRN-AWS-EC2-3 | Discovery | Elastic IP Address Unassociated |
| CLDBRN-AWS-EC2-4 | Discovery | VPC Interface Endpoint Inactive |
| CLDBRN-AWS-EC2-5 | Discovery | EC2 Instance Low Utilization |
| CLDBRN-AWS-EC2-9 | Discovery | EC2 Instance Without Graviton |
| CLDBRN-AWS-EC2-10 | Discovery | EC2 Reserved Instance Expiring |
| CLDBRN-AWS-EC2-11 | Discovery | EC2 Instance Large Size |
| CLDBRN-AWS-EC2-12 | Discovery | EC2 Instance Long Running |
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 are c8, m8, r8, and t4g — current-generation types offering the best price/performance available 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 in the non-preferred list: c1, c3, c4, c5, c5a, c5n, c6a, c6g, c6i, c6in, c7a, c7g, c7i, m1, m2, m3, m4, m5, m5a, m5n, m5zn, m6a, m6g, m6i, m6in, m7a, m7g, m7i, r3, r4, r5, r5a, r5n, r6a, r6g, r6i, r6in, r7a, r7g, r7i, t1, t2, t3, t3a.
How to remediate
Migrate to the equivalent preferred family:
c*family →c8g(e.g.,c6i.large→c8g.large)m*family →m8g(e.g.,m6i.xlarge→m8g.xlarge)r*family →r8g(e.g.,r6i.2xlarge→r8g.2xlarge)t2/t3/t3a→t4g
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 Tool | Resource Type |
|---|---|
| Terraform | aws_instance |
| CloudFormation | AWS::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 Tool | Resource Type |
|---|---|
| Terraform | aws_vpc_endpoint |
| CloudFormation | AWS::EC2::VPCEndpoint |
CLDBRN-AWS-EC2-3
Elastic IP Address Unassociated
Scan type: Discovery
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.
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-9
EC2 Instance Without Graviton
Scan type: Discovery
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 architecture is not arm64 AND the instance family is in the Graviton review set (families that have a direct Graviton equivalent).
How to remediate
- Identify the Graviton equivalent for your instance family (e.g.,
m6i→m6g,c6i→c6g, or migrate all the way tom8g/c8g) - Verify your software stack runs on arm64 — check language runtime support, native libraries, and container image architectures
- Test in a non-production environment, then migrate production using a stop-change-start or blue/green approach
CLDBRN-AWS-EC2-10
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-11
EC2 Instance Large Size
Scan type: Discovery
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 suffix is 2xlarge, 4xlarge, 8xlarge, 12xlarge, 16xlarge, 24xlarge, 32xlarge, 48xlarge, or metal.
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
CLDBRN-AWS-EC2-12
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:
- Check whether the instance type is still the best fit (instance family, size, generation)
- Review utilization to confirm the size is appropriate
- Check whether a Reserved Instance or Savings Plan covers this instance
- If the instance runs a stateless workload, consider replacing it with an auto-scaling group so instances refresh naturally on AMI or type changes
See Also
- CLI discover command — scan live EC2 resources
- CLI scan command — scan IaC templates for EC2 issues
- SDK Reference — run scans programmatically