I'm trying to get a script together to remotely install some windows updates on some remote servers that are connected in an offline domain.
I have tried regular PS Remoting and after some research, I think what I am trying to do isnt supported by microsoft. When checking my event logs I have a bunch of these errors.
Edit
I wanted to add that I have tried running the .\Install2012R2.ps1 script from my local computer, modified to have the Invoke-Command in that and have it run the update portion of the original Install2012R2.ps1 and I would get the same errors.
I was hoping that by placing the script on each server that it would like that more.
End Edit
Windows update could not be installed because of error 2147942405 "Access is denied."
(Command line: ""C:\Windows\System32\wusa.exe" "C:\Updates\windows8.1-kb4556853-x64.msu" /quiet /norestart")
I have tried running Invoke-Command as credentialed to an administrator account on the servers but I have been having no luck and was looking for some advice if someone has maybe tried/done this before.
$Servers = #("V101-Test1","V101-Test2")
$Username = 'admin'
$Password = 'Password'#not actual password
$pass = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$pass
Get-PSSession | Remove-PSSession
New-PSSession -ComputerName $Servers
foreach($Server in $Servers){
Get-ChildItem -Path C:\Source\Temp -Recurse | Copy-Item -Destination "\\$Server\c$\Updates\" -Force
}
Invoke-Command $Servers -Credential $Cred -ScriptBlock{
& "C:\Updates\Install2012R2.ps1"
}
EDIT 2
Here is the actual install code of the Install2012R2.ps1 script
$updatedir= "./"
$files = Get-ChildItem $updatedir -Recurse
$msus = $files | ? {$_.extension -eq ".msu"}
$exes = $files | ? {$_.extension -eq ".exe"}
foreach ($file in $msus){
$KBCtr++
$fullname = $file.fullname
# Need to wrap in quotes as folder path may contain space
$fullname = "`"" + $fullname + "`""
$KBN = $fullname.split('-')[1]
# Need to wrap in quotes as folder path may contain space
$fullname = "`"" + $fullname + "`""
# Specify the command line parameters for wusa.exe
$parameters = $fullname + " /quiet /norestart"
# Start services and pass in the parameters
$install = [System.Diagnostics.Process]::Start( "wusa",$parameters )
$install.WaitForExit()
}
I'm not sure why wusa.exe is failing here with Access Denied, but here is a PowerShell-native approach you can try. If nothing else, it should give you a clearer indication via the captured error information as to what the underlying issue is:
Add-WindowsPackage -Path C:\Updates\OurHeroicUpdate.msu -Online -PreventPending -NoRestart
-Path is the path to the msu file
-Online tells Add-WindowsPackage to modify the currently "mounted image" (the running version) of Windows (as opposed to an offline disk image you could also apply it to)
-PreventPending prevents installing the msu if there is already a pending change, like needing to reboot for updates.
Add-WindowsPackage is part of the DISM module available under Windows PowerShell, and is the functional equivalent of dism /packagepath:"cabfile", although it can take an msu where dism.exe only allows a cab.
Related
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.
I have a task to check the username and password on multiple servers and report the result.
So basically i have a list of IP's And I want I have the same user and password and need to see which authenticate and which don't.
This is what I found so far but it doesn't seems to work it prompts me for password every-time.
$listofServers = Import-Csv '.\Windows Servers.csv'
$username = username
$password = password
foreach($server in $listofServers.ip)
{
try{
$Credentials = Get-Credential $server\$username $password
}
Catch
{
$errorMsg = $_.Exception.Message
}
}
As said more than once, but never too much, you should NOT store a password in plain text. Probably the simplest of the many ways you can store it as a clixml file encrypted by a specific account. You can limit access to the location it's saved to that same account that can decrypt it.
You'll need to use the same username/password in combination with the computer name/IP to build an actual PSCredential object. I assume IP as your code shows $listofservers.ip - if the CSV header is not IP, update the code accordingly. I added some feedback to show you which computer and user connected and the fact that it's able to run the command remotely proves the connection was successful.
$username = 'localadmin'
$password = ConvertTo-SecureString 'PlaintextIsaNoNo' -AsPlainText -Force
Import-Csv '.\Windows Servers.csv' | ForEach-Object {
$cred = New-Object System.Management.Automation.PSCredential ("$($_.ip)\$username", $password)
Try
{
Invoke-Command -ComputerName $_.ip -ScriptBlock {
Write-Host Sucessfully connected to $env:computername as $(whoami) -ForegroundColor Green
} -Credential $cred
}
Catch
{
$PSItem.Exception.Message
}
}
The problem with the above code is it will run one PC at a time. Invoke-Command runs asynchronously if you just give it a list of computers (up to 32 by default on PS5.1) If you change the structure, this will run much faster. This requires a pretty well known trick which is to provide the username as .\username which designates it's a local account. I've tested and it works for me, so please let me know if you see otherwise.
$username = 'localadmin'
$password = ConvertTo-SecureString 'PlaintextIsaNoNo' -AsPlainText -Force
$serverlist = Import-Csv '.\Windows Servers.csv'
$cred = New-Object System.Management.Automation.PSCredential (".\$username", $password)
Try
{
Invoke-Command -ComputerName $serverlist.ip -ScriptBlock {
Write-Host Sucessfully connected to $env:computername as $(whoami) -ForegroundColor Green
} -Credential $cred
}
Catch
{
$PSItem.Exception.Message
}
Update
Removing the previous answer because of our threads below.
Here's your issue.
You cannot do things the way you are trying because of how Windows security boundaries proper works and you'd also have to set up PowerShell Remoting properly.
You have not said whether you are in a domain or in a Workgroup scenario. Hence the previous statement about setting up PSRemoting properly.
Because you are using a local account, this is like using Workgroup mode in PSRemoting and that requires additional settings to be in place. See the details regarding 'TrustedHosts'.
about_Remote_Troubleshooting - PowerShell | Microsoft Docs
Doing a logon to a remote host as you are trying will popup the Logon dialog (Gina) by design.
On your admin machine, try it this way... (tested and validated as working)
### PSRemoting - using local machine accounts
$Creds = Get-Credential -Credential $env:USERNAME
Enable-PSRemoting -SkipNetworkProfileCheck -Force
Get-Item -Path 'WSMan:\localhost\Client\TrustedHosts'
Set-Item -Path 'WSMan:\localhost\Client\TrustedHosts' -Value '*' -Force
Get-Item -Path 'WSMan:\localhost\Client\TrustedHosts'
$TargetList = 'Lab01','172.0.0.10'
$TargetList |
ForEach-Object {
"Validating logon for $($Creds.Username) on target $PSItem"
Try
{
New-PSSession -ComputerName $PSItem -Credential $Creds
Get-PSSession | Remove-PSSession
}
Catch {$PSItem.Exception.Message}
}
Set-Item -Path 'WSMan:\localhost\Client\TrustedHosts' -Value '' -Force
# Results
<#
Validating logon for postanote on target Lab01
Id Name ComputerName ComputerType State ConfigurationName Availability
-- ---- ------------ ------------ ----- ----------------- ------------
26 WinRM26 Lab01 RemoteMachine Opened Microsoft.PowerShell Available...
Validating logon for postanote on target 172.0.0.10
27 WinRM27 172.0.0.10 RemoteMachine Opened Microsoft.PowerShell Available...
#>
Update
Here's a tweak to Doug and my approach and dropping the need to have PSRemoting proper to be involved at all. Again, tested and validated. Except for the one time prompt for the creds, no embedded plain text passwords/files/registry/CredMan storage and retrieval, etc needed.
$Creds = Get-Credential -Credential $env:USERNAME
$TargetList |
ForEach-Object {
"Processing hostname $PSItem"
Try
{
$cred = New-Object System.Management.Automation.PSCredential ("$PSItem\$($Creds.UserName)", $Creds.Password)
(Get-HotFix -ComputerName $PSItem -Credential $cred)[0]
}
Catch {$PSItem.Exception.Message}
}
# Results
<#
Processing hostname Lab01
Source Description HotFixID InstalledBy InstalledOn
------ ----------- -------- ----------- -----------
Lab01 Update KB4576478 NT AUTHORITY\SYSTEM 9/10/2020 12:00:00 AM
Processing hostname 172.0.0.10
Lab01 Update KB4576478 NT AUTHORITY\SYSTEM 9/10/2020 12:00:00 AM
#>
• Tip: Work Remotely with Windows PowerShell without using Remoting or
WinRM
https://technet.microsoft.com/en-us/library/ff699046.aspx
Some cmdlets have a –ComputerName parameter that lets you work with a remote
computer without using Windows PowerShell remoting. This means you can use
the cmdlet on any computer that is running Windows PowerShell, even if the
computer is not configured for Windows PowerShell remoting. These cmdlets
include the following:
• Get-WinEvent
• Get-Counter
• Get-EventLog
• Clear-EventLog
• Write-EventLog
• Limit-EventLog
• Show-EventLog
• New-EventLog
• Remove-EventLog
• Get-WmiObject
• Get-Process
• Get-Service
• Set-Service
• Get-HotFix
• Restart-Computer
• Stop-Computer
• Add-Computer
• Remove-Computer
• Rename-Computer
• Reset-ComputerMachinePassword
I want a Powershell script that will allow:
Deletion of keys in the Windows registry corresponding to 10.x.x.x (if not present proceed to step 2)
Delete an entry in the Windows Vault : 10.x.x.x (if not present proceed to step 3)
Add a printer by name \ 10.x.x.x \ Printer + Authentication on print server (Domain \ User with password by memorizing the information in the Windows Vault)
For my part I tried this :
Write-Output "===========Deleting entry Windows Vault==========="
cmdkey /delete:10.x.x.x
Start-Sleep -s 5
Write-Output "===========Deleting printer==========="
Remove-Printer -Name "\10.x.x.x\PrinterName"
Start-Sleep -s 5
Write-Output "===========Deleting old entries Windows registry==========="
Get-Childitem -path hkcu:\ -recurse -ErrorAction SilentlyContinue |
Where-Object {$.Name -like "*10.x.x.x*"} |
ForEach-Object {Remove-Item $.FullName}
Start-Sleep -s 5
Write-Verbose "===========Configuration printer==========="
$cred = get-credential "DomainName\" $domain = "."
$user = $cred.UserName
$PrinterPath = "\10.x.x.x\PrinterName"
$net = new-Object -com WScript.Network
$pwd = $cred.Password
$net.AddWindowsPrinterConnection($PrinterPath)
Start-Sleep -s 5
It is not very conclusive...
In the example above, is it possible to adapt the script ?
For example, if you want to delete the registry keys and the Windows Vault entry, if the executed command does not find the requested information, make sure you do nothing and proceed to the next step.
Would it be possible to have help please ?
Thank you !
Have a good day.
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.
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') ; $_ }
)
}