Tag Archives: Get-Service

Looking Busy with PowerShell


Notice: The following post was originally published on another website. As the post is no longer accessible, it is being republished here on tommymaynard.com. The post was originally published on February 27, 2019.


Every once in a while, I write a short little article — if, I can even call it that — and then it sits around for a month or so doing nothing. It just hangs out in my drafts and stares at me. It’s happened again. Instead of focusing on it any longer, I’m writing this paragraph as my excuse, so I can publish this now and move on, already.

I saw the below Tweet late last year and I had a thought.

I love how having several windows of #Powershell open makes you look busy and/or like you know what you are doing.
— John Dalek (@DeckerDalek) November 20, 2018

I’ll go ahead and agree; why not?

I can see how this may give off that impression. With that quick and internal agreement, I had an idea. If PowerShell can make you look busy, then let’s use PowerShell to make it appear, you’re busy. We’ll open some consoles and execute some “work,” all with a single invocation of a single function. I’ll just be over here waiting for PowerShell to catch up with me.

The below function creates this illusion, just in case this is something you’re after. Hopefully, it’s not, as we all likely have some real PowerShell to read, write, and review. Me included.

Function Show-MeBeingSuperBusy {
    [CmdletBinding()]
    Param (
        [Parameter()]
        [ValidateRange(1,10)]
        [int]$ConsoleCount = 1
    )
    
    Begin {
        $Argument = '-NoProfile -Command & {1..50 | ForEach-Object {Get-PSDrive}}',
            '-NoProfile -Command & {1..50 | ForEach-Object {Get-Process}}',
            '-NoProfile -Command & {1..50 | ForEach-Object {Get-Service}}',
            '-NoProfile -Command & {1..50 | ForEach-Object {Get-Item -Path env:\}}'
    } # End Begin.
    
    Process {
        For ($i = 1; $i -le $ConsoleCount; $i++) {
            Start-Process -FilePath powershell.exe -ArgumentList ($Argument | Get-Random)
        } # End For.
    } # End Process.
    
    End {
    } # End End.
} # End Function: Show-MeBeingSuperBusy.
 
Show-MeBeingSuperBusy -ConsoleCount 5

And that’s it. Short, simple, and hardly very helpful. Now that this “article” is gone from my drafts, I should be able to focus on something a little more helpful — we’ll see. This might be all the help someone needed from me today, however.

Edit: As a part of bringing this old content back to tommymaynard.com, I tried out this function. It works, but be sure to give it a moment before you begin to think it is not working!

Proving PowerShell’s Usefulness to Newbies, Part III

Here we go! It’s the third time doing what we can, to try and prove the usefulness and power in Windows PowerShell. With a little work, those new in PowerShell, get a glimpse into not only what they can do with PowerShell, but also why they’d want to do it with PowerShell. In the end, most of this is about time savings, and tonight’s post is no exception.

Part I can be found here. In that post, we learned that we could use PowerShell to create 10,000 folders in about 10 seconds — that’s 1,000 folders per second, or 1 folder each millisecond.

Part II is here. This post was about reading in a file’s contents, sorting the contents alphabetically, and then writing the sorted results back to the same file. PowerShell took milliseconds, while my manual attempt took minutes.

Part III
As part of my imaginary Windows PowerShell presentation I thought I might give one day, I always pictured a common theme — saving time. That’s a big part of why many of us have taken so fondly to PowerShell. I pictured telling a story about a few different approaches to the same problem, all to be completed by a different admin, each with their own best way of handling the request. Here’s my hypothetical, yet quite possible situation, beginning with the characters.

Admin One: This admin is fairly new to the industry. While they know their way around a desktop version of Windows, they’re still often learning new things about it. While they’re motivated, they haven’t learned enough to often know better.

Admin Two: This admin has a year or two on admin one, and is quickly picking up better ways to speed things up by utilizing certain tools.

Admin Three: This admin is getting there. It’s almost daily that they’re figuring out faster, and more remote ways to complete tasks. While they aren’t scripting and running commands (yet), they do know some tricks to get their results quicker.

Admin Four: This admin has been studying all things PowerShell whenever there’s a moment for it. Every task assigned is another challenge to learn more PowerShell, and to continue to add to their skills. They’re freeing up time, all the time.

One day the boss comes in with a request of the team. It’s not a difficult task. In fact, any one of the team members can get the task done. They need to check 200 of their computers to determine, and then document, which of the computers are running the print spooler service. Maybe it’s just busy work handed down by the boss, or maybe this is a valid concern somehow.

