Register for Semperis' Hybrid Identity Protection (HIP) Conference - June 30 - July 1 Register for Semperis' Hybrid Identity Protection (HIP) Conference - June 30 - July 1
PowerShell

PowerShell Problem Solver: Finding Orphan Aliases

Efficiency is paramount when working with the PowerShell console to get work done. Because there’s a lot of typing involved when working with PowerShell, there’s several features that intend to limit typing, such as tab completion for command and parameter names. Another ease-of-use feature is the PowerShell alias. Instead of typing Get-WmiObject, you can type a shortcut, gwmi. There’s nothing wrong with that. When it comes time to writing a script, however, the best practice is to use full cmdlet and parameter names. Another use of aliases is as transition-aids, which is demonstrated by using dir instead of having to use Get-ChildItem.

PowerShell automatically defines many aliases whenever you start a new session. You can also create your own aliases. You might do this for your own functions or even command-line tools. In my PowerShell profile, I have a command like this:

Set-alias –name np –value notepad.exe

At any time, I can type np and Notepad will launch. My PowerShell profile has grown over the years, and it has been moved around as I have changed computers. Recently I realized I had some aliases that were pointing to items that no longer existed. It doesn’t really matter if you have orphaned aliases, but I like things neat, so I decided that I needed a tool to test if an alias was still valid. I can use the Get-Alias cmdlet to get details.

Using get-alias to get details in Windows PowerShell. (Image Credit: Jeff Hicks)
Using get-alias to get details in Windows PowerShell. (Image Credit: Jeff Hicks)

As I expected np leads to notepad.exe. Let’s peak behind the curtain and see if there is anything else I can use.

Excellent. The definition property gives me the complete command. This is important because I can use Get-Command to verify it.

$a = Get-alias np
get-command -Name $a.Definition
Using get-command in Windows PowerShell. (Image Credit: Jeff Hicks)
Using get-command in Windows PowerShell. (Image Credit: Jeff Hicks)

That looks good and should mean that notepad.exe exists. But let’s test with something that I now will fail. I’ll create an alias that points to a non-existent command.

set-alias -name foo -value c:\windows\system32\foobar.exe

PowerShell will happily create the alias.

Creating an alias in Windows PowerShell. (Image Credit: Jeff Hicks)
Creating an alias in Windows PowerShell. (Image Credit: Jeff Hicks)

But let’s test for the backing command.

Testing the backing command in Windows PowerShell. (Image Credit: Jeff Hicks)
Testing the backing command in Windows PowerShell. (Image Credit: Jeff Hicks)

I get an exception. That is useful, because now I know I can use a try-catch statement.

Try { $c = Get-Command $b.Definition -erroraction Stop;$True} Catch { $false}

This is a quick and dirty proof of concept.

Using our try-catch statement for our alias. (Image Credit: Jeff Hicks)
Using our try-catch statement for our alias. (Image Credit: Jeff Hicks)

Now I have something that I can wrap into a function to test an alias. Here’s my finished Test-Alias command.

#requires -version 4.0

Function Test-Alias {
<#
.Synopsis
Test if an alias is valid.
.Description
Use this command to verify that an alias is pointing to a command that still exists. Use -Quiet to get only a True/False result.

NOTE:
Using this command will have a side-effect of importing associated modules. If the alias points to a command in a PSSnapin, you will get an incorrect result unless you add the PSSnapin first.
.Parameter Name
The alias name. You can pipe Get-Alias to this command.
.Parameter Quiet
Only display True or False.
.Example
PS C:\> test-alias gsv,ps,foo,np | format-table -AutoSize

Name Definition              Test
---- ----------              ----
gsv  Get-Service             True
ps   Get-Process             True
foo  x                      False
np   C:\windows\notepad.exe  True
.Example
PS C:\> get-alias | test-alias | where {! $_.Test} | format-table -AutoSize

Name  Definition                                                        Test
----  ----------                                                        ----
chr   C:\Users\Jeff\AppData\Local\Google\Chrome\Application\chrome.exe False
foo   x                                                                False
pe    G:\sysinternals\procexp.exe                                      False
TryMe Get-Foo                                                          False

Find aliases that won't work.
.Example
PS C:\> test-alias gci -quiet
True

A simple boolean test
.Notes
Last Updated: 4/2/2015
Version     : 1.0

Learn more about PowerShell:
http://jdhitsolutions.com/blog/essential-powershell-resources/

  ****************************************************************
  * 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.             *
  ****************************************************************
.Link
Get-Alias
Get-Command
.Inputs
[string]
.Outputs
[Boolean]
[PSCustomObject]
#>


[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory,HelpMessage="Enter the name of an alias",
ValueFromPipeline,ValueFromPipelineByPropertyName)]
[ValidateNotNullorEmpty()]
[string[]]$Name,
[switch]$Quiet
)

Begin {
    Write-Verbose "Starting $($MyInvocation.Mycommand)"  
    
} #begin

Process {

    foreach ($alias in $Name) {
        Write-Verbose "Testing alias    : $alias"
 
        Try {
             $def = (Get-Alias -Name $alias -ErrorAction Stop).Definition 
        }
        Catch {
            Write-Warning "No alias found called $alias"
        }
        
        if ($def) {
           Try {
                Write-Verbose "Verifying command: $def"
                if (Get-Command -Name $def -erroraction Stop) {
                    $tested = $True
                }
            }
            Catch {
                $tested = $False
            }

            if ($Quiet) {
                Write $tested
            }
            else {
                #write a custom object to the pipeline
                [pscustomobject]@{
                Name = $alias
                Definition = $def
                Test = $Tested
                }
            }
        
            #clear $def so it doesn't get accidentally re-used
            Remove-Variable -Name Def
        } #if $def
        

    } #foreach
} #process

End {
    Write-Verbose "Ending $($MyInvocation.Mycommand)"
} #end

} #end Test-Alias

#create an alias
Set-Alias -Name ta -Value Test-Alias

Test-Alias Help. (Image Credit: Jeff Hicks)
Test-Alias Help. (Image Credit: Jeff Hicks)

You can test an alias by name.

Testing the alias in PowerShell by name. (Image Credit: Jeff Hicks)
Testing the alias in PowerShell by name. (Image Credit: Jeff Hicks)

Or you can pipe names to it.

Piping a name to the alias. (Image Credit: Jeff Hicks)
Piping a name to the alias. (Image Credit: Jeff Hicks)

The default behavior is to write a custom object to the pipeline, but I also thought there might be a time when you want to test and need a simple Boolean result. Test-Connection works the same way.

If you were scripting with my function, then this means you could write a simple if statement like this:

If (test-alias ls) {
#found
}

But I don’t need a script to validate all of my aliases. I can do it from the console.

get-alias | test-alias | where {-Not $_.Test} | format-table –AutoSize
Validating alias from the console. (Image Credit: Jeff Hicks)
Validating alias from the console. (Image Credit: Jeff Hicks)

Now I know what to clean up. Although I can’t tell where these aliases were defined, if you want to test aliases defined in your profile, start a new PowerShell session and run Test-Alias.

There are a few caveats with Test-Alias. Because it uses Get-Command, if you have an alias that points to a command in a module that isn’t currently loaded in your session, that module will be loaded during the process of using Test-Alias.

As an example, let’s say I have an alias called gau that points to Get-ADUser from the ActiveDirectory module. PowerShell will load the ActiveDirectory module during the process of testing the alias. This is because of PowerShell’s autoloading feature introduced in PowerShell 3.0. This also means that any alias that point to commands in a PSSnapin, will fail to properly resolve, unless you first manually add the PSSnapin.

Even if you don’t need to test aliases, I hope you learned a few thing about creating PowerShell tools. As always I’d love to hear what you think.

Related Topics:

BECOME A PETRI MEMBER:

Don't have a login but want to join the conversation? Sign up for a Petri Account

Register
Comments (0)

Leave a Reply

Register for the Hybrid Identity Protection (HIP) Europe Conference!

Hybrid Identity Protection (HIP) Europe 2021 - Virtual Conference

Mobile workforces, cloud applications, and digitalization are changing every aspect of the modern enterprise. And with radical transformation come new business risks. Hybrid Identity Protection (HIP) is the premier educational forum for identity-centric practitioners. At the inaugural HIP Europe, join your local IAM experts and Microsoft MVPs to learn all the latest from the Hybrid Identity world.