Installing windows updates remotely, using PowerShell. (some PowerCLI) - windows

I am having a problem with a script for remote windows updates.
My goal: update all the windows servers, without using WSUS or SCCM, from PowerShell without having to log into all of them and start the update process.
For my script I am using powercli and PSWindowsUpdate modules.
For test purposes I am using a single host in the $hostname variable.
So here is what I have.
$Cred = Get-Credential
Import-Module PSWindowsUpdate
Invoke-Command -ComputerName $HostName {Import-Module PSWindowsUpdate} -Verbose -Credential $Cred
Invoke-Command -ComputerName $HostName {Add-WUServiceManager -ServiceID 7971f918-a847-4430-9279-4a52d1efe18d -Confirm:$false} -Verbose -Credential $Cred
Invoke-Command -ComputerName $HostName -ScriptBlock {Get-WUInstall -MicrosoftUpdate -IgnoreUserInput -AcceptAll -AutoReboot -Confirm:$FALSE -Verbose | Out-File C:\Setup\PSWindowsUpdate.log } -credential $cred
Get-Content \\$HostName\c$\Setup\PSWindowsUpdate.log
After running the script everything works from the shut down, snapshot, power on but I can't install any updates.
I am getting the error below :
WARNING: To perform some operations you must run an elevated Windows PowerShell console.
WARNING: Can't access to machine "hostName". Try use: winrm qc
As I was searching I see that I can not make any elevation in PowerShell itself and I am reading some posts about running a CMD bat to start PowerShell in elevated rights.
It is not a problem for me to copy to every windows server the update script and have a batch to run it in elevated rights but is this the only way?
I will do the batch solution to see what I can achieve but is there any other solution in the problem I am facing?
Has anyone tried that out?
Thank you very much for your time!
I am also open for any comment on my code or fixes!

If Admin right, elevated prompts are your issue, the following may help you.
PS Code to check if running as an Administrator. If not, It will relaunch as an Administrator.
If (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$Arguments = "& '" + $MyInvocation.MyCommand.Definition + "'"
Start-Process Powershell -Verb RunAs -ArgumentList $Arguments
Break
}
You will also likely need to modify ConsentPromptBehaviorAdmin at HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ in the Registry
-
Here is a simple BAT file that will will get around the PS Execution policy and then launch the PS Script (Assuming they are named the same).
REG ADD "HKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" /T REG_SZ /V ExecutionPolicy /D Unrestricted /F
Start PowerShell.exe -Command "& '%~dpn0.ps1'"
Add the end of your PS Script you can use Set-ExecutionPolicy -ExecutionPolicy Restricted -Scope LocalMachine -Force to set the execution policy back to restricted or what you have ti set to.

I ran into this issue when trying to import modules on remote machines using Invoke-Command. There's a security function that doesn't allow you to do it. You might try running the following to see if you're more successful at importing a new module.
$myPsSession = New-PSSession -ComputerName $HostName
Import-Module -Name PSWindowsUpdate -PSSession $myPsSession

I went with the task scheduler solution as it seems easier.
I created a script named WUpdates.ps1
$Timestamp=((Get-Date).ToString('dd_MM_yyyy_HH_mm'))
Import-Module PSWindowsUpdate -Verbose
Add-WUServiceManager -ServiceID 7971f918-a847-4430-9279-4a52d1efe18d -Confirm:$false -Verbose
Get-WUInstall -MicrosoftUpdate -IgnoreUserInput -AcceptAll -AutoReboot -Confirm:$FALSE -Verbose | Format-Table -AutoSize | Out-File C:\Setup\WUpdates\PSWindowsUpdate_$Timestamp.log
and a script to create a schedule task named WinUpdateScheduler.ps1
Unregister-ScheduledTask -TaskName "WindowsUpdates" -Confirm:$False
$Action = New-ScheduledTaskAction -Execute C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Argument '-f "C:\Setup\WUpdates\WUpdates.ps1"'
$Trigger = New-ScheduledTaskTrigger -Once -At (get-date).AddSeconds(30)
Register-ScheduledTask -Action $Action -Trigger $Trigger -RunLevel Highest -User system -TaskName "WindowsUpdates" -Description "Running Windows updates."
then in the main script I call the schedule script and have my updates installed.
Invoke-Command -ComputerName $HostName { c:\setup\WUpdates\WinUpdateScheduler.ps1 } -Credential $Cred
If anyone want the full script I can provide it.
As of the Nixphoe and Backin answers, I will check them and I will come back later to comment on them.
Thank you very much for your time.

