やりたいこと
大量のIaC化できていないAWSリソースをサクっとTerraform化したい!
想定読者
- 大量のAWSリソースをTerraform管理したいけど、そんなに時間はかけられない
-
terraform importは知ってるけど、リソースごとに実行するのは大変だと思っている -
terraform plan -generate-config-outは知ってるけど、エラーを一つ一つ修正するのは面倒 - ゼロベースでTerraformコードを作成する必要に迫られている
という方向けの記事となっています。
想定ケース
イメージしやすくするために、例として、自分以外の人がマネコンのVPC作成ウィザードで勝手にVPCを作成してしまったケースを考えます。
(インフラエンジニアやSREの方はあるあるなシチュエーションかと思います。)
もちろんこのままだとTerraform管理にはなっていません。この手動で作られたAWSリソースをTerraform管理にするケースを考えます。
VPC作成ウィザードは便利なものでユーザーが意識しなくても勝手に以下のリソースも作ってくれます。
- VPC
- サブネット
- ルートテーブル
- インターネットゲートウェイ
- NAT ゲートウェイ

※丸数字がTerraformで記載するリソースの数です。
※ルートテーブル1つにつき本体と関連付けの2つのリソースを記載します。
これを1個1個Terraformで書くには少し骨が折れますよね。できるだけ楽をしたい。
以下に対処方法を記載します。
以下にリソースID情報が出てきますが、削除済みのリソースとなります。
AWSアカウントIDはマスク済みです。
【方法1】terraform importを使う
Terraform管理したいAWSリソース1個1個に対してterraform importを実行する方法です。
① 空のresourceブロックを記載しておく
resource "aws_vpc" "test_vpc" {
}
resource "aws_internet_gateway" "test_igw" {
}
resource "aws_subnet" "test_subnet_public1" {
}
#・・・省略・・・
② terraform importコマンド実行
$ terraform import aws_vpc.test_vpc vpc-xxxxxxxx
$ terraform import aws_internet_gateway.test_igw igw-xxxxxxxx
$ terraform import aws_subnet.test_subnet_public1 subnet-xxxxxxxx
・・・省略・・・
→stateにリソース情報が書き込まれます。
③ terraform plan → terraform applyコマンドを実行
コードと実体の差分を合わせます。
※実行例は省略します。
【方法1】は以下の点が難点となります。
- ②で全てのリソースに対してterraform importを実行しないといけない
- ③でterraform plan結果を見てコードを手動修正する必要がある
- モノによっては修正してもまたコードを手直しする必要が出てくる
- パッと見、必要な追記事項が分からない場合もある
Terraformの記載を学ぶ上で、実際にterraform plan結果を元に一つ一つコードを手直しするのは大事な経験かと思います。
今回は急いでいるケースを想定して、早くコード化する方法はないか探っていきます。
【方法2】terraform plan -generate-config-outを使う
Terraform 1.5から使える機能で、一気にコードを書いてしまうことができる方法がこの方法です。
① 事前準備としてimportブロックを記載する
# VPC
import {
to = aws_vpc.test_vpc
id = "vpc-0ebd959a9882f8225"
}
# Internet Gateway
import {
to = aws_internet_gateway.test_igw
id = "igw-0aa2f01afdb855dc8"
}
# Public Subnets
import {
to = aws_subnet.test_subnet_public1
id = "subnet-06b1ac9093e4620be"
}
#・・・省略・・・
上記のリソースIDはマネジメントコンソール、もしくはCLIの結果を元に手動で記載する必要があります。
② terraform plan -generate-config-outコマンドを実行
実行結果
$ terraform plan -generate-config-out=generated.tf
aws_subnet.test_subnet_private1: Preparing import... [id=subnet-026e0754e7d6e7271]
aws_subnet.test_subnet_private2: Preparing import... [id=subnet-062c19a03d4a7e7f0]
aws_eip.nat_eip: Preparing import... [id=eipalloc-008965dfabecad0e4]
aws_route_table_association.private2: Preparing import... [id=subnet-062c19a03d4a7e7f0/rtb-0d1d79915c9332d88]
aws_internet_gateway.test_igw: Preparing import... [id=igw-0aa2f01afdb855dc8]
aws_subnet.test_subnet_public3: Preparing import... [id=subnet-00a8e331d96155479]
aws_nat_gateway.test_nat_public1: Preparing import... [id=nat-05d2c6295dd925d43]
aws_route_table.test_rtb_private2: Preparing import... [id=rtb-0d1d79915c9332d88]
aws_subnet.test_subnet_public2: Preparing import... [id=subnet-02e8ab4c179ffbb45]
aws_route_table.test_rtb_private3: Preparing import... [id=rtb-0895530280784f046]
aws_subnet.test_subnet_public3: Refreshing state... [id=subnet-00a8e331d96155479]
aws_eip.nat_eip: Refreshing state... [id=eipalloc-008965dfabecad0e4]
aws_subnet.test_subnet_public2: Refreshing state... [id=subnet-02e8ab4c179ffbb45]
aws_nat_gateway.test_nat_public1: Refreshing state... [id=nat-05d2c6295dd925d43]
aws_subnet.test_subnet_private2: Refreshing state... [id=subnet-062c19a03d4a7e7f0]
aws_subnet.test_subnet_private1: Refreshing state... [id=subnet-026e0754e7d6e7271]
aws_internet_gateway.test_igw: Refreshing state... [id=igw-0aa2f01afdb855dc8]
aws_route_table.test_rtb_private2: Refreshing state... [id=rtb-0d1d79915c9332d88]
aws_route_table.test_rtb_private3: Refreshing state... [id=rtb-0895530280784f046]
aws_route_table_association.private2: Refreshing state... [id=rtbassoc-0d19df5d271ab6865]
aws_route_table.test_rtb_public: Preparing import... [id=rtb-07cd85c4732527a0d]
aws_route_table_association.public3: Preparing import... [id=subnet-00a8e331d96155479/rtb-07cd85c4732527a0d]
aws_subnet.test_subnet_public1: Preparing import... [id=subnet-06b1ac9093e4620be]
aws_subnet.test_subnet_private3: Preparing import... [id=subnet-03cb64ba88effa768]
aws_route_table_association.public2: Preparing import... [id=subnet-02e8ab4c179ffbb45/rtb-07cd85c4732527a0d]
aws_route_table_association.private1: Preparing import... [id=subnet-026e0754e7d6e7271/rtb-081881f72ed96e75e]
aws_vpc.test_vpc: Preparing import... [id=vpc-0ebd959a9882f8225]
aws_route_table.test_rtb_public: Refreshing state... [id=rtb-07cd85c4732527a0d]
aws_subnet.test_subnet_public1: Refreshing state... [id=subnet-06b1ac9093e4620be]
aws_subnet.test_subnet_private3: Refreshing state... [id=subnet-03cb64ba88effa768]
aws_route_table_association.private3: Preparing import... [id=subnet-03cb64ba88effa768/rtb-0895530280784f046]
aws_vpc.test_vpc: Refreshing state... [id=vpc-0ebd959a9882f8225]
aws_route_table_association.private3: Refreshing state... [id=rtbassoc-083f06cc4045f5023]
aws_route_table_association.public2: Refreshing state... [id=rtbassoc-0ca69237d0f006a74]
aws_default_security_group.default: Preparing import... [id=sg-015a2b56b75447aad]
aws_default_security_group.default: Refreshing state... [id=sg-015a2b56b75447aad]
aws_route_table_association.private1: Refreshing state... [id=rtbassoc-01182f6b4381b678b]
aws_route_table_association.public1: Preparing import... [id=subnet-06b1ac9093e4620be/rtb-07cd85c4732527a0d]
aws_route_table.test_rtb_private1: Preparing import... [id=rtb-081881f72ed96e75e]
aws_route_table.test_rtb_private1: Refreshing state... [id=rtb-081881f72ed96e75e]
aws_route_table_association.public3: Refreshing state... [id=rtbassoc-0f006aa50fdadd3d5]
aws_route_table_association.public1: Refreshing state... [id=rtbassoc-053e2bb018b011ea8]
Terraform planned the following actions, but then encountered a problem:
# aws_default_security_group.default will be imported
# (config will be generated)
resource "aws_default_security_group" "default" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:security-group/sg-015a2b56b75447aad"
description = "default VPC security group"
egress = [
{
cidr_blocks = [
"0.0.0.0/0",
]
description = null
from_port = 0
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "-1"
security_groups = []
self = false
to_port = 0
},
]
id = "sg-015a2b56b75447aad"
ingress = [
{
cidr_blocks = []
description = null
from_port = 0
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "-1"
security_groups = []
self = true
to_port = 0
},
]
name = "default"
name_prefix = null
owner_id = "xxxxxxxx"
region = "ap-northeast-1"
tags = {
"Name" = "default"
}
tags_all = {
"Name" = "default"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_eip.nat_eip will be imported
# (config will be generated)
resource "aws_eip" "nat_eip" {
allocation_id = "eipalloc-008965dfabecad0e4"
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:elastic-ip/eipalloc-008965dfabecad0e4"
association_id = "eipassoc-0a514a9800042311e"
carrier_ip = null
customer_owned_ip = null
customer_owned_ipv4_pool = null
domain = "vpc"
id = "eipalloc-008965dfabecad0e4"
instance = null
network_border_group = "ap-northeast-1"
network_interface = "eni-071d916f2cb570d29"
private_dns = "ip-10-0-4-139.ap-northeast-1.compute.internal"
private_ip = "10.0.4.139"
ptr_record = null
public_dns = "ec2-54-64-207-225.ap-northeast-1.compute.amazonaws.com"
public_ip = "54.64.207.225"
public_ipv4_pool = "amazon"
region = "ap-northeast-1"
tags = {
"Name" = "test-eip-ap-northeast-1a"
}
tags_all = {
"Name" = "test-eip-ap-northeast-1a"
}
}
# aws_internet_gateway.test_igw will be imported
# (config will be generated)
resource "aws_internet_gateway" "test_igw" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:internet-gateway/igw-0aa2f01afdb855dc8"
id = "igw-0aa2f01afdb855dc8"
owner_id = "xxxxxxxx"
region = "ap-northeast-1"
tags = {
"Name" = "test-igw"
}
tags_all = {
"Name" = "test-igw"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_route_table_association.private1 will be imported
# (config will be generated)
resource "aws_route_table_association" "private1" {
gateway_id = null
id = "rtbassoc-01182f6b4381b678b"
region = "ap-northeast-1"
route_table_id = "rtb-081881f72ed96e75e"
subnet_id = "subnet-026e0754e7d6e7271"
}
# aws_route_table_association.private2 will be imported
# (config will be generated)
resource "aws_route_table_association" "private2" {
gateway_id = null
id = "rtbassoc-0d19df5d271ab6865"
region = "ap-northeast-1"
route_table_id = "rtb-0d1d79915c9332d88"
subnet_id = "subnet-062c19a03d4a7e7f0"
}
# aws_route_table_association.private3 will be imported
# (config will be generated)
resource "aws_route_table_association" "private3" {
gateway_id = null
id = "rtbassoc-083f06cc4045f5023"
region = "ap-northeast-1"
route_table_id = "rtb-0895530280784f046"
subnet_id = "subnet-03cb64ba88effa768"
}
# aws_route_table_association.public1 will be imported
# (config will be generated)
resource "aws_route_table_association" "public1" {
gateway_id = null
id = "rtbassoc-053e2bb018b011ea8"
region = "ap-northeast-1"
route_table_id = "rtb-07cd85c4732527a0d"
subnet_id = "subnet-06b1ac9093e4620be"
}
# aws_route_table_association.public2 will be imported
# (config will be generated)
resource "aws_route_table_association" "public2" {
gateway_id = null
id = "rtbassoc-0ca69237d0f006a74"
region = "ap-northeast-1"
route_table_id = "rtb-07cd85c4732527a0d"
subnet_id = "subnet-02e8ab4c179ffbb45"
}
# aws_route_table_association.public3 will be imported
# (config will be generated)
resource "aws_route_table_association" "public3" {
gateway_id = null
id = "rtbassoc-0f006aa50fdadd3d5"
region = "ap-northeast-1"
route_table_id = "rtb-07cd85c4732527a0d"
subnet_id = "subnet-00a8e331d96155479"
}
Plan: 9 to import, 0 to add, 0 to change, 0 to destroy.
╷
│ Warning: Config generation is experimental
│
│ Generating configuration during import is currently experimental, and the
│ generated configuration format may change in future versions.
╵
╷
│ Error: "" is not a valid CIDR block: invalid CIDR address:
│
│ with aws_route_table.test_rtb_private2,
│ on generated.tf line 3:
│ (source code not available)
│
╵
╷
│ Error: Conflicting configuration arguments
│
│ with aws_subnet.test_subnet_public3,
│ on generated.tf line 2:
│ (source code not available)
│
│ "availability_zone": conflicts with availability_zone_id
╵
╷
│ Error: Conflicting configuration arguments
│
│ with aws_subnet.test_subnet_public3,
│ on generated.tf line 3:
│ (source code not available)
│
│ "availability_zone_id": conflicts with availability_zone
╵
╷
│ Error: Conflicting configuration arguments
│
│ with aws_nat_gateway.test_nat_public1,
│ on generated.tf line 7:
│ (source code not available)
│
│ "secondary_private_ip_address_count": conflicts with
│ secondary_private_ip_addresses
╵
╷
│ Error: Conflicting configuration arguments
│
│ with aws_nat_gateway.test_nat_public1,
│ on generated.tf line 8:
│ (source code not available)
│
│ "secondary_private_ip_addresses": conflicts with
│ secondary_private_ip_address_count
╵
╷
│ Error: enable_lni_at_device_index must not be zero, got 0
│
│ with aws_subnet.test_subnet_public1,
│ on generated.tf line 7:
│ (source code not available)
│
╵
╷
│ Error: enable_lni_at_device_index must not be zero, got 0
│
│ with aws_subnet.test_subnet_public3,
│ on generated.tf line 7:
│ (source code not available)
│
╵
╷
│ Error: enable_lni_at_device_index must not be zero, got 0
│
│ with aws_subnet.test_subnet_private1,
│ on generated.tf line 7:
│ (source code not available)
│
╵
╷
│ Error: Missing required argument
│
│ with aws_vpc.test_vpc,
│ on generated.tf line 10:
│ (source code not available)
│
│ "ipv6_netmask_length": all of `ipv6_ipam_pool_id,ipv6_netmask_length` must be
│ specified
╵
╷
│ Error: Missing required argument
│
│ with aws_subnet.test_subnet_public1,
│ on generated.tf line 12:
│ (source code not available)
│
│ "map_customer_owned_ip_on_launch": all of
│ `customer_owned_ipv4_pool,map_customer_owned_ip_on_launch,outpost_arn` must be
│ specified
╵
╷
│ Error: Missing required argument
│
│ with aws_subnet.test_subnet_public3,
│ on generated.tf line 12:
│ (source code not available)
│
│ "map_customer_owned_ip_on_launch": all of
│ `customer_owned_ipv4_pool,map_customer_owned_ip_on_launch,outpost_arn` must be
│ specified
╵
╷
│ Error: Missing required argument
│
│ with aws_subnet.test_subnet_private1,
│ on generated.tf line 12:
│ (source code not available)
│
│ "map_customer_owned_ip_on_launch": all of
│ `customer_owned_ipv4_pool,map_customer_owned_ip_on_launch,outpost_arn` must be
│ specified
╵
$
実行すると、以下のようなgenerated.tfがカレントディレクトリに作成されます。
# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.
# __generated__ by Terraform from "igw-0aa2f01afdb855dc8"
resource "aws_internet_gateway" "test_igw" {
region = "ap-northeast-1"
tags = {
Name = "test-igw"
}
tags_all = {
Name = "test-igw"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# __generated__ by Terraform
resource "aws_nat_gateway" "test_nat_public1" {
allocation_id = "eipalloc-008965dfabecad0e4"
availability_mode = "zonal"
connectivity_type = "public"
private_ip = "10.0.4.139"
region = "ap-northeast-1"
secondary_allocation_ids = []
secondary_private_ip_address_count = 0
secondary_private_ip_addresses = []
subnet_id = "subnet-06b1ac9093e4620be"
tags = {
Name = "test-nat-public1-ap-northeast-1a"
}
tags_all = {
Name = "test-nat-public1-ap-northeast-1a"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
#・・・省略・・・
このコマンドはTerraformがresourceブロックを自動で生成してくれるコマンドとなります。
実行結果を見てもらえると分かりますがそれなりの数のErrorが出力されています。
なぜこのようなErrorが出力されてしまうかというと、このコマンドはTerraformの「両方同時に設定できないパラメータ」やIPv6などの「不必要なデフォルト値設定や空の設定」までお構いなしに自動生成してしまうからとなります。
terraform plan/applyを実行してコード差分を無くして、stateファイルにリソース情報を入れるためには、Errorがでないようにresourceブロックを手動修正する必要があります。
③ 手動修正した後terraform plan → terraform applyでリソース情報をstateファイルに取り込む
terraform apply 実行結果
$ terraform apply
aws_vpc.test_vpc: Preparing import... [id=vpc-0ebd959a9882f8225]
aws_vpc.test_vpc: Refreshing state... [id=vpc-0ebd959a9882f8225]
aws_subnet.test_subnet_public1: Preparing import... [id=subnet-06b1ac9093e4620be]
aws_subnet.test_subnet_public3: Preparing import... [id=subnet-00a8e331d96155479]
aws_subnet.test_subnet_private2: Preparing import... [id=subnet-062c19a03d4a7e7f0]
aws_default_security_group.default: Preparing import... [id=sg-015a2b56b75447aad]
aws_subnet.test_subnet_private3: Preparing import... [id=subnet-03cb64ba88effa768]
aws_subnet.test_subnet_public2: Preparing import... [id=subnet-02e8ab4c179ffbb45]
aws_internet_gateway.test_igw: Preparing import... [id=igw-0aa2f01afdb855dc8]
aws_subnet.test_subnet_private1: Preparing import... [id=subnet-026e0754e7d6e7271]
aws_subnet.test_subnet_private2: Refreshing state... [id=subnet-062c19a03d4a7e7f0]
aws_subnet.test_subnet_private1: Refreshing state... [id=subnet-026e0754e7d6e7271]
aws_internet_gateway.test_igw: Refreshing state... [id=igw-0aa2f01afdb855dc8]
aws_subnet.test_subnet_public2: Refreshing state... [id=subnet-02e8ab4c179ffbb45]
aws_subnet.test_subnet_public3: Refreshing state... [id=subnet-00a8e331d96155479]
aws_subnet.test_subnet_public1: Refreshing state... [id=subnet-06b1ac9093e4620be]
aws_default_security_group.default: Refreshing state... [id=sg-015a2b56b75447aad]
aws_subnet.test_subnet_private3: Refreshing state... [id=subnet-03cb64ba88effa768]
aws_eip.nat_eip: Preparing import... [id=eipalloc-008965dfabecad0e4]
aws_route_table.test_rtb_public: Preparing import... [id=rtb-07cd85c4732527a0d]
aws_eip.nat_eip: Refreshing state... [id=eipalloc-008965dfabecad0e4]
aws_route_table.test_rtb_public: Refreshing state... [id=rtb-07cd85c4732527a0d]
aws_route_table_association.public2: Preparing import... [id=subnet-02e8ab4c179ffbb45/rtb-07cd85c4732527a0d]
aws_route_table_association.public1: Preparing import... [id=subnet-06b1ac9093e4620be/rtb-07cd85c4732527a0d]
aws_route_table_association.public3: Preparing import... [id=subnet-00a8e331d96155479/rtb-07cd85c4732527a0d]
aws_nat_gateway.test_nat_public1: Preparing import... [id=nat-05d2c6295dd925d43]
aws_route_table_association.public2: Refreshing state... [id=rtbassoc-0ca69237d0f006a74]
aws_nat_gateway.test_nat_public1: Refreshing state... [id=nat-05d2c6295dd925d43]
aws_route_table_association.public1: Refreshing state... [id=rtbassoc-053e2bb018b011ea8]
aws_route_table_association.public3: Refreshing state... [id=rtbassoc-0f006aa50fdadd3d5]
aws_route_table.test_rtb_private1: Preparing import... [id=rtb-081881f72ed96e75e]
aws_route_table.test_rtb_private3: Preparing import... [id=rtb-0895530280784f046]
aws_route_table.test_rtb_private2: Preparing import... [id=rtb-0d1d79915c9332d88]
aws_route_table.test_rtb_private1: Refreshing state... [id=rtb-081881f72ed96e75e]
aws_route_table.test_rtb_private3: Refreshing state... [id=rtb-0895530280784f046]
aws_route_table.test_rtb_private2: Refreshing state... [id=rtb-0d1d79915c9332d88]
aws_route_table_association.private3: Preparing import... [id=subnet-03cb64ba88effa768/rtb-0895530280784f046]
aws_route_table_association.private1: Preparing import... [id=subnet-026e0754e7d6e7271/rtb-081881f72ed96e75e]
aws_route_table_association.private2: Preparing import... [id=subnet-062c19a03d4a7e7f0/rtb-0d1d79915c9332d88]
aws_route_table_association.private2: Refreshing state... [id=rtbassoc-0d19df5d271ab6865]
aws_route_table_association.private3: Refreshing state... [id=rtbassoc-083f06cc4045f5023]
aws_route_table_association.private1: Refreshing state... [id=rtbassoc-01182f6b4381b678b]
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:
# aws_default_security_group.default will be updated in-place
# (imported from "sg-015a2b56b75447aad")
~ resource "aws_default_security_group" "default" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:security-group/sg-015a2b56b75447aad"
description = "default VPC security group"
egress = [
{
cidr_blocks = [
"0.0.0.0/0",
]
description = null
from_port = 0
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "-1"
security_groups = []
self = false
to_port = 0
},
]
id = "sg-015a2b56b75447aad"
ingress = [
{
cidr_blocks = []
description = null
from_port = 0
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "-1"
security_groups = []
self = true
to_port = 0
},
]
name = "default"
name_prefix = null
owner_id = "xxxxxxxx"
region = "ap-northeast-1"
+ revoke_rules_on_delete = false
~ tags = {
+ "Name" = "default"
}
~ tags_all = {
+ "Name" = "default"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_eip.nat_eip will be imported
resource "aws_eip" "nat_eip" {
allocation_id = "eipalloc-008965dfabecad0e4"
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:elastic-ip/eipalloc-008965dfabecad0e4"
association_id = "eipassoc-0a514a9800042311e"
carrier_ip = null
customer_owned_ip = null
customer_owned_ipv4_pool = null
domain = "vpc"
id = "eipalloc-008965dfabecad0e4"
instance = null
network_border_group = "ap-northeast-1"
network_interface = "eni-071d916f2cb570d29"
private_dns = "ip-10-0-4-139.ap-northeast-1.compute.internal"
private_ip = "10.0.4.139"
ptr_record = null
public_dns = "ec2-54-64-207-225.ap-northeast-1.compute.amazonaws.com"
public_ip = "54.64.207.225"
public_ipv4_pool = "amazon"
region = "ap-northeast-1"
tags = {
"Name" = "test-eip-ap-northeast-1a"
}
tags_all = {
"Name" = "test-eip-ap-northeast-1a"
}
}
# aws_internet_gateway.test_igw will be imported
resource "aws_internet_gateway" "test_igw" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:internet-gateway/igw-0aa2f01afdb855dc8"
id = "igw-0aa2f01afdb855dc8"
owner_id = "xxxxxxxx"
region = "ap-northeast-1"
tags = {
"Name" = "test-igw"
}
tags_all = {
"Name" = "test-igw"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_nat_gateway.test_nat_public1 will be updated in-place
# (imported from "nat-05d2c6295dd925d43")
~ resource "aws_nat_gateway" "test_nat_public1" {
allocation_id = "eipalloc-008965dfabecad0e4"
association_id = "eipassoc-0a514a9800042311e"
availability_mode = "zonal"
connectivity_type = "public"
id = "nat-05d2c6295dd925d43"
network_interface_id = "eni-071d916f2cb570d29"
private_ip = "10.0.4.139"
public_ip = "54.64.207.225"
region = "ap-northeast-1"
+ regional_nat_gateway_address = (known after apply)
secondary_allocation_ids = []
secondary_private_ip_address_count = 0
secondary_private_ip_addresses = []
subnet_id = "subnet-06b1ac9093e4620be"
tags = {
"Name" = "test-nat-public1-ap-northeast-1a"
}
tags_all = {
"Name" = "test-nat-public1-ap-northeast-1a"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_route_table.test_rtb_private1 will be imported
resource "aws_route_table" "test_rtb_private1" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:route-table/rtb-081881f72ed96e75e"
id = "rtb-081881f72ed96e75e"
owner_id = "xxxxxxxx"
propagating_vgws = []
region = "ap-northeast-1"
route = [
{
carrier_gateway_id = null
cidr_block = "0.0.0.0/0"
core_network_arn = null
destination_prefix_list_id = null
egress_only_gateway_id = null
gateway_id = null
ipv6_cidr_block = null
local_gateway_id = null
nat_gateway_id = "nat-05d2c6295dd925d43"
network_interface_id = null
transit_gateway_id = null
vpc_endpoint_id = null
vpc_peering_connection_id = null
},
]
tags = {
"Name" = "test-rtb-private1-ap-northeast-1a"
}
tags_all = {
"Name" = "test-rtb-private1-ap-northeast-1a"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_route_table.test_rtb_private2 will be imported
resource "aws_route_table" "test_rtb_private2" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:route-table/rtb-0d1d79915c9332d88"
id = "rtb-0d1d79915c9332d88"
owner_id = "xxxxxxxx"
propagating_vgws = []
region = "ap-northeast-1"
route = [
{
carrier_gateway_id = null
cidr_block = "0.0.0.0/0"
core_network_arn = null
destination_prefix_list_id = null
egress_only_gateway_id = null
gateway_id = null
ipv6_cidr_block = null
local_gateway_id = null
nat_gateway_id = "nat-05d2c6295dd925d43"
network_interface_id = null
transit_gateway_id = null
vpc_endpoint_id = null
vpc_peering_connection_id = null
},
]
tags = {
"Name" = "test-rtb-private2-ap-northeast-1c"
}
tags_all = {
"Name" = "test-rtb-private2-ap-northeast-1c"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_route_table.test_rtb_private3 will be imported
resource "aws_route_table" "test_rtb_private3" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:route-table/rtb-0895530280784f046"
id = "rtb-0895530280784f046"
owner_id = "xxxxxxxx"
propagating_vgws = []
region = "ap-northeast-1"
route = [
{
carrier_gateway_id = null
cidr_block = "0.0.0.0/0"
core_network_arn = null
destination_prefix_list_id = null
egress_only_gateway_id = null
gateway_id = null
ipv6_cidr_block = null
local_gateway_id = null
nat_gateway_id = "nat-05d2c6295dd925d43"
network_interface_id = null
transit_gateway_id = null
vpc_endpoint_id = null
vpc_peering_connection_id = null
},
]
tags = {
"Name" = "test-rtb-private3-ap-northeast-1d"
}
tags_all = {
"Name" = "test-rtb-private3-ap-northeast-1d"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_route_table.test_rtb_public will be imported
resource "aws_route_table" "test_rtb_public" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:route-table/rtb-07cd85c4732527a0d"
id = "rtb-07cd85c4732527a0d"
owner_id = "xxxxxxxx"
propagating_vgws = []
region = "ap-northeast-1"
route = [
{
carrier_gateway_id = null
cidr_block = "0.0.0.0/0"
core_network_arn = null
destination_prefix_list_id = null
egress_only_gateway_id = null
gateway_id = "igw-0aa2f01afdb855dc8"
ipv6_cidr_block = null
local_gateway_id = null
nat_gateway_id = null
network_interface_id = null
transit_gateway_id = null
vpc_endpoint_id = null
vpc_peering_connection_id = null
},
]
tags = {
"Name" = "test-rtb-public"
}
tags_all = {
"Name" = "test-rtb-public"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_route_table_association.private1 will be imported
resource "aws_route_table_association" "private1" {
gateway_id = null
id = "rtbassoc-01182f6b4381b678b"
region = "ap-northeast-1"
route_table_id = "rtb-081881f72ed96e75e"
subnet_id = "subnet-026e0754e7d6e7271"
}
# aws_route_table_association.private2 will be imported
resource "aws_route_table_association" "private2" {
gateway_id = null
id = "rtbassoc-0d19df5d271ab6865"
region = "ap-northeast-1"
route_table_id = "rtb-0d1d79915c9332d88"
subnet_id = "subnet-062c19a03d4a7e7f0"
}
# aws_route_table_association.private3 will be imported
resource "aws_route_table_association" "private3" {
gateway_id = null
id = "rtbassoc-083f06cc4045f5023"
region = "ap-northeast-1"
route_table_id = "rtb-0895530280784f046"
subnet_id = "subnet-03cb64ba88effa768"
}
# aws_route_table_association.public1 will be imported
resource "aws_route_table_association" "public1" {
gateway_id = null
id = "rtbassoc-053e2bb018b011ea8"
region = "ap-northeast-1"
route_table_id = "rtb-07cd85c4732527a0d"
subnet_id = "subnet-06b1ac9093e4620be"
}
# aws_route_table_association.public2 will be imported
resource "aws_route_table_association" "public2" {
gateway_id = null
id = "rtbassoc-0ca69237d0f006a74"
region = "ap-northeast-1"
route_table_id = "rtb-07cd85c4732527a0d"
subnet_id = "subnet-02e8ab4c179ffbb45"
}
# aws_route_table_association.public3 will be imported
resource "aws_route_table_association" "public3" {
gateway_id = null
id = "rtbassoc-0f006aa50fdadd3d5"
region = "ap-northeast-1"
route_table_id = "rtb-07cd85c4732527a0d"
subnet_id = "subnet-00a8e331d96155479"
}
# aws_subnet.test_subnet_private1 will be imported
resource "aws_subnet" "test_subnet_private1" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:subnet/subnet-026e0754e7d6e7271"
assign_ipv6_address_on_creation = false
availability_zone = "ap-northeast-1a"
availability_zone_id = "apne1-az4"
cidr_block = "10.0.128.0/20"
customer_owned_ipv4_pool = null
enable_dns64 = false
enable_lni_at_device_index = 0
enable_resource_name_dns_a_record_on_launch = false
enable_resource_name_dns_aaaa_record_on_launch = false
id = "subnet-026e0754e7d6e7271"
ipv6_cidr_block = null
ipv6_cidr_block_association_id = null
ipv6_native = false
map_customer_owned_ip_on_launch = false
map_public_ip_on_launch = false
outpost_arn = null
owner_id = "xxxxxxxx"
private_dns_hostname_type_on_launch = "ip-name"
region = "ap-northeast-1"
tags = {
"Name" = "test-subnet-private1-ap-northeast-1a"
}
tags_all = {
"Name" = "test-subnet-private1-ap-northeast-1a"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_subnet.test_subnet_private2 will be imported
resource "aws_subnet" "test_subnet_private2" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:subnet/subnet-062c19a03d4a7e7f0"
assign_ipv6_address_on_creation = false
availability_zone = "ap-northeast-1c"
availability_zone_id = "apne1-az1"
cidr_block = "10.0.144.0/20"
customer_owned_ipv4_pool = null
enable_dns64 = false
enable_lni_at_device_index = 0
enable_resource_name_dns_a_record_on_launch = false
enable_resource_name_dns_aaaa_record_on_launch = false
id = "subnet-062c19a03d4a7e7f0"
ipv6_cidr_block = null
ipv6_cidr_block_association_id = null
ipv6_native = false
map_customer_owned_ip_on_launch = false
map_public_ip_on_launch = false
outpost_arn = null
owner_id = "xxxxxxxx"
private_dns_hostname_type_on_launch = "ip-name"
region = "ap-northeast-1"
tags = {
"Name" = "test-subnet-private2-ap-northeast-1c"
}
tags_all = {
"Name" = "test-subnet-private2-ap-northeast-1c"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_subnet.test_subnet_private3 will be imported
resource "aws_subnet" "test_subnet_private3" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:subnet/subnet-03cb64ba88effa768"
assign_ipv6_address_on_creation = false
availability_zone = "ap-northeast-1d"
availability_zone_id = "apne1-az2"
cidr_block = "10.0.160.0/20"
customer_owned_ipv4_pool = null
enable_dns64 = false
enable_lni_at_device_index = 0
enable_resource_name_dns_a_record_on_launch = false
enable_resource_name_dns_aaaa_record_on_launch = false
id = "subnet-03cb64ba88effa768"
ipv6_cidr_block = null
ipv6_cidr_block_association_id = null
ipv6_native = false
map_customer_owned_ip_on_launch = false
map_public_ip_on_launch = false
outpost_arn = null
owner_id = "xxxxxxxx"
private_dns_hostname_type_on_launch = "ip-name"
region = "ap-northeast-1"
tags = {
"Name" = "test-subnet-private3-ap-northeast-1d"
}
tags_all = {
"Name" = "test-subnet-private3-ap-northeast-1d"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_subnet.test_subnet_public1 will be updated in-place
# (imported from "subnet-06b1ac9093e4620be")
~ resource "aws_subnet" "test_subnet_public1" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:subnet/subnet-06b1ac9093e4620be"
assign_ipv6_address_on_creation = false
availability_zone = "ap-northeast-1a"
availability_zone_id = "apne1-az4"
cidr_block = "10.0.0.0/20"
customer_owned_ipv4_pool = null
enable_dns64 = false
enable_lni_at_device_index = 0
enable_resource_name_dns_a_record_on_launch = false
enable_resource_name_dns_aaaa_record_on_launch = false
id = "subnet-06b1ac9093e4620be"
ipv6_cidr_block = null
ipv6_cidr_block_association_id = null
ipv6_native = false
map_customer_owned_ip_on_launch = false
~ map_public_ip_on_launch = false -> true
outpost_arn = null
owner_id = "xxxxxxxx"
private_dns_hostname_type_on_launch = "ip-name"
region = "ap-northeast-1"
tags = {
"Name" = "test-subnet-public1-ap-northeast-1a"
}
tags_all = {
"Name" = "test-subnet-public1-ap-northeast-1a"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_subnet.test_subnet_public2 will be updated in-place
# (imported from "subnet-02e8ab4c179ffbb45")
~ resource "aws_subnet" "test_subnet_public2" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:subnet/subnet-02e8ab4c179ffbb45"
assign_ipv6_address_on_creation = false
availability_zone = "ap-northeast-1c"
availability_zone_id = "apne1-az1"
cidr_block = "10.0.16.0/20"
customer_owned_ipv4_pool = null
enable_dns64 = false
enable_lni_at_device_index = 0
enable_resource_name_dns_a_record_on_launch = false
enable_resource_name_dns_aaaa_record_on_launch = false
id = "subnet-02e8ab4c179ffbb45"
ipv6_cidr_block = null
ipv6_cidr_block_association_id = null
ipv6_native = false
map_customer_owned_ip_on_launch = false
~ map_public_ip_on_launch = false -> true
outpost_arn = null
owner_id = "xxxxxxxx"
private_dns_hostname_type_on_launch = "ip-name"
region = "ap-northeast-1"
tags = {
"Name" = "test-subnet-public2-ap-northeast-1c"
}
tags_all = {
"Name" = "test-subnet-public2-ap-northeast-1c"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_subnet.test_subnet_public3 will be updated in-place
# (imported from "subnet-00a8e331d96155479")
~ resource "aws_subnet" "test_subnet_public3" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:subnet/subnet-00a8e331d96155479"
assign_ipv6_address_on_creation = false
availability_zone = "ap-northeast-1d"
availability_zone_id = "apne1-az2"
cidr_block = "10.0.32.0/20"
customer_owned_ipv4_pool = null
enable_dns64 = false
enable_lni_at_device_index = 0
enable_resource_name_dns_a_record_on_launch = false
enable_resource_name_dns_aaaa_record_on_launch = false
id = "subnet-00a8e331d96155479"
ipv6_cidr_block = null
ipv6_cidr_block_association_id = null
ipv6_native = false
map_customer_owned_ip_on_launch = false
~ map_public_ip_on_launch = false -> true
outpost_arn = null
owner_id = "xxxxxxxx"
private_dns_hostname_type_on_launch = "ip-name"
region = "ap-northeast-1"
tags = {
"Name" = "test-subnet-public3-ap-northeast-1d"
}
tags_all = {
"Name" = "test-subnet-public3-ap-northeast-1d"
}
vpc_id = "vpc-0ebd959a9882f8225"
}
# aws_vpc.test_vpc will be imported
resource "aws_vpc" "test_vpc" {
arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxx:vpc/vpc-0ebd959a9882f8225"
assign_generated_ipv6_cidr_block = false
cidr_block = "10.0.0.0/16"
default_network_acl_id = "acl-0803a01cfceb41a0a"
default_route_table_id = "rtb-0113f7e869483a952"
default_security_group_id = "sg-015a2b56b75447aad"
dhcp_options_id = "dopt-0f3062a9caed493dc"
enable_dns_hostnames = true
enable_dns_support = true
enable_network_address_usage_metrics = false
id = "vpc-0ebd959a9882f8225"
instance_tenancy = "default"
ipv6_association_id = null
ipv6_cidr_block = null
ipv6_cidr_block_network_border_group = null
ipv6_ipam_pool_id = null
ipv6_netmask_length = 0
main_route_table_id = "rtb-0113f7e869483a952"
owner_id = "xxxxxxxx"
region = "ap-northeast-1"
tags = {
"Name" = "test-vpc"
}
tags_all = {
"Name" = "test-vpc"
}
}
Plan: 21 to import, 0 to add, 5 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
aws_vpc.test_vpc: Importing... [id=vpc-0ebd959a9882f8225]
aws_vpc.test_vpc: Import complete [id=vpc-0ebd959a9882f8225]
aws_subnet.test_subnet_public2: Importing... [id=subnet-02e8ab4c179ffbb45]
aws_subnet.test_subnet_public2: Import complete [id=subnet-02e8ab4c179ffbb45]
aws_subnet.test_subnet_public3: Importing... [id=subnet-00a8e331d96155479]
aws_subnet.test_subnet_public3: Import complete [id=subnet-00a8e331d96155479]
aws_subnet.test_subnet_private1: Importing... [id=subnet-026e0754e7d6e7271]
aws_subnet.test_subnet_private1: Import complete [id=subnet-026e0754e7d6e7271]
aws_subnet.test_subnet_public1: Importing... [id=subnet-06b1ac9093e4620be]
aws_subnet.test_subnet_public1: Import complete [id=subnet-06b1ac9093e4620be]
aws_subnet.test_subnet_private2: Importing... [id=subnet-062c19a03d4a7e7f0]
aws_subnet.test_subnet_private2: Import complete [id=subnet-062c19a03d4a7e7f0]
aws_subnet.test_subnet_private3: Importing... [id=subnet-03cb64ba88effa768]
aws_subnet.test_subnet_private3: Import complete [id=subnet-03cb64ba88effa768]
aws_default_security_group.default: Importing... [id=sg-015a2b56b75447aad]
aws_default_security_group.default: Import complete [id=sg-015a2b56b75447aad]
aws_internet_gateway.test_igw: Importing... [id=igw-0aa2f01afdb855dc8]
aws_internet_gateway.test_igw: Import complete [id=igw-0aa2f01afdb855dc8]
aws_route_table.test_rtb_public: Importing... [id=rtb-07cd85c4732527a0d]
aws_route_table.test_rtb_public: Import complete [id=rtb-07cd85c4732527a0d]
aws_eip.nat_eip: Importing... [id=eipalloc-008965dfabecad0e4]
aws_eip.nat_eip: Import complete [id=eipalloc-008965dfabecad0e4]
aws_nat_gateway.test_nat_public1: Importing... [id=nat-05d2c6295dd925d43]
aws_nat_gateway.test_nat_public1: Import complete [id=nat-05d2c6295dd925d43]
aws_route_table_association.public2: Importing... [id=subnet-02e8ab4c179ffbb45/rtb-07cd85c4732527a0d]
aws_route_table_association.public2: Import complete [id=subnet-02e8ab4c179ffbb45/rtb-07cd85c4732527a0d]
aws_route_table_association.public1: Importing... [id=subnet-06b1ac9093e4620be/rtb-07cd85c4732527a0d]
aws_route_table_association.public1: Import complete [id=subnet-06b1ac9093e4620be/rtb-07cd85c4732527a0d]
aws_route_table_association.public3: Importing... [id=subnet-00a8e331d96155479/rtb-07cd85c4732527a0d]
aws_route_table_association.public3: Import complete [id=subnet-00a8e331d96155479/rtb-07cd85c4732527a0d]
aws_route_table.test_rtb_private3: Importing... [id=rtb-0895530280784f046]
aws_route_table.test_rtb_private3: Import complete [id=rtb-0895530280784f046]
aws_route_table.test_rtb_private1: Importing... [id=rtb-081881f72ed96e75e]
aws_route_table.test_rtb_private1: Import complete [id=rtb-081881f72ed96e75e]
aws_route_table.test_rtb_private2: Importing... [id=rtb-0d1d79915c9332d88]
aws_route_table.test_rtb_private2: Import complete [id=rtb-0d1d79915c9332d88]
aws_route_table_association.private2: Importing... [id=subnet-062c19a03d4a7e7f0/rtb-0d1d79915c9332d88]
aws_route_table_association.private2: Import complete [id=subnet-062c19a03d4a7e7f0/rtb-0d1d79915c9332d88]
aws_route_table_association.private3: Importing... [id=subnet-03cb64ba88effa768/rtb-0895530280784f046]
aws_route_table_association.private3: Import complete [id=subnet-03cb64ba88effa768/rtb-0895530280784f046]
aws_route_table_association.private1: Importing... [id=subnet-026e0754e7d6e7271/rtb-081881f72ed96e75e]
aws_route_table_association.private1: Import complete [id=subnet-026e0754e7d6e7271/rtb-081881f72ed96e75e]
aws_subnet.test_subnet_public2: Modifying... [id=subnet-02e8ab4c179ffbb45]
aws_subnet.test_subnet_public3: Modifying... [id=subnet-00a8e331d96155479]
aws_subnet.test_subnet_public1: Modifying... [id=subnet-06b1ac9093e4620be]
aws_default_security_group.default: Modifying... [id=sg-015a2b56b75447aad]
aws_default_security_group.default: Modifications complete after 0s [id=sg-015a2b56b75447aad]
aws_subnet.test_subnet_public2: Still modifying... [id=subnet-02e8ab4c179ffbb45, 00m10s elapsed]
aws_subnet.test_subnet_public1: Still modifying... [id=subnet-06b1ac9093e4620be, 00m10s elapsed]
aws_subnet.test_subnet_public3: Still modifying... [id=subnet-00a8e331d96155479, 00m10s elapsed]
aws_subnet.test_subnet_public1: Modifications complete after 11s [id=subnet-06b1ac9093e4620be]
aws_subnet.test_subnet_public2: Modifications complete after 11s [id=subnet-02e8ab4c179ffbb45]
aws_subnet.test_subnet_public3: Modifications complete after 11s [id=subnet-00a8e331d96155479]
aws_nat_gateway.test_nat_public1: Modifying... [id=nat-05d2c6295dd925d43]
aws_nat_gateway.test_nat_public1: Modifications complete after 0s [id=nat-05d2c6295dd925d43]
Apply complete! Resources: 21 imported, 0 added, 5 changed, 0 destroyed.
$
【方法2】は以下の点が難点となります。
- ①で全てのリソースに対してimportブロックを作成する必要がある(数が多いとリソースIDの記載が大変)
- ②でTerraformが自動生成したresourceブロックに対して手動でError修正する必要がある
【方法3】Amazon Q Developerを使う
VSCode等IDEツールから使えるAmazon Q Developerもありますが
今回使うのはマネジメントコンソール上から使えるAmazon Q Developerです。
英語しか入力してはダメな雰囲気を醸し出していますが、無視して日本語で直接プロンプトを投げてしまってOKです。ちゃんと日本語で返ってきます。
魔法のプロンプトが以下の通りです。
vpc-xxxxxxxxとそのVPCに関連するリソースを
terraform plan -generate-config-out=generated.tfでIaC化します。
事前するimport.tfとその結果生成されるgenerated.tfを教えてください。
generated.tfはterraform plan実行時にエラーが出ないように修正してください。
全てのリソースを列挙する必要は無く、VPCだけ特定させればあとは関連リソース含めてTerraformコードを作ってくれます。
「terraform plan実行時にエラーが出ないようにしてくれ」といったお願いも叶えてくれるのがミソです。
そうすると、こんな感じの結果を出してくれます。
# VPC
import {
to = aws_vpc.test_vpc
id = "vpc-0ebd959a9882f8225"
}
# Internet Gateway
import {
to = aws_internet_gateway.test_igw
id = "igw-0aa2f01afdb855dc8"
}
# Public Subnets
import {
to = aws_subnet.test_subnet_public1
id = "subnet-06b1ac9093e4620be"
}
#・・・省略・・・
import.tfは【方法2】と同様です
# VPC
resource "aws_vpc" "test_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "test-vpc"
}
}
# Internet Gateway
resource "aws_internet_gateway" "test_igw" {
vpc_id = aws_vpc.test_vpc.id
tags = {
Name = "test-igw"
}
}
# Public Subnets
resource "aws_subnet" "test_subnet_public1" {
vpc_id = aws_vpc.test_vpc.id
cidr_block = "10.0.0.0/20"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = true
tags = {
Name = "test-subnet-public1-ap-northeast-1a"
}
}
#・・・省略・・・
あとはプロンプトに返ってきたimport.tfとgenerated.tfをカレントディレクトリにコピペして置いてterraform plan → terraform applyするだけです
実行結果は【方法2】と同様です
本当に手間要らずでTerraform管理下にできるのでお勧めです!
あとがき
一般的なLLMでもTerraformコードはもちろん作ってくれます。
Amazon Q Developerの良いところは「自AWSアカウントの情報をAPIで取ってきて環境に即したアウトプットを出してくれる」だと思います。
少し前まではAmazon Q Developer精度どうなんだと言われていたと思いますが、モデルの進化とともに他LLMと遜色ないレベルで精度が上がってきてると思いました。
しかも、自AWSアカウントの情報を元にアウトプットしてくれるので、使わない手はないですよね。
これからも様々なシチュエーションで使っていきたいと思います。

