【Terraform】importブロックの機能で遊んでみた！！

いつも記事を読んでいただきありがとうございます！
モブエンジニア（[@mob-engineer](https://qiita.com/mob_engineer))です！

今回は社内LT会で個人的に面白いと感じた**importブロックの機能で遊んでみた**を記事としてアウトプットしたいと思います。

初心者の方でも理解できるように平易な表現で記事を執筆いたしますので、お気軽に読んでいただければ幸いです。

#　目次
- 読んでもらいたい読者層
- import機能とは？
- S3バケットで試してみた
- まとめ

## 読んでもらいたい読者層
次のような読者層に本記事を活用していただきたいと考えています。

- Terraformをより便利に活用したい方
- 既存リソースのコード化を柔軟に行いたい方
- Terraformの機能をキャッチアップしたいと思っている方

:::note info
本記事ではimportブロックの機能を紹介しております。そのため、Terraform初期設定に関しては割愛しておりますのでご留意いただけますと幸いです。

○　Terraform初期設定紹介サイト
https://zenn.dev/takehiro1111/articles/terraform_install
:::

## import機能とは？
**Terraform v1.5.0**でリリースされた機能となり、**既存のインフラリソースをインポートし、Terraformで管理できるようにする機能**となります。

今までも、**terraform import**コマンドで既存リソースをインポートすることは可能でしたが、生成されるファイルは中間ファイルのため、**Terraformの構文に合わせて修正する必要**がありました。

importブロックの管理の場合、修正作業が発生しないため、手軽に既存リソースを管理できるところがオイシイところといった印象があります。

importブロックの構文として次の通りです。

``` terraform
import {
  to = aws_instance.example
  id = "hogehoge"
}

resource "aws_instance" "example" {
  name = "test"
}
```

:::note info
importブロックで必要なパラメータ
- to => TFファイル上でのリソース名
- id => 既存リソースのID情報（インスタンスIDなど）
:::

また、公式ドキュメントを見ると、一つのimportブロックで複数リソースを管理できるようです。

``` terraform
locals {
  buckets = {
    "staging" = "bucket1"
    "uat"     = "bucket2"
    "prod"    = "bucket3"
  }
}

import {
  for_each = local.buckets
  to = aws_s3_bucket.this[each.key]
  id = each.value
}

resource "aws_s3_bucket" "this" {
  for_each = local.buckets
}
```

といった感じで、使いこなせば**既存リソースの管理を楽にできる便利ツール**と思ってもらえればいいのかなぁと思います。

## S3バケットで試してみた
それでは、簡単に私の環境で試してみたいと思います。

#### ディレクトリ構成
- s3-terraform（Terraformで構築したS3）
- s3-handmade（手動で構築したS3）

それぞれのmain.tf設定は以下のとおりです。

・s3-terraform/main.tf
```terraform
resource "random_id" "bucket_suffix" {
  byte_length = 4
}

locals {
  s3_bucket_name = "test-terraform-${random_id.bucket_suffix.hex}"
}

resource "aws_s3_bucket" "s3-test-terraform" {
  bucket = local.s3_bucket_name

  tags = {
    Environment = "Dev"
    Name        = local.s3_bucket_name
  }
}

```

・s3-handmade/main.tf
```terraform
terraform {
  required_version = ">= 1.5.0"
}

locals {
  s3_bucket_name = "test-handmade"
}

resource "aws_s3_bucket" "test-handmade" {
  bucket = local.s3_bucket_name

  tags = {
    Environment = "Production"
    Owner       = "Team"
  }
}

import {
  to   = aws_s3_bucket.test-handmade
  id   = "s3-handmade"
}
```

AWS環境の画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/79d83cd5-6e17-e53a-93e5-fbcd11f92ffd.png)

最初にs3-terraformを構築しましょう

