I need to remotely run powershell commands on multiple windows servers using SysInternal tools but I tried a lot but it seems not to work . Any help is highly appreciated .
Servers.csv (file content)
10.10.10.100
10.0.0.111
Code :
$List = Import-CSV -Path "C:\Users\javed\Desktop\New\servers.csv"
foreach ($entry in $List) {
if (test-Connection -Cn $($entry.Name) -quiet) {
& C:\Users\javed\Downloads\PsTools\psexec.exe \\$($entry.Name) -u "$($entry.Name)\Admin" -p 'P#ssword' -accepteula cmd /c " HostName >> C:\Users\javed\Desktop\New\Script.log"
} else {
"$computer is not online" >> C:\Users\javed\Desktop\New\Script.log
}
}
Output :
PS C:\Users\javed> C:\Users\javed\Desktop\New\Change-NetworkRoute.ps1
PsExec v2.2 - Execute processes remotely
Copyright (C) 2001-2016 Mark Russinovich
Sysinternals - www.sysinternals.com
psexec.exe : The handle is invalid.
At C:\Users\javed\Desktop\New\Change-NetworkRoute.ps1:26 char:9
+ & C:\Users\javed\Downloads\PsTools\psexec.exe \\$($entry.Name) -u "$(
...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (The handle is invalid.:String) [], Re
moteException
+ FullyQualifiedErrorId : NativeCommandError
Connecting to 10.10.10.100...Couldn't access 10.10.10.100:
Connecting to 10.10.10.100...
PS C:\Users\javed>
Apparently your command line isn't completely evaluated, note that PSExec shows \\$($entry.Name) in the error rather then the actual name.
The correct way to troubleshoot this is to put the first put the command is a variable, show the variable, and then execute it:
$CommandLine = "C:\Users\javed\Downloads\PsTools\psexec.exe \\$($entry.Name) -u ..."
Write-Host $CommandLine # Confirm that this is the command line you expect
if (test-Connection -Cn $($entry.Name) -quiet) {
&$CommandLine
} else {
Anyhow, it is a bad idea to use an external command line as PSExec including credentials to achieve something like this. Instead, I would use WMI to retrieve the actual name of the servers. Something like this:
$SPAdmin = "$($entry.Name)\Admin"
$Password = "P#ssword" | convertto-securestring
$Credential = new-object -typename System.Management.Automation.PSCredential -argumentlist $SPAdmin, $Password
$Computer = Get-WmiObject -Class Win32_Computersystem -ComputerName $entry.Name -Credential $Credential
Write-Host $Computer.Name
Related
I created a short PowerShell scrtipt in order to import a .reg file (an ODBC) to another server session.
I faced to this warning/issue.
The message is this (below):
The operation completed successfully.
+ CategoryInfo : NotSpecified: (The operation completed successfully.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
+ PSComputerName : MYSERVERNAME
NotSpecified: (:) [], RemoteException
The script, install without problem the .reg file, but constantly I get the message.
This is my code:
#PARAMETERS - Variables
$Serverlist = Get-Content C:\ServerList.txt
try
{
Foreach ($ServerName in $Serverlist)
{
$session = New-PSSession -ComputerName $servername
Write-Host -Foregroundcolor Green "Copying ODBC Driver for $servername"
$copy_cmd = "C:\MYFILE.reg"
Copy-Item $copy_cmd \\$servername\C$\ -recurse -force;
Write-Host -Foregroundcolor Green "ODBC Successfully copied on $servername"
#$session = New-PSSession -ComputerName $servername
Invoke-Command -Session $session -ScriptBlock {
#Start-Process
reg import C:\CopiedFile.reg #This line generate the message
Write-Host -Foregroundcolor Green "ODBC was installed
}
catch
{
Write-Host "ERROR" -Foregroundcolour Red
exit
}
I tried to incapsulate the Invoke-Command or reg import in to try - catch statement, but the message still appear. I used another command, instead reg import, but the nothing change.
I can use this command line, but I would like to catch the error.
Write-Host -Foregroundcolor Green "ODBC is installed " } ##-ErrorAction SilentlyContinue
There is any way to get the eventually error or handle the message.
Thanks in advance.
If the try block does not generate a terminating error, it will not move into the Catch block. This is controlled by -ErrorAction parameter. So you can set
Invoke-Command Session $session -ScriptBlock {} -ErrorAction Stop
This will cause the Invoke-Command Cmdlet to generate terminating errors(if any error occurs) allowing catch block to execute.
I was trying to create a simple script that would go to a server and get the acl details of a folder. I tested the command:
Invoke-command -Computername Servername -ScriptBlock {(Get-Acl "\\Server\Folder\user folders").access | ft- auto}
This worked ok. However when I was trying to put it into a script that would allow me to enter the path in via a variable I always get:
Cannot validate argument on parameter 'Path'. The argument is null or empty. Supply an argument that is not null or empty and then
try the command again.
+ CategoryInfo : InvalidData: (:) [Get-Acl], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetAclCommand
Here is my script:
#get folder permissions from remote computer
$serverName = read-host "Please enter the name of the target server"
$folderPath = "\\server_name\Folder\user folders"
#read-host "Please enter the full path to the target folder"
Invoke-command -ComputerName $serverName -ScriptBlock {(get-acl $folderPath).access | ft -wrap}
Its probably something very simple, but I'd appreciate the help.
The issue is because you are trying to use the $folderPath variable, but on the remote computer that variable does not exist.
You will need to pass it through as an argument. There are multiple ways to do so, two such ways are below:
# Add desired variable to ArgumentList and define it as a parameter
Invoke-command -ComputerName $serverName -ArgumentList $folderPath -ScriptBlock {
param($folderPath)
(get-acl $folderPath).access | ft -wrap
}
OR
# In PS ver >= 3.0 we can use 'using'
Invoke-command -ComputerName $serverName $folderPath -ScriptBlock {(get-acl $using:folderPath).access | ft -wrap}
I have a VM running Windows Server 2012.And some intended services on it. I want to change a configuration file on this VM machine remotely from my desktop pc.
Currently I change this configuration file by mapping the C: drive of the remote server and then changing the file. Now this blocks me from changing multiple servers as I can't map multiple server c: simultaneously to same drive. Also, mapping hundreds of drives wouldn't be ideal.
The way I am changing the file by mapping drive is:
$password = <password text> | ConvertTo-SecureString -asPlainText -Force
$username = "admin"
$credential = New-Object System.Management.Automation.PSCredential($username,$password)
net use Z: \\$ipAddress\C$ <password> /user:admin type 'z:\Program Files\lalaland\Data\Settings.xml'
(Get-Content 'z:\Program Files\lalaland\Data\Settings.xml') | ForEach-Object { $_ -replace $oldString, $newString } | Set-Content 'z:\Program Files\lalaland\Data\Settings.xml'
type 'z:\Program Files\lalaland\Data\Settings.xml'
net use z: /delete
Therefore I searched for a better option and found this script at https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Replace-String-58fbfa85 but it doesn't work for me.
I am using this script as :
.\Replace-StringInFile.ps1 -ComputerName <RemoteComputerHostName> -TargetPath 'C:\Program Files\lalaland\Data' -Fil
eName Settings.xml -Replace $oldString -ReplaceWith $newString -Credential (Get-Credential)
when I run the command credential window pops up asking for username and password. I enter the username and password that I used for mapping the drive, but it throws the following error:
New-PSSession : [<RemoteHostName>] Connecting to remote server <RemoteHostName> failed with the following error message : The user name or password is incorrect. For more information, see the
about_Remote_Troubleshooting Help topic.
At D:\workspace\Replace-StringInFile.ps1:84 char:14
+ ... $Session = New-PSSession -ComputerName $Computer -Credential $Creden ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotingTransportException
+ FullyQualifiedErrorId : LogonFailure,PSSessionOpenFailed
what I don't understand is when I map the drive with the same credentials it works fine but using the other script, which internally uses New-PSSession doesn't work.
Any idea ?
I was able to do this using the following cmdlet:
function Edit-RemoteFileStringReplace
{
[cmdletbinding()]
param([Parameter(Mandatory=$true)][ValidateScript({$_ -match [IPAddress]$_ })][string]$Address,
[Parameter(Mandatory=$true)][string]$FilePath,
[Parameter(Mandatory=$true)][string]$Replace,
[Parameter(Mandatory=$true)][string]$ReplaceWith,
[Parameter(Mandatory=$true)][System.Management.Automation.PSCredential]$Credential
)
$DriveLetter=""
$FilePathWithoutDriveLetter=""
$DriveName = $Address.replace('.','x')
$DriveName = "PSDrive_$DriveName"
$DriveLetter = (Get-DriveLetterFromPath -Path $FilePath).Substring(0,1)
$FilePathWithoutDriveLetter = Remove-DriveLetterFromPath -Path $FilePath
$MappedFilePath="$DriveName" + ":$FilePathWithoutDriveLetter"
New-PSDrive -Name $DriveName -PSProvider FileSystem -Root \\$Address\$DriveLetter$ -Credential $Credential -ErrorAction Stop
(Get-Content $MappedFilePath) | ForEach-Object { $_ -replace $Replace, $ReplaceWith } | Set-Content $MappedFilePath
Remove-PSDrive -Name $DriveName
}
I have a Power Shell script that sits on a VM and creates a PSSession to a physical machine and runs a batch file. Running the batch file directly in the physical machine works as expected, however when I try running it remotely from the VM is it throwing up error.
Power Shell:
$Username = "Domain\User"
$Password = ConvertTo-SecureString "Password*" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ($Username, $password)
$session = New-PSSession -ComputerName "K2" -Credential $cred
Enter-PSSession -Session $session
Invoke-Command -ComputerName "K2" -ScriptBlock {
Set-Location 'C:\Program Files\SmartBear\ReadyAPI-2.4.0\Go4Schools Tests\Test Runner'
& '.\PSTR.bat'
}
Exit-PSSession
Get-PSSession | Remove-PSSession
Batch File:
"C:\Program Files\SmartBear\ReadyAPI-2.4.0\bin\testrunner.bat" -A -j "-fC:\Users\administrator.HYPERSPHERIC\Desktop\Go4Schools Tests\Test Results" "-RJUnit-Style HTML Report" -FXML -EDevTest "C:\Users\administrator.HYPERSPHERIC\Desktop\Go4Schools Tests\Project Saves\SoapUI_Mobile_API_Project.xml"
Error:
PS C:\> C:\Users\administrator.HYPERSPHERIC\Desktop\PS_TR.ps1
C:\Program Files\SmartBear\ReadyAPI-2.4.0\Go4Schools Tests\Test Runner>"C:\Program Files\SmartBear\ReadyAPI-2.4.0\bin\testrunner.bat" -A -j "-fC:\Program Files\SmartBear\ReadyAPI-2.4.0\Go4Sch
ools Tests\Test Results" "-RJUnit-Style HTML Report" -FXML -EDevTest "C:\Program Files\SmartBear\ReadyAPI-2.4.0\Go4Schools Tests\Project Saves\SoapUI_Mobile_API_Project.xml"
Error: Could not find or load main class com.smartbear.ready.cmd.runner.pro.SoapUIProTestCaseRunner
+ CategoryInfo : NotSpecified: (Error: Could no...oTestCaseRunner:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
+ PSComputerName : K2
Ready API and soapUI use some environment variables in testrunner.bat to set up dependencies and the like. So, whenever I've executed testrunner.bat with a script from outside of the application, I've always CD'ed to the bin directory first:
cd <Ready API installation directory>\bin
./testrunner.bat ...
What I'm trying to achieve is to redirect standard output and standard error from running psexec -nobanner \\1.2.3.4 net localgroup Administrators. When I redirect standard output, the result of the command changes. Something about capturing standard output, in any of the ways I've tried, seems to change the result. I'd like to know why and I'd like to get it working.
In PowerShell, if I run this:
psexec -nobanner \\1.2.3.4 net localgroup Administrators
I see this:
Couldn't access 1.2.3.4:
The trust relationship between this workstation and the primary domain failed.
(Where Couldn't access 1.2.3.4: ends up, I briefly see Connecting to 1.2.3.4... and something else flashes past too quickly to see.)
If I try to capture the output, using this:
$output = psexec.exe -nobanner \\1.2.3.4 net localgroup Administrators
I see:
Couldn't access 1.2.3.4:
The handle is invalid.
(As above, where Couldn't access 1.2.3.4: ends up, I briefly see Connecting to 1.2.3.4....)
I realise that I need to redirect the error stream - that's where I started. But I can't even get the standard output without it changing. This question is about whey the output changes as soon as I try to capture it.
UPDATE
I've just noticed that if I run the same command (that works above in the PowerShell host)
psexec -nobanner \\1.2.3.4 net localgroup Administrators
in PowerShell ISE, I get the same error as below:
psexec : The handle is invalid.
At line:1 char:1
+ psexec -nobanner \\1.2.3.4 net localgroup Administrators
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (The handle is invalid.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Connecting to 1.2.3.4...Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
So, why does running it in ISE give different output from the normal host?
Other things I've tried:
1. Start-Process
Start-Process -Wait -PSPath 'C:\Windows\PSTools\psexec.exe' -NoNewWindow `
-ArgumentList "-nobanner \\$ip net localgroup Administrators" `
-RedirectStandardError '.\tempError.log' -RedirectStandardOutput '.\tempOutput.log'
'O:'
Get-Content .\tempOutput.log
'E:'
Get-Content .\tempError.log
which gives:
O:
E:
The handle is invalid.
Connecting to 1.2.3.4...
Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
2. Redirect Standard Output only
psexec -nobanner \\1.2.3.4 net localgroup Administrators > psexec.log
which gives:
Couldn't access 1.2.3.4:
The handle is invalid.
[psexec.log is empty because I'm only redirecting Standard Output and PsExec writes its own messages to Standard Error.]
3. Redirect Standard Error only
I've noticed something else odd about this: if I only redirect Standard Error, it works (PsExec works, the command fails, the output is redirected):
psexec -nobanner \\1.2.3.4 net localgroup Administrators 2> psexec.log
The file psexec.log contains:
psexec : The trust relationship between this workstation and the primary domain failed.
At line:1 char:1
+ psexec -nobanner \\1.2.3.4 net localgroup Administrators 2> ps ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (The trust relat... domain failed.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Connecting to 1.2.3.4...
Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
4. Redirect all
psexec.exe -nobanner \\1.2.3.4 net localgroup Administrators *>&1 | Set-Variable -Name Output
which gives this:
psexec : The handle is invalid.
At line:1 char:1
+ psexec -nobanner \\1.2.3.4 net localgroup Administrators *>&1 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (The handle is invalid.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Connecting to 1.2.3.4...Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
I repeated some of the above using cmd:
5. Redirect Standard Output only, with cmd
cmd /c C:\Windows\PsTools\psexec -nobanner \\1.2.3.4 net localgroup Administrators --% 1> psexec.log
gives:
Couldn't access 1.2.3.4:
The handle is invalid.
(directly to the console).
6. Redirecting Standard Error only, with cmd
cmd /c C:\Windows\PsTools\psexec -nobanner \\1.2.3.4 net localgroup Administrators --% 2> psexec.log
gives (in psexec.log):
The trust relationship between this workstation and the primary domain failed.
Connecting to 1.2.3.4...
Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
psexec.exe is a simple executable which writes output to stdout (standard output) and stderr (standard error). So, to capture the output use:
psexec.exe > stdout.txt to capture sent to stdout.
psexec.exe 2> sterr.txt to capture output sent to stderr.
psexec.exe > combined.txt 2>&1 to capture both stdout and stderr in a single file.
Interestingly, psexec writes the default message to stderr - usually this would be stdout. So what you see in your shell when running psexec is actually the error output and would need to be captured using 2>.
This returns all the output from myScript.ps1 to the $result variable without all the other junk surrounding PsExec. This is assuming you can get the ps1 file copied to the target machine.
$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -file c:\myScript.ps1 2> $null
In line PS command version
$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -command "get-process" 2> $null
Command line version
$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\cmd.exe /c "ipconfig" 2> $null
This is a full script to run and collect report data from remote systems using psexec and runspaces. It is much, much faster than start-job and uses far less memory.
################################################################################################
# PSEXEC_Command_Runspaces
# Uses PSEXEC to run a command on multiple computers. Useful when PS remoting is not enabled
# but you have admin rights.
#
# Requires RSAT tools for the get-adcomputer command. You could import a csv or other method
# to obtain a list of computers instead.
################################################################################################
# Parameters
################################################################################################
#The list of computers to process
$pclist = get-adcomputer -filter "OperatingSystem -eq 'Windows 10 Enterprise' -and Name -like 'RC-*'" -properties DNSHostName | select -ExpandProperty DNSHostName
$Throttle = 500 #number of concurrent runspaces. The higher this is, the more memory is needed for the runspaces. 500 takes less than 1GB for this script.
################################################################################################
# This is the script that will run in each runspace.
################################################################################################
$scriptblock = {
Param (
$nothing, #this empty variable seems to be required because if you pass a single variable, it gets corrupted.
$PC
)
if (test-connection $PC -Count 1 -ea SilentlyContinue) {
# Create script folders on remote computer and copy report script.
md \\$PC\c$\wsapps -ea SilentlyContinue
md \\$PC\C$\wsapps\QA -ea SilentlyContinue
copy 'C:\tools\Powershell\Review Center\PullBIOSandLoggedOnUser.ps1' "\\$pc\c$\wsapps\qa" -Force
# Run ps exec and collect output
# 2> $null gets rid of the "starting service and other junk from the PSexec output
$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -file c:\wsapps\qa\PullBIOSandLoggedOnUser.ps1 2> $null
#remote script file from remote machine. You could also remove folders if apropriate here.
remove-item \\$pc\C$\wsapps\QA\PullBIOSandLoggedOnUser.ps1 -ea SilentlyContinue
#Parse results from single line of output. PS does not return muliple lines of output from PSEXEC when wrapped in a job or runspace.
$parts = $result.split(",")
$outobj = New-Object psobject
$outobj | Add-Member ComputerName $PC
$outobj | Add-Member WindowsVersion ($parts[1].split(":")[1])
$outobj | Add-Member BiosVersion ($parts[2].split(":")[1])
$outobj | Add-Member LoggedOnUser ($parts[3].split(":")[1])
$outobj | Add-Member IPAddress ($parts[4].split(":")[1])
}
else { #report object indicating offline status.
$outobj = New-Object psobject
$outobj | Add-Member ComputerName $PC
$outobj | Add-Member WindowsVersion "Offline"
$outobj | Add-Member BiosVersion "?"
$outobj | Add-Member LoggedOnUser "?"
$outobj | Add-Member IPAddress "?"
}
write-output $outobj
}
################################################################################################
# Main Logic
# Runspaces are much, much faster than start-job and use far less memory
# 260 computers took 4.5GB memory and > 20 minutes to process with start- job
# 260 computers took 260MB memory and < 1 minute to process with runspaces.
################################################################################################
$RunspacePool = [runspacefactory]::CreateRunspacePool(1,$Throttle)
$RunspacePool.Open()
#RSArrayList contains a link to each runspace. Needed to track progress and obtain results later
$RSArrayList = New-Object System.Collections.ArrayList
#Loop through each PC in the list, creating runspaces. The runspace pool is used for multiple parallel spaces with rate control.
foreach ($PC in $PClist) {
$PowerShell = [powershell]::Create()
[void]$PowerShell.AddScript($scriptblock)
[void]$powershell.AddArgument("").AddArgument($PC) #extra argument to avoid single argument corruption bug.
$PowerShell.RunspacePool = $RunspacePool
$ThisRS = New-Object psobject
$ThisRS | Add-Member Computer $PC
$ThisRS | Add-Member PSInstance $PowerShell
$thisRS | Add-Member Space ($PowerShell.BeginInvoke()) #execution starts here.
$RSArrayList += $thisRS
write-host "Adding $PC"
}
################################################################################################
#Progress bar to track when jobs are finished.
write-host "waiting for runspaces to finish"
while (($RSArrayList.space.iscompleted -eq $false).count -gt 0) {
$Done = $RSArrayList.count - ($RSArrayList.space.iscompleted -eq $false).count
if ($Done -eq 0) {$percentComplete = 0}
else {$percentComplete = $Done / $RSArrayList.count * 100}
write-progress -Activity "Waiting for jobs to complete" -Status (($RSArrayList.count - $Done).ToString() + "Left") -PercentComplete $percentComplete
sleep -Seconds 1
}
################################################################################################
#collecting results and creating report object
write-host "Processing Results"
$Report = New-Object System.Collections.ArrayList
foreach ($RS in $RSArrayList) {
$Report += $RS.PSInstance.EndInvoke($RS.Space) #equivilant to "receive-job"
$RS.PSInstance.Dispose() # frees up memory.
}
$Report | ft
This is the report collection script that is run on the remote system
################################################################################################
# Looks up the computer name, Windows Version, BIOS version, logged on user, and IP address of the computer.
# Designed to be called by start-job or runspaces (much faster).
################################################################################################
$computername = $env:COMPUTERNAME
$WindowsVersion = (Get-WmiObject win32_OperatingSystem).BuildNumber.toString()
$BiosVersion = (Get-WmiObject Win32_BIOS).Name
$IPAddress = "No 10 range IP"
$addr = (Get-NetIPAddress -AddressFamily IPv4).ipaddress | where {$_ -like '10.*'}
if ($addr) {$IPAddress = $addr}
$LoggedOnUser = "None"
$quser = (quser.exe 2> $null | select-string "console").line
if ($quser) {$LoggedOnUser = $quser.Substring(1,$quser.IndexOf(" ",1)-1)}
# For whatever reason, PS will not return multiple lines of output from PSexec when run under start-job or runspaces. This was the workaround.
"Computername:$computername,WindowsVersion:$WindowsVersion,BIOSVersion:$BIOSVersion,LoggedOnUser:$LoggedOnUser,IPAddress:$IPAddress"