Search code examples
ansiblejinja2

Variable in text not replaced and still with braces


There is a json file (this content is transferring as base64 to playbook):

$ cat json
{
  "name": "some_value",
  "envs": "ENV_TEST=true ENV_TEXT={{additional_variable}}"
}

Convert to b64 format:

$ cat json | base64 | tr -d "\n"
ewogICJuYW1lIjogInNvbWVfdmFsdWUiLAogICJlbnZzIjogIkVOVl9URVNUPXRydWUgRU5WX1RFWFQ9e3thZGRpdGlvbmFsX3ZhcmlhYmxlfX0iCn0K

Extra vars for playbook (additional_variable and json_props):

-e additional_variable=additionaltext -e json_props=ewogICJuYW1lIjogInNvbWVfdmFsdWUiLAogICJlbnZzIjogIkVOVl9URVNUPXRydWUgRU5WX1RFWFQ9e3thZGRpdGlvbmFsX3ZhcmlhYmxlfX0iCn0K

Playbook:

---
- hosts: test
  gather_facts: False
  vars:
    props: "{{ json_props | b64decode | from_json }}"
    prop:
      envs: "{{ props.envs }}"
  tasks:
  - debug:
      msg: "{{ additional_variable }}"

  - debug:
      msg: "{{ prop.envs }}"

Output:

TASK [debug] **********************************************************************
ok: [127.0.0.1] => {
    "msg": "additionaltext"
}

TASK [debug] **********************************************************************
ok: [127.0.0.1] => {
    "msg": "ENV_TEST=true ENV_TEXT={{additional_variable}}"
}

How to set variable additional_variable from extra vars to the prop.envs string?

Any ideas?

I expect that output will be "ENV_TEST=true ENV_TEXT=additionaltext"


Solution

  • The problem is that the value of the variable props have already been 'templated' in the expression

      props: "{{ json_props|b64decode|from_json }}"
    

    You've already found out that given the extra variables

    shell> cat evars.yml
    additional_variable: additionaltext
    json_props: ewogICJuYW1lIjogInNvbWVfdmFsdWUiLAogICJlbnZzIjogIkVOVl9URVNUPXRydWUgRU5WX1RFWFQ9e3thZGRpdGlvbmFsX3ZhcmlhYmxlfX0iCn0K
    

    The playbook

    shell> cat pb.yml
    - hosts: localhost
    
      vars:
    
        props: "{{ json_props|b64decode|from_json }}"
    
      tasks:
    
        - debug:
            var: props
    

    gives

    shell> ansible-playbook -e @evars.yml pb.yml
    
    PLAY [localhost] ******************************************************************************
    
    TASK [debug] **********************************************************************************
    ok: [localhost] => 
      props:
        envs: ENV_TEST=true ENV_TEXT={{additional_variable}}
        name: some_value
    

    The variable additional_variable was not substituted in the attribute envs and explicit expansion of the attribute doesn't help either. Ansible doesn't provide a tool to 'template' a value of a variable in vars. But, you can create a custom filter on your own

    shell> cat plugins/filter/template.py
    from jinja2 import Environment, BaseLoader
    
    
    def template(my_string, my_vars):
        rtemplate = Environment(loader=BaseLoader).from_string(my_string)
        return(rtemplate.render(my_vars))
    
    
    class FilterModule(object):
    
        def filters(self):
            return {
                'template': template,
            }
    

    Configure the path to the filter plugins DEFAULT_FILTER_PLUGIN_PATH. For example,

    shell> grep filter ansible.cfg 
    filter_plugins = $PWD/plugins/filter
    

    and use the filter template. As a second argument of the filter you have to provide a dictionary with the variables the template needs for the substitutions

      prop:
        envs: "{{ props.envs|template(vars) }}"
    

    gives what you want

      prop:
        envs: ENV_TEST=true ENV_TEXT=additionaltext
    

    • Example of a complete playbook for testing
    shell> cat pb.yml
    - hosts: localhost
    
      vars:
    
        props: "{{ json_props|b64decode|from_json }}"
        prop:
          envs: "{{ props.envs|template(vars) }}"
    
      tasks:
    
        - debug:
            var: props
        - debug:
            var: additional_variable
        - debug:
            var: prop
    
        - set_fact:
            envs: "{{ props.envs|template(my_vars) }}"
          vars:
            my_vars:
              additional_variable: "{{ additional_variable }}"
        - debug:
            var: envs
    

    gives

    shell> ansible-playbook -e @evars.yml pb.yml
    
    PLAY [localhost] ******************************************************************************
    
    TASK [debug] **********************************************************************************
    ok: [localhost] => 
      props:
        envs: ENV_TEST=true ENV_TEXT={{additional_variable}}
        name: some_value
    
    TASK [debug] **********************************************************************************
    ok: [localhost] => 
      additional_variable: additionaltext
    
    TASK [debug] **********************************************************************************
    ok: [localhost] => 
      prop:
        envs: ENV_TEST=true ENV_TEXT=additionaltext
    
    TASK [set_fact] *******************************************************************************
    ok: [localhost]
    
    TASK [debug] **********************************************************************************
    ok: [localhost] => 
      envs: ENV_TEST=true ENV_TEXT=additionaltext
    
    PLAY RECAP ************************************************************************************
    localhost: ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    • A simpler option is passing the filename instead of the encoded content. For example, given the file
    shell> cat /tmp/test.json 
    {
      "name": "some_value",
      "envs": "ENV_TEST=true ENV_TEXT={{ additional_variable }}"
    }
    

    include the variables from the file into a dictionary by include_vars

    shell> cat pb.yml
    - hosts: localhost
    
      tasks:
    
        - include_vars:
            file: "{{ file_json_props }}"
            name: props
        - debug:
            var: props
    

    This way the expressions are 'templated' (expanded) automatically

    shell> ansible-playbook -e additional_variable=additionaltext -e file_json_props=/tmp/test.json pb.yml
    
    PLAY [localhost] ******************************************************************************
    
    TASK [debug] **********************************************************************************
    ok: [localhost] => 
      additional_variable: additionaltext
    
    TASK [include_vars] ***************************************************************************
    ok: [localhost]
    
    TASK [debug] **********************************************************************************
    ok: [localhost] => 
      props:
        envs: ENV_TEST=true ENV_TEXT=additionaltext
        name: some_value
    
    PLAY RECAP ************************************************************************************
    localhost: ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0