Terraform で既存のインフラリソースをインポートする方法

目次
インフラエンジニアの寺岡です。
皆さんは「既存のインフラ構成も Terraform で管理できるようにしたい」と考えたことはないでしょうか。
Terraform ではそれを実現するコマンドが用意されているのでこの記事で紹介します。
事前に Terraform を利用せずに、VPC を1つ作成しておきました。

今回はこちらを Terraform にインポートしてみます。
ディレクトリ構成
$ tree . ├── README.md ├── provider.tf ├── terraform.tfstate ├── variables.tf └── vpc.tf 0 directories, 5 files
provider と variable を記述する
インポートする前に、provider と variable は最低限必要なので記述します。
provider.tf
provider "aws" {
access_key = var.access_key
secret_key = var.secret_key
region = var.region
assume_role {
role_arn = var.role_arn
}
}
variables.tf
####################
# Provider
####################
variable "access_key" {
description = "AWS Access Key"
}
variable "secret_key" {
description = "AWS Secret Key"
}
variable "role_arn" {
description = "AWS Role Arn"
}
variable "region" {
default = "ap-northeast-1"
}
AWSのアクセスキーなどは環境変数に保存しておきましょう。
$ export TF_VAR_access_key=XXXXXXXXXXXXXXXXXXXX $ export TF_VAR_secret_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX $ export TF_VAR_role_arn=arn:aws:iam::XXXXXXXXXXXX:role/XXXXXXXXXXXXXXXXXXX
plan を実行する
試しに plan を実行します。
今は何もインポートしていないので「No changes.」で実行が終わります。
$ terraform plan Refreshing Terraform state in-memory prior to plan... The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage. ------------------------------------------------------------------------ No changes. Infrastructure is up-to-date. This means that Terraform did not detect any differences between your configuration and real physical resources that exist. As a result, no actions need to be performed.
import を実行する
Terraform で既存リソースをインポートする場合は「terraform import」コマンドを実行します。
コマンドの書式は Terraform のマニュアル(※1)に記載があるので、そちらを参考にして以下のコマンドを実行します。
$ terraform import aws_vpc.vpc vpc-09a9f1827bfd851f4
Error: resource address "aws_vpc.vpc" does not exist in the configuration.
Before importing this resource, please create its configuration in the root module. For example:
resource "aws_vpc" "vpc" {
# (resource arguments)
}
エラーが出てしまいました。
インポートするときは「aws_vpc.vpc」のようにリソースの種類と名前を指定することになり、ここで指定したtfファイル上のリソースに対して、既存リソースの情報がインポートされることになります。
そのため、予め対応するリソースを、tfファイルに記述しておく必要があります。
vpc リソースを記述する
以下のように記載しておきます。
vpc.tf
####################
# VPC
####################
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "vpc-tfstate-test"
}
}
再度 import を実行する
再度実行してみます。
今度は問題なくインポートできましたね。
$ terraform import aws_vpc.vpc vpc-09a9f1827bfd851f4 aws_vpc.vpc: Importing from ID "vpc-09a9f1827bfd851f4"... aws_vpc.vpc: Import prepared! Prepared aws_vpc for import aws_vpc.vpc: Refreshing state... [id=vpc-09a9f1827bfd851f4] 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.
この terraform import は何をやってくれているのでしょうか。
コマンドを実行すると「terraform.tfstate」という名前のファイルが作成されていることがわかります。
terraform.tfstate
{
"version": 4,
"terraform_version": "0.12.24",
"serial": 1,
"lineage": "c0359eb1-d905-e252-2d8d-525710adddb1",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "aws_vpc",
"name": "vpc",
"provider": "provider.aws",
"instances": [
{
"schema_version": 1,
"attributes": {
"arn": "arn:aws:ec2:ap-northeast-1:485076298277:vpc/vpc-09a9f1827bfd851f4",
"assign_generated_ipv6_cidr_block": false,
"cidr_block": "10.0.0.0/16",
"default_network_acl_id": "acl-0cd8abfed52e0e951",
"default_route_table_id": "rtb-08c13269b1b26c9b8",
"default_security_group_id": "sg-007ef29e563b6f9c7",
"dhcp_options_id": "dopt-6c0f430b",
"enable_classiclink": false,
"enable_classiclink_dns_support": false,
"enable_dns_hostnames": true,
"enable_dns_support": true,
"id": "vpc-09a9f1827bfd851f4",
"instance_tenancy": "default",
"ipv6_association_id": "",
"ipv6_cidr_block": "",
"main_route_table_id": "rtb-08c13269b1b26c9b8",
"owner_id": "485076298277",
"tags": {
"Name": "vpc-tfstate-test"
}
},
"private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ=="
}
]
}
]
}
中身を見ると先ほどインポートしたリソースの情報が保存されていますね。
Terraform は実行したときに新規作成 or 変更 or 削除を自動で判断してリソースを作成してくれますが、この判断は実行時に読み込んだtfファイルと、tfstate に書かれている内容の差分を元に行われています。
つまり、指定した既存リソースを Terraform 上で管理するためには、tfstate の内容を変更する必要があり、「指定した既存リソースの情報を読み込み、tfstate に追加してくれるコマンド」が terraform import です。
再度 plan を実行する
terraform import を実行したので、再度 plan を実行してみましょう。
$ terraform plan Refreshing Terraform state in-memory prior to plan... The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage. aws_vpc.vpc: Refreshing state... [id=vpc-09a9f1827bfd851f4] ------------------------------------------------------------------------ No changes. Infrastructure is up-to-date. This means that Terraform did not detect any differences between your configuration and real physical resources that exist. As a result, no actions need to be performed.
aws_vpc.vpc が Terraform 上のリソースとして読み込まれていますね。
enable_dns_hostnames を false に変更してみましょう。
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
aws_vpc.vpc: Refreshing state... [id=vpc-09a9f1827bfd851f4]
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_vpc.vpc will be updated in-place
~ resource "aws_vpc" "vpc" {
arn = "arn:aws:ec2:ap-northeast-1:485076298277:vpc/vpc-09a9f1827bfd851f4"
assign_generated_ipv6_cidr_block = false
cidr_block = "10.0.0.0/16"
default_network_acl_id = "acl-0cd8abfed52e0e951"
default_route_table_id = "rtb-08c13269b1b26c9b8"
default_security_group_id = "sg-007ef29e563b6f9c7"
dhcp_options_id = "dopt-6c0f430b"
enable_classiclink = false
enable_classiclink_dns_support = false
~ enable_dns_hostnames = true -> false
enable_dns_support = true
id = "vpc-09a9f1827bfd851f4"
instance_tenancy = "default"
main_route_table_id = "rtb-08c13269b1b26c9b8"
owner_id = "485076298277"
tags = {
"Name" = "vpc-tfstate-test"
}
}
Plan: 0 to add, 1 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
差分のみを変更しようとする挙動になっていますね。
まとめ
import コマンドの説明ページ(※2)にも記載されていますが、コマンドで書き換えてくれるのは、あくまで「tfstate のみ」です。
つまり、実際のtfファイルについては、 tfstate との差分を見ながら自身で記述する必要があります。将来的にはtfファイルも自動生成されるように機能追加されるようですが、現在のところは対象が多ければ多いほど辛くなってきます。。。
それを解決するために terraformer(※3)が OSS として公開されています。
次回はこちらの使い方をブログにまとめようと思います。
参考URL
※1:https://www.terraform.io/docs/providers/aws/r/vpc.html
※2:https://www.terraform.io/docs/import/index.html
※3:https://github.com/GoogleCloudPlatform/terraformer
2