DEV Community

loading...

Upgrading Terraform Destroy-Time Provisioners

jmarhee profile image Joseph D. Marhee Originally published at Medium on ・2 min read

With Terraform, you can specify when you’d like a resource applied. For example, using when = destroy , the following resource will only be run when that plan is targeted for a destroy:

data "external" "create_cluster" {
  program = ["python3", "${path.module}/scripts/create_cluster.py"]
  query = {
    du_fqdn = var.cluster_fqdn
    user = var.cluster_user
    pw = var.cluster_password
    tenant = var.cluster_tenant
    region = var.cluster_region
    cluster_name = var.cluster_name
    k8s_api_fqdn = metal_reserved_ip_block.cluster_ip.address
    allow_workloads_on_master = var.allow_workloads_on_master
  }
}

resource "null_resource" "delete_cluster" {

provisioner "local-exec" {
    when = destroy
    command = "printf '{\"du_fqdn\": \"${var..cluster_fqdn}\", \"user\": \"${var..cluster_user}\", \"pw\": \"${var..cluster_password}\", \"tenant\": \"${var..cluster_tenant}\", \"region\": \"${var..cluster_region}\", \"cluster_uuid\": \"${lookup(data.external.create_cluster.result, "cluster_id")}\"}' | python3 ${path.module}/scripts/delete_cluster.py"
    environment = {
      du_fqdn = var.cluster_fqdn
      user = var.cluster_user
      pw = var.cluster_password
      tenant = var.cluster_tenant
      region = var.cluster_region
      cluster_uuid = lookup(data.external.create_cluster.result, "cluster_id")
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

In this case, we want the result from our create_cluster datasource. However, in Terraform 0.13+, you can no longer reference data in this way, and when you try, you get a fatal error:

Error: Invalid reference from destroy provisioner

Destroy-time provisioners and their connection configurations may only
reference attributes of the related resource, via 'self', 'count.index',
or 'each.key'.

References to other resources during the destroy phase can cause dependency
cycles and interact poorly with create_before_destroy.
Enter fullscreen mode Exit fullscreen mode

This means that, if you want to continue to reference this external data inside of a destroy-time provisioner, in order to ensure these values are available, you just need to modify the above null_resource slightly:

First, we need to, before the provisioner block, define a map of triggers so when Terraform can reference these objects as part of its class when this destroy event occurs, so in order to, for example, reference your variables, your map should look like:

resource "null_resource" "delete_cluster" {

triggers = {
    cluster_uuid = lookup(data.external.create_cluster.result, "cluster_id")
    cluster_fqdn = var.cluster_fqdn
    cluster_user = var.cluster_user
    cluster_password = var.cluster_password
    cluster_tenant = var.cluster_tenant
    cluster_region = var.cluster_region
  }
Enter fullscreen mode Exit fullscreen mode

and then to reference them in the plan, you simply use self.triggers.[key] :

provisioner "local-exec" {
    when = destroy
    command = "printf '{\"du_fqdn\": \"${self.triggers.cluster_fqdn}\", \"user\": \"${self.triggers.cluster_user}\", \"pw\": \"${self.triggers.cluster_password}\", \"tenant\": \"${self.triggers.cluster_tenant}\", \"region\": \"${self.triggers.cluster_region}\", \"cluster_uuid\": \"${self.triggers.cluster_uuid}\"}' | python3 ${path.module}/scripts/delete_cluster.py"
    environment = {
      du_fqdn = self.triggers.cluster_fqdn
      user = self.triggers.cluster_user
      pw = self.triggers.cluster_password
      tenant = self.triggers.cluster_tenant
      region = self.triggers.cluster_region
      cluster_uuid = self.triggers.cluster_uuid
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

More about other Terraform upgrade (to 0.13 and beyond) concerns can be found here.

Discussion (0)

pic
Editor guide