---
name: terraform-naming
source: https://app.decimal.ai/s/terraform-naming@1/SKILL.md
source_sha256: 317b1f1fbcd2
---

# Terraform resource and identifier naming

## Contract

Enforces the HashiCorp Terraform naming style on every identifier you declare: resource and
data-source local names, variables, outputs, and module names are snake_case, never repeat the
resource type, and use `this` for a module's single primary resource. Apply when writing or
reviewing HCL identifiers; not for the resource arguments, directory layout, or module wiring.

## Rules

1. **Identifiers are snake_case.** Every local name, variable, output, and module label is
   lowercase words joined by single underscores. Never CamelCase (`webServer`), never a leading
   capital (`WebServer`).

2. **No hyphens in an identifier.** A hyphen never appears in a resource local name, variable,
   output, or module name. Hyphens live only INSIDE string VALUES — the real cloud resource
   name or tag (`name = "prod-web-01"`), never in the HCL label itself.

3. **The local name never repeats the resource type.** The type already prefixes every
   reference (`aws_s3_bucket.logs` is read as `aws_s3_bucket.logs`), so repeating the type is
   pure redundancy. Name it `logs`, never `logs_bucket`, `bucket`, or `s3_bucket`. Strip the
   type noun (`bucket`, `role`, `table`, `queue`, `topic`, `instance`) from the local name.

4. **`this` for a module's single primary resource.** When a module (or config) creates exactly
   ONE resource of its main type, name that resource `this` — `resource "aws_vpc" "this"`. Use a
   descriptive name only when there are two or more of the type to tell apart.

5. **Variables and outputs are snake_case descriptive nouns.** `instance_count`,
   `enable_logging`, `subnet_ids`. Carry the unit or purpose in the name; never CamelCase or
   hyphenate them either.

6. **Data sources follow the same rules.** A `data` block's local name is snake_case and does
   not repeat the data-source type: `data "aws_ami" "ubuntu"`, never `data "aws_ami" "ubuntu_ami"`.

## Worked examples

A bucket whose local name repeats the type, loses the repetition:

```
BEFORE  resource "aws_s3_bucket" "app_bucket"    { ... }

AFTER   resource "aws_s3_bucket" "app"           { ... }
```

A role named `..._role`, loses the type noun:

```
BEFORE  resource "aws_iam_role" "lambda_role"    { ... }

AFTER   resource "aws_iam_role" "lambda"         { ... }
```

A CamelCase, hyphenated identifier becomes snake_case:

```
BEFORE  resource "aws_security_group" "web-SG"   { ... }

AFTER   resource "aws_security_group" "web" {
          name = "web-sg"                        # the hyphen lives in the string value
        }
```

A module's single instance of its main type is named `this`:

```
BEFORE  resource "aws_db_instance" "main_database" { ... }

AFTER   resource "aws_db_instance" "this"          { ... }   # the module makes exactly one
```

A CamelCase variable and output become snake_case:

```
BEFORE  variable "instanceCount" {}
        output   "bucketArn"     { value = aws_s3_bucket.app.arn }

AFTER   variable "instance_count" {}
        output   "bucket_arn"     { value = aws_s3_bucket.app.arn }
```

## Edge cases & exceptions

- **Two or more of the same type** — do NOT use `this`; give each a distinguishing snake_case
  name that still omits the type (`aws_subnet.public`, `aws_subnet.private`, not
  `aws_subnet.public_subnet`).
- **A count/for_each set** — the local name is the singular role, still without the type
  (`aws_instance.worker` with `count`, never `aws_instance.workers` or `worker_instance`).
- **The type noun IS the whole meaning** — even then, prefer a role word over the bare type:
  `aws_vpc.this` or `aws_vpc.main`, never `aws_vpc.vpc`.
- **Real resource names need hyphens** — that is fine, but only inside the string value
  (`bucket = "app-prod-logs"`); the HCL label stays snake_case.
- **Provider-required kebab-case values** (a DNS record, an S3 key) are string values, not
  identifiers — hyphens there are correct and untouched.

## Do / Don't

- Do write `aws_s3_bucket.logs`. Don't write `aws_s3_bucket.logs_bucket` or `.bucket`.
- Do strip the type noun from the local name. Don't append `_role`, `_table`, `_queue`, etc.
- Do use `this` for a module's single primary resource. Don't use `main_thing` or the type.
- Do keep identifiers snake_case. Don't use CamelCase or a leading capital.
- Do put hyphens only in string values. Don't hyphenate an HCL label.
- Do name variables/outputs as snake_case nouns. Don't CamelCase them.

## Common mistakes

- The local name echoes the type: `aws_iam_role.role`, `aws_sqs_queue.queue`, `.this_table`.
- CamelCase carried over from application code (`aws_lambda_function.imageResizer`).
- Hyphens in the HCL label (`aws_subnet.public-a`) instead of only in the string value.
- A single-resource module named `main` / `default` / the type instead of `this`.
- Variables like `enableLogging` or `subnet-ids` instead of `enable_logging` / `subnet_ids`.
- Data sources repeating the type (`data "aws_ami" "base_ami"`).

## Quick checklist

- Every identifier snake_case; no hyphen, no CamelCase, no leading capital.
- The resource local name omits the type noun already in the type prefix.
- A module's single primary resource is `this`; siblings get distinguishing names, still typeless.
- Variables and outputs are snake_case descriptive nouns carrying unit/purpose.
- Data-source local names follow the same rules.
- Hyphens appear only inside string values (real cloud names/tags), never in an HCL label.
