Terraform の moved ブロック、removed ブロック、import ブロックを活用しよう
こんにちは、クラウドエース第三開発部の渡辺です。これまで、Terraform の State 操作は主にコマンドで実行していました。そこで、コード内で State 操作を行う方法を知り、その詳細と使用例を調査しました。
本記事では、Terraform の State 操作における moved
、import
、removed
ブロックを解説します。また、Google Cloud 環境でのハンズオン例を紹介します。
対象読者
- Terraform 初心者: Terraform の基本を学び、State 管理・操作の基礎を理解したい方
- コマンドで State 操作経験のある方: 既に Terraform を使用し、State 操作をコマンドで行っているが、コード内での操作方法に興味がある方
Terraform の State について
Terraform は、インフラストラクチャをコードとして管理する際に、State ファイルを使用します。State ファイルは、現在のインフラの状態を追跡します。また、管理する各リソースのメタデータや属性を保持します。これにより、コードと実際のインフラの整合性が保たれます。
State ファイルの重要性
-
リソースの追跡
State ファイルを用いて、Terraform は現在のインフラと構成ファイルの差分から必要な変更を確認します。これにより、既存リソースや新規作成・更新対象を正確に判断できます。 -
並行管理の防止
Terraform のロック機能(特にリモート バックエンドを使用時)により、複数のユーザーが同じ State ファイルを同時に操作することで競合が発生するのを防ぎます。これにより、予期せぬ上書きや不整合を避けることができます。 -
依存関係の管理
リソース間の依存関係を正確に把握し、適切な順序で操作を行います。これにより、予期せぬ上書きや不整合を避けられます。
詳細は、以下の公式ドキュメントをご覧ください。
State 管理の方法
State 管理には、主に 2 つの方法があります。
-
コマンドベースの管理
コマンドラインで State ファイルを操作ができます。例えば、既存リソースを Terraform の管理下に取り込む際にはterraform import
コマンドを使用します。 -
コード内での管理
Terraform のアップデートにより、State 管理を Terraform のコード内で定義できるようになりました。具体的には、moved
ブロック、import
ブロック、removed
ブロック、を使用します。これにより、リソースの移動や削除、インポートをコードベースで操作できます。
本記事ではコード内での管理に焦点を当てます。コマンドベースの管理は本記事では扱いません。
リモート バックエンドと State 管理
State ファイルはローカルに保存も可能ですが、チームでの共同作業やセキュリティを考慮し、リモートバックエンドの利用が推奨されます。リモートバックエンドを利用することで、State ファイルの共有やセキュリティの強化が可能です。以下は、Google Cloud Storage をリモートバックエンドとして使用する例です。
terraform {
backend "gcs" {
bucket = "my-terraform-state-bucket"
prefix = "terraform/state"
project = "my-googlecloud-project"
}
}
詳細は、以下の公式ドキュメントをご覧ください。
State ロック
Terraform では、State ロック機能を利用して State ファイルへの同時アクセスを制御します。これにより、状態の一貫性を保護します。ロック機能はバックエンドがサポートしている場合に自動的に適用され、以下の利点があります。
-
競合の防止
State ロックにより、複数のユーザーやプロセスが同時に State ファイルを変更することを防ぎます。これにより、状態ファイルの破損や不整合を防止し、安全にインフラを管理できます。 -
自動ロック管理
状態を変更する全ての操作(例:terraform apply
、terraform plan
)において、Terraform は自動的に State ロックを取得します。ロック取得中は他の操作がブロックされ、同時実行による問題を回避できます。 -
ロック失敗時の安全性
ロック取得に失敗した場合、Terraform は操作を続行せずエラーを返します。これにより、意図しない変更や上書きを防ぎます。ただし、-lock
フラグを使用してロックを無効化することも可能ですが、推奨されません。ロックを無効にすると、競合による問題が発生するリスクが高まります。
詳細は、以下の公式ドキュメントをご覧ください。
各ブロックの概要
この章では、moved
、import
、removed
ブロックの概要と使い方を説明します。これらのブロックを使用すると、コード内で State 操作が可能になります。
moved ブロックについて
moved
ブロックは、リソースのアドレスを変更する際に使用します。リソースの再作成を避け、旧リソースを新アドレスに移行できます。これは、terraform state mv
に相当し、Terraform v1.1 以降で使用可能です。以下は、公式ドキュメントです。
moved ブロックの使い方
moved
ブロックは、Terraform の設定ファイル内で以下のように定義します。from
と to
の 2 つの引数を使用して、リソースの旧アドレスから新アドレスへの移動を指定します。
moved {
from = <旧リソースアドレス>
to = <新リソースアドレス>
}
-
from
: 移動前のリソースアドレスを指定 -
to
: 移動後のリソースアドレスを指定
import ブロックについて
import
ブロックは、Terraform 管理外のリソースを State ファイルへ取り込み、以後 Terraform で管理可能にします。 これは、terraform import
に相当し、Terraform v1.5 以降で使用可能です。以下は、公式ドキュメントです。
import ブロックの使い方
import
ブロックは、Terraform の設定ファイル内で以下のように定義します。to
引数でインポート先のリソースアドレスを指定し、id
引数でインポートするリソースの固有 ID を指定します。
import {
to = <インポート先のリソースアドレス>
id = <インポートするリソースID>
}
-
to
: インポート先のリソースアドレスを指定 -
id
: インポートするリソース固有の ID を指定
id
のフォーマットは各リソースの Terraform の公式ドキュメントを参照してください。
以下は Google Compute Engine インスタンスの import の Terraform 公式ドキュメントです。
removed ブロックについて
removed
ブロックは、リソースを Terraform の管理から外す際に使用します。リソース本体を削除せず、State からのみ削除できます。これはterraform state rm
に相当し、Terraform v1.7 以降で使用可能です。以下は、公式ドキュメントです。
removed ブロックの使い方
removed
ブロックは、Terraform の設定ファイル内で以下のように定義します。
from
引数で管理から外すリソースアドレスを指定し、lifecycle
ブロック内の destroy
引数でリソースの削除有無を設定します。
removed {
from = <管理から外すリソースアドレス>
lifecycle {
destroy = false or true
}
}
-
from
:Terraform の State から削除するリソースアドレスを指定 -
lifecycle
:destroy
引数を使用して、リソース自体を削除するかどうかを設定-
destroy = false
: リソースを Terraform の管理下から外しますが、インフラには影響を与えない -
destroy = true
: リソースを Terraform の管理下から外し、同時にインフラからも削除
-
lifecycle
ブロックは必須であり、destroy
引数を指定する必要があります。
各ブロック使用例と動作確認
この章では、moved
、import
、removed
ブロック使用例と動作確認を記載します。
動作確認には、terraform plan
および terraform apply
結果を例示します。
moved ブロックの使用例
以下は、Terraform で作成した Google Compute Engine インスタンスのアドレスを新しいアドレスに変更する例です。
変更前のリソース定義
resource "google_compute_instance" "old_instance" {
name = "moved-test-instance"
machine_type = "e2-micro"
zone = "asia-northeast1-a"
boot_disk {
initialize_params {
image = "ubuntu-os-cloud/ubuntu-2004-lts"
}
}
network_interface {
network = "default"
}
}
> terraform state list
google_compute_instance.old_instance
>
変更後のリソース定義と moved ブロックの追加
resource "google_compute_instance" "new_instance" {
name = "moved-test-instance"
machine_type = "e2-micro"
zone = "asia-northeast1-a"
boot_disk {
initialize_params {
image = "ubuntu-os-cloud/ubuntu-2004-lts"
}
}
network_interface {
network = "default"
}
}
moved {
from = "google_compute_instance.old_instance"
to = "google_compute_instance.new_instance"
}
moved ブロックの動作確認
moved
ブロックを使用した時の動作確認を行います。
以下は、terraform plan
および terraform apply
の実行結果です。
Plan の実行
❯ terraform plan
google_compute_instance.new_instance: Refreshing state... [id=projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME]
Terraform will perform the following actions:
# google_compute_instance.old_instance has moved to google_compute_instance.new_instance
resource "google_compute_instance" "new_instance" {
id = "projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME"
name = "moved-test-instance"
tags = []
# (22 unchanged attributes hidden)
# (4 unchanged blocks hidden)
}
Plan: 0 to add, 0 to change, 0 to destroy.
>
Apply の実行
> terraform apply
google_compute_instance.new_instance: Refreshing state... [id=projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME]
Terraform will perform the following actions:
# google_compute_instance.old_instance has moved to google_compute_instance.new_instance
resource "google_compute_instance" "new_instance" {
id = "projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME"
name = "moved-test-instance"
tags = []
# (22 unchanged attributes hidden)
# (4 unchanged blocks hidden)
}
Plan: 0 to add, 0 to change, 0 to destroy.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
>
> terraform state list
google_compute_instance.new_instance
>
この設定により、Terraform は既存の google_compute_instance.old_instance
アドレスを google_compute_instance.new_instance
として扱うようになります。これにより、リソースの再作成やダウンタイムを避けつつ、アドレスの変更が可能となります。
import ブロックの使用例
import
ブロックの使用例を紹介します。
以下は、コンソールで作成された Google Compute Engine インスタンスを Terraform の管理下に取り込む例です。
コンソールで作成されたリソース
> terraform state list
# 何も表示されず Terraform の管理下にないことを確認
>
変更後のリソース定義と import ブロックの追加
resource "google_compute_instance" "import_instance" {
name = "import-test-instance"
machine_type = "e2-micro"
zone = "asia-northeast1-a"
boot_disk {
initialize_params {
image = "ubuntu-os-cloud/ubuntu-2004-lts"
}
}
network_interface {
network = "default"
}
}
import {
to = "google_compute_instance.import_instance"
id = "projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME"
}
import ブロックの動作確認
import
ブロックを使用した時の動作確認を行います。
以下は、terraform plan
および terraform apply
の実行結果です。
Plan の実行
> terraform plan
google_compute_instance.import_instance: Preparing import... [id=projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME]
google_compute_instance.import_instance: Refreshing state... [id=projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# google_compute_instance.import_instance will be updated in-place
~ resource "google_compute_instance" "import_instance" {
id = "projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME"
name = "import-test-instance"
machine_type = "e2-micro"
zone = "ZONE"
network_interface {
network = "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/default"
subnetwork = "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/regions/REGION/subnetworks/default"
network_ip = "10.146.0.17"
- access_config {
- nat_ip = "PUBLIC_IP_ADDRESS" -> null
- network_tier = "PREMIUM" -> null
}
}
# 他の変更点は省略されています
}
Plan: 1 to import, 0 to add, 1 to change, 0 to destroy.
>
Apply の実行
> terraform apply
google_compute_instance.import_instance: Preparing import... [id=projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME]
google_compute_instance.import_instance: Refreshing state... [id=projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# google_compute_instance.import_instance will be updated in-place
~ resource "google_compute_instance" "import_instance" {
id = "projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME"
name = "import-test-instance"
machine_type = "e2-micro"
zone = "ZONE"
boot_disk {
device_name = "import-test-instance"
source = "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/disks/import-test-instance"
initialize_params {
image = "https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-2004-focal-v20241016"
size = 10
type = "pd-balanced"
}
}
network_interface {
network = "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/default"
subnetwork = "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/regions/REGION/subnetworks/default"
network_ip = "10.146.0.17"
- access_config {
- nat_ip = "PUBLIC_IP_ADDRESS" -> null
- network_tier = "PREMIUM" -> null
}
}
# 他の変更点は省略されています
}
Plan: 1 to import, 0 to add, 1 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
google_compute_instance.import_instance: Importing... [id=projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME]
google_compute_instance.import_instance: Import complete [id=projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME]
google_compute_instance.import_instance: Modifying... [id=projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME]
google_compute_instance.import_instance: Modifications complete after 2s [id=projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME]
Apply complete! Resources: 1 imported, 0 added, 1 changed, 0 destroyed.
>
> terraform state list
google_compute_instance.import_instance
>
この設定により、Terraform の管理下になかった Google Compute Engine インスタンスをインポートし、Terraform の管理下に取り込みました。以降、この手動で作成されたリソースを Terraform で管理することが可能となります。
removed ブロックの使用例
removed
ブロックの使用例を紹介します。
以下は、Terraform の管理下にある Google Compute Engine インスタンスのリソースを削除せずに Terraform の管理下からのみ削除する例です。
Terraform 管理下にあるリソース
resource "google_compute_instance" "removed_instance" {
name = "removed-test-instance"
machine_type = "e2-micro"
zone = "asia-northeast1-a"
boot_disk {
initialize_params {
image = "ubuntu-os-cloud/ubuntu-2004-lts"
}
}
network_interface {
network = "default"
}
}
> terraform state list
google_compute_instance.removed_instance
>
Terraform 管理下から削除するため removed ブロックの追加
removed {
from = "google_compute_instance.removed_instance"
lifecycle {
destroy = false
}
}
removed ブロックの動作確認
removed
ブロックを使用した時の動作確認を行います。
以下は、terraform plan
および terraform apply
の実行結果です。
Plan の実行
> 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:
# google_compute_instance.removed_instance will no longer be managed by Terraform, but will not be destroyed
. resource "google_compute_instance" "removed_instance" {
id = "projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME"
name = "removed-test-instance"
# (19 unchanged attributes hidden)
# (4 unchanged blocks hidden)
}
Plan: 0 to add, 0 to change, 0 to destroy.
╷
│ Warning: Some objects will no longer be managed by Terraform
│
│ If you apply this plan, Terraform will discard its tracking information for the following objects, but it will not delete them:
│ - google_compute_instance.removed_instance
│
│ After applying this plan, Terraform will no longer manage these objects. You will need to import them into Terraform to manage them again.
╵
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
>
Apply の実行
> terraform apply
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:
# google_compute_instance.removed_instance will no longer be managed by Terraform, but will not be destroyed
. resource "google_compute_instance" "removed_instance" {
id = "projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME"
name = "removed-test-instance"
# (19 unchanged attributes hidden)
# (4 unchanged blocks hidden)
}
Plan: 0 to add, 0 to change, 0 to destroy.
╷
│ Warning: Some objects will no longer be managed by Terraform
│
│ If you apply this plan, Terraform will discard its tracking information for the following objects, but it will not delete them:
│ - google_compute_instance.removed_instance
│
│ After applying this plan, Terraform will no longer manage these objects. You will need to import them into Terraform to manage them again.
╵
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
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
>
> terraform state list
# 何も表示されず Terraform の管理下にないことを確認
>
Terraform の管理下からのみ削除されたことを確認
この設定により、Terraform はリソースを削除せずに、Terraform の State ファイルからリソースを削除することができました。
まとめ
本記事では、Terraform の moved
、import
、removed
ブロックを解説し、それぞれの使用方法や具体的な例を紹介しました。これらを使うと、コード内で柔軟に State 操作が可能となります。特に、コード内での State 管理を行うことで、インフラストラクチャの変更履歴を明確にし、チームでの共同作業を進めることが可能となります。ぜひ、これらの機能を活用して、Terraform によるインフラ管理をより柔軟的に行ってください。
Discussion