Public/New-EventFilterXml.ps1

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

        [string]$Provider,

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

        [datetime]$StartTime,
        [datetime]$EndTime,

        [Alias('TimeSpan')]
        [timespan]$Since,

        [string]$EventDataFilter,

        [Parameter(ParameterSetName='Default')]
        [Alias('Level')]
        [LogLevelName[]]$LevelDisplayName,

        [Parameter(ParameterSetName='Security')]
        [ValidateSet('Success','Failure')]
        [string[]]$Audit
    )

    #region validate logname when using parameterset Security
    if ($PSCmdlet.ParameterSetName -eq 'Security' -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

    #region validate that Start and EndTime is not used with Since
    if ($PSBoundParameters.Keys -contains 'Since' -and ($PSBoundParameters.Keys -contains 'StartTime' -or $PSBoundParameters.Keys -contains 'EndTime'))  {
        try {
            Write-Error -Message 'Provide a value for Since or provide StartTime and/or EndTime, but not both' -Category InvalidArgument -ErrorAction Stop
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
    #endregion validate that Start and EndTime is not used with Since

    $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') {
        $EventFilterList.Add('TimeCreated[timediff(@SystemTime) <= {0}]' -f $Since.TotalMilliseconds)
    } 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) {
            $EventFilterList.Add("TimeCreated[$StartFilter and $EndFilter]")
        } elseif ($StartFilter -and $null -eq $EndFilter) {
            $EventFilterList.Add("TimeCreated[$StartFilter]")
        } elseif ($null -eq $StartFilter -and $EndFilter) {
            $EventFilterList.Add("TimeCreated[$EndFilter]")
        }
    }
    #endregion set event time filter

    #region add provider
    if ($EventFilterList.Count -gt 0) {
        $FilterListText = ' and {0}' -f ($EventFilterList -join ' and ')
    } else {
        $FilterListText = $null
    }

    if ($Provider) {
        $TextFilter = '*[System[Provider[@Name="{0}"]{1}]]' -f $Provider,$FilterListText
    } else {
        $TextFilter = '*[System[{0}]]' -f $FilterListText
    }

    $TextFilter | Write-Verbose
    #endregion add provider

    #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)

    if ($EventDataFilter) {
        $xmlWriter.WriteString(($TextFilter,$EventDataFilter -join ' and '))
    } else {
        $xmlWriter.WriteString($TextFilter)
    }

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

    $stringBuilder.ToString()

}