I am having so much fun making my own PowerShell tools that I just can't stop. I've been using my Get-Commandmetadata function that I wrote about a few weeks ago. A driving force in all of this is to have a toolset that I can use efficiently from the console. Or to put it in terms you might appreciate: the biggest bang for the least amount of typing. Today, I have another example. I took a common PowerShell expression and simplified it into its own command. And even though the final code looks like a lot, it didn't take me very long because my toolmaking tool "wrote" most of the script for me.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
So the common task is finding services that match a certain state such as running or stopped. You've most likely seen this expression:
get-service -computername server01 | where {$_.status -eq 'running'}
Not the most onerous command to type, but even I have mistyped and had to correct. If this is a common task, why not create an easy to use tool?
#requires -version 3.0 Function Get-MyService { <# .Synopsis Get services by status. .Description This is a proxy version of Get-Service. The only real change is that you can specify services by their status. The default is to get all running services. .Notes Created: 9/11/2014 Learn more: PowerShell in Depth: An Administrator's Guide (http://www.manning.com/jones6/) PowerShell Deep Dives (http://manning.com/hicks/) Learn PowerShell 3 in a Month of Lunches (http://manning.com/jones3/) Learn PowerShell Toolmaking in a Month of Lunches (http://manning.com/jones4/) **************************************************************** * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * **************************************************************** .Example PS C:\> Get-MyService b* -status stopped -comp chi-dc04 Status Name DisplayName ------ ---- ----------- Stopped BITS Background Intelligent Transfer Ser... .Link Get-Service #> [CmdletBinding(DefaultParameterSetName='Default', RemotingCapability='SupportedByCommand')] param( [Parameter(ParameterSetName='Default', Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [Alias('ServiceName')] [string[]]$Name, [ValidateNotNullorEmpty()] [System.ServiceProcess.ServiceControllerStatus]$Status="Running", [Parameter(ValueFromPipelineByPropertyName=$true)] [Alias('Cn')] [ValidateNotNullOrEmpty()] [string[]]$ComputerName = $env:COMPUTERNAME, [Alias('DS')] [switch]$DependentServices, [Alias('SDO','ServicesDependedOn')] [switch]$RequiredServices, [Parameter(ParameterSetName='DisplayName', Mandatory=$true)] [string[]]$DisplayName, [ValidateNotNullOrEmpty()] [string[]]$Include, [ValidateNotNullOrEmpty()] [string[]]$Exclude, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNullOrEmpty()] [System.ServiceProcess.ServiceController[]]$InputObject ) begin { Write-Verbose -Message "Starting $($MyInvocation.Mycommand)" Write-Verbose "Getting services with a status of $status on $($Computername.toUpper())" try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } #remove Status parameter from bound parameters since Get-Service won't recognize it $PSBoundParameters.Remove("Status") | Out-Null $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-Service', [System.Management.Automation.CommandTypes]::Cmdlet) #$scriptCmd = {& $wrappedCmd @PSBoundParameters } #my modified command $scriptCmd = {& $wrappedCmd @PSBoundParameters | Where-Object {$_.status -eq $Status } } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } #begin process { try { $steppablePipeline.Process($_) } catch { throw } } #process end { try { $steppablePipeline.End() } catch { throw } Write-Verbose -Message "Ending $($MyInvocation.Mycommand)" } #end } #end function Get-MyService set-alias -Name gsv2 -Value Get-MyService
Yes, I could have written my own function that called Get-Service, but then I'd have to write all of the code to accommodate all of the parameters for Get-Service. With the proxy command, all I need to do is insert my own code.
#my modified command $scriptCmd = {& $wrappedCmd @PSBoundParameters | Where-Object {$_.status -eq $Status } }
In other words, I let Get-Service do its thing and then filter the result. Now I can easily do this:
PS C:\Scripts> get-myservice w* -Status stopped -ComputerName chi-dc01 Status Name DisplayName ------ ---- ----------- Stopped wbengine Block Level Backup Engine Service Stopped WbioSrvc Windows Biometric Service Stopped WcsPlugInService Windows Color System Stopped WdiServiceHost Diagnostic Service Host Stopped WdiSystemHost Diagnostic System Host Stopped Wecsvc Windows Event Collector Stopped WinHttpAutoProx... WinHTTP Web Proxy Auto-Discovery Se... Stopped wmiApSrv WMI Performance Adapter Stopped wudfsvc Windows Driver Foundation - User-mo...
Or, because I'm typing this, I could use my own alias.
One thing I want to point out, is that for the new Status property, you'll notice I didn't cast it as a string.
[System.ServiceProcess.ServiceControllerStatus]$Status="Running"
I could have made it a string, but by using the actual enumeration, PowerShell will autocomplete possible values.
How did I know what to use? I used Get-Member.
PS C:\Scripts> get-service bits | get-member status TypeName: System.ServiceProcess.ServiceController Name MemberType Definition ---- ---------- ---------- Status Property System.ServiceProcess.ServiceControllerStatus Status {get;}
It is really not that difficult or time consuming. I spent more time writing this blog post than I spent creating the function.
I hope this will inspire you to create your own PowerShell tools and that you will share the fruits of your labors.
Another excellent article.
.
How would you build startup type into this?
You can’t use Get-Service for that because the System.ServiceProcess.ServiceController class that the cmdlet uses doesn’t have a property for StartUp type. You can use WMI and query the Win32_Service class to get that information.