Introduction
Terraform has the meta-argument depends_on
to handle hidden resource or module dependencies that Terraform cannot automatically infer. The depends_on
meta-argument instructs Terraform to complete all actions on the dependency object (including Read actions) before performing actions on the object declaring the dependency. This can have consequences when using it in combination with a Data Resource.
If a resource changes where the Data Resource depends_on
it will mark the Data Resource to be read during apply
as it doesn't know what the outcome of the resource will have on the Data Resource itself. This can have effects on your other resources.
There are 2 examples where the use of a depends_on
with a Data Resource can have an unexpected outcome to what you might think is logical. The examples here will be done using the tfcoremock provider to simulate real world resources which can be executed on any environment.
Prerequisites
- Have the terraform CLI installed for you platform as specified here
Example 1:
In this example we will see that because of the depends_on
the Terraform plan marks a resource to be updated but the eventual apply doesn't perform this action.
Please follow these steps to see what is happening
- Create a directory on your disk called
example1
- Within this directory create a file called
main.tf
with the following content
terraform {
required_providers {
tfcoremock = {
source = "hashicorp/tfcoremock"
version = "0.2.0"
}
}
}
provider "tfcoremock" {}
resource "tfcoremock_simple_resource" "hero1" {
string = "Wonder Woman"
}
data "tfcoremock_simple_resource" "heroes" {
id = "heroes_data"
depends_on = [
tfcoremock_simple_resource.hero1
]
}
resource "tfcoremock_simple_resource" "hero2" {
string = data.tfcoremock_simple_resource.heroes.string
}
- Under the
example1
directory create a directory calledterraform.data
- create a file called
terraform.data/heroes_data.json
with the following content
{
"values": {
"string": {
"string": "Batman"
}
}
}
- the structure should now look like the following
.
├── main.tf
├── terraform.data
│ └── heroes_data.json
- Initialize with terraform
terraform init
- Create the resources
terraform apply
The output should be 2 resources created.
tfcoremock_simple_resource.hero1: Creating...
tfcoremock_simple_resource.hero1: Creation complete after 0s [id=0c26f2f8-67f6-b640-7190-36f936bfa53d]
data.tfcoremock_simple_resource.heroes: Reading...
data.tfcoremock_simple_resource.heroes: Read complete after 0s
tfcoremock_simple_resource.hero2: Creating...
tfcoremock_simple_resource.hero2: Creation complete after 0s [id=d680cd27-e97c-37dc-8d82-f518c95008be]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
- The data resource heroes has a
depends_on
the resource hero1. When hero1 is changed it will force the heroes data resource to be read during apply. - Change the value of hero1 to superman
resource "tfcoremock_simple_resource" "hero1" {
string = "Superman"
}
- Run a terraform plan and notice that it now also wants to change hero2 which is using the data resource as input. This is not expected as the data source itself isn't changed. But because you are using a
depends_on
Terraform doesn't know this.
terraform plan
Output:
# data.tfcoremock_simple_resource.heroes will be read during apply
# (depends on a resource or a module with changes pending)
<= data "tfcoremock_simple_resource" "heroes" {
+ bool = (known after apply)
+ float = (known after apply)
+ id = "heroes_data"
+ integer = (known after apply)
+ number = (known after apply)
+ string = (known after apply)
}
# tfcoremock_simple_resource.hero1 will be updated in-place
~ resource "tfcoremock_simple_resource" "hero1" {
id = "0c26f2f8-67f6-b640-7190-36f936bfa53d"
~ string = "Wonder Woman" -> "Superman"
}
# tfcoremock_simple_resource.hero2 will be updated in-place
~ resource "tfcoremock_simple_resource" "hero2" {
id = "d680cd27-e97c-37dc-8d82-f518c95008be"
~ string = "Batman" -> (known after apply)
}
Plan: 0 to add, 2 to change, 0 to destroy.
- run a terraform apply and notice that the update isn't effecting the hero2 resource and it updates only the hero1 resource.
terraform apply
output:
tfcoremock_simple_resource.hero1: Modifying... [id=0c26f2f8-67f6-b640-7190-36f936bfa53d]
tfcoremock_simple_resource.hero1: Modifications complete after 0s [id=0c26f2f8-67f6-b640-7190-36f936bfa53d]
data.tfcoremock_simple_resource.heroes: Reading...
data.tfcoremock_simple_resource.heroes: Read complete after 0s
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
- During the apply it reads the data source heroes and saw that the value batman was still the same and it didn't have to actually make a change to the resource.
Example 2:
In this example we will see that because of the depends_on
the Terraform plan/apply marks a resource to be destroyed and added even if the underlying data is not changing.
Please follow these steps to see what is happening
- Create a directory on your disk called
example2
- Within this directory create a file called
main.tf
with the following content
terraform {
required_providers {
tfcoremock = {
source = "hashicorp/tfcoremock"
version = "0.2.0"
}
}
}
provider "tfcoremock" {}
resource "tfcoremock_simple_resource" "hero1" {
string = "Wonder Woman"
}
data "tfcoremock_simple_resource" "heroes" {
id = "heroes_data"
depends_on = [
tfcoremock_simple_resource.hero1
]
}
resource "tfcoremock_simple_resource" "hero2" {
id = data.tfcoremock_simple_resource.heroes.string
}
- Under the
example2
directory create a directory calledterraform.data
- create a file called
terraform.data/heroes_data.json
with the following content
{
"values": {
"string": {
"string": "Batman"
}
}
}
- the structure should now look like the following
.
├── main.tf
├── terraform.data
│ └── heroes_data.json
- Initialize with terraform
terraform init
- Create the resources
terraform apply
The output should be 2 resources created.
tfcoremock_simple_resource.hero1: Creating...
tfcoremock_simple_resource.hero1: Creation complete after 0s [id=0c26f2f8-67f6-b640-7190-36f936bfa53d]
data.tfcoremock_simple_resource.heroes: Reading...
data.tfcoremock_simple_resource.heroes: Read complete after 0s
tfcoremock_simple_resource.hero2: Creating...
tfcoremock_simple_resource.hero2: Creation complete after 0s [id=d680cd27-e97c-37dc-8d82-f518c95008be]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
- The data resource heroes has a
depends_on
the resource hero1. When hero1 is changed it will force the heroes data resource to be read during apply. - Change the value of hero1 to Superman
resource "tfcoremock_simple_resource" "hero1" {
string = "Superman"
}
- Run a terraform plan and notice that it now also wants to destroy and add hero2 which is using the data resource as input. This is not expected as the data source itself isn't changed. But because you are using a
depends_on
Terraform doesn't know this and the id attribute within this resource is not something that can be updated
terraform plan
Output:
# data.tfcoremock_simple_resource.heroes will be read during apply
# (depends on a resource or a module with changes pending)
<= data "tfcoremock_simple_resource" "heroes" {
+ bool = (known after apply)
+ float = (known after apply)
+ id = "heroes_data"
+ integer = (known after apply)
+ number = (known after apply)
+ string = (known after apply)
}
# tfcoremock_simple_resource.hero1 will be updated in-place
~ resource "tfcoremock_simple_resource" "hero1" {
id = "51557033-7bd2-9846-235c-e67f6d7faa85"
~ string = "Wonder Woman" -> "Superman"
}
# tfcoremock_simple_resource.hero2 must be replaced
-/+ resource "tfcoremock_simple_resource" "hero2" {
~ id = "Batman" -> (known after apply) # forces replacement
}
Plan: 1 to add, 1 to change, 1 to destroy.
- run a terraform apply and notice that the resource is destroyed before the actual data resource is read to verify if the data is actually changed
terraform apply
output:
tfcoremock_simple_resource.hero2: Destroying... [id=Batman]
tfcoremock_simple_resource.hero2: Destruction complete after 0s
tfcoremock_simple_resource.hero1: Modifying... [id=51557033-7bd2-9846-235c-e67f6d7faa85]
tfcoremock_simple_resource.hero1: Modifications complete after 0s [id=51557033-7bd2-9846-235c-e67f6d7faa85]
data.tfcoremock_simple_resource.heroes: Reading...
data.tfcoremock_simple_resource.heroes: Read complete after 0s
tfcoremock_simple_resource.hero2: Creating...
tfcoremock_simple_resource.hero2: Creation complete after 0s [id=Batman]
Apply complete! Resources: 1 added, 1 changed, 1 destroyed.
- The resource is destroyed before the actual data resource is read to verify if the data is changed. This is a key behaviour of terraform that cannot be changed.
Solution
In both examples removing the depends_on
from the code makes the changes more predictable.