Join our Newsletter — 33% off our NHI Course
Home› Guides› PowerShell Security Guide: Logging, Constrained Language and Abuse
Guide Endpoint Security

PowerShell Security Guide: Logging, Constrained Language and Abuse

← All guides
By Lalit Choda, NHI Mgmt Group Updated 16 October 2026 17 min read
On this page

PowerShell is on every Windows system, runs with the rights of whoever starts it, and is a staple of attacker tradecraft once they are inside a network. Blocking it outright is rarely practical and, as a joint NSA, CISA and NCSC information sheet puts it, "Blocking PowerShell hinders defensive capabilities that current versions of PowerShell can provide." The better answer is to configure it so that malicious use is visible, constrained and attributable. This guide covers script block, module and transcription logging, Constrained Language mode with App Control for Business, AMSI, execution policy, removing PowerShell 2.0, WinRM remoting and Just Enough Administration (JEA). It describes attacker behaviour at the MITRE ATT&CK level only and is written for Windows administrators, endpoint engineers and SOC teams.

Key takeaways

  • Turn on script block logging everywhere. Event 4104 records the deobfuscated code PowerShell actually runs, which defeats most encoding tricks; centralise it and protect it.
  • Execution policy is not a control. Microsoft states it "isn't a security boundary"; any user can override it for a single session.
  • Constrained Language mode needs App Control. Only Constrained Language mode under App Control for Business in System Lockdown is treated by Microsoft as a security boundary; AppLocker-based enforcement is defence in depth.
  • Remove PowerShell 2.0. The legacy engine predates script block logging and AMSI; Microsoft removed it from Windows 11 24H2 and Windows Server 2025 in 2025, but older builds still need it disabled.
  • Delegate with JEA, not admin rights. JEA endpoints let operators run named commands through virtual accounts without holding standing administrator credentials.

How PowerShell is abused

MITRE ATT&CK tracks PowerShell as T1059.001, noting that "Adversaries may abuse PowerShell commands and scripts for execution". The behaviours below recur across ransomware and intrusion reports. Public offensive frameworks such as PowerShell Empire, PowerSploit and Nishang package many of them, and tools such as Invoke-PSImage hide script content inside image files. Defenders need to recognise these names in alerts and threat reports; the defensive response is the same regardless of tooling.

Abuse patternATT&CKPrimary defensive controlTelemetry
Running scripts and one-liners for discovery and executionT1059.001 PowerShellApp Control for Business with Constrained Language mode; least privilege4104, 4103, 4688 with command line
Base64 -EncodedCommand and string obfuscationT1027.010 Command ObfuscationScript block logging (logs decoded content); AMSI4104; 4688 showing -enc or -e
Download-and-execute in memory, often with Invoke-ExpressionT1105 Ingress Tool Transfer; T1059.001AMSI; egress filtering; CLM blocks many .NET calls used to do this4104; proxy and DNS logs
Payloads hidden in images or other filesT1027.003 SteganographyAMSI scans content at execution, after decoding4104; AMSI detections
Starting PowerShell 2.0 to avoid logging and AMSIT1689 Downgrade AttackRemove the PowerShell 2.0 featureEvent 400 with EngineVersion 2.0
Tampering with AMSI or logging settingsT1685 Disable or Modify ToolsTamper protection; restrict registry and policy changes4104 content; registry auditing; EDR alerts
Lateral movement over WinRM with valid accountsT1021.006 Windows Remote ManagementRestrict WinRM sources and accounts; JEA endpointsWinRM operational log; 4624 logon type 3; 4103/4104 on target
Harvesting credentials from scripts and promptsT1552.001 Credentials In Files; T1056.002 GUI Input CaptureNo credentials in scripts; secrets vault; gMSAsSecret scanning of script repositories; 4104

Logging: script block, module and transcription

PowerShell has three complementary logging mechanisms. All three are configured under Computer Configuration > Administrative Templates > Windows Components > Windows PowerShell for Windows PowerShell 5.1. PowerShell 7 ships its own "PowerShell Core" administrative templates and writes to a separate PowerShellCore/Operational log, so configure both if both are installed.

  • Script block logging: "Turn on PowerShell Script Block Logging" writes the content of every script block PowerShell processes to Microsoft-Windows-PowerShell/Operational as event 4104. Because PowerShell logs the block it is about to execute, encoded or concatenated commands appear in readable form. Registry equivalent: HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging, EnableScriptBlockLogging = 1 (PowerShell 7: ...\Policies\Microsoft\PowerShellCore\ScriptBlockLogging). The optional invocation logging adds start and stop events (4105 and 4106) and generates high volume; leave it off unless you have a specific need.
  • Automatic logging: since Windows PowerShell 5.0, PowerShell logs script blocks that contain content often used by malicious scripts even without the policy. Treat it as a safety net, not coverage.
  • Module logging: "Turn on Module Logging" records pipeline execution details, including parameter values, as event 4103. Specify module names (a wildcard covers all modules). It complements 4104 by showing what each command was actually passed at run time.
  • Transcription: "Turn on PowerShell Transcription" writes input and output of every session to text files, which the NSA/CISA sheet calls over-the-shoulder transcription. Point OutputDirectory at a write-only network share; Microsoft warns to limit access so users cannot read other users' transcripts. Enable invocation headers for timestamps.
  • Protected Event Logging: under Windows Components > Event Logging, "Enable Protected Event Logging" encrypts 4104 content with a certificate carrying the Document Encryption EKU (1.3.6.1.4.1.311.80.1). Microsoft notes that the private key "shouldn't be deployed to the machines logging events"; decrypt centrally in the SIEM pipeline.
  • Process creation: enable "Audit Process Creation" and "Include command line in process creation events" so event 4688 captures the powershell.exe or pwsh.exe command line, including -EncodedCommand, -NonInteractive and -ExecutionPolicy Bypass flags. CIS Safeguard 8.8 (Collect Command-Line Audit Logs) names PowerShell explicitly.

Logs capture secrets. Scripts that embed passwords or tokens will write them into 4104 events, transcripts and 4688 command lines, and Microsoft warns that anyone who can read security events can read those command lines. Restrict log readers, use Protected Event Logging where feasible, and fix the scripts.

Event reference

LogEvent IDWhat it recordsEnabled by
Microsoft-Windows-PowerShell/Operational4104Script block content (deobfuscated)Script block logging policy; some blocks logged automatically
Microsoft-Windows-PowerShell/Operational4103Module and pipeline execution detailsModule logging policy
Microsoft-Windows-PowerShell/Operational4105, 4106Script block invocation start and stopScript block invocation logging
Windows PowerShell (classic)400, 403Engine state started and stopped, including host and engine versionAlways on
Windows PowerShell (classic)800Pipeline execution detailsModule logging
Security4688Process creation with command lineAudit Process Creation plus command-line inclusion policy
PowerShellCore/Operational4104, 4103PowerShell 7 equivalentsPowerShell Core policies or powershell.config.json

Constrained Language mode, App Control and execution policy

PowerShell has four language modes: FullLanguage, RestrictedLanguage, ConstrainedLanguage and NoLanguage. Constrained Language mode (CLM) permits all cmdlets but limits the .NET types that can be used: Add-Type cannot load arbitrary C# code or Win32 APIs, New-Object works only on allowed types, and only three COM objects are permitted. That removes most of the techniques attackers use to reach Windows APIs directly from a script.

CLM is not something you switch on by itself. PowerShell enters it automatically when a system application control policy, App Control for Business (formerly WDAC) or AppLocker, is enforced. Scripts the policy allows run in FullLanguage; everything else, including interactive input, runs constrained. Microsoft's language modes documentation is explicit: "You must use ConstrainedLanguage mode in System Lockdown mode with App Control for Business to ensure that ConstrainedLanguage mode can't be bypassed." Setting the mode manually in a session is "only useful for experimenting".

