本記事は学習の備忘録として執筆したものです。内容に誤りが含まれている可能性があります。お気づきの点があればコメントにてご指摘いただけると幸いです。
はじめに
AWSでの構築において、IaCで作成したリソースとコンソールで手動作成したリソースが混在すると、管理が煩雑になります。
今回はそのような状況を解消するための terraform import について学んだので、備忘録として記録します。
もともと、以下の懸念があり terraform import の使用をためらっていました。
VPCだけインポートしたら、他のTerraformで管理していない他のリソースに影響が出ないか?
この記事では実際にその懸念を検証しながら、2種類のimport方法を試して比較します。
| 方法 | 概要 |
|---|---|
| 方法A:自動生成 |
import ブロックを書き、-generate-config-out で .tf を自動生成する |
| 方法B:手動 |
terraform import CLI で state に取り込み、terraform show を見ながら .tf を手書きする |
全体の流れ
① VPC・サブネット・NAT Gatewayを手動作成(AWSコンソール)
② VPCをインポート【方法A:自動生成】
- import ブロック + generate-config-out で vpc.tf を自動生成
- VPCのタグを変更してみる(管理下に置けていることを確認)
③ サブネットをインポート【方法B:手動】
- terraform import CLI → terraform show → .tf を手動で書く
- サブネットのタグを変更してみる(管理下に置けていることを確認)
④ 2つの手法を比較
事前準備
- Terraform v1.5以上
- AWS CLI 設定済み(
aws configure完了)
main.tf と backend.tf を用意した状態からスタートします。
# main.tf
terraform {
required_version = ">= 1.14"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
# backend.tf
terraform {
backend "local" {}
}
① VPC・サブネット・NAT Gatewayを手動作成(AWSコンソール)
AWSコンソールで以下のリソースを作成します。
- VPC(CIDR: 10.10.10.0/24)
- パブリックサブネット(CIDR: 10.10.10.0/28、AZ: ap-northeast-1a)
- NAT Gateway(パブリックサブネット内)
コンソールから「VPCなど」を選択し、VPC・サブネット・ルートテーブルを一括作成します。
作成後のプレビューは以下の通りです。
続けて、パブリックサブネット内にNAT Gatewayを作成します。
作成後のリソース一覧です。
次章では、このVPCをTerraformの管理下に置きます。
② VPCをインポート【方法A:自動生成】
Terraform 1.5以降で使用できる import ブロックと -generate-config-out オプションを使って、VPCをインポートします。
import ブロックを書く
インポート対象のリソースアドレスとAWSリソースIDを指定します。
# imports.tf
import {
to = aws_vpc.main
id = "vpc-xxxxxxxxxxxxxxxxx"
}
plan を実行し、vpc.tf を生成する
terraform init
terraform plan "-generate-config-out=vpc.tf"
このコマンドは以下の2つを同時に行います。
| 部分 | 意味 |
|---|---|
terraform plan |
imports.tf の import ブロックを読み取り、AWSから実際のリソース情報を取得する |
-generate-config-out |
generate(生成)+ config(.tfファイル)+ out(出力先)の組み合わせ |
=vpc.tf |
生成した .tf コードの出力先ファイル名(任意の名前でOK) |
実行すると、AWSコンソールで手動作成したVPCの全属性がTerraformコードとして vpc.tf に直接書き出されます。
vpc.tf を整理する
自動生成された vpc.tf には null 値や tags_all(AWSが自動付与するため不要)など、Terraformの設定として不要な属性が含まれています。これらを削除して整理します。
# vpc.tf
resource "aws_vpc" "main" {
cidr_block = "10.10.10.0/24"
assign_generated_ipv6_cidr_block = false
enable_dns_hostnames = true
enable_dns_support = true
enable_network_address_usage_metrics = false
instance_tenancy = "default"
tags = {
ManagedBy = "Manual"
Name = "For_Terraform_import_demo-vpc"
Project = "terraform-import"
}
}
terraform apply で管理下に置く
terraform apply
Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
差分ゼロでインポートが完了し、VPCがTerraformの管理下に置かれました。
imports.tf を削除する(任意)
terraform apply でインポートが完了した時点で、リソースの情報は terraform.tfstate に記録されます。以降はリソースブロック(vpc.tf)と tfstate で管理されるため、imports.tf の import ブロックは役目を終えています。
削除するかどうかは、好みだと思います。私は削除しました。
rm imports.tf
VPCのタグを変更してみる
管理下に置けているか確認するため、タグの ManagedBy を Manual から Terraform に変更してみます。
手動作成時のタグ
ManagedBy を Manual から Terraform に変更します。
# vpc.tf
tags = {
Name = "For_Terraform_import_demo-vpc"
Project = "terraform-import"
ManagedBy = "Terraform" # ← Manual から変更
}
terraform apply
apply 後のタグ
タグが正常に変更されたことを確認できました。
③ サブネットをインポート【方法B:手動】
続いて、従来の terraform import CLIコマンドを使う方法でサブネットをインポートします。
空のリソースブロックを書く
terraform import を実行するには、事前に対応するリソースブロックが .tf ファイルに存在している必要があります。値はダミーでOKです。
# vpc_subnet.tf
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "0.0.0.0/0" # ダミー(あとで実際の値に書き直す)
}
terraform import で state に取り込む
terraform import aws_subnet.public subnet-xxxxxxxxxxxxxxxxx
Import successful!
terraform show で実際の値を確認する
terraform show は tfstate の内容を人間が読みやすい HCL 形式で表示するコマンドです。tfstate は JSON 形式のため直接読むには不向きですが、このコマンドにより .tf と同じ書き方で内容を確認できます。
terraform show
出力された値をもとに vpc_subnet.tf を書き直します。
.tf を実際の値に書き換える
terraform show の出力から必要な属性のみを抽出し、不要な属性(arn、id、tags_all などの computed 属性や null 値)を除いて記述します。
# vpc_subnet.tf
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.10.10.0/28"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = false
private_dns_hostname_type_on_launch = "ip-name"
tags = {
ManagedBy = "Manual"
Name = "For_Terraform_import_demo-subnet-public1-ap-northeast-1a"
Project = "terraform-import"
}
}
terraform plan で差分ゼロを確認する
terraform plan
No changes. Your infrastructure matches the configuration.
差分がなければインポート完了です。なお、terraform show の出力をそのまま .tf に貼り付けると、computed 属性や競合する属性によってエラーが発生します。
何を残して何を削除するかの判断がなかなか難しいと感じました...
サブネットのタグを変更してみる
VPC と同様に、タグを変更してTerraformから操作できることを確認します。
手動作成時のタグ
ManagedBy を Manual から Terraform に変更します。
tags = {
ManagedBy = "Terraform" # ← Manual から変更
Name = "For_Terraform_import_demo-subnet-public1-ap-northeast-1a"
Project = "terraform-import"
}
terraform apply
apply 後のタグ
タグが正常に変更されたことを確認できました。
④ 2つの手法を比較・まとめ
| 方法A:自動生成 | 方法B:手動 | |
|---|---|---|
| 事前準備 |
imports.tf に import ブロックを書く |
空のリソースブロックを書く |
| 値の確認 |
vpc.tf に自動出力される |
terraform show で確認する |
| .tf の修正 | 不要な属性を削除するだけ | 不要な属性を削除する |
| 差分ゼロまで | ほぼそのままゼロになる | 試行錯誤が必要な場合がある |
まとめ
両方を試した感想として、どちらも結果は同じです。
方法B(手動) は terraform show の出力を見ながら必要な属性を判断して書き直す手間がかかります。
一方、方法A(自動生成) はコマンド一つで .tf ファイルが生成されるため、あとは不要な属性を削除するだけです。
作業量としては方法Aの方が少なく済みますが、大きな差ではないので、やりやすい方を選んでみると良いかと思います。







