Splatting help needed

I am by no means a splatting expert. What I would like to do is clean this up using splatting (if possible) but am struggling to get it right.

$printData = Get-WinEvent -ComputerName $System -FilterHashTable $PrintFilter -ErrorAction Stop | Select-Object @{Label=‘EventID’;Expression={$.Id}}, TimeCreated,
Message, ProviderName, LogName, MachineName, @{Label=‘UserName’;Expression={(New-Object System.Security.Principal.SecurityIdentifier($
.UserId)).Translate([System.Security.Principal.NTAccount]).Value}}, TaskDisplayName

I have a fair number of these in the overall script with similar syntax. If someone can help me with this one, it will be a great help. I did not find much in a google search that helped.

Thanks.

I would use a PSCustomObject to manipulate the output instead of doing it on the fly via Select-Object as in:

$ParameterSet = @{
    ComputerName    = $System 
    FilterHashTable = $PrintFilter 
    ErrorAction     = 'Stop'
}
$myPrintdata = Get-WinEvent @ParameterSet | foreach {
    $Temp = New-Object System.Security.Principal.SecurityIdentifier($_.UserId)
    [PSCustomObject][Ordered]@{
        EventID         = $_.Id
        TimeCreated     = $_.TimeCreated
        Message         = $_.Message
        ProviderName    = $_.ProviderName
        LogName         = $_.LogName
        MachineName     = $_.MachineName
        UserName        = $Temp.Translate([System.Security.Principal.NTAccount]).Value
        TaskDisplayName = $_.TaskDisplayName
    }
}

Hmm… So - I don’t think ‘splatting’ is what you’re looking for. I think you’re wanting to clean up Select-Object, yes? If so, you need to clean up the -Property input. -Property takes an Object array, so we can throw Strings, hashtables, etc into it. Which means, our Select-Object -Property <Object> just needs an Array of Objects which has everything we want to select on, like:

# Test Example - Note changes to the Get-WinEvent command
$Props = @{n="EventID";e={$_.id}},
    "TimeCreated",
    "Message",
    "ProviderName",
    "LogName",
    "MachineName",
    @{n="UserName";e={(New-Object System.Security.Principal.SecurityIdentifier($_.UserId)).Translate([system.security.principal.NTAccount].Value)}}
    "TaskDisplayName"

Get-WinEvent -logname System -max 10 -Erroraction Stop | Select-Object -proper $Props
# What Splatting is meant to refer to, at least from my understanding,
# Taking a Name=Value pair, and using it to fill in the Parameter and input of that parameter.
$PrintDataParameters = @{
    ComputerName = $System
    FilterHashTable = $PrintFilter
    ErrorAction = 'stop'
}
$PrintData = Get-WinEvent @PrintDataParameters

# For Splatting the Select-Object, we really don't save much space...
$Props = @{n="EventID";e={$_.id}},
"TimeCreated",
"Message",
"ProviderName",
"LogName",
"MachineName",
@{n="UserName";e={(New-Object System.Security.Principal.SecurityIdentifier($_.UserId)).Translate([system.security.principal.NTAccount].Value)}}
"TaskDisplayName"

$SelObjParams = @{
    Property = $Props
}

$PrintData | Select-Object @SelObjParams

Hopefully, this gives you another way to look at splatting that makes more sense than what you’ve seen before.

Thank you Sam, that worked perfectly :slight_smile:

I was more after readability. Sam’s method also allows me to give more descriptive names to the properties returned and keep things ordered.

 

$PrintDataParameters = @{
    ComputerName    = $System
    FilterHashTable = $PrintFilter
    ErrorAction     = Stop'
}

$PrintData = Get-WinEvent @PrintDataParameters |
             Select-Object -Property @{Name="EventID";Expression={$_.id}},
                                     TimeCreated,
                                     Message,
                                     ProviderName,
                                     LogName,
                                     MachineName,
                                     @{Name="UserName";Expression={(New-Object System.Security.Principal.SecurityIdentifier($_.UserId)).Translate([system.security.principal.NTAccount].Value)}}
                                     TaskDisplayName

