In the previous page, you created your first infrastructure with Terraform: a VPC network. In this page, we're going to modify your configuration, 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 evolves over time.
»Adding Resources
You can add new resources by adding them to your Terraform configuration and
running terraform apply
to provision them.
First, add a google compute instance resource to main.tf
.
resource "google_compute_instance" "vm_instance" {
name = "terraform-instance"
machine_type = "f1-micro"
boot_disk {
initialize_params {
image = "debian-cloud/debian-9"
}
}
network_interface {
network = google_compute_network.vpc_network.name
access_config {
}
}
}
Note: The order in which resources are defined in your configuration files doesn't affect how Terraform provisions your resources, so you should organize your configuration in ways that make it easy for you and your team to manage.
This resource includes a few more arguments. The name and machine type are
simple strings, but boot_disk
and network_interface
are more complex blocks.
You can see all of the available options in the
documentation.
For this example, your compute instance will use a Debian operating system, and
will be connected to the VPC Network you created earlier. Notice how this
configuration refers to the network's name property with
google_compute_network.vpc_network.name
--
google_compute_network.vpc_network
is the ID, matching the values in the block
that defines the network, and name
is a property of that resource.
The presence of the access_config
block, even without any arguments, ensures
that the instance will be accessible over the internet.
Now run terraform apply
to create the compute instance. Remember to confirm your apply with a yes
.
$ terraform apply
google_compute_network.vpc_network: Refreshing state... [id=projects/testing-project/global/networks/terraform-network]
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# google_compute_instance.vm_instance will be created
+ resource "google_compute_instance" "vm_instance" {
+ can_ip_forward = false
+ cpu_platform = (known after apply)
+ deletion_protection = false
+ guest_accelerator = (known after apply)
+ id = (known after apply)
+ instance_id = (known after apply)
+ label_fingerprint = (known after apply)
+ machine_type = "f1-micro"
+ metadata_fingerprint = (known after apply)
+ min_cpu_platform = (known after apply)
+ name = "terraform-instance"
+ project = (known after apply)
+ self_link = (known after apply)
+ tags_fingerprint = (known after apply)
+ zone = (known after apply)
+ boot_disk {
+ auto_delete = true
+ device_name = (known after apply)
+ disk_encryption_key_sha256 = (known after apply)
+ kms_key_self_link = (known after apply)
+ mode = "READ_WRITE"
+ source = (known after apply)
+ initialize_params {
+ image = "debian-cloud/debian-9"
+ labels = (known after apply)
+ size = (known after apply)
+ type = (known after apply)
}
}
+ network_interface {
+ name = (known after apply)
+ network = "terraform-network"
+ network_ip = (known after apply)
+ subnetwork = (known after apply)
+ subnetwork_project = (known after apply)
+ access_config {
+ nat_ip = (known after apply)
+ network_tier = (known after apply)
}
}
+ scheduling {
+ automatic_restart = (known after apply)
+ on_host_maintenance = (known after apply)
+ preemptible = (known after apply)
+ node_affinities {
+ key = (known after apply)
+ operator = (known after apply)
+ values = (known after apply)
}
}
}
Plan: 1 to add, 0 to change, 0 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:
Once again, answer yes
to the confirmation prompt.
Enter a value: yes
google_compute_instance.vm_instance: Creating...
google_compute_instance.vm_instance: Still creating... [10s elapsed]
google_compute_instance.vm_instance: Creation complete after 11s [id=projects/testing-project/zones/us-central1-c/instances/terraform-instance]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
This is a fairly straightforward change - we added a "google_compute_instance" resource named "vm_instance" to our configuration, and Terraform created the resource in GCP.
»Changing Resources
In addition to creating resources, Terraform can also make changes to those resources.
Add a "tags" argument to your "vm_instance" resource block.
resource "google_compute_instance" "vm_instance" {
name = "terraform-instance"
machine_type = "f1-micro"
+ tags = ["web", "dev"]
# ...
}
Run terraform apply
again.
$ terraform apply
google_compute_network.vpc_network: Refreshing state... [id=projects/testing-project/global/networks/terraform-network]
google_compute_instance.vm_instance: Refreshing state... [id=projects/testing-project/zones/us-central1-c/instances/terraform-instance]
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:
# google_compute_instance.vm_instance will be updated in-place
~ resource "google_compute_instance" "vm_instance" {
id = "projects/testing-project/zones/us-central1-c/instances/terraform-instance"
name = "terraform-instance"
~ tags = [
+ "dev",
+ "web",
]
# (15 unchanged attributes hidden)
# (3 unchanged blocks hidden)
}
Plan: 0 to add, 1 to change, 0 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 update the resource in-place. You can
go and and apply this change now by responding yes
, and Terraform will add the
tags to your instance.
»Destructive Changes
A destructive change is a change that requires the provider to replace the existing resource rather than updating it. This usually happens because the cloud provider doesn't support updating the resource in the way described by your configuration.
Changing the disk image of our instance is one example of a destructive change.
Edit the boot_disk
block inside the vm_instance
resource in your
configuration file to change the image parameter as follows.
boot_disk {
initialize_params {
- image = "debian-cloud/debian-9"
+ image = "cos-cloud/cos-stable"
}
}
This will change the boot disk from being a Debian 9 image to use Google's Container-Optimized OS.
Now run terraform apply
again. Terraform will replace the instance because
Google Cloud Platform doesn't support replacing the boot disk image on a running
instance.
$ terraform apply
google_compute_network.vpc_network: Refreshing state... [id=projects/testing-project/global/networks/terraform-network]
google_compute_instance.vm_instance: Refreshing state... [id=projects/testing-project/zones/us-central1-c/instances/terraform-instance]
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:
# google_compute_instance.vm_instance must be replaced
-/+ resource "google_compute_instance" "vm_instance" {
~ cpu_platform = "Intel Haswell" -> (known after apply)
- enable_display = false -> null
~ guest_accelerator = [] -> (known after apply)
~ id = "projects/testing-project/zones/us-central1-c/instances/terraform-instance" -> (known after apply)
~ instance_id = "3944132344841508591" -> (known after apply)
~ label_fingerprint = "42WmSpB8rSM=" -> (known after apply)
- labels = {} -> null
- metadata = {} -> null
~ metadata_fingerprint = "uvlMXYfy7lg=" -> (known after apply)
+ min_cpu_platform = (known after apply)
name = "terraform-instance"
~ project = "testing-project" -> (known after apply)
~ self_link = "https://www.googleapis.com/compute/v1/projects/testing-project/zones/us-central1-c/instances/terraform-instance" -> (known after apply)
tags = [
"dev",
"web",
]
~ tags_fingerprint = "XaeQnaHMn9Y=" -> (known after apply)
~ zone = "us-central1-c" -> (known after apply)
# (3 unchanged attributes hidden)
~ boot_disk {
~ device_name = "persistent-disk-0" -> (known after apply)
+ disk_encryption_key_sha256 = (known after apply)
+ kms_key_self_link = (known after apply)
~ source = "https://www.googleapis.com/compute/v1/projects/testing-project/zones/us-central1-c/disks/terraform-instance" -> (known after apply)
# (2 unchanged attributes hidden)
~ initialize_params {
~ image = "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-9-stretch-v20201112" -> "cos-cloud/cos-stable" # forces replacement
~ labels = {} -> (known after apply)
~ size = 10 -> (known after apply)
~ type = "pd-standard" -> (known after apply)
}
}
~ network_interface {
~ name = "nic0" -> (known after apply)
~ network = "https://www.googleapis.com/compute/v1/projects/testing-project/global/networks/terraform-network" -> "terraform-network"
~ network_ip = "10.128.0.2" -> (known after apply)
~ subnetwork = "https://www.googleapis.com/compute/v1/projects/testing-project/regions/us-central1/subnetworks/terraform-network" -> (known after apply)
~ subnetwork_project = "testing-project" -> (known after apply)
~ access_config {
~ nat_ip = "34.123.44.123" -> (known after apply)
~ network_tier = "PREMIUM" -> (known after apply)
}
}
~ scheduling {
~ automatic_restart = true -> (known after apply)
~ on_host_maintenance = "MIGRATE" -> (known after apply)
~ preemptible = false -> (known after apply)
+ node_affinities {
+ key = (known after apply)
+ operator = (known after apply)
+ values = (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 boot disk image for an
instance requires recreating it. Terraform and the GCP provider handle these
details for you, and the execution plan makes it clear what Terraform will do.
Additionally, the execution plan shows that the disk image change is what required our instance 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
google_compute_instance.vm_instance: Destroying... [id=projects/testing-project/zones/us-central1-c/instances/terraform-instance]
google_compute_instance.vm_instance: Still destroying... [id=projects/testing-project/zones/...entral1-c/instances/terraform-instance, 10s elapsed]
google_compute_instance.vm_instance: Destruction complete after 16s
google_compute_instance.vm_instance: Creating...
google_compute_instance.vm_instance: Still creating... [10s elapsed]
google_compute_instance.vm_instance: Creation complete after 18s [id=projects/testing-project/zones/us-central1-c/instances/terraform-instance]
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.