Ansible recursively set permission on directory - ansible

I am trying to set permission on directories from a list.
Below is an example list where I would like dirA, dirB, dirC and dirD to get o-w permission.
"__cronjob_paths": [
"/dirA/dirB",
"/dirC/dirD",
]
file module has recursive option but it also sets the permissions on the files within the directories so it's not an option.
Would a regex or filter be the best way to solve this? Maybe filter out the /and save the directories into another list which i can loop through and set permissions?
I also need to have in mind that there could be other directories within the system that has a directory with the same name as the one from the list (which should not get o-w)

Create lists of the subdirs and indexes, e.g.
- set_fact:
_paths: "{{ _paths|d([]) + [{'dir': _dir, 'idx': _idx}] }}"
loop: "{{ __cronjob_paths }}"
vars:
_dir: "{{ item.split('/')|select() }}"
_idx: "{{ range(1, _dir|length + 1) }}"
gives
_paths:
- dir:
- dirA
- dirB
idx:
- 1
- 2
- dir:
- dirC
- dirD
idx:
- 1
- 2
Now, you can iterate idx subelements and join the subdirs, e.g.
- debug:
msg: "chmod o-w /{{ item.0.dir[:item.1]|join('/') }}"
with_subelements:
- "{{ _paths }}"
- idx
gives
msg: chmod o-w /dirA
msg: chmod o-w /dirA/dirB
msg: chmod o-w /dirC
msg: chmod o-w /dirC/dirD
Proceed either with command or file module if this is what you want.

Related

List directory names using Ansible Find module

I need to find folder names in /home/test directory. Once I have the list of folder names I need to loop over a task. I am able to retrieve the complete path, but not able to retrieve the folder name. is my approach wrong, or is there any easy way to retrieve folder names?
--
- hosts: localhost
gather_facts: no
vars:
Files: []
namespaces: []
tasks:
- name: Recursively find folders under /home/test
ansible.builtin.find:
paths: /home/test
file_type: directory
recurse: no
register: output
- name: Adding Files to the LIST
set_fact:
Files: "{{ Files + [item.path] }}"
with_items: "{{ output.files }}"
- name: Remove the path to only and retrieve folder name only
set_fact:
namespaces: "{{ namespaces + item | split('/') | last}}"
with_items: "{{ Files }}"
Error:
fatal: [localhost]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{ namespaces + item | split('/') | last}}): can only concatenate list (not \"str\") to list"}
Put the below declaration into the vars
namespaces: "{{ output.files|map(attribute='path')|map('basename')|list }}"
Example of a complete playbook for testing
Given the tree
shell> tree /tmp/test/
/tmp/test/
├── dir_A
├── dir_B
├── file_1
└── file_2
the playbook
- hosts: localhost
vars:
namespaces: "{{ output.files|map(attribute='path')|map('basename')|list }}"
tasks:
- find:
paths: /tmp/test
file_type: directory
register: output
- debug:
var: namespaces
gives (abridged)
namespaces:
- dir_B
- dir_A
Your expression item | split('/') | last currently returns a string that you try to concatenate to the namespaces list. However, this is not allowed and thus you need to cast it to a list as well by simply put it into braces []:
- name: Remove the path to only and retrieve folder name only
set_fact:
namespaces: "{{namespaces + [item | split('/') | last]}}"
with_items: "{{ Files }}"

How do I check a list of directories from a file, but some entries have multiple directories separated by a comma

