🛠️

Terraform 1.5 で追加された import ブロックと HCL 自動生成オプションの組合せが便利

2024/01/10に公開

こんにちは、SRE 部の小堀内です。

Terraform v1.5.0 で追加された import ブロックと terraform plan 時の HCL コードの自動生成オプション -generate-config-out がとても便利だったのでその使い方をご紹介します。

はじめに

Terraform v1.5.0 より前のバージョンで、実環境のリソースを .tfstate に反映させるためには、下記のように import コマンド を使用する必要がありました。

example
$ terraform import google_project_iam_member.default "my-project roles/viewer user:foo@example.com"

さらに、その後は .tf と import 後の .tfstate の差分を無くすためにドキュメント等を確認しながら .tf を更新していく必要がありました。

しかし、v1.5.0 以降のバージョンでは、少々手間に感じていた上記の作業を効率化することができるようになりました。
それが、本記事のテーマとなっている import ブロック & HCL コードの自動生成オプション となります。

検証環境

  • IaC Tool
    • Terraform v1.6.0
      • Google Provider v4.56.0
  • Infrastructure
    • Google Cloud

使用方法

それでは、import ブロックと HCL コード自動生成オプションを用いた import の方法を紹介していきます。

それに伴い、今回の検証で import 対象とする Google Cloud リソースを下記の 3 つとしました。

  • google_compute_network
  • google_project_iam_member
  • google_storage_bucket

1. import ブロックの定義

構文は下記の通りです。

syntax
import {
  id = import_id
  to = resource_id + resource_name
}

構文の確認が終わったところで、実際に 3 リソース分の import ブロックを import.tf に定義していきます。

import.tf
# google_compute_network
import {
  id = "projects/${local.project}/global/networks/default"
  to = google_compute_network.default
}

# google_project_iam_member
import {
  id = "${local.project} roles/editor user:foo@example.com"
  to = google_project_iam_member.foo_editor
}

# google_storage_bucket
import {
  id = "${local.project}/my-storage-bucket"
  to = google_storage_bucket.my_storage_bucket
}

2. HCL コード自動生成オプションを付与した terraform plan の実行

terraform plan コマンドの HCL コード自動生成オプション -generate-config-out を使用して import リソースに対応する .tf を自動生成します。

公式ドキュメント に記載通りの構文でコマンドを実行します。

$ terraform plan -generate-config-out=generated.tf

実環境のリソース状態を .tfstate へ反映する旨の plan 結果となり、さらに .tfstate に対応する .tf が生成されました。

plan 結果
Terraform will perform the following actions:

  # google_compute_network.default will be imported
    resource "google_compute_network" "default" {
        auto_create_subnetworks         = true
        delete_default_routes_on_create = false
        description                     = "Default network for the project"
        enable_ula_internal_ipv6        = false
        id                              = "projects/my-project/global/networks/default"
        mtu                             = 0
        name                            = "default"
        project                         = "my-project"
        routing_mode                    = "REGIONAL"
        self_link                       = "https://www.googleapis.com/compute/v1/projects/my-project/global/networks/default"

        timeouts {}
    }

  # google_project_iam_member.foo_editor will be imported
    resource "google_project_iam_member" "foo_editor" {
        etag    = "abcdefg"
        id      = "my-project/roles/editor/user:foo@example.com"
        member  = "user:foo@example.com"
        project = "my-project"
        role    = "roles/editor"
    }

  # google_storage_bucket.my_storage_bucket will be imported
    resource "google_storage_bucket" "my_storage_bucket" {
        default_event_based_hold    = false
        force_destroy               = false
        id                          = "my-storage-bucket"
        labels                      = {}
        location                    = "ASIA-NORTHEAST1"
        name                        = "my-storage-bucket"
        project                     = "my-project"
        public_access_prevention    = "enforced"
        requester_pays              = false
        self_link                   = "https://www.googleapis.com/storage/v1/b/my-storage-bucket"
        storage_class               = "STANDARD"
        uniform_bucket_level_access = true
        url                         = "gs://my-storage-bucket"

        timeouts {}
    }

Plan: 3 to import, 0 to add, 0 to change, 0 to destroy.
自動生成された generated.tf
# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.

# __generated__ by Terraform from "projects/my-project/global/networks/default"
resource "google_compute_network" "default" {
  auto_create_subnetworks         = true
  delete_default_routes_on_create = false
  description                     = "Default network for the project"
  enable_ula_internal_ipv6        = false
  internal_ipv6_range             = null
  mtu                             = 0
  name                            = "default"
  project                         = "my-project"
  routing_mode                    = "REGIONAL"
  timeouts {
    create = null
    delete = null
    update = null
  }
}

# __generated__ by Terraform from "my-project roles/editor user:foo@example.com"
resource "google_project_iam_member" "foo_editor" {
  member  = "user:foo@example.com"
  project = "my-project"
  role    = "roles/editor"
}

# __generated__ by Terraform from "my-project/my-storage-bucket"
resource "google_storage_bucket" "my_storage_bucket" {
  default_event_based_hold    = false
  force_destroy               = false
  labels                      = {}
  location                    = "ASIA-NORTHEAST1"
  name                        = "my-storage-bucket"
  project                     = "my-project"
  public_access_prevention    = "enforced"
  requester_pays              = false
  storage_class               = "STANDARD"
  uniform_bucket_level_access = true
  timeouts {
    create = null
    read   = null
    update = null
  }
}

3. terraform apply の実行

次は、3 リソース分の import 計画を実行に移します。
すると .tfstate に実環境リソースの状態が反映されます。

追加された tfstate(一部抜粋)
{
      "mode": "managed",
      "type": "google_project_iam_member",
      "name": "foo_editor",
      "provider": "provider[\"registry.terraform.io/hashicorp/google\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "condition": [],
            "etag": "abcdefg",
            "id": "my-project/roles/editor/user:foo@example.com",
            "member": "user:foo@example.com",
            "project": "my-project",
            "role": "roles/editor"
          },
          "sensitive_attributes": [],
          "private": "abcdef"
        }
      ]
    },

また、3 リソース分の import が完了した旨のメッセージが出力されます。

Apply complete! Resources: 3 imported, 0 added, 0 changed, 0 destroyed.

確認のため terraform plan の再実行

.tfstate への実環境リソース反映が完了したので、再度 terraform plan を実行してみます。

自動生成オプションの使用により .tfstate に対応する .tf も出来上がっているため、差分無しを示す No changes. が出力されるはずです。

import & HCL コード自動生成後の plan 結果
No changes. Your infrastructure matches the configuration.

想定通りの結果となりました。

まとめ

Terraform で import を行う場合、従来のように手作業で .tf ファイルに HCL コードを書いていくのではなく、import ブロック & HCL コード自動生成オプション を使用することで import にかかる時間を大幅に短縮することができました。

注意点としては、既に CI/CD パイプラインが構築されたプロジェクトにおいて、import ブロックを使用したい場合で Terraform バージョンが 1.5.0 未満の場合は、バージョンアップによる影響を十分に考慮する必要があります。

その他の点に関して、import ブロックによる .tfstate への反映は terraform apply 時に行われるため CI/CD パイプラインの大幅な変更は必要ないように感じました。

これからも IaC に対する理解をさらに深めていきたいと思います。

参考

Discussion