Reputation: 27
I want to make a script that which if I open it on my machine (as administrator), it run a command on different machines (without entering credentials). I used to make a script that restarts services. I did it like this:
$out = "lokation\output_NoService.txt"
Clear-Content $out
$data = Get-Content "lokation\input_PCs.txt"
foreach ($line in $data) {
$computer = $line.Split(":")[0]
$ServiceName = $line.Split(":")[1]
if ($sObj = Get-Service-COmputerName $Computer -Name $ServiceName -ErrorAction SilentlyContinue) {
Restart-Service -InputObj $sObj
Write-Host "Restarting $ServiceName on $computer"
} else {
Write-Host "$ServiceName is not running on $computer"
Add-Content $out "$computer;$ServiceName"
}
}
input_PCs.txt
contained the machine names and the name of the service to be restarted.
eg: PCName:Servicename
This is how I want to scan PCs from a txt and run command on the machines that are in txt.
For example, such a command.
run example.exe + paramters from c drive.
Ergo: C:\example.exe -paramaters
Upvotes: 0
Views: 136
Reputation: 61013
The Restart-Service does not have a -ComputerName
parameter to manage services on remote machines. To use that cmdlet remotely, you can use
Invoke-Command -Computername $computer -ArgumentList $ServiceName -ScriptBlock {
param($service)
Restart-Service -Name $service}
Lee_Daily pointed out that the object returned from Get-Service
contains a MachineName
property. When this object is used in a following Restart-Service
as InputObject
parameter, the cmdlet uses that MachineName property to restart the service on that remote machine.
As I have never thought of that, I've been testing with it and indeed it does work that way.
Your code (with corrected ComputerName parameter) should therefore work as intended, provided you have permissions on the remote computers to do so:
$out = "lokation\output_NoService.txt"
Clear-Content $out
$data = Get-Content "lokation\input_PCs.txt"
foreach ($line in $data) {
$computer, $serviceName = $line -split ':', 2
if ($sObj = Get-Service -ComputerName $computer -Name $serviceName -ErrorAction SilentlyContinue) {
Restart-Service -InputObj $sObj
Write-Host "Restarting $serviceName on $computer"
}
else {
Write-Host "Service '$serviceName' cannot be found on computer '$computer'"
Add-Content -Path $out -Value "$computer; $serviceName"
}
}
This however does not let you add user credentials because Get-Service
does not provide a Credentials
object.
If you need to use different credentials, you can do like below:
$credential = Get-Credential
$out = "lokation\output_NoService.txt"
Clear-Content $out
$data = Get-Content "lokation\input_PCs.txt"
# unravel the $credential object into plain username and password:
$user = $credential.GetNetworkCredential().UserName
$password = $credential.GetNetworkCredential().Password
foreach ($line in $data) {
$computer, $serviceName = $line -split ':', 2
# create a connection to the remote machine using credentials
net use "\\$computer\c$" $password /USER:$user
if ($sObj = Get-Service -ComputerName $computer -Name $serviceName -ErrorAction SilentlyContinue) {
Restart-Service -InputObj $sObj
Write-Host "Restarting $serviceName on $computer"
}
else {
Write-Host "Service '$serviceName' cannot be found on computer '$computer'"
Add-Content -Path $out -Value "$computer; $serviceName"
}
}
The last option I can think of is to use the Get-WmiObject
cmdlet which for convenience I've wrapped in a custom function:
function Restart-RemoteService {
[CmdletBinding()]
Param(
[Parameter(Position = 0,Mandatory = $true, ValueFromPipelineByPropertyName = $true,ValueFromPipeline = $true)]
[string]$ComputerName,
[Parameter(Position = 1,Mandatory = $true)]
[string]$ServiceName,
[Parameter(Position = 2,Mandatory = $true)]
[string]$ErrorFile
[Parameter(Mandatory = $false)]
[System.Management.Automation.PSCredential]$Credential
)
$wmiParams = @{
'Class' = 'Win32_Service'
'ComputerName' = $ComputerName
'Filter' = "Name='$ServiceName'"
}
if ($Credential) { $wmiParams['Credential'] = $Credential }
$svc = Get-WmiObject @wmiParams
if (!$svc) {
Write-Warning "Service '$ServiceName' cannot be found on computer '$ComputerName'"
Add-Content -Path "lokation\output_NoService.txt" -Value "$ComputerName; $ServiceName"
}
if ($svc.State -eq 'Running') {
Write-Verbose "Stopping service '$ServiceName' on computer '$ComputerName'"
[void]$svc.StopService()
$maxWait = 20
do {
Start-Sleep -Milliseconds 100
$maxWait--
if ($maxWait -le 0) {
Throw "Could not stop service '$ServiceName'"
}
} while (( Get-WmiObject @wmiParams).State -ne 'Stopped')
}
Write-Verbose "Starting service '$ServiceName' on computer '$ComputerName'"
[void]$svc.StartService()
}
Your code could then look like this:
$out = "lokation\output_NoService.txt"
Clear-Content $out
$data = Get-Content "lokation\input_PCs.txt"
foreach ($line in $data) {
$computer, $serviceName = $line -split ':', 2
# if needed, you can pass Credentials too
Restart-RemoteService -ComputerName $computer -ServiceName $serviceName -ErrorFile $out -Verbose
}
Upvotes: 2
Reputation: 156
Something like this ?
foreach($line in $data) {
$computer = $line.split(":")[0]
Invoke-Command -ComputerName $computer -ScriptBlock {C:\example.exe -parameters}
}
In order for this to work, example.exe will need to be present on your remote computers. If this isn't the case yet, before the above, you can add something like this
if (!(Test-Path -Path "\\$computer\c$\example.exe")) {
Copy-Item -Path "X:\path_to_file\example.exe" -Destination "\\$computer\c$" -Force
}
And if you don't want it to request any credentials, make sure your Powershell session is started under your admin user that has access to all the computers' c-drives.
Upvotes: 0