Terraform removed ブロックで for_each にてインデックスやキーが付与されているリソースを管理外にする回避策

## はじめに

Terraform の管理下から外したいときに使用する `removed` ブロックですが、for_each などで作成した際に付与されるインデックスやキーがアドレス中にある場合エラーとなってしまいます。

GitHub の issue にもあがっていて、記事作成時点(2024.11.18)で Open のままです。
https://github.com/hashicorp/terraform/issues/34439


回避策としてワークアラウンド的になりますが、この issue 内にある `moved` ブロックを経由してやる方法でうまくいったので、`removed` ブロックが対応されるまでこの方法を使用することとしました。
https://github.com/hashicorp/terraform/issues/34439#issuecomment-2178128452



## 事象を改めて

バージョン関連
```hcl
terraform {
  required_version = "1.9.8"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.75.0"
    }
  }
}

```
削除対象のリソース

```hcl
variable "vpcs" {
  type = map(string)
  default = {
    "vpc-sample-1" : "10.0.0.0/16"
    "vpc-sample-2" : "10.1.0.0/16"
  }
}

resource "aws_vpc" "sample" {
  for_each      = var.vpcs
  
  cidr_block       = each.value
  instance_tenancy = "default"
  tags = {
    Name = each.key
  }
}
```
以下のようにそのままアドレスを指定して実行すると
```hcl
removed {
  from = aws_vpc.sample["vpc-sample-1"]
  lifecycle {
    destroy = false # 実体は残すためのオプション
  }
}
```

エラーとなります
```hcl
│ Error: Resource instance keys not allowed
│ 
│   on 4_remove_block_with_for_each.tf line 20, in removed:
│   20:   from = aws_vpc.sample["vpc-sample-1"]
│ 
│ Resource address must be a resource (e.g. "test_instance.foo"), not a resource instance (e.g. "test_instance.foo[1]").
```

## moved を経由しての回避策

以下の通り修正


```diff_hcl
# 差分抜粋
### vpc-sample-1
+ # moved ブロックを記載して、インデックスやキーが無いアドレスへ移動させます。
+ moved {
+   from = aws_vpc.sample["vpc-sample-1"]
+   to   = aws_vpc.sample_vpc-sample-1
+ }
+ 
+ # removed ブロックでは上記で移動した先のアドレスを使用します。
removed {
-   from = aws_vpc.sample["vpc-sample-1"]
+   from = aws_vpc.sample_vpc-sample-1
  lifecycle {
    destroy = false # 実体は残すためのオプション
  }
}
```


```diff_hcl
# 全体
### vpc-sample-1
# moved ブロックを記載して、インデックスやキーが無いアドレスへ移動させます。
moved {
  from = aws_vpc.sample["vpc-sample-1"]
  to   = aws_vpc.sample_vpc-sample-1
}

# removed ブロックでは上記で移動した先のアドレスを使用します。
removed {
  from = aws_vpc.sample_vpc-sample-1
  lifecycle {
    destroy = false # 実体は残すためのオプション
  }
}

### vpc-sample-2
moved {
  from = aws_vpc.sample["vpc-sample-2"]
  to   = aws_vpc.sample_vpc-sample-2
}
removed {
  from = aws_vpc.sample_vpc-sample-2
  lifecycle {
    destroy = false 
  }
}
```
apply します
```hcl
$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:

Terraform will perform the following actions:

 # aws_vpc.sample_vpc-sample-1 will no longer be managed by Terraform, but will not be destroyed
 # (destroy = false is set in the configuration)
  # (moved from aws_vpc.sample["vpc-sample-1"])
 . resource "aws_vpc" "sample_vpc-sample-1" {
        id                                   = "vpc-0707284a01204b7d8"
        tags                                 = {
            "Name" = "vpc-sample-1"
        }
        # (19 unchanged attributes hidden)
    }

 # aws_vpc.sample_vpc-sample-2 will no longer be managed by Terraform, but will not be destroyed
 # (destroy = false is set in the configuration)
  # (moved from aws_vpc.sample["vpc-sample-2"])
 . resource "aws_vpc" "sample_vpc-sample-2" {
        id                                   = "vpc-04492ed99b35fb8b0"
        tags                                 = {
            "Name" = "vpc-sample-2"
        }
        # (19 unchanged attributes hidden)
    }

Plan: 0 to add, 0 to change, 0 to destroy.
╷
│ Warning: Some objects will no longer be managed by Terraform
│ 
│ If you apply this plan, Terraform will discard its tracking information for the following objects, but it will not delete them:
│  - aws_vpc.sample_vpc-sample-1
│  - aws_vpc.sample_vpc-sample-2
│ 
│ After applying this plan, Terraform will no longer manage these objects. You will need to import them into Terraform to manage them again.
╵

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes


Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
```
`moved` で一旦インデックスやキーを持たないアドレスに移動し、その後 `removed` ブロックで管理下から外すことが出来ました！



## Ref
- [Terraform - Removing Resources
](https://developer.hashicorp.com/terraform/language/resources/syntax#removing-resources)
- [Removed block - instance support #34439](https://github.com/hashicorp/terraform/issues/34439)


## おわりに

すぐにサポートされる可能性もありますが、ひとまずこの方法で回避可能です。issue の受け売りですが参考になれば幸いです。