Codementor Events

Sharing sensitive variables between ansible and terraform

Published Apr 07, 2019
Sharing sensitive variables between ansible and terraform

Simple proof of concept, how to share some sensitive variables between terraform and ansible in a way that allows committing into git, while also being reasonably easy to decrypt, and used natively via ansible play.

Ansible

Let's create some sensitive variables in ansible play, env-default-secure-vars.yml:

---
  # Secure vars shared between terraform and ansible

  shared_secure_var1: securevalue1

  shared_secure_var2: securevalue2

and encrypt this file using ansible-vault encrypt env-default-secure-vars.yml

Let's check if ansible can work wish encrypted vars, using playbook


---
- name: TerraformAnsiblePOC
  hosts: localhost
  gather_facts: no

  pre_tasks:

    - include_vars: "env-default-secure-vars.yml"

    - include_vars:  "env-default-vars.yml"

  tasks:
    
    - debug: var="shared_secure_var1"

    - debug: var="shared_secure_var2"



PLAY [TerraformAnsiblePOC] *****************

TASK [debug] *******************************
ok: [localhost] => {
    "shared_secure_var1": "securevalue1"
}

TASK [debug] *******************************
ok: [localhost] => {
    "shared_secure_var2": "securevalue2"
}

PLAY RECAP ******************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0   

Terraform

Now let's see how can we consume in terraform the same data ?

Fortunately, we have built-in provider external, that allows consuming json feed
returned by external program


data "external" "ansible" {
  program  = [ "tf_ansible_vault.sh", "env-default-secure-vars.yml"]
}

output "shared_secure_var1" {
  value = "${data.external.ansible.result.shared_secure_var1}"
}

output "shared_secure_var2" {
  value = "${data.external.ansible.result.shared_secure_var2}"
}


Let's write shell routine, that will return json representation of the encrypted vars.


#!/bin/bash

set -ef -o pipefail
# Keep environment clean
export LC_ALL="C"
# Set variables
readonly TMP_DIR="/tmp"
readonly TMP_OUTPUT="${TMP_DIR}/$$.out"
readonly BASE_DIR="$(dirname "$(realpath "$0")")"
readonly MY_NAME="${0##*/}"
# Cleanup on exit
trap 'rm -rf ${TMP_OUTPUT}' \
  EXIT SIGHUP SIGINT SIGQUIT SIGPIPE SIGTERM

if [[ -z "$ANSIBLE_VAULT_IDENTITY_LIST" ]]
then
      echo "Please export path to vault id via ANSIBLE_VAULT_IDENTITY_LIST"
      exit 1
fi

if [[ -z "$1" ]]
then
      echo "Please provide path to secrets file"
      exit 1
fi

#echo cp $1 $TMP_OUTPUT
cp $1 $TMP_OUTPUT

ansible-vault decrypt $TMP_OUTPUT > /dev/null

python -c 'import sys, yaml, json; json.dump(yaml.load(sys.stdin), sys.stdout, indent=4)' < $TMP_OUTPUT

rm $TMP_OUTPUT


Checking if script works ...


./tf_ansible_vault.sh env-default-secure-vars.yml 
{
    "shared_secure_var2": "securevalue2", 
    "shared_secure_var1": "securevalue1"
}

and now let's check with terraform play:


terraform apply            
data.external.ansible: Refreshing state...

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

shared_secure_var1 = securevalue1
shared_secure_var2 = securevalue2


Seems it works. Looking very promising, but lets look into terraform.tfstate


{
    "version": 3,
    "terraform_version": "0.11.11",
    "serial": 1,
    "lineage": "7f589f33-7ef8-7d45-19ab-d053412dd875",
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {
                "data.external.ansible": {
                    "type": "external",
                    "depends_on": [],
                    "primary": {
                        "id": "-",
                        "attributes": {
                            "id": "-",
                            "program.#": "2",
                            "program.0": "tf_ansible_vault.sh",
                            "program.1": "env-default-secure-vars.yml",
                            "result.%": "2",
                            "result.shared_secure_var1": "securevalue1",
                            "result.shared_secure_var2": "securevalue2"
                        },
                        "meta": {},
                        "tainted": false
                    },
                    "deposed": [],
                    "provider": "provider.external"
                }
            },
            "depends_on": []
        }
    ]
}


We see there our decrypted secure vars, so still be cautious, how you store it. Terraform has
number of tickets around similar issues (https://github.com/hashicorp/terraform/issues/4436) for a
few years, but no good solution until now.

Summary

POC shows how you can share some of your provisioning variables with terraform and back (terraform
can generate variables yml file). Might be suitable for some situations, although not the ideal.

Check out example at https://github.com/Voronenko/poc-terraform-ansible-bridge

Discover and read more posts from Vyacheslav
get started
post commentsBe the first to share your opinion
Show more replies