Amazon EC2 – Consider Using a Preferred Instance Type

·

By

Infracost

Amazon EC2 – Consider Using a Preferred Instance Type

·

By

Infracost

The Amazon EC2 preferred instance type policy restricts Amazon EC2 instances to an approved list that meets your organization's cost, performance, and support requirements. Without that list, engineers pick instance types ad hoc, which leads to oversized instances, fragmented Reserved Instance coverage, and unpredictable spend. Use this policy when several teams provision EC2 through Terraform and you want instance choices to follow one standard.

Attribute

Detail

Cloud Provider

Amazon Web Services (AWS)

Resource Type

Amazon EC2 instances and launch templates

Terraform Attribute

instance_type on aws_instance and aws_launch_template

Compliant Value

Any instance type on your organization's approved list, for example t3.* in development or m5.largein production

Cost Impact

Prevents oversized and non-standard instances; one m5.4xlarge costs about 8x an m5.large on-demand

Why This Policy Matters

How It Helps Reduce Cloud Costs

Amazon EC2 offers hundreds of instance types across general purpose, compute optimized, memory optimized, storage optimized, and accelerated families. Every one of them is a valid value for instance_type in Terraform.

That freedom is the problem. Engineers copy an instance type from an old module, a blog post, or a production stack, and nobody checks whether the workload needs it.

An Amazon EC2 preferred instance type policy narrows the choice to a short list your platform team has already vetted for price, performance, and support. This policy turns instance selection from a per-engineer guess into an organization-wide standard.

A preferred list also protects commitment discounts. Standard Reserved Instances apply to a specific instance family in a region. Usage that drifts to unplanned families falls outside that coverage and bills at on-demand rates.

This policy is most useful in multi-team AWS organizations. A single team with a handful of instances gets less value from a formal list.

Potential Savings

The Amazon EC2 preferred instance type policy saves money mainly by preventing oversizing and by keeping usage inside existing commitments.

The size gap inside a single family is large. On-demand Linux pricing in US East (N. Virginia) shows how quickly cost scales with size.

Instance type

vCPUs

Memory

Approx. monthly on-demand cost (US East, Linux)

t3.medium

2

4 GiB

$30

m5.large

2

8 GiB

$70

m5.xlarge

4

16 GiB

$140

m5.4xlarge

16

64 GiB

$561

Monthly figures assume 730 hours at on-demand Linux rates. Prices vary by region, operating system, and purchase option.

A development instance launched as m5.4xlarge when m5.large would do wastes about $490 per month. Ten of those across a few teams add up to nearly $59,000 per year.

Actual savings depend on how far current usage has drifted from a sensible standard. Organizations with no instance guardrails usually see the largest gains.

Implementation Guide

Infrastructure-as-Code Example (Terraform)

The following examples show an Amazon EC2 instance using a type outside the approved list, and the corrected configuration that enforces the list in Terraform.

Non-compliant configuration: unrestricted instance type

variable "instance_type" {
  type    = string
  default = "m5.4xlarge"
}

resource "aws_instance" "api" {
  ami           = var.ami_id
  instance_type = var.instance_type
  subnet_id     = aws_subnet.private.id

  tags = {
    Name        = "api-dev-01"
    Environment = "dev"
  }
}
variable "instance_type" {
  type    = string
  default = "m5.4xlarge"
}

resource "aws_instance" "api" {
  ami           = var.ami_id
  instance_type = var.instance_type
  subnet_id     = aws_subnet.private.id

  tags = {
    Name        = "api-dev-01"
    Environment = "dev"
  }
}
variable "instance_type" {
  type    = string
  default = "m5.4xlarge"
}

resource "aws_instance" "api" {
  ami           = var.ami_id
  instance_type = var.instance_type
  subnet_id     = aws_subnet.private.id

  tags = {
    Name        = "api-dev-01"
    Environment = "dev"
  }
}

Any value passes here, including a 16 vCPU instance for a development API. This is a common source of wasted spend, because the default gets copied into every new environment.

Compliant configuration: approved instance types enforced with a validation block

variable "instance_type" {
  type        = string
  default     = "t3.medium"
  description = "Must be an approved Amazon EC2 instance type."

  validation {
    condition     = contains(["t3.medium", "t3.large", "m5.large", "m5.xlarge"], var.instance_type)
    error_message = "instance_type must be one of: t3.medium, t3.large, m5.large, m5.xlarge."
  }
}

resource "aws_instance" "api" {
  ami           = var.ami_id
  instance_type = var.instance_type
  subnet_id     = aws_subnet.private.id

  tags = {
    Name        = "api-dev-01"
    Environment = "dev"
  }
}
variable "instance_type" {
  type        = string
  default     = "t3.medium"
  description = "Must be an approved Amazon EC2 instance type."

  validation {
    condition     = contains(["t3.medium", "t3.large", "m5.large", "m5.xlarge"], var.instance_type)
    error_message = "instance_type must be one of: t3.medium, t3.large, m5.large, m5.xlarge."
  }
}

