PowerShell/06advanced14 min

Production automation — patterns from real ZEUS scripts

This lesson brings everything together: what the real, production ZEUS probes look like — Get-LsnForest, Get-LsnInventory, Get-LsnPerf. These are patterns proven in client environments, where a script must run unattended and hand a clean result to the backend.

The contract with the backend: -AsJson on stdout

The ZEUS backend runs a probe remotely and reads only stdout as JSON. That's why diagnostics go to Write-Verbose/Write-Error, and only a single JSON object reaches stdout.

[CmdletBinding()]
param(
    [Parameter(Mandatory)][string]$Forest,
    [switch]$AsJson
)
$ErrorActionPreference = "Stop"

try {
    $data = Get-LsnInventoryData -Forest $Forest   # collects objects
    $out  = [pscustomobject]@{ Ok = $true; Forest = $Forest; Items = $data }
}
catch {
    $out = [pscustomobject]@{ Ok = $false; Error = $_.Exception.Message }
}

if ($AsJson) {
    $out | ConvertTo-Json -Depth 8 -Compress    # THE ONLY output on stdout
} else {
    $out
}

ProfessNet standard: in -AsJson mode exactly one JSON document appears on stdout. No Write-Host, banners or debug prints — they'd pollute the stream the backend parses. Diagnostics → Write-Verbose.

Paged LDAP — large forests without a timeout

A large forest inventory returns tens of thousands of objects. We fetch them in pages (paged search) so as not to exceed the LDAP server's limit or memory.

function Invoke-LsnLdapPaged {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$Filter,
        [int]$PageSize = 1000
    )
    $searcher = [adsisearcher]$Filter
    $searcher.PageSize = $PageSize          # crucial — without it the limit is ~1000
    $searcher.PropertiesToLoad.AddRange(@('name', 'dNSHostName', 'operatingSystem'))

    foreach ($entry in $searcher.FindAll()) {
        [pscustomobject]@{
            Name = $entry.Properties['name'][0]
            Dns  = $entry.Properties['dnshostname'][0]
            Os   = $entry.Properties['operatingsystem'][0]
        }
    }
}

ProfessNet standard: LDAP queries always with a PageSize (e.g. 1000). Without paging the AD server truncates results at the default limit, and the probe returns incomplete data — a silent, dangerous bug.

Remote collection: Invoke-Command over WinRM

We collect performance and local data on the target machines over WinRM. We close the session in finally and limit concurrency with -ThrottleLimit.

function Get-LsnPerf {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string[]]$ComputerName,
        [Parameter(Mandatory)][pscredential]$Credential,
        [switch]$AsJson
    )
    $ErrorActionPreference = "Stop"

    $results = Invoke-Command `
        -ComputerName $ComputerName `
        -Credential $Credential `
        -ThrottleLimit 16 `
        -ScriptBlock {
            [pscustomobject]@{
                Host = $env:COMPUTERNAME
                Cpu  = (Get-CimInstance Win32_Processor).LoadPercentage
                MemFreeMB = [math]::Round((Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory / 1KB)
            }
        }

    $out = [pscustomobject]@{ Ok = $true; Count = $results.Count; Hosts = $results }
    if ($AsJson) { $out | ConvertTo-Json -Depth 6 -Compress } else { $out }
}

ProfessNet standard: Invoke-Command with a host list runs in parallel with -ThrottleLimit (32 by default; we limit it to ~16 so as not to overload the client's network). Credentials always as a [pscredential] — never in the script.

Combining the patterns

PatternWhy
-AsJson + a single JSON on stdouta clean contract with the backend
$ErrorActionPreference = "Stop" + try/catcherror as JSON, not a hang
Paged LDAP (PageSize)complete data from large forests
Invoke-Command + -ThrottleLimitremote collection, load control
[pscredential] + finallysecurity and session cleanup

Tip: test a probe locally with -Verbose (you'll see the flow), and in integration with the backend always via -AsJson, and check that ConvertFrom-Json succeeds on the receiving side — that's a real test of the contract.


-AsJson on stdout, paged LDAP and remote Invoke-Command with throttling are the core of the production ZEUS probes. Put together, they give a script that runs unattended in the client environment and hands the backend a clean, complete result.