<p><strong>Update: As of TF 0.13x, <code>depends_on</code> is supported to create dependencies between modules. Using <code>depends_on</code> should be preferable over using the method detailed below.</strong></p>
<p>The previous most popular answer is out of date with Terraform 0.12.24.</p>
<p><code>depends_on</code> is a protected variable, and cannot be used in a <code>module</code>.
In addition there are a few syntax differences.</p>
<p>I've updated the example below.</p>
<pre><code># ROOT level main.tf
# -------------------------------------------------------------------
# Create NAT Gateway - Associates EIP as well
# -------------------------------------------------------------------
module &quot;vpc_nat_gateway&quot; {
  source            = &quot;./vpc_nat_gateway&quot;
  vpc_id            = module.vpc.id
  public_subnet_ids = module.vpc_subnets.public_subnet_ids
  private_cidr      = var.private_cidr
  common_tags       = local.common_tags
}

# -------------------------------------------------------------------
# Create Private Routes
# -------------------------------------------------------------------
module &quot;vpc_private_route&quot; {
  source         = &quot;./vpc_private_route&quot;
  vpc_id.        = module.vpc.id
  nat_gateway_id = module.vpc_nat_gateway.nat_gateway_id
  common_tags    = local.common_tags

  # Depends is a custom variable, depends_on is a reserved keyword.
  depends = [module.vpc_nat_gateway.nat_gateway_id]
}
</code></pre>
<pre><code># vpc_private_route module - main.tf
variable &quot;depends&quot; {
  default = []
}

resource &quot;null_resource&quot; &quot;depends_on&quot; {
  triggers = {
    depends_on = &quot;${join(&quot;&quot;, var.depends)}&quot;
  }
}

data &quot;aws_nat_gateway&quot; &quot;az1&quot; {
  vpc_id = var.vpc_id

  depends_on = [
    null_resource.depends_on
  ]
}

data &quot;aws_nat_gateway&quot; &quot;az2&quot; {
  vpc_id = var.vpc_id

  depends_on = [
    null_resource.depends_on
  ]
}
</code></pre>

