Thursday 5 May 2022

Terraform Resource Dependencies


Terraform Resource Dependency types:

  • implicit
  • explicit

Resource Attribute Reference

Multiple resources are usually dependent on each other. E.g. output of one resource is used as and input for another. local_file resource might be using the output (id) of the random_pet. We can do that by using the interpolation sequence (${...}):



resource "random_pet" "my_random_text" {
    length = 2
    prefix = "my_rnd_txt_"
    separator = "."

resource "local_file" "foo" {
    filename = "${path.cwd}/temp/foo.txt"
    content = "Let's use the random_pet output which is id with value ${}"

Note that the type of the value in the output and input match in this case (string).
Interpolation takes place during the terraform apply command execution.

resource "time_static" "time_update" {

Resource block is empty because time_static does not need any arguments to be supplied to work.
When applied as it is, terraform creates a logical resource locally (similar to random_pet) with the current time. 

terraform apply output:

Initializing provider plugins...
- Finding latest version of hashicorp/time...
- Installing hashicorp/time v0.7.2...

terraform plan output (snippet):

Terraform will perform the following actions:

  # time_static.time_update will be created
  + resource "time_static" "time_update" {
      + day     = (known after apply)
      + hour    = (known after apply)
      + id      = (known after apply)
      + minute  = (known after apply)
      + month   = (known after apply)
      + rfc3339 = (known after apply)
      + second  = (known after apply)
      + unix    = (known after apply)
      + year    = (known after apply)

attribute id is exported by the time_static resource.

To refer to this attribute we'd use ${} which is the timestamp in form e.g. 2022-04-28T06:13:18Z.

To see resource attribute values that are known after apply, first execute terraform apply and then terraform show.

If value we want to specify is only a reference to the attribute of another resource, we don't need to use interpolation expression but just name of that attribute:

content =

If we use interpolation:

content = "${}"

...then terraform plan will issue the following warning:

Warning: Interpolation-only expressions are deprecated

  on line 8, in resource "local_file" "key_details":
   8:     content="${tls_private_key.pvtkey.private_key_pem}"

Terraform 0.11 and earlier required all non-constant expressions to be
provided via interpolation syntax, but this pattern is now deprecated. To
silence this warning, remove the "${ sequence from the start and the }"
sequence from the end of this expression, leaving just the inner expression.

Template interpolation syntax is still used to construct strings from
expressions when the template includes multiple interpolation sequences or a mixture of literal strings and interpolations. This deprecation applies only
to templates that consist entirely of a single interpolation sequence.

Implicit vs Explicit Resource Dependencies

When Terraform creates resources from the example above, it knows about the dependency so it will first create dependee (random_pet) and then dependent (local_file) and in case of destruction, the order will be first dependent and then dependee. This is a form of implicit dependency as we don't explicitly specify which resources is dependent on which but Terraform figures this out by looking reference expressions used. 
If we want to make sure one resource is created after another, we can specify this explicit dependency via depends_on meta-argument: 

resource "random_pet" "my_random_text" {
    length = 2
    prefix = "my_rnd_txt_"
    separator = "."

resource "local_file" "foo" {
    filename = "${path.cwd}/temp/foo.txt"
    content = "This resource will be created after the random_pet" 
    depends_on = [

In this case we don't use resource attribute reference. Resource R1 is explicitly dependent on Resource R2 it it relies on Resource R2 but doesn't access any of its attributes in its own arguments.

terraform graph command creates a visual presentation of dependencies in TF configuration or execution plan. It can be run as soon as there is a configuration file and configuration directory has been initialized with terraform init. If the configuration directory is not initialized, we'll get the following error:

$ terraform graph                     
Error: Inconsistent dependency lock file
│ The following dependency selections recorded in the lock file are inconsistent with the current configuration:
│   - provider required by this configuration but no version is selected
│   - provider required by this configuration but no version is selected
│ To make the initial dependency selections that will initialize the dependency lock file, run:
│   terraform init

So we first need to initialize the directory:

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/local...
- Finding latest version of hashicorp/random...
- Installing hashicorp/local v2.2.2...
- Installed hashicorp/local v2.2.2 (signed by HashiCorp)
- Installing hashicorp/random v3.1.3...
- Installed hashicorp/random v3.1.3 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

$ terraform graph
digraph {
        compound = "true"
        newrank = "true"
        subgraph "root" {
                "[root] (expand)" [label = "", shape = "box"]
                "[root] provider[\"\"]" [label = "provider[\"\"]", shape = "diamond"]
                "[root] provider[\"\"]" [label = "provider[\"\"]", shape = "diamond"]
                "[root] random_pet.my_random_text (expand)" [label = "random_pet.my_random_text", shape = "box"]
                "[root] (expand)" -> "[root] provider[\"\"]"
                "[root] (expand)" -> "[root] random_pet.my_random_text (expand)"
                "[root] provider[\"\"] (close)" -> "[root] (expand)"
                "[root] provider[\"\"] (close)" -> "[root] random_pet.my_random_text (expand)"
                "[root] random_pet.my_random_text (expand)" -> "[root] provider[\"\"]"
                "[root] root" -> "[root] provider[\"\"] (close)"
                "[root] root" -> "[root] provider[\"\"] (close)"

The output is a graph in DOT format (which is a text and hard to follow). Using graph visualization software can help in making a graph image. We can use e.g. Graphviz

To install Graphviz on Linux:

$ apt update && apt install graphviz -y

To install Graphviz on Mac:

% brew install graphviz

Command to create a graph image is same on Linux and Mac:

% terraform graph | dot -Tsvg > graph.svg



No comments: