As a powerful tool, PowerShell is not only of interest for admins but also for hackers. To detect suspicious activities, it is helpful to have all executed commands recorded. In addition to recording the history in a text file, PowerShell has also supported logging in the event log since version 5.

PowerShell v5 included several innovations in logging. It extended the older method, the so-called "over-the-shoulder transcription," to all PS hosts, including ISE, and hence was no longer limited to the command line. Furthermore, this feature can now also be activated via group policies.

Logging the actual commands

The recording of all commands in a text file has been complemented by the so-called deep scriptblock logging. It not only uses the Windows event log instead of a text file, but also records all commands exactly as executed by PowerShell. This way, malicious activity does not easily go unnoticed.

This applies, for example, to the use of dynamic code generation, where commands are stored in a variable and then executed with the help of Invoke-Expression. The feature also reveals attempts to hide command sequences by encoding them using Base64.

Activation only via GPO

While transcriptions can also be explicitly turned on and off using the Start-Transcript and Stop-Transcript cmdlets, you can enable script block logging only by using GPOs or by setting the appropriate registry key directly. Therefore, there is still a need for the older method, such as recording the output in your own scripts.

Group policy to enable deep scriptblock logging

Group policy to enable deep scriptblock logging

The relevant GPO setting is called Turn on PowerShell Script Block Logging and can be found under Policies > Administrative Templates > Windows Components > Windows PowerShell. If you configure it under Computer and User Configuration, the former setting prevails.

If you select the option for start/stop, then you should expect a considerably higher data volume because markers for the start and stop of all events will be written to the log.

Preparing the event log

While you prepare the logging in text files by creating a directory on a file share and assigning the necessary access rights, different preparatory work is required for the newer logging.

Start by changing the maximum size of the event log from the default of 20 MB to a significantly higher value. This is required for two reasons: First, depending on the configuration of the logging feature, a relatively large amount of data is accumulated. Second, attackers should not be able to simply cover their tracks by filling up the log relatively quickly with unsuspicious entries.

Since the evaluation of the logs is left either to scripts developed for this purpose or to SIEM tools, the recorded events are needed at a central location. For this purpose, forward the entries written by PowerShell to a computer in the network.

The logging is done under PowerShell/Operational

The logging is done under PowerShell/Operational

Event IDs

The logging takes place in the application log under Microsoft > Windows > PowerShell > Operational, and the commands are recorded under event ID 4104. If you also record start and stop events, these appear under the IDs 4105 and 4106.

Custom filter in the event viewer for recorded script blocks

Custom filter in the event viewer for recorded script blocks

If you want to set up a user-defined filter for the recorded commands in the event viewer, activate as source

  • PowerShell (Microsoft-Windows-PowerShell),
  • PowerShell (PowerShell)
  • PowerShellCore

In addition, select Warning as the event type and enter 4104 as the ID.

Merging command sequences

While transcripts can write their data to a text file with virtually no limits, the script block field in the event log limits the length of the record. Therefore, longer scripts are split up and span several entries.

On Microsoft Docs, there is a template for a PowerShell script that can be used to reassemble the log fragments. If for example you want to string together all recordings for a process with ID 6524, then you could proceed as follows:

$created = Get-WinEvent -FilterHashtable @{ProviderName="Microsoft-Windows-PowerShell"; Id = 4104 } |
where ProcessId -eq 6524
$sortedScripts = $created | sort {$_.Properties[0].Value}
$mergedScript = -join ($sortedScripts |
foreach { $_.Properties[2].Value })

Script block logging for PowerShell Core

As with transcripts, group policy enables logging of script blocks only for Windows PowerShell. It has no effect on PowerShell Core 6.x and its successor, PowerShell 7.

If you want to record the commands for version 6.x in the event log, you have to set the registry key yourself. To do this, create the ScriptBlockLogging key under


and assign the value 1 to EnableScriptBlockLogging. The following instructions in a .reg file will accomplish this task:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\PowerShellCore\ScriptBlockLogging] "EnableScriptBlockLogging"=dword:00000001

PowerShell 7, on the other hand, includes its own ADMX template, which you can copy to %systemroot%\policydefinitions or to the central store. It contains all the settings known from PowerShell 5, including those for scriptblock logging.

Group policy settings for PowerShell 7

Group policy settings for PowerShell 7

Finally, it should be noted that the log entries for PowerShell Core are located directly under the Applications and Services logs. The event IDs for logging are the same as for Windows PowerShell.


Leave a reply

Your email address will not be published.


© 4sysops 2006 - 2023


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account