Tools · Windows security and Event Logs · 16 / 27
PowerShell transcription and module logging (Event ID 4103) explained
Compare script block logging, module logging, and transcription, enable transcripts to a protected share, and know what each one captures.
Three complementary logs
Script block logging (4104) records the code. Module logging (4103) records pipeline execution details for specified modules, including parameter values bound to commands. Transcription writes a text file of each session: the commands typed and the output shown, as a user would have seen it.
Transcripts are the easiest for humans to read and are very useful in insider and hands-on-keyboard cases. Script block logging is the most reliable for obfuscated code.
Enable transcription
Group Policy “Turn on PowerShell Transcription” takes an output directory and an option to include invocation headers with timestamps. Point it to a network share where machines can write but not read or delete, so a user cannot tamper with their own transcript. Module logging takes a list of module names; * logs all.
Transcripts can include secrets typed at the prompt. Protect the share accordingly.
$k='HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription' New-Item $k -Force | Out-Null Set-ItemProperty $k EnableTranscripting 1 Set-ItemProperty $k EnableInvocationHeader 1 Set-ItemProperty $k OutputDirectory '\\logsrv\pstranscripts#x27; $m='HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging' New-Item "$m\ModuleNames" -Force | Out-Null Set-ItemProperty $m EnableModuleLogging 1 Set-ItemProperty "$m\ModuleNames" '*' '*'
Lab: compare the three outputs
Enable all three on a lab VM with a local transcript folder. Run a short session: list processes, read a file, and run an encoded command. Open the transcript file, then query 4103 and 4104 for the same minutes. Note what each shows: the transcript has output, 4103 has bound parameters, and 4104 has decoded code.
Decide which ones your organization needs. Many enable 4104 everywhere and transcription on servers and admin workstations.
Get-ChildItem C:\PSTranscripts -Recurse | Sort LastWriteTime | Select -Last 1 | Get-Content | Select -First 40
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational'; Id=4103; StartTime=(Get-Date).AddMinutes(-10)} -MaxEvents 5 | Format-List MessageKnown gaps
Windows PowerShell 2.0, if still installed, bypasses these logging features. Remove it with Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root. PowerShell 7 has its own policy keys and its own log, PowerShellCore/Operational, which need separate configuration.
Code run through custom hosts that load the automation library directly may not create transcripts, which is another reason to keep script block logging on.
Diagram
What each PowerShell log captures
Use them together.
Shows. Typed commands and their screen output.
Best for. Reading a session like a human.
Store. Write-only network share.
Shows. Decoded code (4104) and bound parameters (4103).
Best for. Obfuscated or scripted activity.
Store. Event log, forwarded centrally.
Check yourself
1. Which feature records command output as the user saw it?
2. Why remove Windows PowerShell 2.0?
3. Why use a write-only share for transcripts?