How to enable remote in Windows Management Instrumentation (WMI) on authenticated users on remote computers programmatically through CMD or POWERSHELL?
When I try to write some script then it throws errors like 'access is denied' and 'RPC server is unavailable '.
And when I enable PS Remoting then it is working.
$servers = Get-Content file name
foreach( $server in $servers) {
Write-host "Processing Server $Server"
Get-WmiObject -Class Win32_Product -Impersonation 3 -Credential username -ComputerName $Server | select __SERVER, Name, Version, InstallDate -ErrorAction SilentlyContinue
}
What I want to achieve is, I want to do this through script only not manually. Can anyone please explain?
Related
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.
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 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.
I'm running a script to gather info for running apps on a terminal server. I would like to get the remote ComputerName of the user running the app but I'm only able to get the ComputerName of the Terminal Server instead. Any idea on how I could achieve that? Here's the part of my script to gather the info.
Get-WmiObject Win32_Process -ComputerName MYTSSERVER | Where-Object {
$_.mainwindowhandle -ne 0 -and
$_.Name -eq "WINWORD.exe"
} | Select-Object CSName, ProcessName
You can't get the client hostname or IP address from the Word process, because that process is running locally on the Remote Desktop server. Remote Desktop login information is recorded in the eventlog, so you need to extract it from there. However, you need the remote username for finding the correct logon session, so you still need to run the WMI query first.
Example for a single Word process running on the Remote Desktop server:
$server = '...'
$p = Get-WmiObject Win32_Process -Computer $server -Filter "Name='WINWORD.exe'"
$u = $p.GetOwner().User
$fltr = "*logon type:`t`t`t10*new logon:*account name:`t`t$u"
$addr = Get-EventLog -Computer $server -LogName Security -InstanceId 4624 -Message $fltr -Newest 1 |
Where-Object { $_.Message -match 'source network address:\s*(.*)' } |
ForEach-Object { $matches[1] }
The filter for the eventlog query matches the most recent Remote Desktop login (event ID 4626, logon type 10) of the user who started the Word process (account name:`t`t$u).
That will just give you the client's IP address, though. If you require the hostname you need to resolve it via a reverse DNS lookup or get it from the host itself with another WMI query:
$hostname = Get-WmiObject Win32_OperatingSystem -Computer $addr |
Select-Object -Expand CSName
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') ; $_ }
)
}