Invoke-Command does not kill process on remote machine - powershell

Before deployment, I'm trying to kill processes that lock files using PowerShell Invoke-Command
This is my code:
$password = ConvertTo-SecureString "password" -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PsCredential("Admin",$password)
$scriptBlock = {Get-Process | Where-Object { $_.MainWindowTitle -like 'MyApp*'} | Stop-Process}
Invoke-Command -computername Agent1 -Credential $credentials -scriptblock $scriptBlock
Unfortunately it does not do anything and no errors are thrown.
On the machine, this works fine:
Get-Process | Where-Object { $_.MainWindowTitle -like 'MyApp*'} | Stop-Process

As described above create a PS session object:
$ErrorActionPreference = "Stop"
$password = ConvertTo-SecureString "password" -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PsCredential("Admin",$password)
$scriptBlock = {
$process = Get-Process
$process | Where-Object { $_.MainWindowTitle -like 'MyApp*'} | Stop-Process}
$process
}
$session = New-PsSession -ComputerName "Agent1" -Credentials $credentials
$remoteProcess = Invoke-Command -Session $session -Credential $credentials -scriptblock $scriptBlock
$remoteProcess | format-table
Above code will also return you a list processes running on the remote host. Based on $remoteProcess you'll see if the process to kill was running or not. I also set the ErrorActionPreference to stop which forces above code to stop on the first error (in case of the session could not be created).
Hope that helps

Related

PowerShell cannot use New-PSSessions right after Invoke-CimMethod (The runspace state is not valid for this operation)

The two code below works independently, however, they cannot work in the same script. I really need help, there's got to be something incompatible.
The first part of my script uses Invoke-CimMethod to Enable-PSRemoting, and it works.
Variables
$hostname = 'PC1'
$Session = New-PSSession $hostname
$DestinationPath = "C:\windows\temp"
Part 1
$SessionArgs = #{
ComputerName = $hostname
Credential = $credential
SessionOption = New-CimSessionOption -Protocol Dcom
}
$MethodArgs = #{
ClassName = 'Win32_Process'
MethodName = 'Create'
CimSession = New-CimSession #SessionArgs
Arguments = #{
CommandLine = "powershell Start-Process powershell -ArgumentList 'Enable-PSRemoting -Force'"
}
}
Invoke-CimMethod #MethodArgs
The second part of my code works if the first part above is not present. It is to create a TEMP folder, and then copy an entire folder into TEMP.
Part 2
Invoke-Command -Session $Session -ScriptBlock { Param($Destination) New-Item -Path $Destination -ItemType Directory -ErrorAction SilentlyContinue} -ArgumentList $DestinationPath
Copy-Item -Path "\\shared\folder\foo\bar" -ToSession $Session -Destination "C:\windows\temp\" -recurse -force
Error
Copy-Item : The runspace state is not valid for this operation.
What's weird is I've inserted the Invoke-CimMethod to many other scripts that does similar things and it works fine, like for example
Example of it working
$env:hostname
$env:process
$SessionArgs = #{
ComputerName = $env:hostname
Credential = $credential
SessionOption = New-CimSessionOption -Protocol Dcom
}
$MethodArgs = #{
ClassName = 'Win32_Process'
MethodName = 'Create'
CimSession = New-CimSession #SessionArgs
Arguments = #{
CommandLine = "powershell Start-Process powershell -ArgumentList 'Enable-PSRemoting -Force'"
}
}
Invoke-CimMethod #MethodArgs
$session = New-PSSession $env:hostname
ipconfig
Invoke-Command -Session $session -ScriptBlock {param($process) Stop-Process -ProcessName $process -Force} -ArgumentList $env:process
$Session | Remove-PSSession
Please help! I've tried everything, I even tried Get-CimSession | Remove-CimSession but that didn't work. Why is it incompatible?
I was able to fix this issue by putting the variable
$Session = New-PSSession $hostname
Right before Invoke-Command because I think when I sent enable-pssession it resets the connection.

Restart machine using powershell script

