Terraformでの"Instance cannot be destroyed"に対処する

いま、新規サービスを開発するにあたり、Terraformを利用しています。
Terraformは便利ですが、なかなかハマりどころも多いですね。

Cloud KMSのKeyを扱う際に"Instance cannot be destroyed"エラーが出て時間を溶かしたので、対応した内容を記録します。

## 環境
- GCP
- Terraform : Ver.1.0.1 
- 利用したmodule : [terraform-google-modules/kms/google](https://registry.terraform.io/modules/terraform-google-modules/kms/google/latest) Ver. 1.2


## 手動で作成したCloud KMS KeyをTerraformに反映させる
1. Terraform経由でCloud KMSのkeyringと、key("key_terraform")を作成しました。
2. 手動でKey("key_manual")をGCPのConsoleから手動で作成しました。

手動で作成した鍵をTerraformに反映させようとしました。


## "Instance cannot be destroyed"が発生

Terraformに反映させようと以下のtfファイルで`terraform plan`したところ以下のエラーが発生しました。

```tf
module "kms" {
  source     = "terraform-google-modules/kms/google"
  version    = "~> 1.2"
  project_id = "sample_project"
  location            = "global"
  keyring             = "sample_key_ring"
  keys                = ["key_terraform", "key_manual"]
  set_owners_for      = []
  key_rotation_period = "157680000s"
}
```


```bash
$ terraform plan
....

│ Error: Instance cannot be destroyed
│ 
│   on .terraform/modules/kms/main.tf line 27:
│   27: resource "google_kms_crypto_key" "key" {
│ 
│ Resource module.kms.google_kms_crypto_key.key[0] has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the plan, either disable
│ lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.

│ Error: Instance cannot be destroyed
│ 
│   on .terraform/modules/kms/main.tf line 27:
│   27: resource "google_kms_crypto_key" "key" {
│ 
│ Resource module.kms.google_kms_crypto_key.key has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the plan, either disable
│ lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.
```
このエラーが出ていると、他のリソースのapplyが反映されないので、解決する必要があります。

## prevent_destroyをtrueにする

Terraformはリソースの再作成を行うのに対し、
エラーメッセージの通り、Cloud KMSの鍵は削除できないためです。

moduleの[ドキュメント](https://registry.terraform.io/modules/terraform-google-modules/kms/google/latest)を見ると、prevent_destroyというパラメータがあるので付与します。

```tf
module "kms" {
  source     = "terraform-google-modules/kms/google"
  version    = "~> 1.2"
  project_id = "sample_project"
  location            = "global"
  keyring             = "sample_key_ring"
  keys                = ["key_terraform", "key_manual"]
  set_owners_for      = []
  key_rotation_period = "157680000s" # 追加
}
```

```bash
$ terraform plan
....

│ Error: Instance cannot be destroyed
│ 
│   on .terraform/modules/kms/main.tf line 27:
│   27: resource "google_kms_crypto_key" "key" {
│ 
│ Resource module.kms.google_kms_crypto_key.key has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the plan, either disable
│ lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.
```

エラーは一つ減りましたが、同じエラーが発生しました。
手動で作成した鍵"key_manual"の再作成をTerraformが試みており、
名前は同じでもTerraform設定と実際の鍵が異なるものと認識されているようです。

key_rotation_period（鍵のローテーション期間）をtfファイルと揃えてもだめでした。
GCPのConsole上はterraformで作成したもの"key_terraform"と差分がありません。

![スクリーンショット 2021-07-20 1.38.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222931/90d76889-d4a4-5687-9cce-74249b056e9a.png)

## tfstateファイルを編集する

調べていくうちに以下のstackoverflowの投稿がありました。

https://stackoverflow.com/questions/61595242/error-message-while-deleting-google-kms-crypto-key-resource

こちらにあるように、あとはGCS上に配置したstateファイルを編集するしかなさそうです。
ダウンロードしたstateファイルの中で、"key_terraform"と"key_manual"の設定値を比較します。

![スクリーンショット 2021-07-20 1.50.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222931/2d2ebbc2-5970-a512-ee2b-6965ea8f04d5.png)
terraform経由で作成したkeyと比較すると、やはり内部的にはいくつか差分がありました。
- indexがない
- timeoutsの形式が異なる
- dependenciesがない

バックアップをとりつつ、"key_terraform"にあわせるように編集しました。

そして、改めて、`terraform plan`を実行します。

```bash
$ terraform plan
....

│ No changes. Your infrastructure matches the configuration.
```

ようやく、差分が出なくなり、反映されました！

[stackoverflow](https://stackoverflow.com/questions/61595242/error-message-while-deleting-google-kms-crypto-key-resource)のコメントにあるように、Cloud KMSの鍵は破棄して利用できなくさせることはできても、
完全に削除することができないので、再作成するterraformの設計にはそぐわないようです。

GCPでは、CloudSQLなども同じ名前のインスタンスをすぐ再作成できないなどもあるので、その場合も削除しないように回避したり、stateファイルを直接編集したりする必要がありそうです。