0

I am looking for a way to modify a configuration file while preserving local modifications. The format of the configuration file is similar to this:

entry1: This is a local
entry2: modification.
entry3:

The file consists of a variable number of keys (entry1, entry2, entry3, and possibly later entry4, etc. - it could go up to 100, and in the future maybe even up to 2000 or so) that should be set up by Ansible, followed by additional options that another program will add to the configuration file.

I want Ansible to be able to add new keys to the file, while preserving any of the existing local modifications.

The most natural fit seems lineinfile - but unfortunately it only gives me the option of either preserving the local modifications (using backref=yes), or adding new keys (backref=yes will not add new lines).

Is there a good way to accomplish what I need?

I can modify the existing entries with lineinfile and backref enabled:

- lineinfile:
  path: myfile.conf
  regexp: "^{{ item }}: (.*)"
  backref: yes
  line: "{{ item }}: \1"
  with_items:
    - entry1
    - entry2
    - entry3
    - entry4

But this will not add entry4 to my file.

Or I can use backref: no

- lineinfile:
  path: myfile.conf
  regexp: "^{{ item }}: (.*)"
  backref: no
  line: "{{ item }}:"
  with_items:
    - entry1
    - entry2
    - entry3
    - entry4

But this will clobber local modifications for entry1, entry2 and entry3.

Or I could change the regexp:

- lineinfile:
  path: myfile.conf
  regexp: "^"
  backref: no
  line: "{{ item }}:"
  with_items:
    - entry1
    - entry2
    - entry3
    - entry4

But this will of course add each key on every run.

I've also looked at using a template (but have not found an easy way to use it to manipulate an existing file).

Of course I can write my own Python module, but there must be an easier way to do this?

0

If you are modifying well known configuration files, you can use Augeas, with this ansible augeas plugin: https://github.com/paluh/ansible-augeas

This is one example how to use augeas:

- name: Force password on sudo group
  action: augeas path=/files/etc/sudoers/spec[user=\"sudo\"]/host_group/command/tag value=PASSWD

Another option is to use lineinfile command to make sure the line that you want is available in the file: http://docs.ansible.com/ansible/latest/lineinfile_module.html

example:

- lineinfile:
    path: /etc/selinux/config
    regexp: '^SELINUX='
    line: 'SELINUX=enforcing'
  • I'll check out Augeas - thank you! The lineinfile approach you proposed is actually the second one from my question. It is not an option because it will clobber the local modifications. The configuration file is actually a node list for Torque, but that probably doesn't add any helpful information to the question. – Kevin Keane Dec 5 '17 at 17:12
0

I found a way how to do this. The trick is to first use grep to find only the missing entries.

grep will error out if the file does not exist, so first I am creating the file:

copy: dest: myfile.conf content: "" force: no # set mode, owner, group to taste

Now use grep to find only the missing items. Grep will return 0 if the entry already exists, or 1 when it doesn't. Normally, a return code of 1 means failure to Ansible. failed_when: True changes that. Since this is only retrieving information and not changing anything, it should never report as "changed", so changed_when needs to be set as well.

command: 'grep "^{{ item }}" myfile.conf' with_items: - entry1 - entry2 - entry3 - entry4 failed_when: False changed_when: False register: grep_output

Grep will produce the output into the registered variable grep_output. When used in a loop, grep_output will contain an array called results, which contains one hash for each item from the loop. In that array, we find all the information we need: both the return code (called rc) and the original item from the loop (called item).

Now we can add only the missing entries by checking for rc. I'm not sure if the regexp is even required here.

lineinfile: path: myfile.conf regexp: "^{{ item.item }} *(.*)" insertafter: EOF line: '{{ item.item }}' state: present when: "item.rc > 0" with_items: "{{ grep_output.results }}"

-3
 lineinfile: path: myfile.conf regexp: "^{{ item }}: (.*)" backref: no line: "{{ item }}:" with_items: - entry1 - entry2 - entry3 - entry4
  • This proposal will clobber any local changes. It would replace "entry2: modification" with just "entry2:" – Kevin Keane Dec 5 '17 at 17:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.