Senior Systems Engineer
Senior Systems Engineer

Reputation: 1139

Powershell Calculated Property - Calling HashTable enumerator?

I'm unable to display the calculated property column called Logon Type which is translated from a hash table.

The script below is working fine, but I just need to translate the raw value number into a more meaningful description.

function Get-LogonEvents {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ServerName', 'Server', 'Name')]
        [string[]]$ComputerName,

        [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
        [PSCredential]$Credential,

        [Parameter()]
        [ValidateSet("Service", "Interactive", "RemoteInteractive", "NetworkCleartext", "CachedInteractive", "Unlock", "NewCredentials", "Network", "*")]
        [string[]]$LogonType = @("Interactive", "RemoteInteractive", "CachedInteractive"),

        [string]$UserName,

        [Parameter()]
        [switch]$Oldest,

        [Parameter()]
        [int64]$MaxEvents,

        [Parameter()]
        [datetime]$StartTime = (Get-Date 1/1/1900),

        [Parameter()]
        [datetime]$StopTime = (Get-Date 1/1/2100)
    )
    Begin {
        Function ParseEventMessage {
            [CmdletBinding()]
            param(
                [Parameter(ValueFromPipeline = $true)]
                $obj
            )
            Begin {
                $defaultDisplaySet = 'TimeCreated', 'MachineName', 'TargetDomainName', 'TargetUserName'
                $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet(‘DefaultDisplayPropertySet’, [string[]]$defaultDisplaySet)
                $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
                $myHash = @{ }
            }
            Process {

                ([xml]($obj.ToXml())).event.eventdata.data | ForEach-Object { $myHash[$PSItem.name] = $PSItem.'#text' }

                New-Object -TypeName PSObject -Property $myHash | ForEach-Object {

                    $PSItem.PSObject.TypeNames.Insert(0, "EventLogRecord.XMLParse")

                    $PSItem | Add-Member MemberSet PSStandardMembers $PSStandardMembers -PassThru |
                    Add-Member -MemberType NoteProperty -Name TimeCreated -Value $obj.timecreated -PassThru |
                    Add-Member -MemberType NoteProperty -Name MachineName -Value $obj.MachineName -PassThru
                }

            }
        }

        $hashLogonType = @{
            "Interactive"       = "2"
            "Network"           = "3"
            "Service"           = "5"
            "Unlock"            = "7"
            "NetworkCleartext"  = "8"
            "NewCredentials"    = "9"
            "RemoteInteractive" = "10"
            "CachedInteractive" = "11"
        }

        $filter = @"
    <QueryList>
        <Query Id="0" Path="Security">
        <Select Path="Security">
            *[System[
                (EventID='4624')
                and TimeCreated[@SystemTime&gt;='{0}' and @SystemTime&lt;='{1}']
            ]
                and EventData[
                    Data[@Name='LogonType'] and ({2})
                    {3}
                ]
            ]
        </Select>
        </Query>
    </QueryList>
"@
    }
    Process {
        foreach ($obj in $ComputerName) {
            if ($UserName) {
                $joinUserName = "and Data[@Name='TargetuserName'] and (Data='{0}')" -f $UserName
            }

            $joinLogonType = ($LogonType | ForEach-Object { $hashLogonType[$PSItem] }) -replace '^', "Data='" -replace '$', "'" -join " or "
            $objFilter = $filter -f (Get-Date $StartTime -Format s), (Get-Date $StopTime -Format s), $joinLogonType, $joinUserName

            $hashEventParm = @{
                ComputerName = $obj
                FilterXml    = $objFilter
            }

            if ($Credential) { $hashEventParm['Credential'] = $Credential }
            if ($MaxEvents) { $hashEventParm['MaxEvents'] = $MaxEvents }

            $objFilter | Write-Verbose

            Get-WinEvent @hashEventParm | ParseEventMessage
        }
    }
    End { }
}

$TargetDomainNameException = @('Window Manager','Font Driver Host')
$exceptionRegex = $TargetDomainNameException -join "|"

Get-LogonEvents -ComputerName 'Localhost' -MaxEvents 10 | 
    Where-Object { ($_.TargetDomainName -notmatch $exceptionRegex) } | 
    Select-Object WorkstationName, 
    TargetUserName, 
    TargetDomainName, 
    Type,
    LogonType,
    @{n ='LogonType'; e={$hashLogonType[[string]$_.LogonType]}}, 
    @{n = 'Logon Type'; e = {$hashLogonType["$($_.LogonType)"]}},
    ProcessName, 
    IPAddress, 
    @{n="Host Name"; e={([System.Net.Dns]::GetHostByAddress($_.IPaddress).Hostname)}}, 
    TimeCreated | 
    Out-GridView

Error: I have modifiedthe Calculated property like: @{n = 'Logon Type'; e = {$hashLogonType["$($_.LogonType)"]}},

Somehow it is still not displaying the column "Logon Type", however, the raw value on LogonType column still showing as 10, 3 ,etc...?

Upvotes: 0

Views: 262

Answers (2)

user2674513
user2674513

Reputation:

I see two problems.

  1. $hashLogonType is defined inside the function and won't be available in the global scope.
  2. The keys for $hashLogonType are by [string] not by [int].

If you're able to modify the original function, you might consider adding a property where the string value of LogonType is saved.

Otherwise, keep a copy of $hashLogonType in your variable scope with integers as keys, and base your calculated property on that.


The easiest way to get what you want is to create your own hash table and use it in your pipeline.

# Create a hash table for your own use in your variable scope. 
$myHashTable = @{
    2 = "Interactive"
    3 = "Network"
    5 = "Service"
    7 = "Unlock"
    8 = "NetworkCleartext"
    9 = "NewCredentials"
    10 = "RemoteInteractive"
    11 = "CachedInteractive"
}

