Easily Transition to Get-WinEvent

by Jul 21, 2023

You shouldn’t continue to use Get-EventLog for a number of reasons, and we have previously illustrated some. Get-EventLog is also discontinued in PowerShell 7. Instead, Get-WinEvent is the way to go. It can do everything Get-EventLog can do, plus so much more.

Only, Get-WinEvent is painful to use because it requires structures like hash tables or XML to define the events you are after. That’s why below we present you with a rather lengthy proxy function that adds backwards compatibility to Get-WinEvent. Once you run the below proxy function, you can use Get-WinEvent with the very same parameters that you were used to with Get-EventLog. Plus, you get awesome new IntelliSense, suggesting all log names that contain potentially interesting information.

function Get-WinEvent
{
    [CmdletBinding(DefaultParameterSetName='GetLogSet', HelpUri='https://go.microsoft.com/fwlink/?LinkID=138336')]
    param(

        [Parameter(ParameterSetName='ListLogSet', Mandatory, Position=0)]
        [AllowEmptyCollection()]
        [string[]]
        ${ListLog},

        [Parameter(ParameterSetName='LogNameGetEventlog', Mandatory, Position=0)] <#NEW#> 
        [Parameter(ParameterSetName='GetLogSet', Position=0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string[]]
        [ArgumentCompleter({
          # receive information about current state:
          param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
 
          # list all log files in the path
          Get-WinEvent -ListLog * -ErrorAction Ignore | 
          Where-Object RecordCount -gt 0 |
          Sort-Object -Property LogName |
          # filter results by word to complete
          Where-Object { $_.LogName -like "$wordToComplete*" } | 
          Foreach-Object { 
            # create completionresult items:
            $completion = $_.LogName
            if ($completion -like '* *')
            {
                $completion = "'$completion'"
            }
            $displayname = $_.LogName
            $tooltip = '{0:n0} Records, {1:n0} MB' -f $_.RecordCount, ($_.MaximumSizeInBytes/1MB)
            [System.Management.Automation.CompletionResult]::new($completion, $displayname, "ParameterValue", $tooltip)
          }
            })]
        ${LogName},

        [Parameter(ParameterSetName='ListProviderSet', Mandatory, Position=0)]
        [AllowEmptyCollection()]
        [string[]]
        ${ListProvider},

        <# Get-EventLog supports wildcards, Get-WinEvent does not. Needs to be corrected. #>
        [Parameter(ParameterSetName='GetProviderSet', Mandatory, Position=0, ValueFromPipelineByPropertyName)]
        [string[]]
        ${ProviderName},

        [Parameter(ParameterSetName='FileSet', Mandatory, Position=0, ValueFromPipelineByPropertyName)]
        [Alias('PSPath')]
        [string[]]
        ${Path},

        [Parameter(ParameterSetName='FileSet')]
        [Parameter(ParameterSetName='GetProviderSet')]
        [Parameter(ParameterSetName='GetLogSet')]
        [Parameter(ParameterSetName='HashQuerySet')]
        [Parameter(ParameterSetName='XmlQuerySet')]
        [ValidateRange(1, 9223372036854775807)]
        [long]
        ${MaxEvents},

        <# NEW #>
        [Parameter(ParameterSetName='LogNameGetEventlog')]
        [ValidateRange(0, 2147483647)]
        [int]
        ${Newest},

        [Parameter(ParameterSetName='GetProviderSet')]
        [Parameter(ParameterSetName='ListProviderSet')]
        [Parameter(ParameterSetName='ListLogSet')]
        [Parameter(ParameterSetName='GetLogSet')]
        [Parameter(ParameterSetName='HashQuerySet')]
        [Parameter(ParameterSetName='XmlQuerySet')]
        [Parameter(ParameterSetName='LogNameGetEventlog')] <#NEW#> 
        [Alias('Cn')]
        [ValidateNotNullOrEmpty()] <#CORRECTED#>
        [string] <# used to be [String[]], Get-WinEvent accepts [string] only, should be changed to accept string arrays #>
        ${ComputerName},

        [Parameter(ParameterSetName='GetProviderSet')]
        [Parameter(ParameterSetName='ListProviderSet')]
        [Parameter(ParameterSetName='ListLogSet')]
        [Parameter(ParameterSetName='GetLogSet')]
        [Parameter(ParameterSetName='HashQuerySet')]
        [Parameter(ParameterSetName='XmlQuerySet')]
        [Parameter(ParameterSetName='FileSet')]
        [pscredential]
        [System.Management.Automation.CredentialAttribute()]
        ${Credential},

        [Parameter(ParameterSetName='FileSet')]
        [Parameter(ParameterSetName='GetProviderSet')]
        [Parameter(ParameterSetName='GetLogSet')]
        [ValidateNotNull()]
        [string]
        ${FilterXPath},

        [Parameter(ParameterSetName='XmlQuerySet', Mandatory, Position=0)]
        [xml]
        ${FilterXml},

        [Parameter(ParameterSetName='HashQuerySet', Mandatory, Position=0)]
        [hashtable[]]
        ${FilterHashtable},

        [Parameter(ParameterSetName='GetProviderSet')]
        [Parameter(ParameterSetName='ListLogSet')]
        [Parameter(ParameterSetName='GetLogSet')]
        [Parameter(ParameterSetName='HashQuerySet')]
        [switch]
        ${Force},

        [Parameter(ParameterSetName='GetLogSet')]
        [Parameter(ParameterSetName='GetProviderSet')]
        [Parameter(ParameterSetName='FileSet')]
        [Parameter(ParameterSetName='HashQuerySet')]
        [Parameter(ParameterSetName='XmlQuerySet')]
        [switch]
        ${Oldest},

        <# NEW #>
        [Parameter(ParameterSetName='LogNameGetEventlog')]
        [ValidateNotNullOrEmpty()]
        [datetime]
        ${After},

        <# NEW #>
        [Parameter(ParameterSetName='LogNameGetEventlog')]
        [ValidateNotNullOrEmpty()]
        [datetime]
        ${Before},

        <# NEW #>
        [Parameter(ParameterSetName='LogNameGetEventlog')]
        [ValidateNotNullOrEmpty()]
        [string[]]
        ${UserName},

        <# NEW #>
        [Parameter(ParameterSetName='LogNameGetEventlog', Position=1)]
        [ValidateRange(0, 9223372036854775807)]
        [ValidateNotNullOrEmpty()]
        [long[]]
        ${InstanceId},

        <# NEW #>
        [Parameter(ParameterSetName='LogNameGetEventlog')]
        [ValidateNotNullOrEmpty()]
        [ValidateRange(1, 2147483647)]
        [int[]]
        ${Index},

        <# NEW #>
        [Parameter(ParameterSetName='LogNameGetEventlog')]
        [Alias('ET','LevelDisplayName')]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Error','Information','FailureAudit','SuccessAudit','Warning')]
        [string[]]
        ${EntryType},

        <# NEW #>
        [Parameter(ParameterSetName='LogNameGetEventlog')]
        [Alias('ABO')]
        [ValidateNotNullOrEmpty()]
        [string[]]
        [ArgumentCompleter({
          # receive information about current state:
          param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

          if ($fakeBoundParameters.ContainsKey('LogName'))
          {
            $nofilter = $false
            $chosenLog = $fakeBoundParameters['LogName']
          }
          else
          {
            $nofilter = $true
            $chosenLog = ''
          }
          # list all log files in the path
          Get-WinEvent -ListProvider * -ErrorAction Ignore |
          Where-Object { $nofilter -or ($_.LogLinks.LogName -contains $chosenLog) } | 
          Select-Object -ExpandProperty Name |
          Sort-Object |
          # filter results by word to complete
          Where-Object { $_ -like "$wordToComplete*" } | 
          Foreach-Object { 
            # create completionresult items:
            $completion = $_
            if ($completion -like '* *')
            {
                $completion = "'$completion'"
            }
            $displayname = $_
            $tooltip = $_
            [System.Management.Automation.CompletionResult]::new($completion, $displayname, "ParameterValue", $tooltip)
          }
            })]
        ${Source},

        <# NEW #>
        [Parameter(ParameterSetName='LogNameGetEventlog')]
        [Alias('MSG')]
        [ValidateNotNullOrEmpty()]
        [string]
        ${Message},

        <# NEW #>
        [Parameter(ParameterSetName='LogNameGetEventlog')]
        [switch]
        ${AsBaseObject},

        [Parameter(ParameterSetName='ListGetEventlog')]
        [switch]
        ${List},

        [Parameter(ParameterSetName='ListGetEventlog')]
        [switch]
        ${AsString}


    )

    begin
    {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
            {
                $PSBoundParameters['OutBuffer'] = 1
            }
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Diagnostics\Get-WinEvent', [System.Management.Automation.CommandTypes]::Cmdlet)

            # if the user chose the Get-EventLog compatible parameters,
            # compose the appropriate filterhashtable:
            $scriptCmd = if ($PSCmdlet.ParameterSetName -eq 'LogNameGetEventlog')
            {
                # mandatory parameter:
                $filter = @{
                    LogName = $PSBoundParameters['Logname']
                }
                $null = $PSBoundParameters.Remove('LogName')

                if ($PSBoundParameters.ContainsKey('Before'))
                {
                    $filter['EndTime'] = $PSBoundParameters['Before']
                    $null = $PSBoundParameters.Remove('Before')
                }
                if ($PSBoundParameters.ContainsKey('After'))
                {
                    $filter['StartTime'] = $PSBoundParameters['After']
                    $null = $PSBoundParameters.Remove('After')
                }
                if ($PSBoundParameters.ContainsKey('EntryType'))
                {
                    # severity is translated to an integer array:

                    $levelFlags = [System.Collections.Generic.List[int]]@()

                    # string input converted to integer array:
                    if ($PSBoundParameters['EntryType'] -contains 'Error')
                    {
                        $levelFlags.Add(1) # critical
                        $levelFlags.Add(2) # error
                    }
                    if ($PSBoundParameters['EntryType'] -contains 'Warning')
                    {
                        $levelFlags.Add(3) # warning
                    }
                    if ($PSBoundParameters['EntryType'] -contains 'Information')
                    {
                        $levelFlags.Add(4) # informational
                        $levelFlags.Add(5) # verbose
                    }
  

                    # default to 0:
                    if ($levelFlags.Count -gt 0)
                    {
                        $filter['Level'] = [int[]]$levelFlags
                    }

                    # audit settings stored in Keywords key:
                    if ($PSBoundParameters['EntryType'] -contains 'FailureAudit')
                    {
                        $filter['Keywords'] += 0x10000000000000
                    }
                    if ($PSBoundParameters['EntryType'] -contains 'SuccessAudit')
                    {
                        $filter['Keywords'] += 0x20000000000000
                    }
                    $null = $PSBoundParameters.Remove('EntryType')
                }
                if ($PSBoundParameters.ContainsKey('InstanceId'))
                {
                    $filter['ID'] = $PSBoundParameters['InstanceId']
                    $null = $PSBoundParameters.Remove('InstanceId')
                }
                if ($PSBoundParameters.ContainsKey('Source'))
                {
                    $filter['ProviderName'] = $PSBoundParameters['Source']
                    $null = $PSBoundParameters.Remove('Source')
                }

                $PSBoundParameters['FilterHashtable'] = $filter
                Write-Verbose ("FilterHashtable: " + ($filter | Out-String))

                if ($PSBoundParameters.ContainsKey('Newest'))
                {
                    $PSBoundParameters['MaxEvents'] = $PSBoundParameters['Newest']
                    $null = $PSBoundParameters.Remove('Newest')
                }
            }
            $scriptCmd = if ($PSBoundParameters.ContainsKey('Message'))
                {
                    $null = $PSBoundParameters.Remove('Message')
                    { & $wrappedCmd @PSBoundParameters | Where-Object Message -like $Message }
                }
                else
                {
                    { & $wrappedCmd @PSBoundParameters }
                }

            

            $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
            $steppablePipeline.Begin($PSCmdlet)
        } catch {
            throw
        }
    }

    process
    {
        try {
            $steppablePipeline.Process($_)
        } catch {
            throw
        }
    }

    end
    {
        try {
            $steppablePipeline.End()
        } catch {
            throw
        }
    }
    <#

            .ForwardHelpTargetName Microsoft.PowerShell.Diagnostics\Get-WinEvent
            .ForwardHelpCategory Cmdlet

    #>

} 