CC BY-SA 4.0
2020-04-08T17:16:42.510Z
2022-05-13T21:42:19.407Z
2022-05-13T21:42:19.407Z
MrPooh
<p>These are hacks (understandably as you couldn't explicitly do this in Terraform &lt; 0.13).</p>
<p>Now however, with 0.13, we have <code>depends_on</code> for modules: <a href="https://github.com/hashicorp/terraform/blob/v0.13/CHANGELOG.md#0130-august-10-2020" rel="noreferrer">https://github.com/hashicorp/terraform/blob/v0.13/CHANGELOG.md#0130-august-10-2020</a></p>

CC BY-SA 4.0
2020-11-27T08:17:37.523Z
2020-11-27T08:17:37.523Z
Snowcrash
<p>There is a hacky workaround for getting Terraform to do module dependencies. You can force the module to be aware of the Terraform calling it like this:</p>

<pre><code># ROOT level main.tf
# -------------------------------------------------------------------
# Create NAT Gateway - Associates EIP as well
# -------------------------------------------------------------------
module "vpc_nat_gateway" {
  source            = "./vpc_nat_gateway"
  vpc_id            = "${ module.vpc.id }"
  public_subnet_ids = "${ module.vpc_subnets.public_subnet_ids }"
  private_cidr      = "${ var.private_cidr }"
  common_tags       = "${ local.common_tags }"
}

# -------------------------------------------------------------------
# Create Private Routes
# -------------------------------------------------------------------
module "vpc_private_route" {
  source         = "./vpc_private_route"
  vpc_id.        = "${ module.vpc.id }"
  nat_gateway_id = "${ module.vpc_nat_gateway.nat_gateway_id }"
  common_tags    = "${ local.common_tags }"

  depends_on = ["${module.vpc_nat_gateway.nat_gateway_id}"]
}
</code></pre>

<pre><code># vpc_private_route module - main.tf
variable "depends_on" {
  default = []
}

resource "null_resource" "depends_on" {
  triggers {
    depends_on = "${join("", var.depends_on)}"
  }
}

data "aws_nat_gateway" "az1" {
  vpc_id = "${ var.vpc_id }"

  tags {
    Name = "*NAT GW AZ 1"
  }

  depends_on = [
    "null_resource.depends_on"
  ]
}

data "aws_nat_gateway" "az2" {
  vpc_id = "${ var.vpc_id }"

  tags {
    Name = "*NAT GW AZ 2"
  }

  depends_on = [
    "null_resource.depends_on"
  ]
}
</code></pre>

<p>It adds a lot of boilerplate but you will get the desired effect.</p>

CC BY-SA 4.0
2019-02-02T02:17:43.250Z
Thank you @Briansbum for this input and excellent example. I will try this my next day in office and report back.
Steven K7FAQ
CC BY-SA 4.0
2019-02-01T08:49:57.990Z
2019-02-01T08:49:57.990Z
Briansbum
<p>I've not tried this, but you could have a null_resource with a local-exec running a sleep command and make your resource dependent on that resource with a depends_on lifecycle argument.</p>

CC BY-SA 4.0
2024-05-09T13:33:11.223Z
2024-05-09T13:33:11.223Z
Aaron Brockmeyer
<p>Today I am working on building out several custom Terraform Modules. The issue that I am trying to figure out how to work around dependencies within modules. Until TF 0.12 is released we cannot declare a module to be dependent upon another module. So, in this root level main.tf:</p>

<pre><code># ROOT level main.tf
# -------------------------------------------------------------------
# Create NAT Gateway - Associates EIP as well
# -------------------------------------------------------------------
module "vpc_nat_gateway" {
  source            = "./vpc_nat_gateway"
  vpc_id            = "${ module.vpc.id }"
  public_subnet_ids = "${ module.vpc_subnets.public_subnet_ids }"
  private_cidr      = "${ var.private_cidr }"
  common_tags       = "${ local.common_tags }"
}

# -------------------------------------------------------------------
# Create Private Routes
# -------------------------------------------------------------------
module "vpc_private_route" {
  source         = "./vpc_private_route"
  vpc_id.        = "${ module.vpc.id }"
  nat_gateway_id = "${ module.vpc_nat_gateway.nat_gateway_id }"
  common_tags    = "${ local.common_tags }"
}

# vpc_private_route module - main.tf
data "aws_nat_gateway" "az1" {
  vpc_id = "${ var.vpc_id }"

  tags {
    Name = "*NAT GW AZ 1"
  }
}

data "aws_nat_gateway" "az2" {
  vpc_id = "${ var.vpc_id }"

  tags {
    Name = "*NAT GW AZ 2"
  }
}
</code></pre>

<p>The result output is:</p>

<pre><code>------ SNIP -----
module.vpc_nat_gateway.aws_nat_gateway.nat[1]: Creation complete after 1m50s (ID: nat-02a7f4279cec3a6c8)
module.vpc_nat_gateway.aws_nat_gateway.nat.0: Still creating... (2m0s elapsed)
module.vpc_nat_gateway.aws_nat_gateway.nat[0]: Creation complete after 2m0s (ID: nat-0695a12d9c0305e4c)

    Error: Error applying plan:

    3 error(s) occurred:

    * module.vpc_private_route.data.aws_subnet_ids.private: data.aws_subnet_ids.private: no matching subnet found for vpc with id vpc-0b530d1885e74067b
    * module.vpc_private_route.data.aws_nat_gateway.az2: data.aws_nat_gateway.az2: no matching NAT gateway found: {
    Filter: [{
        Name: "vpc-id",
        Values: ["vpc-0b530d1885e74067b"]
        },{
        Name: "tag:Name",
        Values: ["*NAT GW AZ 2"]
        }]
    }
    * module.vpc_private_route.data.aws_nat_gateway.az1: data.aws_nat_gateway.az1: no matching NAT gateway found: {
    Filter: [{
        Name: "vpc-id",
        Values: ["vpc-0b530d1885e74067b"]
        },{
        Name: "tag:Name",
        Values: ["*NAT GW AZ 1"]
        }]
    }
</code></pre>

<p>As observed in the output the Nat Gateways are created. <code>Terraform show</code> output tells us the Filters are correct:</p>

<pre><code>    module.vpc_nat_gateway.aws_nat_gateway.nat.0:
        id = nat-0695a12d9c0305e4c
        allocation_id = eipalloc-023ca087ad4fb830e
        network_interface_id = eni-015e39fc8d3bc0de3
        private_ip = 172.16.254.16
        public_ip = 18.215.5.116
        subnet_id = subnet-0f2c039e8fd804f30
        tags.% = 7
        tags.Environment = development
        tags.Infrastructure = No
        tags.Name = **redacted** NAT GW AZ 1
    module.vpc_nat_gateway.aws_nat_gateway.nat.1:
        id = nat-02a7f4279cec3a6c8
        allocation_id = eipalloc-0a95264c2eef26673
        network_interface_id = eni-03bddcca2fbeeff44
        private_ip = 172.16.254.84
        public_ip = 3.91.167.246
        subnet_id = subnet-08ee61f3aa43acbe9
        tags.% = 7
        tags.Environment = development
        tags.Infrastructure = No
        tags.Name = **redacted** NAT GW AZ 2
</code></pre>

<p>Executing a subsequent <code>terraform apply</code> runs without any errors.</p>

<pre><code># Yes, the IDs are different in this example than from above.
# ---- SNIP ----
    module.vpc_private_route.data.aws_nat_gateway.az1:
        id = nat-0c127e538a26b2bd5
        allocation_id = eipalloc-01775b8e88502d4b9
        network_interface_id = eni-0b0bd2203bd3f5873
        private_ip = 172.16.254.45
        public_ip = 3.83.199.207
        state = available
        subnet_id = subnet-050f6fc499a455a97
        tags.% = 7
        tags.Environment = development
        tags.Infrastructure = No
        tags.Name = **redacted** NAT GW AZ 1
        vpc_id = vpc-057a1208002394e1b
    module.vpc_private_route.data.aws_nat_gateway.az2:
        id = nat-0325fe2ba1184815b
        allocation_id = eipalloc-0df309e8b533b35b6
        network_interface_id = eni-00e850031318b2a41
        private_ip = 172.16.254.92
        public_ip = 3.88.44.14
        state = available
        subnet_id = subnet-0191ae48f099aa808
        tags.% = 7
        tags.Infrastructure = No
        tags.Name = **redacted** NAT GW AZ 2
        vpc_id = vpc-057a1208002394e1b
    module.vpc_private_route.data.aws_subnet_ids.private:
        id = vpc-057a1208002394e1b
        ids.# = 2
        ids.1528047303 = subnet-03a4f5228ae9f1714
        ids.1908543416 = subnet-0d915cc4899877eb9
        tags.% = 1
        tags.Name = *Private*
        vpc_id = vpc-057a1208002394e1b
</code></pre>

<p><strong>What should I be doing different</strong> to cause this TF Module to wait for the resources to be created before polling for the resource? In Ansible I could just issue a wait command or run a loop searching for criteria before proceeding.</p>

<p>Thank you for your thoughts!</p>

<p><strong>PLEASE NOTE</strong> all of the "ids" shown in this output will be dead before you read this post. All data has been sanitized based on environmental security policies.</p>

<terraform><terraform-module>
How to make Terraform Modules wait for resources to be created in place of using computed values
Steven K7FAQ
# How to make Terraform Modules wait for resources to be created in place of using computed values

(Asked by: username_0 on 2019-01-30)

Today I am working on building out several custom Terraform Modules. The issue that I am trying to figure out how to work around dependencies within modules. Until TF 0.12 is released we cannot declare a module to be dependent upon another module. So, in this root level main.tf:
# ROOT level main.tf
# -------------------------------------------------------------------
# Create NAT Gateway - Associates EIP as well
# -------------------------------------------------------------------
module "vpc_nat_gateway" {
  source            = "./vpc_nat_gateway"
  vpc_id            = "${ module.vpc.id }"
  public_subnet_ids = "${ module.vpc_subnets.public_subnet_ids }"
  private_cidr      = "${ var.private_cidr }"
  common_tags       = "${ local.common_tags }"
}

# -------------------------------------------------------------------
# Create Private Routes
# -------------------------------------------------------------------
module "vpc_private_route" {
  source         = "./vpc_private_route"
  vpc_id.        = "${ module.vpc.id }"
  nat_gateway_id = "${ module.vpc_nat_gateway.nat_gateway_id }"
  common_tags    = "${ local.common_tags }"
}

# vpc_private_route module - main.tf
data "aws_nat_gateway" "az1" {
  vpc_id = "${ var.vpc_id }"

  tags {
    Name = "*NAT GW AZ 1"
  }
}

data "aws_nat_gateway" "az2" {
  vpc_id = "${ var.vpc_id }"

  tags {
    Name = "*NAT GW AZ 2"
  }
}

The result output is:
------ SNIP -----
module.vpc_nat_gateway.aws_nat_gateway.nat[1]: Creation complete after 1m50s (ID: nat-02a7f4279cec3a6c8)
module.vpc_nat_gateway.aws_nat_gateway.nat.0: Still creating... (2m0s elapsed)
module.vpc_nat_gateway.aws_nat_gateway.nat[0]: Creation complete after 2m0s (ID: nat-0695a12d9c0305e4c)

    Error: Error applying plan:

    3 error(s) occurred:

    * module.vpc_private_route.data.aws_subnet_ids.private: data.aws_subnet_ids.private: no matching subnet found for vpc with id vpc-0b530d1885e74067b
    * module.vpc_private_route.data.aws_nat_gateway.az2: data.aws_nat_gateway.az2: no matching NAT gateway found: {
    Filter: [{
        Name: "vpc-id",
        Values: ["vpc-0b530d1885e74067b"]
        },{
        Name: "tag:Name",
        Values: ["*NAT GW AZ 2"]
        }]
    }
    * module.vpc_private_route.data.aws_nat_gateway.az1: data.aws_nat_gateway.az1: no matching NAT gateway found: {
    Filter: [{
        Name: "vpc-id",
        Values: ["vpc-0b530d1885e74067b"]
        },{
        Name: "tag:Name",
        Values: ["*NAT GW AZ 1"]
        }]
    }

As observed in the output the Nat Gateways are created. Terraform show output tells us the Filters are correct:
    module.vpc_nat_gateway.aws_nat_gateway.nat.0:
        id = nat-0695a12d9c0305e4c
        allocation_id = eipalloc-023ca087ad4fb830e
        network_interface_id = eni-015e39fc8d3bc0de3
        private_ip = 172.16.254.16
        public_ip = 18.215.5.116
        subnet_id = subnet-0f2c039e8fd804f30
        tags.% = 7
        tags.Environment = development
        tags.Infrastructure = No
        tags.Name = **redacted** NAT GW AZ 1
    module.vpc_nat_gateway.aws_nat_gateway.nat.1:
        id = nat-02a7f4279cec3a6c8
        allocation_id = eipalloc-0a95264c2eef26673
        network_interface_id = eni-03bddcca2fbeeff44
        private_ip = 172.16.254.84
        public_ip = 3.91.167.246
        subnet_id = subnet-08ee61f3aa43acbe9
        tags.% = 7
        tags.Environment = development
        tags.Infrastructure = No
        tags.Name = **redacted** NAT GW AZ 2

Executing a subsequent terraform apply runs without any errors.
# Yes, the IDs are different in this example than from above.
# ---- SNIP ----
    module.vpc_private_route.data.aws_nat_gateway.az1:
        id = nat-0c127e538a26b2bd5
        allocation_id = eipalloc-01775b8e88502d4b9
        network_interface_id = eni-0b0bd2203bd3f5873
        private_ip = 172.16.254.45
        public_ip = 3.83.199.207
        state = available
        subnet_id = subnet-050f6fc499a455a97
        tags.% = 7
        tags.Environment = development
        tags.Infrastructure = No
        tags.Name = **redacted** NAT GW AZ 1
        vpc_id = vpc-057a1208002394e1b
    module.vpc_private_route.data.aws_nat_gateway.az2:
        id = nat-0325fe2ba1184815b
        allocation_id = eipalloc-0df309e8b533b35b6
        network_interface_id = eni-00e850031318b2a41
        private_ip = 172.16.254.92
        public_ip = 3.88.44.14
        state = available
        subnet_id = subnet-0191ae48f099aa808
        tags.% = 7
        tags.Infrastructure = No
        tags.Name = **redacted** NAT GW AZ 2
        vpc_id = vpc-057a1208002394e1b
    module.vpc_private_route.data.aws_subnet_ids.private:
        id = vpc-057a1208002394e1b
        ids.# = 2
        ids.1528047303 = subnet-03a4f5228ae9f1714
        ids.1908543416 = subnet-0d915cc4899877eb9
        tags.% = 1
        tags.Name = *Private*
        vpc_id = vpc-057a1208002394e1b

What should I be doing different to cause this TF Module to wait for the resources to be created before polling for the resource? In Ansible I could just issue a wait command or run a loop searching for criteria before proceeding.
Thank you for your thoughts!
PLEASE NOTE all of the "ids" shown in this output will be dead before you read this post. All data has been sanitized based on environmental security policies.

**Tags:** terraform, terraform-module


---

## 4 answers

### Answer by username_1 on 2020-04-08. Score: 7

Update: As of TF 0.13x, depends_on is supported to create dependencies between modules. Using depends_on should be preferable over using the method detailed below.
The previous most popular answer is out of date with Terraform 0.12.24.
depends_on is a protected variable, and cannot be used in a module.
In addition there are a few syntax differences.
I've updated the example below.
# ROOT level main.tf
# -------------------------------------------------------------------
# Create NAT Gateway - Associates EIP as well
# -------------------------------------------------------------------
module "vpc_nat_gateway" {
  source            = "./vpc_nat_gateway"
  vpc_id            = module.vpc.id
  public_subnet_ids = module.vpc_subnets.public_subnet_ids
  private_cidr      = var.private_cidr
  common_tags       = local.common_tags
}

# -------------------------------------------------------------------
# Create Private Routes
# -------------------------------------------------------------------
module "vpc_private_route" {
  source         = "./vpc_private_route"
  vpc_id.        = module.vpc.id
  nat_gateway_id = module.vpc_nat_gateway.nat_gateway_id
  common_tags    = local.common_tags

  # Depends is a custom variable, depends_on is a reserved keyword.
  depends = [module.vpc_nat_gateway.nat_gateway_id]
}

# vpc_private_route module - main.tf
variable "depends" {
  default = []
}

resource "null_resource" "depends_on" {
  triggers = {
    depends_on = "${join("", var.depends)}"
  }
}

data "aws_nat_gateway" "az1" {
  vpc_id = var.vpc_id

  depends_on = [
    null_resource.depends_on
  ]
}

data "aws_nat_gateway" "az2" {
  vpc_id = var.vpc_id

  depends_on = [
    null_resource.depends_on
  ]
}


---

### Answer by username_2 on 2020-11-27. Score: 5

These are hacks (understandably as you couldn't explicitly do this in Terraform < 0.13).

Now however, with 0.13, we have `depends_on` for modules: <https://github.com/hashicorp/terraform/blob/v0.13/CHANGELOG.md#0130-august-10-2020>


---

### Answer by username_3 on 2019-02-01. Score: 4

There is a hacky workaround for getting Terraform to do module dependencies. You can force the module to be aware of the Terraform calling it like this:
# ROOT level main.tf
# -------------------------------------------------------------------
# Create NAT Gateway - Associates EIP as well
# -------------------------------------------------------------------
module "vpc_nat_gateway" {
  source            = "./vpc_nat_gateway"
  vpc_id            = "${ module.vpc.id }"
  public_subnet_ids = "${ module.vpc_subnets.public_subnet_ids }"
  private_cidr      = "${ var.private_cidr }"
  common_tags       = "${ local.common_tags }"
}

# -------------------------------------------------------------------
# Create Private Routes
# -------------------------------------------------------------------
module "vpc_private_route" {
  source         = "./vpc_private_route"
  vpc_id.        = "${ module.vpc.id }"
  nat_gateway_id = "${ module.vpc_nat_gateway.nat_gateway_id }"
  common_tags    = "${ local.common_tags }"

  depends_on = ["${module.vpc_nat_gateway.nat_gateway_id}"]
}

# vpc_private_route module - main.tf
variable "depends_on" {
  default = []
}

resource "null_resource" "depends_on" {
  triggers {
    depends_on = "${join("", var.depends_on)}"
  }
}

data "aws_nat_gateway" "az1" {
  vpc_id = "${ var.vpc_id }"

  tags {
    Name = "*NAT GW AZ 1"
  }

  depends_on = [
    "null_resource.depends_on"
  ]
}

data "aws_nat_gateway" "az2" {
  vpc_id = "${ var.vpc_id }"

  tags {
    Name = "*NAT GW AZ 2"
  }

  depends_on = [
    "null_resource.depends_on"
  ]
}

It adds a lot of boilerplate but you will get the desired effect.

> **Comments:**
> **username_0**: Thank you @username_3 for this input and excellent example. I will try this my next day in office and report back.  

---

### Answer by username_4 on 2024-05-09. Score: 0

I've not tried this, but you could have a null\_resource with a local-exec running a sleep command and make your resource dependent on that resource with a depends\_on lifecycle argument.


---

