How to Get Values from a Nested Ansible Dictionary

The following block of code is used to get a list of files from a directory. It then registers a variable with the contents of any files that were found.

---
- name: Get a list of files
  ansible.builtin.find:
    path: "/home/svc-ansible/Documents"
    patterns: "file*.txt"
  register: home_files

This next block is used to display the contents of the registered variable.

- name: "Print the variable"
  debug: var=home_files

The result of the debug command is below. Notice that the “files” key contains a nested dictionary. This is what I am am after, especially the “path” key in this case.

"home_files": {
        "changed": false,
        "examined": 3,
        "failed": false,
        "files": [
            {
                "atime": 1662746480.558686,
                "ctime": 1662746480.558686,
                "dev": 2052,
                "gid": 100,
                "gr_name": "users",
                "inode": 163856,
                "isblk": false,
                "ischr": false,
                "isdir": false,
                "isfifo": false,
                "isgid": false,
                "islnk": false,
                "isreg": true,
                "issock": false,
                "isuid": false,
                "mode": "0640",
                "mtime": 1662746480.558686,
                "nlink": 1,
                "path": "/home/svc-ansible/Documents/file2.txt",
                "pw_name": "svc-ansible",
                "rgrp": true,
                "roth": false,
                "rusr": true,
                "size": 0,
                "uid": 1002,
                "wgrp": false,
                "woth": false,
                "wusr": true,
                "xgrp": false,
                "xoth": false,
                "xusr": false
            },
            {
                "atime": 1662746525.3352597,
                "ctime": 1662746520.0151916,
                "dev": 2052,
                "gid": 100,
                "gr_name": "users",
                "inode": 163855,
                "isblk": false,
                "ischr": false,
                "isdir": false,
                "isfifo": false,
                "isgid": false,
                "islnk": false,
                "isreg": true,
                "issock": false,
                "isuid": false,
                "mode": "0640",
                "mtime": 1662746520.0151916,
                "nlink": 1,
                "path": "/home/svc-ansible/Documents/file1.txt",
                "pw_name": "svc-ansible",
                "rgrp": true,
                "roth": false,
                "rusr": true,
                "size": 26,
                "uid": 1002,
                "wgrp": false,
                "woth": false,
                "wusr": true,
                "xgrp": false,
                "xoth": false,
                "xusr": false
            },
            {
                "atime": 1662746489.2627974,
                "ctime": 1662746489.2627974,
                "dev": 2052,
                "gid": 100,
                "gr_name": "users",
                "inode": 163860,
                "isblk": false,
                "ischr": false,
                "isdir": false,
                "isfifo": false,
                "isgid": false,
                "islnk": false,
                "isreg": true,
                "issock": false,
                "isuid": false,
                "mode": "0640",
                "mtime": 1662746489.2627974,
                "nlink": 1,
                "path": "/home/svc-ansible/Documents/file3.txt",
                "pw_name": "svc-ansible",
                "rgrp": true,
                "roth": false,
                "rusr": true,
                "size": 0,
                "uid": 1002,
                "wgrp": false,
                "woth": false,
                "wusr": true,
                "xgrp": false,
                "xoth": false,
                "xusr": false
            }
        ],
        "matched": 3,
        "msg": "All paths examined",
        "skipped_paths": {}
    }

Initially, I expected to be able to just access the “path” key by using dot notation but that didn’t quite work out.

- debug: "{{ home_files.files.path }}"

The above resulted in a large fatal error.

TASK [aaronrombaut_photon3_vrops : debug] *******************************************************
fatal: [172.16.174.128]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'path'\n\nThe error appears to be in '/home/svc-ansible/Documents/ansible/VMware-vRealize-Operations-8.X/VMware-vRealize-Operations-8.X/roles/aaronrombaut_photon3_vrops/tasks/test_accessing_list.yml': line 11, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- debug: \"{{ home_files.files.path }}\"\n  ^ here\nWe could be wrong, but this one looks like it might be an issue with\nmissing quotes. Always quote template expression brackets when they\nstart a value. For instance:\n\n    with_items:\n      - {{ foo }}\n\nShould be written as:\n\n    with_items:\n      - \"{{ foo }}\"\n"}

Yuck, that was nasty and I surely didn’t want to read all that. So I tried various attempts with array indexing. Either way, I received the same error over and over again.

