Using OutVariable — Why Don’t I Do that More Often?

This week, Microsoft Virtual Academy had two live events about DSC (Desired State Configuration), hosted by Jeffery Snover and Jason Helmick. I watched as much as I was able, but there were some problems at work that demanded my attention, and so I was grudgingly pulled away from a good portion of both sessions. Luckily for me, and for you, if you missed them, is that the videos should be up in the next two to three weeks. That will allow anyone who is interested the ability to move through the modules (think sections, not PowerShell modules) around other ongoing tasks — like work.

I didn’t start this post to discuss DSC, but instead because of what I watched Jeffery Snover do several times. While I’ve always been aware of the existence of the -OutVariable common parameter, I’m not even sure if I’ve ever used it or not (although I’m certain I’ve used -ErrorVariable). This parameter is a great way to view your command’s results immediately, and write them to a variable at the same time.

In this example, we return the computers’ names from (all of) Active Directory (AD) that have the word ‘physical’ somewhere inside their description property. The problem here is that if we need to generate this list a second time, we’ll have to run the command again. This can be resource intensive, depending on the command, and not inline with best practice — at least, my best practice.

PS C:\> (Get-ADComputer -Filter {Description -like '*physical*'}).Name
DC01
DC02
DC03
WEB01
WEB02

In this example, we write our results to the variable $Physical. The difference here is that we don’t write the results to the screen automatically, but only when we echo the variable’s contents.

PS C:\> $Physical = (Get-ADComputer -Filter {Description -like '*physical*'}).Name
PS C:\> $Physical
DC01
DC02
DC03
WEB01
WEB02

In this example, we combine the best of both worlds: instant results written to the screen, with the “same” values stored in a variable. Notice that when I echo the variable, $P, it returns more than just the Name. This is because all the properties were written to the variable, before we displayed only the Name property. Note: I’ve concatenated the results after the first computer’s full results.

PS C:\> (Get-ADComputer -Filter {Description -like '*physical*'} -OutVariable P).Name
DC01
DC02
DC03
WEB01
WEB02
PS C:\> $P
DistinguishedName : CN=DC01,OU=Domain Controllers,DC=mydomain,DC=com
DNSHostName       : dc01.mydomain.com
Enabled           : True
Name              : DC01
ObjectClass       : computer
ObjectGUID        : ...
SamAccountName    : DC01$
SID               : ...
UserPrincipalName :
...

Here’s how we can return only the Name property, using this variable.

PS C:\> $P.Name
DC01
DC02
DC03
WEB01
WEB02

Hopefully I can remember to use this common parameter more often. We’ve been taught to store our results in a variable, so we aren’t continually performing resource intensive queries. This is a great way to do that, with the option to have the results of a command written to the screen immediately. Adios, friends.

Extra – PowerShell Summit North America 2015

This series of posts was linked from PowerShell.org: https://powershell.org/2015/05/16/whats-it-like-at-powershell-summit/.

I decided in late February that I would document my trip and experience at the PowerShell Summit North America 2015 (that happens in April). This page is going to serve as the landing page for this project. I’ll link each of the new posts right from this post, so they can easily be read in chronological order (beginning at the bottom link), providing someone wants to do that, or reads any of this anyway. I truly believe this is going to be an incredible opportunity, and so I want to document my experience, and make it available to the PowerShell community, current and future. Thanks, and enjoy.

