TIPS: Howto implement Module depends_on emulation

Good morning. I tought I would share a workaround I put together to implement a missing “depends_on” support for modules. It is fairly simple and will be easy to swap out once Terraform implement built-in depends_on support.

1st, create a file called something like modult_depends_on.tf and throw the following into it:

/*
    Add the following line to the resource in this module that depends on the completion of external module components:

    depends_on = ["null_resource.module_depends_on"]

    This will force Terraform to wait until the dependant external resources are created before proceeding with the creation of the
    resource that contains the line above.

    This is a hack until Terraform officially support module depends_on.
*/

variable "module_depends_on" {
  default = [""]
}

resource "null_resource" "module_depends_on" {
  triggers = {
    value = "${length(var.module_depends_on)}"
  }
}

Next, follow the commented section and add the “depends_on = [“null_resource.module_depends_on”]” line to the resource in your module you want Terraform to wait for. In my case, it was the in the azurerm_virtual_machine resource:

resource azurerm_virtual_machine VM {
  name                             = "${var.name}"
  depends_on                       = ["null_resource.module_depends_on"]
...
...
}

Finally, when you call the module in a Terraform tf file you simply do it like this. The interesting line is the one called module_depends_on. Put as many resource references you need in this list of resources:

module "jumpbox01" {
  source = "github.com/canada-ca-terraform-modules/terraform-azurerm-basicwindowsvm?ref=20190809.1"

  module_depends_on                 = ["${module.FWCore01.firewall}", "${module.FWMGMT01.firewall}"]
  name                              = "${var.envprefix}-jumpbox01"

...
...
}

Thanks for sharing this tip, @bernardmaltais!

The key insight here is that variables are nodes in the dependency graph too, and so you can use them as a “hub” for passing dependencies across the module boundary.

If using Terraform 0.12, this pattern is more straightforward because depends_on can refer directly to variables in Terraform 0.12 and later:

variable "vm_depends_on" {
  type    = any
  default = null
}

resource "azure_virtual_machine" "example" {
  depends_on = [var.vm_depends_on]

  # ...
}

Since this variable is being used only for its dependencies and not for its value, I defined it as having type any to get the most flexibility. The caller of this module can then use vm_depends_on in the same way as the first-class depends_on meta-argument:

module "example" {
  source = "..."

  vm_depends_on = [module.fw_core01.firewall]
}

One nice side-benefit of this approach is that you can potentially offer multiple different depends_on-like variables so that different parts of the module can have different dependencies. Keeping the dependencies tightly scoped will improve the performance of terraform apply because it will allow more actions to potentially run concurrently, but of course that comes at the expense of some usability for the caller of having to think about the dependencies of different parts of the module separately.

2 Likes

Thank you for this improved solution! Much cleaner. I will implement this in my modules in the future.

@bernardmaltais @apparentlymart

I am also looking for the same solution but would like to understand before proceeding.

I have the following implementation:

main.tf
vpc.tf
public_subnet.tf
ec2.tf

I would like the execution order else I am getting error as ec2 executes and is not able to find the VPC.

So here

ec2 is dependent on public_subnet and public_subnet is dependent of vpc

based upon the above recommendation,

I will need the following in public_subnet.tf

variable “vm_depends_on” {
type = any
default = null
}
resource aws_subnet “public_subnet”{
count=2
vpc_id=var.vpc_id
depends_on = [var.vm_depends_on]

And in main.tf

I will need to add,

variable “vm_depends_on” {
type = any
default = null
}

module “pub_sub” {
source = “…/mod/pub_sub”
vpc_id = “${module.my_vpc.id}”
vm_depends_on = [module.< What will be put here > ]
}

Is my approach correct ?

Thanks