Tag Archives: Select-String

Put the Alias Description Property to Work II

In part I of Put the Alias Description Property to Work, I showed and explained how to add a description property to each of my personal, profile script-created aliases. This allowed me to use a basic function, that filtered the Get-Alias results to return only my aliases. In this post, we will take this one step further and we will add descriptions to the built-in aliases in PowerShell. If you remember, we determined that most of the built-in aliases do not include a source property. Therefore, sorting or filtering on that property is of little value.

The source property is not a settable property, meaning that no matter how hard I try, I cannot edit it. The Description property, as we learned, is open for me to make changes. This is also true with built-in aliases and not just the aliases I create. Here is what we are going to do. All the aliases resolve to a command, and all but four of those commands have a source property. We are going to acquire a command’s source and set it as the description of its alias. This might make more sense with some examples, so let’s start there.

But before we do, let’s look at the default properties returned by Get-Alias.

Get-Alias | Get-Random
CommandType     Name                Version    Source
-----------     ----                -------    ------
Alias           pwd -> Get-Location 

Get-Alias returns CommandType, Name, Version, and Source. Kind of. Name is really a property called DisplayName. It may be better to know that now. In the first, real example, we are also going to use Get-Random to randomly select an alias for us. Then, we will return its ResolvedCommandName, DisplayName, Description, and Options properties.

$Alias = Get-Alias | Get-Random
Get-Alias -Name $Alias |
    Select-Object ResolvedCommandName,DisplayName,Description,Options
ResolvedCommandName DisplayName               Description  Options
------------------- -----------               -----------  -------
Connect-PSSession   cnsn -> Connect-PSSession             ReadOnly

Notice that currently, its Description property is blank. In the next example, we will use the value stored in $Alias and run it through a ForEach-Object loop. Take a look at the PowerShell now, and I will do that recently experimented with, line-by-line explanation further below.

Get-Alias -Name $Alias | ForEach-Object {
    if ($_.Description -eq '') {
        $Params = @{
            Name = $_.Name
            Value = $_.ResolvedCommandName
            Description = if ((Get-Command -Name $_.ResolvedCommandName).Source) {
                ((Get-Command -Name $_.ResolvedCommandName).Source)
            } else {'None'}
            Option = $_.Options
            Force = $true
        }
        Set-Alias @Params -Verbose
    }
}

VERBOSE: Performing the operation "Set Alias" on target "Name: cnsn Value: Connect-PSSession".

Line 1: Pipe our single alias into a ForEach-Object loop.
Line 2: Continue into an if construct if the alias’ description is empty. If it is not, we would move on to the next alias if there were more than just one.
Line 3: Create a hash table and store it in the $Params variable.
Line 4, 5: Add the name and the resolved command name into the Name and Value keys, respectively.
Line 6 – 8: Add a description to the Description key based on whether or not there is a source. If there is a source, add it, if there is not a source add the string None.
Line 9, 10: Add the options and $true value into the Option and Force keys, respectively.
Line 12: Invoke the Set-Alias command, splatting the $Params hash table as parameters and associated parameter values.

Let’s rerun the command we did just a minute ago.

Get-Alias -Name $Alias |
    Select-Object ResolvedCommandName,DisplayName,Description,Options
ResolvedCommandName DisplayName               Description                Options
------------------- -----------               -----------                -------
Connect-PSSession   cnsn -> Connect-PSSession Microsoft.PowerShell.Core ReadOnly

We have taken the source of the command Connect-PSSession and placed its value into the description of an alias that resolves to Connect-PSSession. It is not genius, but it is something!

Moving on, the next command pipes out all the known aliases and the values in each of the included properties. I will only include the first ten, as there are almost 150.

Get-Alias |
    Select-Object ResolvedCommandName,DisplayName,Description,Options
ResolvedCommandName DisplayName           Description              Options
------------------- -----------           -----------              -------
Where-Object        ? -> Where-Object                   ReadOnly, AllScope
ForEach-Object      % -> ForEach-Object                 ReadOnly, AllScope
Add-Content         ac -> Add-Content                             ReadOnly
Clear-Host          c -> Clear-Host        tommymaynard               None
Get-Content         cat -> Get-Content                                None
Set-Location        cd -> Set-Location                            AllScope
Set-Location        chdir -> Set-Location                             None
Clear-Content       clc -> Clear-Content                          ReadOnly
Clear-Host          clear -> Clear-Host                               None
Clear-History       clhy -> Clear-History                         ReadOnly

