I have a power shell script to download and install windows updates on Windows server 2012 R2. The script is working fine when i login to the remote machine and execute the script. The script fails when i try tio install it via powershell using invoke command. When i use invoke command the script downloads the files without any issue but it never installs the files. Below is the script i have
$MSUs = #('https://download.microsoft.com/download/D/6/0/D60ED3E0-93A5-4505-8F6A-8D0A5DA16C8A/Windows8.1-KB2919442-x64.msu',
'https://download.microsoft.com/download/2/5/6/256CCCFB-5341-4A8D-A277-8A81B21A1E35/Windows8.1-KB2919355-x64.msu',
'https://download.microsoft.com/download/2/5/6/256CCCFB-5341-4A8D-A277-8A81B21A1E35/Windows8.1-KB2932046-x64.msu',
'https://download.microsoft.com/download/2/5/6/256CCCFB-5341-4A8D-A277-8A81B21A1E35/Windows8.1-KB2934018-x64.msu',
'https://download.microsoft.com/download/2/5/6/256CCCFB-5341-4A8D-A277-8A81B21A1E35/Windows8.1-KB2938439-x64.msu'
)
foreach ($MSU in $MSUs) {
$FileName = "C:\Temp\$(Split-Path $MSU -Leaf)"
Write-Host "Downloading package: $(Split-Path $MSU -Leaf)"
(New-Object System.Net.WebClient).DownloadFile($MSU, $FileName)
Write-Host "Installing package: $(Split-Path $MSU -Leaf)"
Start-Process wusa -ArgumentList "$FileName /quiet /norestart" -Wait
}
I try to execute the script with below command
Invoke-Command -Credential $cred -ComputerName 10.xx.xx.xxx -ScriptBlock { C:\\Temp\\1_KB.ps1| write-host}
Related
UserData by default will run with Powershell V5.1 on the Windows Server 2022 AMI on an AWS EC2 instance that spins up. However, I want to use some cmdlets that are only supported in Powershell version 7 and greater.
How am I best able to run a script with Powershell 7+ when booting the instance with UserData?
I currently have a script that installs powershell 7, but then from that point I am not sure how to use v7 to run the rest of the commands that I have.
Invoke-WebRequest -Uri https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/PowerShell-7.3.1-win-x64.msi -OutFile PowerShell.msi
Start-Process msiexec.exe -ArgumentList '/i PowerShell.msi /quiet' -Wait
I am using the WINDOWS_SERVER_2022_ENGLISH_FULL_BASE AMI.
I have tried using something like Invoke-Expression, and also have tried to get the script to call itself recursively with some conditionals, e.g.
# First Run with ps 5.1
if ($PSVersionTable.PSVersion -lt [Version]"7.0") {
Invoke-WebRequest -Uri https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/PowerShell-7.3.1-win-x64.msi -OutFile PowerShell.msi
Start-Process msiexec.exe -ArgumentList '/i PowerShell.msi /quiet' -Wait
cd "C:\Program Files\PowerShell\7"
# Run this same script with ps7
./pwsh $PSCommandPath
exit
}
#
if ($PSVersionTable.PSVersion -gt [Version]"7.0") {
# Do the things I need to do with ps7...
}
Both of my attempts have been silently failing, and with ec2 userdata it is very hard to get info on why.
The approach that ended up working was to have 2 different scripts. The first script installs PS7, and then downloads the second script from S3 and executes it using PS7.
User Data exectued with PS5:
#init.ps1
<powershell>
Invoke-WebRequest -Uri https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/PowerShell-7.3.1-win-x64.msi -OutFile PowerShell.msi
Start-Process msiexec.exe -ArgumentList '/i PowerShell.msi /quiet' -Wait
cd "C:\Program Files\PowerShell\7"
mkdir (Split-Path -Path 'C:/temp/setupGateway.ps1' ) -ea 0
Read-S3Object -BucketName 'my-bucket' -key 'setupGateway.ps1' -file 'C:/temp/setupGateway.ps1' -ErrorAction Stop
& "C:\Program Files\PowerShell\7\pwsh" "C:\temp\setupGateway.ps1"
</powershell>
<persist>true</persist>
PS7 script, executed separately:
# setup.ps1
Write-Output $PSVersionTable
Write-Output "Hello from PS7"
All that needs to happen to make this work is to make sure that you copy the setup.ps1 script to an S3 location. Which can be achieved in lots of different ways depending on the rest of your setup.
I wrote a ps1 script to automate some package installation but the strange part is when I run the command snippet for executing the .exe file for SEP (Symantec Endpoint Protection) , it is executing fine , but when I execute the entire script , it does run the command snippet.
Iam only running a simple .exe file , and even if I run it manually , it does not show any installer , rather it installs silently in the background.
So in the script, Iam only running the .exe file, thats it .
Should I be giving any wait time or any other inputs ?
Start-Process -Wait -FilePath "C:\Temp\Symantec-Windows\SEP 14.3.3384.1000 x64.exe" -passthru
$SymVersion = Get-WmiObject -Class Win32_Product -ComputerName $hostname | Where-Object -FilterScript {$_.Name -eq "symantec endpoint protection"} | Format-List -Property version, InstallState, name
echo $SymVersion
if($SymVersion)
{
echo 'Symantec is successfully installed' -ForegroundColor Green
}
else
{
echo 'Symantec is not successfully installed' -ForegroundColor Red
}
The symantec antivirus exe files are made for silent installations. If you want to proceed with GUI mode, better unzip the file and use MSI file with arguments. With your current script,Its better to check the process is exited with code 0. The following code is not tested.
$process = Start-Process -FilePath "C:\Temp\Symantec-Windows\SEP 14.3.3384.1000 x64.exe" -passthru -Wait
if($process.ExitCode -ne 0)
{
throw "Installation process returned error code: $($process.ExitCode)"
} else { Write-Host "Installation Successful"}
I have an update script for running the Dell Command Update tool. In short dcu-cli.exe. The thing now is than when i run the same script code on the computer local then everything runs OK but when i run the exact same code in a script with invoke-command(and yes i have full admin rights) than the exitcode is 2 meaning An unknown application error has occurred instead of 0 (everything OK)
It is a very large script so i created a new one to debug this. This is the shorted code:
Invoke-Command -ComputerName "MyComputer" -ScriptBlock {
$ExitCode = 0
#Declare path and arguments
$DcuCliPath = 'C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe'
$DellCommand = "/applyUpdates -autoSuspendBitLocker=enable -outputLog=C:\Dell_Update.log"
#Verify Dell Command | Update exists
If (Test-Path -Path $DcuCliPath) {
$objWMI = Get-WmiObject Win32_ComputerSystem
Write-Host ("Dell Model [{0}]" -f $objWMI.Model.Trim())
$serviceName = "DellClientManagementService"
Write-Host ("Service [{0}] is currently [{1}]" -f $serviceName, (Get-Service $serviceName).Status)
If ((Get-Service $serviceName).Status -eq 'Stopped') {
Start-Service $serviceName
Write-Host "Service [$serviceName] started"
}
#Update the system with the latest drivers
Write-Host "Starting Dell Command | Update tool with arguments [$DellCommand] dcu-cli found at [$DcuCliPath]"
$ExitCode = (Start-Process -FilePath ($DcuCliPath) -ArgumentList ($DellCommand) -PassThru -Wait).ExitCode
Write-Host ("Dell Command | Update tool finished with ExitCode: [$ExitCode] current Win32 ExitCode: [$LastExitCode] Check log for more information: C:\Dell_Update.log")
}
}
When i remove the Invoke-Command -ComputerName "MyComputer" -ScriptBlock { and then copy + run the script local on the PC then the exitcode = 0
What i also noticed than when i run the command via 'Invoke-Command' then there is also no log file created as i passed along in the arguments... So my best guess is something is going wrong with local an remote paths?
So what am i missing? I'm guessing it is something simple but i spend several hours to get this running without any luck...
Try running it this way. You should be able to see any output or error messages. I typically add to the path first rather than using & or start-process.
invoke-command mycomputer {
$env:path += ';C:\Program Files (x86)\Dell\CommandUpdate';
dcu-cli /applyUpdates -autoSuspendBitLocker=enable -outputLog=C:\Dell_Update.log }
Using start-process inside invoke-command seems pretty challenging. I can't even see the output of findstr unless I save it to a file. And if I didn't wait the output would be truncated. By default start-process runs in the background and in another window. There's a -nonewwindow option too but it doesn't help with invoke-command.
invoke-command localhost { # elevated
start-process 'findstr' '/i word c:\users\joe\file1' -wait -RedirectStandardOutput c:\users\joe\out }
#js2010, thanks for your additional help. Unfortunately this didn't helped either.
So i did some more debugging and it turns out it was a bug in the dcu-cli version running on my test machine, DOH...!!
On the test machine version 3.1.1 was running and on another machine version 4.0 was running and that worked fine via remote Powershell. So i looked for the release notes, which i found here: https://www.dell.com/support/kbdoc/000177325/dell-command-update
And as you can see in version 3.1.3 there was this fix:
A problem was solved where dcu-cli.exe was not executed in an external interactive session of PowerShell.
I've setup a deployment group successfully via Azure Pipelines and have deployed my Api successfully as well. My homework says that I have to prove that the Api was deployed successfully so I thought that I should run this via IIS. However, a 502.5 error is being thrown and I find out that a server hosting bundle is needed. How do you automate this via Azure PIpelines? I found an Invoke-Webrequest script that does this but it only installs 1.0.0...
I'm not sure if there is a built in way to do this, but in our project we've done it by including the DotNetCore.2.0.7-WindowsHosting.exe installer in our build artifacts and simply executing the installer with a Powershell step at the beginning of the release process.
You'll want to use the /quiet and /norestart flags:
$Path = "path to your installer exe in artifacts"
$args = New-Object -TypeName System.Collections.Generic.List[System.String]
$args.Add("/quiet")
$args.Add("/norestart")
Start-Process -FilePath $Path -ArgumentList $args -NoNewWindow -Wait -PassThru
Good luck!
if you are looking to download this directly from MS instead you can use this script:
$ErrorActionPreference="Stop";
If(-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole( [Security.Principal.WindowsBuiltInRole] “Administrator”)){
throw "Run command in Administrator PowerShell Prompt"
};If($PSVersionTable.PSVersion -lt (New-Object System.Version("3.0"))){ throw "The minimum version of Windows PowerShell that is required by the script (3.0) does not match the currently running version of Windows PowerShell." };
$tempDir = [System.IO.Path]::GetTempPath()
$downloadPath="$tempdir\netCoreHostingBundle.exe";
$DefaultProxy=[System.Net.WebRequest]::DefaultWebProxy;
$securityProtocol=#();
$securityProtocol+=[Net.ServicePointManager]::SecurityProtocol;
$securityProtocol+=[Net.SecurityProtocolType]::Tls12;
[Net.ServicePointManager]::SecurityProtocol=$securityProtocol;
$WebClient=New-Object Net.WebClient;
$Uri='https://download.visualstudio.microsoft.com/download/pr/9b9f4a6e-aef8-41e0-90db-bae1b0cf4e34/4ab93354cdff8991d91a9f40d022d450/dotnet-hosting-3.1.6-win.exe';
if($DefaultProxy -and (-not $DefaultProxy.IsBypassed($Uri))){$WebClient.Proxy= New-Object Net.WebProxy($DefaultProxy.GetProxy($Uri).OriginalString, $True);};
$WebClient.DownloadFile($Uri, $downloadPath);
$args = New-Object -TypeName System.Collections.Generic.List[System.String]
$args.Add("/quiet")
$args.Add("/norestart")
Start-Process -FilePath $downloadPath -ArgumentList $args -NoNewWindow -Wait -PassThru -WorkingDirectory $tempDir
I am trying to use this script to install Python on the remote computer. If I run this file directly on the server. This is the Python_Pip_Proxy_PyWinAuto.ps1 file. It works.
Set-ExecutionPolicy -ExecutionPolicy Unrestricted
Write-Host("Hi")
$installer="C:\temp\python-3.6.2.exe"
& $installer /quiet PrependPath=1 InstallAllUsers=1 TargetDir="C:\Python36"
However if I run the Invoke-Command using the following script to run this remotely on the same server, It print's the Hi message so I know that the file is running but Python doesn't get installed.
# Getting the list of servers from a .txt file to an array #
$SRVListFile = "C:\Scripts\ServerList.txt"
$SRVList = Get-Content $SRVListFile -ErrorAction SilentlyContinue
# Copying the .exe file from a shared location to each server in the array #
# Invoking the .ps1 file which runs natively on each server #
Foreach($computer in $SRVList) {
Get-Service remoteregistry -ComputerName $computer | start-service
Copy-item -Path "E:\Software\python-3.6.2.exe" -Destination \\$computer\c$\temp -Recurse
Copy-item -Path "C:\My Files\Work\Episode 003 - MongoDB Back Up\Python_GUI.py" -Destination \\$computer\c$\temp -Recurse
Invoke-Command -ComputerName $computer -FilePath "C:\My Files\Work\Episode 003 - MongoDB Back Up\Python_Pip_Proxy_PyWinAuto.ps1"
}
What is going wrong. What should I change the code to?
Try using the -scriptblock {Your command here} parameter to execute the command inside the scriptblock parenthesis on the remote computer.
Perhaps you can do it like
$Scriptblock = {
PowerShell -file "C:\My Files\Work\Episode 003 - MongoDB Back Up\Python_Pip_Proxy_PyWinAuto.ps1"
"This is Working" | out-file "C:\Hi.txt"
}
Invoke-Command -ComputerName $computer -Scriptblock $Scriptblock
You might want to remove the Write-Host "Hi" part because that gives the script an interactive nature. If you want to check for execution on remote computer, you can use out-file cmdlet to create a file on the remote computer as an indication.