🛝

【Terraform】terraform importで既存リソースをTerraform管理下に置く

2024/12/31に公開

はじめに

既に手動で作成されているリソースをTerraform管理下に置きたいケースはよくあると思います。

この記事では、terraform importで既存リソースをTerraform管理下に置く手順について書きます。

手動でリソースを作成する

ここでは、AzureのパブリックIPが既にリソースとして存在しており、このリソースをTerraform側に取り込むこととします。

実験のため、Azure Portalから手動でパブリックIPを作成します。

設定は、ここでは以下のようにしますが、任意です。

パブリック IP アドレスの作成
リソースグループ terraform-sample-dev-rg
リージョン Japan East
名前 pip-terraform-import-test
IPバージョン Ipv4
SKU Standard
可用性ゾーン Zone-redundant
レベル Regional
IP アドレスの割り当て 静的
ルーティングの優先順位 Microsoftネットワーク
アイドルタイムアウト 4
DDoS保護 ネットワーク(仮想ネットワークからDDoS保護を継承する)

Terraformコードを準備する

インポートをする前に、パブリックIPリソースをTerraform側に定義する必要があります。以下はそのソースコードです。ここでは、本記事に直接関係ないファイルは省略しています。

modules/public_ip/main.tf
resource "azurerm_public_ip" "main" {
  name                     = var.name                # リソース名
  resource_group_name      = var.resource_group_name # 所属するリソースグループ
  location                 = var.location            # 作成するリージョン
  allocation_method        = "Static"                # IP割り当て方法(動的/静的)
}
modules/public_ip/variables.tf
variable "name" {
  type = string
}

variable "resource_group_name" {
  type = string
}

variable "location" {
  type = string
}
main.tf
resource "azurerm_resource_group" "rg" {
  name     = var.resource_group_name
  location = var.location
}

module "public_ip" {
  source = "../../modules/public_ip"

  name                 = "pip-terraform-import-test"
  resource_group_name  = azurerm_resource_group.rg.name
  location             = var.location
}
terraform.tfvars
resource_group_name    = "terraform-sample-dev-rg"
location               = "Japan EAST"

terraform importコマンド

tfstateファイルについて理解する

tfstateファイルは、Terraformが管理対象とするリソースの現在の状態を保持するファイルのことです。Terraformでリソースを管理するためには、tfstateファイルにそのリソースの状態を反映する必要があります。当たり前ですが、現状のtfstateファイルには手動で作成したパブリックIPのリソースは存在しません。

https://zenn.dev/shimiyu/articles/b87482f56a96ed#tfstateファイルとは何か?

このtfstateファイルにリソースの状態を反映するのに必要なのがterraform importコマンドです。

terraform importコマンドの概要

terraform importコマンドにより、先ほど手動作成したパブリックIPをtfstateファイルに追加します。

# <リソースタイプ>.<リソース名>は、Terraformコードで定義するもの
# <リソースID>は、Terraformに取り込みたい既存リソースのID
terraform import <リソースタイプ>.<リソース名> <リソースID>

より具体的には、

  • 手動作成したリソースの状態
  • Terraformコードで定義したリソースブロックと既存リソースとの紐づけ

tfstateファイルに反映されます。

https://developer.hashicorp.com/terraform/cli/commands/import

terraform importを実行する

今回は、module内に定義したパブリックIPリソースを取り込みたいので、下記のようなimportコマンドになります。注意点としては、module内にパブリックIPのresourceブロックを定義しているので、module.public_ip.のようにモジュール部分から指定する必要があります。

手動作成したリソースIDは、Azure Portalの各リソースページから確認してください。

terraform import module.public_ip.azurerm_public_ip.main <リソースID>

では、実際にterraform importを実行します。なお、インポート前に必ずterraform initを実行しておきましょう。

terraform init

terraform import module.public_ip.azurerm_public_ip.main /subscriptions/****/resourceGroups/terraform-sample-dev-rg/providers/Microsoft.Network/publicIPAddresses/pip-terraform-import-test