This should look familiar. There are a couple of differences in this code block compared to the one above. One, we are piping in all of the aliases (not just one), and two, there is no Verbose parameter included when the Set-Alias command is invoked. We do not need to output the change made to each alias. Yeah, no thanks.

Get-Alias | ForEach-Object {
    if ($_.Description -eq '') {
        $Params = @{
            Name = $_.Name
            Value = $_.ResolvedCommandName
            Description = if ((Get-Command -Name $_.ResolvedCommandName).Source) {
                ((Get-Command -Name $_.ResolvedCommandName).Source)
            } else {'None'}
            Option = $_.Options
            Force = $true
        }
        Set-Alias @Params
    }
}

This is the same command we saw earlier. I have only included the first ten here, as well. Notice the changes made to the Description property for each alias. Now I can easily see which alias goes with which source.

Get-Alias |
    Select-Object ResolvedCommandName,DisplayName,Description,Options
ResolvedCommandName DisplayName           Description                                Options
------------------- -----------           -----------                                -------
Where-Object        ? -> Where-Object     Microsoft.PowerShell.Core       ReadOnly, AllScope
ForEach-Object      % -> ForEach-Object   Microsoft.PowerShell.Core       ReadOnly, AllScope
Add-Content         ac -> Add-Content     Microsoft.PowerShell.Management           ReadOnly
Clear-Host          c -> Clear-Host       tommymaynard                                  None
Get-Content         cat -> Get-Content    Microsoft.PowerShell.Management               None
Set-Location        cd -> Set-Location    Microsoft.PowerShell.Management           AllScope
Set-Location        chdir -> Set-Location Microsoft.PowerShell.Management               None
Clear-Content       clc -> Clear-Content  Microsoft.PowerShell.Management           ReadOnly
Clear-Host          clear -> Clear-Host   None                                          None
Clear-History       clhy -> Clear-History Microsoft.PowerShell.Core                 ReadOnly

Like all worthy PowerShell code, I am going to wrap this in a function, and for the foreseeable future, copy this into my profile script. There is one change in this version that should be mentioned. The Scope parameter is now being included inside the $Params hash table with the Global value. Like the other parameters and parameters values in this hash table, it will be splatted onto the Set-Alias cmdlet when it is invoked. While the Scope parameter is not required with Get-Alias, it absolutely is with Set-Alias. We want the aliases that exist outside the function to be the ones we modify.

function Set-AliasDescription
Get-Alias | ForEach-Object {
    if ($_.Description -eq '') {
        $Params = @{
            Name = $_.Name
            Value = $_.ResolvedCommandName
            Description = if ((Get-Command -Name $_.ResolvedCommandName).Source) {
                ((Get-Command -Name $_.ResolvedCommandName).Source)
            } else {'None'}
            Option = $_.Options
            Force = $true
            Scope = 'Global'
        }
        Set-Alias @Params
    }
}
Set-AliasDescription

With the descriptions set, we can filter such as in the next two examples. Remember, the Get-Alias Description property is not displayed by default.

Get-Alias | Where-Object -Property Description -eq 'None'
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           clear -> Clear-Host
Alias           cls -> Clear-Host
Alias           man -> help
Alias           md -> mkdir
Get-Alias | Where-Object -Property Description -like '*core'
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           ? -> Where-Object
Alias           % -> ForEach-Object
Alias           clhy -> Clear-History
Alias           cnsn -> Connect-PSSession
Alias           dnsn -> Disconnect-PSSession
Alias           etsn -> Enter-PSSession
Alias           exsn -> Exit-PSSession
Alias           foreach -> ForEach-Object
Alias           gcm -> Get-Command
Alias           ghy -> Get-History
Alias           gjb -> Get-Job
Alias           gmo -> Get-Module
Alias           gsn -> Get-PSSession
Alias           h -> Get-History
Alias           history -> Get-History
Alias           icm -> Invoke-Command
Alias           ihy -> Invoke-History
Alias           ipmo -> Import-Module
Alias           nmo -> New-Module
Alias           nsn -> New-PSSession
Alias           oh -> Out-Host
Alias           r -> Invoke-History
Alias           rcjb -> Receive-Job
Alias           rcsn -> Receive-PSSession
Alias           rjb -> Remove-Job
Alias           rmo -> Remove-Module
Alias           rsn -> Remove-PSSession
Alias           sajb -> Start-Job
Alias           spjb -> Stop-Job
Alias           where -> Where-Object
Alias           wjb -> Wait-Job

Put the Alias Description Property to Work

I use a small handful of my own PowerShell aliases and of course some of those included with PowerShell natively. There is a best practice around aliases in PowerShell, and that is to not use them in anything that is going to live longer than a one-time use. Therefore, do not use them in scripts, functions, and modules (except in the case where your module exports aliases). I would recommend not even using them in forum posts or emails or work-only documentation. Keep them away from your own personal documentation, too. You might know what spsv, ndr, rcjb, and shcm mean today, at this moment, but you may not a few months from now. They have a place, however. Use them in your console, or shell, to speed up what would otherwise be a longer, manually entered command. Let’s check my $PROFILE script and see how many of my own PowerShell aliases I have.

Get-Content -Path $PROFILE | Select-String -Pattern '-alias'
Set-Alias -Name c -Value Clear-Host
Set-Alias -Name psrh -Value Open-PSReadLineHistoryFile
Set-Alias -Name sel -Value Select-Object
Set-Alias -Name wa -Value Watch-Apartment
Set-Alias -Name psgal -Value Show-PSGalleryProjectJob
'l','link' | ForEach-Object {Set-Alias -Name $_ -Value Find-Link}
Set-Alias -Name cts -Value Convert-TextToSpeech

Based on the above command and its results, I have seven. Even that is too many to remember when they are not being used often. Well, I forgot one recently and I was annoyed I had to open my profile script file and search for -alias. Sure, I could have used the above Get-Content command I wrote and tried out a minute ago, but why? Why not avoid ever having to search for my aliases from inside a source file again. They exist in the PowerShell session; why not search there? I am already in the session.

My first thought was, why does Microsoft not indicate which aliases are built-in? There is a source property. It is just too bad that not many aliases include that information. It would be much simple to filter aliases that way, removing those that are not a part of a Microsoft.<something>.<something> module. But, if you return this property there is often nothing. To begin with, there are currently 147 aliases on my machine.

(Get-Alias).Count
147

Only seven include a value in their source property.

Get-Alias | Where-Object Source -ne ''
CommandType     Name                    Version    Source
-----------     ----                    -------    ------
Alias           fhx -> Format-Hex       7.0.0.0    Microsoft.PowerShell.Utility
Alias           gcb -> Get-Clipboard    7.0.0.0    Microsoft.PowerShell.Management
Alias           gin -> Get-ComputerInfo 7.0.0.0    Microsoft.PowerShell.Management
Alias           gtz -> Get-TimeZone     7.0.0.0    Microsoft.PowerShell.Management
Alias           scb -> Set-Clipboard    7.0.0.0    Microsoft.PowerShell.Management
Alias           stz -> Set-TimeZone     7.0.0.0    Microsoft.PowerShell.Management

Weird right? One command alias is from the Microsoft.PowerShell.Utility module and six are from the Microsoft.PowerShell.Management module. It is not like there are only one and six aliases, respectively, from each entire module. There are plenty more commands and aliases, as well. Take a look; I have included both modules. We will begin with the Microsoft.PowerShell.Utility module.

((Get-Command -Module Microsoft.PowerShell.Utility).Name |
    ForEach-Object {Get-Alias -Definition $_ -ErrorAction SilentlyContinue}).Count
43
(Get-Command -Module Microsoft.PowerShell.Utility).Name |
    ForEach-Object {Get-Alias -Definition $_ -ErrorAction SilentlyContinue}
CommandType     Name                        Version    Source
-----------     ----                        -------    ------
Alias           clv -> Clear-Variable
Alias           compare -> Compare-Object
Alias           diff -> Compare-Object
Alias           dbp -> Disable-PSBreakpoint
Alias           ebp -> Enable-PSBreakpoint
Alias           epal -> Export-Alias
Alias           epcsv -> Export-Csv
Alias           fc -> Format-Custom
Alias           fhx -> Format-Hex           7.0.0.0    Microsoft.PowerShell.Utility
Alias           fl -> Format-List
Alias           ft -> Format-Table
Alias           fw -> Format-Wide
Alias           gal -> Get-Alias
Alias           gerr -> Get-Error
Alias           gm -> Get-Member
Alias           gbp -> Get-PSBreakpoint
Alias           gcs -> Get-PSCallStack
Alias           gu -> Get-Unique
Alias           gv -> Get-Variable
Alias           group -> Group-Object
Alias           ipal -> Import-Alias
Alias           ipcsv -> Import-Csv
Alias           iex -> Invoke-Expression
Alias           irm -> Invoke-RestMethod
Alias           iwr -> Invoke-WebRequest
Alias           measure -> Measure-Object
Alias           nal -> New-Alias
Alias           nv -> New-Variable
Alias           ogv -> Out-GridView
Alias           rbp -> Remove-PSBreakpoint
Alias           rv -> Remove-Variable
Alias           select -> Select-Object
Alias           sls -> Select-String
Alias           sal -> Set-Alias
Alias           sbp -> Set-PSBreakpoint
Alias           set -> Set-Variable
Alias           sv -> Set-Variable
Alias           shcm -> Show-Command
Alias           sort -> Sort-Object
Alias           sleep -> Start-Sleep
Alias           tee -> Tee-Object
Alias           echo -> Write-Output
Alias           write -> Write-Output

And continue with the Microsoft.PowerShell.Management module.

((Get-Command -Module Microsoft.PowerShell.Management).Name |
    ForEach-Object {Get-Alias -Definition $_ -ErrorAction SilentlyContinue}).Count
62
(Get-Command -Module Microsoft.PowerShell.Management).Name |
    ForEach-Object {Get-Alias -Definition $_ -ErrorAction SilentlyContinue}
CommandType     Name                         Version    Source
-----------     ----                         -------    ------
Alias           ac -> Add-Content
Alias           clc -> Clear-Content
Alias           cli -> Clear-Item
Alias           clp -> Clear-ItemProperty
Alias           cvpa -> Convert-Path
Alias           copy -> Copy-Item
Alias           cp -> Copy-Item
Alias           cpi -> Copy-Item
Alias           cpp -> Copy-ItemProperty
Alias           dir -> Get-ChildItem
Alias           gci -> Get-ChildItem
Alias           ls -> Get-ChildItem
Alias           gcb -> Get-Clipboard         7.0.0.0    Microsoft.PowerShell.Management
Alias           gin -> Get-ComputerInfo      7.0.0.0    Microsoft.PowerShell.Management
Alias           cat -> Get-Content
Alias           gc -> Get-Content
Alias           type -> Get-Content
Alias           gi -> Get-Item
Alias           gp -> Get-ItemProperty
Alias           gpv -> Get-ItemPropertyValue
Alias           gl -> Get-Location
Alias           pwd -> Get-Location
Alias           gps -> Get-Process
Alias           ps -> Get-Process
Alias           gdr -> Get-PSDrive
Alias           gsv -> Get-Service
Alias           gtz -> Get-TimeZone          7.0.0.0    Microsoft.PowerShell.Management
Alias           ii -> Invoke-Item
Alias           mi -> Move-Item
Alias           move -> Move-Item
Alias           mv -> Move-Item
Alias           mp -> Move-ItemProperty
Alias           ni -> New-Item
Alias           mount -> New-PSDrive
Alias           ndr -> New-PSDrive
Alias           popd -> Pop-Location
Alias           pushd -> Push-Location
Alias           del -> Remove-Item
Alias           erase -> Remove-Item
Alias           rd -> Remove-Item
Alias           ri -> Remove-Item
Alias           rm -> Remove-Item
Alias           rmdir -> Remove-Item
Alias           rp -> Remove-ItemProperty
Alias           rdr -> Remove-PSDrive
Alias           ren -> Rename-Item
Alias           rni -> Rename-Item
Alias           rnp -> Rename-ItemProperty
Alias           rvpa -> Resolve-Path
Alias           scb -> Set-Clipboard         7.0.0.0    Microsoft.PowerShell.Management
Alias           si -> Set-Item
Alias           sp -> Set-ItemProperty
Alias           cd -> Set-Location
Alias           chdir -> Set-Location
Alias           sl -> Set-Location
Alias           stz -> Set-TimeZone          7.0.0.0    Microsoft.PowerShell.Management
Alias           saps -> Start-Process
Alias           start -> Start-Process
Alias           sasv -> Start-Service
Alias           kill -> Stop-Process
Alias           spps -> Stop-Process
Alias           spsv -> Stop-Service

