Get Started - Google Cloud

Resource Dependencies

In this section, you will learn more about resource dependencies and how to use resource parameters to share information about one resource with other resources.

Real-world infrastructure has a diverse set of resources and resource types. Terraform configurations can contain multiple resources, multiple resource types, and these types can even span multiple providers.

On this page, we'll show a basic example of how to configure multiple resources and how to use resource attributes to configure other resources.

If you've been following this track, you destroyed your resources in the last guide, and so terraform show will show no resources.

$ terraform show

Now recreate your network and instance by running terraform apply.

$ terraform apply

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
...

Plan: 2 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
...
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

After you respond to the prompt with yes, the resources will be created.

Assigning a Static IP Address

Now add to your configuration by assigning a static IP to the VM instance in main.tf.

resource "google_compute_address" "vm_static_ip" {
  name = "terraform-static-ip"
}

This should look familiar from the earlier example of adding a VM instance resource, except this time we're creating an "google_compute_address" resource type. This resource type allocates a reserved IP address to your project.

You can see what will be created with terraform plan:

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

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:
  + create

Terraform will perform the following actions:

  # google_compute_address.vm_static_ip will be created
  + resource "google_compute_address" "vm_static_ip" {
      + address            = (known after apply)
      + address_type       = "EXTERNAL"
      + creation_timestamp = (known after apply)
      + id                 = (known after apply)
      + name               = "terraform-static-ip"
      + network_tier       = (known after apply)
      + project            = (known after apply)
      + region             = (known after apply)
      + self_link          = (known after apply)
      + subnetwork         = (known after apply)
      + users              = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

Unlike terraform apply, the plan command will only show what would be changed, and never actually apply the changes directly. Notice that the only change you have made so far is to add a static IP. Next, you need to attach the IP address to your instance.

Update the network_interface configuration for your instance like so:

  network_interface {
    network = google_compute_network.vpc_network.self_link
    access_config {
      nat_ip = google_compute_address.vm_static_ip.address
    }
  }

The access_config block has several optional arguments, and in this case we'll set nat_ip to be the static IP address. When Terraform reads this configuration, it will:

  1. Ensure that vm_static_ip is created before vm_instance
  2. Save the properties of vm_static_ip in the state
  3. Set nat_ip to the value of the vm_static_ip.address property

Plan Changes

We'll run terraform plan again, but this time, let's save the plan:

$ terraform plan -out static_ip
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

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:
  + create
  ~ update in-place

Terraform will perform the following actions:

  # google_compute_address.vm_static_ip will be created
  + resource "google_compute_address" "vm_static_ip" {
      + address            = (known after apply)
      + address_type       = "EXTERNAL"
...

  # google_compute_instance.vm_instance will be updated in-place
  ~ resource "google_compute_instance" "vm_instance" {
        can_ip_forward       = false
        cpu_platform         = "Intel Haswell"
        deletion_protection  = false
...
          ~ access_config {
              ~ nat_ip       = "34.66.211.126" -> (known after apply)
                network_tier = "PREMIUM"
...
Plan: 1 to add, 1 to change, 0 to destroy.

------------------------------------------------------------------------

This plan was saved to: static_ip

To perform exactly these actions, run the following command to apply:
    terraform apply "static_ip"

Saving the plan this way ensures that we can apply exactly the same plan in the future. If we try to apply the file created by the plan, Terraform will first check to make sure the exact same set of changes will be made before applying the plan.

In this case, we can see that Terraform will create a new google_compute_address and update the existing VM to use it.

Apply Changes

Run terraform apply "static_ip" to see how Terraform plans to apply this change. The output will look similar to the following:

$ terraform apply "static_ip"
google_compute_address.vm_static_ip: Creating...
google_compute_address.vm_static_ip: Creation complete after 5s [id=hc-training-test/us-central1/terraform-static-ip]
google_compute_instance.vm_instance: Modifying... [id=terraform-instance]
google_compute_instance.vm_instance: Still modifying... [id=terraform-instance, 10s elapsed]
google_compute_instance.vm_instance: Still modifying... [id=terraform-instance, 20s elapsed]
google_compute_instance.vm_instance: Modifications complete after 28s [id=terraform-instance]

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

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate

As shown above, Terraform created the static IP before modifying the VM instance. Due to the interpolation expression that passes the IP address to the instance's network interface configuration, Terraform is able to infer a dependency, and knows it must create the static IP before updating the instance.

Implicit and Explicit Dependencies

By studying the resource attributes used in interpolation expressions, Terraform can automatically infer when one resource depends on another. In the example above, the reference to google_compute_address.vm_static_ip.address creates an implicit dependency on the google_compute_address named vm_static_ip.

Terraform uses this dependency information to determine the correct order in which to create and update different resources. In the example above, Terraform knows that the vm_static_ip must be created before the vm_instance is updated to use it.

Implicit dependencies via interpolation expressions are the primary way to inform Terraform about these relationships, and should be used whenever possible.

Sometimes there are dependencies between resources that are not visible to Terraform. The depends_on argument can be added to any resource and accepts a list of resources to create explicit dependencies for.

For example, perhaps an application we will run on our instance expects to use a specific Cloud Storage bucket, but that dependency is configured inside the application code and thus not visible to Terraform. In that case, we can use depends_on to explicitly declare the dependency.

Add a Cloud Storage bucket and an instance with an explicit dependency on the bucket by adding the following to main.tf.

# New resource for the storage bucket our application will use.
resource "google_storage_bucket" "example_bucket" {
  name     = "<UNIQUE-BUCKET-NAME>"
  location = "US"

  website {
    main_page_suffix = "index.html"
    not_found_page   = "404.html"
  }
}

# Create a new instance that uses the bucket
resource "google_compute_instance" "another_instance" {
  # Tells Terraform that this VM instance must be created only after the
  # storage bucket has been created.
  depends_on = [google_storage_bucket.example_bucket]

  name         = "terraform-instance-2"
  machine_type = "f1-micro"

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

  network_interface {
    network = google_compute_network.vpc_network.self_link
    access_config {
    }
  }
}

Google storage buckets must be globally unique. Because of this, you will need to replace <UNIQUE-BUCKET-NAME> with a unique, valid name for a bucket. Using your name and the date is usually a good way to guess a unique bucket name. For example:

  bucket_name = "robin-example-2020-01-24"

You may wonder where in your configuration these resources should go. The order that resources are defined in a terraform configuration file has no effect on how Terraform applies your changes. Organize your configuration files in a way that makes the most sense for you and your team.

Now run terraform plan and terraform apply to see these changes in action.

Before moving on, you can remove these new resources from your configuration and run terraform apply once again to destroy them. We won't use the bucket or the second instance any further in the getting started guide.