Kill only a specific Command Prompt Process Remotely with PowerShell - powershell

I am trying to write a script to remote kill a specific command prompt process.
If I locally run get-process I can see that the CMD.exe process can be narrowed down by what is set in the field "MainWindowTitle"
If I get use Get-Process -computer name or get CIMInstance the field "MainTitleWindow" comes back as blank.
$ses = New-CimSession -ComputerName $computer -Credential $cred
$process = Get-CimInstance -ClassName CIM_process -CimSession $ses -filter "name = 'cmd.exe'"
$process | Select-Object name,MainWindowTitle
Remove-CimSession -CimSession $ses
name MainWindowTitle
---- ---------------
cmd.exe

Extracted from MSDN:
A process has a main window associated with it only if the process has
a graphical interface. If the associated process does not have a main
window (so that MainWindowHandle is zero), MainWindowTitle is an empty
string ("").
More info here.
I tried to compare with other processes but the result is the same...

You could parse the output of the tasklist command. Like this:
$cmds = tasklist /v |
Where-Object {$_ -like "cmd.exe*"} |
ForEach-Object {
New-Object -TypeName PSObject -Prop #{"ID"=[int]$_.Substring(30,5); `
"Title"=$_.Substring(157)}
}
$cmds

$Process = Get-CimInstance CIM_Process -CimSession $Ses | Where-Object{ $ProcessNames -contains $_.Name }
using this you can also get the ProcessID and ParentProcessID.
you can then use the similar commands to then find the CMD process that uses the ParentProcessID of your main script.
You must close both the main process of the script and the command prompt as well or else the script will keep running.

Related

Using Powershell Get-ItemProperty through all of AD computers object

