Getting Started - Google Cloud Platform

Change Infrastructure

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

We can add resources to our configuration. We'll create a compute instance by adding the following 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 {
    }
  }
}

We're setting a few more arguments this time. 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, we'll use a Debian operating system, and the VPC Network we created earlier. Notice how we refer 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.

We can now apply that change:

$ terraform apply
google_compute_network.vpc_network: Refreshing state... [id=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)
      + 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)
          + source                     = (known after apply)

          + initialize_params {
              + image = "debian-cloud/debian-9"
              + size  = (known after apply)
              + type  = (known after apply)
            }
        }

      + network_interface {
          + address            = (known after apply)
          + name               = (known after apply)
          + network            = "https://www.googleapis.com/compute/v1/projects/hc-training-test/global/networks/terraform-network"
          + network_ip         = (known after apply)
          + subnetwork         = (known after apply)
          + subnetwork_project = (known after apply)

          + access_config {
              + assigned_nat_ip = (known after apply)
              + 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: yes

google_compute_instance.vm_instance: Creating...
google_compute_instance.vm_instance: Still creating... [10s elapsed]
google_compute_instance.vm_instance: Still creating... [20s elapsed]
google_compute_instance.vm_instance: Still creating... [30s elapsed]
google_compute_instance.vm_instance: Still creating... [40s elapsed]
google_compute_instance.vm_instance: Creation complete after 41s [id=terraform-instance]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Once again, answer yes to the confirmation prompt.

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

Adding new resources is one thing. Terraform also supports changing resources. Add a "tags" argument to your "vm_instance" so that it looks like this:

resource "google_compute_instance" "vm_instance" {
  name         = "terraform-instance"
  machine_type = "f1-micro"
  tags         = ["web", "dev"]

# ...

Run terraform apply again. You should see output like this:

$ terraform apply
google_compute_network.vpc_network: Refreshing state... [id=terraform-network]
google_compute_instance.vm_instance: Refreshing state... [id=terraform-instance]

...

Terraform will perform the following actions:

  # google_compute_instance.vm_instance will be updated in-place
  ~ resource "google_compute_instance" "vm_instance" {

...

~ tags                 = [
    + "dev",
    + "web",
  ]

...

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

Let's change the disk image of our instance. Edit the boot_disk block inside your vm_instance resource your configuration file and change it to the following:

  boot_disk {
    initialize_params {
      image = "cos-cloud/cos-stable"
    }
  }

We've changed the boot disk from being a Debian 9 image to use Google's Container-Optimized OS. After editing the configuration, run terraform apply again to see how Terraform will apply this change to the existing resources.

$ terraform apply
google_compute_network.vpc_network: Refreshing state... [id=terraform-network]
google_compute_instance.vm_instance: Refreshing state... [id=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" {
        can_ip_forward       = false
      ~ cpu_platform         = "Intel Haswell" -> (known after apply)
        deletion_protection  = false
      ~ guest_accelerator    = [] -> (known after apply)
      ~ id                   = "terraform-instance" -> (known after apply)
      ~ instance_id          = "2506428060139560363" -> (known after apply)
      ~ label_fingerprint    = "42WmSpB8rSM=" -> (known after apply)
      - labels               = {} -> null
        machine_type         = "f1-micro"
      - metadata             = {} -> null
      ~ metadata_fingerprint = "AWPE2PjCWIY=" -> (known after apply)
        name                 = "terraform-instance"
      ~ project              = "hc-training-test" -> (known after apply)
      ~ self_link            = "https://www.googleapis.com/compute/v1/projects/hc-training-test/zones/us-central1-c/instances/terraform-instance" -> (known after apply)
      - tags                 = [] -> null
      ~ tags_fingerprint     = "42WmSpB8rSM=" -> (known after apply)
      ~ zone                 = "us-central1-c" -> (known after apply)

      ~ boot_disk {
            auto_delete                = true
          ~ device_name                = "persistent-disk-0" -> (known after apply)
          + disk_encryption_key_sha256 = (known after apply)
          ~ source                     = "https://www.googleapis.com/compute/v1/projects/hc-training-test/zones/us-central1-c/disks/terraform-instance" -> (known after apply)

          ~ initialize_params {
              ~ image = "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-9-stretch-v20190618" -> "cos-cloud/cos-stable" # forces replacement
              ~ size  = 10 -> (known after apply)
              ~ type  = "pd-standard" -> (known after apply)
            }
        }

      ~ network_interface {
          + address            = (known after apply)
          ~ name               = "nic0" -> (known after apply)
            network            = "https://www.googleapis.com/compute/v1/projects/hc-training-test/global/networks/terraform-network"
          ~ network_ip         = "10.128.0.2" -> (known after apply)
          ~ subnetwork         = "https://www.googleapis.com/compute/v1/projects/hc-training-test/regions/us-central1/subnetworks/terraform-network" -> (known after apply)
          ~ subnetwork_project = "hc-training-test" -> (known after apply)

          ~ access_config {
              + assigned_nat_ip = (known after apply)
              ~ nat_ip          = "34.66.211.126" -> (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 a VM 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=terraform-instance]
google_compute_instance.vm_instance: Still destroying... [id=terraform-instance, 10s elapsed]
google_compute_instance.vm_instance: Still destroying... [id=terraform-instance, 20s elapsed]
google_compute_instance.vm_instance: Still destroying... [id=terraform-instance, 30s elapsed]
google_compute_instance.vm_instance: Still destroying... [id=terraform-instance, 40s elapsed]
google_compute_instance.vm_instance: Still destroying... [id=terraform-instance, 50s elapsed]
google_compute_instance.vm_instance: Destruction complete after 57s
google_compute_instance.vm_instance: Creating...
google_compute_instance.vm_instance: Still creating... [10s elapsed]
google_compute_instance.vm_instance: Still creating... [20s elapsed]
google_compute_instance.vm_instance: Creation complete after 29s [id=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.