Create an Inventory Plugin
Goal: Get an inventory about all installed Powershell Modules on Windows Systems
The plugin ps_modules.ps1
This will get installed on the monitored host at C:\ProgramData\checkmk\agent\plugins\
# To get an object from the command running in another shell
# we need to return json and convert it back to an object
# before returning it
function Get-ModulesFromPwsh {
$pwsh = Get-Command 'pwsh.exe' -ErrorAction SilentlyContinue
if (-not $pwsh) { return @() }
$code = @'
Get-Module -ListAvailable -PSEdition Core |
Select-Object Name,
@{n='Version';e={$_.Version.ToString()}},
ModuleBase,
Path,
CompatiblePSEditions |
ConvertTo-Json -Depth 6 -Compress -AsArray
'@
try {
$json = & $pwsh.Path -NoProfile -Command $code
$objs = $json | ConvertFrom-Json
return $objs
} catch { @() }
}
$engine = if ($PSVersionTable.PSEdition -eq 'Core') { 'Core' } else { 'PowerShell5' }
$myPsEdition = if ($engine -eq 'Core') { 'Core' } else { 'Desktop' }
$res5 = @()
Get-Module -ListAvailable -PSEdition $myPsEdition | # <-- Filtert nach Manifest-Eignung
ForEach-Object {
$res5 += [PSCustomObject]@{
Name = $_.Name
Version = ($_.Version | ForEach-Object { $_.ToString() }) -join ''
ModuleBase = $_.ModuleBase
Engine = $engine
}
}
$ps7 = Get-ModulesFromPwsh
$res7 = @()
$ps7 | ForEach-Object {
$res7 += [pscustomobject]@{
Name = $_.Name
Version = ($_.Version | ForEach-Object { $_.ToString() }) -join ''
ModuleBase = $_.ModuleBase
Engine = 'Core'
}
}
$all = $res5
# pwsh has all powershell5 pathes also for compatibility, we strip these...
$all += $res7 | where-object { $_.ModuleBase -notin $all.ModuleBase }
$dedup = $all
# Agent-Section ausgeben (eine JSON-Zeile pro Modul)
'<<<psmodules>>>'
foreach ($m in $dedup) {
$m | ConvertTo-Json -Compress
}
The server side
You need to extend the Check_MK server to interpret the new section the plugin will produce.
Login as the OMD site user and create path and file ~/local/lib/python3/cmk_addons/plugins/powershell/agent_based/psmodules_inventory.py
Additionally copy your ps1 plugin from above here: ~/local/share/check_mk/agents/windows/plugins/ on server.
Now create the python addon.
# ~/local/lib/python3/cmk_addons/plugins/powershell/agent_based/psmodules_inventory.py
# Checkmk 2.4 (Check API v2)
from __future__ import annotations
import json
from typing import Any, Dict, List
from cmk.agent_based.v2 import (
AgentSection,
InventoryPlugin,
TableRow,
)
Section = List[Dict[str, Any]]
def parse_psmodules(string_table: List[List[str]]) -> Section:
"""Rekonstruiert JSON je Zeile und gibt eine Liste von Dicts zurück."""
out: Section = []
for row in string_table:
# Row ist tokenisiert; wir fügen die Zeile wieder zusammen
js = " ".join(row).strip()
if not js:
continue
try:
obj = json.loads(js)
if isinstance(obj, dict):
out.append(obj)
except Exception:
# Ignoriere fehlerhafte Zeilen, um die Section robust zu halten
continue
return out
agent_section_psmodules = AgentSection(
name="psmodules",
parse_function=parse_psmodules,
)
def inventory_psmodules(section: Section):
"""Erzeugt Inventory-Einträge unter software/applications/powershell/modules."""
path = ["software", "applications", "powershell", "modules"]
for item in section:
name = str(item.get("Name", "")).strip()
if not name:
continue
engine = str(item.get("Engine", "")).strip()
version = str(item.get("Version", "")).strip()
base = str(item.get("ModuleBase", "")).strip()
# Key-Spalten: Name + Engine (Edition). Version als Inventar-Spalte, damit Änderungen sichtbar werden.
yield TableRow(
path=path,
key_columns={"name": name, "engine": engine},
inventory_columns={"version": version, "module_base": base},
# status_columns könnten hier optional ergänzt werden, wenn dynamische Zustände benötigt werden.
)
inventory_plugin_psmodules = InventoryPlugin(
name="psmodules",
inventory_function=inventory_psmodules,
sections=["psmodules"],
)
Packaging the plugin (server)
# make sure folder exists
mkdir -p ~/tmp/check_mk
# create template
mkp template psmodules_inventory
Edit the template/manifest file to your needs
{
"name": "psmodules_inventory",
"title": "PowerShell Modules Inventory (PS5.1 & PS7)",
"version": "1.0.0",
"author": "Thor himself",
"description": "Create inventory of PowerShell modules and show in inventory unter software/applications/powershell/modules in the GUI.",
"download_url": "",
"version.min_required": "2.4.0",
"version.packaged": "2.4.0",
"version.usable_until": null,
"files": {
"agent_based": [
"cmk_addons/plugins/powershell/agent_based/psmodules_inventory.py"
],
"agents": [
"plugins/ps_modules.ps1"
]
}
}
The pathes in the above file are relative to ~/local/lib/python3/ for agent_based and ~/local/share/check_mk/agents/ for the agents pathes.
Create the package & install
mkp package ~/tmp/check_mk/psmodules_inventory.manifest.temp
# install package to site
mkp add ~/var/check_mk/packages_local/psmodules_inventory-1.0.1.mkp
# enable package in site
mkp enable psmodules_inventory
# check
mkp list
mkp show psmodules_inventory
Optionally you can give that plugin a longer timeout to be processed and activate caching.
On monitored host edit: C:\ProgramData\checkmk\agent\check_mk.user.yml
Make sure to find the ‘plugins’ section and the ‘execution’ section and add your plugin there.
plugins:
[...]
execution:
- pattern : '$CUSTOM_PLUGINS_PATH$\ps_modules.ps1'
run : yes
async : yes
timeout : 120
cache_age : 7200
[...]
- pattern : '$CUSTOM_PLUGINS_PATH$\*.*'
timeout : 30
run : yes
To upgrade an installed package, create the new version like 1.0.2 and create the package like above: psmodules_inventory-1.0.2.mkp
# update addon
mkp add ~/var/check_mk/packages_local/psmodules_inventory-1.0.2.mkp
mkp enable psmodules_inventory
# rollback from 1.0.2 to 1.0.1
mkp add ~/var/check_mk/packages_local/psmodules_inventory-1.0.1.mkp
mkp enable psmodules_inventory
# disable/remove
mkp disable psmodules_inventory
mkp remove psmodules_inventory