ControlMicrosoft classificationWhat it stopsMain limitation
Execution policyDefence in depthAccidental running of unsigned or downloaded scriptsOverridden per session with -ExecutionPolicy Bypass; does not apply to typed commands
CLM via AppLockerDefence in depth; AppLocker is not a security feature under MSRC servicing criteria and receives security fixes onlyArbitrary .NET and Win32 access from untrusted scriptsBypasses are not serviced as vulnerabilities
CLM via App Control for Business in System LockdownSecurity boundaryArbitrary code from untrusted scripts and interactive sessionsNeeds a maintained allow policy and signed admin scripts
AMSIAnti-malware integrationKnown-malicious script content, after deobfuscationDepends on the registered anti-malware engine and signatures
JEA (NoLanguage endpoints)Delegation and least privilegeRunning anything outside the role's allowed commandsDoes not protect against users who are already administrators
Logging (4103, 4104, transcription)VisibilityNothing directly; enables detection and investigationNeeds central collection and tamper resistance

Two script-authoring rules matter under lockdown: never dot-source scripts inside modules, and export module functions by explicit name rather than wildcard. Microsoft blocks wildcard exports in locked-down modules to stop a planted script being dot-sourced into a trusted one. CIS Safeguard 2.7 (Allowlist Authorized Scripts) expects digital signatures and version control to ensure only authorised scripts run.

Execution policy

Execution policy controls whether PowerShell loads configuration files and runs scripts. The policies are AllSigned, Bypass, Default, RemoteSigned, Restricted, Undefined and Unrestricted; Default means Restricted on Windows clients and RemoteSigned on Windows servers. Scopes set by Group Policy (MachinePolicy and UserPolicy, through "Turn on Script Execution") override Process, CurrentUser and LocalMachine. Get-ExecutionPolicy -List shows the effective value at each scope.

Microsoft's own documentation says execution policy "isn't a security boundary, it's defense in depth". The -ExecutionPolicy parameter changes the policy for the current session without touching the configured value, so a user or attacker can run a script under Bypass with no elevation. Set RemoteSigned or AllSigned through Group Policy as hygiene and to stop accidental double-clicks, then put your real effort into App Control, logging and AMSI. A process command line containing -ExecutionPolicy Bypass or -ep bypass is worth a detection rule, tuned for legitimate management agents that use it.

AMSI

The Antimalware Scan Interface lets applications pass content to the installed anti-malware engine before it runs. Microsoft lists PowerShell (scripts, interactive use and dynamic code evaluation), Windows Script Host, JavaScript and VBScript, Office VBA macros and UAC among the Windows components that integrate with it. From Windows PowerShell 5.1, script blocks are passed to AMSI on Windows 10 and later, and PowerShell 7.3 extended this to all .NET method invocations. Because the scan happens at execution time, obfuscation layers have already been removed.

  • Keep the engine registered and current: AMSI is only as good as the anti-malware product behind it. Confirm on servers as well as clients.
  • Enable application-level AMSI integration: server products such as SharePoint and Exchange can submit request content to AMSI. After ToolShell, Microsoft recommended enabling AMSI with Microsoft Defender Antivirus on SharePoint servers alongside patching and machine key rotation.
  • Alert on tampering: script content that references AMSI internals, or registry and policy changes that disable anti-malware, is a high-fidelity signal (T1685). Use tamper protection where the anti-malware product offers it.

Removing PowerShell 2.0

Windows PowerShell 2.0 was deprecated in Windows 10 version 1709 and lacks script block logging, AMSI and Constrained Language mode. Starting it alongside a modern engine is a known way to avoid those controls, tracked as T1689 Downgrade Attack, and the NSA/CISA/NCSC sheet recommends "explicitly disabling and uninstalling" it.

  • Windows 11 24H2 and Windows Server 2025: Microsoft removed PowerShell 2.0 from August 2025 (Windows 11) and with the September 2025 update KB5065426 (Windows Server 2025). Requests for version 2 now launch PowerShell 5.1.
  • Older supported builds: disable the optional features MicrosoftWindowsPowerShellV2Root and MicrosoftWindowsPowerShellV2 through your configuration tool, and check that the .NET Framework 2.0/3.5 dependency is not reintroduced by application installers.
  • Detection: alert on Windows PowerShell event 400 where the engine version is 2.0, and on command lines that request -Version 2.
  • Compatibility: Microsoft's guidance is to remove the -Version 2 requirement from scripts and update or replace applications that need it; reinstalling 2.0 is described as "a last resort temporary mitigation".

Remoting, WinRM and Just Enough Administration