Admin One gets his clipboard and heads down to one of the computer labs. Once the three labs are checked, he’ll head to each of the offices to check those computers manually, too. Here’s the steps this admin takes: (1) Log on to the computer, (2) Open Computer Management, (3) Click on Services, (4) Find the Print Spooler service, noting the status, and (5) Write down the computer name and service’s status.

Let’s assume Admin One takes 5 minutes per computer which includes the time spent walking from computer to computer and office to office. At 200 computers, that’s 1,000 minutes, or approximately 16.6 hours. That’s longer than two full 8 hour days at the office. That kind of time loss equates to zero time spent learning anything new for two days in the week. You can’t stay in the field using this procedure.

Admin Two’s first though is Remote Desktop. While this admin still has to log on, it can be done without physically visiting each computer. In addition, this admin can work with more than one remote computer at a time. This allows Admin Two the ability to complete one computer every 2 1/2 minutes, cutting the time it takes Admin One in half. That’s only a little over one work day — 8.3 hours — lost to this project. While it’s shorter, it’s still too much time.

Admin Three knows enough to know that they can connect to a remote computer from inside Computer Management, which means they can also avoid leaving their desk. This removes the step of interactively logging on to a computer, and loading a desktop environment, too. We’ll assume this admin can do one computer every minute. That’s 200 total minutes, or 3.3 hours. That admin can actually finish before lunch if they start soon enough in the day. This might be reasonable by some boss’ standards, but PowerShell has been designed to do this quicker.

The final admin, Admin Four, knows PowerShell, and they know it well enough to pull back all those results without the need for any GUIs. There’s no wandering around the building, writing things down to a clipboard, or even entering data into a file, as Admin Two and Three do from their desks, as they use RDP and Computer Management, respectively.

Admin Four has a couple approaches and plenty of time to try them both out. While there’s many ways to read in computer names, we’ll assume they have a list of the 200 computers in a text file with each computer name on its own line. They may have had to build this file, but if they’re pulling from Active Directory, then it was probably automated, as well.

The first option is to use the Get-Service cmdlet, as seen below, in the first part of the example. The second part, which requires PowerShell Remoting, uses Invoke-Command. I’ve only include 20 computers in the computers.txt file, and to save space, only included 5 results per command in the example.

PS> # Uses the Get-Service cmdlet with -ComputerName parameter.
PS> # Have to add the MachineName property to see which results goes with which computer.
PS> Get-Service -ComputerName (Get-Content -Path .\computers.txt) -Name spooler | Select-Object MachineName,Status,Name,DisplayName

MachineName                                          Status Name                          DisplayName
-----------                                          ------ ----                          -----------
DC01                                                 Running spooler                       Print Spooler
DC02                                                 Running spooler                       Print Spooler
DC03                                                 Running spooler                       Print Spooler
WEB01                                                Running spooler                       Print Spooler
WEB02                                                Running spooler                       Print Spooler
...

PS> # Uses Invoke-Command which automatically adds the name of the remote computer by default.
PS> Invoke-Command -ComputerName (Get-Content -Path .\computers.txt) -ScriptBlock {Get-Service -Name spooler}

Status   Name               DisplayName                            PSComputerName
------   ----               -----------                            --------------
Running  spooler            Print Spooler                          SQL01
Running  spooler            Print Spooler                          DC02
Running  spooler            Print Spooler                          DC02
Running  spooler            Print Spooler                          WEB02
Running  spooler            Print Spooler                          DC03
...

While I didn’t include the results, each of these two options — Get-Service, and Get-Service inside Invoke-Command — took about the same amount of time when they were measured. Even so, we’ll round up and say it takes 10 second for each set of 20 computers. At 200 computers, that 100 seconds for all of them. At 100 seconds, we’re talking about an approximation of 1 1/2 minutes to check all two hundred computers.

If I was in front of a crowd right now, and had just talked thought this post, I’d take a moment to ask the crowd which admin they thought would be around after budgets cuts. The admin that showed up with the results written down, after two days, or the one who sent the boss the results via email after two minutes? Keep in mind, that either of these cmdlets above can be used in conjunction with Export-Csv — a cmdlet that produces a text-based file that can be opened and edited with Microsoft Excel — something in which the boss probably has on their computer. In addition to automatically writing the results to file, by using the Send-MailMessage cmdlet, Admin Four can also automate sending the results by email.

