Search code examples
variablesansiblejinja2

Ansible write on file from jinja file


I want to lanch a application :

command=/usr/bin/toto --config /var/toto/conf1.json /var/toto/conf2.json /var/toto/conf3.json

the config file are on /var/toto directory

task.yml

- name: Ansible find file
  find:
    paths: "/var/toto"
  register: found_files

- name: print files
  debug:
    msg: "{{ found_files['files'] | map(attribute='path') | map('regex_replace','^.*/(.*)$','\\1') | list }}"
  register: file_name
 
- name: Create the Jinja2 based template
  template:
    src: "etc/control/config.conf.j2"
    dest: "/etc/control/config.conf"
  with_dict: "{{ file_name }}"

config.conf.j2

command=/usr/bin/toto --config {% for item in file_name %} /var/toto/{{ item }} {% endfor %}

but, I have get this on my file

/etc/control/config.conf

command=/usr/bin/toto --config  /var/opt/msg  /var/opt/failed  /var/opt/changed

varfile_name :

"msg": [                                                                                                                                                                        "conf1.json",                                                                                                                                                       "conf2.json",                                                                                                                                                          "conf3.json"                                                                                                                                                                                                                                                                                                               
]

Solution

  • You're iterating over the dictionary in file_name, which is a task result. If you were to print that out, you would find that it contains something like:

    TASK [debug] *********************************************************************************************************************************************************************************
    ok: [localhost] => {
        "file_name": {
            "changed": false,
            "failed": false,
            "msg": [
                "file1",
                "file2",
                "file3"
            ]
        }
    }
    

    So when you iterate over it using for item in file_name, you're iterating over the top level keys (changed, failed, msg).

    But this is all the wrong way to do it. You never use the debug module to create variables; that's what set_fact is for. You want something like:

    - name: build list of files
      set_fact:
        file_name: "{{ found_files['files'] | map(attribute='path') | map('regex_replace','^.*/(.*)$','\\1') | list }}"
    
    - name: Create the Jinja2 based template
      template:
        src: "config.conf.j2"
        dest: "config.conf"
    

    After the set_fact task, the variable file_name will contain a list of file names.


    It looks like you're using that regular expression to get the basename of the files found in your find task. There's a basename filter that can do that with less complexity:

    - name: print files
      set_fact:
        file_name: "{{ found_files['files'] | map(attribute='path') | map('basename') | list }}"
    
    - name: Create the Jinja2 based template
      template:
        src: "config.conf.j2"
        dest: "config.conf"
    

    Here's the playbook I'm using to test this locally:

    - hosts: localhost
      gather_facts: true
      tasks:
        - name: find files
          find:
            paths: /tmp/example
          register: found_files
    
        - name: print files
          set_fact:
            file_name: "{{ found_files['files'] | map(attribute='path') | map('basename') | list }}"
    
        - name: Create the Jinja2 based template
          template:
            src: "config.conf.j2"
            dest: "config.conf"
    

    Before running this, I run:

    mkdir /tmp/example
    touch /tmp/exampe/file{1,2,3}
    

    This produces a config.conf file that looks like:

    command=/usr/bin/toto --config  /var/toto/file3  /var/toto/file2  /var/toto/file1