Ansible Create SubFolders Matching Pattern - ansible

I have an ansible playbook, that creates directories by passed an array of directories, owner, and permissions. Our admins are worried, someone will create directories under our O/S Volumes and cause issues with the system. Since we only have a few folders that require root, I'm researching how to whitelist specific folders that are passed in for root only. Other directories and use our internal user to directories that don't require root.
This is what I've come up with, but I have concern with /vs_volue/etc instead of /etc being passed. I can't find a starts with /etc for example. Is there a better way?
---
- name: Create Directories
hosts: target_hosts
vars:
dir_list: '{{ dir_list }}'
permissions: {{ permissions }}
linux_user: 'webuser'
whitelist_dir:
- "/etc"
- "/usr"
tasks:
- name: User to root when creating folders in /etc or /usr
set_fact:
linux_user: "root"
when: dir_list|string|regex_search('{{ item }}')
with_items:
- "{{ whitelist_dir }}"
- name: Create Directories as WebUser by Directory Array Lists by Line Feed
file:
path: "{{ item }}"
mode: "{{ permissions }}"
recurse: yes
state: directory
become: true
become_user: "{{ linux_user }}"
with_items: "{{ dir_list.split('\n') }}"
when: dir_list | search('\n')

Try this.
main.yml
- hosts: target_hosts
vars:
default_linux_user: "webuser"
permissions: "{{ permissions | default(0664) }}"
whitelist_dir:
- "^/etc/.*"
- "^/usr/.*"
tasks:
- include_tasks: create_dir.yml
loop: "{{ dir_list.split('\n') }}"
loop_control:
loop_var: dir
create_dir.yml
- block:
- set_fact:
linux_user: "{{ 'root' if dir is regex(item) else default_linux_user }}"
when: linux_user is undefined and (not linux_user == 'root')
loop: "{{ whitelist_dir }}"
- debug:
msg: "For {{ dir }} - {{ linux_user }} will be set as owner"
- file:
path: "{{ dir }}"
state: directory
mode: "{{ permissions }}"
owner: "{{ linux_user | default(default_linux_user) }}
recurse: yes
become: true
become_user: root
become_method: sudo
always:
- set_fact:
linux_user: default_linux_user

Related

Ansible - loop over multiple items in stdout_lines

I am performing a grep with multiple items.
---
- hosts: my_host
gather_facts: false
vars:
my_list:
- whatever
- something
tasks:
- name: grep for item in search path
shell: "grep -rIL {{ item }} /tmp"
register: the_grep
loop: "{{ my_list }}"
- debug:
msg: "{{ item.stdout_lines }}"
loop: "{{ the_grep.results }}"
Depending on the result, multiple files could match.
msg:
- /tmp/something.conf
- /tmp/folder/file.txt
Q: How would I configure Ansible to loop over the items in stdout_lines?
The use case I'm solving is to delete .ini sections based on the item, but in this case, Ansible doesn't loop over the stdout_lines.
- name: remove stanza from ini file
ini_file:
path: "{{ item.stdout_lines }}"
section: "{{ item.item }}"
mode: '0600'
state: absent
loop: "{{ the_grep.results }}"
when: item.stdout_lines | length > 0
It seems that this doesn't work, but configuring item.stdout_lines[0] gives the partially expected result, since Ansible will use only the first item in that list. But ofc, not the 2nd and so on.
Perhaps there's a prettier answer, but solved it by using with_nested and creating a json_query:
- name: remove stanza from ini file
ini_file:
path: "{{ item.0 }}"
section: "{{ item.1.item }}"
mode: '0600'
state: absent
with_nested:
- "{{ the_grep | json_query('results[].stdout_lines[]') }}"
- "{{ the_grep.results }}"

ansible.builtin.file is using user from wrong host

