Null resources depends_on doesn't work on replacement

Hi,
I am having issues with null provision depend_on strategies. I am trying to recreate the null provision resource if any changes happens to template file that’s used during the provision. The resources in which the template file changed performs the force replacement correctly. However the dependent resources are not recreated.

Terraform Version

0.12.26

Terraform Configuration Files

  • resource 1:
data "template_file" "disk_tmp_file" {
    count       =   var.node_count
    template    =   file(var.script_source_tpl)
    vars = {
        disk                =   var.disk
    }
}

resource "null_resource" "first_resource" {
    count       =   var.node_count
    triggers    =   {
        private_ip  =   element(var.private_ip,count.index)
        data        =   element(data.template_file.disk_tmp_file.*.rendered,count.index)
    }
    connection {
        host            =   element(var.private_ip,count.index)
        type            =   var.login_type
        user            =   var.user_name
        password        =   var.instance_password
        port            =   var.port
    }
    provisioner "file" {
        content     =   element(data.template_file.disk_tmp_file.*.rendered,count.index)
        destination =   var.script_destination
    }
}

  • resource 2:
resource "null_resource" "second_resource" {
    depends_on  =   [var.md_depends_on]
}

  • module 1:
module "first_module" {
    source     = ${resource1}
    script_source_tpl               =   "../../scripts/${var.platform}/diskPart.tpl"
}
  • module 2:
module "second_module" {
    source     = ${resource2}
    md_depends_on                       =   [module.first_module]
}
  • md_depends_on var:
variable "md_depends_on" {
    type    =   any
    default =   null
}

Expected Behavior

On template change - Should recreate both the resources - resource 1 and resource 2

Actual Behavior

On template change - Updates only the resource 1

Additional Context

Dependency works perfectly if I create or destroy resources. The problem is only if the template file changes and triggers tries to update the resource.

Hi @Dineshk77,

The depends_on meta attribute only creates dependencies between resources, it does not force any changes upon a resource. This means if the resource has no changes, depends_on does not have any effect.

The only way to recreate a resource automatically like this is to assign a change to a field that forces the recreation of that resource. In the null_resource case, that is the triggers attribute.

@jbardin In my above example (resource 2 depends on resource1) : resource 1 has changed due to a change in triggers. However the resource 2 didn’t recreate. if resource1 recreated then I believe resource 2 has to be recreated ? (correct me if I am wrong).

You will need to include a triggers map in null_resource.second_resource which includes a value that changes when null_resource.first_resource is replaced.

As @jbardin noted, the depends_on only ensures that null_resource.second_resource will be created after null_resource.first_resource. depends_on alone can never cause an action to be taken: it only decides on the ordering of actions Terraform was going to take for other reasons.

For example, you could add a second_resource_triggers input variable to your second module and then use it as the triggers:

variable "second_resource_triggers" {
  type    = map(string)
  default = {}
}

resource "null_resource" "second_resource" {
  triggers = var.second_resource_triggers
}

With that said, I can only show a contrived example because the example you shared is also contrived. If you can say more about what your final goal is – ignoring for the moment how you will describe that to Terraform – I can maybe give a different suggestion that doesn’t involve exposing the implementation detail that the second module contains a null_resource resource.

Thanks for the detailed explanation. Understanding that depends_on attribute only decides the order of actions. However below actions produce different outcomes.

Resource details:

  • null_resource 1
  • null_resource 2 (depends on null_resource 1)

lets say : terraform destroy -target=null_resource1

  • this will delete both resource 1 and resource 2, because they are dependent

lets say - triggers attribute changed in null_resource1

  • sample trigger change plan
  # module.ecs_prov_choco.null_resource.run_prov must be replaced
-/+ resource "null_resource" "null_resource1" {
      ~ id       = "****" -> (known after apply)
      ~ triggers = { # forces replacement
          + "data"       = <<~EOT
                :InstallDependencies
                    @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "[System.Net.ServicePointManager]::SecurityProtocol = 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
                    if %errorlevel% neq 0 exit /b %errorlevel%
            EOT
          ~ "private_ip" = "***" -> "****"
        }
    }

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

  • it just destroys only the null_resource1. isn’t supposed to destroy the dependent resource as well (in this case - null_resource2)?

May I know why the behaviour here is different?.

@apparentlymart any update on this?. It will be helpful if you can share info on the behaviour I have mentioned. Thanks

@Dineshk77,

What you are seeing here is due to the behavior of -target with destroy. The destroy command indicates that terraform intends to destroy all resources, then -target reduces the graph down to the minimum set of downstream dependencies starting at the targeted resources. This means that the plan for null_resource2 will be to destroy it.

When you run a normal plan, terraform’s goal is to make the minimum set of changes necessary, rather than the goal of destroying all resources. This means the resources specified by -target must only retain all upstream dependencies, and the change to null_resource2 is ignored.

While the defined behavior of -target is intended to cover the most common use cases without creating invalid situations, that behavior is still somewhat arbitrary. I believe there are open feature requests to add more options for flexibility, but without some more research it’s unclear exactly what should be implement in that area, and more likely a new workflow needs to be conceived to cover more specific use cases. I generally recommend avoiding -target whenever possible, and using changes in the configuration itself to drive terraform.