PowerShell remoting uses WinRM on TCP 5985 (HTTP) and 5986 (HTTPS). Microsoft notes that WinRM encrypts all remoting traffic after initial authentication even over HTTP, using Kerberos by default when connecting to a domain computer by name. Connections by IP address or to workgroup machines fall back to NTLM, which is disabled by default unless HTTPS or TrustedHosts is configured. By default only members of the local Administrators group can connect, and on public networks the firewall rule accepts only the same subnet.

  • Restrict sources: allow 5985/5986 inbound only from privileged access workstations, jump hosts and management servers. ATT&CK lists network segmentation and separate WinRM accounts as mitigations for T1021.006.
  • Avoid CredSSP: Kerberos and NTLM remoting do not send reusable credentials to the remote host, which is why a "second hop" fails. CredSSP solves the second hop by delegating credentials to the target, exposing them to anyone who controls it. Prefer resource-based Kerberos constrained delegation or JEA with a gMSA.
  • Consider SSH remoting: PowerShell 7 supports remoting over SSH with public key authentication and, as the NSA/CISA sheet notes, without TrustedHosts.
  • Do not disable WinRM blindly: many management tools depend on it. Disable it where it has no use (M1042), and restrict it elsewhere.

JEA configuration essentials

JEA "enables delegated administration for anything managed by PowerShell". A session configuration file (.pssc) defines the endpoint and maps groups to roles through RoleDefinitions; role capability files (.psrc) list the cmdlets, functions and external commands each role may run.

  • Session type: SessionType = 'RestrictedRemoteServer' runs the endpoint in NoLanguage mode with no providers or external programs unless explicitly allowed.
  • Run-as identity: RunAsVirtualAccount = $true gives each session a temporary local administrator; narrow it with RunAsVirtualAccountGroups. Microsoft warns that on a domain controller virtual accounts belong to Domain Admins. Use GroupManagedServiceAccount only when network access is needed, accepting that all users share one identity.
  • Transcripts: set TranscriptDirectory; standard users should have no access to it.
  • Role design: avoid wildcards. Microsoft's example is that allowing *-Process also allows Start-Process, which can launch any program as administrator. Do not expose the FileSystem or Certificate providers.
  • Limits: JEA "doesn't protect against users who already have administrator privileges"; remove those users' standing admin rights or JEA is decorative.

Where identity fits

PowerShell is where many organisations' service accounts and secrets actually live: scheduled tasks running as domain accounts, scripts with passwords in clear text, and remoting sessions run as Domain Admin.

  • Scheduled and automated scripts: run them as gMSAs scoped to the task, not as personal or shared admin accounts. See the Service Account Security Guide.
  • Credentials in scripts: Microsoft no longer recommends SecureString for new development because it is easily converted to plain text. Retrieve secrets at run time from a vault, for example through the SecretManagement module, and scan script repositories for secrets. See the Secrets Management Guide.
  • Admin sessions: replace standing admin rights with JEA endpoints and just-in-time elevation. See the JIT Access Guide.

Rolling out PowerShell hardening

  1. Inventory use: enable script block and module logging plus 4688 command lines everywhere first. They are low risk and give you the data for every later step.
  2. Centralise and protect logs: forward the Operational, classic Windows PowerShell and Security logs to the SIEM; size the Operational log well above its default; add Protected Event Logging once the decryption pipeline works.
  3. Remove PowerShell 2.0: query the optional feature state, fix the few scripts that request version 2, then disable it in pilot and broad rings.
  4. Set execution policy by Group Policy: RemoteSigned or AllSigned, so the effective policy is consistent and changes are visible.
  5. Sign administrative scripts: issue a code signing certificate to the team that owns automation, and move scripts into version control.
  6. Deploy App Control in audit mode: start with a pilot ring of standard user devices, review audit events (PowerShell 7.4 and later also log restriction details) and add signers for legitimate scripts.
  7. Enforce in rings: standard users first, then IT devices, then servers. Keep a tested rollback: redeploying the audit-mode policy restores FullLanguage for affected scripts.
  8. Build JEA endpoints: start with the highest-volume admin tasks (service restarts, DNS, print, file shares) and remove the corresponding admin group memberships.

