How to Install Windows Updates on Remote Computer with PowerShell - powershell

I'm trying to install Windows Updates on a Remote Computer with this command:
$InstallSplat = #{
AcceptAll = $true
SendReport = $true
IgnoreReboot = if ($Reboot) { $false } else { $true }
PSWUSettings = #{
SmtpServer = "my mail server"
From = "myfrom <myfrom#myfrom.com>"
To = "myto <myto#myto.com>"
Port = 25
}
}
Invoke-Command -ComputerName $_ -Credential $cred -AsJob -ArgumentList $InstallSplat -ScriptBlock {
param([hashtable]$InstallSplat)
Import-Module PSWindowsUpdate
Install-WindowsUpdate #InstallSplat
$Error | out-file C:\install\installwinupdate.log -Append
}
I pass a credential Object with domain admin privileges in $cred but I still always get this error
Install-WindowsUpdate : Access denied (Ausnahme von HRESULT: 0x80070005 (E_ACCESSDENIED)) In Zeile:4 Zeichen:25
+ Install-WindowsUpdate #InstallSplat
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-WindowsUpdate], UnauthorizedAccessException
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,PSWindowsUpdate.GetWindowsUpdate
The Command Install-WindowsUpdate itself does not have a credential parameter I could use. The Command needs to run in an elevated PowerShell, but I use an elevated PowerShell when starting this command on my Computer.
I Also tried creating a New-PSSession with my $cred and run Invoke-Command -Session $session instead of Invoke-Command -ComputerName $_ with the same result.
Does anybody know what's happening here? Why do I get Access denied?
It can't have anything to do with passing the $InstallSplat because the same thing happens if I don't pass any parameter at all and write the parameters and their Values directly at the command instead of splatting.

The Problem was, that you can't Download or Install Updates on a machine from another remote machine. Here's a list what you can or can't do remotely when it comes to Windows Updates
The solution is, to create a scheduled task on each server you want to install updates from a remote script, and start that task.
luckily, when you use the PSWindowsUpdate module, you don't have to do that yourself, you can just use Invoke-WUJob (formerly Invoke-WUInstall) which does the trick for you.
I used it like so ($ServerData.Value contains a list of my Servers) and it works like a charm. It creates a scheduled task on each server, and runs them immediately, if you add the -RunNow Parameter.
invoke-WUJob -ComputerName $ServerData.Value -Script { Import-Module PSWindowsUpdate ; Install-WindowsUpdate -AcceptAll -SendReport -IgnoreReboot -PSWUSettings #{From='xy';Port=25;SmtpServer='xy';To='xy'} | Out-File C:\install\PSWindowsUpdateLog.txt -Append} -Confirm:$false -verbose -RunNow
Note that what you specify as a script block in -Script will be pasted to -Command " <here> " in your scheduled task, so you should work with ' inside -Script.

Related

invoke-command with a parameter accessing UNC

I am trying to generate some IDs using a adobe tool called adobe-licensing-toolkit.exe.
I need to run the command remotely in 100 computers.
Executing the command manually works flawless
C:\temp\adobe-licensing-toolkit.exe -c -f \\XXXXXXX\c$\temp\IDs.csv
Adobe Licensing Toolkit (1.1.0.98)
Operation Successfully Completed
Now I tried to replicate that using remote PS without success. I think it is a matter of parameters.
The following command ends correctly but it generates the file locally in the remote computer.
Invoke-Command -ComputerName $comp -ScriptBlock { param($whatToDo,$targetCSV) &('C:\TEMP\adobe-licensing-toolkit.exe') --$whatToDo --$targetCSV "C:\temp\ID.csv"} -ArgumentList "generateChallengeKey","filepath"
If I try to use the UNC in the parameter, the result is Operation failed.
Invoke-Command -ComputerName $comp -ScriptBlock { param($whatToDo,$targetCSV) &('\\XXXXXXXX\c$\TEMP\adobe-licensing-toolkit.exe') --$whatToDo --$targetCSV "C:\temp\ID.csv"} -ArgumentList "generateChallengeKey","filepath"
I also tried to add path in the parameter. In that case is powershell who complains.
Invoke-Command -Session $Server01 -ScriptBlock { param($whatToDo,$targetCSV) &('C:\TEMP\FRL\adobe-licensing-toolkit.exe') --$whatToDo --$targetCSV } -ArgumentList #("generateChallengeKey","filepath \\XXXXXXX\c$\temp\ID.csv")
unknown option -- filepath \\XXXXX\c$\temp\ID.csv
+ CategoryInfo : NotSpecified: (unknown option ...c$\temp\ID.csv:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
+ PSComputerName : XXXXXXX
I have the feeling that the issue is in the way parameter is passed but I haven't managed to find the solution.
The exe file is already present in all target computers.
Any suggestion?
Thanks
I would presume you have toolkit present in all remote computer in path: "C:\TEMP\adobe-licensing-toolkit.exe". You can simply use
Invoke-Command -ComputerName $comp -ScriptBlock { & "C:\TEMP\adobe-licensing-toolkit.exe" -c -f \\XXXXX\$env:Computername-IDs.csv}
Adding $env:Computername in share path would generate unique file for each computer.

Set-Service : A parameter cannot be found that matches parameter name 'Credential'

I am trying to use a powershell script to change the login credential of a service. As per this Microsoft documentation (example 8), I am using the following code:
$credential = Get-Credential
Set-Service -Name serviceName -Credential $credential
When I run the script, I am prompted for a username and password, which I enter, but then the following error results:
Set-Service : A parameter cannot be found that matches parameter name 'Credential'.
At line:2 char:28
+ Set-Service -Name serviceName -Credential $credential
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Set-Service], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.SetServiceCommand
What might be causing this? This machine is not on a domain, but I am logged on as a local administrator, and running powershell as an administrator. I have permissions to change these services and can enter the login details normally via the usual Services GUI.
The Credential parameter only exist for PowerShell version 6+ (which is out of support).
Probably you are using a version from before PowerShell 6 (most probably 5.1 which is still the main version in Windows 10 and 11).
See below Set-Service in:
PowerShell 5.1
PowerShell 6.0 (Archive)
PowerShell 7.0
Run $PSVersionTable.PSVersion, to your PowerShell version.
If you run (Get-Help -Name Set-Service).Parameters.parameter.Name, you'll find all the available Parameters in the active version.
If you're running 5.1 or older, the Get-Help -Name Set-Service -Parameter Credential should return the error "No parameter matches criteria.".
Installing or updating to PowerShell 7.X is as easy as running winget install Microsoft.PowerShell.
Why don't you want to use:
sc.exe config "[servicename]" obj= "[.\username]" password= "[password]"
If you want the user to enter a username/password each time, then you can use:
$cred = Get-Credential
$username = $cred.username
$password = $cred.GetNetworkCredential().password
&sc.exe config "[servicename]" obj= "$username" password= "$password"
if you need to check that the username and password are valid, then you can use:
$currentdomain = "LDAP://" + ([ADSI]"").distinguishedName
$domain = New-Object System.DirectoryServices.DirectoryEntry($currentdomain,$username,$password)
if ($domain.name -eq $null) {
write-host "Authentication failed - please verify your username and password."
exit
}
else {
write-host "Successfully authenticated with domain $domain.name"
}

Executing CMD or EXE file using PSSession (remote powershell) caused Error 1603 access denied

I have the following script powershell command but it returns access denied. I assume the Error 1603 is caused by remote accessing the server. However, the $username has admin rights in the computer01 server.
To recheck if my hunch was right, I tried to test with the following and I got access denied:
Start-Process cmd -Credential $Cred
Update
The error was due to the $Cred . Removing the -Credential argument works fine.
End of Update
The commands have no problems executing directly in the computer01 machine using the cmd.exe.
I want to use cmd /c in this case as I need to get the real exit code from the SETUP.EXE installer.
See full script below:
$script = {
#Param(
# [String]$username,
# [String]$password
#)
# $Cred = New-Object System.Management.Automation.PSCredential ($username, $password)
$respfile = "$env:TEMP\test.resp"
echo 'key=value' > $respfile
$username = "$env:USERDOMAIN\$env:USERNAME"
Write-Host Hello $username
$Creds = (Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME" )
Start-Process cmd -Credential $Creds
#This command cannot be run due to the error: Access is denied.
# + CategoryInfo : InvalidOperation: (:) [Start-Process], #InvalidOperationException
# + FullyQualifiedErrorId : #InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand
# + PSComputerName : computer01
# cmd /c "$path\SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'"
runas /user:$Username "SETUP.EXE" /INSTALL -s /RESPFILE:"$respfile"
echo $LASTEXITCODE
# Error 1603
}
#$username = 'domain/user'
#$password = 'password'
$server = 'computer01'
$Creds = New-Object System.Management.Automation.PSCredential
$session = New-PSSession -ComputerName $server
#Invoke-Command -Session $session -Scriptblock $script -Argumentlist $username, $password
Invoke-Command -Session $session -Scriptblock $script -Credential $Creds #updated based on #postanote advise
Remove-PSSession -ComputerName $server
I have found the following similar link install-remotely but do not want to use the ENTER-PSSession command. I do not want to exit the current PSSession and remotely join again in server just to install then exit.
Any suggestions how to use only PSSession and successfully executing installers in the remote server?
As one mentioned in the comments, you don't need cmd.exe. You can use the call/invocation operator - & - to specify that the next token on the line is a command:
& "$path\SETUP.EXE" /INSTALL -s /RESPFILE:$respfile
Of course, for this to work, the parameters to SETUP.EXE need to be correct (I don't know whether that's the case or not).
Never pass plain text passwords in scripts. It exposes you to uneeded risks.
Use proper secured credentials models.
• Working with Passwords, Secure Strings and Credentials in Windows PowerShell
• quickly-and-securely-storing-your-credentials-powershell
PowerShell remoting requires the use of an implicit (New-PSSession) or explicit (Enter-PSSession) session.
• About Remote Requirements
There are only a handful of cmdlets you can use as non-Admin ir run without PSRemoting enabled.
• Tip: Work Remotely with Windows PowerShell without using Remoting or WinRM
As noted in the Powershell Help file | MS Docs link above, with PSRemoting, you must be using an account that is an admin on the remote host.
In Windows OS proper, to install software, you must be an admin and running that in an admin session.
PowerShell runs in the context of the user who started it.
If you are trying to run in another user context, that is a Windows Security boundary, and you cannot do that without PowerShell natively, you'd need other tools like MS Sysinternals PSExec. See also:
Find-Module -Name '*Invoke*' | Format-Table -AutoSize
# Results
<#
Version Name Repository Description
------- ---- ---------- -----------
...
3.1.6 Invoke-CommandAs PSGallery Invoke Command as System/User on Local/Remote computer using ScheduleTask.
...
#>
Try this refactored option...
$script = {
$Creds = (Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME" )
$respfile = 'whatever this is'
& "SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'"
Write-Output $LASTEXITCODE
}
$server = 'computer01'
$session = New-PSSession -ComputerName $server
Invoke-Command -Session $session -Scriptblock $script -Credential $Creds
Remove-PSSession -ComputerName $server
Details
# Get specifics for a module, cmdlet, or function
(Get-Command -Name Invoke-Command).Parameters
(Get-Command -Name Invoke-Command).Parameters.Keys
Get-help -Name Invoke-Command -Examples
# Results
<#
Invoke-Command -ComputerName server01 -Credential domain01\user01 -ScriptBlock {Get-Culture}
$s = New-PSSession -ComputerName Server02 -Credential Domain01\User01
$LiveCred = Get-Credential
Invoke-Command -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.exchangelabs.com/PowerShell -Credential $LiveCred -Authentication Basic
Invoke-Command -Session $s -ScriptBlock { Get-HotFix } -SessionOption $so -Credential server01\user01
Enable-WSManCredSSP -Delegate Server02
Set-Item WSMan:\Server02*\Service\Auth\CredSSP -Value $True
Invoke-Command -Session $s -ScriptBlock {Get-Item \\Net03\Scripts\LogFiles.ps1} -Authentication CredSSP -Credential Domain01\Admin01
#>
Get-help -Name Invoke-Command -Full
Get-help -Name Invoke-Command -Online
So, I was able to solve my problem.
1603 is the error thrown by the setup.exe.
Just to be sure, I manually executed first the following directly in the server using CMD and it was working!
$path\SETUP.EXE /INSTALL -s /RESPFILE:'$respfile'
I did a lot of testings. Researched and as mentioned from comments above, I did different ways to execute programs using powershell. I even used ACL to change ownership of installer directory/ files, switching to different user accounts (with different priviledges) but still getting access denied (including the Admin account).
It took days before I realized the difference in output file size of manual run in machine and the remote. The cause was the $respfile. It really is worth checking every possible reason/ scenario why there's access denied. Plus I cannot extract the setup.exe and its contents to troubleshoot.
The $respfile was created via powershell. I noticed the size created by powershell is doubled compared to a CMD size that was needed. With that, I assumed that the setup.exe reads file in UTF-8 format. I only know that it's working when triggered via CMD and not via powershell.
I suddenly bumped on this links differrent Powershell and CMD sizes and convert file content to CMD readable file - utf8. After converting the $respfile to UTF-8 format, I was able to run the exe successfully.
Hopefully, this can help others too!

Unresolved parameters (Invoke-Command)

When running the code below I get the error message
Invoke-Command : Missing an argument for parameter 'ComputerName'. Specify a
parameter of type 'System.String[]' and try again.
At line:11 char:16
+ Invoke-Command -ComputerName -ScriptBlock $scriptblock -Credential $Cred -Argum ...
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Invoke-Command],
ParameterBindingException
+ FullyQualifiedErrorId : MissingArgument,Microsoft.PowerShell.Commands.InvokeCommandCommand
The code:
$item = "1337"
$username = "username"
$password = "password"
$Cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, ($password | ConvertTo-SecureString -AsPlainText -Force)
$scriptblock = {
New-PSDrive -Name SampleDC -PSProvider FileSystem -Root \\sampleDC\scripts
."C:\scripts\sample.ps1" # include global functions scripts
new_user $args[0] # new_user is a function in global functions
}
Invoke-Command -ScriptBlock $scriptblock -Credential $Cred -ArgumentList $item
You cannot run Invoke-Command with different credentials without specifying a computer. The error you're getting is because you used the parameter -ComputerName without an argument.
To have Invoke-Command run the scriptblock on the local computer use either of the following commands:
Invoke-Command -Computer . -ScriptBlock $scriptblock -Credential $Cred ...
Invoke-Command -Computer localhost -ScriptBlock $scriptblock -Credential $Cred ...
Invoke-Command -Computer $env:COMPUTERNAME -ScriptBlock $scriptblock -Credential $Cred ...
Note that if the user whose credentials you're passing does not have admin privileges you'll be getting an error like this:
[localhost] Connecting to remote server localhost failed with the following
error message : Access is denied. For more information, see the
about_Remote_Troubleshooting Help topic.
+ CategoryInfo : OpenError: (localhost:String) [], PSRemotingTransportException
+ FullyQualifiedErrorId : AccessDenied,PSSessionStateBroken
In that case you need to enable PowerShell Remoting for them first. Run the following command in an elevated PowerShell console and add the user or group in the dialog that pops up.
Set-PSSessionConfiguration Microsoft.PowerShell -ShowSecurityDescriptorUI
From the documentation:
HOW TO ENABLE REMOTING FOR NON-ADMINISTRATIVE USERS
ERROR: ACCESS IS DENIED
To establish a PSSession or run a command on a remote computer, the user must have permission to use the session configurations on the remote computer.
By default, only members of the Administrators group on a computer have permission to use the default session configurations. Therefore, only members of the Administrators group can connect to the computer remotely.
To allow other users to connect to the local computer, give the user Execute permissions to the default session configurations on the local computer.
The following command opens a property sheet that lets you change the security descriptor of the default Microsoft.PowerShell session configuration on the local computer.
Set-PSSessionConfiguration Microsoft.PowerShell -ShowSecurityDescriptorUI
The computername parameter is missing. I believe you need to be at an elevated prompt to invoke-command on localhost.

Access denied while running Windows Update using Powershell's Invoke-Command

I've been trying to setup a Powershell module that would remotely call Windows/Microsoft update on a server using Invoke-Command, then process the updates, and send everything back to the calling server so it can send an email report.
My issue comes when I try and call the downloader: Powershell seems to be requesting Elevated rights on the remote computer.
Here is a snippet of what I'm trying to run and fail:
Invoke-Command -ComputerName $Server -Credential $Credentials -ScriptBlock {
$UpdateSession = New-Object -ComObject "Microsoft.Update.Session"
Write-Progress -Activity "Updating" -Status "Checking for new updates"
$Criteria = "IsInstalled=0 and Type='Software'"
$Updates = $UpdateSession.CreateUpdateSearcher().Search($Criteria).updates
$Downloader = $UpdateSession.CreateUpdateDownloader()
$Downloader.Updates = $Updates
}
I know the issue isn't with remoting, as the first 4 commands work fine.
The $Credentials variable points to pre-defined credentials, which are Local Admin on the remote server.
When the script gets to the 5th line, $Downloader = $UpdateSession.CreateUpdateDownloader(), I get this error from Powershell:
Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
+ CategoryInfo : OperationStopped: (:) [], UnauthorizedAccessException
+ FullyQualifiedErrorId : System.UnauthorizedAccessException
+ PSComputerName : SERVER.sidlee.inc
What could be causing this exactly ?
Thanks in advance for the help!
As i just hit the same wall, and Google isn't of much help either, here is what i could dig up.
For the record, i am pretty much doing the same thing (using custom PS code to check remote systems for Windows Updates) but using WinRM over Python instead of Invoke-Command and also got stuck on Microsoft.Update.Searcher.Search() throwing a E_ACCESSDENIED error.
The UnauthorizedAccessException is indeed not related to Powershell but the underlying API.
I suspect Microsoft started cutting off impersonation in remote session in some recent update (Powershell v5?) as this was (and still is) working just fine on older Windows versions (e.g. Server 2012 with Powershell v3 or 2012 R2 with v4)
To get around this you will need to authenticate (on the remote server) prior to executing your stuff with a PSCredential object.
So Remote Auth -> Local Auth -> Run stuff for example using Start-Process -Credential ...
e.g.
$pass = ConvertTo-SecureString "PA$$W0RD" -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential "User", $pass
Start-Process -Credential $creds powershell -ArgumentList "-Command & { ... whatever you want to do ... }"
Keep in mind that this poses a security risk as your password will be parsed in clear text, so don't do this over an
unencrypted channel!