I'm a complete newbie in Powershell (and programming as you may have guessed), I want to get the result of the following PS command for each of our AD computer object and print the result in a text file...but I'm completely lost. Does anyone have a lifeline I could hold on to?
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" | Select-String -Pattern "mysoftwarename"
Thank you very much.
$ScriptBlock = {Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" | Select-String -Pattern "mysoftwarename"}
$Computers = (Get-ADComputers -filter * ).name
$Creds = (Get-Credential)
foreach ($Computer in $Computers)
{
"`n`n$Computer`n" >> .\file.txt # "`n" just emulates Enter key press
Invoke-Command -ComputerName $Computer -ScriptBlock $ScriptBlock -Credential $Creds >> .\file.txt
}
This will work fine if you have all your computers online and PS remoting configured properly. Otherwise, it will require modifications.

Unable to re-write the file in powershell using Out-File

I am trying to write a powershell script to write the output of a command to a file. It runs perfectly fine for the first time. When i re-run the script it comes with an error
Out-File : The process cannot access the file '//Filepath' because it is
being used by another process.
+ (Invoke-Command -Session $s -ScriptBlock $command )| Out-File
$file -For ...
Code block :
$command = {
$x = (Get-Date).AddMinutes(-5).ToShortTimeString() ;
$y = "{0:HH:mm}" -f [datetime] $x ;
cd $path ;
dumplog ctisvr /bt $y /m "CSTAUniversalFailureConfEvent"
}
#$s = New-PSSession -ComputerName
(Invoke-Command -Session $s -ScriptBlock $command )| Out-File $file -Force
My suggestion would be that this is not PoSH cause issue but the dumplog command you are using. There are programs with will not lock files and those that will. I've never used dumplog, I can only speculate here.
So, it would be best to check for the process state of the exe and file before doing anything with it afterwards.
Like simply looking for a dumplog process after the first serialization is done, the kill any dumplog process before trying again.
If this is on a file share you can use...
Get-SmbOpenFile
Or
openfiles
If it is local you can do something like...
What file is open by a given program
Get-CimInstance Win32_Process -Filter "name = 'dumplog.exe'" | Format-List -Property *
Get the owner of the process
$ProcessId = Get-CimInstance Win32_Process -Filter "name = 'dumplog.exe'"
Invoke-CimMethod -InputObject $ProcessId -MethodName GetOwner
What process has a file locked or filter the results based on the file name:
($ProcessId = Get-CimInstance Win32_Process | where commandline -match 'CSTAUniversalFailureConfEvent')
Kill the process
Invoke-CimMethod -InputObject $ProcessId -MethodName Terminate

How can I list the Startup Type of a serive running on a remote machine?

I'm trying to find out the startup type of a service running on a remote machine.
I've tried the below but it gives me the Start Mode rather than the Startup Type.
[cmdletbinding()]
param(
[string[]]$Service,
[switch]$Disabled,
[switch]$Automatic,
[switch]$Manual,
[string]$ComputerName = $env:ComputerName
)
foreach($Ser in $Service) {
try {
$Obj = Get-WmiObject -Class Win32_Service -Filter "Name='$Ser'"-ComputerName $ComputerName -ErrorAction Stop
$Obj | select Name, DisplayName, StartMode
} catch {
Write-Error " Failed to get the information. More details: $_"
}
}
.\Get-ServiceStartupType.ps1 –Service wscsvc –ComputerName Computername
The Service is "wscsvc" Security Center
If you use
Get-Service -name $ser -computername $computername | select-object Name,StartType
Instead of get-wmiobject. I've also used the pipeline instead of a variable to make the code a little cleaner.
You'll need to use Get-Service instead of Get-WmiObject:
$svc = Get-Service wscsvc
$svc.StartType
Used in your code like this:
$Obj = Get-Service $Ser -ComputerName $ComputerName -ErrorAction Stop
$Obj | select Name, DisplayName, StartType

PowerShell loop issue

I've got a piece of code to check the status of a particular service and if the status is not "running" to start it. The variable seems to have the proper data before the if/else statement but it always defers to else even when I have one of the test server's service set to off. It will report one service as running and one as stopped but will echo "It's Down" for both.
Thanks in advance for your help
$Servers = get-content c:\scripts\list.txt
$Servers|foreach {
$service = Get-WmiObject -ComputerName $_ -Class Win32_Service -Filter "Name='Buildbot'"
$status = Get-Service -ComputerName $_ -Name "Buildbot" | select Status
echo $status
if ($status -eq "Running") {"Its Up!"} else {"It's down"}
#$service.StartService()
#"It should be up now"}
}
Pretty sure your issue is that you are doing a string comparison on an object with the property status as supposed a string of the status state. Update the line that populates $status like either of the following examples
$status = Get-Service -ComputerName $_ -Name "Buildbot" | select -ExpandProperty Status
$status = (Get-Service -ComputerName $_ -Name "Buildbot").Status
As an aid consider the output of the following two commands
PS C:\Users\mcameron> Get-Service -Name "Print Spooler" | select Status
Status
------
Running
PS C:\Users\mcameron> Get-Service -Name "Print Spooler" | select -expand Status
Running
The first result is an object with the property Status. Second is just a string.

Powershell Kill all processes except system

In powershell, I would like to kill all processes for all users, except explorer and processes used by the system
This is where I am including the errors that are given:
$Cred = Get-Credential;
Invoke-Command -ComputerName localhost -Credential $Cred -ScriptBlock { Get-Process $env:ALLUSERSPROFILE | Where-Object -FilterScript {$_.Name -ne "SYSTEM, NETWORK SERVICE, LOCAL SERVICE"} | Where-Object -filterscript {$_.Name -ne "explorer"} | Stop-Process -WhatIf }
Cannot find a process with the name "C:\ProgramData". Verify the process name and call the cmdlet again.
+ CategoryInfo : ObjectNotFound: (C:\ProgramData:String) [Get-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand
+ PSComputerName : localhost
Here, this should work for you.
Function Stop-UserProcesses{
Param([string]$Computer = "localhost")
$Cred = Get-Credential
Invoke-Command -ComputerName $Computer -Credential $Cred -ScriptBlock {
Get-Process -IncludeUserName | Where{!($_.UserName -match "NT AUTHORITY\\(?:SYSTEM|(?:LOCAL|NETWORK) SERVICE)") -and !($_.ProcessName -eq "explorer")}|Stop-Process -WhatIf
}
}
Once you are convinced that it is functional remove the -WhatIf. Then just call it as Stop-UserProcesses to end everything locally, or Stop-UserProcesses SomeComputer01 to end everything on a remote system (assuming you have remote sessions enabled in your environment).
Edit: Well then, evidently the -IncludeUserName switch is new in v4. So, in order to do what you want we have to jump through hoops and use Get-WMIObject on the win32_process class, then execute the GetOwner() method for each process. Probably want to filter it so we don't end up with things like Idle throwing errors when they don't have an owner, so we'll make sure that the CommandLine property exists.
Function Stop-UserProcesses{
Param([string]$Computer = "localhost")
$Cred = Get-Credential
Invoke-Command -ComputerName $Computer -Credential $Cred -ScriptBlock {
#Get all processes
$Processes = get-wmiobject win32_process|Where{![string]::IsNullOrEmpty($_.commandline)}|Select *,#{l='Owner';e={$_.getowner().user}}
#Filter out System and service processes
$Processes = $Processes | Where { !($_.Owner -match "(?:SYSTEM|(?:LOCAL|NETWORK) SERVICE)") }
#Get processes and filter on the Process ID and name = explorer, then pipe to stop-process
Get-Process | Where { $Processes.ProcessID -contains $_.id -and $_.name -ne "explorer" } | Stop-Process -WhatIf
}
}