Manage resources in Terraform state
Terraform stores information about your infrastructure in a state file. This state file keeps track of resources created by your configuration and maps them to real-world resources.
Terraform compares your configuration with the state file and your existing
infrastructure to create plans and make changes to your infrastructure. When you
run terraform apply
or terraform destroy
against your initialized
configuration, Terraform writes metadata about your configuration to the state
file and updates your infrastructure resources accordingly. Occasionally,
you may need to manipulate your projects state outside of the standard
workflow. For example, you may want to remove a resource from your project
without destroying the real-world resource associated with it.
In this tutorial, you will create an AWS instance and security group, examine your project's state file, and use Terraform to remove infrastructure from your project's state.
Prerequisites
This tutorial assumes that you are familiar with the usual Terraform plan/apply workflow. If you are new to Terraform, refer first to the Getting Started tutorial.
For this tutorial, you will need:
- The Terraform CLI 1.7+ installed locally
- An AWS account
- The AWS CLI installed
- Your AWS credentials configured locally with your access keys and a default region.
Note
This tutorial will provision resources that qualify under the AWS free-tier. If your account doesn't qualify under the AWS free-tier, we're not responsible for any charges that you may incur.
Create infrastructure and state
Clone the Learn Terraform State Management repository.
$ git clone https://github.com/hashicorp-education/learn-terraform-state
Change into the new directory.
$ cd learn-terraform-state
Review the main.tf
file. This configuration deploys an Ubuntu EC2 instance publicly accessible on port 8080.
main.tf
provider "aws" {
region = var.aws_region
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_security_group" "sg_8080" {
name = "terraform-learn-state-sg-8080"
ingress {
from_port = "8080"
to_port = "8080"
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
// connectivity to ubuntu mirrors is required to run `apt-get update` and `apt-get install apache2`
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "example" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.sg_8080.id]
user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y apache2
sed -i -e 's/80/8080/' /etc/apache2/ports.conf
echo "Hello World" > /var/www/html/index.html
systemctl restart apache2
EOF
tags = {
Name = "terraform-learn-state-ec2"
}
}
This configuration uses the AWS provider to create an EC2 instance and a security group that allows public access.
Initialize the directory.
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Installing hashicorp/aws v5.31.0...
- Installed hashicorp/aws v5.31.0 (signed by HashiCorp)
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
After Terraform initializes, apply the configuration and approve the run by
typing yes
at the prompt.
$ terraform apply
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 0s [id=ami-027a754129abb5386]
Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.example will be created
+ resource "aws_instance" "example" {
##...
Plan: 2 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ aws_region = "us-east-1"
+ instance_id = (known after apply)
+ public_ip = (known after apply)
+ security_group = (known after apply)
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_security_group.sg_8080: Creating...
aws_security_group.sg_8080: Creation complete after 3s [id=sg-0adfd0a0ade3eebdc]
aws_instance.example: Creating...
aws_instance.example: Still creating... [10s elapsed]
aws_instance.example: Still creating... [20s elapsed]
aws_instance.example: Still creating... [30s elapsed]
aws_instance.example: Creation complete after 32s [id=i-05a8893f05c6a37be]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
aws_region = "us-east-1"
instance_id = "i-05a8893f05c6a37be"
public_ip = "18.212.104.187"
security_group = "sg-0adfd0a0ade3eebdc"
Examine the state file
Now that you have applied this configuration, you have a local state file that
tracks the resources Terraform created. Check your directory to confirm the
terraform.tfstate
file exists.
$ ls -1
LICENSE
README.md
main.tf
new_state
outputs.tf
terraform.tf
terraform.tfstate
variables.tf
You should not manually change information in your state file in a real-world
situation to avoid unnecessary drift between your Terraform configuration,
state, and infrastructure. Any change in state could result in your
infrastructure being destroyed and recreated at your next terraform apply
.
Warning
Do not manually modify state files.
Open the terraform.tfstate
file in your file editor.
This example contains few resources, so your actual state file is relatively small.
This file is the JSON encoded state that Terraform writes and reads at each operation. The first stanza contains information about your Terraform application.
Explore resources
in state
The resources
section of the state file contains the schema for any resources
you create in Terraform. Review the resources
section of this file.
"resources": [
{
"mode": "data",
"type": "aws_ami",
"name": "ubuntu",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"architecture": "x86_64",
"arn": "arn:aws:ec2:us-east-1::image/ami-027a754129abb5386",
##...
},
##...
]
The first key in this schema is the mode
. Mode refers to the type of resource
Terraform creates — either a resource (managed
) or a data source (data
). The
type
key refers to the resource type - in this case, the aws_ami
type is a
resource available in the aws
provider.
##FIXME
##...
{
"mode": "managed",
"type": "aws_instance",
"name": "example",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
"ami": "ami-027a754129abb5386",
"arn": "arn:aws:ec2:us-east-1:949008909725:instance/i-05a8893f05c6a37be",
"associate_public_ip_address": true,
"availability_zone": "us-east-1a",
##...
"public_ip": "18.212.104.187",
##...
"secondary_private_ips": [],
"security_groups": [
"terraform-learn-state-sg-8080"
],
"source_dest_check": true,
"spot_instance_request_id": "",
"subnet_id": "subnet-0e75b9376618c682a",
"tags": {
"Name": "terraform-learn-state-ec2"
},
##...
}
}
]
},
The aws_instance
type is a managed
resource with the AMI from the data.aws_ami
source.
The instances
section in this resource contains the attributes
of the resource. The security_groups
attribute, for example, is captured in plain text in state as opposed to the variable interpolated string in the configuration file.
Terraform also marks dependencies between resources in state with the built-in dependency tree logic.
##...
"dependencies": [
"aws_security_group.sg_8080",
"data.aws_ami.ubuntu"
]
##...
Because your state file has a record of your dependencies, enforced by you with a depends_on
attribute or by Terraform automatically, any changes to the dependencies will force a change to the dependent resource.
Examine State with CLI
The Terraform CLI allows you to review resources in the state file without interacting with the .tfstate
file itself. This is how you should interact with your state.
Run terraform show
to get a human-friendly output of the resources contained in your state.
$ terraform show
# data.aws_ami.ubuntu:
data "aws_ami" "ubuntu" {
architecture = "x86_64"
arn = "arn:aws:ec2:us-east-1::image/ami-027a754129abb5386"
block_device_mappings = [
##...
}
# aws_instance.example:
resource "aws_instance" "example" {
ami = "ami-027a754129abb5386"
arn = "arn:aws:ec2:us-east-1:949008909725:instance/i-05a8893f05c6a37be"
##...
}
# aws_security_group.sg_8080:
resource "aws_security_group" "sg_8080" {
arn = "arn:aws:ec2:us-east-1:949008909725:security-group/sg-0adfd0a0ade3eebdc"
description = "Managed by Terraform"
##...
}
Outputs:
aws_region = "us-east-1"
instance_id = "i-05a8893f05c6a37be"
public_ip = "18.212.104.187"
security_group = "sg-0adfd0a0ade3eebdc"
Run terraform state list
to get the list of resource names and local identifiers in your state file. This command is useful for more complex configurations where you need to find a specific resource without parsing state with terraform show
.
$ terraform state list
data.aws_ami.ubuntu
aws_instance.example
aws_security_group.sg_8080
Replace a resource with CLI
Terraform usually only updates your infrastructure if it does not match your configuration. You can use the -replace
flag for terraform plan
and terraform apply
operations to safely recreate resources in your environment even if you have not edited the configuration, which can be useful in cases of system malfunction. Replacing a resource is also useful in cases where a user manually changes a setting on a resource or when you need to update a provisioning script. This allows you to rebuild specific resources and avoid a full terraform destroy
operation on your configuration. The -replace
flag allows you to target specific resources and avoid destroying all the resources in your workspace just to fix one of them.
In older versions of Terraform, you may have used the terraform taint
command
to achieve a similar outcome. That command has now been deprecated in favor of
the -replace
flag, which allows for a simpler, less error-prone workflow. If
you are using an older version of Terraform, consider upgrading or review the
taint
documentation
for more information.
Tip
The -replace
flag was introduced in Terraform 0.15.2. Ensure you are using the correct version of Terraform for this next step.
Run terraform plan -replace="aws_instance.example"
to see the actions Terraform would take if you replaced the instance.
$ terraform plan -replace="aws_instance.example"
data.aws_ami.ubuntu: Reading...
aws_security_group.sg_8080: Refreshing state... [id=sg-0adfd0a0ade3eebdc]
data.aws_ami.ubuntu: Read complete after 1s [id=ami-027a754129abb5386]
aws_instance.example: Refreshing state... [id=i-05a8893f05c6a37be]
Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# aws_instance.example will be replaced, as requested
-/+ resource "aws_instance" "example" {
~ arn = "arn:aws:ec2:us-east-1:949008909725:instance/i-05a8893f05c6a37be" -> (known after apply)
##...
}
Plan: 1 to add, 0 to change, 1 to destroy.
Changes to Outputs:
~ instance_id = "i-05a8893f05c6a37be" -> (known after apply)
~ public_ip = "18.212.104.187" -> (known after apply)
───────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.
As shown in the output, when you apply this change, Terraform will destroy your running instance and create a new one.
Run terraform apply
with the -replace
flag to force Terraform to destroy and
recreate the resource. Type yes
when prompted to accept this update.
$ terraform apply -replace="aws_instance.example"
data.aws_ami.ubuntu: Reading...
aws_security_group.sg_8080: Refreshing state... [id=sg-0adfd0a0ade3eebdc]
data.aws_ami.ubuntu: Read complete after 0s [id=ami-027a754129abb5386]
aws_instance.example: Refreshing state... [id=i-05a8893f05c6a37be]
Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# aws_instance.example will be replaced, as requested
-/+ resource "aws_instance" "example" {
~ arn = "arn:aws:ec2:us-east-1:949008909725:instance/i-05a8893f05c6a37be" -> (known after apply)
###...
Plan: 1 to add, 0 to change, 1 to destroy.
Changes to Outputs:
~ instance_id = "i-05a8893f05c6a37be" -> (known after apply)
~ public_ip = "18.212.104.187" -> (known after apply)
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_instance.example: Destroying... [id=i-05a8893f05c6a37be]
aws_instance.example: Still destroying... [id=i-05a8893f05c6a37be, 10s elapsed]
aws_instance.example: Still destroying... [id=i-05a8893f05c6a37be, 20s elapsed]
aws_instance.example: Still destroying... [id=i-05a8893f05c6a37be, 30s elapsed]
aws_instance.example: Destruction complete after 31s
aws_instance.example: Creating...
aws_instance.example: Still creating... [10s elapsed]
aws_instance.example: Still creating... [20s elapsed]
aws_instance.example: Still creating... [30s elapsed]
aws_instance.example: Creation complete after 32s [id=i-0c517d96d291b7e26]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
Outputs:
aws_region = "us-east-1"
instance_id = "i-0c517d96d291b7e26"
public_ip = "54.159.61.68"
security_group = "sg-0adfd0a0ade3eebdc"
Using the terraform apply
command with the -replace
flag is the
HashiCorp-recommended process for managing resources without manually editing
your state file.
Move a resource to a different state file
Some of the Terraform state subcommands are useful in very specific situations. HashiCorp recommends only performing these advanced operations as the last resort.
The terraform state mv
command moves resources from one state file to another.
You can also rename resources with mv
. The move command will update the
resource in state, but not in your configuration file. Moving resources is
useful when you want to combine modules or resources from other states, but do
not want to destroy and recreate the infrastructure.
The new_state
subdirectory contains a new Terraform configuration. This
configuration creates a new EC2 instance named aws_instance.example_new
and
uses a data resource to use the same security group from your root configuration
file. Change into the subdirectory.
$ cd new_state
Run terraform init
.
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v5.31.0
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Apply your configuration. Respond to the confirmation prompt with a yes
.
$ terraform apply
data.terraform_remote_state.root: Reading...
data.terraform_remote_state.root: Read complete after 0s
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 0s [id=ami-027a754129abb5386]
Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.example will be created
+ resource "aws_instance" "example" {
##...
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_instance.example: Creating...
aws_instance.example: Still creating... [10s elapsed]
aws_instance.example: Still creating... [20s elapsed]
aws_instance.example: Still creating... [30s elapsed]
aws_instance.example: Creation complete after 33s [id=i-0bf5ee79542833739]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
instance_id = "i-0bf5ee79542833739"
public_ip = "3.80.21.81"
Now, you have a second state file with a managed resource and a data source.
Move the new EC2 instance resource you just created, aws_instance.example_new
, to the old configuration's file in the directory above your current location, as specified with the -state-out
flag. Set the destination name to the same name, since in this case there is no resource with the same name in the target state file.
$ terraform state mv -state-out=../terraform.tfstate aws_instance.example_new aws_instance.example_new
Move "aws_instance.example_new" to "aws_instance.example_new"
Successfully moved 1 object(s).
Note
Resource names must be unique to the intended state file. The terraform state mv
command can also rename resources to make them unique.
Change into your root directory.
$ cd ..
Run terraform state list
to confirm that the new EC2 instance, aws_instance.example_new
, is present in the in original configuration's state file.
$ terraform state list
data.aws_ami.ubuntu
aws_instance.example
aws_instance.example_new
aws_security_group.sg_8080
Without adding the EC2 resource you moved to your configuration files, create a Terraform plan. Because the new EC2 instance is present in state but not in the configuration, Terraform plans to destroy the moved instance, and remove the resource from the state file.
$ terraform plan
data.aws_ami.ubuntu: Reading...
aws_security_group.sg_8080: Refreshing state... [id=sg-0adfd0a0ade3eebdc]
aws_instance.example_new: Refreshing state... [id=i-084a99085ac1aab41]
data.aws_ami.ubuntu: Read complete after 1s [id=ami-027a754129abb5386]
aws_instance.example: Refreshing state... [id=i-0c517d96d291b7e26]
Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# aws_instance.example_new will be destroyed
# (because aws_instance.example_new is not in configuration)
- resource "aws_instance" "example_new" {
- ami = "ami-027a754129abb5386" -> null
- arn = "arn:aws:ec2:us-east-1:949008909725:instance/i-084a99085ac1aab41" -> null
##...
}
Plan: 0 to add, 0 to change, 1 to destroy.
───────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.
Open the main.tf
file in your root directory. Copy and paste the resource definition below.
resource "aws_instance" "example_new" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.sg_8080.id]
user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y apache2
sed -i -e 's/80/8080/' /etc/apache2/ports.conf
echo "Hello World" > /var/www/html/index.html
systemctl restart apache2
EOF
tags = {
Name = "terraform-learn-state-ec2"
}
}
Apply your configuration. Your configuration now matches your state file and Terraform will not perform any changes.
$ terraform apply
data.aws_ami.ubuntu: Reading...
aws_security_group.sg_8080: Refreshing state... [id=sg-0adfd0a0ade3eebdc]
data.aws_ami.ubuntu: Read complete after 0s [id=ami-027a754129abb5386]
aws_instance.example_new: Refreshing state... [id=i-084a99085ac1aab41]
aws_instance.example: Refreshing state... [id=i-0c517d96d291b7e26]
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration and
found no differences, so no changes are needed.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
aws_region = "us-east-1"
instance_id = "i-0c517d96d291b7e26"
public_ip = "54.159.61.68"
security_group = "sg-0adfd0a0ade3eebdc"
Change into your new_state
directory.
$ cd new_state
Run terraform destroy
and you should have no resources to destroy. Your security_group
resource is a data source and you moved the aws_instance
resource to another state file. Accept the changes by typing yes
when prompted.
$ terraform destroy
data.terraform_remote_state.root: Reading...
data.terraform_remote_state.root: Read complete after 0s
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-027a754129abb5386]
Changes to Outputs:
- instance_id = "i-084a99085ac1aab41" -> null
- public_ip = "3.208.8.142" -> null
You can apply this plan to save these new output values to the Terraform state,
without changing any real infrastructure.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
Destroy complete! Resources: 0 destroyed.
Remove a resource from state
Use a removed
block to remove specific resources from your state. This does
not destroy the infrastructure itself, instead it indicates that your Terraform configuration
will no longer manage the resource.
Change into your root directory.
$ cd ..
Remove the aws_instance.example_new
from your project's state.
Comment out the entire resource "aws_instance" "example_new"
block from
main.tf
and add a removed
block to instruct Terraform to remove the resource
from state, but not destroy it.
main.tf
removed {
from = aws_instance.example_new
lifecycle {
destroy = false
}
}
# resource "aws_instance" "example_new" {
# ami = data.aws_ami.ubuntu.id
# instance_type = "t2.micro"
# vpc_security_group_ids = [aws_security_group.sg_8080.id]
# user_data = <<-EOF
# #!/bin/bash
# apt-get update
# apt-get install -y apache2
# sed -i -e 's/80/8080/' /etc/apache2/ports.conf
# echo "Hello World" > /var/www/html/index.html
# systemctl restart apache2
# EOF
# tags = {
# Name = "terraform-learn-state-ec2"
# }
# }
Tip
The removed
block was introduced in Terraform 1.7. Previous versions of
Terraform used the terraform state rm
command to remove resources from state.
Ensure you are using the correct version of Terraform for this step.
Apply your configuration. Before you remove the new instance from your state, make a note of the value of the instance's id field. You will use this value later in this tutorial to re-import the instance.
Respond to the confirmation prompt with a yes
to remove
aws_instance.example_new
from your project's state.
$ terraform apply
data.aws_ami.ubuntu: Reading...
aws_security_group.sg_8080: Refreshing state... [id=sg-0adfd0a0ade3eebdc]
aws_instance.example_new: Refreshing state... [id=i-084a99085ac1aab41]
data.aws_ami.ubuntu: Read complete after 0s [id=ami-027a754129abb5386]
aws_instance.example: Refreshing state... [id=i-0c517d96d291b7e26]
Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
Terraform will perform the following actions:
# aws_instance.example_new will no longer be managed by Terraform, but will not be destroyed
# (destroy = false is set in the configuration)
. resource "aws_instance" "example_new" {
id = "i-084a99085ac1aab41"
tags = {
"Name" = "terraform-learn-state-ec2"
}
# (32 unchanged attributes hidden)
# (8 unchanged blocks hidden)
}
Plan: 0 to add, 0 to change, 0 to destroy.
Changes to Outputs:
- security_group = "sg-0adfd0a0ade3eebdc" -> null
╷
│ Warning: Some objects will no longer be managed by Terraform
│
│ If you apply this plan, Terraform will discard its tracking information for
│ the following objects, but it will not delete them:
│ - aws_instance.example_new
│
│ After applying this plan, Terraform will no longer manage these objects. You
│ will need to import them into Terraform to manage them again.
╵
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
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
aws_region = "us-east-1"
instance_id = "i-0c517d96d291b7e26"
public_ip = "54.159.61.68"
Confirm the change by reviewing the state with terraform state list
.
$ terraform state list
data.aws_ami.ubuntu
aws_instance.example
aws_security_group.sg_8080
The aws_instance.example_new
resource does not exist in your project's state,
but the resource still exists in your AWS account.
Import the instance back into your project. First, uncomment the
aws_instance.example_new
block, and comment out the removed
block you added
in the previous step.
main.tf
# removed {
# from = aws_instance.example_new
# lifecycle {
# destroy = false
# }
# }
resource "aws_instance" "example_new" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.sg_8080.id]
user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y apache2
sed -i -e 's/80/8080/' /etc/apache2/ports.conf
echo "Hello World" > /var/www/html/index.html
systemctl restart apache2
EOF
tags = {
Name = "terraform-learn-state-ec2"
}
}
Run terraform import
to bring this instance back into your state file. Replace
<INSTANCE_ID>
with the id of the aws_instance.example_new
resource from the
output of the last step.
Tip
This tutorial uses terraform import
to bring infrastructure under Terraform
management. Terraform 1.5+ supports configuration-driven
import, which lets you import multiple resources at
once, review the import in your plan-and-apply workflow, and generate
configuration for imported resources. Review the import
tutorial to learn more.
$ terraform import aws_instance.example_new <INSTANCE_ID>
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-027a754129abb5386]
aws_instance.example_new: Importing from ID "i-084a99085ac1aab41"...
aws_instance.example_new: Import prepared!
Prepared aws_instance for import
aws_instance.example_new: Refreshing state... [id=i-084a99085ac1aab41]
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.
Refresh modified infrastructure
The terraform refresh
command updates the state file when physical resources change outside of the Terraform workflow.
Delete the original EC2 instance from your AWS account using the AWS CLI or the AWS Console. It may take a few moments for AWS to destroy your instance.
$ aws ec2 terminate-instances --instance-ids $(terraform output -raw instance_id) --region $(terraform output -raw aws_region)
{
"TerminatingInstances": [
{
"CurrentState": {
"Code": 32,
"Name": "shutting-down"
},
"InstanceId": "i-0c517d96d291b7e26",
"PreviousState": {
"Code": 16,
"Name": "running"
}
}
]
}
By deleting this instance, you have created a difference between your state and the real-world resources mapped to it. The state file no longer reflects the reality of your environment. It may take up to five minutes for AWS to destroy your instance.
Run the terraform refresh
command to update your state file.
$ terraform refresh
data.aws_ami.ubuntu: Reading...
aws_security_group.sg_8080: Refreshing state... [id=sg-0adfd0a0ade3eebdc]
data.aws_ami.ubuntu: Read complete after 0s [id=ami-027a754129abb5386]
aws_instance.example: Refreshing state... [id=i-0c517d96d291b7e26]
aws_instance.example_new: Refreshing state... [id=i-084a99085ac1aab41]
Outputs:
aws_region = "us-east-1"
instance_id = "i-0c517d96d291b7e26"
public_ip = "54.159.61.68"
Run terraform state list
to confirm Terraform deleted the original aws_instance.example
resource from state.
$ terraform state list
data.aws_ami.ubuntu
aws_instance.example_new
aws_security_group.sg_8080
Your state file now reflects reality. You deleted the aws_instance.example
and the terraform refresh
command removed it from state.
The terraform refresh
command does not update your configuration file. Run terraform plan
to review the proposed infrastructure updates.
$ terraform plan
data.aws_ami.ubuntu: Reading...
aws_security_group.sg_8080: Refreshing state... [id=sg-0adfd0a0ade3eebdc]
data.aws_ami.ubuntu: Read complete after 0s [id=ami-027a754129abb5386]
aws_instance.example_new: Refreshing state... [id=i-084a99085ac1aab41]
Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.example will be created
+ resource "aws_instance" "example" {
+ ami = "ami-027a754129abb5386"
+ arn = (known after apply)
##...
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
~ instance_id = "i-0c517d96d291b7e26" -> (known after apply)
~ public_ip = "54.159.61.68" -> (known after apply)
───────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.
Remove the original aws_instance.example
resource from main.tf
.
main.tf
- resource "aws_instance" "example" {
- ami = data.aws_ami.ubuntu.id
- instance_type = "t2.micro"
- vpc_security_group_ids = [aws_security_group.sg_8080.id]
- user_data = <<-EOF
- #!/bin/bash
- apt-get update
- apt-get install -y apache2
- sed -i -e 's/80/8080/' /etc/apache2/ports.conf
- echo "Hello World" > /var/www/html/index.html
- systemctl restart apache2
- EOF
- tags = {
- Name = "terraform-learn-state-ec2"
- }
- }
Open outputs.tf
and remove the output values that reference the instance.
outputs.tf
- output "instance_id" {
- value = aws_instance.example.id
- }
- output "public_ip" {
- value = aws_instance.example.public_ip
- description = "The public IP of the web server"
- }
Apply the configuration, which will confirm that your configuration matches your
state file, and remove their outputs from state. Accept the changes by typing
yes
when prompted.
$ terraform apply
data.aws_ami.ubuntu: Reading...
aws_security_group.sg_8080: Refreshing state... [id=sg-0adfd0a0ade3eebdc]
data.aws_ami.ubuntu: Read complete after 1s [id=ami-027a754129abb5386]
aws_instance.example_new: Refreshing state... [id=i-084a99085ac1aab41]
Changes to Outputs:
- instance_id = "i-0c517d96d291b7e26" -> null
- public_ip = "54.159.61.68" -> null
You can apply this plan to save these new output values to the Terraform state,
without changing any real infrastructure.
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
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
aws_region = "us-east-1"
Notice that Terraform changed the outputs and did not destroy any infrastructure.
Note
Terraform automatically performs a refresh
during the plan
, apply
, and destroy
operations. All of these commands will reconcile state by default, and have the potential to modify your state file.
Destroy your infrastructure
Terraform also updates your state file when you run a terraform destroy
operation.
Destroy your infrastructure. Accept the changes by typing yes
when prompted.
$ terraform destroy
data.aws_ami.ubuntu: Reading...
aws_security_group.sg_8080: Refreshing state... [id=sg-0adfd0a0ade3eebdc]
data.aws_ami.ubuntu: Read complete after 1s [id=ami-027a754129abb5386]
aws_instance.example_new: Refreshing state... [id=i-084a99085ac1aab41]
Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# aws_instance.example_new will be destroyed
- resource "aws_instance" "example_new" {
- ami = "ami-027a754129abb5386" -> null
- arn = "arn:aws:ec2:us-east-1:949008909725:instance/i-084a99085ac1aab41" -> null
##...
Plan: 0 to add, 0 to change, 2 to destroy.
Changes to Outputs:
- aws_region = "us-east-1" -> null
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
aws_instance.example_new: Destroying... [id=i-084a99085ac1aab41]
aws_instance.example_new: Still destroying... [id=i-084a99085ac1aab41, 10s elapsed]
aws_instance.example_new: Still destroying... [id=i-084a99085ac1aab41, 20s elapsed]
aws_instance.example_new: Still destroying... [id=i-084a99085ac1aab41, 30s elapsed]
aws_instance.example_new: Still destroying... [id=i-084a99085ac1aab41, 40s elapsed]
aws_instance.example_new: Still destroying... [id=i-084a99085ac1aab41, 50s elapsed]
aws_instance.example_new: Destruction complete after 51s
aws_security_group.sg_8080: Destroying... [id=sg-0adfd0a0ade3eebdc]
aws_security_group.sg_8080: Destruction complete after 1s
Destroy complete! Resources: 2 destroyed.
Your terraform.tfstate
file still exists, but does not contain any resources.
Run terraform show
to confirm.
$ terraform show
The state file is empty. No resources are represented.
Open the terraform.tfstate
file in your file editor. The empty resources
attribute confirms Terraform destroyed all your previous resources.
{
"version": 4,
"terraform_version": "1.7.0",
"serial": 18,
"lineage": "0c41e079-7e11-bcb9-4c2d-050228201fa6",
"outputs": {},
"resources": [],
"check_results": null
}
Next steps
In this tutorial, you created an EC2 Ubuntu instance and corresponding security group. Then, you examined your local state file and used terraform state
to move, remove, and modify your resources across multiple configurations.
For more information about Terraform state, review the following documentation: