Ansible stat module not working - ansible

I need some help here:
I am creating virtual machines with Vagrant to configure some cluster on them. For the first VM, which I want to use as an Ansible control node, I'm running Ansible from my local machine to install and configure Ansible in my Control Node.
The problem appears when I try to check if the .inventory file exists and try to copy the file to the home directory (inside the VM).
The same command that I use for the inventory file works just file to check the stat of the .ansible.cfg file but not for the inventory file.
Do you guys have any idea what I'm doing wrong?
roles/ansible/tasks/main.yml
---
- name: install epel-release
yum:
name: epel-release
state: present
- name: install ansible
yum:
name: ansible
state: present
- name: stat ansible configuration file
stat:
path: "{{ cfg_file }}"
register: stat_ansible_config
- name: copy .ansible.cfg to home directory
copy:
src: .ansible.cfg
dest: /home/{{ user }}/.ansible.cfg
owner: "{{ user }}"
group: "{{ group }}"
mode: 0644
when: stat_ansible_config.stat.exists
- name: stat ansible inventory file
stat:
path: "{{ inventory_file }}"
register: stat_inventory
- name: copy .inventory to home directory
copy:
src: .inventory
dest: /home/{{ user }}/.inventory
owner: "{{ user }}"
group: "{{ group }}"
mode: 0644
when: stat_inventory.stat.exists
...
roles/ansible/vars/main.yml
---
user: vagrant
group: vagrant
cfg_file: /{{ user }}/provision/playbooks/roles/ansible/files/.ansible.cfg
inventory_file: /{{ user }}/provision/playbooks/roles/ansible/files/.inventory
...
the playbook:
---
- hosts: controller
become: yes
roles:
- ansible
...
and the output:
TASK [ansible : stat ansible configuration file] *******************************
ok: [controller] => {
"changed": false,
"invocation": {
"module_args": {
"checksum_algorithm": "sha1",
"follow": false,
"get_checksum": true,
"get_md5": true,
"mime": false,
"path": "/vagrant/provision/playbooks/roles/ansible/files/.ansible.cfg"
},
"module_name": "stat"
},
"stat": {
"atime": 1485527858.0,
"checksum": "46acc076fda7e38fd7262fbc88f8ab4e1f52ddca",
"ctime": 1485452789.0,
"dev": 38,
"executable": false,
"exists": true,
"gid": 1000,
"gr_name": "vagrant",
"inode": 118,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"md5": "0cb8c97246776dc7e88fe44f19c3278f",
"mode": "0644",
"mtime": 1485452789.0,
"nlink": 1,
"path": "/vagrant/provision/playbooks/roles/ansible/files/.ansible.cfg",
"pw_name": "vagrant",
"readable": true,
"rgrp": true,
"roth": true,
"rusr": true,
"size": 164,
"uid": 1000,
"wgrp": false,
"woth": false,
"writeable": true,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
}
TASK [ansible : copy .ansible.cfg to home directory] ***************************
ok: [controller] => {
"changed": false,
"checksum": "46acc076fda7e38fd7262fbc88f8ab4e1f52ddca",
"dest": "/home/vagrant/.ansible.cfg",
"diff": {
"after": {
"path": "/home/vagrant/.ansible.cfg"
},
"before": {
"path": "/home/vagrant/.ansible.cfg"
}
},
"gid": 1000,
"group": "vagrant",
"invocation": {
"module_args": {
"backup": null,
"content": null,
"delimiter": null,
"dest": "/home/vagrant/.ansible.cfg",
"diff_peek": null,
"directory_mode": null,
"follow": false,
"force": false,
"group": "vagrant",
"mode": 420,
"original_basename": ".ansible.cfg",
"owner": "vagrant",
"path": "/home/vagrant/.ansible.cfg",
"recurse": false,
"regexp": null,
"remote_src": null,
"selevel": null,
"serole": null,
"setype": null,
"seuser": null,
"src": ".ansible.cfg",
"state": null,
"unsafe_writes": null,
"validate": null
}
},
"mode": "0644",
"owner": "vagrant",
"path": "/home/vagrant/.ansible.cfg",
"secontext": "unconfined_u:object_r:user_home_t:s0",
"size": 164,
"state": "file",
"uid": 1000
}
TASK [ansible : stat ansible inventory file] ***********************************
ok: [controller] => {
"changed": false,
"invocation": {
"module_args": {
"checksum_algorithm": "sha1",
"follow": false,
"get_checksum": true,
"get_md5": true,
"mime": false,
"path": null
},
"module_name": "stat"
},
"stat": {
"exists": false
}
}
TASK [ansible : copy .inventory to home directory] *****************************
task path: /Users/alessandro/Go/src/github.com/alesr/neo4go/provision/playbooks/roles/ansible/tasks/main.yml:31
skipping: [controller] => {
"changed": false,
"skip_reason": "Conditional check failed",
"skipped": true
}

inventory_file is a magic variable and is set by Ansible during playbook run overwriting any values you try to assign.
In your inventory stat task you may notice: invocation.module_args.path: null.
Rename your inventory_file variable to my_inventory_file and it will work.

copy sees the file is already there and is saying there's no change, but is identical to your source and not making a change.
The debug output shows:
stat_ansible_config.stat.exists = True
stat_inventory.stat.exists = False
and that explains the behavior difference.

Related

Find/list files with specific extension and rename them dynamically using ansible playbook

I'm trying to rename a jar file in a path for deployment using ansible and it is successful. I have tried multiple ways but still, it's failing,
For example, there are multiple Jar files in the path /appdata/tomcat/lib/jars/
we havemultiple jar files with version numbers like below
app_deploy-1.1.1.jar
app_deploy-1.1.2_old_1.jar
app_deploy-1.1.2.jar_before
app_deploy-1.1.2_old_2022.jar
app_deploy-1.1.2_before.jar
I have move all files with .jar extension with date using custom date variable {deploy_date} like this app_deploy-1.1.2_before.jar_{deploy_date}
I have method 1 as below :
- name: Get the name of the current jar files
shell: ls -l /appdata/tomcat/lib/jars/ | grep .*.jar
register: jar_files_list
- debug:
msg: "{{ jar_files_list }}"
- name: Get the date
shell: date +%Y%m%d%H%M%S
register: timestamp
when: jar_files_list.stdout != ''
- name: Rename the current jar file
file:
src: /appdata/tomcat/lib/jars/{{ jar_files_list }}
dest: /appdata/tomcat/lib/jars/{{ jar_files_list }}_{{ ansible_date_time.date }}_backup
when: jar_files_list.stdout != ''
I have method 2 as below :
- name: Rename the current jar files
shell: mv /appdata/tomcat/lib/jars/*.jar {{ repo_name }}-*.jar_backup_{{ ansible_date_time.date }}
- name: Move current filesto backup directory
shell: mv /appdata/tomcat/lib/jars/{{ repo_name }}-.*.jar /appdata/tomcat/lib/jars//backup_jars/`
Both of the solutions doest work, can someone help me with some solutions
The find module can be used to find and generate a list of files located on a remote host based on a pattern:
- name: Get jars in {{ jars_path }}
find:
paths: "{{ jars_path }}"
file_type: file
patterns: '*.jar'
register: jars_list
The output will be a dictionary of only the *.jar files in the specified path:
TASK [Get jars in /appdata/tomcat/lib/jars] ************************
ok: [test-001] => {
"changed": false,
"examined": 4,
"files": [
{
"atime": 1661249640.3583002,
"ctime": 1661249640.3583002,
"dev": 64768,
"gid": 0,
"gr_name": "root",
"inode": 8980720,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1661249640.3583002,
"nlink": 1,
"path": "/appdata/tomcat/lib/jars/test1.jar",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 0,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
},
{
"atime": 1661249640.3583002,
"ctime": 1661249640.3583002,
"dev": 64768,
"gid": 0,
"gr_name": "root",
"inode": 8980722,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1661249640.3583002,
"nlink": 1,
"path": "/appdata/tomcat/lib/jars/test2.jar",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 0,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
},
{
"atime": 1661249640.3583002,
"ctime": 1661249640.3583002,
"dev": 64768,
"gid": 0,
"gr_name": "root",
"inode": 8980726,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1661249640.3583002,
"nlink": 1,
"path": "/appdata/tomcat/lib/jars/test3.jar",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 0,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
],
"invocation": {
"module_args": {
"age": null,
"age_stamp": "mtime",
"contains": null,
"depth": null,
"excludes": null,
"file_type": "file",
"follow": false,
"get_checksum": false,
"hidden": false,
"paths": [
"/appdata/tomcat/lib/jars"
],
"patterns": [
"*.jar"
],
"read_whole_file": false,
"recurse": false,
"size": null,
"use_regex": false
}
},
"matched": 3,
"msg": "All paths examined",
"skipped_paths": {}
}
"{{ jars_list.files }}" will list the files and .path will provide the full path of the file.
Now the copy module with the option remote_src: yes can be used to rename the files:
- name: Rename jars
copy:
src: "{{ item.path }}"
dest: "{{ item.path }}_{{ deploy_date }}"
remote_src: yes
loop: "{{ jars_list.files }}"
Delete the old files:
- name: Remove old jars
file:
path: "{{ item.path }}"
state: absent
loop: "{{ jars_list.files }}"
The complete playbook
- hosts: all
vars:
deploy_date: "{{ ansible_date_time.date }}"
jars_path: /appdata/tomcat/lib/jars
tasks:
- name: Get jars in {{ jars_path }}
find:
paths: "{{ jars_path }}"
file_type: file
patterns: '*.jar'
register: jars_list
- name: Rename jars
copy:
src: "{{ item.path }}"
dest: "{{ item.path }}_{{ deploy_date }}"
remote_src: yes
loop: "{{ jars_list.files }}"
- name: Remove old jars
file:
path: "{{ item.path }}"
state: absent
loop: "{{ jars_list.files }}"

How to get all indices of output array in Ansible

I'm gathering info on SSL certs on servers (looking for expiration date) using the find module.
- name: Find certs on server
find:
path: /etc/ssl/custom/certs
file_type: file
patterns: "*.crt"
recurse: yes
register: find_result
- debug:
var: find_result
The results are:
ok: [server00] => {
"find_result": {
"changed": false,
"examined": 5,
"failed": false,
"files": [
{
"atime": 1622749788.1552677,
"ctime": 1622744497.4393551,
"dev": 2050,
"gid": 0,
"gr_name": "root",
"inode": 19531534,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1622744497.4393551,
"nlink": 1,
"path": "/etc/ssl/custom/certs/somewebsite0.com.crt",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 1879,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
},
{
"atime": 1622719627.2477663,
"ctime": 1616545902.3681087,
"dev": 2050,
"gid": 0,
"gr_name": "root",
"inode": 19531253,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1613754568.0,
"nlink": 1,
"path": "/etc/ssl/custom/certs/somewebsite1.com.crt",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 2081,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
},
{
"atime": 1622719627.2197664,
"ctime": 1616545902.3721087,
"dev": 2050,
"gid": 0,
"gr_name": "root",
"inode": 19535012,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1601653231.0,
"nlink": 1,
"path": "/etc/ssl/custom/certs/somewebsite2.com.crt",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 2269,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
],
"matched": 3,
"msg": ""
}
}
I'm needing the path portion of the output ("path": "/etc/ssl/custom/certs/somewebsite1.com.crt"), and if I use find_result.files[0].path it only gives me a single result for each host, when I need every *.crt file.
How can I access each index? I try to use the shell module to perform an action on the .crt file, but again, it's only grabbing the first one due to the [0] index, like so:
- name: Check expiration
shell: "cat {{ find_result.files[0].path }} | openssl x509 -noout -enddate"
register: date
- debug:
var: date.stdout_lines
ok: [server00] => {
"date.stdout_lines": [
"notAfter=Apr 2 19:50:38 2018 GMT"
]
}
Here would be an example playbook based on it:
- hosts: localhost
tasks:
- name: Find certs on server
find:
path: /etc/ssl/custom/certs
file_type: file
patterns: "*.crt"
recurse: yes
register: find_result
- debug:
var: find_result
- name: Play with the data just to demonstrate
set_fact:
IRGeekSauce_list: "{{ (IRGeekSauce_list|default([])) + [item.path] }}"# <-- add each list item to a custom list
with_items: '{{ find_result.files }}' # <-- here we get the files as a list.
- name: your list
debug:
msg: '{{ IRGeekSauce_list }}'
- include_tasks: anothertasklist.yml
loop: '{{ IRGeekSauce_list }}'
loop_control:
loop_var: singlepathvariable
And then you have another "playbook" with just the tasks 'anothertasklist.yml'
- name: hello
debug:
msg: 'You are now in another playbook'
- name:
debug:
msg: 'Woho: {{ singlepathvariable }}'
- name:
openssl_certificate_info:
howeverthatmoduleworks...
And you should be able to just take the entire and include find_result.files as the loop, and then instead just use the loopvar singlepathvariable(and maybe rename it) and just take out the path as {{ singlepathvariable.path }}

Ansible - Find File with a Specific Path and Copy it to a Specific Pre-Set Destination from a Dictionary

I've got a problem that I've been trying to solve for a few days and I'm starting to think I'm just not going to get it. So, here I am.
I have to write an Ansible playbook that will look for a a bunch of files in a bunch of repos and, if it should happen to find that file in those repos, copy it to a specific destination directory which will be pre-set and specific to that file. A catch is that there might be multiple instances of that file but we only want that file if it's under a specific sub-directory in the path (the location of the sub-directory changes from repo to repo and the directory structure of the repos are all pretty different).
So that I don't put you all through our terrible naming conventions, I've abstracted the problem to this kinda silly example.
Let's say that this is our directory structure:
/tmp/example
└── foods
├── breakfast
│   ├── pancakes
│   └── waffles
├── dinner
│   ├── fish
│   └── pasta
└── lunch
├── burger
└── burrito
And this is our dictionary that states the user, the food they want, and the destination path of the food should we find it:
orders:
'Bob':
food: 'burrito'
dst: '/tmp/example-homes/home/bob/plate'
'Jack':
food: 'fish'
dst: '/tmp/example-homes/home/jack/plate'
'Mary':
food: 'fish'
dst: '/tmp/example-homes/mary/plate'
Now, let's say that we want to iterate through the list of people in the orders dictionary and find their food item in /tmp/example, BUT it should only match if their food choice is under the foods/lunch directory. If we happen to find their food in the foods/lunch directory, copy it to that user's specified dst directory (notice that not all of the dst directories are the same). Skip the food if it's found under foods/breakfast or foods/dinner or even something like restaurant/lunch; we only care about foods/lunch. For example, Mary wants fish, and fish does exist, but it's in the foods/dinner directory so we're going to consider it as missing and not copy it.
I've gotten to the point where I can find the food but I'm stuck trying to tie that found food file to the dst field that tells us where the food should go. It's frustrating because I feel that all of the data that I need is actually in the find_food_results dictionary. I just don't know how to act on the results of find so as to perform the logical equivalent of "if food matched, and path contains 'foods/lunch', copy it to item.value.dst".
I also can't help but feel that there's a simpler way to do this and I'm just chasing my tail at this point. Anyhow, thanks a bunch in advance. Please let me know if I can clarify anything.
Here's the code:
---
- name: "directory finder"
hosts: 127.0.0.1
connection: local
vars:
orders:
'Bob':
food: 'burrito'
dst: '/tmp/example-homes/home/bob/plate'
'Jack':
food: 'fish'
dst: '/tmp/example-homes/home/jack/plate'
'Mary':
food: 'fish'
dst: '/tmp/example-homes/mary/plate'
tasks:
- name: "describe orders"
debug:
var: orders
- name: "describe orders | dict2items"
debug:
var: orders | dict2items
- name: "find the food"
find:
paths: "/tmp/example"
recurse: yes
file_type: file
patterns: "{{ item.value.food }}"
with_items:
- "{{ orders | dict2items }}"
register: find_food_results
- name: "describe find_food_results"
debug:
var: find_food_results
- name: "narrow down our findings to paths that contain 'foods/lunch'"
set_fact:
food_lunch_directory_paths: "{{ food_lunch_directory_paths | default([]) }} + [ '{{ item.path }}' ]"
with_items: "{{ find_food_results.results | map(attribute='files') | list }}"
when: "'foods/lunch' in item.path"
- name: "describe our food/lunch paths"
debug:
var: food_lunch_directory_paths
And here's the output:
PLAY [directory finder] *******************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************************
ok: [127.0.0.1]
TASK [describe orders] ********************************************************************************************************************************************
ok: [127.0.0.1] => {
"orders": {
"Bob": {
"dst": "/tmp/example-homes/home/bob/plate",
"food": "burrito"
},
"Jack": {
"dst": "/tmp/example-homes/home/jack/plate",
"food": "fish"
},
"Mary": {
"dst": "/tmp/example-homes/mary/plate",
"food": "fish"
}
}
}
TASK [describe orders | dict2items] *******************************************************************************************************************************
ok: [127.0.0.1] => {
"orders | dict2items": [
{
"key": "Bob",
"value": {
"dst": "/tmp/example-homes/home/bob/plate",
"food": "burrito"
}
},
{
"key": "Jack",
"value": {
"dst": "/tmp/example-homes/home/jack/plate",
"food": "fish"
}
},
{
"key": "Mary",
"value": {
"dst": "/tmp/example-homes/mary/plate",
"food": "fish"
}
}
]
}
TASK [find the food] **********************************************************************************************************************************************
ok: [127.0.0.1] => (item={u'key': u'Bob', u'value': {u'food': u'burrito', u'dst': u'/tmp/example-homes/home/bob/plate'}})
ok: [127.0.0.1] => (item={u'key': u'Jack', u'value': {u'food': u'fish', u'dst': u'/tmp/example-homes/home/jack/plate'}})
ok: [127.0.0.1] => (item={u'key': u'Mary', u'value': {u'food': u'fish', u'dst': u'/tmp/example-homes/mary/plate'}})
TASK [describe find_food_results] *********************************************************************************************************************************
ok: [127.0.0.1] => {
"find_food_results": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"examined": 10,
"failed": false,
"files": [
{
"atime": 1604184466.0584693,
"ctime": 1604184466.0584693,
"dev": 66305,
"gid": 0,
"gr_name": "root",
"inode": 58724223,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1604184466.0584693,
"nlink": 1,
"path": "/tmp/example/foods/lunch/burrito",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 0,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
],
"invocation": {
"module_args": {
"age": null,
"age_stamp": "mtime",
"contains": null,
"depth": null,
"excludes": null,
"file_type": "file",
"follow": false,
"get_checksum": false,
"hidden": false,
"paths": [
"/tmp/example"
],
"patterns": [
"burrito"
],
"recurse": true,
"size": null,
"use_regex": false
}
},
"item": {
"key": "Bob",
"value": {
"dst": "/tmp/example-homes/home/bob/plate",
"food": "burrito"
}
},
"matched": 1,
"msg": ""
},
{
"ansible_loop_var": "item",
"changed": false,
"examined": 10,
"failed": false,
"files": [
{
"atime": 1604184480.3713806,
"ctime": 1604184480.3713806,
"dev": 66305,
"gid": 0,
"gr_name": "root",
"inode": 62917805,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1604184480.3713806,
"nlink": 1,
"path": "/tmp/example/foods/dinner/fish",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 0,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
],
"invocation": {
"module_args": {
"age": null,
"age_stamp": "mtime",
"contains": null,
"depth": null,
"excludes": null,
"file_type": "file",
"follow": false,
"get_checksum": false,
"hidden": false,
"paths": [
"/tmp/example"
],
"patterns": [
"fish"
],
"recurse": true,
"size": null,
"use_regex": false
}
},
"item": {
"key": "Jack",
"value": {
"dst": "/tmp/example-homes/home/jack/plate",
"food": "fish"
}
},
"matched": 1,
"msg": ""
},
{
"ansible_loop_var": "item",
"changed": false,
"examined": 10,
"failed": false,
"files": [
{
"atime": 1604184480.3713806,
"ctime": 1604184480.3713806,
"dev": 66305,
"gid": 0,
"gr_name": "root",
"inode": 62917805,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1604184480.3713806,
"nlink": 1,
"path": "/tmp/example/foods/dinner/fish",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 0,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
],
"invocation": {
"module_args": {
"age": null,
"age_stamp": "mtime",
"contains": null,
"depth": null,
"excludes": null,
"file_type": "file",
"follow": false,
"get_checksum": false,
"hidden": false,
"paths": [
"/tmp/example"
],
"patterns": [
"fish"
],
"recurse": true,
"size": null,
"use_regex": false
}
},
"item": {
"key": "Mary",
"value": {
"dst": "/tmp/example-homes/mary/plate",
"food": "fish"
}
},
"matched": 1,
"msg": ""
}
]
}
}
TASK [narrow down our findings to paths that contain 'foods/lunch'] ***********************************************************************************************
ok: [127.0.0.1] => (item={u'rusr': True, u'uid': 0, u'rgrp': True, u'xoth': False, u'islnk': False, u'woth': False, u'nlink': 1, u'issock': False, u'mtime': 1604184466.0584693, u'gr_name': u'root', u'path': u'/tmp/example/foods/lunch/burrito', u'xusr': False, u'atime': 1604184466.0584693, u'inode': 58724223, u'isgid': False, u'size': 0, u'isdir': False, u'wgrp': False, u'ctime': 1604184466.0584693, u'isblk': False, u'xgrp': False, u'isuid': False, u'dev': 66305, u'roth': True, u'isreg': True, u'isfifo': False, u'mode': u'0644', u'pw_name': u'root', u'gid': 0, u'ischr': False, u'wusr': True})
skipping: [127.0.0.1] => (item={u'rusr': True, u'uid': 0, u'rgrp': True, u'xoth': False, u'islnk': False, u'woth': False, u'nlink': 1, u'issock': False, u'mtime': 1604184480.3713806, u'gr_name': u'root', u'path': u'/tmp/example/foods/dinner/fish', u'xusr': False, u'atime': 1604184480.3713806, u'inode': 62917805, u'isgid': False, u'size': 0, u'isdir': False, u'wgrp': False, u'ctime': 1604184480.3713806, u'isblk': False, u'xgrp': False, u'isuid': False, u'dev': 66305, u'roth': True, u'isreg': True, u'isfifo': False, u'mode': u'0644', u'pw_name': u'root', u'gid': 0, u'ischr': False, u'wusr': True})
skipping: [127.0.0.1] => (item={u'rusr': True, u'uid': 0, u'rgrp': True, u'xoth': False, u'islnk': False, u'woth': False, u'nlink': 1, u'issock': False, u'mtime': 1604184480.3713806, u'gr_name': u'root', u'path': u'/tmp/example/foods/dinner/fish', u'xusr': False, u'atime': 1604184480.3713806, u'inode': 62917805, u'isgid': False, u'size': 0, u'isdir': False, u'wgrp': False, u'ctime': 1604184480.3713806, u'isblk': False, u'xgrp': False, u'isuid': False, u'dev': 66305, u'roth': True, u'isreg': True, u'isfifo': False, u'mode': u'0644', u'pw_name': u'root', u'gid': 0, u'ischr': False, u'wusr': True})
TASK [describe our food/lunch paths] ******************************************************************************************************************************
ok: [127.0.0.1] => {
"food_lunch_directory_paths": [
"/tmp/example/foods/lunch/burrito"
]
}
PLAY RECAP ********************************************************************************************************************************************************
127.0.0.1 : ok=7 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Let's take into account missing files. For example, given the orders
orders:
'Bob':
food: 'burrito'
dst: '/tmp/example-homes/home/bob/plate'
'Jack':
food: 'fish'
dst: '/tmp/example-homes/home/jack/plate'
'Mary':
food: 'fish'
dst: '/tmp/example-homes/mary/plate'
'Joe':
food: 'steak'
dst: '/tmp/example-homes/joe/plate'
Create the list of the foods
- name: "create list of foods"
set_fact:
foods: "{{ orders|dict2items|
map(attribute='value.food')|unique|sort|list }}"
- debug:
var: foods
gives
foods:
- burrito
- fish
- steak
Find the files and create a dictionary of foods and related files. Then use the dictionary to copy the existing files to destinations
- name: "find the foods"
find:
paths: "/tmp/example"
recurse: yes
file_type: file
patterns: "{{ item }}"
loop: "{{ foods }}"
register: find_food_results
- name: "create dictionary of foods"
set_fact:
foods: "{{ dict(foods|zip(paths)) }}"
vars:
paths: "{{ find_food_results.results|
map(attribute='files')|list }}"
- name: "copy property files to destination"
debug:
msg: "Copy {{ foods[item.value.food][0]['path'] }} to {{ item.value.dst }}"
with_dict: "{{ orders }}"
loop_control:
label: "{{ item.key }}"
when: foods[item.value.food]|length > 0
give
ok: [localhost] => (item=Bob) =>
msg: Copy /tmp/example/foods/lunch/burrito to /tmp/example-homes/home/bob/plate
ok: [localhost] => (item=Jack) =>
msg: Copy /tmp/example/foods/dinner/fish to /tmp/example-homes/home/jack/plate
ok: [localhost] => (item=Mary) =>
msg: Copy /tmp/example/foods/dinner/fish to /tmp/example-homes/mary/plate
skipping: [localhost] => (item=Joe)
Q: "File 'burrito' in the breakfast, lunch, and dinner directories ... How can I iterate over ... find_food_results.files? Almost like a nested loop."
A: Display the dictionary foods with selected paths only. The task
- debug:
msg: "{{ msg.split('\n')[:-1] }}"
vars:
msg: |
{{ item.key }}
{{ item.value|map(attribute='path')|list|to_nice_yaml }}
loop: "{{ foods|dict2items }}"
loop_control:
label: "{{ item.key }}"
gives
TASK [debug] ****
ok: [localhost] => (item=burrito) =>
msg:
- burrito
- '- /tmp/example/foods/breakfast/burrito'
- '- /tmp/example/foods/dinner/burrito'
- '- /tmp/example/foods/lunch/burrito'
ok: [localhost] => (item=fish) =>
msg:
- fish
- '- /tmp/example/foods/dinner/fish'
ok: [localhost] => (item=steak) =>
msg:
- steak
- '[]'
Use subelements if you want to iterate the lists of the files. For example
- name: "List all property files"
debug:
msg: "{{ item.0.key }} {{ item.1.path }}"
with_subelements:
- "{{ foods|dict2items }}"
- value
loop_control:
label: "{{ item.0.key }}"
gives
TASK [List all property files] ****
ok: [localhost] => (item=burrito) =>
msg: burrito /tmp/example/foods/breakfast/burrito
ok: [localhost] => (item=burrito) =>
msg: burrito /tmp/example/foods/dinner/burrito
ok: [localhost] => (item=burrito) =>
msg: burrito /tmp/example/foods/lunch/burrito
ok: [localhost] => (item=fish) =>
msg: fish /tmp/example/foods/dinner/fish
And of course as soon as I ask the question, I find the answer to it :-/
Well, in case anyone else was struggling with this, here's how I fixed it. Though, I'd love to see more efficient solutions, so please do chime in if you have one.
I was able to create a new value in the dictionary called "src" that contained the path of the file found using the find module. Then, in the copy module, I was able to iterate over the dictionary and only copy when the src contained 'foods/lunch'.
Here's the code:
---
- name: "directory finder"
hosts: 127.0.0.1
connection: local
vars:
orders:
'Bob':
food: 'burrito'
dst: '/tmp/example-homes/home/bob/plate'
'Jack':
food: 'fish'
dst: '/tmp/example-homes/home/jack/plate'
'Mary':
food: 'fish'
dst: '/tmp/example-homes/mary/plate'
tasks:
- name: "describe orders"
debug:
var: orders
- name: "describe orders | dict2items"
debug:
var: orders | dict2items
- name: "find the food"
find:
paths: "/tmp/example"
recurse: yes
file_type: file
patterns: "{{ item.value.food }}"
with_items:
- "{{ orders | dict2items }}"
register: find_food_results
- name: "create a src key/value dictionary element"
set_fact:
orders: "{{ orders | combine( new_item, recursive=true ) }}"
vars:
new_item: "{ '{{ item.item.key }}': { 'src': '{{ item.files[0].path }}' } }"
with_items: "{{ find_food_results.results }}"
- name: "copy property files to destination"
copy:
src: "{{ item.value.src }}"
dest: "{{ item.value.dst }}"
owner: root
group: root
mode: 0644
with_dict: "{{ orders }}"
when: "'foods/lunch' in item.value.src"

Ansible retrieve multiple array values from results

I'd like to be able to iterate over all of the name values, but I'm not sure how to do so with Ansible. The variable domain is a list, and register is used.
- name: find *.ccfg files in domain(s)
find:
paths: "/tmp/opt/{{ item }}/ccfg"
patterns: "*.ccfg"
recurse: yes
excludes: "Admin.ccfg"
with_items: "{{ domain }}"
register: files
when: ('local' in group_names)
- debug:
msg: "{{ files.results }}"
The path value in each array could be anywhere from 1 to 20. Each index in the array has multiple values. Some arrays may not have any values
Standard Output:
ok: [127.0.0.1] => {
"msg": [
{
"_ansible_ignore_errors": null,
"_ansible_item_label": "CIE",
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"examined": 3,
"failed": false,
"files": [
{
"atime": 1541632866.4095802,
"ctime": 1541632866.4095802,
"dev": 64768,
"gid": 0,
"gr_name": "root",
"inode": 52174935,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1541632866.4095802,
"nlink": 1,
"path": "/tmp/opt/CIE/ccfg/cie.ccfg",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 0,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
],
Take a look at the json_query filter:
- debug:
msg: "{{ item }}"
with_items: "{{ files | json_query('results[*].files[*].path') }}"
Official doco: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#json-query-filter
Shameless plug with more examples: https://parko.id.au/2018/08/16/complex-data-structures-and-the-ansible-json_query-filter

Ansible register check_path and used it in with_dict loop

I have a following hash/dict structure of sites:
sites:
example.com:
site: example.com
mail: info#example.com
site_alias: www.example.com
example.fi:
site: example.fi
mail: info#example.fi
site_alias: example.fi
...
I register a value for every site, if there is also a folder for it. Print the result.
name: "Check if path already exists so it is the first time."
stat: path={{ cert_files }}/{{ item.value.site }}
register: check_path
with_dict: "{{ sites }}"
debug: var=check_path.results
# No need to print the whole dictionary, all results are already there.
# with_dict: "{{ sites }}"
So I get something like:
TASK [letsencrypt : debug] *****************************************************
ok: [78.47.67.114] => (item={'key': u'example.com', 'value': {u'mail': u'mail#example.com', u'site_alias': u'www.example.com', u'site': u'example.com'}}) => {
"check_path.results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"invocation": {
"module_args": {
"checksum_algorithm": "sha1",
"follow": false,
"get_checksum": true,
"get_md5": true,
"mime": false,
"path": "/etc/letsencrypt/live/example.com"
},
"module_name": "stat"
},
"item": {
"key": "example.com",
"value": {
"mail": "info#example.com",
"site": "example.com",
"site_alias": "www.example.com"
}
},
"stat": {
"atime": 147869032.3522692,
"ctime": 149636484.0226028,
"dev": 2049,
"executable": true,
"exists": true,
"gid": 0,
"gr_name": "root",
"inode": 15725,
"isblk": false,
"ischr": false,
"isdir": true,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": false,
"issock": false,
"isuid": false,
"mode": "0755",
"mtime": 14632684.026028,
"nlink": 2,
"path": "/etc/letsencrypt/live/example.com",
"pw_name": "root",
"readable": true,
"rgrp": true,
"roth": true,
"rusr": true,
"size": 4096,
"uid": 0,
"wgrp": false,
"woth": false,
"writeable": true,
"wusr": true,
"xgrp": true,
"xoth": true,
"xusr": true
}
},
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"invocation": {
"module_args": {
"checksum_algorithm": "sha1",
"follow": false,
"get_checksum": true,
"get_md5": true,
"mime": false,
"path": "/etc/letsencrypt/live/example.com"
},
"module_name": "stat"
},
"item": {
"key": "example.fi",
"value": {
"mail": "info#example.fi",
"site": "example.fi",
"site_alias": "www.example.fi"
}
},
"stat": {
"atime": 1493734857.9738503,
"ctime": 1485960159.8090317,
"dev": 2049,
"executable": true,
"exists": true,
How can I the use or get the value "check_path.results.stats.exists" the last value in the next task if I want to iterate again through {{ sites }} ?
I have tried something like this with no success.
- name: Make a certificate the first time.
command: /bin/bash /opt/letsencrypt/letsencrypt-auto certonly -- standalone --email "{{ item.value.mail }}" --agree-tos --keep-until- expiring -d "{{ item.value.site }}" -d "{{ item.value.site_alias }}"
with_items: check_path
when: check_path.results.stat.exists == false
or
- name: Make a certificate the first time.
command: /bin/bash /opt/letsencrypt/letsencrypt-auto certonly standalone --email "{{ item.value.mail }}" --agree-tos --keep-until-expiring -d "{{ item.value.site }}" -d "{{ item.value.site_alias }}"
with_dict: "{{ sites }}"
when: check_path.results.stat.exists == false
You should iterate over result, not over original list:
- name: Make a certificate the first time.
command: /bin/bash /opt/letsencrypt/letsencrypt-auto certonly standalone --email "{{ item.item.value.mail }}" --agree-tos --keep-until-expiring -d "{{ item.item.value.site }}" -d "{{ item.item.value.site_alias }}"
with_items: "{{ check_path.results }}"
when: not item.stat.exists
here item.item is an item of the original list.

Resources