Using Ansible to modify files
Managing many configuration files can be tedious. There may be vast differences between OS and service. This page is intended to show how to modify configuration and other files on systems
Ansible template module
Ansible Templates are very useful for configuring a system with lots of changes per configuration file. They can also be helpful when trying to build out vastly different configurations automatically by using dynamic/changing variables.
Documentation for them can be found here.
To properly leverage templates, it requires that one is already using with_first_found
function within the include_vars
module for OS separation. For more information on this, see the docs here.
A decent example of how templates work, can be found in the examples within this repository. Here is the snipit for how to do this:
- name: gather os specific variables
include_vars: "{{ item }}"
with_first_found:
- "{{ ansible_distribution }}-{{ ansible_distribution_major_version}}.yml"
- "{{ ansible_distribution }}.yml"
- "defaults.yml"
tags: vars
- name: configure ssh
template: src={{ item }} dest={{ SSH_CONFIG }} backup=yes
with_first_found:
- "{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.sshd_config.j2"
- "{{ ansible_distribution }}.sshd_config.j2"
tags: template-sshd-config
The directory structure will look something like this:
ssh/
...
├── tasks
│ └── main.yml
├── templates
│ ├── CentOS-5.sshd_config.j2
│ ├── CentOS-6.sshd_config.j2
│ ├── CentOS-7.sshd_config.j2
│ ├── Ubuntu-12.sshd_config.j2
│ └── Ubuntu-14.sshd_config.j2
└── vars
├── CentOS.yml
└── Ubuntu.yml
Vagrant can be extremely useful in getting blank configuration files to then be used as templates later on. One important thing to note is that configuration files change, so be sure to replace templates with updated versions whenever new ones are released. CentOS system configurations will be appended with an *.rpm.new
where as Ubuntu systems will append a *.dpkg-old
OR *.dpkg-dist
Ansible lineinfile module
lineinefile
+ with_items
- name: add groups to sudoers
lineinfile: dest=/etc/sudoers regexp="^root(\s+)ALL=(ALL)(\s+)ALL" insertafter="^root" line='{{ item }}' state=present backup=yes backrefs=yes
with_items:
- '%admin\tALL=(ALL:ALL)\tALL'
- '%users\tALL=(ALL:ALL)\tALL'
tags: sudoers
Using lineinfile to comment out a line:
ansible -m lineinfile -i hosts -e "ansible_ssh_port=2222" -a "dest=/etc/aliases regexp='^(.*)root:(.*)' line='#root: systems@domain.com'" host-group --limit "host1"
BEFORE:
root: systems@domain.com
AFTER:
#root: systems@domain.com
Ansible ini_file module
This module is a life-saver when modifying ini
type files with many changes.
The configuration file may look like this:
[DEFAULT]
# Print more verbose output (set logging level to INFO instead of default WARNING level).
# verbose = False
# Print debugging output (set logging level to DEBUG instead of default WARNING level).
# debug = False
ansible multi-os -m ini_file -a "dest=/etc/setting.conf section=DEFAULT option=verbose value=True backup=yes" -i hosts
ini_file
module example:
- name: enable verbose mode
ini_file: dest=/etc/setting.conf section=DEFAULT option=verbose value=True backup=yes
tags: configuration
After change:
[DEFAULT]
# Print more verbose output (set logging level to INFO instead of default WARNING level).
verbose = True
# Print debugging output (set logging level to DEBUG instead of default WARNING level).
# debug = False
Using RegEx
Ansible uses Python's Regular Expressions to modify files with modules like lineinfile
. Since everyone loves RegEx, but hates writing them, having tools to make it easier always helps.
To see what RegEx are available, see this page: https://docs.python.org/2/library/re.html
When work has begun, it is often hard to tell without running code if it is working or not. This incredibly helpful site can help verify your RegEx: http://pythex.org/
One last major caveat is that Python RE is different in that what works in vanilla Python code for Regular Expressions, do not often do well when being present in YAML. For this reason, be sure that every RE is double escaped when put into Ansible. See the example below:
Python RE
RegEx Checker: http://pythex.org/
Regular Expression:
^{{ ATMOUSERNAME }}\s+ALL=\(ALL\)\s*ALL
Test string:
{{ ATMOUSERNAME }} ALL=(ALL)ALL
Matched string (Ensure that entire matched result is GREEN):
{{ ATMOUSERNAME }} ALL=(ALL)ALL
Python RE in Ansible
Python Regular Expression:
^{{ ATMOUSERNAME }}\s+ALL=\(ALL\)\s*ALL
Ansible Regular Expression:
^{{ ATMOUSERNAME }}\\s+ALL=\\(ALL\\)\\s*ALL
Usage example:
- lineinfile: dest=/etc/sudoers backup=yes regexp="^{{ ATMOUSERNAME }}\\s+ALL=\\(ALL\\)\\s*ALL" line="{{ ATMOUSERNAME }} ALL=(ALL) ALL" state=present