Terrafrom to Read in .yaml and Assign a Value to a Var
Our teams working with Terraform ofttimes find that information technology is helpful to create a variable hierarchy that supports defining global, environment, and stack-specific variables. While this is a fairly bones concept, Terraform not only doesn't requite you a way to exercise it out of the box only actively works against supporting the concept, despite strong user desire.
Chris, BTI360 engineer and author of thirstydeveloper.io shares in this post how teams at BTI360 implement a Terraform variable hierarchy using Terragrunt and YAML files.
Case Infrastructure
Permit's say nosotros have a simple two-stack, two-surround terraform project using Terragrunt. Information technology might look something similar this:
deployments/ dev/ # dev environs stacks database/ # dev environment database stack terragrunt.hcl # dev surroundings database stack terragrunt config webserver/ # dev environment webserver stack terragrunt.hcl # dev environment webserver stack terragrunt config prod/ # production environment stacks database/ terragrunt.hcl webserver/ terragrunt.hcl root.hcl # parent terragrunt config file modules/ stacks/ # stacks deployed by deployments database/ chief.tf # terraform manifest for database stack webserver/ main.tf # terraform manifest for webserver stack with each of the terragrunt.hcl files including shared terragrunt configuration from the root.hcl with: # terragrunt.hcl include { path = find_in_parent_folders("root.hcl") } Now, say both the database and webserver stacks have in a project_name variable that is always set to sample_project. We could create a terraform.tfvars file in each deployment directory to set this variable value, something like:
deployments/ dev/ database/ terraform.tfvars # dev database variable values terragrunt.hcl webserver/ terraform.tfvars # dev webserver variable values terragrunt.hcl prod/ database/ terraform.tfvars # prod database variable values terragrunt.hcl webserver/ terraform.tfvars # prod webserver variable values terragrunt.hcl root.hcl
where each terraform.tfvars file contains:
project_name = "sample_project"
Following the DRY master however, nosotros'd prefer to but ascertain this variable once at a global level, perhaps with a terraform.tfvars file underneath deployments/:
deployments/ dev/ prod/ terraform.tfvars # global variable values root.hcl # parent terragrunt config file
Then we could load this root terraform.tfvars with the root.hcl similar so:
terraform { ... extra_arguments "load_global_variables" { commands = get_terraform_commands_that_need_vars() optional_var_files = ["${get_parent_terragrunt_dir()}/terraform.tfvars"] } } This, however, would be a mistake.
Why tfvars Don't Work
The tfvars approach higher up worked well until Terraform 0.12, which introduced a controversial feature to print warnings when values are specified for undefined variables. For instance, if we add an unused variable to deployments/terraform.tfvars:
# deployments/terraform.tfvars project_name = "sample_project" unused = true
whatsoever stack we plan or apply now prints:
Alarm: Value for undeclared variable The root module does non declare a variable named "unused" only a value was found in file...
The warnings are annoying enough in that they ataxia the programme output, just worse nonetheless the warning states:
Using a variables file to set up an undeclared variable is deprecated and will go an error in a future release.
That means we can't reliably utilize tfvars files for a variable hierarchy, at to the lowest degree not if yous have any variables divers that go unused past whatever stacks.
This poses a trouble considering it doesn't take long for an infrastructure project to end upwards with some variables defined at some parent level that some stacks beneath don't demand or use, as many issue comments point out.
Hashicorp does give you a workaround. You lot tin can specify variable values using environment variables, TF_VAR_project_name=sample_project for example, simply Terraform leaves the challenge of easily and reproducibly defining those environment variables across systems every bit an exercise to the developer.
We can practise better.
Variable Hierarchy with Terragrunt Inputs
Terragrunt offers two key capabilities that offer usa a fashion out.
The first is Terragrunt's inputs attribute, which accepts an HCL object and translates each fundamental/value pair into an surroundings variable passed to Terraform.
The 2nd is that Terragrunt allows access to all of Terraform's built-in functions within the terragrunt.hcl / root.hcl files. Specifically, nosotros have access to:
- file – for loading file contents to a string
- yamldecode – for converting a YAML cord to an HCL object
- fileexists – for checking if a file exists earlier attempting to load it
- merge – for merging multiple HCL objects
By combining these functions with Terragrunt's inputs attribute, we tin can establish our variable bureaucracy using YAML files. Here'south how:
Start by converting the terraform.tfvars files to config.yml files1. For case, convert:
# deployments/terraform.tfvars project_name = "sample_project" unused = true
to:
# deployments/config.yml --- project_name: "sample_project" unused: true
You lot'll cease upwardly with a structure looking like:
dev/ database/ config.yml # dev/database variable values terragrunt.hcl webserver/ config.yml # dev/webserver variable values terragrunt.hcl config.yml # dev environment variable values prod/ database/ config.yml # prod/database variable values terragrunt.hcl webserver/ config.yml # prod/webserver variable values terragrunt.hcl config.yml # prod environs variable values config.yml # global variable values root.hcl
Next, modify your root.hcl to:
- Recursively observe every config.yml between the
root.hcland theterragrunt.hcl - Load each config.yml file that exists into a cord using
file() - Catechumen each YAML string into a HCL object using
yamldecode() - Merge all the objects using
merge(), ensuring lower-level objects override higher-level ones - Pass the merged object into
inputs
Here'due south a sample root.hcl that does the above:
# root.hcl locals { root_deployments_dir = get_parent_terragrunt_dir() relative_deployment_path = path_relative_to_include() deployment_path_components = compact(carve up("/", local.relative_deployment_path)) # Get a list of every path between root_deployments_directory and the path of # the deployment possible_config_dirs = [ for i in range(0, length(local.deployment_path_components) + i) : join("/", concat( [local.root_deployments_dir], piece(local.deployment_path_components, 0, i) )) ] # Generate a list of possible config files at every possible_config_dir # (support both .yml and .yaml) possible_config_paths = flatten([ for dir in local.possible_config_dirs : [ "${dir}/config.yml", "${dir}/config.yaml" ] ]) # Load every YAML config file that exists into an HCL object file_configs = [ for path in local.possible_config_paths : yamldecode(file(path)) if fileexists(path) ] # Merge the objects together, with deeper configs overriding higher configs merged_config = merge(local.file_configs...) } # Pass the merged config to terraform every bit variable values using TF_VAR_ # environs variables inputs = local.merged_config With this approach, we tin:
- Put global variables in
deployments/config.yml - Put environment-specific variables in
deployments/dev/config.ymlanddeployments/prod/config.yml - Put stack specific variables in the
config.ymlnext to the correspondingterragrunt.hcl
Furthermore, nosotros can override values in higher-level config files by redefining them at lower-levels. For instance, we could redefine project_name in deployments/dev/webserver/config.yml every bit:
# deployments/dev/webserver/config.yml project_name: webserver
and that would override the value of sample_project divers in deployments/config.yml.
Using YAML files as our Terraform variable hierarchy has proved very successful for our teams. We hope it helps yours besides. For more information on this approach, including a fully worked example, meet Chris' terraform-skeleton series on thirstydeveloper.io. Good luck!
Footnotes
- The YAML loading doesn't play nice with
config.ymlfiles that are empty or contain merely a---start of document marking. Y'all tin can delete the emptyconfig.ymlfile and everything will piece of work, due to thefileexistscheck. If you prefer to keep the emptyconfig.yml, the most minimal contents required are:
--- {} Interested in Solving Challenging Issues? Work Hither!
Are yous a software engineer, interested in joining a software company that invests in its teammates and promotes a potent engineering culture? So you're in the right identify! Check out our current Career Opportunities. We're always looking for like-minded engineers to join the BTI360 family.

Related Posts:
- Organizing Terraform Code with Terragrunt
- Advantages and Limitations of Terragrunt-Managed Backends
- Managing Terraform Remote State with CloudFormation
barnhillbourn1977.blogspot.com
Source: https://www.bti360.com/creating-a-terraform-variable-hierarchy-with-terragrunt/
0 Response to "Terrafrom to Read in .yaml and Assign a Value to a Var"
Post a Comment