目的
terraform apply
時の、but it already exists]
エラーに対するトラブルシューティング方法を知る。
結論
上記エラーの場合は、tfstate ファイルにはリソースが存在しなくて、実際のインフラにはそのリソースが存在する場合に起きる。
terraform import
コマンドで tfstate にも反映してあげる。
説明
今回のエラーが起きた場面
(1)
下記は、SSL証明書の検証のために、CNAME レコードを作成している。
aws_acm_certificate.acmcert.domain_validation_options
が、複数の CNAME レコードの情報を持っているということである。
resource "aws_route53_record" "r53r_acmcertvalidation" {
for_each = {
for dvo in aws_acm_certificate.acmcert.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
type = dvo.resource_record_type
record = dvo.resource_record_value
}
}
zone_id = aws_route53_zone.r53zone.zone_id
name = each.value.name
type = each.value.type
ttl = 300
records = [each.value.record]
}
(2)
terraform apply
を実施すると下記のエラーが出た。
※ CIで実行していたり、docker コンテナやterragrunt
を使っていたりして分かりにくいですが、terraform apply
を実行しているものと考えてください。
$ Run docker exec terraformmy-tfcontainer-1 sh -c "(cd envs/${TARGET_ENV} && terragrunt apply -auto-approve)"
...
15:47:50.598 STDERR terraform: │ Error: creating Route53 Record: operation error Route 53: ChangeResourceRecordSets, https response error StatusCode: 400, RequestID: abc12345-xxxx-xxxx-xxxx-xxxxxxxxxxxx, InvalidChangeBatch: [Tried to create resource record set [name='_example-validation.dev.example.com.', type='CNAME'] but it already exists]
15:47:50.598 STDERR terraform: │
15:47:50.598 STDERR terraform: │ with aws_route53_record.r53r_acmcertvalidation["dev.example.com"],
15:47:50.598 STDERR terraform: │ on aws_route53_record.tf line 2, in resource "aws_route53_record" "r53r_acmcertvalidation":
15:47:50.598 STDERR terraform: │ 2: resource "aws_route53_record" "r53r_acmcertvalidation" {
15:47:50.598 STDERR terraform: │
15:47:50.598 STDERR terraform: ╵
上記エラーは、tfstate ファイルにはリソースが存在しなくて、実際のインフラにはそのリソースが存在する場合に起きる。
tfstate ファイルと実際のインフラ状態に齟齬があるので、よろしくない状況である。
今回の例では、CNAME レコードが、tfstate にうまく反映されていないということである。
トラブルシューティング
(1)
tfstate ファイルと、実際のインフラに齟齬が起きたときに、一番手っ取り早く解決できる可能性があるのは、terraform refresh
である。
terraform refresh
は、tfstate ファイルと、実際のインフラの齟齬を解消する。
しかし、terraform refresh
は万能ではない。
terraform refresh
は、tfstate と実際のインフラ状態の齟齬をなくすというのは少し語弊があり、正確には、tfstate と tfstate(terraform) で管理されている実際のインフラ状態との齟齬をなくすのである。
今回、terraform refresh
をしても、CNAME は tfstate の管理対象にはならなかったので、terraform apply
をしたときにエラーが起きた。CNAMEが実際のインフラに存在するのにも関わらず、terraform apply
は tfstate の情報を信じてCNAMEの作成を試みたが、すでに作成してあったのでエラーが起きた。
(2)
次に、公式ドキュメントを参考にして、terraform import
の実行を試みる。
下記のコマンドで成功した。
※ 相変わらず、dockerコンテナなどを使っていて分かりにくくてすみません。
※ ID とかの文字列は機密情報のため、適当に改変しています。
※ Z0256789SDF6HDFJJ4EF
がゾーンIDで、_89098748f6b765436ghjk0567f67c900.dev.example.com
が レコードのname です。
※ AWSコンソールからCNAMEレコードの情報を見ればわかりますが、他のレコードと違って、name がdev.example.com
ではなくて、_89098748f6b765436ghjk0567f67c900.dev.example.com
のようになっているので注意してください。
※ envs/dev/aws_route53_record.tfの r53r_acmcertvalidation
リソースはforループで作成されているので、どれを使うのかを明示するために、[\\\"dev.example.com\\\"]
と書いています。
※ ["dev.example.com"]
ではなくて、[\\\"dev.example.com\\\"]
と書いている理由は、コンテナにコマンドを渡すために、エスケープしているだけです。
~/TerraformMy$ sudo docker exec terraformmy-tfcontainer-1 sh -c "(cd envs/dev && terraform import aws_route53_record.r53r_acmcertvalidation[\\\"dev.example.com\\\"]
Z0256789SDF6HDFJJ4EF__89098748f6b765436ghjk0567f67c900.dev.example.com_CNAME)"