tfvars

nicolaw 2nd July 2021 at 10:33am
GeoSpock jq SergejAlikov Terraform

A top-tip for creating new Terraform "tfvars" variables files, ... SergejAlikov wrote a rather nice and simple bit of Golang that supports parsing of v0.11 and v0.12 Terraform HCL files, and outputs a nice JSON structure of inputs, outputs and locals. https://github.com/GeoSpock/tf-named-vals The upshot being you can do things like this:

tf-named-vals a <(cat *.tf) | jq -erf <(cat <<-'EOF'
.variables|to_entries[]|
  ([.value.type]|flatten) as $types|
  ($types|join(",")) as $type|
  (if ([.value.type]|flatten|contains(["list"])) or (.value.default == null) then "" else "\"" end) as $defquot|
  (if .value.default == null then "" else "#" end) as $defcomment|
  (if .value.default == null then "" else .value.default end) as $val|
  (if .value.description == null then "No description available." else .value.description end) as $desc|
  [
    "# \($desc) (\($type))",
    "\($defcomment)\(.key) = \($defquot)\($val)\($defquot)\n"
  ] | join("\n")
EOF
)

A more helpful generic version using tooling directly from HashiCorp is as follows:

#!/usr/bin/env bash

# Requires:
#   https://www.gnu.org/software/bash/
#   https://stedolan.github.io/jq/
#   https://github.com/tmccombs/hcl2json
#   https://github.com/hashicorp/terraform-config-inspect
#   https://www.terraform.io/

set -Eeuo pipefail

# shellcheck disable=SC2154
trap 'declare rc=$?;
  >&2 echo "Unexpected error (exit-code $rc) executing $BASH_COMMAND at ${BASH_SOURCE[0]} line $LINENO";
  exit $rc' ERR

read_tfvars_as_json () {
  declare file="${1:-}"
  if [[ -e "$file" && ! -d "$file" && -r "$file" ]] ; then
    hcl2json < "${1:-}"
  elif [[ ! -t 0 ]] ; then
    hcl2json < /dev/stdin
  else
    echo '{}'
  fi
}

merge_tfvars () {
  printf '# Generated by "%s%s" command at %s.\n\n' \
    "${0##.*/}" "${1:+ }${1:-}" "$(date -R)"

  jq -serf <(cat <<-'EOF'
(.[0].variables) as $tf_inspect |
($tf_inspect|map({(.name): .default})|add) as $tf_defaults |
(.[1]) as $tfvars |

  ($tf_defaults * $tfvars) | to_entries[] |

    (.key) as $key |
    ($tf_inspect[$key]) as $config |
    ($config.default) as $default |

    ([$config.type]|flatten|join(",")|gsub("[[:space:]]+";" ")) as $type |
    (if $type == "" then "**unknown**" else $type end) as $type |

    (if ((.value == $default) and (.value != null)) then "#" else "" end) as $as_comment |

    (if $config.description == null then "No description available." else $config.description end) as $description |

    [
      "# \($description) (\($type))",
      "# Default: \($default|@json)",
      "\($as_comment)\(.key) = \(if .value == null then "" else .value|@json end)\n"
    ] | join("\n")
EOF
) \
    <(terraform-config-inspect --json) \
    <(read_tfvars_as_json "${1:-}")
}

main () {
  if ! compgen -G "*.tf" > /dev/null ; then
    >&2 echo "No Terraform module(s) detected."
    exit 1
  fi

  declare rendered_tfvars=""
  rendered_tfvars="$(merge_tfvars "$@")"

  declare reformatted_tfvars=""
  if ! reformatted_tfvars="$(terraform fmt - <<< "$rendered_tfvars" 2>/dev/null)" ; then
    reformatted_tfvars="$rendered_tfvars"
  fi

  if [[ -n "${1:-}" ]] ; then
    echo "$reformatted_tfvars" > "${1:-}"
    if [[ -t 0 ]] && [[ -n "${EDITOR:-}" ]] ; then
      exec "$EDITOR" "${1:-}"
    fi
  else
    echo "$reformatted_tfvars"
  fi
}

main "$@"