Here I am again with Ansible’s local facts, but this time on Windows nodes, where the local facts behave differently.

Local fact files on Unix/Linux must be named *.fact irrespective of whether they contain INI or JSON or whether they are executable programs which emit JSON, and they are searched for in a default fact_path which is typically /etc/ansible/facts.d (or /usr/local/etc/ansible/facts.d on BSD systems).

There’s no default fact directory for Windows nodes, but we specify one with the fact_path parameter to the setup module or in an ansible.cfg file:

[defaults]
nocows = 1
fact_path = c:/users/r1/facts

That directory must exist and it MUST contain PowerShell (*.ps1) or JSON (*.json) files. The PowerShell scripts output objects which Ansible formats as JSON:

@{
    username = 'janej'
    lat = 48.856826
    lon = 2.292713
}

When Ansible’s setup module runs, we obtain the following fact from the Windows node:

"ansible_os_family": "Windows",
"ansible_osm": {
  "lat": 48.856826,
  "lon": 2.292713,
  "username": "janej"
}

Note that Ansible places these objects alongside other Ansible facts, whereas on Unix, if I have a JSON file /etc/ansible/facts.d/osm.fact, Ansible returns osm nested under ansible_local:

"ansible_os_family": "Darwin",
"ansible_local": {
    "osm": {
        "lat": 48.856826,
        "lon": 2.292713,
        "username": "janej"
    }
}

dynamic

On Unix/Linux *.fact files may be executable and return JSON, so I install my program as /etc/ansible/facts.d/hungry.fact

$ /etc/ansible/facts.d/hungry.fact
{"id":4,"meal":"dinner","dish":"tikka masala"}

The utility is executed during setup and the result is:

"ansible_local": {
    "hungry": {
        "dish": "tikka masala",
        "id": 4,
        "meal": "dinner"
    },
    "osm": {
        "lat": 48.856826,
        "lon": 2.292713,
        "username": "janej"
    }
}

On Windows nodes fact files MUST be *.ps1 so I figure out^W^W google how to invoke a program from a PowerShell script and produce this masterpiece in a file called currybeer.ps1:

Invoke-Expression "& `"C:\Users\r1\facts\hungry.exe`"  /run /exit /SilentMode"

Sadly, that doesn’t do what I thought it would: the PowerShell script is returning a string:

"ansible_currybeer": "{\"id\":4,\"meal\":\"dinner\",\"dish\":\"tikka masala\"}"

A bit more figuring out later, I add | ConvertFrom-Json to the .ps1 script, and Ansible correctly interprets its output:

"ansible_currybeer": {
  "dish": "tikka masala",
  "id": 4,
  "meal": "dinner"
},
"ansible_osm": {
  "lat": 48.856826,
  "lon": 2.292713,
  "username": "janej"
},

I can now use these facts like any others:

- hosts: win
  gather_facts: true
  tasks:
  - debug: msg="{{ ansible_currybeer.dish }} for {{ ansible_currybeer.meal }}"

and look forward to this evening:

TASK [debug] *************************
ok: [192.168.1.163] => {
    "msg": "tikka masala for dinner"
}

I don’t know why Ansible nests local facts differently on Windows nodes, and I don’t see an advantage in doing so.

And if you need inspiration, we’re collecting ideas for using local facts

ansible and windows :: 28 Oct 2022 :: e-mail