Introduction
You will need to write the terraform code that goes with the resource you are importing. This is sometimes difficult with all the options available to get this right. Terraform is able to do this for you. Depending on the terraform version this requires different approaches.
Expected Outcome
Have the required code for an imported resource
Example 1: Procedure using Terraform <1.5.x
We have a manually created EC2 instance in AWS that we would like to import into our state and have appropriate code.
- Only write the code for the resource so you are able to import it into your state file.
resource "aws_instance" "web" {]
- Use the terraform import command. For more details see the official documention here
terraform import aws_instance.web i-0cfc7230a4cf634b5
output
aws_instance.web: Importing from ID "i-0cfc7230a4cf634b5"...
aws_instance.web: Import prepared!
Prepared aws_instance for import
aws_instance.web: Refreshing state... [id=i-0cfc7230a4cf634b5]
Import successful!
- Generate the code you can use in your code
terraform state show aws_instance.web
- copy the output of the code and replace it with the original code you created in the first step
resource "aws_instance" "web" {
ami = "ami-062550af7b9fa7d05"
arn = "arn:aws:ec2:ap-southeast-1:xxxxxxxxx123:instance/i-0cfc7230a4cf634b5"
associate_public_ip_address = true
availability_zone = "ap-southeast-1b"
cpu_core_count = 1
cpu_threads_per_core = 1
disable_api_stop = false
disable_api_termination = false
ebs_optimized = false
get_password_data = false
hibernation = false
id = "i-0cfc7230a4cf634b5"
instance_initiated_shutdown_behavior = "stop"
instance_state = "running"
instance_type = "t2.micro"
ipv6_address_count = 0
ipv6_addresses = []
monitoring = false
placement_partition_number = 0
primary_network_interface_id = "eni-06e41d754b844d127"
private_dns = "ip-172-31-20-122.ap-southeast-1.compute.internal"
private_ip = "172.31.20.122"
public_dns = "ec2-xxx-xxx-xxx-xxx.ap-southeast-1.compute.amazonaws.com"
public_ip = "xxx.xxx.xxx.xxx"
secondary_private_ips = []
security_groups = [
"default",
]
source_dest_check = true
subnet_id = "subnet-2a92464c"
tags = {
"Name" = "HelloWorld"
}
tags_all = {
"Name" = "HelloWorld"
}
tenancy = "default"
vpc_security_group_ids = [
"sg-d1738aa5",
]
capacity_reservation_specification {
capacity_reservation_preference = "open"
}
cpu_options {
core_count = 1
threads_per_core = 1
}
credit_specification {
cpu_credits = "standard"
}
enclave_options {
enabled = false
}
maintenance_options {
auto_recovery = "default"
}
metadata_options {
http_endpoint = "enabled"
http_put_response_hop_limit = 1
http_tokens = "optional"
instance_metadata_tags = "disabled"
}
private_dns_name_options {
enable_resource_name_dns_a_record = false
enable_resource_name_dns_aaaa_record = false
hostname_type = "ip-name"
}
root_block_device {
delete_on_termination = true
device_name = "/dev/sda1"
encrypted = false
iops = 100
tags = {}
throughput = 0
volume_id = "vol-0c165794d5a136af4"
volume_size = 8
volume_type = "gp2"
}
}
- Execute a terraform plan to verify the code is correct.
You will get a lot of errors like the following. This is because of attributes that shouldn't be configured in your own code, but are needed by the state for proper management.
Remove the attributes it is complaining about.
Error: Invalid or unknown key
Error: Conflicting configuration arguments
Error: Value for unconfigurable attribute
- Following is the code after removing the attributes it complained about.
resource "aws_instance" "web" {
ami = "ami-062550af7b9fa7d05"
associate_public_ip_address = true
availability_zone = "ap-southeast-1b"
disable_api_stop = false
disable_api_termination = false
ebs_optimized = false
get_password_data = false
hibernation = false
instance_initiated_shutdown_behavior = "stop"
instance_type = "t2.micro"
monitoring = false
placement_partition_number = 0
private_ip = "172.31.20.122"
secondary_private_ips = []
security_groups = [
"default",
]
source_dest_check = true
subnet_id = "subnet-2a92464c"
tags = {
"Name" = "HelloWorld"
}
tags_all = {
"Name" = "HelloWorld"
}
tenancy = "default"
vpc_security_group_ids = [
"sg-d1738aa5",
]
capacity_reservation_specification {
capacity_reservation_preference = "open"
}
cpu_options {
core_count = 1
threads_per_core = 1
}
credit_specification {
cpu_credits = "standard"
}
enclave_options {
enabled = false
}
maintenance_options {
auto_recovery = "default"
}
metadata_options {
http_endpoint = "enabled"
http_put_response_hop_limit = 1
http_tokens = "optional"
instance_metadata_tags = "disabled"
}
private_dns_name_options {
enable_resource_name_dns_a_record = false
enable_resource_name_dns_aaaa_record = false
hostname_type = "ip-name"
}
- run another terraform plan and it should show you no changes
terraform plan
aws_instance.web: Refreshing state... [id=i-0cfc7230a4cf634b5]
No changes. Your infrastructure matches the configuration.
- Some attributes are not needed because they show the default value. If you remove these as well the code would like like this. For the default value look at the provider documentation for the resource.
resource "aws_instance" "web" {
ami = "ami-062550af7b9fa7d05"
instance_type = "t2.micro"
tags = {
Name = "HelloWorld"
}
}
Example 2: Procedure using Terraform >=1.5.x
We have a manually created EC2 instance in AWS that we would like to import into our state and have appropriate code.
- Add the following code to import the resource
import {
# ID of the cloud resource
# Check provider documentation for importable resources and format
id = "i-0cfc7230a4cf634b5"
# Resource address
to = aws_instance.web
}
- run terraform plan with the generation of the code into a file
terraform plan -generate-config-out=web_instance.tf
- Depending on the resource you may see some errors. This is because of attributes that shouldn't be configured in your own code, but are wrongly populated by the generated code option.
Planning failed. Terraform encountered an error while generating this plan.
╷
│ Warning: Config generation is experimental
│
│ Generating configuration during import is currently experimental, and the generated configuration format may change in future versions.
╵
╷
│ Error: Conflicting configuration arguments
│
│ with aws_instance.web,
│ on web_instance.tf line 14:
│ (source code not available)
│
│ "ipv6_address_count": conflicts with ipv6_addresses
╵
╷
│ Error: Conflicting configuration arguments
│
│ with aws_instance.web,
│ on web_instance.tf line 15:
│ (source code not available)
│
│ "ipv6_addresses": conflicts with ipv6_address_count
- Remove the attributes from the configuration file you generated web_instance.tf
# ipv6_address_count = 0
# ipv6_addresses = []
- run another terraform plan and view the output
terraform plan
Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
- Run a terraform apply to actually import the resource into your state
terraform apply
Apply complete! Resources: 1 imported, 0 added, 0 changed, 0 destroyed.
- You can now remove the import code if you like
# import {
# # ID of the cloud resource
# # Check provider documentation for importable resources and format
# id = "i-0cfc7230a4cf634b5"
# # Resource address
# to = aws_instance.web
# }