4

I have two files: a Terraform variables file (vars.tf), and a Makefile.

What I want to be able to do, is to have the Makefile "read" some of the variables in the Terraform vars.tf file.

The vars.tf file:

variable "region" {
    default = "us-west-1"
    description = "AWS Region"
}
variable "profile" {
    default = "bobs-profile"
    description = "Please enter your AWS CLI profile name"
}
variable "env" {
    default = "dev"
    description = "Please label the environment this infrastructure will be deployed to eg dev, uat, prod"
}

I would like to be able to call the variables listed in the vars.tf file in my Makefile - here is a short example:

.ONESHELL:
.SHELL := /usr/bin/bash
.PHONY: apply plan prep
VARS="variables/$(ENV)-$(REGION).tfvars"
CURRENT_FOLDER=$(shell basename "$$(pwd)")
AWS_PROFILE"bobs-profile"

I would like, for example, the values of:

  • $(ENV)
  • $(REGION)
  • $(AWS_PROFILE)

To be pulled from the Terraform vars.tf file, so that the Makefile will be run with the variables as follows:

ENV=dev
REGION=us-west-1
AWS_PROFILE=bobs-profile

Is there a way that I can achieve this?

1
  • Did you consider using tfvars.json files and then processing them using jq?
    – Clemens
    Nov 27, 2020 at 15:05

2 Answers 2

2

The file that you show is written in HCL (the HashiCorp configuration language). It can be transformed to JSON, which makes it easier to parse with common tools like jq.

To convert your HCL file to JSON, you may use a tool such as yj (from https://github.com/sclevine/yj). You may then have jq create the make command for you:

eval "$(
    yj -cj <vars.tf |
    jq -r '
        [
            "make",
            (.variable[] | keys[]) as $key |
                "\($key)=\(.variable[][$key][].default)"
        ] | @sh'
)"

For the given example, this creates the command

'make' 'env=dev' 'profile=bobs-profile' 'region=us-west-1'

Which, when evaluated, would run the make with the named variables set to the mentioned values. The variables would be in lower case, which is what you want. Note, for example, that setting ENV may influence the way shell commands in a Makefile are executed (ENV is a special variable in the shell, which may point to a file that the shell should source upon startup).

You could also get the variables and values as a tab-delimited list that you then read in the shell:

set --

yj -cj <vars.tf |
jq -r '
    (.variable[] | keys[]) as $key |
    [ $key, .variable[][$key][].default ] |
    @tsv' |
{
    while IFS=$'\t' read -r key value; do
        set -- "$@" "$key"="$value"
    done

    make "$@"
}

This would collect the variables and their values in the list of positional parameters, and then invoke make with them. The command that would executed, given the document in the question, would be the equivalent of

make 'env=dev' 'profile=bobs-profile' 'region=us-west-1'

For reference, the JSON document created by yj above, would be the equivalent of the following:

{
  "variable": [
    {
      "region": [
        {
          "default": "us-west-1",
          "description": "AWS Region"
        }
      ],
      "profile": [
        {
          "default": "bobs-profile",
          "description": "Please enter your AWS CLI profile name"
        }
      ],
      "env": [
        {
          "default": "dev",
          "description": "Please label the environment this infrastructure will be deployed to eg dev, uat, prod"
        }
      ]
    }
  ]
}
0

Assuming you have GNU make, you can use the shell command to assign the result of an external command to a Makefile variable.

For your case, you could use:

ENV:=$(shell awk '$$1=="variable" && $$2=="\"env\"" {i=1} $$1=="}" {i=0} i && $$1=="default" {split($$0,a,"\""); print a[2]}' test.tf)

REGION:=$(shell awk '$$1=="variable" && $$2=="\"region\"" {i=1} $$1=="}" {i=0} i && $$1=="default" {split($$0,a,"\""); print a[2]}' test.tf)

AWS_PROFILE:=$(shell awk '$$1=="variable" && $$2=="\"profile\"" {i=1} $$1=="}" {i=0} i && $$1=="default" {split($$0,a,"\""); print a[2]}' test.tf)

This will look for regions of interest starting with variable and ending with }, set a flag i when the "correct" region was found, and if that flag is set and a line with keyword default encountered, it will extract the element in double-quotes (" ... ").

Note that double $$ are necessary since make will interpret the entire content of $( ... ) (without respect to the ' ... '), and to pass a literal $ to the shell command we need to "escape" it by prepending another $.

It is a little clumsy (a loop construct would be nicer since the parsing structure is basically the same for all three) but it should work.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .