This is part 2 of our mini-series covering PowerShell script block logging. Today, we’ll again read the script block logging log, but this time we’ll try and get back the log data in a more useful object-oriented way:

function Get-LoggedCode
{
    # read all raw events
    $logInfo = @{ ProviderName="Microsoft-Windows-PowerShell"; Id = 4104 }
    Get-WinEvent -FilterHashtable $logInfo | 
        # take each raw set of data...
        ForEach-Object {
            # create a new object and extract the interesting
            # parts from the raw data to compose a "cooked"
            # object with useful data
            [PSCustomObject]@{
                # when this was logged
                Time = $_.TimeCreated
                # script code that was logged
                Code = $_.Properties[2].Value
                # if code was split into multiple log entries,
                # determine current and total part
                PartCurrent = $_.Properties[0].Value
                PartTotal = $_.Properties[1].Value
                
                # if total part is 1, code is not fragmented
                IsMultiPart = $_.Properties[1].Value -ne 1
                # path of script file (this is empty for interactive
                # commands)
                Path = $_.Properties[4].Value
                # log level
                # by default, only level "Warning" will be logged
                Level = $_.LevelDisplayName
                # user who executed the code (SID)
                User = $_.UserId
            }
        } 
}

When you run this code, you now have a new command named Get-LoggedCode. When you execute it, it returns objects like this:

 
Time        : 25.05.2018 10:57:36
Code        : function Get-LoggedCode
              {
                  # read all raw events
                  $logInfo = @{ ProviderName="Microsoft-Windows-PowerShell"; Id = 4104 }
                  Get-WinEvent -FilterHashtable $logInfo | 
                      # take each raw set of data...
                      ForEach-Object {
                          # create a new object and extract the interesting
                          # parts from the raw data to compose a "cooked"
                          # object with useful data:
                          [PSCustomObject]@{
                              # when this was logged:
                              Time = $_.TimeCreated
                              # script code that was logged:
                              Code = $_.Properties[2].Value
                              # if code was split into multiple log entries,
                              # determine current and total part:
                              PartCurrent = $_.Properties[0].Value
                              PartTotal = $_.Properties[1].Value
                              
                              # if total part is 1, code is not fragmented:
                              IsMultiPart = $_.Properties[1].Value -ne 1
                              # path of script file (this is empty for interactive
                              # commands)
                              Path = $_.Properties[4].Value
                              # log level
                              # by default, only level "Warning" will be logged:
                              Level = $_.LevelDisplayName
                              # user who executed the code (SID)
                              User = $_.UserId
                          }
                      } 
              }
              
              
              
PartCurrent : 1
PartTotal   : 1
IsMultiPart : False
Path        : D:\sample.ps1
Level       : Warning
User        : S-1-5-21-2012478179-265285931-690539891-1001 
 

In our example, we added “Select-Object” to not read the entire log but just the last entry. Here, we got back the code that we just executed. This may be different for you, and here is why:

By default, script block logging only logs code considered “security relevant” (in the returned data, Level is “Warning”). PowerShell determines internally which code is considered security relevant. In tomorrow’s tip, we will explain how you enable the “Verbose” mode. When this mode is turned on, any code is logged, so your log file grows in size considerably. Here is how you can check the ratio of default (“Warning”) logs versus verbose logs (taken from our test machine):

 
PS> Get-LoggedCode | Group-Object Level

Count Name                      Group                                                             
----- ----                      -----                                                             
  549 Verbose                   {@{Time=25.05.2018 10:57:52; Code=prompt;..
   36 Warning                   {@{Time=25.05.2018 10:57:52; Code={...   
 

Please note: Since log entries have a maximum size, long code is split up in chunks. The “IsMultiPart”, “PartCurrent”, and “PartTotal” properties provide information about this.

Twitter This Tip! ReTweet this Tip!

Anonymous