Create dictionary / list from registered variable in Ansible - windows

Hej *,
I'm writing a playbook which should set some disk attributes in the Windows registry.
All disk attributes are stored under a specific vendor folder. There is a value under each disk folder that I want to change in the end.
- Disk&Ven_QEMU&Prod_QEMU_HARDDISK
|- 00000
|- 00001
- Disk&Ven_VMware&Prod_Virtual_disk
|- 00000
I'm able to determine the vendor folder as well as the disk folders under the root of HKLM:\SYSTEM\CurrentControlSet\Enum\SCSI:
- hosts: some_host
tasks:
- name: Get vendor folders
win_reg_stat:
path: HKLM:\SYSTEM\CurrentControlSet\Enum\SCSI
register: vendors
- name: Get disk folders
win_reg_stat:
path: "HKLM:\\SYSTEM\\CurrentControlSet\\Enum\\SCSI\\{{ item }}"
with_items:
- "{{ vendors.sub_keys }}"
register: disks
- debug:
var: disks.results
The output looks like this:
ok: [some_host] => {
"disks.results": [
{
"ansible_loop_var": "item",
"changed": false,
"exists": true,
"failed": false,
"item": "CdRom&Ven_QEMU&Prod_QEMU_DVD-ROM",
"properties": {},
"sub_keys": [
"000001"
]
},
{
"ansible_loop_var": "item",
"changed": false,
"exists": true,
"failed": false,
"item": "Disk&Ven_QEMU&Prod_QEMU_HARDDISK",
"properties": {},
"sub_keys": [
"000000",
"000001"
]
},
{
"ansible_loop_var": "item",
"changed": false,
"exists": true,
"failed": false,
"item": "Disk&Ven_VMware&Prod_Virtual_disk",
"properties": {},
"sub_keys": [
"000000"
]
}
]
}
I'm a bit stuck right now. I'm trying to get a dictionary or list for iterating through all those disk folders. Something like that:
Disk&Ven_QEMU&Prod_QEMU_HARDDISK: 000000
Disk&Ven_QEMU&Prod_QEMU_HARDDISK: 000001
Disk&Ven_VMware&Prod_Virtual_disk: 000000
Is there a way to accomplish that?
Thanks and regards
Erik

For example
- set_fact:
my_list: "{{ my_list|default([]) + [{item.0.item:item.1}] }}"
with_subelements:
- "{{ disks.results }}"
- sub_keys
gives
my_list:
- CdRom&Ven_QEMU&Prod_QEMU_DVD-ROM: '000001'
- Disk&Ven_QEMU&Prod_QEMU_HARDDISK: '000000'
- Disk&Ven_QEMU&Prod_QEMU_HARDDISK: '000001'
- Disk&Ven_VMware&Prod_Virtual_disk: '000000'
It's not possible to create a dictionary because, in a dictionary, the keys must be unique.

I want to share my final solution with you. Thanks to Vladimir for the help.
- name: Get vendors
win_reg_stat:
path: HKLM:\SYSTEM\CurrentControlSet\Enum\SCSI
register: os_vendors
- name: Get vendors subfolders
win_reg_stat:
path: "HKLM:\\SYSTEM\\CurrentControlSet\\Enum\\SCSI\\{{ item }}"
loop: "{{ os_vendors.sub_keys }}"
register: os_subfolders
- name: Build registry pathes with vendors and disks
set_fact:
os_regpath_dict: "{{ os_regpath_dict | default({}) | combine ({item.item : item.sub_keys })}}"
loop: "{{ os_subfolders.results }}"
- name: Get disk folders
win_reg_stat:
path: "HKLM:\\SYSTEM\\CurrentControlSet\\Enum\\SCSI\\{{ item.0.key }}\\{{ item.1 }}\\Device Parameters\\Partmgr"
name: "Attributes"
loop: "{{ os_regpath_dict | dict2items | subelements('value') }}"
when: "'Disk' in item.0.key"
register: os_disks
- name: Set value of entry Attributes to 0
win_regedit:
path: "HKLM:\\SYSTEM\\CurrentControlSet\\Enum\\SCSI\\{{ item.item.0.key }}\\{{ item.item.1 }}\\Device Parameters\\Partmgr"
name: Attributes
data: 0
type: dword
loop: "{{ os_disks.results }}"
when: item.value is defined

Related

Ansible How to get the windows update kb number

