Search code examples
environment-variablesansibleansible-facts

Reading multiple values from an env file with ansible and storing them as facts


I have the following code which reads values from an environment (.env) file and stores them as facts:

- name: Read values from environment
  shell: "source {{ env_path }}; echo $DB_PASSWORD"
  register: output
  args:
    executable: /bin/bash
  changed_when: false

- name: Store read password
  set_fact:
    db_password: "{{ output.stdout }}"
  when:
    - db_password is undefined
  changed_when: false

- name: Read values from environment
  shell: "source {{ env_path }}; echo $DB_USER"
  register: output
  args:
    executable: /bin/bash
  changed_when: false

- name: Store read user
  set_fact:
    db_user: "{{ output.stdout }}"
  when:
    - db_user is undefined
  changed_when: false

- name: Read values from environment
  shell: "source {{ env_path }}; echo $DB_NAME"
  register: output
  args:
    executable: /bin/bash
  changed_when: false

- name: Store read db_name
  set_fact:
    db_name: "{{ output.stdout }}"
  when:
    - db_name is undefined
  changed_when: false

- name: Container environment loaded; the following facts are now available for use by ansible
  debug: "var={{ item }}"
  with_items:
    - db_name
    - db_user
    - db_password

It's quite bulky and unwieldy. I would like to write it something like this, but I can't figure out how :

vars:
    values:
       - db_name
       - db_password
       - db_user
tasks:
- name: Read values from environment
  shell: "source {{ env_path }}; echo {{ item|upper }}"
  register: output
  with_items: values
  args:
    executable: /bin/bash
  changed_when: false

- name: Store read value
  set_fact:
    "{{ item.0 }}": "{{ item.1.stdout }}"
  when:
    - item.0 is undefined
  with_together:
    - values
    - output.results
  changed_when: false

Instead, I get this output:

ok: [default] => (item=values) => {"changed": false, "cmd": "source /var/www/mydomain.org/.env; echo VALUES", "delta": "0:00:00.002240", "end": "2017-02-15 15:25:15.338673", "item": "values", "rc": 0, "start": "2017-02-15 15:25:15.336433", "stderr": "", "stdout": "VALUES", "stdout_lines": ["VALUES"], "warnings": []}

TASK [sql-base : Store read password] ******************************************
skipping: [default] => (item=[u'values', u'output.results'])  => {"changed": false, "item": ["values", "output.results"], "skip_reason": "Conditional check failed", "skipped": true}

Even more ideal of course would be if there is an ansible module I have overlooked that allows me to load values from an environment file.


Solution

  • Generally I would either put my variables into the inventory files themselves, or convert them to yml format and use the include_vars module (you might be able to run a sed script to convert your environment file to yml on the fly). Before using the below code make sure you are really required to use those environment files, and you cannot easily use some other mechanism like:

    • a YAML file with the include_vars module
    • putting the config inside the inventory (no modules required)
    • putting the config into an ansible vault file (no modules required, you need to store the decryption key somewhere though)
    • use some other secure storage mechanism, like Hashicorp's vault (with a plugin like https://github.com/jhaals/ansible-vault)

    Back to your code, it is actually almost correct, you were missing a dollar from the read task, and some curly braces from the condition:

    test.env

    DB_NAME=abcd
    DB_PASSWORD=defg
    DB_USER=fghi
    

    Note: make sure that this file adheres to sh standards meaning you don't put spaces around the = sign. Having something like DB_NAME = abcd will fail

    play.yml

    - hosts: all
      connection: local
      vars:
          env_path: 'test.env'
          values:
             - db_name
             - db_password
             - db_user
      tasks:
      - name: Read values from environment
        shell: "source {{ env_path }}; echo ${{ item|upper }}"
        register: output
        with_items: "{{ values }}"
        args:
          executable: /bin/bash
        changed_when: false
    
      - name: Store read value
        set_fact:
          "{{ item.0 }}": "{{ item.1.stdout }}"
        when: '{{ item.0 }} is undefined'
        with_together:
          - "{{ values }}"
          - "{{ output.results }}"
        changed_when: false
    
      - name: Debug
        debug:
          msg: "NAME: {{db_name}} PASS: {{db_password}} USER: {{db_user}}"
    

    Running with ansible-playbook -i 127.0.0.1, play.yml:

    TASK [Debug] *******************************************************************
    ok: [127.0.0.1] => {
        "msg": "NAME: abcd PASS: defg USER: fghi"
    }