Small vs large organisations: a small team without capacity to maintain an App Control policy should still do steps 1 to 4 and enforce AMSI-backed anti-malware; those deliver most of the visibility. Large estates should treat App Control in System Lockdown as the target state for user devices and high-value servers, because it is the only option Microsoft services as a boundary.

Detection and measurement

High-value detections for SOC teams, all based on events described above: 4104 content containing AMSI or logging tamper strings; 4688 command lines with encoded commands, hidden windows or Bypass from Office applications, browsers or script hosts as parent processes; event 400 with engine version 2.0; WinRM logons to servers from non-management sources; and new JEA or session configurations being registered.

  • Logging coverage: percentage of Windows endpoints and servers sending 4104 and 4103 to the SIEM in the last 24 hours (target near 100%).
  • PowerShell 2.0 exposure: number of systems with the v2 feature still enabled (target zero).
  • Lockdown coverage: percentage of devices with App Control enforced and reporting ConstrainedLanguage for interactive sessions.
  • Script signing: percentage of production automation scripts signed and in version control.
  • Delegation: number of standing local or domain admin accounts replaced by JEA roles, and count of remaining admin group members.
  • Detection quality: mean time to triage PowerShell alerts and false positive rate per rule.

Framework mapping

FrameworkReferencePowerShell controls
MITRE ATT&CKT1059.001, T1021.006, T1027.010, T1689M1042 Disable or Remove Feature (remove unneeded PowerShell after review, restrict WinRM); M1030 and M1026 for WinRM
NIST CSF 2.0PR.PS-04, PR.PS-05, PR.AA-05, DE.CM-09Logging for continuous monitoring; preventing unauthorised software execution (App Control); least privilege (JEA); monitoring runtime environments
NIST SP 800-53 Rev. 5AU-12, CM-7(5), AC-6, SI-3Audit record generation; allow-by-exception software; least privilege; malicious code protection (AMSI)
CIS Controls v8.12.5, 2.7, 8.8Allowlist software and scripts; collect command-line audit logs including PowerShell
ISO/IEC 27001:2022 Annex A8.2, 8.7, 8.15, 8.16, 8.18Privileged access rights; protection against malware; logging; monitoring; use of privileged utility programs

Common failure patterns

  • Relying on execution policy: teams set AllSigned and consider PowerShell "locked down", while every attacker command line carries -ExecutionPolicy Bypass.
  • Logging on but not collected: 4104 is enabled locally, the Operational log rolls over in hours, and nothing reaches the SIEM before the attacker clears it.
  • CLM through AppLocker only: organisations assume a boundary where Microsoft offers defence in depth, and do not plan for bypasses.
  • PowerShell 2.0 left enabled on older builds: logging and AMSI are in place, but the downgrade path remains.
  • JEA roles with wildcards or full admins still in place: the endpoint looks restricted, but Start-Process or an untouched Domain Admins membership undoes it.
  • Secrets in scripts and transcripts: passwords embedded for convenience end up in logs readable by a wide audience.

Practitioner checklist

  • Enable script block logging, module logging and transcription for Windows PowerShell and PowerShell 7.
  • Enable Audit Process Creation with command lines and forward 4688, 4103, 4104 and event 400 to the SIEM.
  • Protect transcripts and logs: write-only shares, restricted readers, Protected Event Logging where feasible.
  • Disable or confirm removal of PowerShell 2.0 on every Windows build.
  • Set execution policy by Group Policy, and stop treating it as a security control.
  • Deploy App Control for Business, audit first, then enforce System Lockdown so CLM applies to untrusted code.
  • Keep AMSI-integrated anti-malware active with tamper protection on clients and servers.
  • Restrict WinRM to management sources and avoid CredSSP.
  • Replace standing admin rights with JEA endpoints using explicit, wildcard-free role capabilities.
  • Remove credentials from scripts; use gMSAs and a secrets vault.

Standards and references

Related NHI Mgmt Group resources: Service Account Security Guide · Secrets Management Guide · Privileged Access Management Guide · Active Directory and Entra ID Hardening Guide · JIT Access Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Written and reviewed by Lalit Choda, NHI Mgmt Group. Last updated 16 October 2026.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org