PowerShell's get-process cmdlet gets the processes that are running on the local computer, and is restricted to the local computer only, you can't interact with processes that runs on remote computers nor create ones locally or remotely.
You can use WMI (see below), but I want to show another way of doing it, less documented one - static methods.
Static methods give you the option to break loose of the cmdlet 'wrapper' and work directly on the underlying NET class. To find which static methods you can use on processes we'll use PowerShell Get-Process cmdlet, we'll pipe it to Get-Member to reveal the underlying NET type that get-process is using:
PS:55 >Get-Process | Get-Member
TypeName: System.Diagnostics.Process
Name MemberType Definition
---- ---------- ----------
Handles AliasProperty Handles = Handlecount
Name AliasProperty Name = ProcessName
(...)
Managing processes with static methods
As you can see, below the surface, Get-Process is using the [System.Diagnostics.Process] NET type to retrieve a process object for each process. From now on, you can work with the static methods of this type to interact with process objects. To find the static methods (surround the type name with square brackets), type:
PS > [System.Diagnostics.Process] | Get-Member -Static -MemberType method
TypeName: System.Diagnostics.Process
Name MemberType Definition
---- ---------- ----------(...)
EnterDebugMode Method static System.Void EnterDebugMode()...
Equals Method static System.Boolean Equals(Object...
GetCurrentProcess Method static System.Diagnostics.Process G...
GetProcessById Method static System.Diagnostics.Process G...
GetProcesses Method static System.Diagnostics.Process[]...
GetProcessesByName Method static System.Diagnostics.Process[]...
LeaveDebugMode Method static System.Void LeaveDebugMode()...
ReferenceEquals Method static System.Boolean ReferenceEqua...
Start Method static System.Diagnostics.Process S...
(...)
The Definition column shows all the available overloads for each method. The notation for calling a static method is:
PS > [System.Diagnostics.Process]::StaticMethodName(<args>)
Lets find the overloads for the GetProcesses method, (each overload is delimited with a comma):
PS > ([System.Diagnostics.Process] | gm -s GetProcesses).Definition
static System.Diagnostics.Process[] GetProcesses(),
static System.Diagnostics.Process[] GetProcesses(String machineName)
The output shows two ways to call the method. You can call it with no arguments and get all running processes on the local computer or use the later to get processes from a remote computer. We are interested in the second one since it accept a string argument for a computer name. To save typing space we'll assign the type name to a variable:
PS > $process = [System.Diagnostics.Process]
# examples
$server="server"
$process = [System.Diagnostics.Process]
# get all running processes on the remote server
PS > $process::GetProcesses($server)
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
165 7 15364 21684 56 2992 vmserverdWin32
196 6 2764 5132 54 3740 LVComSX
73 4 1100 3392 30 2452 ctfmon
(...)
As you can see the output is the same output produced by the get-process. To find which methods you can invoke on a process, pipe it to get-member:
PS > $process::GetProcessById(2992,$server) | gm -member method
TypeName: System.Diagnostics.Process
Name MemberType Definition
---- ---------- ----------
(...)
Close Method System.Void Close()
CloseMainWindow Method System.Boolean CloseMainWindow()
CreateObjRef Method System.Runtime.Remoting.ObjRef Cr
Dispose Method System.Void Dispose()
Kill Method System.Void Kill()
Refresh Method System.Void Refresh()
Start Method System.Boolean Start()
ToString Method System.String ToString()
WaitForExit Method System.Boolean WaitForExit(...
(...)
This are the same methods you'll get if you run get-process | gm -member method. To call a static method simply append it to the command:
PS > $process::GetProcessById(2992,$server).Kill()
In the same manner you can get a list all properties of a process.
PS > $process::GetProcessById(2992,$server) | gm -member property
# get remote process by its process id
PS > $process::GetProcessById(2992,$server)
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
165 7 15364 21684 56 2992 vmserverdWin32
# get remote process by its name
PS > $process::GetProcessesByName("vmserverdWin32",$server)
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
165 7 15364 21684 56 2992 vmserverdWin32
# get PowerShell's process, locally
PS > [System.Diagnostics.Process]::GetCurrentProcess()
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
512 9 39340 33148 163 4.06 11952 powershell
Creating local processes
To create processes, locally, you need to use the static Start() method. To find what overloads this method takes:
PS > $process = [System.Diagnostics.Process]
PS > (($process | gm -static start).definition).replace("static","`nstatic")
static System.Diagnostics.Process Start(String fileName, String userName, SecureString password, String domain),
static System.Diagnostics.Process Start(String fileName, String arguments, String userName, SecureString password, String domain),
static System.Diagnostics.Process Start(String fileName)
static System.Diagnostics.Process Start(String fileName, String arguments),
static System.Diagnostics.Process Start(ProcessStartInfo startInfo)
There are five different ways to call it, each overload accepts different set of arguments:
1. Run a file (executable) with alternate credentials and a secure password.
2. Adds an option to add arguments for the file name.
3. Gets only file name.
4. File name and arguments, no alternate credentials.
5. Gets a ProcessStartInfo object.
# Use the third overload to run notepad
PS > [System.Diagnostics.Process]::Start("notepad")
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
0 1 136 84 1 0.00 13720 notepad
# Use the fourth to run notepad with arguments, in this case make notepad load the system.ini file.
PS > [System.Diagnostics.Process]::Start("notepad","$env:windir\system.ini")
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
5 1 340 840 8 0.03 23728 notepad
BTW, if you want to avoid the default output, assign the command to a variable or cast it to [void], as in:
PS > [void][System.Diagnostics.Process]::Start("notepad","$env:windir\system.ini")
PS >
The fifth option is the one I want to use since it is using another type to specify what starting info to assign to the new process, including alternate credentials (though in plain text and not secure as the first and second overloads which uses the SecureString parameter). Here's a partial property list, you can find the full list here:
- Arguments
- FileName
- Password
- UserName
- WorkingDirectory
Using the New-Object cmdlet
In this example I'll use the New-Object cmdlet to instantiate a new System.Diagnostics.ProcessStartInfo object to specify a set of values to be used when the process start:
# Specifies a set of values that are used when you start a process.
$si = new-object System.Diagnostics.ProcessStartInfo
$si.fileName = "notepad.exe"
$si.Arguments= "$env:windir\system.ini"
$si.WorkingDirectory = $pwd
# this will launch the process in the background (hidden)
# $si.windowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
# run process under alternate credentials
# $si.UserName="username"
# $si.Password="Password"
# start the process
$process = New-Object System.Diagnostics.Process
$process.startInfo=$si
# this is also valid
# $process = [System.Diagnostics.Process]::Start($si)
# close notepad and watch the output
if($process.start()) { $process.waitForExit(); "existed"}
Note: The WaitForExit() method is available only for processes that are running on the local computer. If you need to track a process on a remote system construct a loop to check if the remote process exists.
Want to know how to list all name/value pairs of the [System.Diagnostics.ProcessWindowStyle] type or any other [enum] type, see my post on Listing enumeration type values.
Creating remote processes
So far so good. But how about creating new processes on remote computers? Well, that's another story (Class). None of the above, Get-Process nor its underlying type, let you do that. The only way to do it is by using WMI.
You can access WMI with the Get-WmiObject cmdlet or with PowerShell's Type accelerators (shortcuts). Type accelerators were developed by the Windows PowerShell team to make WMI scripts more easy to write for system administrators:
[WMI] - type accelerator for ManagementObject.
[WMICLASS] - type accelerator for ManagementClass.
[WMISEARCHER] - type accelerator for a ManagementObjectSearcher.
See the second resource link below for more information on this.
Here's a one-liner to create new process(es) on local/remote computers:
PS > ([WMICLASS]"\\$computer\ROOT\CIMV2:win32_process").Create("notepad")
__GENUS : 2
__CLASS : __PARAMETERS
__SUPERCLASS :
__DYNASTY : __PARAMETERS
__RELPATH :
__PROPERTY_COUNT : 2
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ProcessId : 7380
ReturnValue : 0
If the ReturnValue property is set to 0 then you've successfully created the process on the remote computer, the ProcessId property shows the process id for the new process on the remote computer.
Note: For security reasons the WMI Win32_Process.Create method cannot be used to start an interactive process remotely (it runs hidden, in the background).
Summary
The above technique on static methods, opens a whole new world of discovering PowerShell and NET classes. You can apply it to any cmdlet and work your way up or down, depending on your point of view :-). Soon enough you'll find your way to MSDN, where you can get more information and code examples on each class.
This post only scratches the surface and there's a lot more to write on processes... but now comes your part. Check out what Get-Service has to offer and what can you get by applying all of this to it.
Last but not least, I'd like to thank Hal Rottenberg from TechProsaic and the PowerScripting Podcast for his help with editing and prompting me to write this article.
Happy New Year!
Additional Resources:
Managing Processes in PowerShell
Improved Support for WMI
Getting Services associated with a Process via PowerShell
Process Class
ProcessStartInfo Class
Create Method of the Win32_Process Class
Casts and Get-WmiObject