PowerShell Summit North America 2015 [#8]: Day Three
April 22, 2015

PowerShell Summit North America 2015 [#7]: Day Two
April 21, 2015

PowerShell Summit North America 2015 [#6]: Day One
April 20, 2015

PowerShell Summit North America 2015 [#5]: In Flight
April 19, 2015

PowerShell Summit North America 2015 [#4]: Closest
April 18, 2015

PowerShell Summit North America 2015 [#3]: Closer
April 13, 2015

PowerShell Summit North America 2015 [#2]: Close
April 10, 2015

PowerShell Summit North America 2015 [#1]: It’s So Far Away
February 22, 2015

Extra – PowerShell Summit North America 2015 [#1]

Read them all here: http://tommymaynard.com/extra-powershell-summit-north-america-2015-0-2015/

I decided I am going to write about my experience at my first PowerShell Summit—from beginning to end. The word ‘beginning’ is being used a bit loosely, as the PowerShell North America 2015 Summit is still 55 days away (see how I figured that out below). Regardless, I continue to find myself looking forward to this opportunity as it has every potential to be the highlight of my IT career, and so I’m going to post about it.

PS C:\> (New-TimeSpan -Start (Get-Date) -End 4/20/2015).Days
55
PS C:\> "$((New-TimeSpan -Start (Get-Date) -End 4/20/2015).Days) Days"
55 Days

Although, I’ve been registered for the summit for a few months now, and had my plane tickets for nearly as long, I just secured my hotel room. There was a bit of an internal debate with myself about where to stay. While some mentioned they prefer downtown, I just couldn’t expect that the daily cab fare both ways, and perhaps the extra cost of a downtown hotel, would be worth it. I really don’t know the first thing about North Carolina, or Charlotte, so it’s quite possible that I am very, very wrong.

That said, if anyone reading this is staying near the venue and wants to venture downtown for a meal, perhaps with others, then feel free speak up. Spitting the cab fare up a time or two would definitely be worth it, especially to discuss PowerShell over dinner. That is just something I cannot do with my wife and children. At least not often, and not for long—trust me, I’ve tried.

I’d be willing to discuss PowerShell over a meal closer to the venue, too. I’ve never eaten at a Ruby Tuesday, but that’s the closest food to my hotel. I added the link for me, but feel free to look at all the incredible food coming my way. Honestly, until today, I thought Ruby Tuesday was a buffet for retirees and grandparents, but some of that food looks surprisingly appetizing. The place looks much more appealing than I had assumed, too.

Moving on. Long before I knew I’d be attending the summit, PowerShell.org mentioned a program called Verified Effective. While the cost to take the test had always been mildly prohibitive, I was ecstatic to discover that the cost of the summit included this opportunity. Well, up until the program was cancelled really close after the time when I registered. My emotions were really being played with, even more so when I found out it was back on, although I was excited again. The difference was that the test would have to be taken in person (not remotely), and at the summit. I seriously may have been the first person to click and register on the Eventbrite URL that came via email. Based on opening the exam up to approximately 60 people, it appears that as of today, a touch more than half of the Verified Effective registrations have been claimed. I’m nervous, but looking forward to this opportunity.

I recently decided to read Don Jones’ and Jeffery Hicks’ Month of Lunches PowerShell books (book one & book two). I’ve finished the first one and a little over half way to complete the second title. I’d like to get these signed by their authors, but simply couldn’t do that without reading the books first. While I already know most of what I’ve read thus far, it has been a beneficial and comprehensive review. I long worried that in my learning, which was never front to back in any PowerShell book, that I may have missed something here and there. I had, and I’ve been filling in those little holes nicely since January. I would, without question, recommended these two titles to anyone that wants to learn PowerShell. They would’ve been a great way to learn, had I started with them first. Just find the money, buy them, learn it, and thank the three of us later—more so to them, of course.

I’ll stop here for now, but will be back with more summit ramblings as I find time and topics to cover. This really is going to be an amazing event. I’ll be up close and personal to people that think like I do, and that love automation and PowerShell. This occasion is going to bring together all the rock stars and celebrities of the PowerShell community. If you don’t hear more from me before my flight in April, then you can at least expect I’ll be writing on April 19th from the sky, somewhere between Arizona and North Carolina.

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}

Get Some Help for Later Reading

I’m just starting to get my hands wet with Microsoft Lync. As I often do, I use the Windows PowerShell cmdlets to help learn more about a product; I did this same thing with Hyper-V. The unfortunate thing about the GUI (think, an MMC snap-in for instance), is that the menu options don’t always tell you exactly what that option is going to do, or its exact purpose. While the GUI can be unclear, PowerShell tells you exactly what a cmdlet does. With that knowledge, I’ve often been able to relate a cmdlet, and its purpose, to its respective menu option in the GUI.

I wanted to read though the Lync cmdlet’s help files, but only the cmdlet name and its synopsis. Here’s the command I ran to extract this information for later reading.

PS C:> Get-Command -Module Lync | Get-Help | Select-Object Name,Synopsis | Export-Csv -Path C:\LyncCmdlets.csv -NoTypeInfomation

The command above works this way: it returns all the cmdlets included in the Lync Module and sends (pipes) those to the Get-Help cmdlet. The Get-Help cmdlet pipes its results to Select-Object which filters the returned properties to just the Name and Synopsis from each cmdlet’s help file. At the end, those filtered results are sent to Export-Csv which creates a file I can read at my leisure.

Although there’s over 500 cmdlets, it’s safe to say that many of the nouns (the part after the dash [-]) will be the same across some of the cmdlets. That means that many of the Get-* cmdlets will have a partnering Set-* cmdlet. Get reads information and Set changes it. These nouns may also have a matching New-*, Remove-*, and possibly even a Test-* cmdlet. Now off to do some reading…

A Quick, Learn Windows PowerShell in a Month of Lunches, Review

I’ve been using, and continuing to learn, Windows PowerShell for a while now. While I’ve used various resources to promote my learning and understanding, I had never sat down and actually read a PowerShell book, front to back. Well, now I have.

While I knew upwards of 95% of the content, I went ahead and read Learn Windows PowerShell in a Month of Lunches by Don Jones and Jeffery Hicks. I have followed both authors in the past and was certain this would be a good title to start with—it was. In fact, being familiar with these two authors was why I was able to recommend this title, even long before I read the book myself. A bit backwards perhaps, but undeniable true. It’s a wonderfully, comprehensive guide to getting started with Windows PowerShell. As stated by Bennett Scharf, on the back cover, this in fact will be an extremely useful reference. With a generous and complete index, I will be able to easily pull up the concepts I read about in this book, whenever necessary.

Besides being able to say I’ve read the title (and with a good conscience, get it signed by the authors at the PowerShell Summit North America 2015 in April), I wanted to make sure that my method of learning PowerShell was in fact complete. I’ve learned PowerShell by reading help files, blogs, and articles posted to Twitter, as well as, trying things in the shell (this is key), and helping people on PowerShell forums. Even so, I wanted to be sure I hadn’t missed some of the fundamentals. I know many of the ‘hows,’ but was worried I may have missed a ‘why’ along the way. Like, why does it (PowerShell) do it this way?

My first, favorite part was the discussion on pipeline parameter binding. Parts of that topic never just came to me, and it is a concept that requires a complete understanding. The fantastic explanations in the book (chapter 9) have helped ensure I won’t have any questions about this concept again. After all my non-book learning, I never once read anywhere that you can only have one ByValue per cmdlet, even though it makes perfect sense as to why.

The second part that I greatly appreciated was the Regex (Regular Expressions) review. For whatever reason, I have the hardest time cementing these in my mind, and often find myself in need of a quick review. Knowing this book will spend all, or most, of it’s life after this weekend sitting with me at the office, will allow me to get a quick refresh when that’s required. It can be a scary concept for many, and this book laid it out in a quick and calm approach. I wish I read this the first time I was introduced to Regex. No kidding, but I folded down the top corner of this page—something I just don’t do to my books.

In the end, I will continue to recommend this book to people starting out with PowerShell. It explains PowerShell from the start, up to your first parameterized script. I had already purchased the toolmaking followup, Learn PowerShell Toolmaking in a Month of Lunches, even before I started this one, and plan to start reading it tomorrow. I left my copy in my office and so, sadly, I couldn’t start sooner.

What PowerShell Modules does the RSAT Provide (on Windows 8.1)

I built a new Windows 8.1 machine recently, and as we all know, doing that requires new software installations. In my case, that included the RSAT. The RSAT, or Remote Server Administration Tools, allow IT admins the ability to remotely manage features on Windows Server operating systems from a client operating system—here’s a link to the RSAT for Windows 8.1.

Prior to running the RSAT installer, I wanted to collect the currently available Windows PowerShell modules that I already had on my computer. This would allow me to know exactly what modules were added after the RSAT installer finished. The command below collects all the modules, returns only their name, and then drops that into a file on my computer. We’ll use the file in a moment, as part of a comparison.

PS C:\> Get-Module -ListAvailable | Select-Object -ExpandProperty Name | Out-File C:\Users\tommymaynard\Pre-RSAT-PowerShell-Modules.txt

I verified the file contained the module names by using the Get-Content cmdlet. In addition, I ran a slightly modified command to get the item count in the file—both can be seen below. The output created by the first command has been truncated to save space.

PS C:\> Get-Content C:\Users\tommymaynard\Pre-RSAT-PowerShell-Modules.txt
AppBackgroundTask
AppLocker
Appx
AssignedAccess
...
PS C:\> (Get-Content C:\Users\tommymaynard\Pre-RSAT-PowerShell-Modules.txt).Count
57

Once this file was in placed, and I was satisfied that it contained what I wanted, I went ahead with the RSAT install. Upon completion, I reran the Get-Module command above, after modifying the name of the file it would create (pre vs. post). I then read in the contents of the new file (which has been truncated again), and checked the number of modules listed in the file. There were now 75 modules where there had only been 57 before the RSAT install.

PS C:\> Get-Module -ListAvailable | Select-Object -ExpandProperty Name | Out-File C:\Users\tommymaynard\Post-RSAT-PowerShell-Modules.txt
PS C:\> Get-Content C:\Users\tommymaynard\Post-RSAT-PowerShell-Modules.txt
ActiveDirectory
AppBackgroundTask
AppLocker
Appx
...
PS C:\> (Get-Content C:\Users\tommymaynard\Post-RSAT-PowerShell-Modules.txt).Count
75

Knowing that there’s 18 new modules is helpful, but which ones were added? The Compare-Object cmdlet can tell us. As seen below, we have the cmdlet read in the lines of each file and then determine which ones aren’t included in both files.

PS C:\> Compare-Object -ReferenceObject (Get-Content C:\Users\tommymaynard\Desktop\Pre-RSAT-PowerShell-Modules.txt) -DifferenceObject (Get-Content C:\Users\tommymaynard\Desktop\Post-RSAT-PowerShell-Modules.txt)

InputObject                                                 SideIndicator
-----------                                                 -------------
ActiveDirectory                                             =>
BestPractices                                               =>
ClusterAwareUpdating                                        =>
DFSN                                                        =>
DFSR                                                        =>
DhcpServer                                                  =>
DnsServer                                                   =>
FailoverClusters                                            =>
GroupPolicy                                                 =>
IpamServer                                                  =>
IscsiTarget                                                 =>
NetworkLoadBalancingClusters                                =>
NFS                                                         =>
RemoteAccess                                                =>
RemoteDesktop                                               =>
ServerManager                                               =>
ServerManagerTasks                                          =>
UpdateServices                                              =>

As we can see above, our Compare-Object results indicate that the difference object (the post file, or the file on the right) has new modules—now we know which ones were added since installing the RSAT.

Using the Range Operator for Calculating Total Push-Ups

In December of 2014, I decided that my life in 2015 was in need of some push-ups. Instead of just starting with 10 a day, or some other arbitrary number, I thought I would do as many push-ups a day as it was the day in the year. This meant that on day one (January 1, 2015), I would do one push-up and on day two, I would do two push-ups, and so on. Today is the 20th day of the new year, and so I’ll have to do 20 tonight. I wanted to know how many push-ups I will have done by January 31st. Being the Windows PowerShell hobbyist that I am, I enlisted PowerShell to do my calculations for me.

I started with a variable, $EndDay, and the range operator (..). The combination of the two provides me an integer array of the days in January, such as 1..$EndDay (or, 1..31). Using this, I can calculate how many total push-ups I will have done by the end of the day on January 31st. The example below sets up the integer array, as well as the ForEach-Object loop where we’ll do our calculations. Note: I’m using the ForEach-Object alias, foreach.

$EndDay = 31
1..$EndDay | foreach {

}

The first thing we do, below, is include a second variable, $PushUps, that will collect the total number of push-ups for the month. We’ll use the += assignment operator. This operator takes whatever is already in $PushUps, and adds to it. If the current value stored in $PushUps was 1, and we used the += assignment operator like so, $PushUps += 2, then the value in $PushUps would be 3 (1 + 2 is equal to 3). If we used the standard assignment operator (=), then $PushUps would be 2, as 1 would be overwritten.

On the next line, below, we write some information on the screen. We write the current day: that’s the current number from the integer array represented by $_ (as of PowerShell 3.0, $_ can be represented as $PSItem). Then we write out the total number of push-ups completed by that day: $PushUps.

$EndDay = 31
1..$EndDay | foreach {
    $PushUps += $_
    Write-Output -Verbose "Day: $_ / PushUp Total: $PushUps"
}

I noticed that when I reran the code in the ISE, that the value of $PushUps was incorrect on the second run. This is because the variable already exists, and by the end of the first run already contains 496—the number of push-ups I’ll have done by the end of January! Therefore, I added an If statement that removed the $PushUps variable when $_ was equal to $EndDay. This happens on the final run through the foreach.

$EndDay = 31
1..$EndDay| foreach {
    $PushUps += $_
    Write-Output -Verbose "Day: $_ / PushUp Total: $PushUps"
    If ($_ -eq $EndDay) {
        Remove-Variable PushUps
    }
}

If you change the value for $EndDay to 365, you’ll be able to determine that after December 31st (if I can somehow keep this up) I will have done 66,299 total push-ups for the year. It’s hard to imagine that I could do 365 push-ups at once, but then again, it’s hard to imagine I’ll get though the rest of the month. Here’s an image that shows the the full results when we run the function above.

Using the Range Operator for Push-Up Calculations

Thanks for reading, and wish me good luck—I’m going to need it.

Write-Host, Does it Have a Place?

I’ve been working on an advanced function that I can’t wait to share (and no, it’s not the one in this post). I really think it’s something that the Windows PowerShell community has been missing. Okay fine, maybe it’s just something I’ve been missing.

I noticed in development that my Write-Output messages to the user were crossing the pipeline, when the custom object (created by the function), was passed to Select-Object (in certain manners). I have a function below that does the same thing as the one in development.

Here’s how this thing works: The function requires the user to provide the value Write-Output (or, wo), or Write-Host (or, wh) for the -Option parameter. This will determine how the message to the user is written inside the Begin block. The only other thing that’s happening in this function, is that a custom object is being created in the Process block, based on some properties of Win32_BIOS.

Function Test-TMWriteOutputVsHost {
    [CmdletBinding()]
    Param (
    [Parameter(Mandatory=$true,
        HelpMessage="Enter Write-Output or Write-Host")]
    [ValidateSet('Write-Output','wo','Write-Host','wh')]
    [string]$Option
    )

    Begin {
        If ($Option -eq 'Write-Output' -or $Option -eq 'wo') {
            Write-Output 'Inside the Begin block (using Write-Output).'

        } ElseIf ($Option -eq 'Write-Host' -or $Option -eq 'wh') {
            Write-Host 'Inside the Begin block (using Write-Host).'
        }
    } # End Begin

    Process {
        $CollectionVariable = Get-WmiObject -Class Win32_BIOS
        $Object = @()
        $Object += [pscustomobject]@{
            Manufacturer = $CollectionVariable.Manufacturer;
            Name = $CollectionVariable.Name;
            Version = $CollectionVariable.Version
        }
    } # End Process

    End {
        Write-Output $Object
    }
    # End, End
} # End Function

As we can see below, everything works great with both Write-Output or Write-Host, when we don’t pipe the function to the Select-Object cmdlet.

PS C:\> Test-TMWriteOutputVsHost -Option Write-Output
Inside the Begin block (using Write-Output).

Manufacturer                            Name                                    Version
------------                            ----                                    -------
Dell Inc.                               BIOS Date: 08/27/13 11:12:44 Ver: A1... DELL   - 1072009

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host
Inside the Begin block (using Write-Host).

Manufacturer                            Name                                    Version
------------                            ----                                    -------
Dell Inc.                               BIOS Date: 08/27/13 11:12:44 Ver: A1... DELL   - 1072009

Now, let’s pipe our object to some variations of the Select-Object cmdlet and watch some things blow up (when using Write-Output).

PS C:\> # Use the horizontal scrollbar to see the results...
PS C:\> Test-TMWriteOutputVsHost -Option Write-Output | select *

                                                                                                                 Length
                                                                                                                 ------
                                                                                                                     44

PS C:\> Test-TMWriteOutputVsHost -Option Write-Output | select Name,Ver*

Name                                                        Ver*
----                                                        ----

BIOS Date: 08/27/13 11:12:44 Ver: A13.00

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host | select *
Inside the Begin block (using Write-Host).

Manufacturer                            Name                                    Version
------------                            ----                                    -------
Dell Inc.                               BIOS Date: 08/27/13 11:12:44 Ver: A1... DELL   - 1072009

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host | select Name,Ver*
Inside the Begin block (using Write-Host).

Name                                                        Version
----                                                        -------
BIOS Date: 08/27/13 11:12:44 Ver: A13.00                    DELL   - 1072009

The problem with Write-Output, is that the object it’s producing is crossing our pipeline and causing unpredictable behavior—something we can’t include in a function we want to distribute. Here’s the proof: When we pipe our function to Get-Member, we reveal the objects that show up on the other side, and we don’t want the string object coming over with us.

PS C:\> Test-TMWriteOutputVsHost -Option Write-Output | Get-Member | Select-Object TypeName -Unique

TypeName
--------
System.String
System.Management.Automation.PSCustomObject

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host | Get-Member | Select-Object TypeName -Unique
Inside the Begin block (using Write-Host).

TypeName
--------
System.Management.Automation.PSCustomObject

I don’t profess to know it all, so if there’s a way to get around this using Write-Output, then I’d love to hear about it. While I haven’t tried it, I suspect I may be able to create an embedded function—that might be the trick I need. Perhaps I’ll play with that option at another time. Thanks for reading!

Oh, and before someone mentions it, I explained how I feel about Write-Verbose in the comments on a post by Adam Bertram: http://www.adamtheautomator.com/use-write-host-lot.

Out-GridView in a PSRemoting Session

Twitter Reply to:

We can’t use the Out-GridView cmdlet in a remote session. How do we know this? Well for one, the documentation says so (search for ‘You cannot use a remote command’ on that webpage), and two, because if you try, you’ll get a straight forward error message on the topic: “Out-GridView does not work in a remote session.” Okay, but why?

Out-GridView produces a Graphic User Interface (GUI) — something we don’t use in PSRemoting sessions. In fact, everything about a PSRemoting session is text only. This isn’t just about Out-GridView, though. Other graphical elements in Windows PowerShell aren’t going to work either. This includes Get-Help’s -ShowWindow parameter, and the Show-Command cmdlet. It just wasn’t designed to work this way.

Other GUI elements don’t work either. While you won’t get a helpful message, like you do with the PowerShell cmdlets and parameters, launching notepad.exe and calc.exe isn’t going to work like it does on a local computer. Those programs work a little differently though, as they will actually launch on the remote computer. It just won’t be in any useable fashion from your remote session. Bonus: They may lock up your remote session until they are closed.

Note: If you were to try this, one way to rectify the situation would be to open a second PowerShell console and run the following, assuming you launched calc.exe: Invoke-Command -ComputerName computername -ScriptBlock {Get-Process -Name calc.exe | Stop-Process}. This would end the process on the remote computer, and give you back your prompt on the console where you were running your PSRemoting session.

Everything until now, assumed we were talking about an interactive PSRemoting session (using the Enter-PSSession cmdlet). Well, what about Invoke-Command? Invoke-Command is used to run commands on remote systems and return the results to the local computer. As you can see in the examples below, we can run a command on a remote computer and then display the results on our local computer, inside Out-GridView.

Note: Although I didn’t have any problems with the small handful of cmdlets I tried, the same webpage linked above indicates that data returned from a remote computer may not be formatted correctly for use with Out-GridView.

PS C:\> $Services = Invoke-Command -ComputerName dc01 -ScriptBlock {Get-Service}
PS C:\> $Services | Out-GridView
PS C:\> $PSWARules = Invoke-Command -ComputerName PSWAServer01 -ScriptBlock {Get-PSWAAuthorizationRule}
PS C:\> $PSWARules | Out-GridView

Thanks for the inspiration to write a little about this topic, Tim.