Today's topic is one of those things that I don't know why I've never addressed before. Well, I have for myself in a manual process. Hopefully you'll find it useful.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
As you probably know, PowerShell uses a set of profile scripts. These scripts have hard-coded paths and PowerShell runs them in order from broadest to narrowest.
- All Users All Hosts
- All Users Current Host
- Current User All Hosts
- Current User Current Host
The host refers to the PowerShell host that you see when you look at $host. The $profile variable shows you the value for current user current host script. But you can easily see all the values.
Here's the "issue" that often arises. Someone will mention that PowerShell, and this includes PowerShell Core, takes too long to load. In fact, PowerShell now shows you how long it took to load. Almost always, the issue is something profile related. Sometimes a command is taking too long to run, or maybe the profile needs a little cleanup. I know my PS7 load times were high until I cleaned up a few items and re-structured some of the commands.
To make this easier, I put together a simple script that you can run in Windows PowerShell, or PowerShell (even cross-platform) that will run your profile scripts and report how long they take to complete.
#requires -version 5.1
#test profile script performance
<#
C:\scripts\Test-ProfilePerf.ps1
C:\scripts\Test-ProfilePerf.ps1 -outvariable p | measure-object -Property TimeMS -sum
$p
#>
[cmdletbinding()]
Param()
#this is the scriptblock of commands to run in a new Powershell/pwsh session
$sb = {
$profiles = [ordered]@{
AllUsersAllHosts = $profile.AllUsersAllHosts
AllUsersCurrentHost = $profile.AllUsersCurrentHost
CurrentUserAllHosts = $profile.CurrentUserAllHosts
CurrentUserCurrentHost = $profile.CurrentUserCurrentHost
}
#only need to get these values once
$psver = $PSVersionTable.PSVersion
$computer = [System.Environment]::MachineName
$user = "$([System.Environment]::UserDomainName)\$([system.Environment]::userName)"
foreach ($prof in $profiles.GetEnumerator()) {
If (Test-Path $prof.value) {
# Write-Host "Measuring script for $($prof.key)" -ForegroundColor cyan
$m = Measure-Command { . $prof.value }
#create a result
[pscustomobject]@{
Computername = $computer
Username = $user
PSVersion = $psver
Profile = $prof.key
Path = $prof.value
TimeMS = $m.totalMilliseconds
}
} #if test path
} #foreach profile
} #scriptblock
#use the detected PowerShell version
if ($PSEdition -eq 'Core') {
#Windows uses pwsh.exe and non-Windows uses pwsh.
$cmd = (Get-Command pwsh).source
$result = &$cmd -nologo -noprofile -command $sb
}
else {
$result = powershell.exe -nologo -noprofile -command $sb
}
#use $result as the output of this command
$result
The script file runs a scriptblock in a new PowerShell or pwsh session with no profiles. The scriptblock runs each profile script in order and measures how long each detected profile script runs. The performance times should be pretty close.
If you'd like to see a total you could run a command like:
C:\scripts\Test-ProfilePerf.ps1 -outvariable p | measure-object -Property TimeMS -sum
The script is configured with cmdletbinding so you can use a common parameter like OutVariable.
If you test, and one of the scripts seems to take a long time, wait a minute and repeat the test. If you consistently get a value you don't like, at least now you have something to investigate.
Caveats
This script makes an assumption that your profile scripts aren't taking drastic steps like creating new Active Directory users or deleting files. I'm assuming you are using a profile script to define things like variables and PSDrives, and import modules or define functions.
This version of the script also won't test profile scripts under hosts such as VS Code or the PowerShell ISE.
I hope you find this helpful.