I have a text file that looks like this:
hints_directory: /opt/cassandra/hints
data_file_directory: /opt/cassandra/data,/opt/cassandra/data2
commitlog_directory: /opt/cassandra/commitlog
cdc_raw_directory: /opt/cassandra/cdc_raw
saved_caches_directory: /opt/cassandra/saved_caches
tmp_directory: /opt/cassandra/tmp
jna_directory: /opt/cassandra/jna
dump_directory: /opt/cassandra/dump
I want to read that file and check for the existence of each directory using ansible. The tricky part comes on the second line, where the line contains 2 directory paths. How do I get ansible to read each line of the file as in the example below and check for the existence of each directory.
Here is what I have thus far, but it stumbles on the data_file_directory due to having multiple path values for the single line. I've tried to split the line entry, but that doesn't solve the issue.
Thoughts?
- name: Ensure the directories exist
file:
path: "{{item.split(':')[1].split(' ')[1].split(',')}}"
state: directory
with_lines: cat <<pathtofile>>/directories.txt
Read the variables from the file into a dictionary and convert the strings to lists, e.g.
- include_vars:
file: directories.txt
name: dirs_str
- set_fact:
dirs_list: "{{ dict(_keys|zip(_vals)) }}"
vars:
_keys: "{{ dirs_str.keys()|list }}"
_vals: "{{ dirs_str.values()|map('split', ',')|list }}"
gives
dirs_list:
cdc_raw_directory:
- /opt/cassandra/cdc_raw
commitlog_directory:
- /opt/cassandra/commitlog
data_file_directory:
- /opt/cassandra/data
- /opt/cassandra/data2
dump_directory:
- /opt/cassandra/dump
hints_directory:
- /opt/cassandra/hints
jna_directory:
- /opt/cassandra/jna
saved_caches_directory:
- /opt/cassandra/saved_caches
tmp_directory:
- /opt/cassandra/tmp
Select and flatten the values, e.g.
- set_fact:
allDirectories: "{{ dirs_list.values()|flatten }}"
gives
allDirectories:
- /opt/cassandra/hints
- /opt/cassandra/data
- /opt/cassandra/data2
- /opt/cassandra/commitlog
- /opt/cassandra/cdc_raw
- /opt/cassandra/saved_caches
- /opt/cassandra/tmp
- /opt/cassandra/jna
- /opt/cassandra/dump
, or use the expression directly in the loop
- name: Ensure the directories exist
file:
path: "{{ item }}"
state: directory
loop: "{{ dirs_list.values()|flatten }}"
For anyone else that may have a similar exercise, I have figured out how to do this:
- set_fact:
allDirectories: []
- set_fact:
allDirectories: "{{ item.split(':')[1].split(' ')[1].split(',') + allDirectories}}"
with_lines: cat <<pathtofile>>/directories.txt
- name: Ensure the directories exist
file:
path: "{{item}}"
state: directory
with_items: "{{allDirectories}}"

Delete all old files, but keep newest 3 files for same file name and different path

