Terraform import, moved, removed ブロック サンプル

# はじめに

Terraform の便利なブロックである3つのブロックを自身の備忘も兼ねて、簡単なサンプルと共にまとめました。


それぞれ以下のバージョンで追加されたブロックとなります。
- [import](https://developer.hashicorp.com/terraform/language/import): 1.5 
- [moved](https://developer.hashicorp.com/terraform/language/moved): 1.1
- [removed](https://developer.hashicorp.com/terraform/language/resources/syntax#removing-resources): 1.7

# サンプルユースケース

今回のサンプルは以下のユースケースのシナリオとなります。


1. **[import]** : これまでコード化されていなかったバケットを import して Terraform 管理下にする
1. **[moved]** : ステートを module の配下に移動する
1. **[removed]** : やっぱり Terraform 管理から外す

対象のサンプル S3 バケット名: `test-bucket-js3t5x9`

```terraform
# 全フロー共通 terraform ブロック
terraform {
  required_version = "1.9.8"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.75.0"
    }
  }
}
```

# import

:::note
**[import]** : これまでコード化されていなかったバケットを import して Terraform 管理下にする
:::

まず、対象の tf ファイルにimport ブロックを記載します。

import ブロックに使用する id はリソースの種類ごとに異なるので、公式ドキュメントの import 欄にて確認します。 [S3 バケットの例](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket#import)


```terraform
# main.tf (←任意のファイル名)

import {
  to = aws_s3_bucket.sample
  id = "test-bucket-js3t5x9" # id はリソースの種類ごとに異なる S3バケットの場合はバケット名
}
```

続いてステートを出力するので `-generate-config-out` オプションを付加して plan を打ちます。

```terraform
$ terraform plan -generate-config-out=generated.tf # generated.tf は任意のファイル名
```

`generated.tf` が生成されるので、適宜に移動します。(もちろん移動せずそのまま使うでもOK)

`main.tf` に移動した例
```terraform
# main.tf

resource "aws_s3_bucket" "sample" {
  bucket              = "test-bucket-js3t5x9"
  bucket_prefix       = null
  force_destroy       = null
  object_lock_enabled = false
  tags                = {}
  tags_all            = {}
}

import {
  to = aws_s3_bucket.sample
  id = "test-bucket-js3t5x9"
}
```

続いて apply

```terraform
$ terraform apply
```


結果
```terraform
$ terraform apply
aws_s3_bucket.sample: Preparing import... [id=test-bucket-js3t5x9]
aws_s3_bucket.sample: Refreshing state... [id=test-bucket-js3t5x9]

Terraform will perform the following actions:

  # aws_s3_bucket.sample will be imported
    resource "aws_s3_bucket" "sample" {
        acceleration_status         = null
        arn                         = "arn:aws:s3:::test-bucket-js3t5x9"
        bucket                      = "test-bucket-js3t5x9"
        bucket_domain_name          = "test-bucket-js3t5x9.s3.amazonaws.com"
        bucket_prefix               = null
        bucket_regional_domain_name = "test-bucket-js3t5x9.s3.ap-northeast-1.amazonaws.com"
        hosted_zone_id              = "XXXXXXXXXXXXXX"
        id                          = "test-bucket-js3t5x9"
        object_lock_enabled         = false
        policy                      = null
        region                      = "ap-northeast-1"
        request_payer               = "BucketOwner"
        tags                        = {}
        tags_all                    = {}

        grant {
            id          = "999999-dummy"
            permissions = [
                "FULL_CONTROL",
            ]
            type        = "CanonicalUser"
            uri         = null
        }

        server_side_encryption_configuration {
            rule {
                bucket_key_enabled = true

                apply_server_side_encryption_by_default {
                    kms_master_key_id = null
                    sse_algorithm     = "AES256"
                }
            }
        }

        versioning {
            enabled    = false
            mfa_delete = false
        }
    }

Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.

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

aws_s3_bucket.sample: Importing... [id=test-bucket-js3t5x9]
aws_s3_bucket.sample: Import complete [id=test-bucket-js3t5x9]

Apply complete! Resources: 1 imported, 0 added, 0 changed, 0 destroyed.
```
これで import が完了です！

:::note warn
これ以降 `import` ブロックは任意のタイミングで削除してOKです。削除しても plan で差分となりません。
:::


# moved

:::note
**[moved]** : ステートを module の配下に移動する
:::

移動元、移動先のコードをそれぞれ実装

```terraform
# main.tf (移動元)

module "s3" {
  source  = "./modules/s3"
  bucket_name = "test-bucket-js3t5x9"
}

# 移動するので削除でOK
# resource "aws_s3_bucket" "sample" {
#   bucket              = "test-bucket-js3t5x9"
#   bucket_prefix       = null
#   force_destroy       = null
#   object_lock_enabled = false
#   tags                = {}
#   tags_all            = {}
# }

# moved ブロックで移動元, 移動先を設定
moved {
  from = aws_s3_bucket.sample
  to   = module.s3.aws_s3_bucket.this
}
```

```terraform
# ./modules/s3/main.tf (移動先)

resource "aws_s3_bucket" "this" {
  bucket              = var.bucket_name
  bucket_prefix       = null
  force_destroy       = null
  object_lock_enabled = false
  tags                = {}
  tags_all            = {}
}

variable "bucket_name" {
  description = "バケット名"
  type        = string
}
```

plan で差分が出ていないことを確認

```terraform
$ terraform plan
module.s3.aws_s3_bucket.this: Refreshing state... [id=test-bucket-js3t5x9]

Terraform will perform the following actions:

  # aws_s3_bucket.sample has moved to module.s3.aws_s3_bucket.this
    resource "aws_s3_bucket" "this" {
        id                          = "test-bucket-js3t5x9"
        tags                        = {}
        # (12 unchanged attributes hidden)

        # (3 unchanged blocks hidden)
    }

Plan: 0 to add, 0 to change, 0 to destroy.
```

続いて apply
```terraform
$ terraform apply

(省略)
.....

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
```
これで移動が完了です！

:::note warn
これ以降 `moved` ブロックは任意のタイミングで削除してOKです。削除しても plan で差分となりません。
:::

# removed

:::note
**[removed]** : やっぱり Terraform 管理から外す
:::

削除するリソースの実装(コメントアウトや行削除など) と removed ブロックを記載します。

注意点として `lifecycle` オプションで `destroy = false` の指定が必要です。指定しなかった場合、管理外にするのではなく、リソースが削除されてしまいます。

```terraform
# main.tf

# module の呼び出しをやめた
# module "s3" {
#   source  = "./modules/s3"  # Terraform Registry上のモジュール
#   bucket_name = "test-bucket-js3t5x9"
# }

removed {
  from = module.s3.aws_s3_bucket.this
  lifecycle {
    destroy = false 
  }
}
```

plan で差分が出ていない & リソース自体は削除されないことを確認

```terraform
$ terraform plan
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:

 # module.s3.aws_s3_bucket.this will no longer be managed by Terraform, but will not be destroyed
 # (destroy = false is set in the configuration)
 . resource "aws_s3_bucket" "this" {
        id                          = "test-bucket-js3t5x9"
        tags                        = {}
        # (12 unchanged attributes hidden)

        # (3 unchanged blocks hidden)
    }

Plan: 0 to add, 0 to change, 0 to destroy.
```

続いて apply
```terraform
$ terraform apply

(省略)
.....

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
```

Terraform 管理からの除外が完了です！

:::note warn
これ以降 `removed` ブロックは任意のタイミングで削除してOKです。削除しても plan で差分となりません。
:::