Invoke-WUInstall -ComputerName Test-1 -Script { ipmo PSWindowsUpdate; Get-WUInstall -AcceptAll | Out-File C:\PSWindowsUpdate.log }
-Confirm:$false –Verbose
https://4sysops.com/archives/install-windows-updates-remotely-with-the-powershell/

Related

Registry value not change

so i just start learn about powershell script
my objective is to uncheck this one
system properties
so i create powershell script to run the file.reg
this is my test1.ps1
$username = "desktop-2ussd\viola"
$password = "qwerty"
$AdminCred = New-Object System.Management.Automation.PSCredential -ArgumentList #($username,
(ConvertTo-SecureString -String $password -AsPlainText -Force))
$regFile = ".\file.reg"
$regArg1 = "import $regFile"
Start-Process reg.exe -ArgumentList $regArg1 -Credential $AdminCred
and this is my file.reg
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server]
"fDenyTSConnections"=dword:00000000
"updateRDStatus"=dword:00000001
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp]
"UserAuthentication"=dword:00000000
after that i run the script like this
powershell -ExecutionPolicy Bypass -File .\test1.ps1
there is no error output but the checkbox is still checked
please help me
Currently you wont recognize if something goes wrong as you do not get a return code. In case of start-process you would need to specify the parameters:
-wait
-passthru
to get the return code.
But you can directly write to the registry from PowerShell instead of using reg.exe. - e.g.:
set-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -name fDenyTSConnections -value 0
The above mentioned registry change gets effective immediately without restarting the related service.
Based on your comment you missed to specify the computer where the command should run. Also make use of $using to access variables of the caller machine from the remote machine e.g.:
$code = {
$newValue = $using:value
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "UserAuthentication" -Value $newValue
}
$value = 0
invoke-command -computername [TargetComputerName] -credential $cred -scriptblock $code
In your example you did pass the value to the paramter -value as $args[0] - this only works if you specify the paramter -argumentlist of the invoke-command cmdlet. But I would advise to use $using as outlined in my example.

Create scheduled task w/ Admin access from PowerShell?

I have a script that sets up the (AWS) instance (joins domain, hardening etc).
Part of it (load GPO backups, create OUs and users) needs to run after a reboot, so I figured I create a scheduled task for it.
But I can't get it to get the access it needs!
$psexe = Join-Path $PSHOME powershell.exe
$T = New-ScheduledTaskTrigger -AtStartup
$P = New-ScheduledTaskPrincipal -UserId "NT AUTHORITY\SYSTEM" -RunLevel Highest -LogonType ServiceAccount
$S = New-ScheduledTaskSettingsSet
$A = New-ScheduledTaskAction -Execute $psexe -Argument "-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Unrestricted -File C:\Windows\Temp\install_gpo.ps1"
$D = New-ScheduledTask -Action $A -Principal $P -Trigger $T -Settings $S
Register-ScheduledTask -TaskName "Install GPO" -InputObject $D -ErrorAction Ignore
The script itself is fairly simple:
Start-Transcript -path C:\log-install_gpo.txt
Import-GPO -BackupGpoName LogonBanner -TargetName LogonBanner -Path C:\Windows\Temp\GpoBackups -CreateIfNeeded
gpupdate
Stop-Transcript
Running the task (!) manually from the task scheduler GUI and looking at the log file, I see:
Username: DOMAIN\SYSTEM
RunAs User: DOMAIN\SYSTEM
[...]
Import-GPO : Access is denied. (Exception from HRESULT: 0x80070005
(E_ACCESSDENIED))
At C:\Windows\Temp\install_gpo.ps1:2 char:1
+ Import-GPO -BackupGpoName LogonBanner -TargetName LogonBanner -Path C ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Import-GPO], UnauthorizedAcce
ssException
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.Gro
upPolicy.Commands.ImportGpoCommand
So it seems that the 'System' user doesn't have access to do what I want/need! Not sure why that is, I specify the highest RunLevel and Unrestricted execution policy..
Running the Import-GPO command in an elevated PS shell works just fine!
I can't run it as Administrator, because that would require a password (that doesn't work anyway, Register-ScheduledTask refuses to accept that on the command line), I can't specify both -UserID and -GroupID (...\Administrator for example) at the same time.
I've tried using the RunOnce registry key, but that seem to work even less. Also, if I understand the docs for that correctly, it is only run when a/any user logs in, which might never happen! This is (supposed to be) a "headless server" so no-one should ever login to it..
I'm at a loss to what to try next.

