I have noticed that the PowerShell console sometimes takes a long time to open. This issue occurs on different computers. In some cases, it can take several minutes for PowerShell to load. This affects the opening of the command shell itself (powershell.exe or pwsh.exe) and the execution time of logon PowerShell scripts launched via GPO or scripts in the scheduled tasks. This post explains how to identify potential reasons for slow PowerShell startup and reduce loading time.
Here are some common reasons why PowerShell may take a long time to load:
- User-defined functions in PowerShell profile files are loaded each time the PowerShell.exe process starts
- A significant number of PS modules are installed and load automatically
- The PSReadLine module loads a huge file containing the history of PowerShell commands
- Significant slowdowns in PowerShell and module loading are caused by conflicts or corruption in .NET Framework components.
The Measure-Command cmdlet can be used to measure the load time of a PowerShell process. Use this command to check the load time of PowerShell in normal mode.:
powershell -noprofile -ExecutionPolicy Bypass ( Measure-Command { powershell "Write-Host 1" } ).TotalSeconds
The command will display how long it took for the PowerShell process to load and execute a simple command.
When PowerShell starts, it may report the time it took to load.
Loading personal and system profiles took XXX ms
As you can see in this example, the PowerShell process took 12 seconds to load. That’s quite a lot. This message is automatically displayed if the load time of PowerShell profiles exceeds 500 ms.
This suggests that PowerShell profile files are executing slow code each time the process starts.
Compare the startup speed of the PowerShell process when profiles are not loaded. To do this, run the shell process with the -noprofile option:
powershell.exe -noprofile
or to run the PowerShell Core:
pwsh.exe -noProfile
As you can see, PowerShell started much faster when profiles weren’t loaded. PowerShell profiles are PS1 files that are used to configure the user environment. They also allow the user to run specific commands and load custom functions or modules.
By default, PowerShell profile files are not used (no profile files are created). The following command displays a list of all the profile files that will load when the PowerShell process starts.
$profile | select *
Check the contents of all profile files for the user manually, or use the following commands to display them:
Get-Content $PROFILE.AllUsersAllHosts
Get-Content $PROFILE.AllUsersCurrentHost
Get-Content $PROFILE.CurrentUserAllHosts
Get-Content $PROFILE.CurrentUserCurrentHost
In my case, some commands are added to the Microsoft.PowerShell_profile.ps1 user file. Review the profile file to ensure that all the commands and functions are necessary. If possible, remove unnecessary commands and optimize the code.
A large number of modules installed could be another possible reason for the slow startup of PowerShell. PowerShell automatically loads modules from the following folders:
C:\Users\%username%\Documents\WindowsPowerShell\Modules C:\Program Files\WindowsPowerShell\Modules C:\Windows\system32\WindowsPowerShell\v1.0\Modules
Their list can be displayed as follows:
$Env:PSModulePath -split ';'
List the PS modules that have been loaded into the session.
Get-Module -ListAvailable
List the installed third-party modules.
Get-InstalledModule
Check the list to see if you need all of the modules. Uninstall any unused PowerShell modules.:
Remove-Module -Name BurntToast
Uninstall-Module -Name BurntToast -AllVersions -Force
To analyze the loading time of each module, use the following command:
Measure-Command { Import-Module ModuleName -Force }
Or check the load time of all PS modules in bulk:
$modules = Get-InstalledModule| Select-Object -ExpandProperty Name -Unique
foreach ($mod in $modules) {
$time = Measure-Command { Import-Module $mod -Force }
[PSCustomObject]@{
Module = $mod
LoadTimeSec = $time.Totalseconds
}
}
See which modules take the longest to load.
To prevent all PS modules from loading automatically, add the following line to the profile file:
$PSModuleAutoLoadingPreference = 'None'
In this case, you can manually load the required module using the following command:
Import-Module [ModuleName]
Import-Module Microsoft.PowerShell.Utility
Import-Module Microsoft.PowerShell.Management
There is also a known issue when loading the VMware Infrastructure Management PowerShell module, also known as VMware PowerCLI. The module may take several minutes to load on a computer that is not connected to the Internet. The problem is that, when loading, the module tries to check an external list of revoked certificates (CRL, Certificate Revocation List). Due to a lack of internet connectivity, this process timed out. The solution is to disable CRL checking in Windows. You can disable CRL checking via the registry:
reg add HKLM\SYSTEM\CurrentControlSet\Services\SstpSvc\Parameters /v NoCertRevocationCheck /t REG_DWORD /d 0x00000001 /f
Or in the Internet Properties applet: inetcpl.cpl
-> Advanced –> untick the option Check for publisher (server) certificate revocation.
The reason PowerShell may take a long time to start is often the antivirus software that is installed on the computer. You can check which external DLLs and modules are loaded when the PowerShell process starts.
- Open
cmd.exe
and run the command:powershell.exe -c "Write-Host $PID;Start-Sleep -s 60"
- This command returns the process ID (PID) of the running PowerShell.exe process. Copy the PID and paste it into the following command in a new PowerShell session:
Get-Process | where {$_.Id -eq <YOUR_PID>} | select -ExpandProperty modules
- You will receive a list of the DLLs loaded when the PowerShell process starts. Check if your antivirus library is listed. If so, try adding the pwsh.exe and powershell.exe processes to your antivirus exceptions.
For example, you can use the command below to add exclusions to the built-in Windows Defender antivirus:
Add-MpPreference -ExclusionProcess "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe","C:\Program Files\PowerShell\7\pwsh.exe"
If you suspect that PowerShell’s slow startup is caused by slow .NET Framework library operation, you can compile all used assemblies into native machine code using the ngen.exe
tool (Native Image Generator). This will significantly improve the performance of .NET applications, including PowerShell:
$env:PATH = [Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()
[AppDomain]::CurrentDomain.GetAssemblies() | ForEach-Object {
$path = $_.Location
if ($path) {
$name = Split-Path $path -Leaf
Write-Host -ForegroundColor Yellow "`r`nRunning ngen.exe on '$name'"
ngen.exe install $path /nologo
}
}
The classic Process Monitor tool can also be used to analyze PowerShell startup time. (https://technet.microsoft.com/en-us/sysinternals/processmonitor.aspx). Run ProcMon64.exe
, enable the filter on the PowerShell.exe process, open the PS shell, and identify the operations that caused the greatest delay.
In my example, reading the PowerShell command history file maintained by the PSReadline module took about 30 seconds. ( C:\Users\username\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
). The problem was caused by the ConsoleHost_history.txt file exceeding 2 GB in size. Cleaning the history file significantly reduced PowerShell’s startup time.