Splatting help needed

Welcome Forums General PowerShell Q&A Splatting help needed

Viewing 11 reply threads
  • Author
    Posts
    • #227293
      Participant
      Topics: 6
      Replies: 93
      Points: 428
      Helping Hand
      Rank: Contributor

      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.

    • #227308
      Participant
      Topics: 12
      Replies: 524
      Points: 1,219
      Helping Hand
      Rank: Community Hero

      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
          }
      }
      
      • This reply was modified 3 weeks, 1 day ago by Sam Boutros.
      • This reply was modified 3 weeks, 1 day ago by Sam Boutros.
    • #227323
      Participant
      Topics: 4
      Replies: 12
      Points: 89
      Rank: Member

      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.

    • #227326
      Participant
      Topics: 6
      Replies: 93
      Points: 428
      Helping Hand
      Rank: Contributor

      Thank you Sam, that worked perfectly 🙂

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

       

      • This reply was modified 3 weeks, 1 day ago by TonyD05.
    • #227329
      Participant
      Topics: 12
      Replies: 1623
      Points: 2,565
      Helping Hand
      Rank: Community Hero
      $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
      
    • #227353
      Participant
      Topics: 4
      Replies: 12
      Points: 89
      Rank: Member

      Thank you Sam, that worked perfectly 🙂

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

      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.

    • #227587
      Participant
      Topics: 6
      Replies: 93
      Points: 428
      Helping Hand
      Rank: Contributor

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

    • #227785
      Participant
      Topics: 6
      Replies: 93
      Points: 428
      Helping Hand
      Rank: Contributor

      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.

    • #227788
      Participant
      Topics: 12
      Replies: 524
      Points: 1,219
      Helping Hand
      Rank: Community Hero

      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
          }
      }
      
      • This reply was modified 2 weeks, 6 days ago by Sam Boutros.
    • #227794
      Participant
      Topics: 6
      Replies: 93
      Points: 428
      Helping Hand
      Rank: Contributor

      Thanks Sam. That was fast 🙂

      This worked:

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

      Thanks again Sam 🙂

      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.

       

      • This reply was modified 2 weeks, 6 days ago by TonyD05.
    • #227812
      Participant
      Topics: 4
      Replies: 12
      Points: 89
      Rank: Member

      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.

    • #228250
      Participant
      Topics: 6
      Replies: 93
      Points: 428
      Helping Hand
      Rank: Contributor

      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.

Viewing 11 reply threads
  • You must be logged in to reply to this topic.