Let’s do a test drive and try and get the 20 newest errors from the system log file. As you quickly see, Get-WinEvent now accepts the same parameters that you used with Get-EventLog. When looking at the results, you can quickly see how Get-EventLog did not return back the correct event log messages, whereas Get-WinEvent did.

PS> Get-EventLog -LogName System -EntryType Error -Newest 3

   Index Time          EntryType   Source                 InstanceID Message                                 
   ----- ----          ---------   ------                 ---------- -------                                 
  551873 Jun 02 06:40  Error       DCOM                        10010 The description for Event ID '10010' ...
  551872 Jun 02 06:38  Error       DCOM                        10010 The description for Event ID '10010' ...
  551848 Jun 02 03:35  Error       DCOM                        10010 The description for Event ID '10010' ...

PS> Get-WinEvent -LogName System -EntryType Error -Newest 3

   ProviderName: Microsoft-Windows-DistributedCOM

TimeCreated                      Id LevelDisplayName Message                                                 
-----------                      -- ---------------- -------                                                 

02.06.2023 06:40:14           10010 Error            The server {A463FCB9-6B1C-4E0D-A80B-A2CA7999E25D} did...
02.06.2023 06:38:14           10010 Error            The server {A463FCB9-6B1C-4E0D-A80B-A2CA7999E25D} did...
02.06.2023 03:35:23           10010 Error            The server {776DBC8D-7347-478C-8D71-791E12EF49D8} did...

Add the -Verbose parameter to get verbose information about the filter hash table values or XML query your parameters translate to:

PS> Get-WinEvent -LogName System -EntryType Error -Newest 3 -Verbose
VERBOSE: FilterHashtable: 
Name                           Value                                                                                                 
----                           -----                                                                                                 
LogName                        {System}                                                                                              
Level                          {1, 2}                                                                                                

VERBOSE: Constructed structured query:
<QueryList><Query Id="0" Path="system"><Select Path="system">*[((System/Level=1) or 
(System/Level=2))]</Select></Query></QueryList>.

   ProviderName: Microsoft-Windows-DistributedCOM
TimeCreated                      Id LevelDisplayName Message                                                                         
-----------                      -- ---------------- -------                                                                         
02.06.2023 06:40:14           10010 Error            The server {A463FCB9-6B1C-4E0D-A80B-A2CA7999E25D} did not register with 
DCOM ...
02.06.2023 06:38:14           10010 Error            The server {A463FCB9-6B1C-4E0D-A80B-A2CA7999E25D} did not register with 
DCOM ...
02.06.2023 03:35:23           10010 Error            The server {776DBC8D-7347-478C-8D71-791E12EF49D8} did not register with 
DCOM ...