Get Started - Google Cloud

Provision

The compute instance we launched at this point is based on the Google image given, but has no additional software installed or configuration applied.

GCP allows customers to manage their own custom operating system images . This can be a great way to ensure the instances you provision with Terraform are pre-configured based on your needs. Packer is the perfect tool for this and includes a builder for GCP.

In general, we recommend that you use a tool like Packer so that your infrastructure requires little or no provisioning after it's deployed. Managing your infrastructure this way is sometimes called immutable infrastructure, and can help ensure your infrastructure is more robust and fault tolerant.

That said, many infrastructures still require some sort of initialization or software provisioning step. Terraform uses provisioners to upload files, run shell scripts, or install and trigger other software like configuration management tools.

Defining a Provisioner

To define a provisioner, modify the resource block defining the first vm_instance in your configuration to look like the following.

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

  provisioner "local-exec" {
    command = "echo ${google_compute_instance.vm_instance.name}:  ${google_compute_instance.vm_instance.network_interface[0].access_config[0].nat_ip} >> ip_address.txt"
  }

  # ...
}

This adds a provisioner block within the resource block. Multiple provisioner blocks can be added to define multiple provisioning steps. Terraform supports many provisioners, but for this example we are using the local-exec provisioner.

The local-exec provisioner executes a command locally on the machine running Terraform, not the VM instance itself. We're using this provisioner versus the others so we don't have to worry about specifying any connection info right now.

This also shows a more complex example of string interpolation than we've seen before. Each VM instance can have multiple network interfaces, so we refer to the first one with network_interface[0] - counting starting from 0, as most programming languages do. Each network interface can have multiple access_config blocks as well, so once again we specify the first one.

Running Provisioners

The results of running terraform apply at this point may be confusing at first:

$ terraform apply
google_compute_network.vpc_network: Refreshing state... [id=terraform-network]
google_compute_address.vm_static_ip: Refreshing state... [id=hc-training-test/us-central1/terraform-static-ip]
google_storage_bucket.example_bucket: Refreshing state... [id=example-bucket-robin-jul-9-2019]
google_compute_instance.another_instance: Refreshing state... [id=terraform-instance-2]
google_compute_instance.vm_instance: Refreshing state... [id=terraform-instance]

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

Terraform found nothing to do - and if you check, you'll find that there's no ip_address.txt file on your local machine.

Terraform treats provisioners differently from other module arguments. Provisioners only run when a resource is created, but adding a provisioner does not force that resource to be destroyed and recreated. Use terraform taint to tell Terraform to recreate the instance.

$ terraform taint google_compute_instance.vm_instance
Resource instance google_compute_instance.vm_instance has been marked as tainted.

A tainted resource will be destroyed and recreated during the next apply. Run terraform apply now:

$ terraform apply
# ...

Terraform will perform the following actions:

  # google_compute_instance.vm_instance is tainted, so must be replaced
-/+ resource "google_compute_instance" "vm_instance" {

# ...

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: yes

# ...

google_compute_instance.vm_instance: Provisioning with 'local-exec'...
google_compute_instance.vm_instance (local-exec): Executing: ["/bin/sh" "-c" "echo 104.154.236.90 > ip_address.txt"]
google_compute_instance.vm_instance: Creation complete after 52s [id=terraform-instance]

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

Verify everything worked by looking at the contents of the ip_address.txt file.

$ cat ip_address.txt
terraform-instance: 35.194.46.141

It contains the IP address, just as we asked.

Failed Provisioners and Tainted Resources

If a resource is successfully created but fails a provisioning step, Terraform will error and mark the resource as tainted. A resource that is tainted still exists, but shouldn't be considered safe to use, since provisioning failed.

When you generate your next execution plan, Terraform will remove any tainted resources and create new resources, attempting to provision them again after creation.

Destroy Provisioners

Provisioners can also be defined that run only during a destroy operation. These are useful for performing system cleanup, extracting data, etc.

For many resources, using built-in cleanup mechanisms is recommended if possible (such as init scripts), but provisioners can be used if necessary.

The getting started guide won't show any destroy provisioner examples. If you need to use destroy provisioners, please see the provisioner documentation.