ansible playbook find/replace acting weird on second run - ansible

I have this task that finds 2 lines in sshd_config, and changes them. This works perfectly on the first run, it replaces the src lines with the dest lines. But if I run it a second time, after the 2 lines in the file are already correct, it appends 2 new lines to the bottom of the file.
I tried adding state: present but it didn't change the behavior.
- name: Configuring sshd_config
lineinfile:
path: /etc/ssh/sshd_config
regexp: "{{ item.src }}"
state: present
line: "{{ item.dest }}"
with_items:
- { src: "#PrintLastLog yes", dest: "PrintLastLog no" }
- { src: "#Banner none", dest: "Banner /etc/issue.net" }
I was expecting it to not match the src lines and make no changes.

What you are getting is the exact expected behaviour. Quoting the documentation about the regexp parameter when using state: present
When modifying a line the regexp should typically match both the initial state of the line as well as its state after replacement by line to ensure idempotence.
Modifying your task as follow should do the job.
- name: Configuring sshd_config
lineinfile:
path: /etc/ssh/sshd_config
regexp: "{{ item.src }}"
state: present
line: "{{ item.dest }}"
with_items:
- { src: "^(# *)?PrintLastLog", dest: "PrintLastLog no" }
- { src: "^(# *)?Banner", dest: "Banner /etc/issue.net" }
This will match the commented line (with optionnal spaces after the hash) or the newly modified line.

Related

Ansible delete Files with wildcard/regex/glob with exception

I want to delete files based on a wildcard but also add exceptions to the rule.
- hosts: all
tasks:
- name: Ansible delete file wildcard
find:
paths: /etc/wild_card/example
patterns: "*.txt"
use_regex: true
register: wildcard_files_to_delete
- name: Ansible remove file wildcard
file:
path: "{{ item.path }}"
state: absent
with_items: "{{ wildcard_files_to_delete.files }}"
For example I want to except a file named "important.txt". How can I do that?
Just add a when condition to the task that deletes files. E.g., something like:
- name: Ansible remove file wildcard
file:
path: "{{ item.path }}"
state: absent
when: item.path != '/etc/wild_card/example/important.txt'
with_items: "{{ wildcard_files_to_delete.files }}"
This will skip a specific file. If you have a list of files to skip you could do this instead:
- name: Ansible remove file wildcard
file:
path: "{{ item.path }}"
state: absent
when: item.path not in files_to_skip
with_items: "{{ wildcard_files_to_delete.files }}"
vars:
files_to_skip:
- /etc/wild_card/example/important.txt
- /etc/wild_card/example/saveme.txt
And if you want to preserve files based on some sort of pattern, you could make use of ansible's match or search tests:
- name: Ansible remove file wildcard
file:
path: "{{ item.path }}"
state: absent
when: item.path is not search('important.txt')
with_items: "{{ wildcard_files_to_delete.files }}"

Split filename with multiple variables

