In 99% of cases, Terraform will resolve dependencies automatically. There are two problems you can encounter when you rely solely on automatic resolution:
For both problems, there is a solution in Terraform. Let's first look at how you can force dependencies with depends_on
. For each resource, you can specify the depends_on
parameter, which accepts a list of resources that this resource depends on. As a result, this resource won't be created until the ones listed inside this parameter are created.
There might be different use cases for this. For example, your private OpenStack installation could be implemented in a way that it is impossible to create virtual routers in parallel, so you have to force dependency for each router to force Terraform to create them one after another. Or your instances could depend on the existence of one central master instance (which could be Chef Server or Puppet Master). Let's implement this scenario in our template.
Add two new resources to template.tf
:
resource "aws_instance" "master-instance" { ami = "ami-9bf712f4" instance_type = "t2.micro" subnet_id = "${aws_subnet.public.id}" } resource "aws_instance" "slave-instance" { ami = "ami-9bf712f4" instance_type = "t2.micro" subnet_id = "${aws_subnet.public.id}" depends_on = ["aws_instance.master-instance"] }
Draw the graph
:
$> terraform graph | dot -Tpng > graph.png
Let's remove depends_on
for the slave instance and draw graph
again:
With depends_on
, all resources would be created sequentially. Without it, both EC2 instances will be created in parallel.
Now, let's say we want to include a private hostname
of master
in the list of tags
of the slave, but we don't want to update it if master
was recreated. To achieve this, we will use the ignore_changes
parameter. This parameter is part of lifecycle
block, responsible for few other create/destroy-related parameters. The ignore_changes
parameter accepts the list of parameters to ignore when updating, in our case -tags
:
resource "aws_instance" "slave-instance" { ami = "ami-9bf712f4" instance_type = "t2.micro" subnet_id = "${aws_subnet.public.id}" tags { master_hostname = "${aws_instance.master-instance.private_dns}" } lifecycle { ignore_changes = ["tags"] } }
Run the terraform apply
command, then remove the tags
parameter from the aws_instance.slave
instance configuration and run the terraform plan
command. Terraform will show you that there is nothing to do because it was told to ignore changes of the tags
parameter.
The most common use case for ignore_changes
is, perhaps, user_data
for cloud instances. For most providers, if you change user_data
(the script to be executed on instance creation by the cloud-init
utility), Terraform will try to recreate the instance.
It is often unwanted behavior because most likely you use the same user_data
string for multiple instances and you want changes to be applied only for new instances, while keeping the others running (or by recreating them one by one yourself).
With depends_on
and ignore_changes
, you can achieve a bit more flexibility when dealing with dependencies inside Terraform.
There are two other lifecycle block parameters that should be mentioned:
create_before_destroy
boolean parameter allows us to tell Terraform to first create new resource and then destroy previous one in case of recreation.prevent_destroy
parameter, also boolean, marks resource as indestructible and can save you some nerves. One example of a resource that can benefit from this option is an Elastic IP--a dedicated IP address inside AWS than you can attach to an EC2 instance.18.119.139.50