Hi can i know how can i keep the latest 3 files based on file name and path. For instance i have
fileAbackup1.war, fileAbackup2.war, fileAbackup3.war, fileAbackup4.war
fileBbackup1.war, fileBbackup2.war, fileBbackup3.war, fileBbackup4.war
So i should keep
fileAbackup1.war, fileAbackup2.war, fileAbackup3.war
fileBbackup1.war, fileBbackup2.war, fileBbackup3.war
I know the part of finding files and deleting files older than x days. I already done the coding part. But i need have some filter to keep 3 backup files of same name of different path
Updated
Below is my ansible code
- name: Find files
find:
paths: "{{ remove_file_path }}"
use_regex: yes
patterns:
- '*.war.*.backup*'
- '{{ rar_file_name }}.rar.*.bakup*'
- '.*\..*\.\d+\.\d{4}-\d{2}-\d{2}#\d{2}:\d{2}:\d{2}~$'
age: 5d
recurse: yes
register: removeFile
- name: Delete files
file:
path: "{{ item.path }}"
state: absent
loop: "{{ removeFile.files }}"
The tasks below
- find:
paths: dir1
patterns: '*.war'
register: result
- set_fact:
my_files: "{{ result.files|
map(attribute='path')|
sort|
list }}"
- debug:
var: my_files[0:3]
give
my_files[0:3]:
- dir1/fileAbackup1.war
- dir1/fileAbackup2.war
- dir1/fileAbackup3.war
If you need the filenames only map the filter basename. For example
- set_fact:
my_files: "{{ result.files|
map(attribute='path')|
map('basename')|
sort|
list }}"
- debug:
var: my_files[0:3]
give
my_files[0:3]:
- fileAbackup1.war
- fileAbackup2.war
- fileAbackup3.war
Q: "Will it have file fileBbackup1.war, fileBbackup2.war, and fileBbackup3.war as well?"
A: Yes. It will. Create a dictionary of the lists. For example
- set_fact:
my_files: "{{ my_files|default({})|
combine({item: result.files|
map(attribute='path')|
map('basename')|
select('search', item)|
sort|
list}) }}"
loop:
- fileAbackup
- fileBbackup
- debug:
msg: "{{ item.value[0:3] }}"
loop: "{{ my_files|dict2items }}"
loop_control:
label: "{{ item.key }}"
give
ok: [localhost] => (item=fileAbackup) =>
msg:
- fileAbackup1.war
- fileAbackup2.war
- fileAbackup3.war
ok: [localhost] => (item=fileBbackup) =>
msg:
- fileBbackup1.war
- fileBbackup2.war
- fileBbackup3.war
This command will provide the information you need.
ls -1 *.war | sort -r | head -n -3
Explanation
ls -1 *.war | sort -r Performs a reverse sort and displays just the filenames
head -n -3 Displays all except the first 3
You can pipe this to the command that deletes the files or you can run the following
rm $(ls -1 | sort -r | head -n -3)

Ansible list windows folder in D drive and sort the output by numeric order get the last element

I have folders like this in my windows machine. using ansible want to sort this app-* pattern folder and get the last element. from this example I want to get d:\app-9.7.8 in ansible variable
d:\app-1.0.3
d:\app-1.0.7
d:\app-2.0.4
d:\app-7.0.4
d:\app-9.7.8
This code list the folder, but not sure how to sort and get the last element.
- name: Find dir
win_find:
paths: D:\
recurse: no
file_type: directory
register: result
- name: Find dir
debug:
msg: "output {{ result.files }}"
I am getting the last element like this:
- name: set dir
set_fact:
mule_dir_list: "{{ result.files | sort(attribute='path') | map(attribute='path') | list }}"
when: "( result.files |length > 0 )"
- name: print dir
debug:
msg: "dir {{ mule_dir_list[-1] }}"

Creating directory symlinks in a nested loop

I would appreciate if anyone could point me in the right direction. I'm trying to find a list of directories in the $HOME/dotfiles directory for each of a list of users, and then from that list create a symlink of that directory in $HOME.
# Get list of directories in $HOME/dotfiles
- name: Get list of directories in $HOME/dotfiles
find:
paths: "/home/{{ user.username }}/dotfiles"
file_type: directory
recurse: false
register: dirs_matched
become: "{{ user.username }}"
loop: "{{ users|flatten(levels=1)}}"
loop_control:
loop_var: user
# Symlink any directories in dotfiles to $HOME
- name: Symlink dirs in ~/dotfiles to $HOME
file:
src: "{{item.0.path}}"
dest: "/home/{{item.1.username}}/{{item.0.path|basename}}"
state: link
force: true
loop: "{{ dirs_matched.files |product(users)|list }}"
when: dirs_matched.matched > 0
I do get results but they are in dirs_matched.results.files. I'm not sure how to map the results to the file module loop.
You need subelements loop but not nested/(product).
For every top-level item (user) iterate every subelement (files).
- name: Symlink dirs in ~/dotfiles to $HOME
file:
src: "{{ item.1.path }}"
dest: "/home/{{ item.0.user.username }}/{{ item.1.path | basename }}"
state: link
force: true
loop: "{{ dirs_matched.results | subelements('files') }}"
when condition is not required because loop over zero elements does nothing.

Resources