I'm trying to get the pending installed windows update kb using ansible.
- name: Check for missing updates.
win_updates:
state: searched
category_names: "{{ win_updates_categories }}"
register: update_count
ignore_errors: yes
- debug: msg="{{ update_count.updates.kb }}"
but runs in error, could anyone help me, thank you !
Here is the output for register updatte
- debug:
var: update_count
"update_count": {
"changed": false,
"failed": false,
"filtered_updates": {},
"found_update_count": 1,
"installed_update_count": 0,
"reboot_required": false,
"updates": {
"67eab6a6-099b-42c5-86ce-63681f58ebd2": {
"categories": [
"Security Updates",
"Windows Server 2016"
],
"id": "67eab6a6-099b-42c5-86ce-63681f58ebd2",
"installed": false,
"kb": [
"4593226"
],
}
}
}
}
Here is the error if i only want show kb info
- debug:
var: update_count.updates.kb
"update_count.updates.kb": "VARIABLE IS NOT DEFINED!"
Please see below, I used the {{ item.value.id }} to get the ID value (last task) and saving them in an array if they are more than one, same way you should be able to retrieve the KBs. hopefully this explains
install critical updates only - reboot if nece
- name: install category critical updates only
win_updates:
category_names: ['CriticalUpdates']
server_selection: windows_update
state: installed
ignore_errors: yes
register: CriticalUpdateResults
- name: Postgres Column Values assigned
set_fact: UpdateCategory="{{ CriticalUpdateResults }}"
- name: CriticalUpdates Category assigned
set_fact: PatchCategory="CriticalUpdates"
- name: UpdatesArray assigned space
set_fact:
UpdatesArray: ""
- name: UpdatesArray assigned None if no update count
set_fact:
UpdatesArray: "None"
when: UpdateCategory.found_update_count == 0
- name: Updates Array assigned
set_fact:
UpdatesArray: "{{ UpdatesArray }} {{ **item.value.id** }}"
with_items:
- "{{ UpdateCategory.updates | dict2items }}"
when: UpdateCategory.found_update_count != 0

how to get vlan id from vcenter using ansible