I am running the below code but the restart is not working. My intention was to run restart command parallelly on all remote machines at once.
$YourFile = gc "machinelst.txt"
$username = "user1"
$password = "pass1"
$secpw = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object Management.Automation.PSCredential ($username, $secpw)
foreach ($computer in $YourFile)
{
Invoke-Command -ComputerName $computer -credential $cred -ErrorAction Stop -ScriptBlock { Restart-Computer -ComputerName $computer -Force } -AsJob
}
That looks like its the output from Get-Job - could you try Receive-Job $id (Receive-Job 80).
Should give you the actual exception.
This likely runs in parallel just like invoke-command does with an array of computernames:
restart-computer computer01,computer02,computer03,computer04,computer05
Or this. It takes a couple minutes for the winrm service to come back, but they all seem to reboot at the same time.
$c = get-credential
$list = 1..10 | % tostring computer00
restart-computer $list -wait -protocol wsman -cr $c
try this (you will can add -asjob if it's work) :
$username = "yourdomain\user1"
$password = ConvertTo-SecureString "pass1" -AsPlainText -Force
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password
get-content "machinelst.txt" | %{
Restart-Computer -ComputerName $_ -Authentication default -Credential $cred
}
if you want use job, you can do it :
$listjob=#()
get-content "machinelst.txt" | %{
$listjob+=Restart-Computer -ComputerName $_ -Authentication default -Credential $cred -AsJob
}
$listjob | Wait-Job -Timeout 30
$listjob | %{
if ($_.State -eq 'Failed' )
{
Receive-Job -Job $_ -Keep
}
}

Invoke-Command with remote session: Cannot validate argument on parameter

I wrote a script to restart a few ASP.NET websites on a remote server:
$computerName = #...
$password = #...
$secureStringPassword = ConvertTo-SecureString -AsPlainText -Force -String $password
$userName = #...
$credential= New-Object System.Management.Automation.PSCredential ($userName, $secureStringPassword)
$websiteNames = #..., #..., #...
Get-PSSession -ComputerName $computerName -Credential $credential | Remove-PSSession
$psSession = New-PSSession -ComputerName $computerName -Credential $credential
Invoke-Command -Session $psSession -ScriptBlock { $websiteNames | foreach{ Stop-Website -Name $_ } }
Invoke-Command -Session $psSession -ScriptBlock { $websiteNames | foreach{ Start-Website -Name $_ } }
$psSession | Remove-PSSession
For some reasons my Invoke-Command do not run properly, I have the following error message:
Cannot validate argument on parameter 'Name'. The argument is null. Provide a valid value for the argument, and then try running the command again.
When the commands are run after an Enter-PSSession it works fine within a -ScriptBlock it kinda mess up the -Name parameter, any idea how to fix that up?
The remote session cannot access the variables you have defined locally. They can be referenced with $using:variable
Invoke-Command -Session $psSession -ScriptBlock { $using:websiteNames | foreach{ Stop-Website -Name $_ } }
Invoke-Command -Session $psSession -ScriptBlock { $using:websiteNames | foreach{ Start-Website -Name $_ } }
More information in the about_remote_variables help:
get-help about_remote_variables -Full
Actually just needed to pass the arguments to the -ArgumentList of the -ScriptBlock and use $args to reference to it within the function block:
Invoke-Command -Session $psSession -ScriptBlock { $args | foreach{ Stop-Website -Name $_ } } -ArgumentList $websiteNames
Invoke-Command -Session $psSession -ScriptBlock { $args | foreach{ Start-Website -Name $_ } } -ArgumentList $websiteNames

Enter PSSession with Variable for ComputerName

I am trying to enter a PSSession using -Computername $Server which was previously defined, but I can't seem to get this to work.
I have tried single, double, and no quotes around the variable at all. What am I doing wrong?
$Servers = Import-Csv "C:\Users\username\Desktop\DNS.csv"
$secpass = ConvertTo-SecureString 'mypassword' -AsPlainText -Force
$myCred = New-Object System.Management.Automation.PSCredential("username", $secpass)
foreach ($Object in $Servers) {
$Server = $Object.Name
Enter-PSSession -ComputerName "$Server" -Credential $myCred
sl HKLM:\SYSTEM\CurrentControlSet\Services\NetBT\Parameters
Invoke-Command -ScriptBlock {Get-Item -Path HKLM:\SYSTEM\CurrentControlSet\Services\NetBT\Parameters}
Exit-PSSession
}
We use enter pssession for creating an interactive session with the remote computer.
In your case, you do not need to have an interaction with the remote system. You just need to fetch the details from the remote systems which are present in the csv file.
So, Instead of this:
foreach($Object in $Servers) {
$Server = $Object.Name
Enter-PSSession -ComputerName "$Server" -Credential $myCred
sl HKLM:\SYSTEM\CurrentControlSet\Services\NetBT\Parameters
Invoke-Command -ScriptBlock {Get-Item -Path HKLM:\SYSTEM\CurrentControlSet\Services\NetBT\Parameters}
Exit-PSSession
}
Do This:
foreach($Object in $Servers)
{
$Server = $Object.Name
Invoke-Command -ComputerName $Server -ScriptBlock {Get-Item -Path HKLM:\SYSTEM\CurrentControlSet\Services\NetBT\Parameters} -Credential $myCred
}
Note: I believe you have enabled PSRemoting and have edited trusted hosts.
The ComputerName param of Invoke-Command will accept an array of servers so you can do away with the foreach loop entirely and simplify your code to:
$Servers = Import-Csv "C:\Users\username\Desktop\DNS.csv" | Select-Object -ExpandProperty Name
$secpass = ConvertTo-SecureString 'mypassword' -AsPlainText -Force
$myCred = New-Object System.Management.Automation.PSCredential("username", $secpass)
Invoke-Command -ComputerName $Servers -ScriptBlock {Get-Item -Path HKLM:\SYSTEM\CurrentControlSet\Services\NetBT\Parameters} -Credential $myCred

Remote management with powershell

I'm trying to get some information from several machines on the network but I get loads of entries of the local machine.. for each entry in the text file I get an entry from the local machine.
Any idea where I'm going wrong.. winrm is configured on the remote machines and running.
$Username = Read-Host "Please enter Username"
$Password = read-host "please enter Password"
$pass = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$pass
$computers = gc c:\test\file.txt
foreach ($Computer in $computers)
{
Invoke-command -ComputerName $computers -credential $cred -ErrorAction Stop -ScriptBlock {Invoke-Expression -Command:"cmd.exe /c 'ipconfig'" | out-file c:\test\output.txt -append}
}
cls
Thanks in advance :)
Invoke-Command will take an array for the ComputerName param so you can use $computers instead of using a foreach loop (assuming that you have one computer name per-line in the file).
I've also used Get-Credential to prompt for the full credential in one go rather than asking for username and password individually.
$Cred = Get-Credential
$computers = Get-Content c:\test\file.txt
Invoke-Command -ComputerName $computers -Credential $cred -ErrorAction Stop -ScriptBlock {Invoke-Expression -Command:"cmd.exe /c 'ipconfig'" | Out-File c:\test\output.txt -Append}
The reason you are only seeing a single computers info in c:\test\output.txt is because the output of the the ipconfig command is being saved to the remote computer... so you will have a c:\test\output.txt file on each computer you run the command against.
EDIT:
To take the output of each remote command and save it to your local computer just move the Out-File outside the Invoke-Command like this:
$Cred = Get-Credential
$computers = Get-Content c:\test\file.txt
Invoke-Command -ComputerName $computers -Credential $cred -ErrorAction Stop -ScriptBlock {Invoke-Expression -Command:"cmd.exe /c 'ipconfig'"} | Out-File c:\test\output.txt -Append
The issue is you are iterating one by one but you are not passing one by one to the invoke-command, $computer will have each value at a time in the foreach loop.
Instead of this:
foreach ($Computer in $computers)
{
Invoke-command -ComputerName $computers -credential $cred -ErrorAction Stop -ScriptBlock {Invoke-Expression -Command:"cmd.exe /c 'ipconfig'" | out-file c:\test\output.txt -append}
}
Do this:
foreach ($Computer in $computers)
{
Invoke-command -ComputerName $computer -credential $cred -ErrorAction Stop -ScriptBlock {Invoke-Expression -Command:"cmd.exe /c 'ipconfig'" | out-file c:\test\output.txt -append}
}
Further improvement:
You do not have to give Invoke-Expression -Command:"cmd.exe /c 'ipconfig'"
Instead of this,you can directly use ipconfig inside the scriptblock.