Terraformer が import した resource は不要な属性を含む場合がある
小ネタです。
以前このブログの 3rd Party tool をきっかけに Terraform のソースコードを少し嗜んだ話 · the world as code というエントリーでも取り上げた terraformer 。既存のクラウドリソースを元に terraform file を作成してくれる便利ツールなのだが、時に必要ない属性値を resource に含んだファイルを作ることがある。
1resource "aws_s3_bucket" "tfer--chroju-002E-net" {
2 acl = "private"
3 arn = "arn:aws:s3:::chroju.net"
4 bucket = "chroju.net"
5 force_destroy = "false"
6 hosted_zone_id = "XXXXXXXXXXXXXX"
7 region = "ap-northeast-1"
8 request_payer = "BucketOwner"
9
10 versioning {
11 enabled = "false"
12 mfa_delete = "false"
13 }
14
15 website {
16 index_document = "index.html"
17 }
18
19 website_domain = "s3-website-ap-northeast-1.amazonaws"
20 website_endpoint = "chroju.net.s3-website-ap-northeast-1.amazonaws.com"
21}
例えばこれは自分の S3 バケットを import してみた実際の結果(一部マスクあり)なのだけど、いくつか不要な値を含んでいる。具体的には arn
, website_domain
, website_endpoint
の3点。ドキュメント AWS: aws_s3_bucket - Terraform by HashiCorp を見るとわかるが、これらは Attributes References
、つまり他の resource から参照可能な属性であって設定に必要な値ではない。
ではこれは余計な値でバグなのではないか、と思いたくなるところなのだけど、面白いのはこのまま terraform
コマンドを打ってもエラーにはならないということ。以下、試しに ARN を hogefuga
という値に変更して terraform apply
して、その後再度 plan
で結果を確認してみたサンプルを貼る。
ARN は AWS 側が一定の法則に則って自動採番する ID であり、当然ながらユーザーが任意で変更はできない。それなのに plan
の結果として ARN の変更は予告され、そして apply
も通ってしまうのが面白い。しかし apply
後に plan
を実行すると、依然差分として表示はされるので、実際に apply
が通ったわけではない。当たり前だけど、ちょっと挙動としては不安にもなる。
ちなみにこの状態から ARN を削除してみても、 terraform plan
の結果は No Changes
になり、 ARN が削除されるようなことはない(そりゃそうなんだが)。そのため Terraformer の import 結果に不要な値が含まれていたら、運用上の混乱を避ける意味でも、手で削除してしまうのが無難ではある。
原因
ではなぜこのような挙動になるのかだが、原因は Terraform の実装にある。
Terraform の各 resource にどのような attributes を含むかは、 &schema.Resource.Schema
に map
の形で定義されている。 AWS S3 の場合は terraform-provider-aws/resource_aws_s3_bucket.go at master · terraform-providers/terraform-provider-aws で、冒頭だけ抜き出すとこのような形。
1 Schema: map[string]*schema.Schema{
2 "bucket": {
3 Type: schema.TypeString,
4 Optional: true,
5 Computed: true,
6 ForceNew: true,
7 ConflictsWith: []string{"bucket_prefix"},
8 ValidateFunc: validation.StringLenBetween(0, 63),
9 },
10 "bucket_prefix": {
11 Type: schema.TypeString,
12 Optional: true,
13 ForceNew: true,
14 ConflictsWith: []string{"bucket"},
15 ValidateFunc: validation.StringLenBetween(0, 63-resource.UniqueIDSuffixLength),
16 },
そしてここに並列で Attributes References
に含まれる値も並んでいる。どれが Arguments
(設定値)でどれが Attributes
なのかを識別できるような箇所はない。そのため resource の中に Attributes
にあたる値を書き入れても、エラーにはならない。
また Terraformer を使用した際の挙動としては、 Terraform の refresh
コマンドに相当するメソッドを呼び出す形になる。 refresh
といえば現在のクラウドリソースの状態を取得して、 tfstate を書き換えるというもの。 tfstate 側には Attributes
に相当する値も含まれているため、 refresh
を呼ぶ形だと Attributes
も一緒に読み込んでくる形になる。だから Terraformer が作成する tf ファイルには、 Attributes
が含まれるということになる。
現状、先述の通り Terraform の実装として Arguments
と Attributes
を識別する方法がないため Terraformer がこのような挙動をするのはやむを得ないと考えている。 Terraformer は Terraform の API を効率よく活用することで、個々のクラウドリソースの API を最小限にしか知る必要がない形で実装されている。各値が Attributes
かどうかという情報は Terraform 側が持つべきであって、 Terraformer に実装させる必要はないだろう。幸い、見てきた通り Attributes
を含めても Terraform はエラーにならないので、割り切ることもできなくはない。
Impression
以上、トリビアに近い小ネタを書いてみた。きっかけは実際に terraformer を使っていてこの挙動に気づいたことなのだけど、もうすぐ Terraform Source Code Reading#3 - connpass というイベントに参加することもあり、少し Terraform のコードを読む機会が欲しかった形。このイベント、それなりにレベル高いことやろうとしてるので、生きて帰れるのかむちゃくちゃ不安。