Find the Account that Starts a Service

There was an email I wrote to a team member last week. The point behind it was to help the team member determine what service(s) might be running under a certain account. The Get-Service cmdlet has never provided this functionality, so I mentioned that they use the Win32_Service WMI class (with Windows PowerShell, of course).

This suggestion led to me write and send them a list of command examples to include local vs. remote computers, pulling computer’s from a text file and Csv file, and using Active Directory. It seemed like a fairly solid list, and so I thought it should be posted here. I won’t bother including any output, because it’s fairly easy to figure out what’s going to be returned — the name of the service, the account used to start/run the service, and the remote computer name, in some of the commands that run against remote computers.

This first example is how to run the command against the local computer. This will return all the services on the local computer to include the account that is used to start the listed service.

PS> Get-WmiObject -Class Win32_Service | Select-Object Name,StartName

This example shows how to run the same command above, against a single remote computer. The only difference is the addition of the -ComputerName parameter and the Server01 parameter value.

PS> Get-WmiObject -ComputerName Server01 -Class Win32_Service | Select-Object Name,StartName

In this example, we run the same command above, except we do run it against two computers. In order to know which computer returned which result, we include the PSComputerName property.

PS> Get-WmiObject -ComputerName Server01,Server02 -Class Win32_Service | Select-Object PSComputerName,Name,StartName

Here’s one way to use a text file. The Get-Content cmdlet executes first, because it’s in parenthesis. The results of this command are then provided to the -ComputerName parameter.

PS> Get-WmiObject -ComputerName (Get-Content -Path C:\computers.txt) -Class Win32_Service | Select-Object PSComputerName,Name,StartName

This example does the same thing as the command above; however, it does so by piping the results of the Get-Content cmdlet to the ForEach-Object cmdlet. Within each iteration of the foreach loop, it runs the Get-WmiObject command. In testing, there wasn’t much different in the time it took to complete this example when compared to the last one.

PS> Get-Content -Path C:\computers.txt | ForEach-Object {Get-WmiObject -ComputerName $_ -Class Win32_Service | Select-Object PSComputerName,Name,StartName}

This next example utilizes a Csv File. In a Csv file, we have (column) headers; therefore, we need to indicate to PowerShell the column we’d like to use. Let’s assume we have a Csv with three columns of data with the headers Location, NameOfComputer, and Year. We’ll need to make sure we’re instructing our command to only use the data in the NameOfComputer column.

PS> Get-WmiObject -ComputerName ((Import-Csv -Path C:\computers.csv).NameOfComputer) -Class Win32_Service | Select-Object PSComputerName,Name,StartName

Here’s the same example as above, but this one uses a pipeline and ForEach-Object cmdlet, much like we did with the text file. While there’s little difference in the time it takes to complete, I would recommend that we don’t use a pipe when it’s not necessary, or when it’s doesn’t give us an advantage.

PS> Import-Csv -Path C:\computers.csv | ForEach-Object {Get-WmiObject -ComputerName $_.NameOfComputer -Class Win32_Service | Select-Object PSComputerName,Name,StartName}

Let’s incorporate Active Directory (AD), since it’s another place where we can get computers. This first AD example returns all the computers from a specific Organization Unit (OU) and runs them through the Get-WmiObject cmdlet. As in previous examples, this command will return all the values before it provides them as the value to the -ComputerName parameter.

PS> Get-WmiObject -ComputerName ((Get-ADComputer -Filter * -SearchBase 'OU=Engineering,OU=Workstations,DC=mydomain,DC=com').Name) -Class Win32_Service | Select-Object PSComputerName,Name,StartName

In my coworker’s situation, one of the computers in the OU was down, so we needed a way to filter around that problem. Here’s two ways to do that; however, the second option is the better of the two. If you’ve never heard it before, hear it now: filter as close to the left of your commands as possible.

PS> Get-WmiObject -ComputerName ((Get-ADComputer -Filter * -SearchBase 'OU=Engineering,OU=Workstations,DC=mydomain,DC=com' | Where-Object {$_.Name -ne 'Server22'}).Name) -Class Win32_Service | Select-Object PSComputerName,Name,StartName

PS> Get-WmiObject -ComputerName ((Get-ADComputer -Filter {Name -ne 'Server22'} -SearchBase 'OU=Engineering,OU=Workstations,DC=mydomain,DC=com').Name) -Class Win32_Service | Select-Object PSComputerName,Name,StartName

 