We have sets of config files that end in the environment extension (eg. app.properties.prod, app.properties.dev, db.prod, db.dev, and so on). I am passing Ansible a variable named environment=prod with the intention of pulling just the files ending in a .prod extension from a filerepo and then need to drop that suffix from the filename so it ends up as app.properties
Something like this will find the correct files:
(env = prod)
copy:
src: "{{item}}"
dest: /app/homedir
with_fileglob:
- /go/to/my/repo/*{{env}}
This copies the correct files to my /app/homedir
However, trying to drop the env file extension does not work
copy:
src: "{{dropsuffix}}"
dest: "{{dropsuffix.split('.{{env}}')[0] }}"
with_fileglob:
-/app/homedir/*.{{env}}
loop_control:
loop_var: dropsuffix
However removing the {{env}} and just adding the text 'prod' will work
dest: "{{dropsuffix.split('.prod')[0] }}"
I'm assuming there is some jinja formatting issue with the variable nested in there, I've tried various permutations and I'm stumped
It's possible to use regex_replace filter to remove the extension.
- copy:
src: "{{ item }}"
dest: "/app/homedir/{{ item|basename|regex_replace(regex, replace) }}"
loop: "{{ lookup('fileglob', '/go/to/my/repo/*.' ~ env, wantlist=True) }}"
vars:
regex: "{{ '^(.*)\\.' ~ env ~ '$' }}"
replace: "{{ '\\1' }}"

who i can remplace multiple line in a cfg

I would like some help to be able to replace several lines in a cfg file with the ansible module lineinfile
- name: "[MODIFY /etc/zabbix/zabbix.agentd.conf]
lineinfile:
path: /etc/zabbix/zabbix.agentd.conf]
insertafter: "{{ item}}"
with_items:
- line 99
- line 77
line: "{{ item }}"
with_items:
- set number
- :colorschem murphy
become: yes
become_method: sudo
A: It's not possible to use two loops (with_items) in one task.
Put the configuration data into one loop. Try the task below
- name: MODIFY /etc/zabbix/zabbix.agentd.conf
lineinfile:
path: /etc/zabbix/zabbix.agentd.conf
line: "{{ item.line }}"
insertafter: "{{ item.after }}"
loop:
- line: 'line 99'
after: 'set number'
- line: 'line 77'
after: 'colorschem murphy'
become: yes
become_method: sudo
Q: I want replaces line 99 by set number
A: Use replace module.
- name: MODIFY /etc/zabbix/zabbix.agentd.conf
replace:
path: /etc/zabbix/zabbix.agentd.conf
regexp: "{{ item.regexp }}"
replace: "{{ item.replace }}"
loop:
- regexp: 'line 99'
replace: 'set number'
become: yes
become_method: sudo
(not tested)
This might be a good time to use templates:
- name: MODIFY /etc/zabbix/zabbix.agentd.conf
template:
src: template/zabbix.agentd.conf.j2
dest: /etc/zabbix/zabbix.agentd.conf
The file zabbix.agentd.conf.j2 should be a copy of the file you want to copy at the dest path. Your variable goes in the j2 file and can be change at each interaction.
Example:
vi zabbix.agentd.conf.j2
line 1 my file
line 2
line 3
.
.
line 99 {{ set number }}

Remove the users from /etc/ssh/sshd_config with Ansible

I am trying to remove users added to the AllowGroups line in the /etc/ssh/sshd_conf file using the following after the comments from #imjoseangel.
The current line is
AllowGroups devops1 devops2 devops3 user01 user02 user03
The desired line is
AllowGroups devops1 devops2 user02 user03
The play is
name: Remove User from AllowGroups (opening dash removed)
replace:
regexp: "(^AllowGroups)\\s.*"
backup: True
path: /etc/ssh/sshd_config.bak
replace: "\\1 {{ item }} "
with_items:
- devops3
- user01
After the play is complete I am getting the following
"AllowGroups user01"
The whole is replaced by last user in the "item".
This solution worked.
tasks:
- name: Remove User from AllowGroup and AllowUsers
replace:
regexp: "{{ item }}"
backup: True
path: /etc/ssh/sshd_config.bak
replace: "\b"
with_items:
- devops1
- user03

Ansible loops in lineinfile

Commenting out multiple lines should work with a standard loop [1] like this:
- name: "Allow /srv folder accessed by default. Just comment out the lines to allow."
lineinfile: dest=/etc/apache2/apache2.conf regexp={{ item.regexp }} line={{ item.line }} state=present
with_items:
- { regexp: '#<Directory /srv/>', line: '<Directory /srv/>' }
But I got an error:
failed: [192.168.101.101] => (item={'regexp': '#<Directory /srv/>', 'line': '<Directory /srv/>'}) => {"failed": true, "item": {"line": "<Directory /srv/>", "regexp": "#<Directory /srv/>"}}
msg: this module requires key=value arguments (['dest=/etc/apache2/apache2.conf', 'regexp=#<Directory', '/srv/>', 'line=<Directory', '/srv/>', 'state=present'])
FATAL: all hosts have already failed -- aborting
So how to get this working with multiple lines/items?
[1] http://docs.ansible.com/playbooks_loops.html#standard-loops
Thank you, tedder42! You we're more than right.
To be idempotent, the lineinfile task needs to match both the commented and uncommented state of the line so we start it: ^#?
So the fully functioning play set out to be:
- name: "Allow /srv folder accessed by default. Comment out the lines to allow. "
lineinfile:
dest=/etc/apache2/apache2.conf
regexp="{{ item.regexp }}"
line="{{ item.line }}"
state=present
with_items:
- { regexp: '^#?<Directory /srv/>', line: '<Directory /srv/>' }
- { regexp: '^#?\tOptions Indexes FollowSymLinks', line: '\tOptions Indexes FollowSymLinks' }
- { regexp: '^#?\tAllowOverride None', line: '\tAllowOverride None' }
- { regexp: '^#?\tRequire all granted', line: '\tRequire all granted' }
- { regexp: '^#?</Directory>', line: '</Directory>'}
This is actually not a good idea. Definitely better is use the copy with backup=yes.
You were really close to having it working. Simply add quotes around the regexp and line.
lineinfile: dest=/etc/apache2/apache2.conf regexp="{{ item.regexp }}" line="{{ item.line }}" state=present
I wasn't entirely sure, but the error message implied there were problems with seeing the regexp and line args, so I tried a few things.
As a reminder, lineinfile is somewhat of an antipattern. When you find yourself using it, that's a sign you should consider switching to copy or template.

Resources