How to import existing infrastructure resources with Terraform

table of contents
My name is Teraoka and I am an infrastructure engineer
Have you ever thought, "I want to be able to manage my existing infrastructure configuration with Terraform?"
Terraform provides commands that make this possible, and I will introduce them in this article.
I created one VPC in advance without using Terraform

This time, we will import this into Terraform
Directory structure
$ tree . ├── README.md ├── provider.tf ├── terraform.tfstate ├── variables.tf └── vpc.tf 0 directories, 5 files
Describing the provider and variable
Before importing, we need to write the provider and variable as they are the minimum required
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" }
Store your AWS access keys in environment variables
$ export TF_VAR_access_key=XXXXXXXXXXXXXXXXXX $ export TF_VAR_secret_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX $ export TF_VAR_role_arn=arn:aws:iam::XXXXXXXXXXXX:role/XXXXXXXXXXXXXXXXXXX
Run the plan
Let’s try running plan.
Since we haven’t imported anything yet, the execution will end with “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.
Run the import
To import existing resources using Terraform, run the "terraform import" command.
The command format is described in the Terraform manual (※1) , so refer to it and run the following command.
$ 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) }
An error occurred
When importing, you will specify the resource type and name, such as "aws_vpc.vpc", and the information of existing resources will be imported into the resources in the tf file specified here
Therefore, you need to write the corresponding resources in the tf file in advance
Describe a vpc resource
I will write it as follows:
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" } }
Run the import again
Let's try again.
This time the import went smoothly.
$ 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.
What does this terraform import do?
When you run the command, you will see that a file named "terraform.tfstate" is created.
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==" } ] } ] }
If you look at the contents, you will see that the information about the resources you just imported is saved
When Terraform is executed, it automatically determines whether to create, modify, or delete resources and creates them accordingly. This determination is made based on the difference between the tf file loaded at runtime and the contents written in tfstate
In other words, in order to manage specified existing resources on Terraform, you need to change the contents of tfstate, and terraform import is a command that reads the information of specified existing resources and adds them to tfstate
Run plan again
Now that we've run terraform import, let's run plan again
$ 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 is loaded as a resource on Terraform.
Let's change enable_dns_hostnames to 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.
It seems to be trying to change only the differences
summary
As stated on the import command explanation page (※2)
In other words, you need to write the actual tf file yourself while looking at the differences with tfstate. In the future, it seems that a function will be added to automatically generate tf files, but for now, the more targets there are, the more difficult it becomes..
To solve this problem, Terraformer (※3) has been released as open source software.
In my next blog post, I will explain how to use it.
Reference 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