In the end, the Microsoft.PowerShell.Utilityhas 43 aliases and the Microsoft.PowerShell.Management module has 63 aliases. Maybe there is a good reason for the source, not being included most of the time. But, we are here to make my aliases easier to find. The source property is not a settable property, therefore, we are going to use the Description property to indicate when an alias is one of mine. Here are my aliases from earlier, each with a new addition to their Description property. It is my name.

Set-Alias -Name c -Value Clear-Host -Description 'tommymaynard'
Set-Alias -Name psrh -Value Open-PSReadLineHistoryFile -Description 'tommymaynard'
Set-Alias -Name sel -Value Select-Object -Description 'tommymaynard'
Set-Alias -Name wa -Value Watch-Apartment -Description 'tommymaynard'
Set-Alias -Name psgal -Value Show-PSGalleryProjectJob -Description 'tommymaynard'
'l','link' | ForEach-Object {Set-Alias -Name $_ -Value Find-Link -Description 'tommymaynard'}
Set-Alias -Name cts -Value Convert-TextToSpeech -Description 'tommymaynard'

Now, after my profile script runs, I can invoke a modified Get-Alias command to return just my aliases from the current PowerShell session.

Get-Alias | Where-Object -Property Description -eq 'tommymaynard'
CommandType     Name                               Version    Source
-----------     ----                               -------    ------
Alias           c -> Clear-Host
Alias           cts -> Convert-TextToSpeech
Alias           l -> Find-Link
Alias           link -> Find-Link
Alias           psgal -> Show-PSGalleryProjectJob
Alias           psrh -> Open-PSReadLineHistoryFile
Alias           sel -> Select-Object
Alias           wa -> Watch-Apartment

This post would not be complete if there was no new function to add to my $PROFILE script. So, with that, here is that new function and alias. Notice I added the description to the new alias, too!

Set-Alias -Name gmal -Value Get-MyAlias -Description 'tommymaynard'
function Get-MyAlias {
    Get-Alias | Where-Object -Property Description -eq 'tommymaynard'
}
gmal
CommandType     Name                              Version    Source
-----------     ----                               -------    ------
Alias           c -> Clear-Host
Alias           cts -> Convert-TextToSpeech
Alias           gmal -> Get-MyAlias
Alias           l -> Find-Link
Alias           link -> Find-Link
Alias           psgal -> Show-PSGalleryProjectJob
Alias           psrh -> Open-PSReadLineHistoryFile
Alias           sel -> Select-Object
Alias           wa -> Watch-Apartment

Part II has been published!

Parsing nslookup: Only Return the Resolved IP

It was late last Friday evening when a vendor and I worked though some maintenance. As a part of the work (which actually lasted well into the weekend), we needed to remove two DNS A records and replace them with CNAME records. I figured that before the change, I would return the current resolution results, so I would be able to ensure the DNS change took place, after the night’s weekend’s work.

You’re probably familiar with the standard output of nslookup.

PS> nslookup subdomain.mydomain.com
Server:  mydomain.com
Address:  10.10.10.1

Name:    subdomain.mydomain.com
Address:  10.20.30.40

These results indicate the name, subdomain.mydomain.com, resolves to an IP address of 10.20.30.40 when using the mydomain.com nameserver, at 10.10.10.1. I wanted to clean up this output, and since I had time to kill while fighting off sleep, I figured — why not? I started by piping the results of nslookup to the Select-String cmdlet looking for the string, ‘Address.’ As you can see in the example below, it returns both the IP of the server where the resolution took place, and the results. After that, there doesn’t seem to be a way to distinguish one line from the other. I needed another way to only return the results.

PS> nslookup subdomain.mydomain.com | Select-String Address

Address: 10.10.10.1
Address: 10.20.30.40

