Public/New-EventFilterXml.ps1

function New-EventFilterXml {
    [CmdLetBinding(DefaultParameterSetName='TimeSpan')]
    param(
        [Parameter(Mandatory)]
        [string]$LogName,

        [string]$Provider,

        [Alias('Id')]
        [string[]]$EventId,

        [Parameter(ParameterSetName='TimeRange')]
        [datetime]$StartTime,

        [Parameter(ParameterSetName='TimeRange')]
        [datetime]$EndTime,

        [Parameter(ParameterSetName='TimeSpan')]
        [Alias('TimeSpan')]
        [timespan]$Since,

        [string]$EventDataFilter,

        [Alias('Level')]
        [LogLevelName[]]$LevelDisplayName,

        [ValidateSet('Success','Failure')]
        [string[]]$Audit,

        [switch]$XPath
    )

    #region validate logname when using parameterset Security
    if ($PSBoundParameters.Keys -contains 'Audit' -and $LogName -ne 'Security')  {

        try {
            Write-Error -Message 'Audit filtering must specify Security as LogName' -Category InvalidArgument -ErrorAction Stop
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
    #endregion validate logname when using parameterset Security

    $EventFilterList = [System.Collections.Generic.List[object]]::new()

    #region set event log level filter
    $LogLevel = [System.Collections.Generic.List[object]]::new()
    foreach ($Level in $LevelDisplayName) {
        if ($Level -eq 'Issues') {
            $LogLevel.Add('Level=1 or Level=2 or Level=3')
        } else {
            $LogLevel.Add('Level={0}' -f [LogLevelname]::$Level.Value__)
        }
    }
    if ($LogLevel.Count -gt 0) {
        $LogLevelFilter = '({0})' -f ($LogLevel -join ' or ')
        $EventFilterList.Add($LogLevelFilter)
    }
    #endregion set event log level filter

    #region set event id filter
    $EventIdList = [System.Collections.Generic.List[object]]::new()
    $EventIdRangeList = [System.Collections.Generic.List[object]]::new()
    foreach ($Id in $EventId) {
        if ($Id -as [int]) {
            $EventIdList.Add("EventID=$Id")
        } elseif ($Id -match '^\d+-\d+$') {
            $EventIdRange = $Id.Split('-')
            if ($EventIdRange.Count -eq 2) {
                if ($EventIdRange[0] -lt $EventIdRange[1] ) {
                    $EventIdRangeFilter = '(EventID >= {0} and EventID <= {1})' -f $EventIdRange[0],$EventIdRange[1]
                    $EventIdRangeList.Add($EventIdRangeFilter)
                } else {
                    'Invalid event Id range: {0} is larger than {1}' -f $EventIdRange[0],$EventIdRange[1] | Write-Warning
                    return
                }
            } else {
            'Invalid event Id range: {0}' -f $Id | Write-Warning
            return
            }
        } else {
            'Invalid event Id: {0}' -f $Id | Write-Warning
            return
        }
    }
    if ($EventIdRangeList.Count -gt 0) {
        foreach ($EventIdRangeFilter in $EventIdRangeList) {
            $EventIdList.Add($EventIdRangeFilter)
        }
    }
    if ($EventIdList.Count -gt 0) {
        $EventIdFilter = '({0})' -f ($EventIdList -join ' or ')
        $EventFilterList.Add($EventIdFilter)
    }
    #endregion set event id filter

    #region set event time filter
    if ($PSBoundParameters.Keys -contains 'Since') {
        $SinceTime = 'TimeCreated[timediff(@SystemTime) <= {0}]' -f $Since.TotalMilliseconds.ToString()
        $EventFilterList.Add($SinceTime)
    } else {
        $StartFilter = $EndFilter = $null
        if ($StartTime) {
            $StartFilter = "@SystemTime>='{0}Z'" -f $StartTime.ToUniversalTime().ToString('s')
        }
        if ($EndTime) {
            $EndFilter = "@SystemTime<='{0}Z'" -f $EndTime.ToUniversalTime().ToString('s')
        }
        if ($StartFilter -and $EndFilter) {
            $StartEndTime = 'TimeCreated[{0} and {1}]' -f $StartFilter,$EndFilter
            $EventFilterList.Add($StartEndTime)
        } elseif ($StartFilter -and $null -eq $EndFilter) {
            $StartTime = 'TimeCreated[{0}]' -f $StartFilter
            $EventFilterList.Add($StartTime)
        } elseif ($null -eq $StartFilter -and $EndFilter) {
            $EndTime = 'TimeCreated[{0}]' -f $EndFilter
            $EventFilterList.Add($EndTime)
        }
    }
    #endregion set event time filter

    #region add provider and build filter
    if ($EventFilterList.Count -gt 1) {
        $FilterListText = $EventFilterList -join ' and '
    } elseif ($EventFilterList.Count -eq 1) {
        $FilterListText = $EventFilterList[0]
    } else {
        $FilterListText = $null
    }
    if ($FilterListText) {
        if ($Provider) {
            $XPathFilter = '*[System[Provider[@Name="{0}"] and {1}]]' -f $Provider,$FilterListText
        } else {
            $XPathFilter = '*[System[{0}]]' -f $FilterListText
        }
    } else {
        $XPathFilter = '*'
    }
    #endregion add provider

    #region combine data filter
    if ($EventDataFilter) {
        $CombinedFilter = $XPathFilter,$EventDataFilter -join ' and '
    } else {
        $CombinedFilter = $XPathFilter
    }
    #endregion

    #region output XPath filter only
    if ($XPath) {
        '{0}{1}' -f [System.Environment]::NewLine,$CombinedFilter | Write-Verbose
        $CombinedFilter
        return
    }
    #endregion

    #region create xml
    $xmlWriterSettings = [System.Xml.XmlWriterSettings]::new()
    $xmlWriterSettings.OmitXmlDeclaration = $true
    $xmlWriterSettings.Indent = $true
    $xmlWriterSettings.IndentChars = ' '
    $xmlWriterSettings.NewLineChars = [System.Environment]::NewLine
    $xmlWriterSettings.NewLineHandling = [System.Xml.NewLineHandling]::Replace

    $stringBuilder = [System.Text.StringBuilder]::new()
    $xmlWriter = [System.Xml.XmlWriter]::Create($StringBuilder,$xmlWriterSettings)

    $xmlWriter.WriteStartDocument()
    $xmlWriter.WriteStartElement('QueryList')
    $xmlWriter.WriteStartElement('Query')
    $xmlWriter.WriteAttributeString('Id',0)
    $xmlWriter.WriteAttributeString('Path',$LogName)
    $xmlWriter.WriteStartElement('Select')
    $xmlWriter.WriteAttributeString('Path',$LogName)

    $xmlWriter.WriteString($CombinedFilter)

    $xmlWriter.WriteEndElement() # end Select
    $xmlWriter.WriteEndElement() # end Query
    $xmlWriter.WriteEndElement() # end QueryList
    $xmlWriter.WriteEndDocument() # end document
    $xmlWriter.Flush()
    $xmlWriter.Close()
    #endregion create xml

    '{0}{1}' -f [System.Environment]::NewLine,$stringBuilder.ToString() | Write-Verbose
    $stringBuilder.ToString()

}