```text
$ cd s3-terraform
$ terraform init
    Initializing the backend...
    Initializing provider plugins...
    - Finding latest version of hashicorp/aws...
    - Installing hashicorp/aws v5.76.0...
    - Installed hashicorp/aws v5.76.0 (signed by HashiCorp)
    Terraform has created a lock file .terraform.lock.hcl to record the provider
    selections it made above. Include this file in your version control repository
    so that Terraform can guarantee to make the same selections by default when
    you run "terraform init" in the future.
    
    Terraform has been successfully initialized!
    
    You may now begin working with Terraform. Try running "terraform plan" to see
    any changes that are required for your infrastructure. All Terraform commands
    should now work.
    
    If you ever set or change modules or backend configuration for Terraform,
    rerun this command to reinitialize your working directory. If you forget, other
    commands will detect it and remind you to do so if necessary.
$ terraform apply
  
    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
      + create
    
    Terraform will perform the following actions:
    
      # aws_s3_bucket.s3-terraform will be created
      + resource "aws_s3_bucket" "s3-terraform" {
          + acceleration_status         = (known after apply)
          + acl                         = (known after apply)
          + arn                         = (known after apply)
          + bucket                      = (known after apply)
          + bucket_domain_name          = (known after apply)
          + bucket_prefix               = (known after apply)
          + bucket_regional_domain_name = (known after apply)
          + force_destroy               = false
          + hosted_zone_id              = (known after apply)
          + id                          = (known after apply)
          + object_lock_enabled         = (known after apply)
          + policy                      = (known after apply)
          + region                      = (known after apply)
          + request_payer               = (known after apply)
          + tags                        = {
              + "Environment" = "Dev"
              + "Name"        = "test-terraform-made"
            }
          + tags_all                    = {
              + "Environment" = "Dev"
              + "Name"        = "test-terraform-made"
            }
          + website_domain              = (known after apply)
          + website_endpoint            = (known after apply)
    
          + cors_rule (known after apply)
    
          + grant (known after apply)
    
          + lifecycle_rule (known after apply)
    
          + logging (known after apply)
    
          + object_lock_configuration (known after apply)
    
          + replication_configuration (known after apply)
    
          + server_side_encryption_configuration (known after apply)
    
          + versioning (known after apply)
    
          + website (known after apply)
        }
    
    Plan: 1 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.s3-terraform: Creating...
    aws_s3_bucket.s3-terraform: Creation complete after 2s [id=test-terraform-made]
    
    Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
```

実行後のAWS環境
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/f70bc265-41c5-4a49-a986-c461e0e572ae.png)


では、本題のs3-handmadeをTerraform管理下に置いて、削除してみよう

