Reputation: 1036
This is my wanted flow for a playbook:
Take a backup of a remote Linux configuration file I intend to (possibly) modify. The backup should be persisted to disk (on remote server) before running my main changing tasks (in case the playbook crashes half-way).
Perform several Ansible tasks that may change the file. For example, 2 lineinfile and 3 blockinfile tasks.
If the file has been changed by the tasks in (2.) I want to keep my backup file. Otherwise the file should be deleted.
In addition, I would like to do the same for multiple configuration files (about 10), for example /etc/ssh/sshd_config
and /etc/ntp.conf
etc. I would like the backup code to be as concise as possible. How can I perform the steps (1.) and (3.) in the best way?
What I have tried/investigated:
backup
parameter that exist in many of the file modifying
modules (lineinfile, blockinfile etc) is not optimal since it will
create one backup per task invocation.Below is a naïve and verbose example on how to do it for only 1 configuration file. For 9 more configuration files the code would get much bigger.
---
- hosts: all
gather_facts: False
become: yes
tasks:
- name: Create temporary backup of /etc/ssh/sshd_config
copy:
src: "/etc/ssh/sshd_config"
remote_src: yes
dest: "/etc/ssh/sshd_config_{{ now().strftime('%Y-%m-%d_%H_%M_%S') }}.bak"
register: "sshd_config_backup"
changed_when: false
- name: Change sshd ciphers
lineinfile:
dest: /etc/ssh/sshd_config
regexp: '^Ciphers '
line: "Ciphers aes192-ctr"
notify: "sshd config changed"
# 3 more lineinfile/blockinfile tasks that (may) change the same file
# name: ...
# name: ...
# name: ...
# Removing backup file if not changed
- name: Get checksum of /etc/ssh/sshd_config
stat:
path: "/etc/ssh/sshd_config"
get_checksum: yes
register: sshd_config_stat
- name: Remove backup of /etc/ssh/sshd_config if there are no changes
file:
path: "{{ sshd_config_backup.dest }}"
state: absent
changed_when: false
when: sshd_config_stat.stat.checksum == sshd_config_backup.checksum
handlers:
- name: Reload sshd service
listen: sshd config changed
service:
name: sshd
state: reloaded
Upvotes: 0
Views: 6929
Reputation: 67984
The play below probably does the job that you described
- name: conf_light
hosts: all
gather_facts: no
become: yes
vars_files:
- data1.yml
vars:
cl_backup: yes
tasks:
- name: Create time-stamp
when: cl_backup
set_fact:
cl_timestamp: "{{ '%Y-%m-%d_%H_%M_%S'|strftime }}"
- name: Create backup files
when: cl_backup
copy:
remote_src: yes
src: "{{ item.value.path }}"
dest: "{{ item.value.path }}_{{ cl_timestamp }}.bak"
loop: "{{ cl_confs|dict2items }}"
- name: Configure lines in files
lineinfile:
path: "{{ item.0.path }}"
regexp: "{{ item.1.regexp }}"
line: "{{ item.1.line }}"
loop: "{{ cl_confs|subelements('lines') }}"
notify: "{{ item.0.handler|default(omit) }}"
register: cl_results_lines
- name: Configure blocks in files
blockinfile:
path: "{{ item.0.path }}"
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ item.1.marker }}"
block: "{{ item.1.block }}"
loop: "{{ cl_confs|subelements('blocks') }}"
notify: "{{ item.0.handler|default(omit) }}"
register: cl_results_blocks
- name: Remove backup files that did not change
when: cl_backup
file:
state: absent
path: "{{ item }}_{{ cl_timestamp }}.bak"
loop: "{{ cl_confs|
json_query('*.path')|
difference(cl_results_lines.results|default([])|
json_query('[?changed==`true`].invocation.module_args.path'))|
difference(cl_results_blocks.results|default([])|
json_query('[?changed==`true`].invocation.module_args.path'))
}}"
handlers:
- name: ssh reload
service:
name: ssh
state: reloaded
I've tested it in Ubuntu 18.04 with the data below. (It should not be a problem to fit the data to any other Linux)
shell> cat data1.yml
cl_confs:
sshd_config:
path: /etc/ssh/sshd_config
handler: ssh reload
lines:
- regexp: '^Ciphers '
line: 'Ciphers aes192-ctr'
blocks: []
ssh_config:
path: /etc/ssh/ssh_config
lines: []
blocks:
- marker: 'srv1.example.com'
block: |2
Host srv1.example.com
Protocol 2
ForwardAgent no
It seemed like a good idea for a simple role.
Upvotes: 3