# Shim object. 
$exampleObject = [PSCustomObject]@{
    LogonType = 2
    WorkstationName = "myHost.example.com"
    }

# Modify your pipeline to use your hash table. 
$exampleObject | 
  Select-Object -Property WorkstationName, LogonType, @{label="Logon Title";expression={$myHashTable[$_.LogonType]}}
PS> ./Answer 02.ps1

WorkstationName    LogonType Logon Title
---------------    --------- -----------
myHost.example.com         2 Interactive

In principle, it is possible to modify the original function. But, I don't have any data to test with. Maybe Doug can help. He seems to have access to an event log.

You would have to do two things.

  1. Add a hash table with integer keys in scope for ParseEventMessage(). For example, add the hash table to ParseEventMessage()'s Begin block.
  2. Where it says
Add-Member -MemberType NoteProperty -Name MachineName -Value $obj.MachineName -PassThru

Add another property by extending that pipeline:

Add-Member -MemberType NoteProperty -Name LogonTitle -Value {$myHashTable[$_.LogonType]} -PassThru

Upvotes: 2

Doug Maurer
Doug Maurer

Reputation: 8868

Edit: Yes Mike is absolutely correct, the hashtable was defined inside the get-logonevents function and not used. I've moved it out and now it should work.

I think you should reverse the assignment of the hashtable. Either as an int or a string should work then. I did it like this and it worked fine.

    function Get-LogonEvents {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ServerName', 'Server', 'Name')]
        [string[]]$ComputerName,

        [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
        [PSCredential]$Credential,

        [Parameter()]
        [ValidateSet("Service", "Interactive", "RemoteInteractive", "NetworkCleartext", "CachedInteractive", "Unlock", "NewCredentials", "Network", "*")]
        [string[]]$LogonType = @("Interactive", "RemoteInteractive", "CachedInteractive"),

        [string]$UserName,

        [Parameter()]
        [switch]$Oldest,

        [Parameter()]
        [int64]$MaxEvents,

        [Parameter()]
        [datetime]$StartTime = (Get-Date 1/1/1900),

        [Parameter()]
        [datetime]$StopTime = (Get-Date 1/1/2100)
    )
    Begin {
        Function ParseEventMessage {
            [CmdletBinding()]
            param(
                [Parameter(ValueFromPipeline = $true)]
                $obj
            )
            Begin {
                $defaultDisplaySet = 'TimeCreated', 'MachineName', 'TargetDomainName', 'TargetUserName'
                $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet(‘DefaultDisplayPropertySet’, [string[]]$defaultDisplaySet)
                $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
                $myHash = @{ }
            }
            Process {

                ([xml]($obj.ToXml())).event.eventdata.data | ForEach-Object { $myHash[$PSItem.name] = $PSItem.'#text' }

                New-Object -TypeName PSObject -Property $myHash | ForEach-Object {

                    $PSItem.PSObject.TypeNames.Insert(0, "EventLogRecord.XMLParse")

                    $PSItem | Add-Member MemberSet PSStandardMembers $PSStandardMembers -PassThru |
                    Add-Member -MemberType NoteProperty -Name TimeCreated -Value $obj.timecreated -PassThru |
                    Add-Member -MemberType NoteProperty -Name MachineName -Value $obj.MachineName -PassThru
                }

            }
        }

        $filter = @"
    <QueryList>
        <Query Id="0" Path="Security">
        <Select Path="Security">
            *[System[
                (EventID='4624')
                and TimeCreated[@SystemTime&gt;='{0}' and @SystemTime&lt;='{1}']
            ]
                and EventData[
                    Data[@Name='LogonType'] and ({2})
                    {3}
                ]
            ]
        </Select>
        </Query>
    </QueryList>
"@
    }
    Process {
        foreach ($obj in $ComputerName) {
            if ($UserName) {
                $joinUserName = "and Data[@Name='TargetuserName'] and (Data='{0}')" -f $UserName
            }

            $joinLogonType = ($LogonType | ForEach-Object { $hashLogonType[$PSItem] }) -replace '^', "Data='" -replace '$', "'" -join " or "
            $objFilter = $filter -f (Get-Date $StartTime -Format s), (Get-Date $StopTime -Format s), $joinLogonType, $joinUserName

            $hashEventParm = @{
                ComputerName = $obj
                FilterXml    = $objFilter
            }

            if ($Credential) { $hashEventParm['Credential'] = $Credential }
            if ($MaxEvents) { $hashEventParm['MaxEvents'] = $MaxEvents }

            $objFilter | Write-Verbose

            Get-WinEvent @hashEventParm | ParseEventMessage
        }
    }
    End { }
}

$hashLogonType = @{
    2 = "Interactive"
    3 = "Network"
    5 = "Service"
    7 = "Unlock"
    8 = "NetworkCleartext"
    9 = "NewCredentials"
    10 = "RemoteInteractive"
    11 = "CachedInteractive"
}

$TargetDomainNameException = @('Window Manager','Font Driver Host')
$exceptionRegex = $TargetDomainNameException -join "|"

Get-LogonEvents -ComputerName 'Localhost' -MaxEvents 10 -OutVariable LogonEvents | 
    Where-Object { ($_.TargetDomainName -notmatch $exceptionRegex) } | 
    Select-Object WorkstationName, 
    TargetUserName, 
    TargetDomainName, 
    Type,
    @{n="LogonType";e={$hashLogonType.[int]$_.logontype}}, 
    ProcessName, 
    IPAddress, 
    @{n="Host Name"; e={([System.Net.Dns]::GetHostByAddress($_.IPaddress).Hostname)}}, 
    TimeCreated | 
    Out-GridView

Upvotes: 1

Related Questions