``` text
$ terraform init
    Initializing the backend...
    Initializing provider plugins...
    - Finding latest version of hashicorp/aws...
    - Installing hashicorp/aws v5.76.0...
    - Installed hashicorp/aws v5.76.0 (signed by HashiCorp)
    Terraform has created a lock file .terraform.lock.hcl to record the provider
    selections it made above. Include this file in your version control repository
    so that Terraform can guarantee to make the same selections by default when
    you run "terraform init" in the future.
    
    Terraform has been successfully initialized!
    
    You may now begin working with Terraform. Try running "terraform plan" to see
    any changes that are required for your infrastructure. All Terraform commands
    should now work.
    
    If you ever set or change modules or backend configuration for Terraform,
    rerun this command to reinitialize your working directory. If you forget, other
    commands will detect it and remind you to do so if necessary.
$ terraform apply
    aws_s3_bucket.test-handmade: Preparing import... [id=s3-handmade]
    aws_s3_bucket.test-handmade: Refreshing state... [id=s3-handmade]
    
    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
    -/+ destroy and then create replacement
    
    Terraform will perform the following actions:
    
      # aws_s3_bucket.test-handmade must be replaced
      # (imported from "s3-handmade")
      # Warning: this will destroy the imported resource
    -/+ resource "aws_s3_bucket" "test-handmade" {
          + acceleration_status         = (known after apply)
          + acl                         = (known after apply)
          ~ arn                         = "arn:aws:s3:::s3-handmade" -> (known after apply)
          ~ bucket                      = "s3-handmade" -> "test-handmade" # forces replacement
          ~ bucket_domain_name          = "s3-handmade.s3.amazonaws.com" -> (known after apply)
          + bucket_prefix               = (known after apply)
          ~ bucket_regional_domain_name = "s3-handmade.s3.ap-northeast-1.amazonaws.com" -> (known after apply)
          + force_destroy               = false
          ~ hosted_zone_id              = "Z2M4EHUR26P7ZW" -> (known after apply)
          ~ id                          = "s3-handmade" -> (known after apply)
          ~ object_lock_enabled         = false -> (known after apply)
          + policy                      = (known after apply)
          ~ region                      = "ap-northeast-1" -> (known after apply)
          ~ request_payer               = "BucketOwner" -> (known after apply)
          ~ tags                        = {
              + "Environment" = "Production"
              + "Owner"       = "Team"
            }
          ~ tags_all                    = {
              + "Environment" = "Production"
              + "Owner"       = "Team"
            }
          + website_domain              = (known after apply)
          + website_endpoint            = (known after apply)
    
          ~ cors_rule {
              + acceleration_status         = (known after apply)
              + acl                         = (known after apply)
              + arn                         = (known after apply)
              + bucket                      = (known after apply)
              + bucket_domain_name          = (known after apply)
              + bucket_prefix               = (known after apply)
              + bucket_regional_domain_name = (known after apply)
              + force_destroy               = (known after apply)
              + hosted_zone_id              = (known after apply)
              + id                          = (known after apply)
              + object_lock_enabled         = (known after apply)
              + policy                      = (known after apply)
              + region                      = (known after apply)
              + request_payer               = (known after apply)
              + tags                        = (known after apply)
              + tags_all                    = (known after apply)
              + website_domain              = (known after apply)
              + website_endpoint            = (known after apply)
            } -> (known after apply)
    
          ~ grant {
              + acceleration_status         = (known after apply)
              + acl                         = (known after apply)
              + arn                         = (known after apply)
              + bucket                      = (known after apply)
              + bucket_domain_name          = (known after apply)
              + bucket_prefix               = (known after apply)
              + bucket_regional_domain_name = (known after apply)
              + force_destroy               = (known after apply)
              + hosted_zone_id              = (known after apply)
              + id                          = (known after apply)
              + object_lock_enabled         = (known after apply)
              + policy                      = (known after apply)
              + region                      = (known after apply)
              + request_payer               = (known after apply)
              + tags                        = (known after apply)
              + tags_all                    = (known after apply)
              + website_domain              = (known after apply)
              + website_endpoint            = (known after apply)
            } -> (known after apply)
    
          ~ lifecycle_rule {
              + acceleration_status         = (known after apply)
              + acl                         = (known after apply)
              + arn                         = (known after apply)
              + bucket                      = (known after apply)
              + bucket_domain_name          = (known after apply)
              + bucket_prefix               = (known after apply)
              + bucket_regional_domain_name = (known after apply)
              + force_destroy               = (known after apply)
              + hosted_zone_id              = (known after apply)
              + id                          = (known after apply)
              + object_lock_enabled         = (known after apply)
              + policy                      = (known after apply)
              + region                      = (known after apply)
              + request_payer               = (known after apply)
              + tags                        = (known after apply)
              + tags_all                    = (known after apply)
              + website_domain              = (known after apply)
              + website_endpoint            = (known after apply)
            } -> (known after apply)
    
          ~ logging {
              + acceleration_status         = (known after apply)
              + acl                         = (known after apply)
              + arn                         = (known after apply)
              + bucket                      = (known after apply)
              + bucket_domain_name          = (known after apply)
              + bucket_prefix               = (known after apply)
              + bucket_regional_domain_name = (known after apply)
              + force_destroy               = (known after apply)
              + hosted_zone_id              = (known after apply)
              + id                          = (known after apply)
              + object_lock_enabled         = (known after apply)
              + policy                      = (known after apply)
              + region                      = (known after apply)
              + request_payer               = (known after apply)
              + tags                        = (known after apply)
              + tags_all                    = (known after apply)
              + website_domain              = (known after apply)
              + website_endpoint            = (known after apply)
            } -> (known after apply)
    
          ~ object_lock_configuration {
              + acceleration_status         = (known after apply)
              + acl                         = (known after apply)
              + arn                         = (known after apply)
              + bucket                      = (known after apply)
              + bucket_domain_name          = (known after apply)
              + bucket_prefix               = (known after apply)
              + bucket_regional_domain_name = (known after apply)
              + force_destroy               = (known after apply)
              + hosted_zone_id              = (known after apply)
              + id                          = (known after apply)
              + object_lock_enabled         = (known after apply)
              + policy                      = (known after apply)
              + region                      = (known after apply)
              + request_payer               = (known after apply)
              + tags                        = (known after apply)
              + tags_all                    = (known after apply)
              + website_domain              = (known after apply)
              + website_endpoint            = (known after apply)
            } -> (known after apply)
    
          ~ replication_configuration {
              + acceleration_status         = (known after apply)
              + acl                         = (known after apply)
              + arn                         = (known after apply)
              + bucket                      = (known after apply)
              + bucket_domain_name          = (known after apply)
              + bucket_prefix               = (known after apply)
              + bucket_regional_domain_name = (known after apply)
              + force_destroy               = (known after apply)
              + hosted_zone_id              = (known after apply)
              + id                          = (known after apply)
              + object_lock_enabled         = (known after apply)
              + policy                      = (known after apply)
              + region                      = (known after apply)
              + request_payer               = (known after apply)
              + tags                        = (known after apply)
              + tags_all                    = (known after apply)
              + website_domain              = (known after apply)
              + website_endpoint            = (known after apply)
            } -> (known after apply)
    
          ~ server_side_encryption_configuration {
              + acceleration_status         = (known after apply)
              + acl                         = (known after apply)
              + arn                         = (known after apply)
              + bucket                      = (known after apply)
              + bucket_domain_name          = (known after apply)
              + bucket_prefix               = (known after apply)
              + bucket_regional_domain_name = (known after apply)
              + force_destroy               = (known after apply)
              + hosted_zone_id              = (known after apply)
              + id                          = (known after apply)
              + object_lock_enabled         = (known after apply)
              + policy                      = (known after apply)
              + region                      = (known after apply)
              + request_payer               = (known after apply)
              + tags                        = (known after apply)
              + tags_all                    = (known after apply)
              + website_domain              = (known after apply)
              + website_endpoint            = (known after apply)
            } -> (known after apply)
    
          ~ versioning {
              + acceleration_status         = (known after apply)
              + acl                         = (known after apply)
              + arn                         = (known after apply)
              + bucket                      = (known after apply)
              + bucket_domain_name          = (known after apply)
              + bucket_prefix               = (known after apply)
              + bucket_regional_domain_name = (known after apply)
              + force_destroy               = (known after apply)
              + hosted_zone_id              = (known after apply)
              + id                          = (known after apply)
              + object_lock_enabled         = (known after apply)
              + policy                      = (known after apply)
              + region                      = (known after apply)
              + request_payer               = (known after apply)
              + tags                        = (known after apply)
              + tags_all                    = (known after apply)
              + website_domain              = (known after apply)
              + website_endpoint            = (known after apply)
            } -> (known after apply)
    
          ~ website {
              + acceleration_status         = (known after apply)
              + acl                         = (known after apply)
              + arn                         = (known after apply)
              + bucket                      = (known after apply)
              + bucket_domain_name          = (known after apply)
              + bucket_prefix               = (known after apply)
              + bucket_regional_domain_name = (known after apply)
              + force_destroy               = (known after apply)
              + hosted_zone_id              = (known after apply)
              + id                          = (known after apply)
              + object_lock_enabled         = (known after apply)
              + policy                      = (known after apply)
              + region                      = (known after apply)
              + request_payer               = (known after apply)
              + tags                        = (known after apply)
              + tags_all                    = (known after apply)
              + website_domain              = (known after apply)
              + website_endpoint            = (known after apply)
            } -> (known after apply)
        }
    
    Plan: 1 to import, 1 to add, 0 to change, 1 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.test-handmade: Importing... [id=s3-handmade]
    aws_s3_bucket.test-handmade: Import complete [id=s3-handmade]
    aws_s3_bucket.test-handmade: Destroying... [id=s3-handmade]
    aws_s3_bucket.test-handmade: Destruction complete after 1s
    aws_s3_bucket.test-handmade: Creating...
    aws_s3_bucket.test-handmade: Creation complete after 1s [id=test-handmade]
    
    Apply complete! Resources: 1 imported, 1 added, 0 changed, 1 destroyed.

$ terraform destroy
    aws_s3_bucket.test-handmade: Refreshing state... [id=test-handmade]
    
    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
      - destroy
    
    Terraform will perform the following actions:
    
      # aws_s3_bucket.test-handmade will be destroyed
      - resource "aws_s3_bucket" "test-handmade" {
          - arn                         = "arn:aws:s3:::test-handmade" -> null
          - bucket                      = "test-handmade" -> null
          - bucket_domain_name          = "test-handmade.s3.amazonaws.com" -> null
          - bucket_regional_domain_name = "test-handmade.s3.ap-northeast-1.amazonaws.com" -> null
          - force_destroy               = false -> null
          - hosted_zone_id              = "Z2M4EHUR26P7ZW" -> null
          - id                          = "test-handmade" -> null
          - object_lock_enabled         = false -> null
          - region                      = "ap-northeast-1" -> null
          - request_payer               = "BucketOwner" -> null
          - tags                        = {
              - "Environment" = "Production"
              - "Owner"       = "Team"
            } -> null
          - tags_all                    = {
              - "Environment" = "Production"
              - "Owner"       = "Team"
            } -> null
            # (3 unchanged attributes hidden)
    
          - grant {
              - id          = "7f5f52403d181505dfc57937ba2cd54d898c7baeee69ef70ac8cbe0b899f6100" -> null
              - permissions = [
                  - "FULL_CONTROL",
                ] -> null
              - type        = "CanonicalUser" -> null
                # (1 unchanged attribute hidden)
            }
    
          - server_side_encryption_configuration {
              - rule {
                  - bucket_key_enabled = false -> null
    
                  - apply_server_side_encryption_by_default {
                      - sse_algorithm     = "AES256" -> null
                        # (1 unchanged attribute hidden)
                    }
                }
            }
    
          - versioning {
              - enabled    = false -> null
              - mfa_delete = false -> null
            }
        }
    
    Plan: 0 to add, 0 to change, 1 to destroy.
    
    Do you really want to destroy all resources?
      Terraform will destroy all your managed infrastructure, as shown above.
      There is no undo. Only 'yes' will be accepted to confirm.
    
      Enter a value: yes
    
    aws_s3_bucket.test-handmade: Destroying... [id=test-handmade]
    aws_s3_bucket.test-handmade: Destruction complete after 0s
    
    Destroy complete! Resources: 1 destroyed.
```

AWS画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/720c6788-b52e-93f0-cbc5-cca5c032f484.png)

手動作成したs3-handmadeが無事消えていますね。

## まとめ
今回の記事ではimportブロックの機能紹介と簡単なハンズオンを行ってみました。そのうえで、Terraformが意外と癖のある動きをするので、ハンズオンを行うのが少々苦労しました。

そのうえで、簡単な記述で既存リソースを管理できるので、エンジニア視点ではオイシイ印象を持ちました。また、今回取り上げたのがS3のため、**単体で機能する**リソース**のため、EC2などの**複数リソースが絡む場合**の挙動調査も行ってみたいと思いました。

最後まで記事を読んでいただきありがとうございます。
本記事を通じて、少しでもTerraformに親しみを持っていただける方が増えれば幸いです。

## 参考サイト
https://developer.hashicorp.com/terraform/language/import

https://dev.classmethod.jp/articles/terraform-import-command-and-import-block/