Eventually, with enough research and trial and error, this is how I actually was able to access the nested dictionary. This was very confusing to understand for me at first. The biggest thing for me was where the word “item” came from and what it was referencing. I am assuming that behind the scenes in Python, “item” is the generic term for what is in a foreach loop. (ex. (foreach item in items) { })

- debug: 
    msg: "{{ item.path }}"
  loop: "{{ home_files.files }}"
TASK [aaronrombaut_photon3_vrops : debug] *******************************************************
ok: [172.16.174.128] => (item={'path': '/home/svc-ansible/Documents/file2.txt', 'mode': '0640', 'isdir': False, 'ischr': False, 'isblk': False, 'isreg': True, 'isfifo': False, 'islnk': False, 'issock': False, 'uid': 1002, 'gid': 100, 'size': 0, 'inode': 163856, 'dev': 2052, 'nlink': 1, 'atime': 1662746480.558686, 'mtime': 1662746480.558686, 'ctime': 1662746480.558686, 'gr_name': 'users', 'pw_name': 'svc-ansible', 'wusr': True, 'rusr': True, 'xusr': False, 'wgrp': False, 'rgrp': True, 'xgrp': False, 'woth': False, 'roth': False, 'xoth': False, 'isuid': False, 'isgid': False}) => {
    "msg": "/home/svc-ansible/Documents/file2.txt"
}

So now I can loop through other code and programmatically feed it paths using the looping construct. The code is more dynamic now and as files are added to directories, they will be accounted for in future runs.

Windows File and Folder Sharing Issues with Ansible and Chocolatey

This article (https://controlaltfail.wordpress.com/2017/11/23/ansible-and-chocolatey-with-windows-fileshare-repository/) proved to be a big help. I don’t want to take anything away from it, but would like to add my own notes. It was a huge help in getting this working.

Working with a client in a Windows 10 and System Center Configuration Manager (SCCM) environment, but trying to modernize how desktop images get built and deployed for a virtual desktop infrastructure (VDI). They are not going to deviate away from using SCCM, so we are trying to create an environment where the two can co-exist. Unfortunately, Chocolatey’s documentation is hard to follow and (in my opinion) not quite mature enough for the masses. I have confidence they will get there in the future and also realize that this use case is very specific and unlikely needed in the majority of environments.

In an attempt to install application packages using a Windows File and Folder source for Chocolatey packages, my colleague and I ran into issues with how the folder share was being accessed. It was successful 100% of the time by running choco install vscode from an elevated PowerShell.

So that looked good, it was assumed that it would also work in an Ansible Playbook. The playbook is very simple.

---
- hosts: all
  tasks:
  - name: Install Microsoft Visual Studio Code
    win_chocolatey:
    name: vscode
    state: latest
...

I’ll save you from zooming in and hurting your eyes. The part that catches my eyes is, “The package was not found with the source(s) listed.” Well, that’s odd because it just worked when I was on the machine. So I added another line to add an explicit source (not shown), added other lines to authenticate the source (not shown), and even added another task to add another source (not shown). Every time the playbook ran, this error displayed. So it is not the Chocolatey source.

Let me take a step back here. I forgot to mention that I originally had my .nupkg files stored on a CIFS share from my Synology NAS, not on a Windows folder share. I ended up creating a Windows File and Folder share as a troubleshooting step later.

And we’re back…I double checked my Windows share, it was shared out, the file was present, the source was accurate and visible to Chocolatey as was apparent by the test install directly on the machine. It had to be something else. My colleague ended up finding the article I posted in the top of this post. Sure enough, it worked.

How to fix it

On the folder that is being shared out, add ANONYMOUS LOGIN to the Share Permissions. Ensure the Read checkbox is checked under allow.

On the Security tab, add the ANONYMOUS LOGIN group and set the permissions to List folder contents and Read.

Lastly, open secpol.msc and add the shared folder to the Network access: Shares that can be accessed anonymous. If you look in the snip below, you can see that I have added the Chocolatey share. The setting is not well documented (without going to Internet) so I was unsure if I was supposed to use the UNC path, the explicit location, or just the name. I opted to add just the name and it worked. Mileage may very, but was not tested.

Went back to my Ansible control node and ran another test and was pleased to see that this was resolved.

I thought that was an awful lot of work and feel like I lowered security in my system just to make this work, though. There has to be a better way, but unfortunately, at this time, I am unsure of such. Please reach out to me or comment if you know how to make this work without all the leg work. Like I mentioned above, this is going to probably be an unlikely case because most Chocolatey users will be able to use a robust server or even the simple server solution.