Public/New-NSPEventTaskTrigger.ps1

function New-NSPEventTaskTrigger {
    <#
    .SYNOPSIS
        Builds an "on an event" scheduled-task trigger (the kind New-ScheduledTaskTrigger can't make).
    .DESCRIPTION
        Give a log and event ID (and optionally the provider), or a complete -Subscription query
        (QueryList XML, as Event Viewer's "Custom" filter writes it). The trigger fires once per
        matching event; pair it with MultipleInstances IgnoreNew or Queue in the definition so a
        burst of events doesn't start a burst of runs.
 
        A common use: Sysmon event 11 (FileCreate) on a watched folder, to run a job as soon as a
        file lands.
    .PARAMETER ValueQuery
        Optional map of names to event XPath expressions. Task actions can reference these
        named values; omitting the map preserves existing behavior.
    .EXAMPLE
        New-NSPEventTaskTrigger -LogName 'Microsoft-Windows-Sysmon/Operational' -EventId 11 -Provider 'Microsoft-Windows-Sysmon'
    .EXAMPLE
        New-NSPEventTaskTrigger -Subscription '<QueryList><Query Id="0" Path="Application"><Select Path="Application">*[System[EventID=1000]]</Select></Query></QueryList>'
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Builds an object in memory; nothing on the system changes.')]
    [CmdletBinding(DefaultParameterSetName = 'Event')]
    [OutputType([ciminstance])]
    param(
        [Parameter(Mandatory, ParameterSetName = 'Event')][string]$LogName,
        [Parameter(Mandatory, ParameterSetName = 'Event')][int[]]$EventId,
        [Parameter(ParameterSetName = 'Event')][string]$Provider,
        [Parameter(Mandatory, ParameterSetName = 'Subscription')][string]$Subscription,
        [timespan]$Delay,
        [System.Collections.IDictionary]$ValueQuery
    )

    if ($PSCmdlet.ParameterSetName -eq 'Event') {
        $escape = { param($s) [Security.SecurityElement]::Escape($s) }
        $ids = ($EventId | ForEach-Object { "EventID=$_" }) -join ' or '
        $system = if ($Provider) { "Provider[@Name='$(& $escape $Provider)'] and ($ids)" } else { $ids }
        $log = & $escape $LogName
        $Subscription = "<QueryList><Query Id=`"0`" Path=`"$log`"><Select Path=`"$log`">*[System[$system]]</Select></Query></QueryList>"
    }
    try { [void][xml]$Subscription } catch { throw "Subscription is not valid XML: $($_.Exception.Message)" }

    $queries = @()
    if ($ValueQuery) {
        foreach ($name in $ValueQuery.Keys) {
            if ([string]$name -notmatch '^[A-Za-z_][A-Za-z0-9_]*$') { throw 'Value-query names must be identifiers.' }
            $xpath = [string]$ValueQuery[$name]
            if ([string]::IsNullOrWhiteSpace($xpath)) { throw 'Value-query XPath cannot be empty.' }
            try { $null = [Xml.XPath.XPathExpression]::Compile($xpath) } catch { throw 'Invalid value-query XPath.' }
        }
        $valueClass = Get-CimClass -ClassName MSFT_TaskNamedValue -Namespace 'Root/Microsoft/Windows/TaskScheduler' -ErrorAction Stop
        $queries = @(foreach ($name in @($ValueQuery.Keys | Sort-Object)) {
            New-CimInstance -CimClass $valueClass -ClientOnly -Property @{Name=[string]$name; Value=[string]$ValueQuery[$name]}
        })
    }
    $class = Get-CimClass -ClassName MSFT_TaskEventTrigger -Namespace 'Root/Microsoft/Windows/TaskScheduler' -ErrorAction Stop
    $trigger = New-CimInstance -CimClass $class -ClientOnly
    $trigger.Enabled = $true
    $trigger.Subscription = $Subscription
    if ($queries.Count) { $trigger.ValueQueries = [ciminstance[]]$queries }
    if ($Delay) { $trigger.Delay = [Xml.XmlConvert]::ToString($Delay) }
    $trigger
}