「providerを固定したのになぜ直らない?」Terraform初心者がハマる落とし穴
ある日、既存の AWS リソース(セキュリティグループ)をコード管理下に置くため、terraformer import というツールで .tf ファイルと tfstate を自動生成しました。生成されたコードに対して terraform plan を実行すると、こんなエラーが出ました。
Error: Unsupported argument
on security_group.tf line 27, in resource "aws_security_group" "example":
27: tags_all = {
An argument named "tags_all" is not expected here.
その場は該当箇所を手動で削除して乗り切ったのですが、数ヶ月後に同じリソースを再度 import し直したところ、まったく同じエラーが再発しました。「.terraform.lock.hcl を Git 管理して AWS provider のバージョンを固定したはずなのに、なぜ直らないのか」という疑問が上がったのですが、結論から言うと、この疑問自体が「直すべき場所」を取り違えていました。この記事では、その勘違いが起きた理由を入り口に、Terraform 初心者がつまずきやすいファイル・ツールの役割分担を整理します。
登場人物を整理する
Terraform をちょっと触り始めると、似たような名前のファイルやツールが一気に出てきて混乱します。まず全体像を押さえましょう。
| 名前 | 役割 |
|---|---|
Terraform 本体(terraform コマンド) | 汎用エンジン。.tf を読んで依存関係を解決し、実行計画(plan)を立てて実行(apply)するだけで、クラウド固有の知識は一切持たない |
provider(例: terraform-provider-aws) | Terraform 本体の指示を実際のクラウド API 呼び出しに翻訳する専用プラグイン。クラウドごとに別バイナリ |
.tf ファイル | リソース定義。人間が書くこともあれば、ツールが自動生成することもある |
.tfstate ファイル | 「今クラウド上に実際に何が存在するか」の記録 |
.terraform.lock.hcl | terraform init 時に生成される、provider のバージョンを固定するためのファイル。Git 管理が公式推奨 |
terraformer | Terraform 本体のエコシステムには含まれない、完全に別の OSS ツール。既存のクラウドリソースを読み取り、.tf と tfstate を逆生成する(reverse Terraform) |
ここでの一番の注意点は、terraformer だけ毛色が違うということです。他の要素は全部「Terraform 本体が知っている世界」の話ですが、terraformer は Terraform 本体が存在すら知らない外部のツールです。今回の混乱は、この違いを意識せずに「Terraform まわりの設定を直せば全部直るはず」と思い込んでしまったところから始まっています。
tags_all とは何か
AWS リソースにタグをつける方法には、実は2つの入力元があります。
- リソースブロックに書く
tags - provider ブロック全体にかける
default_tags
provider "aws" {
default_tags {
tags = {
Environment = "prod"
}
}
}
resource "aws_security_group" "example" {
tags = {
Name = "example"
}
}
この2つを AWS provider が自動的に合成した結果が tags_all です。ユーザーが値を書き込むものではなく、Terraform が計算して埋める「Computed(参照専用)属性」という位置づけになっています。だから、そもそも .tf ファイルの中に自分で tags_all = {...} と書くこと自体が想定されておらず、書けばエラーになります。
なぜ terraformer が tags_all を書き出してしまうのか
terraformer は「AWS の実際の state に含まれる属性を機械的に全部 .tf へダンプする」という作りになっています。その属性が「ユーザーが定義すべきもの」なのか「provider が計算するだけの参照専用(Computed)」なのかを区別せずに全部書き出してしまうため、tags_all もそのまま .tf に紛れ込みます。
さらにややこしいのが、tags_all という属性自体が AWS provider のバージョン 3.38.0(2021年5月ごろ)で導入された比較的新しい概念だという点です。つまり、同じ terraformer コマンドを同じように打っても、「いつ実行したか」によって挙動が変わります。
- 古い provider バージョンの時代に terraformer を実行 → state に tags_all が存在しないので、書き出されない
- 新しい provider バージョンの時代に terraformer を実行 → state に tags_all が含まれているので、.tf に書き出されてしまう
なぜ「provider を固定」しても直らないのか
ここが今回一番混乱した部分です。.terraform.lock.hcl が固定するのは、「そのプロジェクトが terraform plan/apply を実行するときに使う provider(AWS との通訳担当)のバージョン」です。
一方で tags_all が書き出されるかどうかを決めているのは、terraformer というバイナリ自身が内蔵している「AWS リソースはこういうスキーマだろう」という理解であり、これは terraformer 自身のバージョンに依存します。
この2つは別のツール・別のタイミングで動いているので、lock.hcl をどれだけ厳密に固定しても terraformer の生成ロジックには一切影響しません。「provider を固定したのになぜ直らないのか」という問いは、そもそも直る見込みのない場所を直そうとしていた、ということになります。
じゃあどう対処すればいいのか
その場しのぎ: エラーになった tags_all = {...} のブロックを手動で削除してから plan/apply する。今回のケースもまずはこれで乗り切りました。
ただしこれだと、次に terraformer で同じリソースを再度 import(再生成)した瞬間に同じ行が復活し、同じエラーが再発します。実際、数ヶ月後にまったく同じ現象が起きました。
恒久対策の方向性:
- terraformer 実行後、生成された .tf から tags_all のような Computed 専用属性を機械的に取り除く後処理ステップ(sed やスクリプト)を手順に組み込み、手作業に頼らないようにする
- 手順書に「なぜこの現象が起きるのか」という構造の説明を残しておく。「削除してOK」という結論だけを書き残すと、担当者が変わったときに同じ疑問がまた繰り返される
まとめ
- Terraform 本体・provider・.tf・.tfstate・.terraform.lock.hcl は同じエコシステム内の話だが、terraformer は完全に別の外部ツール
- tags_all は AWS provider が自動計算する Computed 属性で、そもそも .tf に手書きするものではない
- terraformer は「これは書いていい・これは書いてはいけない」の区別をせず、state の中身を無差別にダンプするため、新しい provider 世代の情報を含む state を読むと tags_all も書き出してしまう
- provider バージョンの固定(lock.hcl)はこの問題の発生レイヤーとは無関係。だから固定しても直らない
Terraform を触っていて「直したはずなのになぜ直らない」と感じたときは、一度「そもそも、その設定は"どのツールのどのレイヤー"に効くものなのか」を疑ってみると、案外あっさり謎が解けることがあります。

