One of the features of the ‘lineinfile‘ regexp parameter is the ability to use regular expression capture groups in the line output. That allows you to extract values on a found line when constructing the output line.
Specifically, that can mean pulling information such as hostname/port, file path, or preserving the yaml indentation of an original line as I will show in this article.
Please understand there are more yaml native ways of manipulating yaml with Ansible, such as the ansible-role-yedit role or using a tool such as yq from an Ansible shell command, however yaml indentation provides a clear example of regex capture groups for our purpose here. Assume a sample yaml file:
root: child1: key1: value1 #key2: value2 arrays: - animals - bear - cat - dog #- elephant - servers - www.google.com - www.ansible.com
A regular expression like below can capture the amount of spacing in front of the item, so that instead of having to hardcode a certain number of spaces, you can simply use the “\1” as representative of the first capture group when creating the output.
'^(\s*)[#]?{{ item.search }}(: )*'
Consider the Ansible playbook below:
- name: Replace values in yml file lineinfile: backup: no backrefs: yes state: present path: ./my.yml regexp: '^(\s*)[#]?{{ item.search }}(: )*' line: '\1{{ item.replace }}' with_items: - { search: 'key1', replace: 'key1: NEWvalue1' } - { search: 'key2', replace: 'key2: NEWvalue2' } - { search: '\- elephant', replace: '- heffalump' } - { search: '\- www.ansible.com', replace: '- www.redhat.com' }
This regular expression can take either scalar (e.g. ‘key2’) or list items (e.g. ‘- elephant’) and update their value and remove the comment hash that may prefix it. Notice that the output line has the “\1” capture group which preserves the same spacing as the original line.
Running the sample yaml through the ansible playbook results in:
root: child1: key1: NEWvalue1 key2: NEWvalue2 arrays: - animals - bear - cat - dog - heffalump - servers - www.google.com - www.redhat.com
The ‘key1’ value has been updated, the ‘key2’ has been uncommented, the place where ‘elephant’ was commented out in the animal array has been updated with ‘heffalump’, and www.ansible.com has been replaced with www.redhate.com.
If you have Ansible installed and want to test on a local playbook, download my files here:
- my.yml.bak – sample yml file
- test-lineinfile.yml – local playbook for doing regex lineinfile replacement
- test-ansible-lineinfile.sh – script that executes local playbook
This playbook goes one step further and shows how child entries can be added to the yaml at the specified level. The below adds a ‘key3’ at the same level as ‘key2’ and adds a new ‘deer’ animal right under the cat array item.
- name: add item to yml file at correct indentation level lineinfile: backup: no backrefs: yes state: present path: ./my.yml regexp: '^(\s*)[#]?{{ item.search }}(.*)' line: '\1{{ item.search }}\2\n\1{{ item.add }}' with_items: - { search: 'key2', add: 'key3: INSvalue3' } - { search: '- cat', add: '- deer' }
REFERENCES
ansible.com, lineinfile documentation
stackoverflow.com, yaml editing module for ansible
github kwoodson, ansible-role-yedit