I have this playbook below to set user/group on the user's home directory.
jimbo and bobo here have different UID and GIDs on the different boxes.
Running this script will set the UID/GID ownership of the directories incorrectly.
For example, it will set /home/jimbo on operatorbox1 (1) to be owned by the UID of jimbo from operatorbox2 (2) - which is of course not the correct UID on operatorbox1 (1).
It does this seemingly randomly. If I run this playbook multiple times the ownership of the directories will flip back and forth.
Guessing I have something fundamental missing here. Why is this happening? Thanks!
ansible-playbook v2.9.23
./vars/operators.yml
---
operators:
jimbo: sshekeywhatever
bobo: sshkeywhatever
playbook.yml
---
- name: Setup operators
hosts:
- bastionbox
- operatorbox
become: true
vars_files:
- "./vars/operators.yml"
tasks:
- name: Set home directory permissions
file:
path: "/home/{{ item.key }}"
state: directory
owner: "{{ item.key }}"
group: "{{ item.key }}"
recurse: true
with_dict:
- "{{ operators }}"
I can't reproduce the problem. Below is a playbook for testing
- hosts: bastionbox,operatorbox
gather_facts: false
become: true
vars:
operators: [jimbo, bobo]
tasks:
- name: Create users
user:
name: "{{ item }}"
shell: /usr/sbin/nologin
uid: "{{ range(2500, 2600)|random }}"
loop: "{{ operators }}"
when: create_users|d(false)|bool
- name: List users uid
block:
- getent:
database: passwd
- debug:
msg: "{{ inventory_hostname }} {{ item }} uid: {{ getent_passwd[item].1 }}"
loop: "{{ operators }}"
when: list_users|d(false)|bool
- name: Set home directory owner and group
file:
state: directory
path: "/home/{{ item }}"
owner: "{{ item }}"
group: "{{ item }}"
recurse: true
loop: "{{ operators }}"
when: set_homes|d(false)|bool
- name: List homes
block:
- find:
paths: /home
file_type: directory
patterns: "{{ operators }}"
register: out
- debug:
msg: "{{ inventory_hostname }} {{ item.path }} uid: {{ item.uid }}"
loop: "{{ out.files }}"
loop_control:
label: "{{ inventory_hostname }}"
when: list_homes|d(false)|bool
- name: Delete users
user:
name: "{{ item }}"
state: absent
remove: true
loop: "{{ operators }}"
when: delete_users|d(false)|bool
Create users
shell> ansible-playbook -e create_users=true pb.yml
List users
shell> ansible-playbook -e list_users=true pb.yml
msg: 'bastionbox jimbo uid: 2572'
msg: 'operatorbox jimbo uid: 2537'
msg: 'bastionbox bobo uid: 2505'
msg: 'operatorbox bobo uid: 2557'
List homes
shell> ansible-playbook -e list_homes=true pb.yml
msg: 'bastionbox /home/bobo uid: 2505'
msg: 'operatorbox /home/jimbo uid: 2537'
msg: 'bastionbox /home/jimbo uid: 2572'
msg: 'operatorbox /home/bobo uid: 2557'
Set homes (task is idempotent)
shell> ansible-playbook -e set_homes=true pb.yml
TASK [Set home directory owner and group] *************************
ok: [operatorbox] => (item=jimbo)
ok: [bastionbox] => (item=jimbo)
ok: [operatorbox] => (item=bobo)
ok: [bastionbox] => (item=bobo)

In Ansible loop, test existence of files from registered results

I have several files that I need to backup in different directories. I have tried the code below and not working for me.
vars:
file_vars:
- {name: /file1}
- {name: /etc/file2}
- {name: /etc/file/file3}
tasks:
- name: "Checking if config files exists"
stat:
path: "{{ item.name }}"
with_items: "{{ file_vars }}"
register: stat_result
- name: Backup Files
copy: src={{ item.name }} dest={{ item.name }}{{ ansible_date_time.date }}.bak
with_items: "{{ file_vars }}"
remote_src: yes
when: stat_result.stat.exists == True
The problem is the condition
when: stat_result.stat.exists == True
There is no attribute stat_result.stat. Instead, the attribute stat_result.results is a list of the results from the loop. It's possible to create a dictionary of files and their statuses. For example
- set_fact:
files_stats: "{{ dict(my_files|zip(my_stats)) }}"
vars:
my_files: "{{ stat_result.results|json_query('[].item.name') }}"
my_stats: "{{ stat_result.results|json_query('[].stat.exists') }}"
Then simply use this dictionary in the condition
when: files_stats[item.name]
Below is a shorter version which creates the dictionary more efficiently
- set_fact:
files_stats: "{{ dict(stat_result.results|
json_query('[].[item.name, stat.exists]')) }}"
Please try using below worked for me:
---
- name: Copy files
hosts: localhost
become: yes
become_user: root
vars_files:
- files.yml
tasks:
- name: "Checking if config files exists"
stat:
path: "{{ item }}"
with_items: "{{ files }}"
register: stat_result
- name: Ansible
debug:
msg: "{{ stat_result }}"
- name: Backup Files
copy:
src: "{{ item }}"
dest: "{{ item.bak }}"
with_items: "{{ files }}"
when: stat_result == "True"
and files.yml will look like:
---
files:
- /tmp/file1
- /tmp/file2
you can check you playbook syntax using below command:
ansible-playbook copy.yml --syntax-check
Also you do dry run your playbook before actual execution.
ansible-playbook -i localhost copy.yml --check

win_find FAILED! => {"msg": "'dict object' has no attribute 'files'"}

