Image: Pexels - Evgeny Tchebotarev

Writing config files

ansible Apr 2, 2023

That's an amazingly boring title for something that simply could be seen as magic. Sometimes with Ansible you'd really want to write out files without actually defining the contents. Vague ? :) Yeah i'd guess.

As an example for a configuration file lets assume that using Ansible combined with a Jinja template you would have something like this:

# Jinja template
set_default = {{ template_set_default }}
change_color = {{ some_variable }}

All the above variables would have to be set ( when not using defined and other logic ), but what if you have multiple files with different content.

Well, that was the challenge for today :)

Writing the role

Since i havent reached my caffeine high yet, lets create a role named arwain simply an acronymn for 'Ansible Role Without An Interesting Name'.

ansible-galaxy init --offline roles/adhoc/arwain

Variables

I'll be testing/working on seenine . Lets define some variables to work with. Preferably in inventory/host_vars/seenine

arwain_variables:
  testing_myfile1.txt:
    INI_main: true
    entry1: 'yes'
    using_color: 'no'
  testing_myfile2.txt:
    text: 'no'
    hold_camera: 'maybe'

Tasks

In this particular example looking at the variables i'd like to see files written to (and i'll append /tmp/ to it) /tmp/testing/myfile1.txt and myfile2.txt , Will test if a variable starts with INI to encapsulate it with '[]'. And use the rest as file contents. Obviously when all this works we can write any type of file or add more magic.

roles/adhoc/arwain/tasks/main.yml

---
# tasks file for roles/adhoc/arwain

- name: Create list of keys as dir/filenames
  set_fact:
    l_filenames: "{{ arwain_variables.keys() | list }}"

- name: Include tasks for writing multiple templates
  include_tasks: write_template.yml
  with_items: "{{ l_filenames }}"
  loop_control:
    loop_var: d_filename

roles/adhoc/arwain/tasks/write_template.yml

---
- name: Create fact with variables per file
  set_fact:
    my_items: "{{ arwain_variables[d_filename] }}"

- name: Create directory
  file:
    path: "/tmp/{{ d_filename.split('_')[0] }}"
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: 0750
    state: directory

- name: Write template
  template:
    src: templates/write_template.j2
    dest: "/tmp/{{ d_filename|replace('_','/') }}"
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: 0644

Template(s)

And for the template file roles/adhoc/arwain/templates/write_template.j2

{{ ansible_managed | comment }}
{% for entry in my_items %}
{% if entry.startswith('INI') %}
[{{ entry.split('_')[1] }}]
{% else %}
{{ entry }} = {{ my_items[entry] }}
{% endif %}
{% endfor %}

Tests

Change the roles/adhoc/arwain/tests/tests.yml file to match your test environment.

---
- hosts: 'seenine*'
  remote_user: ansible
  become: no
  gather_facts: no
  roles:
    - roles/adhoc/arwain

Lets put it to the test

ansible-playbook roles/adhoc/arwain/tests/test.yml

That seems to work, lets check the contents of a file:

cat /tmp/testing/myfile1.txt

#
# Okairi.nl - This file is managed by Ansible.
#
[main]
entry1 = yes
using_color = no

And file 2:

#
# Okairi.nl - This file is managed by Ansible.
#
text = no
hold_camera = maybe

And thats it :)

Tags

Riccardo B.

Riccardo is an all round Linux Systems Engineer with over 20 years of experience and a knack for Automation. Favoring acronyms like NAO, IaC, SRE and more. Also hardly ever writes in third person :)