Skip to content

Instantly share code, notes, and snippets.

@bozhanglab49
Last active September 21, 2021 13:12
Show Gist options
  • Save bozhanglab49/4445411ed361db588d48c36f0eed26cf to your computer and use it in GitHub Desktop.
Save bozhanglab49/4445411ed361db588d48c36f0eed26cf to your computer and use it in GitHub Desktop.
TWL-Lab49/Learning IaC - Relative Path in Terragrunt

One of the gotchas mentioned in Terragrunt doc is about Relative File Path. To summarize, this works:

terraform {
  source = "git::git@github.com:foo/modules.git//frontend-app?ref=v0.0.3"

  extra_arguments "custom_vars" {
    commands = [
      "apply",
      "plan",
      "import",
      "push",
      "refresh"
    ]

    # With the get_terragrunt_dir() function, you can use relative paths!
    arguments = [
      "-var-file=${get_terragrunt_dir()}/../common.tfvars"
    ]
  }
}

But this doesn't:

terraform {
  source = "git::git@github.com:foo/modules.git//frontend-app?ref=v0.0.3"

  extra_arguments "custom_vars" {
    commands = [
      "apply",
      "plan",
      "import",
      "push",
      "refresh"
    ]

    arguments = [
      "-var-file=../common.tfvars" # Note: This relative path will NOT work correctly!
    ]
  }
}

Terragrunt doc explains very well why relative path doesn't work:

Note how the source parameter is set, so Terragrunt will download the frontend-app code from the modules repo into a temporary folder and run terraform in that temporary folder. Note also that there is an extra_arguments block that is trying to allow the frontend-app to read some shared variables from a common.tfvars file. Unfortunately, the relative path (../common.tfvars) won’t work, as it will be relative to the temporary folder! Moreover, you can’t use an absolute path, or the code won’t work on any of your teammates’ computers.

However, it does not stress why relative path works in following code:

include "root" {
  path = find_in_parent_folders()
}

terraform {
  source = "github.com/<org>/modules.git//app?ref=v0.1.0"
}

dependency "vpc" {
  config_path = "../vpc"
}

dependency "mysql" {
  config_path = "../mysql"
}

inputs = {
  env            = "qa"
  basename       = "example-app"
  vpc_id         = dependency.vpc.outputs.vpc_id
  subnet_ids     = dependency.vpc.outputs.subnet_ids
  mysql_endpoint = dependency.mysql.outputs.endpoint
}

Notice the config_path "../vpc" and "../mysql"; they are relative paths but they are working. Why? I can't find clear documentation on how Terragrunt runs under the hood, nor can I google any useful information from other sources but after reading through all the use cases, I can share my understanding (Please point out where I'm wrong :)).

Essentially, Terragrunt is a thin layer that generates configurations (e.g., inputs, remote state etc.) for Terraform modules. It breaks down into two stages. First stage is generate all the necessary .tf files or merge all the includes (e.g. find from parents) into one final .hcl file or wait for all dependencies to resolve - we can think of this stage as "compilation"; second stage is copy all the generated or downloaded .tf files into a folder directory and execute whatever Terraform command we specified in that temporary folder - we can think of this stage as "runtime".

This becomes clear now. dependency happens in compilation which is run by Terragrunt in the source folder which is why relative path works. But anything used by running Terraform command at runtime is based on the temporary folder which is why it must call get_terragrunt_dir to get the absolute path to the source file during compilation time.

Again, this is just my understanding which might be wrong, and we have to resort to Terragrunt source code to get how it works exactly under the hood which is a long shot.

Happy learning:)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment