【Terraform】terraform importで既存リソースをTerraform管理下に置く
はじめに
既に手動で作成されているリソースを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側に定義する必要があります。以下はそのソースコードです。ここでは、本記事に直接関係ないファイルは省略しています。
resource "azurerm_public_ip" "main" {
name = var.name # リソース名
resource_group_name = var.resource_group_name # 所属するリソースグループ
location = var.location # 作成するリージョン
allocation_method = "Static" # IP割り当て方法(動的/静的)
}
variable "name" {
type = string
}
variable "resource_group_name" {
type = string
}
variable "location" {
type = string
}
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
}
resource_group_name = "terraform-sample-dev-rg"
location = "Japan EAST"
terraform importコマンド
tfstateファイルについて理解する
tfstate
ファイルは、Terraformが管理対象とするリソースの現在の状態を保持するファイルのことです。Terraformでリソースを管理するためには、tfstate
ファイルにそのリソースの状態を反映する必要があります。当たり前ですが、現状のtfstate
ファイルには手動で作成したパブリックIPのリソースは存在しません。
このtfstate
ファイルにリソースの状態を反映するのに必要なのがterraform import
コマンドです。
terraform importコマンドの概要
terraform import
コマンドにより、先ほど手動作成したパブリックIPをtfstate
ファイルに追加します。
# <リソースタイプ>.<リソース名>は、Terraformコードで定義するもの
# <リソースID>は、Terraformに取り込みたい既存リソースのID
terraform import <リソースタイプ>.<リソース名> <リソースID>
より具体的には、
- 手動作成したリソースの状態
- Terraformコードで定義したリソースブロックと既存リソースとの紐づけ
がtfstate
ファイルに反映されます。
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.
zones
にforces replacement
とあるので、zones
が原因でリソースが作り直されてしまうようです。今回は既存リソースの内容を変えずにTerraform側に取り込みたいので、これは意図しない動作です。そのため、Terraformコード側を修正します。
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