Let's play around with our VPC a bit to better understand how resource dependencies are handled. Instead of adding a subnet, let's destroy the complete infrastructure we have so far and then plan creation from scratch:
$> terraform destroy $> terraform plan # ... + aws_subnet.public availability_zone: "<computed>" cidr_block: "10.0.1.0/24" map_public_ip_on_launch: "false" vpc_id: "${aws_vpc.my_vpc.id}" # ...
Terraform doesn't know the VPC ID yet, so it doesn't show it to you in the plan. Let's apply the template and observe the order of resource creation:
$> terraform apply aws_vpc.my_vpc: Creating... cidr_block: "" => "10.0.0.0/16" default_network_acl_id: "" => "<computed>" default_security_group_id: "" => "<computed>" dhcp_options_id: "" => "<computed>" enable_classiclink: "" => "<computed>" enable_dns_hostnames: "" => "<computed>" enable_dns_support: "" => "<computed>" instance_tenancy: "" => "<computed>" main_route_table_id: "" => "<computed>" aws_vpc.my_vpc: Creation complete aws_subnet.public: Creating... availability_zone: "" => "<computed>" cidr_block: "" => "10.0.1.0/24" map_public_ip_on_launch: "" => "false" vpc_id: "" => "vpc-8f8568e7" aws_subnet.public: Creation complete
Terraform knew (from the graph it built) that subnet requires VPC to exist, so it created it first, followed by subnet.
What happens if we recreate the VPC? Let's try it out with the help of the taint command. terraform taint marks a single resource for recreation. The resource will be destroyed and then created again.
$> terraform taint aws_vpc.my_vpc The resource aws_vpc.my_vpc in the module root has been marked as tainted! $> terraform plan -/+ aws_subnet.public availability_zone: "eu-central-1b" => "<computed>" cidr_block: "10.0.1.0/24" => "10.0.1.0/24" map_public_ip_on_launch: "false" => "false" vpc_id: "vpc-8f8568e7" => "${aws_vpc.my_vpc.id}" (forces new resource) -/+ aws_vpc.my_vpc (tainted) cidr_block: "10.0.0.0/16" => "10.0.0.0/16" default_network_acl_id: "acl-a52febcd" => "<computed>" default_security_group_id: "sg-feafde96" => "<computed>" dhcp_options_id: "dopt-b82bc8d1" => "<computed>" enable_classiclink: "" => "<computed>" enable_dns_hostnames: "false" => "<computed>" enable_dns_support: "true" => "<computed>" instance_tenancy: "default" => "<computed>" main_route_table_id: "rtb-1913d071" => "<computed>"
Terraform has got us covered: after recreating a VPC, it will also recreate a subnet because it knows that a subnet depends on the VPC to exist. As AWS doesn't allow simply changing the VPC ID of an existing subnet, Terraform will force the creation of a completely new subnet.
Which parameters the resource will use depends on provider implementation. Normally, it is mentioned in the Terraform documentation page for a specific resource.
If you try to draw a graph again, you won't see much difference from the previous one. The special destroy nodes are not included by default, and in order to see them, you need to specify the -verbose argument:
$> terraform graph -verbose | dot -Tpng > graph.png
Now we can see one node of the graph for the existing resource and another node to destroy it. Nodes are added to the graph in an order that will lead to the correct removal of resources that need to be removed.
Before we finish with graphs, let's take a quick look at how dependencies are specified inside the state file:
"aws_subnet.public": { "type": "aws_subnet", "depends_on": [ "aws_vpc.my_vpc" ], "primary": { "id": "subnet-2116e25b", "attributes": { "availability_zone": "eu-central-1b", "cidr_block": "10.0.1.0/24", "id": "subnet-2116e25b", "map_public_ip_on_launch": "false", "tags.%": "0", "vpc_id": "vpc-8f8568e7" }, "meta": {}, "tainted": false },
Note the depends_on part - Terraform saves references to resources, and this one depends on inside this key. Most of the time, dependencies in Terraform just work. You just need to reference resources inside the template and Terraform will do the job of building a graph and order operations with it. But, sometimes, you need a little bit more control over dependencies.