In the previous page, you created your first infrastructure with Terraform: a single EC2 instance. In this page, we're going to modify that resource, and see how Terraform handles change.
Infrastructure is continuously evolving, and Terraform was built to help manage and enact that change. As you change Terraform configurations, Terraform builds an execution plan that only modifies what is necessary to reach your desired state.
By using Terraform to change infrastructure, you can version control not only your configurations but also your state so you can see how the infrastructure evolved over time.
»Prerequisites
This tutorial assumes that you're continuing from the previous tutorials. If not, create a directory named learn-terraform-aws-instance
and paste this code into
a file named example.tf
.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 2.70"
}
}
}
provider "aws" {
profile = "default"
region = "us-west-2"
}
resource "aws_instance" "example" {
ami = "ami-830c94e3"
instance_type = "t2.micro"
}
»Configuration
Now update the ami
of your instance. Change the aws_instance.example
resource under the provider block in example.tf
by replacing the current AMI ID with a new one.
Tip: The below snippet is formatted as a diff to give you context about what in your configuration should change. Replace the content displayed in red with the content displayed in green (exclude the leading +
and -
signs).
resource "aws_instance" "example" {
- ami = "ami-830c94e3"
+ ami = "ami-08d70e59c07c61a3a"
instance_type = "t2.micro"
}
This update changes the AMI to an Ubuntu 16.04 AMI. Terraform configurations are designed to be modified directly. and Terraform will know to destroy the old one.
»Apply Changes
After changing the configuration, run terraform apply
again to see how
Terraform will apply this change to the existing resources.
$ terraform apply
aws_instance.example: Refreshing state... [id=i-0f57d1b36088f27ae]
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# aws_instance.example must be replaced
-/+ resource "aws_instance" "example" {
~ ami = "ami-830c94e3" -> "ami-08d70e59c07c61a3a" # forces replacement
~ arn = "arn:aws:ec2:us-west-2:561656980159:instance/i-0f57d1b36088f27ae" -> (known after apply)
~ associate_public_ip_address = true -> (known after apply)
~ availability_zone = "us-west-2c" -> (known after apply)
~ cpu_core_count = 1 -> (known after apply)
~ cpu_threads_per_core = 1 -> (known after apply)
- disable_api_termination = false -> null
- ebs_optimized = false -> null
- hibernation = false -> null
+ host_id = (known after apply)
~ id = "i-0f57d1b36088f27ae" -> (known after apply)
~ instance_state = "running" -> (known after apply)
~ ipv6_address_count = 0 -> (known after apply)
~ ipv6_addresses = [] -> (known after apply)
+ key_name = (known after apply)
- monitoring = false -> null
+ network_interface_id = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
~ primary_network_interface_id = "eni-0b899ad3349b26177" -> (known after apply)
~ private_dns = "ip-172-31-7-180.us-west-2.compute.internal" -> (known after apply)
~ private_ip = "172.31.7.180" -> (known after apply)
~ public_dns = "ec2-52-40-175-176.us-west-2.compute.amazonaws.com" -> (known after apply)
~ public_ip = "52.40.175.176" -> (known after apply)
~ security_groups = [
- "default",
] -> (known after apply)
~ subnet_id = "subnet-31855d6c" -> (known after apply)
- tags = {} -> null
~ tenancy = "default" -> (known after apply)
~ volume_tags = {} -> (known after apply)
~ vpc_security_group_ids = [
- "sg-0edc8a5a",
] -> (known after apply)
# (3 unchanged attributes hidden)
- credit_specification {
- cpu_credits = "standard" -> null
}
+ ebs_block_device {
+ delete_on_termination = (known after apply)
+ device_name = (known after apply)
+ encrypted = (known after apply)
+ iops = (known after apply)
+ kms_key_id = (known after apply)
+ snapshot_id = (known after apply)
+ volume_id = (known after apply)
+ volume_size = (known after apply)
+ volume_type = (known after apply)
}
+ ephemeral_block_device {
+ device_name = (known after apply)
+ no_device = (known after apply)
+ virtual_name = (known after apply)
}
~ metadata_options {
~ http_endpoint = "enabled" -> (known after apply)
~ http_put_response_hop_limit = 1 -> (known after apply)
~ http_tokens = "optional" -> (known after apply)
}
+ network_interface {
+ delete_on_termination = (known after apply)
+ device_index = (known after apply)
+ network_interface_id = (known after apply)
}
~ root_block_device {
~ delete_on_termination = true -> (known after apply)
~ device_name = "/dev/sda1" -> (known after apply)
~ encrypted = false -> (known after apply)
~ iops = 0 -> (known after apply)
+ kms_key_id = (known after apply)
~ volume_id = "vol-0ae0e70f9b4874d59" -> (known after apply)
~ volume_size = 8 -> (known after apply)
~ volume_type = "standard" -> (known after apply)
}
}
Plan: 1 to add, 0 to change, 1 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:
The prefix -/+
means that Terraform will destroy and recreate
the resource, rather than updating it in-place. While some attributes
can be updated in-place (which are shown with the ~
prefix), changing the
AMI for an EC2 instance requires recreating it. Terraform handles these details
for you, and the execution plan makes it clear what Terraform will do.
Additionally, the execution plan shows that the AMI change is what required your resource to be replaced. Using this information, you can adjust your changes to possibly avoid destroy/create updates if they are not acceptable in some situations.
Once again, Terraform prompts for approval of the execution plan before
proceeding. Answer yes
to execute the planned steps:
Enter a value: yes
aws_instance.example: Destroying... [id=i-0f57d1b36088f27ae]
aws_instance.example: Still destroying... [id=i-0f57d1b36088f27ae, 10s elapsed]
aws_instance.example: Still destroying... [id=i-0f57d1b36088f27ae, 20s elapsed]
aws_instance.example: Still destroying... [id=i-0f57d1b36088f27ae, 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: Still creating... [40s elapsed]
aws_instance.example: Creation complete after 45s [id=i-0f0252485d3db35c8]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
As indicated by the execution plan, Terraform first destroyed the existing
instance and then created a new one in its place. You can use terraform show
again to see the new values associated with this instance.