Execution policy modification

I need to run few scripts in different servers which will in turn stop/start the services in many other servers.
I have created a script to change the policy to "Bypass" and run the scripts and then made the changes back to normal.
$LOGPATH = "output.txt"
$system_default_policy = Get-ExecutionPolicy
"Current execution policy $system_default_policy" | Out-File -FilePath $LOGPATH
if ($syste_default_policy -ne 'Bypass') {
"Changing the execution policy from $system_default_policy to Bypass" | Out-File -FilePath $LOGPATH -Append
Set-ExecutionPolicy Bypass -Force
"Successfully changed the execution policy to Bypass" | Out-File -FilePath $LOGPATH -Append
}
### executing the commands to stop/start the services
"Re-writing the changes to default policy" | Out-File -FilePath $LOGPATH -Append
Set-ExecutionPolicy $system_default_policy -Force
"Changed the policy to " + $(Get-ExecutionPolicy) | Out-File -FilePath $LOGPATH -Append
However, this seems to be a redundant process in the below case.
If the execution policy is already Bypass, then I'm simply resetting it in the last line.
I have to run multiple scripts in the same server, so for each script I'm changing it to 'Bypass' and setting it back to original.
Is there any other way where I will run this script only once (to change the execution policy) before executing the scripts and then changing it to original value at the end after running all the scripts.
The execution policy does only apply to scripts, hence it does not apply to code that is being invoked on the host or passed as an command. There are various ways to do that, some of which are:
Invoke-Command from a remote computer.
Invoke-Command -ComputerName $Computer -ScriptBlock {
# Code
}
powershell.exe -Command locally
powershell.exe -Command "#code"
However, usually the easiest way to run scripts without changing the configuration is
powershell.exe -ExecutionPolicy Bypass -File C:\yourscript.ps1

Remote Installation of Python using Powershell fails using Invoke-Command

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.

Starting a .exe file on a remote computer using powershell

