External facts provide a way to use arbitrary powershell scripts as facts, or set facts statically with data structured as INIFILE, JSON or YAML. If you’ve ever wanted to write a custom fact in Powershell, this is how.
To add external facts to your Puppet modules, just place them in MODULEPATH/MODULENAME/facts.d/. at the same level as you would place manifests, files or templates directories.
They will be automatically distributed with pluginsync during the puppet run.
The external facts are located in the following common directory.
C:\ProgramData\PuppetLabs\facter\facts.d\
Facter can parse structured data files stored in the external facts directory and set facts based on their contents. Structured data files must use one of the supported data types and must have the correct file extension.
The file encoding must be either ANSI or UTF8 without BOM (Byte Order Mark). Here an example how you can write a file as UTF8 without BOM encoding in powershell:
$json_example_content = '{"key1": "val1", "key2": "val2"}'
Set-Content -Encoding Ascii -Path "${externalFactsLocation}/json_example.json" -Value $content
Facter supports the following extensions and data types:
- .yaml: YAML data, in the following format:
---
key1: val1
key2: val2
key3: val3
- .json: JSON data, in the following format:
{
"key1": "val1",
"key2": "val2",
"key3": "val3"
}
- .txt: Key value pairs, in the following format:
key1=value1
key2=value2
key3=value3
Structured data files can set multiple facts at once.
{
"datacenter":
{
"location": "bfs",
"workload": "Web Development Pipeline",
"contact": "Blackbird"
},
"provision":
{
"birth": "2017-01-01 14:23:34",
"user": "alex"
}
}
Executable facts on Windows work by dropping an executable file into the external fact path. The external facts interface expects Windows scripts to end with a known extension. The following extensions are currently supported:
- .com and .exe: binary executables
- .bat and .cmd: batch scripts
- .ps1: PowerShell scripts
Each script must return key/value pairs on STDOUT in the format:
key1=value1
key2=value2
key3=value3
Using this INIFILE format, a single script can return multiple facts in one return.
CAUTION: Only the INIFILE format is supported as executable fact output. See bellow for a workaround.
Here is a sample PowerShell script which outputs facts using the required format:
Write-Host "key1=val1"
Write-Host 'key2=val2'
Write-Host key3=val3
As a workaround, if you need to generate a structured fact dynamically, you cannot use the script output, but you can write a file with the correct extension. This file will be interpreted by facter in the same run as the execution of the script writing the file.
Here an example with a powershell script writing a .json file. The script has to be located in the facts.d directory. We named it 'psversiontable.ps1'. At each facter invokation, the script will generate a new psversiontable.json file.
# create a hashtable with a 'psversiontable' key (this will be the fact name) and use $psversiontable for the value
# also create an additional key 'psversion' with the psversion as string. This will be a second fact.
$PSVersionTableFact = @{
"psversiontable" = $PSVersionTable;
"psversion" = $PSVersionTable.PSVersion.ToString();
}
# convert the powershell hashtable in a json format
$content = $PSVersionTableFact | ConvertTo-Json
# Write the external fact file in the same directory ($PSScriptRoot)
Set-Content -Encoding Ascii -Path "${PSScriptRoot}/psversiontable.json" -Value $content
You should be able use this fact with:
$ facter -p psversiontable
{
BuildVersion => {
Build => 14409,
Major => 10,
MajorRevision => 0,
Minor => 0,
MinorRevision => 1005,
Revision => 1005
},
CLRVersion => {
Build => 30319,
Major => 4,
MajorRevision => 0,
Minor => 0,
MinorRevision => -23536,
Revision => 42000
},
PSCompatibleVersions => [
"1.0",
"2.0",
"3.0",
"4.0",
"5.0",
"5.1.14409.1005"
],
PSEdition => "Desktop",
PSRemotingProtocolVersion => {
Build => -1,
Major => 2,
MajorRevision => -1,
Minor => 3,
MinorRevision => -1,
Revision => -1
},
PSVersion => {
Build => 14409,
Major => 5,
MajorRevision => 0,
Minor => 1,
MinorRevision => 1005,
Revision => 1005
},
SerializationVersion => {
Build => 0,
Major => 1,
MajorRevision => 0,
Minor => 1,
MinorRevision => 1,
Revision => 1
},
WSManStackVersion => {
Build => -1,
Major => 3,
MajorRevision => -1,
Minor => 0,
MinorRevision => -1,
Revision => -1
}
}
As it's a structured fact, you can use the dot notation:
$ facter -p psversiontable.PSVersion.Major
5
Our additional 'psversion' fact is also available:
$ facter -p psversion
5.1.14409.1005