I decided to pipe the command above to the Get-Member cmdlet to see what kind of object the Select-String cmdlet returned. The property that jumped out at me right away was LineNumber — was it really going to be that easy?

   TypeName: Microsoft.PowerShell.Commands.MatchInfo

Name         MemberType Definition
----         ---------- ----------
Equals       Method     bool Equals(System.Object obj)
GetHashCode  Method     int GetHashCode()
GetType      Method     type GetType()
RelativePath Method     string RelativePath(string directory)
ToString     Method     string ToString(), string ToString(string directory)
Context      Property   Microsoft.PowerShell.Commands.MatchInfoContext Context {get;set;}
Filename     Property   string Filename {get;}
IgnoreCase   Property   bool IgnoreCase {get;set;}
Line         Property   string Line {get;set;}
LineNumber   Property   int LineNumber {get;set;}
Matches      Property   System.Text.RegularExpressions.Match[] Matches {get;set;}
Path         Property   string Path {get;set;}
Pattern      Property   string Pattern {get;set;}

Here’s the thing, the output of nslookup is always the same: this on a this line, and that on that line. This is likely why the LineNumber property looked so promising, so quickly. Now that we know we have this property, let’s try and use it. While my first line number guess worked (read, dumb luck), let’s do it wrong first. Consider again our most recent output below.

PS> nslookup subdomain.mydomain.com | Select-String Address

Address: 10.10.10.1
Address: 10.20.30.40

You might think you would want line 3 of our output, but that’s not correct. Even though Select-String has trimmed down the output, the line numbers are all still intact. Look at the very first example at the top of this post. Line 3 is blank — it’s line 5 that we want! Take a look.

PS> nslookup subdomain.mydomain.com | Select-String Address | Where-Object LineNumber -eq 5

Address: 10.20.30.40

If you want to store this as as a string, use the .ToString() method.

PS> (nslookup subdomain.mydomain.com | Select-String Address | Where-Object LineNumber -eq 5).ToString()
Address: 10.20.30.40

And, if you want to remove the word “address,” the colon, and the space, in order to only return the IP address, then you can parse the string by using the .Split() method. The example below splits the string at the space and return the last element — what’s on the right side of the space.

PS> (nslookup subdomain.mydomain.com | Select-String Address | Where-Object LineNumber -eq 5).ToString().Split(' ')[-1]
10.20.30.40

I hope this was helpful for someone. Until next time.

Finding Non-Resolveable IP Addresses

When preparing to bring new servers online, I often need a quick way to know what IP addresses, in a range of IP addresses, do not resolve to a name. This is the first step to determine unused IP addresses. The first time I did this was back before I was running Windows PowerShell 4.0 on Windows 8.1 which includes the Resolve-DnsName cmdlet. It meant I needed to use the command line tool nslookup; we’ll discuss both options in this post.

The first two examples below, use nslookup to try and resolve two different IPs: one that does resolve and one that doesn’t. It should be noted that resolving an IP address to a name is called Reverse DNS (rDNS). In the first example we have an IP address that resolves. That means it likely wouldn’t be an IP address I would want to use for a new server.

PS C:\> nslookup 10.10.10.2
Server:  dns.mydomain.com
Address:  10.10.10.1

Name:    dc01.mydomain.com
Address:  10.10.10.2

The next example shows an IP address that doesn’t resolve, and therefore, would likely be a useable IP address for one of my servers. Remember, we want to collect our failed name resolutions.

PS C:\> nslookup 10.10.10.4
Server:  dns.mydomain.com
Address:  10.10.10.1

*** dns.mydomain.com can't find 10.10.10.4: Non-existent domain

Let’s assume our range of possibly useable IPs is 10.10.10.2 – 10.10.10.254. This assumes we’re using .1 for the gateway and .255 for broadcast. We’ll use a sub range of this full range (10.10.10.2 through 10.10.10.5) for most of our testing and examples. We’re going to use the range operator (..) to help automate checking the IP addresses.

PS C:\> 2..5 | ForEach-Object {nslookup 10.10.10.$_}
Server:  dns.mydomain.com
Address:  10.10.10.1

Name:    dc01.mydomain.com
Address:  10.10.10.2

Server:  dns.mydomain.com
Address:  10.10.10.1

