Using Ansible templates to maintain partial file blocks

Ansible provides some excellent utilities for maintaining single lines and partial blocks of text. Both modules have support for handling template and fact variables, and a variety of options to support your use case. Here are a couple of examples using each module.

Using the lineinfile module for a single line. This module will not support inserting newline \n characters:

- lineinfile: >
    dest=/etc/memcached.conf
    regexp='^-m [\d]*'
    line='-m {{ memory }}'
    state=present

Using the blockinfile module for multiple lines:

- blockinfile:
    dest: /etc/hosts
    content: |
      127.0.0.1 {{ ansible_hostname }}.local
      ::1       {{ ansible_hostname }}.local
    state: present

The blockinfile module offers several useful options such as:

  • insertafter and insertbefore to manage exactly where you need the block to be inserted. Useful for structured files like XML.
  • marker to customize block markers, allowing you to manage multiple blocks in the same file.

In my case, I have a much larger block I'd like to be able to maintain using a separate Jinja2 template file. For this we will need to use the more advanced lookup plugin, and capture the template content.

- set_fact:
    hosts_content: "{{ lookup('template', 'templates/etc-hosts.j2') }}"

- blockinfile:
    dest: /etc/hosts
    content: '{{ hosts_content }}'
    state: present

In this manner, you can keep your tasks configuration file concise by storing large blocks of content more appropriately in a separate file.


A Single Line Shell Solution

In some cases, Ansible might be overkill. If you only need to ensure that a single line exists, and the order within the file does not matter, you can use something like this simple one-liner. This will check if the line exists, and append it to the end of the file if it does not.

Append line to file if it doesn't exist:

LN="127.0.0.1 dev-local"
grep -q -F "${LN}" /etc/hosts || echo "${LN}" | sudo tee -a /etc/hosts