Terraform
Change infrastructure
In the previous tutorial, you created your first infrastructure with Terraform: a VPC network. In this tutorial, you will modify your configuration, and learn how to apply changes to your Terraform projects.
Infrastructure is continuously evolving, and Terraform was built to help manage resources over their lifecycle. When you update Terraform configurations, Terraform builds an execution plan that only modifies what is necessary to reach your desired state.
When using Terraform in production, we recommend that you use a version control system to manage your configuration files, and store your state in a remote backend such as HCP Terraform or Terraform Enterprise.
Note
This tutorial builds on configuration from the previous tutorial. If you have not yet completed it, do so now.
Create a new resource
You can create new resources by adding them to your Terraform configuration and
running terraform apply
to provision them.
Add the following configuration for 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-11"
}
}
network_interface {
network = google_compute_network.vpc_network.name
access_config {
}
}
}
Note
The order in which resources are defined in your configuration files does not affect how Terraform provisions your resources, so you should organize your configuration however is easiest for you and your team to manage.
This new resource includes a few 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 supported arguments for the resource in the
GCP provider documentation.
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
. This establishes a dependency
between the two resources. Terraform builds a dependency graph of the
resources it manages to ensure they are created in appropriate order.
The presence of the access_config
block, even without any arguments, gives
the VM an external IP address, making it accessible over the internet.
Tip
All traffic to instances, even from other instances, is blocked by
the firewall unless firewall rules are created to allow it. Add a
google_compute_firewall
resource to allow traffic to access your instance.
Now run terraform apply
to create the compute instance. Terraform will prompt you to confirm the operation.
$ terraform apply
google_compute_network.vpc_network: Refreshing state... [id=projects/hc-16016bbc5bd4494da8c084045df/global/networks/terraform-network]
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:
# google_compute_instance.vm_instance will be created
+ resource "google_compute_instance" "vm_instance" {
+ can_ip_forward = false
+ cpu_platform = (known after apply)
+ current_status = (known after apply)
+ deletion_protection = false
+ effective_labels = {
+ "goog-terraform-provisioned" = "true"
}
+ 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 = "hc-16016bbc5bd4494da8c084045df"
+ self_link = (known after apply)
+ tags_fingerprint = (known after apply)
+ terraform_labels = {
+ "goog-terraform-provisioned" = "true"
}
+ zone = "us-central1-c"
+ 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-11"
+ labels = (known after apply)
+ provisioned_iops = (known after apply)
+ provisioned_throughput = (known after apply)
+ resource_policies = (known after apply)
+ size = (known after apply)
+ type = (known after apply)
}
}
+ network_interface {
+ internal_ipv6_prefix_length = (known after apply)
+ ipv6_access_type = (known after apply)
+ ipv6_address = (known after apply)
+ name = (known after apply)
+ network = "terraform-network"
+ network_ip = (known after apply)
+ stack_type = (known after apply)
+ subnetwork = (known after apply)
+ subnetwork_project = (known after apply)
+ access_config {
+ nat_ip = (known after apply)
+ network_tier = (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 — you added a google_compute_instance
resource named vm_instance
to the configuration, and Terraform created the
resource in GCP.
Modify configuration
In addition to creating resources, Terraform can also make changes to those resources.
Add a tags
argument to your vm_instance
resource block.
Tip
The below snippet is formatted as a diff to give you context about what in your configuration should change. Add the content in green (exclude the leading +
sign).
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
apply this change now by responding yes
, and Terraform will add the
tags to your instance.
Introduce 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 does not support updating the resource in the way described by your configuration.
Changing the disk image of your 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.
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).
boot_disk {
initialize_params {
- image = "debian-cloud/debian-11"
+ image = "cos-cloud/cos-stable"
}
}
This modification changes the boot disk from a Debian 11 image to Google's Container-Optimized OS.
Now run terraform apply
again. Terraform will replace the instance because
Google Cloud Platform does not 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-11-bullseye-v20220822" -> "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. Terraform and the GCP provider handle these
details for you, and the execution plan reports what Terraform will do.
Additionally, the execution plan shows that the disk image change is the modification that forced the instance replacement. Using this information, you can adjust your changes to possibly avoid destructive updates if they are not acceptable.
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.