module.public_ip.azurerm_public_ip.main: Importing from ID "/subscriptions/****/resourceGroups/terraform-sample-dev-rg/providers/Microsoft.Network/publicIPAddresses/pip-terraform-import-test"...
module.public_ip.azurerm_public_ip.main: Import prepared!
  Prepared azurerm_public_ip for import
module.public_ip.azurerm_public_ip.main: Refreshing state... [id=/subscriptions/****/resourceGroups/terraform-sample-dev-rg/providers/Microsoft.Network/publicIPAddresses/pip-terraform-import-test]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

インポートが成功しました。

tfstateファイルを見てみると、先ほどは存在しなかったパブリックIPの定義が追加されていることが分かります。また、このリソースが属するmoduleもしっかり定義されています。

{
    "module": "module.public_ip",
    "mode": "managed",
    "type": "azurerm_public_ip",
    "name": "main",
    "provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]",
    "instances": [
        {
            "schema_version": 0,
            "attributes": {
                "allocation_method": "Static",
                "ddos_protection_mode": "VirtualNetworkInherited",
                "ddos_protection_plan_id": null,
                "domain_name_label": null,
                "edge_zone": "",
                "fqdn": null,
                "id": "/subscriptions/*****/resourceGroups/terraform-sample-dev-rg/providers/Microsoft.Network/publicIPAddresses/pip-terraform-import-test",
                "idle_timeout_in_minutes": 4,
                // ...以下略

インポート後の確認と差分の解消

terraform importによりtfstateファイルに追加されるのは既に存在しているリソースの状態であり、Terraformで定義したコードの内容ではありません。そのため、両者に違いがある場合はterraform plan実行時に差分が出ます。planコマンドで実行計画を確認し、インポートしたリソースとTerraformのコードの定義が一致していることを確かめてみることにします。

terraform planを実行すると、手動作成したパブリックIPリソースが削除され、新しいリソースが作成されるようになってしまいました。

terraform plan
# ... 略
Terraform will perform the following actions:

  # module.public_ip.azurerm_public_ip.main must be replaced
-/+ resource "azurerm_public_ip" "main" {
      + fqdn                    = (known after apply)
      ~ id                      = "/subscriptions/****/resourceGroups/terraform-sample-dev-rg/providers/Microsoft.Network/publicIPAddresses/pip-terraform-import-test" -> (known after apply)
      ~ ip_address              = "74.***.***.56" -> (known after apply)
      - ip_tags                 = {} -> null
        name                    = "pip-terraform-import-test"
      - tags                    = {} -> null
      - zones                   = [ # forces replacement
          - "1",
          - "2",
          - "3",
        ] -> null
        # (9 unchanged attributes hidden)
    }

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

zonesforces replacementとあるので、zonesが原因でリソースが作り直されてしまうようです。今回は既存リソースの内容を変えずにTerraform側に取り込みたいので、これは意図しない動作です。そのため、Terraformコード側を修正します。

modules/public_ip/main.tf
resource "azurerm_public_ip" "main" {
  name                     = var.name
  resource_group_name      = var.resource_group_name
  location                 = var.location
  allocation_method        = "Static"

+ zones                    = ["1", "2", "3"]
}

再度terraform planを実行します。すると、今度は実際のリソースとTerraformコードに差分がないことが確認できました。

terraform plan

# ...略
No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
Releasing state lock. This may take a few moments...

既存リソースの内容をそのままTerraform管理下に置く想定であれば、差分がなくなるまでTerraform側のコードを修正します。Terraform側のコードの内容を正としたい場合はapplyしてその内容をリソース側に反映すれば良いでしょう。

これで、手動作成したリソースをterraform管理下に取り込むことに成功しました。

最後に

以上です。

今回はシンプルに単体のリソースを取り込むやり方について書きました。

importコマンドで1つ1つ取り込んでいくのは大変なので、取り込みたいリソースが沢山ある場合は、別の手段を検討した方が良さそうです。

Discussion