An Improved Measure-Command: Multiple Commands, Multiple Repetitions, Calculated Averages, and Pauses Between Runs

Download the Measure-TMCommand function here: https://gist.github.com/tommymaynard/c97c5248d76aba08f1c8aa01096aa12b

In Windows PowerShell, there are often several ways to complete the same task. With that in mind, it makes sense that we might want to determine how long commands and scripts take to complete. Until now, Measure-Command has been the cmdlet we’ve used.

While Measure-Command has been helpful, I’ve often thought it should include some additional functionality. Therefore, I’ve written an advanced function, Measure-TMCommand, that adds all the benefits listed below:

– Continually measure the execution time of a single command and/or script, up to a user-defined number of repetitions.

– Continually measure the execution time of multiple commands and/or scripts, up to a user-defined number of repetitions.

– Calculate the average time a command(s), and/or a script(s)  takes to execute.

– Display limited hardware information about the computer where the command and/or script is being measured.

– Includes an option to display the output of the command and/or script, as well as the measurement results.

Updated 4/15/2015 (v1.2.1): Added a parameter -TimeInBetweenSeconds with a parameter alias of -Pause. This will pause the function between executions, allowing the ability to test at different times between a set time. For instance, let’s say you want to measure a command every 1/2 hour for six hours: 12 repetitions with 30-minute pauses. You would then run the command with the -Repetitions parameter with a value of 12 and the -TimeInBetweenSeconds (or -Pause) with a value of 1800 (as in 1800 seconds, or 30 minutes).

Here’s the function in action:

Measure-TMCommand1.2.1

In the example, above, we can easily determine that using the -Name parameter of the Get-Service cmdlet is faster than piping the entire result set to the Where-Object cmdlet, and then filtering on the name. Notice that not all properties were returned — only the ones in which I was interested.

With the addition of the -TimeBetweenInSeconds, or -Pause, parameter I have considered that this function might be better served to also have an -AsJob parameter. I’ll look into it, but no promises. Thanks, and enjoy.

about_Aliases

This post is the help rewrite for about_Aliases. While the help files for Windows PowerShell are invaluable, the idea behind a rewrite is so true beginners might even better understand the help file concepts. At times, some things discussed in the Windows PowerShell help file will not be included in a help rewrite. Therefore, it is always best to read the actual help file after reading this post. (PS3.0)

An Alias in Windows PowerShell is a simplified, or quicker, way to type a cmdlet using an alternate name. Get-Alias (or the alias for Get-Alias, gal) will display a list of all of the aliases that the Windows PowerShell session knows about. This includes both built-in aliases and any additional aliases created or imported. The first two examples below, indicate two ways to accomplish the same thing–listing all the aliases. These examples only show the first four results.

PS C:\> Get-Alias

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Alias           % -> ForEach-Object
Alias           ? -> Where-Object
Alias           ac -> Add-Content
Alias           asnp -> Add-PSSnapin

This example uses the alias for the Get-Alias cmdlet, gal.

PS C:\> gal

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Alias           % -> ForEach-Object
Alias           ? -> Where-Object
Alias           ac -> Add-Content
Alias           asnp -> Add-PSSnapin

To find the cmdlet associated with a single alias, the alias needs to be provided, as the value for the -Name parameter, to the Get-Alias cmdlet.

PS C:\> gal -Name gc

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Alias           gc -> Get-Content

The name parameter (-Name) is not required to use it. This means that if there is something after the Get-Alias cmdlet, such as gc in this example, then it will default to using the -Name parameter.

PS C:\> gal gc

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Alias           gc -> Get-Content

Windows PowerShell will error if the -Name parameter is supplied with a cmdlet name or another value that is not an alias.

PS C:\> gal Get-Content

gal : This command cannot find a matching alias because an alias with the name ‘Get-Content’ does not exist.
At line:1 char:1
+ gal Get-Content
+ ~~~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (Get-Content:String) [Get-Alias], ItemNotFoundException
+ FullyQualifiedErrorId : ItemNotFoundException,Microsoft.PowerShell.Commands.GetAliasCommand

In order to get an alias (or aliases, if there is more than one) for a cmdlet, the -Definition parameter must be used.

PS C:\> gal -Definition Get-Content

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Alias           cat -> Get-Content
Alias           gc -> Get-Content
Alias           type -> Get-Content