I am trying to run win_find with variables for "paths" to find temp files (e.g. c:\users\public\appdata\local\temp) but when I pass it to debug I get the error message
"FAILED! => {"msg": "'dict object' has no attribute 'files'"}"
I have tried swapping the "register" and "with_items" lines but that seems to make no difference at all.
- hosts: windows-02
vars:
dir: \AppData\Local\Temp\
tasks:
- name: find user directories
win_find:
paths: c:\users
recurce: yes
file_type: directory
get_checksum: no
register: user_dir
- name: debug 1
debug:
msg: "{{ item.path + dir }}"
loop: "{{ user_dir.files }}"
- name: find temp files
win_find:
paths: "{{ item.path + dir }}"
recurce: yes
hidden: yes
get_checksum: no
register: files_to_delete
with_items: "{{ user_dir.files }}"
- name: debug
debug:
msg: "{{ item }}"
loop: "{{ files_to_delete.files }}"
- name: remove
win_file:
path: "{{ item.path }}"
state: absent
with_items: "{{ files_to_delete.files }}"
I expect to get a list of files to be deleted which will be passed to the "win_files" module. Instead I'm getting the error message
"FAILED! => {"msg": "'dict object' has no attribute 'files'"}"
There is a typo in win_find module, it is recurse: yes
Please find the below code whcih worked for me
---
- name: Find files
win_find:
paths: "{{paths}}"
age: "{{duration}}"
register: log_files
- name: Delete the files
win_file:
path: "{{item.path}}"
state: absent
with_items: "{{log_files.files}}"
Also files_to_delete will not have files_to_delete.files as it is in loop. It will have files_to_delete.results
So, it appears that win_find, at least in my case, does not return files when using "when_items". I got around this by creating a list of paths and passing it into the "paths" parameter of the module.
Here is my code:
- hosts: "{{ host }}"
vars:
dir: \AppData\Local\Temp\
temp_paths: []
line_break: \n
tasks:
- name: find user directories
win_find:
paths: c:\users
recurse: no
file_type: directory
get_checksum: no
register: user_dir
- name: debug 1
debug:
msg: "{{ item.path + dir }}"
loop: "{{ user_dir.files }}"
- name: set temp path
set_fact: temp_paths="{{temp_paths + [ item.path + dir ] }}"
when: item.path != 'C:\\users\\Public'
with_items: "{{ user_dir.files }}"
- name: find temp files
win_find:
paths: "{{ temp_paths }}"
recurse: True
patterns: '*'
hidden: False
get_checksum: False
register: files_to_delete
# ignore_errors: yes
- name: debug
debug:
msg: "{{ item.path }}"
loop: "{{ files_to_delete.files }}"
- name: remove
win_file:
path: "{{ item.path }}"
state: absent
with_items: "{{ files_to_delete.files }}"

Ansible, iterate over multiple registers and make extended use of build in methods

To start off I have all my variables defined in YAML
app_dir: "/mnt/{{ item.name }}"
app_dir_ext: "/mnt/{{ item.0.name }}"
workstreams:
- name: tigers
service_workstream: tigers-svc
app_sub_dir:
- inbound
- inbound/incoming
- inbound/error
- inbound/error/processed
- name: lions
service_workstream: lions-svc
app_sub_dir:
- inbound
- inbound/incoming
- inbound/error
- inbound/error/processed
You may note app_dir: "/mnt/{{ item.name }}" and app_dir_ext: "/mnt/{{ item.0.name }}" looking odd, so I originally had my variables set as below in YAML but decided to use the above mainly due to less lines in YAML when I have a large amount of workstreams.
workstreams:
- name: tigers
service_workstream: tigers-svc
app_dir: /mnt/tigers
...
I then have Ansible code to check if the directories exists, if not create them and apply permissions (!note, have taken this approach due to a ssh timeout on operation when using the file: module on a number of very big NFS mounted shares).
- name: Check workstreams app_dir
stat:
path: "{{ app_dir }}"
register: app_dir_status
with_items:
- "{{ workstreams }}"
- name: Check workstreams app_sub_dir
stat:
path: "{{ app_dir_ext }}/{{ item.1 }}/"
register: app_sub_dir_status
with_subelements:
- "{{ workstreams }}"
- app_sub_dir
- name: create workstreams app_dir
file:
path: "/mnt/{{ item.0.name }}"
state: directory
owner: "ftp"
group: "ftp"
mode: '0770'
recurse: yes
with_nested:
- '{{ workstreams }}'
- app_dir_status.results
when:
- '{{ item.1.stat.exists }} == false'
This is a little hacky but works, however I have a 2nd, 3rd, 4th path to check..etc
My question here is how to I update/refactor the above code to use <register_name>.stat.exists == false from both app_dir_status and app_sub_dir_status to control my task ?
You don't need to make nested loop! There's all required data inside app_sub_dir_status – just strip unnecessary items.
Here's simplified example:
---
- hosts: localhost
gather_facts: no
vars:
my_list:
- name: zzz1
sub:
- aaa
- ccc
- name: zzz2
sub:
- aaa
- bbb
tasks:
- stat:
path: /tmp/{{ item.0.name }}/{{ item.1 }}
register: stat_res
with_subelements:
- "{{ my_list }}"
- sub
- debug:
msg: "path to create '{{ item }}'"
with_items: "{{ stat_res.results | rejectattr('stat.exists') | map(attribute='invocation.module_args.path') | list }}"
You can iterate over stat_res.results | rejectattr('stat.exists') | list as well, but will have to construct path again as /tmp/{{ item.item.0.name }}/{{ item.item.1 }} – note double item, because first item is an element of stat_res.results which contain another item as element of your original loop for stat task.
P.S. Also I see no reason for your first task, as subdir task can detect all missing directories.

Resources