Script for massive update does not finish - powershell

I have written a simple script to massively download and install Windows Updates onto fresh OS installations. For most of them the script well accomplish the installation. Last but not the least, the most recent cumulative update is downloaded and installation started. But the installation never ends (ie the progress bar does not complete) and the script remains hanged up. How to force the finalization?
Edit: the same task, from the Windows Update GUI, works
Here there is my script:
# elevated script execution with admin privileges
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$testadmin = $currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
if ($testadmin -eq $false) {
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))
exit $LASTEXITCODE
}
# setting script execution policy
$ErrorActionPreference= 'SilentlyContinue'
Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy Bypass -Force
$ErrorActionPreference= 'Inquire'
$ErrorActionPreference= 'Stop'
try {
Import-Module PSWindowsUpdate
} catch {
Install-Module PSWindowsUpdate -Confirm:$False -Force
Import-Module PSWindowsUpdate
}
$ErrorActionPreference= 'Inquire'
# list of available updates
# Get-Windowsupdate
# install the updates
Install-WindowsUpdate -AcceptAll -Install -Confirm:$False

Related

Trying to check for a powershell module, install if it doesn't exist, then rerun the same script

So I am trying to write a script that will check if the user has a certain module installed, and if it doesn't, to install it, then rerun itself.
When I try to run this, the script just keeps rerunning and trying to install. I have to use a setup.exe and I have it waiting for the window to cl
$mypath = $MyInvocation.MyCommand.Path
$Path = Split-Path $mypath -Parent
$Location = "$Path" + "\setup.exe"
if (Get-Module -ListAvailable -Name ActiveRolesManagementShell) {
Write-Host "QAD Is installed"
pause
}
else{
Write-Host "Installing QAD"
Start-Process $Location
Wait-Process -Name "setup"
Pause
$CommandLine = "-File `"" + $MyInvocation.MyCommand.Path + "`" " + $MyInvocation.UnboundArguments
Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList $CommandLine
Pause
}
Normally, modules are installed using Install-Module. Then you might need to tell PowerShell about it for the current session to be able to use it.
if (-not(Get-Module -ListAvailable -Name ActiveRolesManagementShell)) {
Install-Module ActiveRolesManagementShell
Import-Module ActiveRolesManagementShell # Might not be needed
}
# Add code that uses ActiveRolesManagementShell module
But with regards to ActiveRolesManagementShell, what version are you using?
The old Quest module was probably meant to work on PowerShell v2. And I can't find a current version...

Checking for a powershell version

Hi I currently have a very basic PowerShell script that runs when a computer is installed with Windows (any version). This script is below
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
Write-Output "Installing OS Updater Modules Please Wait...."
Write-Output " "
Install-PackageProvider -Name NuGet -Force
Install-Module -Name PSWindowsUpdate -Force
Write-Output " "
Write-Output "Installing Updates...."
Write-Output " "
Install-WindowsUpdate -AcceptAll -Install -IgnoreReboot | Out-File "c:\temp\WindowsUpdate.log" -force
As I said very simple code this will install a few modules then update windows with any updates, however, it only works with PowerShell 5 and above is there a way I can check what version is installed then carry on with the script if it is PowerShell 4 and below stop executing the script.
I know it's possible to install Windows Management Framework 5.1 into the install.wim/esd which will get around this but for people who don't have this, I would like the script to stop and not have any errors.
u can use cmdlet named "get-host", it works in ps version >=2.0
if ((get-host).version.major -le 4)
{
#code
}
else
{
#code
}
or if u need exact match , u can use switch construction
switch ((get-host).version.major)
{
4 {#installcode4}
5 {#installcode5}
7 {#installcode7}
}

During Installation process of a software using Start-Process "msiexec.exe" ... -wait, how to fix error and continue the installation process?

For the installation of the "Self-Hosted Integration runtime software, using PowerShell command
Start-Process "msiexec.exe" "/i $path /quiet /passive" -Wait, error comes up for "DIAHostService" not having "LocalSystem" access.
I have the code to change the login to the LocalSystem ("sc.exe config "ServiceName" obj="LocalSystem") But How can I do this during the Installation Process, how do I catch the error and do the required change and continue for the installation automatically?
Code:-
param([string]$path, [string]$authKey)
function Install-Gateway([string] $gwPath)
{
# uninstall any existing gateway
UnInstall-Gateway
Write-Host "Start Gateway installation"
Start-Process "msiexec.exe" "/i $path /quiet /passive" -Wait
Start-Sleep -Seconds 30
Write-Host "Succeed to install gateway"
}
Since I am in no position to replicate what you are doing, take this generic approach as an example.
param([string]$path, [string]$authKey)
function Install-Gateway([string] $gwPath)
{
Write-Warning -Message 'Starting Gateway uninstallation'
UnInstall-Gateway
Try
{
'Starting Gateway installation'
Start-Process -FilePath 'msiexec.exe' -ArgumentList "/i $path /quiet /passive" -Wait -ErrorAction Stop
Start-Sleep -Seconds 30
Write-Verbose -Message 'Gateway installation successful.' -Verbose
}
Catch
{
Write-Warning -Message 'AN error occurred'
$PSItem.Exception.Message
}
}
If you need to run this with a different user, then you need to add the RunAs as part of the Start-Process error logic. Yet using RunAs means starting a new Powershell instance. It will not run in the same process.
References
• PowerShell: Running Executables
Direct - Using the environment path or local folder
Invoke-Expression (IEX)
Invoke-Command (ICM)
Invoke-Item (II)
The Call Operator &
cmd /c - Using the old cmd shell
Start-Process (start/saps)
[Diagnostics.Process] Start()
WMI Win32_Process Create() Method
Stop-Parsing Symbol --%
see also:
'powershell start-process msiexec'
Powershell: Installing MSI files
'PowerShell start-process msiexec' try/catch
about_Try_Catch_Finally - PowerShell | Microsoft Docs
'PowerShell error preference'
Handling Errors the PowerShell Way | Scripting Blog
-ErrorAction and -ErrorVariable
You could also look at using PowerShell jobs for this use case.
start-process 'about PowerShell jobs'

How to run command on list and have PsExec or Powershell automatically jump to the next item on this list

I have a list of computers in 'computers.txt'.
I am trying to run an .exe on each remote computer name in a list. I have to execute a .ps1 script on each computer that installs the .exe properly. In PsExec, I have to press enter after a minute or 2 in between each computer name. This will go through the list of remote computers and run an .exe that is on each computer.
In PowerShell, only the first computer runs the .exe and the rest don't do anything.
Is there any way to go through the list without having to press Enter between computer name when the script is running? I want it all to run automatically.
Here is what I am using in PsExec.
psexec -s #C:\App\computers.txt cmd /c "Powershell Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass && PowerShell -noninteractive -file "C:\SpeedInstall.ps1""
Here is what I am trying in PowerShell
$a = Get-Content "C:\App\computers.txt"
foreach($line in $a) {
psexec -s \\$line cmd /c "Powershell Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass && PowerShell -noninteractive -file C:\SpeedInstall.ps1"
}
You can use the /d switch with PSExec to make it not wait for the previous command to finish before moving on to the next one. There is a tradeoff in that you don't get to see any error messages that the command may generate, but it allows you to get through your list much much more quickly. Your command will look like this:
$a = Get-Content "C:\App\computers.txt"
foreach($line in $a) {
psexec -s -d \\$line cmd /c "Powershell Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass && PowerShell -noninteractive -file C:\SpeedInstall.ps1"
}
Why do you even want to use psexec? How about PSRemoting?
$Computers = Get-Content -Path C:\computers.txt
foreach ($Computer in $Computers) {
Copy-Item -Path C:\install.exe -Destination \\$Computer\c$\Windows\Temp\install.exe
}
$Script =
#"
# Write down your installation script here
& C:\install.exe /silent
Set-ItemProperty -Path HKLM:\SOFTWARE\Install -Name Setting -Value 1 -Type DWord
"#
$ScriptBlock = [Scriptblock]::Create($Script)
$PSSession = New-PSSession -ComputerName $Computers -SessionOption (New-PSSessionOption -NoMachineProfile)
Invoke-Command -Session $PSSession -ScriptBlock $ScriptBlock

Creating a simple PowerShell based installer with elevated privileges

I am trying to make a script that performs a bunch of installation operations, including executing other files, such as .reg files to update the registry. I created a batch file to kick it off and have code in the powershell script to self elevate to run as admin. The problem is that once run as admin, the working path is C:\Windows\system32, after which the script cannot file to other (relative) files it needs to run.
Install.bat:
powershell InstallSteps.ps1
InstallSteps.ps1:
param([switch]$Elevated)
function Test-Admin {
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if ((Test-Admin) -eq $false) {
if ($elevated)
{
# tried to elevate, did not work, aborting
}
else {
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))
}
exit
}
# THIS SHOWS THE PATH IS "C:\WINDOWS\SYSTEM32"
Write-Host $pwd
# THIS FAILS, BECAUSE IT CAN'T FIND THE FILE
reg IMPORT EnableAllTrustedApps.reg
I have not found a way to pass in the relative path to these to make into the working path. It seems the "Start-Process" method of elevating loses all context of where the script was originally located.
I recommend always using this function to call external things. It's more reliable than relative paths.
function Get-Script-Directory
{
$scriptInvocation = (Get-Variable MyInvocation -Scope 1).Value
return Split-Path $scriptInvocation.MyCommand.Path
}
e.g.
$script_home = Get-Script-Directory
reg.exe IMPORT "$script_home\EnableAllTrustedApps.reg"
Note - in 3.0 or above you can use this new built-in variable instead of the function.
$PSScriptRoot
You might also want to try specifying the -WorkingDirectory parameter for Start-Process e.g. Start-Process -FilePath powershell.exe -verb RunAs -WorkingDirectory $PWD.Path