Hey everyone I'm attempting to start a exe file on remote systems, we have an agent that runs to connect our website to our database and printers. It hangs occasionally, and requires us to remote in and/or go to the station and restart the application. I've attempted many different solutions on here and none seem to work.
I'm able to kill the process that is running but then it will not reopen the exe file to start it again. Here is the small script so far
################################################
write-host "This will Restart Workbench helper please press the any key to start"
pause
$mycreds = Get-Credential
$computer = Read-Host -prompt "Please Enter Computer Name"
$WBSTART1 = Read-Host -prompt "please enter command"
$pers = New-PSSession -ComputerName $computer -Credential $mycreds
# Gets Computer Name and Kills Workbench Helper #
(Get-WmiObject win32_process -ComputerName $computer -Credential $mycreds | ?{ $_.ProcessName -match "Workbench3.helper" }).Terminate()
# Writes Host countdown to screen #
write-host -ForegroundColor black -NoNewline "Stopping Process"
foreach ($element in 1..10)
{
Write-Host -NoNewline "${element} " -BackgroundColor 'white' - ForegroundColor 'black'
Start-Sleep -Seconds 1
}
Write-Host ''
Write-host "Process Complete"
Start-Process -filepath "C:\Program Files (x86)\Company Security\Workbench3 Helper\WorkBench3.Helper.exe"
# Writes Host countdown to termination Screen #
write-host -ForegroundColor black -NoNewline "Starting Workbench Process"
foreach ($element in 1..10)
{
Write-Host -NoNewline "${element} " -BackgroundColor 'white' -ForegroundColor 'black'
Start-Sleep -Seconds 1
}
Write-Host ''
Write-host "Process Complete"
# Starts Process #
$WBSTART = {start-process -filepath 'C:\Program Files (x86)\FrontPoint Security\Workbench3 Helper\WorkBench3.Helper.exe'}
Invoke-Command –ComputerName $computer -Credential $mycreds -ScriptBlock $WBSTART
#Starts Process# at the end runs with no errors, but nothing happens on the remote machine, killing the process works fine. Any help would be greatly appreciated.
You created a pssession to the remote computer but didn't use it. Your Start-Process was run on the computer hosting the script. You need to use Enter-PSSession since it:
Starts an interactive session with a remote computer.
Once you are in a session then you do not need to run the WMI commands remotely. You can run them like you were running on the server directly. Borrowing from the example on MSDN
The first command uses the Enter-PSSession cmdlet to start an interactive session with Server01, a remote computer. When the session starts, the command prompt changes to include the computer name.
PS C:\> Enter-PSSession -Computer Server01
[Server01]: PS C:\>
The second command gets the Windows PowerShell process and redirects the output to the Process.txt file. The command is submitted to the remote computer, and the file is saved on the remote computer.
[Server01]: PS C:\> Get-Process Powershell > C:\ps-test\Process.txt
Don't forget to call Exit-PSSession when you are done.
Invoke-Command
If you didn't want to use pssessions then you could also you Invoke-Command on the remote machine as well. You already are trying to do this with your last lines of code.
Invoke-Command -ComputerName $computer -ScriptBlock {Start-Process "bagel.exe"}
If you do go down this path be aware of scoping issues and look up how to pass arguments to the scriptblock.
I am not sure why yours is not working. Perhaps you have some errors being suppressed? I would try something simpler for testing. Is your application GUI based? It might not work when run like this. Try something like ipconfig and see what the results are.
I would also consider a while loop with a timeout condition while you are waiting for the process to terminate/start on the remote machine. That was you can account for failures better instead of assuming that 10 seconds is enough.
I think you've a problem with your local/remote execution.
(Get-WmiObject win32_process -ComputerName $computer -Credential $mycreds | ?{ $_.ProcessName -match "Workbench3.helper" }).Terminate()
Above line returns all processes from the remote computer to your local computer. Than you're fitering the returned process-objects on your LOCAL machine, and call Terminate() on your LOCAL machine. I would suggest you use the following line instead:
Invoke-command -session $pers -ScriptBlock { (Get-WmiObject win32_process -ComputerName $computer -Credential $mycreds | ?{ $_.ProcessName -like"*Workbench3.helper*" }).Terminate()}
This should kill the Workbench3 process(es). I used the like operator to make filtering less strict.
After that, you can reuse the session stored in $pers to start the process again.
Invoke-command -session $pers -scriptblock {Start-Process -filepath "C:\Program Files (x86)\Company Security\Workbench3 Helper\WorkBench3.Helper.exe" -Wait
}
I additionally used the -Waitswitch. The switch forces Start-Process to wait for the programm to be started (and return afterwards).
When doing remote operations I would always suggest to perform these operations via Invoke-Commandand not via the -ComputerName parameters of the several commands. Invoke-Command uses WS-MAN via HTTP(S), while the remote-implemenation of commands offering the -ComputerNameparameter differ in implementation from cmdlet to cmdlet. This advantage of invoke-commandmakes it easier to configure e.g. firewalls.
Hope that helps.
Thanks for all your help! So this is what I ended up with to accomplish what I was trying to do, what do you think?
$mycreds = Get-Credential
$computer = Read-Host -prompt "Please Enter Computer Name"
$s = New-PSSession -ComputerName $computer -Credential $mycreds
#### Kills Workbench Helper Proccess ####
Invoke-command -ScriptBlock { (Get-WmiObject win32_process -ComputerName $computer -Credential $mycreds | ?{ $_.ProcessName -like"*Workbench3.helper*" }).Terminate()}
################ Starting Workbench Helper #####################
Invoke-Command $s {$run = (Get-Date).AddMinutes(1) Register-ScheduledTask -TaskName "ZZ_Start WorkBench Helper" -User "domain\user" -InputObject (
(
New-ScheduledTask -Action (
New-ScheduledTaskAction -Execute "C:\Program Files (x86)\Company Security\Workbench3 Helper\WorkBench3.Helper.exe" -Argument (
" "
)
) -Trigger (
New-ScheduledTaskTrigger -Once -At ($run.TimeOfDay.ToString("hh\:mm")) # As a "TimeOfDay" to get 24Hr format
) -Settings (
New-ScheduledTaskSettingsSet -DeleteExpiredTaskAfter 00:00:01 #
)
) | %{ $_.Triggers[0].EndBoundary = $run.AddMinutes(1).ToString('s') ; $_ }
)
}

Resources