Tags:
As I learn PowerShell, I quickly get overwhelmed with the number of commands available. I try to anticipate the Verb/Noun syntax, and command completion helps a lot.
I can rattle off PowerShell commands for lots of things, but something I always have to look up are the object properties. Fortunately, PowerShell makes this pretty easy. Let me explain by posing a question:
What are the dependent services for the Windows EventLog service?
The first part of this is pretty easy: Get-Service will enumerate the services. We can specify the service name as an unnamed argument, or following -Name:
PS C:\WINDOWS\system32> Get-Service eventlog Status Name DisplayName ------ ---- ----------- Running eventlog Windows Event Log PS C:\WINDOWS\system32> Get-Service -Name EventLog Status Name DisplayName ------ ---- ----------- Running EventLog Windows Event Log
In PowerShell, the pipeline allows us to process the objects that are returned from the command. If objects seems vague, it's intentionally so: objects can be properties, code, scripts, alias, events, and more. Often, we're interested in the properties, displayed as columns of information.
To identify the available properties, we have some options. The first is to send the output of Get-Service to Get-Member:
PS C:\WINDOWS\system32> Get-Service | Get-Member TypeName: System.ServiceProcess.ServiceController Name MemberType Definition ---- ---------- ---------- Name AliasProperty Name = ServiceName RequiredServices AliasProperty RequiredServices = ServicesDependedOn Disposed Event System.EventHandler Disposed(System.Object, System.EventArgs) Close Method void Close() Continue Method void Continue() CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType) Dispose Method void Dispose(), void IDisposable.Dispose() Equals Method bool Equals(System.Object obj) ExecuteCommand Method void ExecuteCommand(int command) GetHashCode Method int GetHashCode() GetLifetimeService Method System.Object GetLifetimeService() GetType Method type GetType() InitializeLifetimeService Method System.Object InitializeLifetimeService() Pause Method void Pause() Refresh Method void Refresh() Start Method void Start(), void Start(string[] args) Stop Method void Stop() WaitForStatus Method void WaitForStatus(System.ServiceProcess.ServiceControllerStatus desiredSt... CanPauseAndContinue Property bool CanPauseAndContinue {get;} CanShutdown Property bool CanShutdown {get;} CanStop Property bool CanStop {get;} Container Property System.ComponentModel.IContainer Container {get;} DependentServices Property System.ServiceProcess.ServiceController[] DependentServices {get;} DisplayName Property string DisplayName {get;set;} MachineName Property string MachineName {get;set;} ServiceHandle Property System.Runtime.InteropServices.SafeHandle ServiceHandle {get;} ServiceName Property string ServiceName {get;set;} ServicesDependedOn Property System.ServiceProcess.ServiceController[] ServicesDependedOn {get;} ServiceType Property System.ServiceProcess.ServiceType ServiceType {get;} Site Property System.ComponentModel.ISite Site {get;set;} StartType Property System.ServiceProcess.ServiceStartMode StartType {get;} Status Property System.ServiceProcess.ServiceControllerStatus Status {get;} ToString ScriptMethod System.Object ToString();
This is a lot of output, and for our purposes we are mostly interested in properties, so we can refine our pipeline adding -MemberType Property:
PS C:\WINDOWS\system32> Get-Service | Get-Member -MemberType Property TypeName: System.ServiceProcess.ServiceController Name MemberType Definition ---- ---------- ---------- CanPauseAndContinue Property bool CanPauseAndContinue {get;} CanShutdown Property bool CanShutdown {get;} CanStop Property bool CanStop {get;} Container Property System.ComponentModel.IContainer Container {get;} DependentServices Property System.ServiceProcess.ServiceController[] DependentServices {get;} DisplayName Property string DisplayName {get;set;} MachineName Property string MachineName {get;set;} ServiceHandle Property System.Runtime.InteropServices.SafeHandle ServiceHandle {get;} ServiceName Property string ServiceName {get;set;} ServicesDependedOn Property System.ServiceProcess.ServiceController[] ServicesDependedOn {get;} ServiceType Property System.ServiceProcess.ServiceType ServiceType {get;} Site Property System.ComponentModel.ISite Site {get;set;} StartType Property System.ServiceProcess.ServiceStartMode StartType {get;} Status Property System.ServiceProcess.ServiceControllerStatus Status {get;}
In this output we see all of the properties available in the object sent to the pipeline from Get-Service. This is useful, but the definition column doesn't always offer intuitive insight. Our second option is to use Select-Object instead:
PS C:\WINDOWS\system32> Get-Service | Select-Object -First 1 -Property * Name : AarSvc_23e54 RequiredServices : {} CanPauseAndContinue : False CanShutdown : False CanStop : True DisplayName : AarSvc_23e54 DependentServices : {} MachineName : . ServiceName : AarSvc_23e54 ServicesDependedOn : {} ServiceHandle : SafeServiceHandle Status : Running ServiceType : 240 StartType : Manual Site : Container :
We see the same property names, but sometimes seeing the property name in the context of a sample value can be more useful than the Get-Member output.
Note: Using -First 1 in the Select-Object command limits the output to just a single property list; omitting -First 1 would return the properties for all objects, which may be more than you really want to read though.
In the output of Get-Member and Select-Object, we see the property DependentServices. Let's take a look for the EventLog service:
PS C:\WINDOWS\system32> Get-Service EventLog | Select-Object -Property DependentServices DependentServices ----------------- {Wecsvc, uhssvc, NcdAutoSetup, AppVClient...}
Fantastic! We have the list of dependent services for EventLog. However, Notice the ellipsis at the end of the dependent services list, and the curly braces surrounding the values? This indicates that the output is an object that is truncated.
To get the complete list, we have to backtrack a bit. When we use the Select-Object cmdlet in a pipeline, it will change the output object to a custom [PSObject](https://msdn.microsoft.com/en-us/library/system.management.automation.psobject%28v=vs.85%29.aspx) class. That is, it's a wrapper that provides alternate ways of viewing the available member objects. Instead of working with the Select-Object output, we can return right to Get-Service, declaring the output as a variable:
PS C:\WINDOWS\system32> $eventlogsvc = Get-Service EventLog PS C:\WINDOWS\system32>
With the variable $eventlogsvc, we can access the member object DependentServices using dot notation:
PS C:\WINDOWS\system32> $eventlogsvc.DependentServices Status Name DisplayName ------ ---- ----------- Stopped Wecsvc Windows Event Collector Running uhssvc Microsoft Update Health Service Stopped NcdAutoSetup Network Connected Devices Auto-Setup Stopped AppVClient Microsoft App-V Client Running netprofm Network List Service Running NlaSvc Network Location Awareness
To get the complete list of dependent service names, we can use Select-Object again to access just the Name property:
PS C:\WINDOWS\system32> $eventlogsvc.DependentServices | Select-Object -Property Name Name ---- Wecsvc uhssvc NcdAutoSetup AppVClient netprofm NlaSvc
PowerShell purists will point out that declaring a variable is not necessary, since you can enclose the Get-Service command in parenthesis to accomplish the same goal: (Get-Service EventLog).DependentServices | Select-Object -Property name. While this is true, I think declaring the $eventlogsvc is worth the memory required for the enhanced legibility. YMMV.
PowerShell often feels like a safe form of what I imagine spelunking is like. There's lots of twists and turns, and sometimes you have to backtrack quite a bit, but eventually you find your way, or you give up trying.
-Joshua Wright
Return to Getting Started With PowerShell
Joshua Wright is the author of SANS SEC504: Hacker Tools, Techniques, and Incident Handling, a faculty fellow for the SANS Institute, and a senior technical director at Counter Hack.