そんな状況を経験したことはないだろうか。
Terraformでインフラをコード管理する最大のメリットは再現性と追跡可能性だ。しかし命名規則とタグ戦略が統一されていないと、「このEC2は本番か?」「先月の費用増加はどのサービスが原因か?」をAWSコンソールで瞬時に判断できなくなる。後から命名を直そうとすると
terraform state mv を多用する危険なリファクタリングが必要になり、本番障害のリスクを伴う。この記事では、Terraform 1.7以降 / AWS provider 5.x を前提に、HCLリソースの命名規則とAWSタグ設計のベストプラクティスを実装例つきで解説する。チームで長期運用できる設計パターンを、環境分離・コスト管理・障害対応の3軸で整理する。
この記事のポイント
・HCLブロックラベルとNameタグは役割が異なり、それぞれに命名規則が必要
・localsブロックで共通タグを一元管理すると、全リソースへの変更が1か所で完結する
・環境・サービス・コスト配分の3タグをデフォルト基本セットとして設計する
・命名が崩れるのは「env変数の渡し忘れ」と「コンソール手動上書き」が主な原因
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜTerraformで命名規則とタグを統一するのか
インフラ運用で命名規則が重要な理由は3つある。障害対応速度
深夜のアラートで「i-0a1b2c3d4e5f」と表示されても、それが本番のWebサーバーか検証環境のバッチかを判断する時間が無駄になる。Nameタグに
prod-lm-web-01 と付いていれば即断できる。コスト管理
AWSのコスト配分タグ(Cost Allocation Tags)を使うと、サービスごと・環境ごとの費用をBilling Dashboardで可視化できる。タグが統一されていなければ、この機能が使えない。
Terraformのstate整合性
HCLのリソースブロックラベル(
resource "aws_instance" "web" の web 部分)はTerraformが内部的にstateと紐付けるキーになる。ここが変わると既存リソースの削除→再作成が走る。最初から一貫したルールを設けることで、リファクタリング時の事故を防げる。Terraformを使ったAWSインフラ管理の体系的な学習については terraform.linuxmaster.jp でも詳しく解説している。
HCLリソース名とAWS Nameタグの設計方針
混乱の原因として多いのが「HCLブロックラベル」と「AWSのNameタグ」を同じものと思い込んでいるケースだ。この2つは役割が異なる。| 項目 | HCLブロックラベル | AWSリソース Nameタグ |
|---|---|---|
| 用途 | Terraform内部でstateを識別する | AWSコンソール・CLIで表示される人間向けの名前 |
| 変更コスト | 高(再作成トリガー) | 低(タグ変更だけで済む) |
| 命名ルール | 短く・role中心(web / db / bastion) | 長く・環境+サービス+連番(prod-lm-web-01) |
1. HCLブロックラベルの命名規則
HCLブロックラベルはTerraform内部のアドレスになるため、変更コストが高い。以下の方針を推奨する。・役割(role)中心の短い名前を使う
・環境名は含めない(環境はworkspaceや変数で切り替える)
・複数台ある場合はfor_eachのキーで表現し、連番は避ける
# NG: 環境名を入れると環境ごとにリソースブロックが増殖する resource "aws_instance" "prod_web_01" { ... } resource "aws_instance" "stg_web_01" { ... } # OK: 役割中心・環境は変数で切り替える resource "aws_instance" "web" { ami = var.ami_id instance_type = var.instance_type tags = local.common_tags }
2. AWSリソース Nameタグの命名規則
Nameタグはタグ変更だけで済むため変更コストが低い。以下の形式を推奨する。# 形式: {env}-{project}-{role}-{index} # 例 prod-lm-web-01 # 本番 / linuxmaster / Webサーバー / 1台目 stg-lm-web-01 # ステージング / linuxmaster / Webサーバー / 1台目 prod-lm-db-primary # 本番 / linuxmaster / DBサーバー / プライマリ
localsブロックで共通タグを一元管理する
Terraformで全リソースに同じタグセットを付けるもっとも効率的な方法は、localsブロックに共通タグマップを定義することだ。# variables.tf variable "env" { description = "デプロイ環境(dev / stg / prod)" type = string validation { condition = contains(["dev", "stg", "prod"], var.env) error_message = "envはdev / stg / prodのいずれかを指定してください。" } } variable "project" { description = "プロジェクト識別子" type = string default = "lm" } variable "cost_center" { description = "コスト配分タグ(AWSコスト配分タグ設定と一致させる)" type = string }
# locals.tf locals { # 全リソースに付与する共通タグ common_tags = { env = var.env project = var.project managed_by = "terraform" cost_center = var.cost_center } # Nameタグ付きのリソース別タグを生成するヘルパー # merge(local.common_tags, { Name = "..." }) を各リソースで呼ぶ name_prefix = "${var.env}-${var.project}" }
3. 共通タグをリソースに適用する
# main.tf resource "aws_instance" "web" { ami = data.aws_ami.amazon_linux.id instance_type = var.instance_type subnet_id = aws_subnet.public.id tags = merge(local.common_tags, { Name = "${local.name_prefix}-web-01" role = "web" }) } resource "aws_db_instance" "main" { identifier = "${local.name_prefix}-db" engine = "mysql" instance_class = var.db_instance_class allocated_storage = 20 tags = merge(local.common_tags, { Name = "${local.name_prefix}-db-primary" role = "database" }) }
merge()を使うことで、共通タグにリソース固有のタグ(NameやRole)を追加できる。全リソースに同じ local.common_tags を使うため、var.env を1か所変えるだけで全リソースのタグが更新される。
環境・サービス・コスト配分に対応したタグ設計
AWSのコスト管理に直結する3種のタグを、設計の基本セットとして定義する。| タグキー | 値の例 | 用途 |
|---|---|---|
| env | dev / stg / prod | 環境分離・リソース識別 |
| project | lm / store / admin | プロジェクト・サービス識別 |
| cost_center | backend-01 / frontend-02 | AWSコスト配分タグと一致させる |
| managed_by | terraform | 手動作成リソースとの区別(固定値) |
| Name | prod-lm-web-01 | AWSコンソールでの表示名(リソースごとに付与) |
4. コスト配分タグをAWSに有効化する手順
localsでタグを統一しても、AWSのコスト配分タグ機能を有効化しないとBilling Dashboardで集計できない。以下の手順を実施する。・AWSコンソール → Billing → Cost Allocation Tags を開く
・「User-defined cost allocation tags」の一覧から対象タグを確認する
・Terraformで付与した
project / cost_center / env を「アクティブ化」する・24時間後からBilling Dashboardでフィルタリングが有効になる
この設定はTerraformリソース(
aws_ce_cost_allocation_tag)でも管理できる。# cost_allocation.tf resource "aws_ce_cost_allocation_tag" "env" { tag_key = "env" status = "Active" } resource "aws_ce_cost_allocation_tag" "project" { tag_key = "project" status = "Active" } resource "aws_ce_cost_allocation_tag" "cost_center" { tag_key = "cost_center" status = "Active" }
for_eachとmergeでリソースごとのタグ差分を吸収する
同じ種類のリソースを複数台デプロイする場合、for_eachとタグのmergeを組み合わせると重複コードを大幅に削減できる。# variables.tf variable "web_servers" { description = "Webサーバーのマップ(キーがNameタグの一部になる)" type = map(object({ instance_type = string subnet_id = string extra_tags = map(string) })) default = {} }
# terraform.tfvars(dev環境) web_servers = { "web-01" = { instance_type = "t3.micro" subnet_id = "subnet-aabbcc11" extra_tags = { az = "ap-northeast-1a" } } "web-02" = { instance_type = "t3.micro" subnet_id = "subnet-ddeeff22" extra_tags = { az = "ap-northeast-1c" } } }
# main.tf resource "aws_instance" "web" { for_each = var.web_servers ami = data.aws_ami.amazon_linux.id instance_type = each.value.instance_type subnet_id = each.value.subnet_id tags = merge( local.common_tags, { Name = "${local.name_prefix}-${each.key}" role = "web" }, each.value.extra_tags # AZなどリソース固有のタグ ) }
merge()の優先順位は「後の引数が勝つ」。共通タグ → 役割タグ → リソース固有タグの順で上書きが起きる。extra_tagsで共通タグを意図せず上書きしてしまわないよう、extra_tagsに env や managed_by を入れないことをチーム規約として設定しておく。デプロイ後にタグを確認すると、各リソースに意図したタグが付いていることを実機で検証できる。
$ aws ec2 describe-instances \ --filters "Name=tag:env,Values=dev" \ --query 'Reservations[].Instances[] .{ID:InstanceId, Name:Tags[?Key==`Name`].Value|[0], Env:Tags[?Key==`env`].Value|[0], Cost:Tags[?Key==`cost_center`].Value|[0]}' \ --output table -------------------------------------------------------------------- | DescribeInstances | +----------------------+----------+--------------+-----------------+ | Cost | Env | ID | Name | +----------------------+----------+--------------+-----------------+ | backend-01 | dev | i-0a1b2c3d4e | dev-lm-web-01 | | backend-01 | dev | i-0b2c3d4e5f | dev-lm-web-02 | +----------------------+----------+--------------+-----------------+
命名・タグ設計の典型的な失敗パターンと対処手順
5. 失敗パターン1: env変数を渡し忘れてデフォルト値のまま適用される
var.env にデフォルト値を設定していると、-var env=prodを渡し忘れても適用できてしまう。本番に env=dev タグが付いたリソースが生まれる。# NG: デフォルト値があると渡し忘れを検出できない variable "env" { type = string default = "dev" # ← 渡し忘れても通ってしまう } # OK: デフォルト値を設定せず、validationで許容値を明示する variable "env" { type = string validation { condition = contains(["dev", "stg", "prod"], var.env) error_message = "envはdev / stg / prodのいずれかを指定してください。" } }
6. 失敗パターン2: コンソールで手動タグ上書きしてterraform planに差分が出続ける
AWSコンソールでタグを手動変更すると、次のterraform plan で差分が検出される。# aws_instance.web will be updated in-place ~ resource "aws_instance" "web" { ~ tags = { ~ "cost_center" = "backend-02" -> "backend-01" # (3 unchanged elements hidden) } } Plan: 0 to add, 1 to change, 0 to destroy.
terraform.tfvars を修正してapplyする。手動変更を一切禁止したい場合は、lifecycle { ignore_changes = [tags] } を使わず「コンソール手動変更禁止」をチーム規約として徹底する方が、stateの整合性を保てる。7. 失敗パターン3: HCLブロックラベルを後から変更してリソースが再作成される
# 変更前 resource "aws_instance" "web_server" { ... } # stateキー: aws_instance.web_server # 変更後(命名を直した) resource "aws_instance" "web" { ... } # stateキー: aws_instance.web
terraform apply すると「aws_instance.web_server削除 + aws_instance.web新規作成」が実行される。本番EC2の停止・再起動が発生する。Terraform 1.1以降なら
movedブロックでstateのキーを変更できる。# moved.tf(一時的に追加し、apply後に削除する) moved { from = aws_instance.web_server to = aws_instance.web }
movedブロックを使うと、リソースを破壊せずstateのアドレスだけを変更できる。適用後は moved.tf を削除し、次の terraform plan で差分がゼロになることを確認する。本記事のまとめ
| 設計項目 | 推奨方針 |
|---|---|
| HCLブロックラベル | 役割中心の短い名前(web / db / bastion)。環境名は含めない |
| AWSリソース Nameタグ | {env}-{project}-{role}-{index}形式。localsで生成する |
| 共通タグの管理 | locals.common_tagsに定義。全リソースはmerge()で適用する |
| 基本タグセット | env / project / cost_center / managed_by / Name の5つ |
| リソース固有タグ | merge()の最後の引数で追加。共通タグを上書きしない設計にする |
| var.envのバリデーション | デフォルト値を設定せず、validationブロックで許容値を明示する |
| ブロックラベル変更 | movedブロックで対応。直接変更は削除→再作成トリガーになる |
locals.tf と variables.tf を先にコミットすることを推奨する。後から直すコストは、最初に設計するコストの何倍にもなる。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:TerraformでS3バケットを管理する方法|バージョニング・ライフサイクル・暗号化のリソース分割設計
- この記事の属するカテゴリ:Terraformへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます