まえがき
Terraform を運用していると、時には不思議な差分が発生することがあります。
やんごとなき事情により、Terraform で管理しているリソースのリストの順番が狂って差分をなんとか潰さないといけない状況に追い込まれ、何かと不思議なことをやったので記しておきます。
環境
$ terraform version
Terraform v0.11.13
入れ替える
元々はこんな感じでした。
私の場合は、 aws_acm_certificate のリソースを元に、複数の aws_route53_record のリソースを定義していました。
$ terraform state list module.my_service.aws_route53_record.my_service_com_use1_cert_validations
module.my_service.aws_route53_record.my_service_com_use1_cert_validations[0]
module.my_service.aws_route53_record.my_service_com_use1_cert_validations[1]
module.my_service.aws_route53_record.my_service_com_use1_cert_validations[2]
これの my_service_com_use1_cert_validations[0] と my_service_com_use1_cert_validations[2] を入れ替えた tfstate を作りたいのです。
そんなことあるのか? あります……。少なくとも目の前には……。
しかし、 terraform state mv コマンドは一度に一つのリソースしか操作出来ないので、いい感じにシュッと入れ替える事が出来ません。困りましたね。
ここでおもむろに tfstate の中身を見てみましょう。
"aws_route53_record.my_service_com_use1_cert_validations.0": {
略
},
"aws_route53_record.my_service_com_use1_cert_validations.1": {
略
},
"aws_route53_record.my_service_com_use1_cert_validations.2": {
略
},
Terraform のリソースのリストは、 tfstate.json 上では配列ではなくオブジェクトのキーの末尾に .n を付与した形で表現されます。
つまり、以下のような操作が可能なのではないかという予感がしてきますよね ![]()
$ terraform state mv module.my_service.aws_route53_record.my_service_com_use1_cert_validations[0] module.my_service.aws_route53_record.my_service_com_use1_cert_validations[3]
state の様子を見てみましょう
$ terraform state list module.my_service.aws_route53_record.my_service_com_use1_cert_validations
module.my_service.aws_route53_record.my_service_com_use1_cert_validations[1]
module.my_service.aws_route53_record.my_service_com_use1_cert_validations[2]
module.my_service.aws_route53_record.my_service_com_use1_cert_validations[3]
![]()
![]()
![]()
![]()
入れ替えていきましょう
$ terraform state mv module.my_service.aws_route53_record.my_service_com_use1_cert_validations[2] module.my_service.aws_route53_record.my_service_com_use1_cert_validations[0]
$ terraform state mv module.my_service.aws_route53_record.my_service_com_use1_cert_validations[3] module.my_service.aws_route53_record.my_service_com_use1_cert_validations[2]
$ terraform state list module.my_service.aws_route53_record.my_service_com_use1_cert_validations
module.my_service.aws_route53_record.my_service_com_use1_cert_validations[0]
module.my_service.aws_route53_record.my_service_com_use1_cert_validations[1]
module.my_service.aws_route53_record.my_service_com_use1_cert_validations[2]
ああ、何もかもが完璧です!
最悪の場合は tfstate を直接操作しないといけないのではないかと戦々恐々としていましたが、無事に terraform のコマンドのみでリソースのリストの順序を入れ替えることが出来ました。
正式にサポートされた操作という感じはしませんが、少なくとも今のところはこのような操作が可能なようです。
みなさんもこのような不思議な操作を行って、 Terraform の差分を無理やり潰して幸せな日々を手に入れましょう!
付記
無理やり入れ替えることには成功しましたが、後に、どうにもランダムに差分が発生することがわかりました。
aws_acm_certificate の domain_validation_options というアトリビュートを用いてリソースを定義していると、(いつのタイミングからは不明ですが) AWS API 側の戻り値の配列が順不同になっている影響で、Terraform 側での差分がランダムになるという現象のようです。
terraform-provider-aws にも Issue が作成されています https://github.com/terraform-providers/terraform-provider-aws/issues/8531
この PR を含んだリリースが行われたら、この現象は防ぐことが出来そうです
https://github.com/terraform-providers/terraform-provider-aws/pull/8657
2019年5月30日時点で ACM の API の戻り値が順不同になる現象が発生しなくなりました
https://github.com/terraform-providers/terraform-provider-aws/issues/8531#issuecomment-497233510
ですがこれが想定通りの挙動であることの保証はどこにもないので、引き続き terraform-provider-aws 側が順不同に対応してくれる日を待望していきましょう。