I am trying to use below ansible module to get vlan_id information.
- name: Get all portgroups in dvswitch vDS
vmware_dvs_portgroup_find:
hostname: "{{ vsphere_host }}"
username: "{{ vcenter_user }}"
password: "{{ vcenter_password }}"
dvswitch: 'test-123' --- dvswitch
validate_certs: no
with_items: "{{ dvswitch_list }}"
register: test
- debug: msg="{{ test }}"
I am getting out in below format.
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"dvs_portgroups": [
{
"dvswitch": "test-123",
"name": "DPortGroup2",
"pvlan": false,
"trunk": false,
"vlan_id": "220"
},
{
"dvswitch": "test-123",
"name": "PP1",
"pvlan": false,
"trunk": false,
"vlan_id": "465"
},
{
"dvswitch": "test-123",
"name": "PP2",
"pvlan": false,
"trunk": false,
"vlan_id": "685"
},
I am using below debug msg to get vlan_id but some how it does not work.
- debug: msg="{{ item.vlan_id }}"
with_items: "{{ test.results.dvs_portgroups }}"
ASK [role_vmware_datastore_size : debug] ********************************************************************************
fatal: [192.168.90.00]: FAILED! => {"msg": "'list object' has no attribute 'dvs_portgroups'"}
test.results is the list of results for each item of the with_items of the vmware_dvs_portgroup_find task. Therefore you have to iterate over this list before accessing a field of each element.
If you want to iterate over all the dvs_portgroups of all the results, you can do that:
- debug: msg="{{ item.vlan_id }}"
with_items: "{{ test.results | map(attribute='dvs_portgroups') | flatten | list }}"
map(attribute='dvs_portgroups') is transforming the list of results into a list of the dvs_portgroups of each result
| flatten is transforming the list of lists (the dvs_portgroups) into a list of all elements of each lists
| list is transforming an iterator back to a list that can be interpreted by with_items
above debug code is working fine. I am getting list of vlan_id. Just to add more details. I have 4 dvswich and getting vlan_id from all dvswtich. I want to make it format like each dvswitch name with there vlan_id.
test-123 -- dvswith name
220
221
222
test-456 -- dvswtich name
300
301
302
I am trying below way but its giving only one vlan_id from each dvswitch.
- name: Final VLAN Details
set_fact:
vlan_details: "{{ vlan_details | default([]) + [dict(name=item[0], size=item[1])] }}"
loop: "{{ dvswitch | zip(vlan_id) | list }}"
loop_control:
label: " "

Find files in a loop and delete them

I would like to delete file in some sub directory which contains certain format. However, I am getting the error
'dict object' has no attribute 'files'.
Below is my code. The file pattern would be file_name.file_extension.processID.YYYY-MM-DD#HH:MM:SS~
My variables
fileToFindInAllSubDirecotry
- "home/usr/file1"
- "home/usr/file2"
- "home/usr/file3/file4"
- "home/usr/file5"
My playbook role
- name: Find file
find:
paths: "{{ item }}"
use_regex: yes
patterns:
- '.*\.\d+\.\d{4}-\d{2}-\d{2}#\d{2}:\d{2}:\d{2}~$'
age: 1d
recurse: yes
register: fileToDelete
loop: "{{ fileToFindInAllSubDirecotry }}"
- name: Delete file
file:
path: "{{ item.path }}"
state: absent
loop: "{{ fileToDelete.files }}"
This is the sample file and directory
home
|-------usr
|-------file1
|-------configFile.xml
|-------file2
|-------propertiesFile.txt.2012.2020-07-13#23:08:10~
|-------file3
|-------file4
|-------content.yml.2012.2020-04-04#23:08:10~
|-------file5
|-------configFile.xml.2012.2020-03-05#13:08:10~
This is happening because you are populating the find with a loop, so you end up with a result that would be a dictionary having a list or results.
Something like:
ok: [localhost] => {
"msg": {
"changed": false,
"msg": "All items completed",
"results": [
{
...
"files": [ ... ],
...
"item": "/home/usr/file1",
...
},
{
...
"files": [ ... ],
...
"item": "/home/usr/file2",
...
},
...
]
}
}
There is two ways to fix this:
The nicest one, because, as pointed by the documentation, the paths parameter of the module find can accept lists of paths, just pass it your whole fileToFindInAllSubDirecotry variables instead of using a loop, this way your deletion works as is:
- name: Find file
find:
paths: "{{ fileToFindInAllSubDirecotry }}"
use_regex: yes
patterns:
- '.*\.\d+\.\d{4}-\d{2}-\d{2}#\d{2}:\d{2}:\d{2}~$'
age: 1d
recurse: yes
register: fileToDelete
- name: Delete file
file:
path: "{{ item.path }}"
state: absent
loop: "{{ fileToDelete.files }}"
Use json_query to fetch the result[*].files then flatten the resulting list of list
- name: Find file
find:
paths: "{{ item }}"
use_regex: yes
patterns:
- '.*\.\d+\.\d{4}-\d{2}-\d{2}#\d{2}:\d{2}:\d{2}~$'
age: 1d
recurse: yes
register: fileToDelete
loop: "{{ fileToFindInAllSubDirecotry }}"
- name: Delete file
file:
path: "{{ item.path }}"
state: absent
loop: "{{ fileToDelete | json_query('results[*].files') | flatten }}"

Find filename in Ansible and Save to a file

How can I take filename from a specific directory and use it as a variable?
Then I'll use this variable to copy a new file with variable filename
For example: In the directory /home/user1/test/ there is always only one file named test1 or some other name.
I need to extract the filename (test1) from this path to some variable
- hosts: linux
become: yes
tasks:
- name: Ansible find file examples
find:
paths: /home/user1/test/
register: files_matched
- debug:
msg: "{{ files_matched.files }}"
- name: "Save find results to file"
copy:
content: "{{ files_matched.files }}"
dest: "/tmp/find_result.txt"
Then I've should get "test1" as variable and use it in the new filename in this code:
copy:
src: /home/myuser/myfile
dest: "{{ item.dest }}"
owner: root
group: root
mode: 0666
with_items:
- { dest: '/home/user2/test/{{ files_matched }}' }
As result of first script i've got:
: 0, "ischr": false, "wusr": true, "xoth": false, "islnk": false, "nlink": 1, "issock": false, "rgrp": true, "gr_name": "root", "path": "/home/user1/test/test1", "xusr":
false, "atime": 1564553299.6092095, "isdir": false, "ctime": 1564553304.7172158, "isblk": false, "xgrp": false, "dev": 2050, "wgrp": false, "isfifo": false, "mode": "0644
", "rusr": true}]
But i need only test1 part as result, not this all.
Thank you!
Q: "How can I take filename from a specific directory and use it as a variable?'
A: Given the tree
$ tree /home/user1/test/
/home/user1/test/
├── test1
├── test2
└── test3
the tasks below
- find:
paths: /home/user1/test/
register: files_matched
- debug:
msg: "{{ '/home/user2/test/' ~ item|basename }}"
loop: "{{ files_matched.files|json_query('[*].path') }}
give
"msg": "/home/user2/test/test1"
"msg": "/home/user2/test/test3"
"msg": "/home/user2/test/test2"
I hope you are expecting something like this. this will save the list of files under the specified directory into the /tmp/find_result.txt. I hope from here you can proceed.
---
- name: find file
hosts: linux
tasks:
- name: Ansible find file examples
find:
paths: /home/user1/test/
register: files_matched
- name: "Save find results to file"
lineinfile:
line: "{{ item.path }}"
path: "/tmp/find_result.txt"
create: yes
loop: "{{ files_matched.files | flatten }}"
note: make sure that you delete the /tmp/find_result.txt once your task completed.
if you want to store only the filename not the entire path, then replace line: "{{ item.path }}" with line: "{{ item.path | basename }}"

How to fix "Undefind error" while parsing a dictionary in ansible registered variable?

I'm creating some ec2 instances from a specific image, then trying to get a list of disks attached to these instances.
The problem is when I try to loop over the registered variable from the create instance task, I got an error
I have tried the solution from this post but with no luck
ansible get aws ebs volume id which already exist
- name: create instance
ec2:
region: us-east-1
key_name: xxxxxxx
group: xxxxxx
instance_type: "{{ instance_type }}"
image: "{{ instance_ami }}"
wait: yes
wait_timeout: 500
instance_tags:
Name: "{{ item.name }}"
vpc_subnet_id: "{{ item.subnet }}"
register: ec2
loop: "{{ nodes }}"
- name: show attached volumes Ids
debug:
msg: "{{ item.block_device_mapping | map(attribute='volume_id') }}"
loop: "{{ ec2.results[0].instances }}"
while printing only msg: "{{ item.block_device_mapping }}" I get:
"msg": {
"/dev/sda1": {
"delete_on_termination": true,
"status": "attached",
"volume_id": "vol-xxxxxxx"
},
"/dev/xvdb": {
"delete_on_termination": false,
"status": "attached",
"volume_id": "vol-xxxxxx"
},
"/dev/xvdc": {
"delete_on_termination": false,
"status": "attached",
"volume_id": "vol-xxxxxx"
}
}
but when I use
msg: "{{ item.block_device_mapping | map(attribute='volume_id') }}"
I get this error:
"msg": "[AnsibleUndefined, AnsibleUndefined, AnsibleUndefined]"
The task below
- debug:
msg: "{{ item }}: {{ block_device_mapping[item].volume_id }}"
loop: "{{ block_device_mapping.keys() }}"
gives the {device: volume_id} tuples (grep msg):
"msg": "/dev/xvdb: vol-xxxxxx"
"msg": "/dev/xvdc: vol-xxxxxx"
"msg": "/dev/sda1: vol-xxxxxxx"
To iterate instances use json_query. The task below
- debug:
msg: "{{ item.block_device_mapping|json_query('*.volume_id') }}"
loop: "{{ ec2.results[0].instances }}"
gives:
"msg": [
"vol-xxxxxx",
"vol-xxxxxx",
"vol-xxxxxxx"
]
and the task below with zip
- debug:
msg: "{{ item.block_device_mapping.keys()|zip(
item.block_device_mapping|json_query('*.volume_id'))|list }}"
loop: "{{ ec2.results[0].instances }}"
gives the list of lists:
"msg": [
[
"/dev/xvdb",
"vol-xxxxxx"
],
[
"/dev/xvdc",
"vol-xxxxxx"
],
[
"/dev/sda1",
"vol-xxxxxxx"
]
]
and the task below with dict
- debug:
msg: "{{ dict (item.block_device_mapping.keys()|zip(
item.block_device_mapping|json_query('*.volume_id'))) }}"
loop: "{{ ec2.results[0].instances }}"
gives the tuples
"msg": {
"/dev/sda1": "vol-xxxxxxx",
"/dev/xvdb": "vol-xxxxxx",
"/dev/xvdc": "vol-xxxxxx"
}
The mistake:
So the main mistake you made was thinking of item.block_device_mapping as if it was the map you wanted to work with instead of a map within a map. That is, the keys that you have to first find would, according to the msg that you printed /dev/sda, /dev/xvdb and /dev/xvdc.
So first you'd have to make an array with the keys of the parent map. In the question you can see the necessary code to make Jinja get you the necessary strings:
# The necessary filter to get that array should be something along these lines
item['block_device_mapping'] | list() | join(', ')
You should register that to then loop over,giving you the keys you need to access those elements' attributes.

Resources