[quote quote=227326]Thank you Sam, that worked perfectly :slightly_smiling_face:

I was more after readability. Sam’s method also allows me to give more descriptive names to the properties returned and keep things ordered.

[/quote]

I think Sam and I just latched onto different parts of your original post. Looks like Sam latched onto “What I would like to do is clean this up”, whereas I (and maybe Rob too) latched onto “using splatting (if possible)”. Hopefully you learned something new from both! Only thing I’d change about Sam’s code is:

# $Temp = New-Object System.Security.Principal.SecurityIdentifier($_.UserId)
# would become:
$Temp = [System.Security.Principal.SecurityIdentifier]::New($_.UserId)

It’s not really a performance boost, but since the code is already using a pscustomobject type accelerator, might as well stick with type accelerators.

Mitch, I did learn something, thanks to everyone that posted. You all saved me a ton of time as well.

Need more help on this. It turns out that I am not 100% there yet. It appears the conversion to Splatting has induced an error on remote systems. If $System is defined as a remote system, the Translate Method is not able to resolve the SID to a username which makes sense as the method is executed from the system running the script through foreach. The part that is above my pay grade is how Select-Object is able to work.

I found this in a google search:

The SecurityReference object’s Translate method does work on non-local SIDs but only for domain accounts. For accounts local to another machine or in a non-domain setup you would need to PInvoke the function LookupAccountSid specifying the specific machine name on which the look up needs to be performed.

In my use case, there is no Domain involved. Any thoughts on how I can still resolve the SID to Username on remote systems and still use Splatting?

Thanks.

Some commands like Get-WinEvent feature implicit remoting. If you need to further process that output referencing objects on remote computer(s), you need to explicitly remote into the remote computer(s) via Invoke-command for example…

$ParameterSet = @{
    ComputerName    = $System 
    FilterHashTable = $PrintFilter 
    ErrorAction     = 'Stop'
}
$myPrintdata = Get-WinEvent @ParameterSet | foreach {
    $UserId = $_.UserId
    [PSCustomObject][Ordered]@{
        EventID         = $_.Id
        TimeCreated     = $_.TimeCreated
        Message         = $_.Message
        ProviderName    = $_.ProviderName
        LogName         = $_.LogName
        MachineName     = $_.MachineName
        UserName        = $(
            Invoke-Command -ComputerName $System -ScriptBlock {
                $Temp = New-Object System.Security.Principal.SecurityIdentifier($Using:UserId)
                $Temp.Translate([System.Security.Principal.NTAccount]).Value
            }
        )
        TaskDisplayName = $_.TaskDisplayName
    }
}

Thanks Sam. That was fast :slight_smile:

This worked:

$UserName = (New-Object System.Security.Principal.SecurityIdentifier($Using:UserId.Value)).Translate([System.Security.Principal.NTAccount]).Value

Thanks again Sam :slight_smile:

Having to add more logic to determine if $System is local or remote, I will likely be going back to Select-Object. There are just too many of these in the script which it too big already.

 

 

Just a suggestion - think about breaking your script up into Functions/modules. Might help with reused code, and make it more modular/flexible/easier to rework. And don’t forget, Don Jones’ advice about Tools vs Controllers is still very valid.

OK, one last question solely born out of curiosity.

Why was Select-Object able to resolve the user SID versus having to go through special hoops after Splatting? It would seem to me that since both methods are accepting data from the pipeline, I should have had to go through the same hoops with either method.

I had to go through even nastier hoops for group change ID’s using a fair number of Event ID’s. I had to convert to XML and use XML nodes for my data as M$ was not consistent in their properties for each event ID. Thankfully, the XML was. I was surprised that it worked, again using Select-Object.

Thanks in advance.