Unified Windows plugin output and unified parsing function for checks

The title already says what the objective should be.
At the moment the situation is very cluttered if you look at the output of Windows plugins. Powershell gives use the tools to have a very nice structured output. Format-List is no structured output :smiley:

The “mk_msoffice.ps1” script goes already in the right direction. It uses string formatting to get a well defined output. There is only one point i would change, the separation character. In the mentioned plugin it is only the normal space. But what happens if one of my formatted strings has a space inside then the output is broke. In my proposal i use the “classic” pipe as a separator, as there is less chance that the pipe is included in a normal string.

If we compare the “old” Powershell script and my changed version, we see that it is not so much what is different now.

old (only the relevant line)

write-host "<<<msoffice_licenses>>>"
foreach ($license in Get-MsolAccountSku) {
    $line = "{0} {1} {2} {3}" -f $license.AccountSkuId, $license.ActiveUnits, $license.WarningUnits, $license.ConsumedUnits
    write-host $line
}

write-host "<<<msoffice_serviceplans>>>"
foreach ($license in Get-MsolAccountSku) {
    foreach ($serviceplan in $license.servicestatus) {
        $line = "{0} {1} {2}" -f $license.AccountSkuId, $serviceplan.serviceplan.servicename, $serviceplan.provisioningstatus
        write-host $line
    }
}

new

Write-Output("<<<msoffice_licenses:sep(124)>>>")
Write-Output("accountskuid|active|warning_units|consumed")
foreach ($license in Get-MsolAccountSku) {
    "{0}|{1}|{2}|{3}" -f $_.AccountSkuId, $_.ActiveUnits, $_.WarningUnits, $_.ConsumedUnits
}

Write-Output("<<<msoffice_serviceplans:sep(124)>>>")
Write-Output("accountskuid|servicename|provstatus")
foreach ($license in Get-MsolAccountSku) {
    foreach ($serviceplan in $license.servicestatus) {
        "{0}|{1}|{2}" -f $license.AccountSkuId, $_.serviceplan.servicename, $_.provisioningstatus
    }
}

The biggest difference is the header line in every section. This is used later as keys for the dictionary.

Now we take a look at the parsing function for the “licenses” check.

def parse_msoffice_licenses(info):
    parsed = {}

    for line in info:
        if len(line) != 4:
            continue

        try:
            parsed.setdefault(line[0], {
                "active": int(line[1]),
                "warning_units": int(line[2]),
                "consumed": int(line[3])
            })
        except ValueError:
            pass

    return parsed

You need to specify the names of every key and you have to check if the number of expected elements are correct.

def parse_windows(info, key):
    parsed = {}
    for i in info[1:]:
        element = dict(zip(info[0], i))
        parsed[element[key]] = element

    return parsed

This is the parse function for my plugin output and it only need to know what key should be used as identifier for every line.
The result is also a dictionary of dictionaries and can be used with the modern “discover()” function.
Also the “@get_parsed_item_data” is working without a problem with this.
The parsing function itself can be called like this example a took from another already existing check.

"parse_function": lambda info: parse_windows(info, key="accountskuid"),

Result from my parsing function.

{
'msonline:VISIOCLIENT': 
    {'accountskuid': 'msonline:VISIOCLIENT', 'active': '11', 'warning_units': '0', 'consumed': '10'}, 
'msonline:POWER_BI_PRO': 
    {'accountskuid': 'msonline:POWER_BI_PRO', 'active': '13', 'warning_units': '0', 'consumed': '11'}
}

The agent output from booth plugins don’t look very different.

old plugin

<<<msoffice_licenses>>>
msonline:VISIOCLIENT 11 0 10
msonline:POWER_BI_PRO 13 0 11

new plugin

<<<msoffice_licenses:sep(124)>>>
accountskuid|active|warning_units|consumed
msonline:VISIOCLIENT|11|0|10
msonline:POWER_BI_PRO|13|0|11

At the moment i rework my HyperV “special agent” with all this in mind and hope that you have some comments about this topic.