The Get-Service cmdlet returns the computer’s services, the Get-Process cmdlet returns the processes running on the computer, and the Get-ChildItem cmdlet returns the directories and/or files from the root of a drive or from a folder. Here is how a user can get the aliases for multiple cmdlets at the same time.

PS C:\> gal -Definition Get-Service,Get-Process,Get-ChildItem

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Alias           gsv -> Get-Service
Alias           gps -> Get-Process
Alias           ps -> Get-Process
Alias           dir -> Get-ChildItem
Alias           gci -> Get-ChildItem
Alias           ls -> Get-ChildItem

There are a few other cmdlets that allow a user to work with aliases. By using the Get-Command cmdlet (or its alias–if it has one), additional cmdlets can be returned that all end with -Alias.

PS C:\> Get-Command *-Alias

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Cmdlet          Export-Alias                                       Microsoft.PowerShell.Utility
Cmdlet          Get-Alias                                          Microsoft.PowerShell.Utility
Cmdlet          Import-Alias                                       Microsoft.PowerShell.Utility
Cmdlet          New-Alias                                          Microsoft.PowerShell.Utility
Cmdlet          Set-Alias                                          Microsoft.PowerShell.Utility

Export-Alias: Exports information about currently defined aliases to a file.

PS C:\> Export-Alias -Path 'C:\aliases.txt'

Import-Alias: Imports an alias, or aliases, from a file.

PS C:\> Import-Alias -Path 'C:\ImportedAliases.txt'

Trying to import aliases that already exist will cause an error for every alias Windows PowerShell tries to import (that already exists).

PS C:\> Export-Alias -Path 'C:\aliases.txt'
PS C:\> Import-Alias -Path 'C:\aliases.txt'
Import-Alias : The alias is not allowed, because an alias with the name ‘ac’ already exists.
At line:1 char:1
+ Import-Alias -Path ‘C:\aliases.txt’
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceExists: (ac:String) [Import-Alias], SessionStateException
    + FullyQualifiedErrorId : AliasAlreadyExists,Microsoft.PowerShell.Commands.ImportAliasCommand

New-Alias: Creates a new alias.
Set-Alias: Changes an existing alias, or creates an alias if it does not already exist.

PS C:\> New-Alias -Name MyAlias -Value Get-Process
PS C:\> MyAlias | select -First 4

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    224      19     3440        772   110    16.50   4612 ALMon
    164      14     2476       2108    44     5.36   2744 ALsvc
     77       9     1336       5288    75   137.55   4076 ApMsgFwd
     90       8     1372       5788    76   162.11   4324 ApntEx

PS C:\> Set-Alias -Name MyAlias -Value Get-Service
PS C:\> MyAlias | select -First 4

Status   Name               DisplayName
------   ----               -----------
Running  AdobeARMservice    Adobe Acrobat Update Service
Stopped  AdobeFlashPlaye... Adobe Flash Player Update Service
Stopped  AeLookupSvc        Application Experience
Stopped  ALG                Application Layer Gateway Service

Bonus Information

Use the Measure-Object cmdlet, or the count property, to find out how many aliases Windows PowerShell knows about.

PS C:\> Get-Alias | Measure-Object

Count    : 182
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

PS C:\> gal | measure

Count    : 182
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

PS C:\> (gal | measure).count
182
PS C:\> (gal).count
182

Real World

While aliases are helpful in the console, the belief is that they should not be used in a script file (.ps1 file). Using full cmdlet names in a script is preferred for script readability. There are plenty of people writing Windows PowerShell who adhere to this best practice even while using aliases for the object cmdlets (select for Select-Object, where for Where-Object, etc.).

Learn More

This information, and more, are stored in the help file about_Aliases that comes with Windows PowerShell. This information can be read by typing any of the commands below. The first example will display the help file in the Windows PowerShell console, the second example will open the full help in its own window, and the third example will send the contents of the help file to the clipboard (so it can be pasted into Word, Notepad, etc.), and the fourth example will open the help file in Notepad.

PS C:\> Get-Help about_aliases
PS C:\> Get-Help about_aliases -ShowWindow
PS C:\> Get-Help about_aliases | clip
PS C:\> Notepad C:\Windows\System32\WindowsPowerShell\v1.0\en-US\about_Aliases.help.txt

There is a built-in, automatic variable $PSHOMEthat stores the installation path of Windows PowerShell. This means that the third example above could have been partially written using that variable.

PS C:\> Notepad $PSHOME\en-us\about_Aliases.help.txt