Name:    dc02.mydomain.com
Address:  10.10.10.3

Server:  dns.mydomain.com
Address:  10.10.10.1

*** dns.mydomain.com can't find 10.10.10.4: Non-existent domain
Server:  dns.mydomain.com
Address:  10.10.10.1

*** dns.mydomain.com can't find 10.10.10.5: Non-existent domain

Our results, above, indicate that .2 and .3 are resolving and therefore are likely not useable for us. It also indicates we may be able to use .4 and .5, since those are not resolving. There’s still too much information returned by nslookup, so let’s clean that up by only returning failed resolutions.

In the example below, we’ll pipe our nslookup results to the Select-String cmdlet and search for the three asterisks (***) that begin each line where there is a failed name resolution. By default, the Select-String cmdlet does a regex match, so we’ll need to use the more literal approach and include the -SimpleMatch parameter of Select-String.

PS C:\> 2..5 | ForEach-Object {nslookup 10.10.10.$_ | Select-String -Pattern *** -SimpleMatch}
*** dns.mydomain.com can't find 10.10.10.4: Non-existent domain
*** dns.mydomain.com can't find 10.10.10.5: Non-existent domain

PowerShell 3.0 on Windows 8 and Server 2012 brought us the Resolve-DnsName cmdlet as part of the DnsClient module. This is essentially the replacement for nslookup on newer versions of PowerShell, on newer versions of the underlying operating system. By default, this cmdlet will return much more information on a successful resolution and an error on a failed resolution. Although I’ve truncated the successful results, there is an example of both a success and failure, below.

PS C:\> Resolve-DnsName -Name 10.10.10.2

Name                       Type   TTL   Section    NameHost
----                       ----   ---   -------    --------
2.10.10.10.in-addr.arpa    PTR    214   Answer     dc01.mydomain.com
...

PS C:\> Resolve-DnsName -Name 10.10.10.4
Resolve-DnsName : 10.10.10.4 : The filename, directory name, or volume label syntax is incorrect
At line:1 char:1
+ Resolve-DnsName -Name 10.10.10.4
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (10.10.10.4:String) [Resolve-DnsName], Win32Exception
    + FullyQualifiedErrorId : ERROR_INVALID_NAME,Microsoft.DnsClient.Commands.ResolveDnsName

Now, what we need to do is find the failed name resolutions for our IP test range without displaying errors. This will require a little more work, but gives us more control over what’s actually displayed, instead of relying on the default output of nslookup.

In this example, we’ll run Resolve-DnsName for each IP address in our test range: 10.10.10.2 through 10.10.10.5. If it is unable to resolve, we’ll suppress the error using the -ErrorAction parameter, and then display our own message to inform us which IPs in our range didn’t resolve.

PS C:\> 2..5 | ForEach-Object {
>> If (-not(Resolve-DnsName -Name 10.10.10.$_ -ErrorAction SilentlyContinue)) {
>> Write-Output -Verbose "10.10.10.$_ -- Not Resolving"}
>> }
>>
10.10.10.4 -- Not Resolving
10.10.10.5 -- Not Resolving

If you made it all the way down here, then thanks for reading this post; I hope you found something useful. I would like to mention how to use .NET to resolve names to IPs, and IPs to names. Here’s a couple examples of both using a Microsoft name and IP.

PS C:\> [System.Net.Dns]::GetHostAddresses('microsoft.com')

Address            : 783919750
AddressFamily      : InterNetwork
ScopeId            :
IsIPv6Multicast    : False
IsIPv6LinkLocal    : False
IsIPv6SiteLocal    : False
IsIPv6Teredo       : False
IsIPv4MappedToIPv6 : False
IPAddressToString  : 134.170.185.46

Address            : 3720129158
AddressFamily      : InterNetwork
ScopeId            :
IsIPv6Multicast    : False
IsIPv6LinkLocal    : False
IsIPv6SiteLocal    : False
IsIPv6Teredo       : False
IsIPv4MappedToIPv6 : False
IPAddressToString  : 134.170.188.221

PS C:\> [System.Net.Dns]::GetHostByAddress('134.170.185.46')

HostName                                Aliases                                 AddressList
--------                                -------                                 -----------
grv.microsoft.com                       {}                                      {134.170.185.46}