I have slow PowerShell console startup times (always more than 5 second wait) and was hoping for advice on troubleshooting steps to find out where the bottlenecks might be?
I have read that for running scripts, -NoProfile is important to prevent Modules etc loading, but how, in general, should we approach finding out what is slowing things down? I don't have many Modules installed and I know that since PowerShell 3.0, Modules are just referenced at startup and not fully loaded (a Module is only fully loaded when a function from a given Module is invoked) so I just can't understand why it takes 5+ seconds to start a bare console (my $profile also is empty).
Any advice on various steps that I can look at to debug the console startup process would be appreciated? Also, are there maybe some Microsoft or third-party tools that exist to debug the various steps in the console startup process to look for bottlenecks?
When PowerShell starts to become slow at startup, an update of the .NET framework might be the cause.
To speed up again, use ngen.exe on PowerShell's assemblies.
It generate native images for an assembly and its dependencies and install them in the Native Images Cache.
Run this as Administrator
$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
}
}
Hope that helps
Step 1: Stop using PowerShell.
Now, seriously, something that needs ~13 seconds (YMMV) on an quad-core i7 cpu to launch off an ssd drive is an abomination of software architecture.
But yes, I hear you, "no viable alternative" etc...
... but if forced, bribed or blackmailed to still use it, check if your Windows has DNS cache service enabled.
For me, with DNS cache disabled and powershell executable firewalled, the built-in 5.1.19041.906 version starts quickly, but the new pwsh 7.1.4 would take around 13 seconds to get responsive to keyboard input under the same circumstances. It's so desperate to call home that it would just synchronously wait for some network timeout while blocking all user input, as if threads were a thing for the weak.
My resolution was to stick with the olden powershell 5.
My work computer stored the main profile on a remote server. Another minor problem was that it imported duplicate modules from 4 different profile.ps1 files.
Use the following commands to see where your profiles and modules are stored. Delete the unnecessary profile.ps1 and move all your modules into one directory.
echo $env:PSModulePath
$profile | select *
My loading time was reduced from 21000ms to 1300ms.
Found this solution when I googled having the same problem, but in 2022. Unfortunately this did not fix my issue.
Our systems have a group policy security requirement to "Turn on PowerShell Transcription". The policy requires that we specify "the Transcript output directory to point to a Central Log Server or another secure location". The server name changed and no one updated the policy. As soon as I updated the GPO with the new location, PowerShell opened instantly again.
Press Windows+R
Type %temp% and hit enter
C+A & SHIFT+DEL
That should do it
Related
I have been picking at this issue for a few weeks now with no resolve or solution found on the webs. When I run a Dell BIOS update with /s and /f during a new Windows 10 21H1 build task sequence, the update runs successfully with the BIOS update log showing error 2 that a reboot is needed to perform the BIOS update upon reboot. So the next step in the task sequence I perform a reboot but the BIOS never does the update, it just boots into Windows. I tried this from command line, PowerShell and as an application with the reboot box checked. All the ways I run this the log says ready to update on reboot but never does. I can get the update to work if I manually perform the reboot by using the mouse before MDT reboots it. This actually performs the update at the reboot as it should! However this of course creates a dirty environment and MDT is grumpy.
This happens on all different Dell builds I try that are only one or two steps newer. We currently use PDQ to run the updates. When I call the install from there, this too works fine. We want to move away from PDQ to a free solution such as just straight from MDT. I found many different ways people have performed this via task sequence and no mention of this hiccup. What I seem to be running into is MDT is removing whatever the BIOS is putting into the boot sequence so it never gets performed. I've tried different credentials, Dell's flash64w.exe and too much to list. Things seem to work until reboot. I'm stumped.
Sample of simple working PowerShell:
# Get model of system to be updated
$Model = (gwmi Win32_ComputerSystem).Model
Write-Host "Model Found: $($Model)"
# Get root folder where BIOS for model is stored
$BIOSRoot = "Z:\Applications\BIOSUpdates\Dell\$Model"
Write-Host "BIOSRoot: $($BIOSRoot)"
# Get path with BIOS executable and list of arguments
$BIOSFile = Get-Childitem -Path "$BIOSRoot" -Include *.exe -Recurse -ErrorAction SilentlyContinue
#Write-Host "BIOSFile: $($BIOSFile)"
$ARGS = #('/s', '/f')
Write-Host "BIOSFile and arguments: $($BIOSFile) $($ARGS)"
#Start BIOS Update with completed path
Start-Process "$BIOSFile" -ArgumentList "$ARGS" -Wait
Is anyone else having this show stopping issue?
So after much testing, the answer is to run the Start-Process at the end of my code twice. Why? I have no idea. I accidentally had it looping and it worked on the second loop. It just needed to run twice. I thought maybe it just needed more time. I put a sleep at end of the code, but time is not what it wanted. Very bizarre.
So I need to uninstall some program and clean all the leftovers (folders + registry)
and then install the new app with all it dependencies - all this on 150 workstation
I've created a Powershell script and it's doing all the mention above without any issues,
the issue begins when I need to run the installation, it's located on a network share, here is the syntax:
Set-Location "\\imapps\FileSiteClientInstall\Setup Imanage\DRIVE -iManage Drive for Windows 10.2.6.48"
Start-Process iManageDrive.exe /s
The issue is that i have 6 lines like this one (each installation file is in a different folder),
and when I run this from the network I'am getting this UAC
is there a way to disable it, and make my script to run fully automatically?
Thanks alot for your help :)
So, not really UAC for what most people think of UAC. This is warning about running an executable from an untrusted location (not on a local drive). You'd get the same thing with a script that you download from the internet. What you can do is use the Unblock-File cmdlet to get past that warning like this:
Set-Location "\\imapps\FileSiteClientInstall\Setup Imanage\DRIVE -iManage Drive for Windows 10.2.6.48"
Unblock-File .\iManageDrive.exe
Start-Process iManageDrive.exe /s
This should mark the file as safe, and avoid the prompt you've been getting.
I have slow PowerShell console startup times (always more than 5 second wait) and was hoping for advice on troubleshooting steps to find out where the bottlenecks might be?
I have read that for running scripts, -NoProfile is important to prevent Modules etc loading, but how, in general, should we approach finding out what is slowing things down? I don't have many Modules installed and I know that since PowerShell 3.0, Modules are just referenced at startup and not fully loaded (a Module is only fully loaded when a function from a given Module is invoked) so I just can't understand why it takes 5+ seconds to start a bare console (my $profile also is empty).
Any advice on various steps that I can look at to debug the console startup process would be appreciated? Also, are there maybe some Microsoft or third-party tools that exist to debug the various steps in the console startup process to look for bottlenecks?
When PowerShell starts to become slow at startup, an update of the .NET framework might be the cause.
To speed up again, use ngen.exe on PowerShell's assemblies.
It generate native images for an assembly and its dependencies and install them in the Native Images Cache.
Run this as Administrator
$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
}
}
Hope that helps
Step 1: Stop using PowerShell.
Now, seriously, something that needs ~13 seconds (YMMV) on an quad-core i7 cpu to launch off an ssd drive is an abomination of software architecture.
But yes, I hear you, "no viable alternative" etc...
... but if forced, bribed or blackmailed to still use it, check if your Windows has DNS cache service enabled.
For me, with DNS cache disabled and powershell executable firewalled, the built-in 5.1.19041.906 version starts quickly, but the new pwsh 7.1.4 would take around 13 seconds to get responsive to keyboard input under the same circumstances. It's so desperate to call home that it would just synchronously wait for some network timeout while blocking all user input, as if threads were a thing for the weak.
My resolution was to stick with the olden powershell 5.
My work computer stored the main profile on a remote server. Another minor problem was that it imported duplicate modules from 4 different profile.ps1 files.
Use the following commands to see where your profiles and modules are stored. Delete the unnecessary profile.ps1 and move all your modules into one directory.
echo $env:PSModulePath
$profile | select *
My loading time was reduced from 21000ms to 1300ms.
Found this solution when I googled having the same problem, but in 2022. Unfortunately this did not fix my issue.
Our systems have a group policy security requirement to "Turn on PowerShell Transcription". The policy requires that we specify "the Transcript output directory to point to a Central Log Server or another secure location". The server name changed and no one updated the policy. As soon as I updated the GPO with the new location, PowerShell opened instantly again.
Press Windows+R
Type %temp% and hit enter
C+A & SHIFT+DEL
That should do it
I'm using InstallShield 2013 to make a Basic MSI installer for an application that requires Windows Platform Update KB2670838.
For .NET frameworks and other requirements, I select them in InstallShield in the Redistributables section. KB2670838 is not available.
If I download KB2670838 from Microsoft I get a .msu file. Can that be included in the installer somehow so that it automatically installs if needed? If not, is there a way to stop the install and tell the user that "KB2670838 is required but not installed. Get it here..."?
In InstallShield, you should typically deliver this sort of update as a prerequisite (Tools > Prerequisite Editor), or as a package included in a Suite (reference [SystemFolder]wusa.exe to install an .msu file). In both cases this keeps the redistributable installation logically separate from your package's installation, while providing your users a single installer experience.
Glytzhkof mentions several really good points about how to determine whether the update has been installed. You will want to incorporate these into your conditions (on the prerequisite or suite package), and also into detecting the update or lack thereof in your .msi package so it can abort if the required update has not been installed by the time the .msi is launched.
The Add/Remove programs list in the registry could help you get a rough idea of what's installed:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
It seems this doesn't provide a full list of what is installed though: http://social.technet.microsoft.com/Forums/windows/en-US/d913471a-d7fb-448d-869b-da9025dcc943/where-does-addremove-programs-get-its-information-from-in-the-registry?forum=w7itprogeneral
Another way may be to use the file information from the knowledge base article:
http://support.microsoft.com/kb/2670838/en (For More Information : File Information) and use WIX / MSI's AppSearch / LaunchCondition feature. That should do the trick, though I find the syntax a bit counterintuitive.
Another approach is to write a custom action and combine these two sources (add /remove entry and file info). Such a custom action will make no changes to the system, and is hence less problematic than other custom actions that cause rollback-problems. I find it easier to test and maintain a custom action in case there are further prerequisites that are needed at some point. This is a matter of taste though. I just find it easier to run a prerequisite script against a selection of files to test that it identifies them correctly and run through as expected than to keep running the MSI file for every test.
Here is a similar question with some pointers from superuser.com:
https://superuser.com/questions/521175/determine-if-windows-hotfix-has-been-applied
And another link to serverfault.com (system administration site). Nice approach using PowerShell which can certainly be migrated to a custom action:
https://serverfault.com/questions/312778/determine-if-user-has-hotfix-981889-installed
Even more serverfault.com stuff involving update.exe, WMI and a Powershell script to view all installed hotfixes:
https://serverfault.com/questions/263847/how-can-i-query-my-system-via-command-line-to-see-if-a-kb-patch-is-installed . Recommended read. Microsoft: http://technet.microsoft.com/en-us/library/hh849836.aspx
PSInfo appears to be able to show installed hotfixes: http://technet.microsoft.com/en-us/sysinternals/bb897550
#Glytzhkof Good point. So how do I get InstallShield to abort and give the user a nice message so they know what to do? – shoelzer 1 hour ago
I will just add a new answer then - too long to write in a comment.
Locate the file details you need to scan for under "For More
Information : File Information" in this kdb article:
http://support.microsoft.com/kb/2670838/en
Select a few files to scan for and add as file searches in Installshield (see below screenshot). You specify a property for each file (FILE1FOUND, FILE2FOUND, FILE3FOUND, etc...), and if the search matches the file details (version, size, date, etc...) the property is set to the full path of the file. Otherwise the property is undefined or set to a default value (screenshot shows predefined search, and not file search, but you get the idea).
Finally you add LaunchCondition entries for each file to ensure that all files you have selected to check are the correct version or higher. I guess this is in Prerequisites or similar - I can't recall. Open the compiled MSI and check that it looks like the LaunchConditon table.
For the record: (not part of above suggestion)
Personally I am in favor of coding a single script for complex logic like this to ensure the logic can be inspected as a whole and crucially tested as a whole outside the MSI file. It is also good to add comments to such code to explain what the script is checking, and why (helps corporate deployment). A script can be run through dozens of tests against the machine directly without recompiling the MSI. This can save a lot of time if the logic is complex. If you write a compiled dll you can show a message box and attach the visual studio debugger to the msiexec.exe process (client or server depending on what context your custom action is running in) and step-through the code whilst embedded in the MSI, but this seems out of scope for your scenario. Just want to mention it for other people who might read this. Also check Stefan Kruger's installsite.com for more information on complex setup debugging like this.
It is important to note that scripting is never generally recommended for scenarios where the script makes changes to the system - if there is a built-in MSI way to achieve the same result. The reason for this is that a script that makes changes to a machine will need a separate rollback-operation to be specified for it for the MSI to follow best practice. This can be a spectacular amount of work and complexity to get right. The above script would only check system conditions, so there is no need for rollback support.
Let me try and add a reference style answer since my other answer is a bit organic to say the least at this point - I will leave it in since it contains an MSI discussion. See MSI recommendation in the middle section below:
WMI:
wmic qfe where "HotfixID = 'KB973687'"
PowerShell: (just get-hotfix for full list)
get-hotfix | findstr "981889"
SystemInfo (remove arguments for list format):
systeminfo /fo csv
PSInfo (seems to not list everything on all machines, and may not run silently properly):
PSinfo -h
Registry (apparently not complete list of hotfixes):
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
For MSI custom action use, I would actually use a custom action that inspects file versions as explained in my other answer. Very reliable, and takes into account that the hotfix may be deprecated whilst the files are still up to date.
References:
Recommended: https://serverfault.com/questions/263847/how-can-i-query-my-system-via-command-line-to-see-if-a-kb-patch-is-installed
How do I get a list of installed updates and hotfixes?
SystemInfo: https://serverfault.com/questions/334552/how-to-check-that-a-known-windows-vulnerability-has-been-patched
http://windowsitpro.com/scripting/get-hotfix-information-quickly-wmic
https://serverfault.com/questions/69467/list-all-hotfixes-applied-to-windows-server
wmic in general: http://technet.microsoft.com/en-us/library/bb742610.aspx
Recommended: http://www.dedoimedo.com/computers/windows-wmic.html
http://www.techsupportalert.com/content/quick-and-easy-way-list-all-windows-updates-installed-your-system.htm
http://pario.no/2011/06/19/list-installed-windows-updates-using-wmic/
Had the same issue and solved it by adding a prerequisite of a PowerShell script and a batch file to execute it.
The pre.ps1 file looks something like this:
function TestConnection
{
Test-Connection -ComputerName "8.8.8.8" -Quiet
}
get-hotfix -id KB2670838
if(!$?){
#SourceURI = "https://download.microsoft.com/download/1/4/9/14936FE9-4D16-4019-A093-5E00182609EB/Windows6.1-KB2670838-x64.msu";
#$FileName = $SourceURI .Split('/')[-1]
#$BinPath = Join-Path $DownloadPath -ChildPath $FileName
Invoke-Webrequest -Uri $SourceURI -OutFile $BinPath
#Start-Process -FilePath $BinPath -ArgumentList "/q /norestart" -Wait -NoNewWindow
}
the pre.cmd file looks something like this:
#echo off
::set PS_FILE=%~dp0Prerequisite.ps1
set PS_FILE=%~dpn0.ps1
set PS_EXEC_PATH=%SystemRoot%\sysnative\WindowsPowerShell\v1.0\
set PS_EXEC_PATH=%SystemRoot%\System32\WindowsPowerShell\v1.0\
::set PS_EXEC_PATH=%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\
set PS_EXEC_PATH=
set PS_EXEC=%PS_EXEC_PATH%powershell.exe
echo %PS_EXEC%
echo %PS_FILE%
::%PS_EXEC% -file %PS_FILE% set-executionpolicy remotesigned
::%PS_EXEC% -NoProfile -ExecutionPolicy Bypass -Command "& '%PS_FILE%'"
::This is with admin rights
%PS_EXEC% -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%PS_FILE%""' -Verb RunAs}"
::pause
I've written a powershell script that I've turned into a scheduled task. So far, so good. The issue I have is that every time Powershell is started, it causes my floppy drive to seek; this means every five minutes (on the task schedule), I get a little "grind" from the floppy drive.
How do I disable this behavior?
C:\fa>powershell -noprofile
Windows PowerShell
Copyright (C) 2006 Microsoft Corporation. All rights reserved.
PS C:\fa> exit
C:\fa>
When I do this test, I get a seek when I start and when I exit. I assume the error would go away if I had a floppy disk to put in the drive.
As a poor work-around, I disabled the floppy drive in BIOS. I've removed the red herring about the drive not being available; this was because of another issue.
The suggested way to find what, in my profile, points to A:, turned up nothing.
PS C:\fa2> dir Env:\ | ?{$_.Value -like "A:\*"}
PS C:\fa2>
this was, of course, after I re-enabled the floppy drive in BIOS.
I'd say its the drive enumeration that happens at runtime. Powershell builds a list of drives and creates aliases for each one so you can type "A:" and have it work like cmd.exe. I think this because if you start Powershell on a clustered server, that does not own a shared drive (is owned by the other node), it issues an error saying that the drive is not accessible. Yet, everything is normal in the shell.
That being said I've never seen this behaviour (floppy drive seek on load) on any of the hundreds of machines I administer.
It could be something in your profile script(s). I've not experienced PowerShell causing any floppy drive activity...
If you start PowerShell.exe with the -noprofile option, it will not run the profile scripts (so long as your script does not depend on anything from your profile).
I think it may be a permissions problem. Can you access the drive outside of PowerShell?
I found a couple references to this problem here and here.
Check to make sure nothing in your Environment is pointing to the drive.
dir Env:\ | ?{$_.Value -like "A:\*"}
Stop using floppy disks?
/ducks