compliance.tf

Restrict Instance Types

Records every aws_instance in the module source whose instance_type is a literal matching a denied family. Reports; does not block.

When to use this rule

Use this when: You pull modules that write instance_type directly into their resources, and you want a record whenever one of them names a GPU or high-memory family.

Do not use this when: You need the deployment stopped rather than recorded β€” this rule cannot block anything. Or you run ML and HPC workloads that legitimately require those families.


Why this rule exists

AWS instance families built for GPU computing (p3, p4, p5), memory-intensive workloads (x1, x2), and high-memory bare metal (u-*) run at many times the hourly rate of a general-purpose instance. A module that hardcodes one of them keeps charging for as long as nobody looks.

This rule gives you the signal at download time. It reads the module source and records any aws_instance whose instance_type is written in as a literal and matches a denied pattern. It is a detection control, not an enforcement one: the module arrives exactly as published and terraform apply still runs. What you get is a finding you can act on before the module reaches production.


Affected resources

ResourceServiceWhy
aws_instanceAmazon EC2instance_type determines compute cost

What this rule does

Reports β€” it does not rewrite. Every resource in the module source is scanned, and a literal instance_type value matching a denied pattern is recorded as a finding. The module is delivered byte-for-byte unchanged, and terraform init still succeeds.

Patterns are shell-style globs matched against the whole value, so p3.* matches p3.2xlarge but not p3dn.24xlarge.

Denied patterns: p3.*, p4.*, x1.*, x2.*, u-*


What a finding looks like

The rule reads the module source. A literal value that matches a denied pattern produces a finding:

resource "aws_instance" "this" {
  instance_type = "p3.8xlarge"
}
! resource.aws_instance.this: GPU and high-memory instance types are not permitted without approval

A value outside the deny list produces nothing:

resource "aws_instance" "this" {
  instance_type = "m5.xlarge"
}

Either way the module source is delivered unchanged. Nothing is blocked, rewritten, or added.

Real-world scenario

A machine-learning proof of concept left a p3.8xlarge running in a staging account. Nothing in the pipeline flagged it, and it surfaced weeks later in a cost review. The module that created it had the instance type written straight into the resource β€” exactly the case this rule reports.


Known limits

  • Reports only. The module source is delivered unchanged, no validation block is added, and terraform init, plan, and apply all proceed normally.
  • Reads literal values only. instance_type = var.instance_type, a local, a data lookup, an interpolation, or a function call is skipped, because the value is not knowable at download time. Most terraform-aws-modules take the instance type from a variable, so the rule finds nothing in them.
  • Reads the module source, not your own configuration. An instance_type you set in your root module or pass into a module block is never examined.
  • The default patterns are globs matched against the whole value, and several match less than their names suggest: p4.* matches no released p4 instance (they are named p4d and p4de), x2.* matches no released x2 instance (x2gd, x2idn, x2iedn, x2iezn), and p3dn.24xlarge, the x1e family, and the whole p5 family are not matched either.
  • Only checks instance_type on aws_instance. Does not check aws_launch_template, aws_autoscaling_group, aws_sagemaker_endpoint_configuration, or ECS task size.
  • Does not restrict other expensive resource types (e.g., large RDS instances, Redshift nodes, OpenSearch domains).
  • Only .tf and .tofu files are read. Modules written in JSON syntax (.tf.json, .tofu.json) are left untouched.
  • Only the module's own directory and its first-level modules/* submodules are processed. Modules that this module calls by source URL are separate downloads and are handled on their own request.

Compliance framework support

This rule is not a compliance control. It supports these framework objectives as an operational safeguard:

FrameworkControlsRole
SOC 2CC6.3Provides a detection signal for disallowed resource configurations in module source

Default configuration

These are the values the rule ships with. They are not settable from a module source URL: ?rules= carries rule names only, so a per-request enable uses exactly the defaults below. Your organization sets them per rule, in its Baseline or in a ruleset, from the Operational Rules page; see Baseline rules and rulesets.

ParameterTypeDefaultDescription
deniedlist(string)["p3.*", "p4.*", "x1.*", "x2.*", "u-*"]Denied instance type patterns
messagestring"GPU and high-memory instance types are not permitted without approval"Validation error message

How to enable

On a framework host, per request:

Add ?rules=resource_restrict_instance_types to your HTTPS module source. Use the full namespaced id. A bare resource_restrict_instance_types matches no rule: it is skipped server-side and your terraform init still succeeds, without the rule.

module "example" {
  source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&rules=resource_restrict_instance_types"
}

On your organization's host:

Add the rule to the organization's Baseline, which applies to every module served from the bare organization form <alias>.compliance.tf/<namespace>/<name>/<provider>, or to a named ruleset that one module selects with ?ruleset=<name> on the HTTPS form of that address. A project environment (<alias>.compliance.tf/<project>/<env>/<module>) serves the rules frozen in its bound snapshot instead: there the rule goes into the config draft and reaches the environment by promotion. On both organization forms ?rules= is refused with a 400 β€” including the subtractive - prefix β€” rather than dropped, so you cannot mistake an ignored override for an applied one. Change the configuration, not the URL. See Baseline rules and rulesets for the configuration flow, and registry resolution for how the address forms differ.


Failure modes

ScenarioResult
A module hardcodes a denied instance typeThe rule records a finding against that resource address. The module is delivered unchanged and the deployment proceeds.
A module sets instance_type from a variableNo finding. The value is not knowable when the rule runs, so the resource is skipped.
AWS releases a new expensive familyNot matched. The deny list is a fixed set of patterns and has to be extended before the new family is recognised.

Terraform and OpenTofu compatible

This rule works with both Terraform (1.x+) and OpenTofu (1.6+). It reads ordinary HCL, and anything it changes stays ordinary HCL, so the module behaves the same under either tool.

Help us improve this page

Operational Rules are a new feature. We'd love your feedback on this rule page β€” what's useful, what's missing, what's confusing. Share feedback.

On this page

Ask AI about this

Help improve this page