resource "aws_instance" "api" {
  ami           = var.ami_id
  instance_type = var.instance_type
  subnet_id     = aws_subnet.private.id

  tags = {
    Name        = "api-dev-01"
    Environment = "dev"
  }
}
variable "instance_type" {
  type        = string
  default     = "t3.medium"
  description = "Must be an approved Amazon EC2 instance type."

  validation {
    condition     = contains(["t3.medium", "t3.large", "m5.large", "m5.xlarge"], var.instance_type)
    error_message = "instance_type must be one of: t3.medium, t3.large, m5.large, m5.xlarge."
  }
}

resource "aws_instance" "api" {
  ami           = var.ami_id
  instance_type = var.instance_type
  subnet_id     = aws_subnet.private.id

  tags = {
    Name        = "api-dev-01"
    Environment = "dev"
  }
}

The validation block makes terraform plan fail when a caller passes a type outside the list. It only protects modules that use it, so instance types hardcoded elsewhere still slip through.

The same rule applies to launch templates used by Auto Scaling groups:

resource "aws_launch_template" "workers" {
  name_prefix   = "workers-"
  image_id      = var.ami_id
  instance_type = "m5.large" # previously "m5.4xlarge"
}
resource "aws_launch_template" "workers" {
  name_prefix   = "workers-"
  image_id      = var.ami_id
  instance_type = "m5.large" # previously "m5.4xlarge"
}
resource "aws_launch_template" "workers" {
  name_prefix   = "workers-"
  image_id      = var.ami_id
  instance_type = "m5.large" # previously "m5.4xlarge"
}

Step-by-Step Fix Instructions

  1. Agree on an approved Amazon EC2 instance type list with platform, FinOps, and application owners. Separate lists for production and non-production are common.

  2. Search Terraform code for every instance_type value on aws_instance and aws_launch_template, including variable defaults and .tfvars files. In an aws_autoscaling_group, override blocks inside mixed_instances_policy also set instance_type.

  3. Compare each value against the approved list and flag every mismatch.

  4. For each mismatch, check CPU and memory utilization in Amazon CloudWatch before choosing the replacement type.

  5. Change the instance_type value and run terraform plan. For aws_instance, the plan should show an in-place update, which stops and starts the instance.

  6. Apply the change during a maintenance window, then validate the workload.

  7. Add validation blocks to shared modules, and add Infracost to CI/CD so new non-approved instance types are flagged in pull requests.

Best Practices

  • Keep the approved list short. A list with 40 types gives engineers little guidance.

  • Use wildcards by family for non-production, such as all t3 sizes, and exact sizes for production.

  • Align the list with existing Reserved Instances and Savings Plans, so new usage lands inside committed capacity.

  • Include current-generation types only. A preferred list that still contains older generations locks in higher prices.

  • Document an exception path. Some workloads need GPU, high-memory, or storage-optimized types, and a blocked engineer with no exception path will route around the policy.

  • This policy sets the boundary of acceptable choices. It does not replace rightsizing inside that boundary.

Tools and Scripts

How does Infracost detect non-approved Amazon EC2 instance types automatically?

The Amazon EC2 preferred instance type policy is available in Infracost, including in the free trial. Teams enable the preferred instance types policy in Infracost Cloud under Governance > FinOps policies, then enter approved region and instance type pairs, one per line.

Infracost supports wildcards in that list. For example, *:t3.* allows any t3 size in any region, and us-east-1:* allows any type in US East. More specific patterns override broader ones.

When Infracost runs in CI/CD, it checks Amazon EC2 resources in each pull request. Any instance type outside the approved list appears in the PR comment with the file and line to change.

Teams choose whether a violation warns the engineer or blocks the pull request. They can also limit the check to new resources, so an unrelated pull request does not force a risky instance change.

Infracost also scans connected repositories for non-approved instance types already in code. FinOps and platform teams can burn down that backlog over time and track issues prevented before merge separately from existing issues fixed.

To list instance types already used in Terraform code:

grep -rnE 'instance_type\s*=' --include="*.tf" --include="*.tfvars"
grep -rnE 'instance_type\s*=' --include="*.tf" --include="*.tfvars"
grep -rnE 'instance_type\s*=' --include="*.tf" --include="*.tfvars"

To count running Amazon EC2 instances by instance type with the AWS CLI:

aws ec2 describe-instances \
  --filters "Name=instance-state-name,Values=running" \
  --query "Reservations[].Instances[].InstanceType" \
  --output text | tr '\t' '\n' | sort | uniq -c | sort -rn
aws ec2 describe-instances \
  --filters "Name=instance-state-name,Values=running" \
  --query "Reservations[].Instances[].InstanceType" \
  --output text | tr '\t' '\n' | sort | uniq -c | sort -rn
aws ec2 describe-instances \
  --filters "Name=instance-state-name,Values=running" \
  --query "Reservations[].Instances[].InstanceType" \
  --output text | tr '\t' '\n' | sort | uniq -c | sort -rn

AWS also offers runtime controls. A Service Control Policy can deny ec2:RunInstances when the ec2:InstanceTypecondition key is outside the list. The AWS Config desired-instance-type managed rule reports non-matching instances after launch.

Examples of Impact

The Amazon EC2 preferred instance type policy has the most impact where many engineers provision infrastructure independently.

Illustrative example: oversized development stacks. A shared Terraform module defaults to m5.4xlarge, and six teams use it for development environments. Switching the module default to t3.medium cuts each development instance from about $561 to about $30 per month.

Illustrative example: Reserved Instance drift. A platform team buys Reserved Instances for the m5 family. New services then launch on a mix of c5, r5, and m4. Adding m5 types to the preferred list steers new services that fit m5 onto the reserved capacity. Those services stop billing at on-demand rates.

(These are illustrative scenarios, not specific customer accounts.)

Considerations and Caveats

The Amazon EC2 preferred instance type policy is only as good as the list behind it, and a stale list causes more harm than no list.

  • Specialized workloads. GPU, high-memory, and storage-optimized workloads often need types outside a general list. Plan an exception process.

  • List maintenance. AWS releases new generations regularly. Review the approved list at least quarterly.

  • Restart required. Changing instance_type on an existing aws_instance stops and starts the instance.

  • Coverage gaps. A Terraform validation block only guards modules that include it. Infracost and AWS runtime controls catch the rest.

  • Provider-specific. This page covers Amazon EC2 through the aws Terraform provider. Azure and Google Cloud use different resource and attribute names.

Related Policies and Concepts

  • Google Compute Engine - Consider using a preferred instance type: the same approved-list approach applied to google_compute_instance machine types.

  • Azure Virtual Machines - Consider using a preferred instance type: the Azure equivalent for restricting VM sizes to an approved list.

  • Amazon EC2 - consider using latest generation instances for r family instances: a related Compute-group policy that keeps memory-optimized types on current, cheaper generations.

  • Amazon EC2 - consider using latest generation instances for i family instances: the storage-optimized counterpart, useful when deciding which generations belong on a preferred list.

  • Google Compute Engine - Consider using predefined machine types: a related Compute-group policy that standardizes machine shapes instead of allowing custom sizes.

Frequently Asked Questions (FAQs)

Is this policy supported in Infracost?

Yes. The Amazon EC2 preferred instance type policy is supported in Infracost and available in the free trial. Infracost flags Amazon EC2 instance types outside the approved list in pull requests, before the change merges.

Can this policy be customized?

Yes. Customization is the core of the Amazon EC2 preferred instance type policy. Teams define the approved list in Infracost as region and instance type pairs, with wildcards such as *:t3.* or us-east-1:*. Teams also choose whether a violation warns or blocks the pull request.

Does Infracost automatically fix violations?

Infracost reports each Amazon EC2 instance type outside the preferred list in the pull request, with the file and line to change. Infracost AutoFix can also propose fix pull requests for policy failures, which engineers review before merging. Picking the right approved instance type still depends on the workload's CPU and memory needs.

Is this policy cloud-agnostic?

No. This page covers the Amazon EC2 preferred instance type policy for the aws Terraform provider. Infracost applies the same preferred instance type approach to Azure Virtual Machines and Google Compute Engine, each with its own instance type names.

How is a preferred instance type policy different from an AWS Service Control Policy?

An AWS Service Control Policy denies a non-approved Amazon EC2 instance type at launch time, through the AWS API. The Infracost preferred instance type policy flags the same choice earlier, in the Terraform pull request, before anything is applied. Many teams use both layers.

How often should I review the Amazon EC2 preferred instance type list?

Review the Amazon EC2 preferred instance type list at least quarterly, and whenever AWS releases a new instance generation. An outdated preferred list can push teams toward older, more expensive instance types.

Create Free Account

This policy is supported in Infracost and available in the free trial. Sign up today and scan your code using our entire library of FinOps policies.

Get started
with Infracost

© 2026 Infracost Inc

Manage cookies

Get started
with Infracost

© 2026 Infracost Inc

Manage cookies

Get started
with Infracost

© 2026 Infracost Inc

Manage cookies