Combine Output With Add-Member


Sometime in the year 1793 (the exact date is unknown), the Italian artist Leonardo Da Vinci was strolling through the newly-opened Musée de Louvre when he happened upon his famous portrait of the Mona Lisa. "Mamma mia!" he exclaimed upon looking at the painting. "I forgot to paint the eyebrows!"

 

There's a good lesson behind that story, although we should mention the fact that not everyone accepts it as a true story; for example, many scholars have claimed that, seeing as how he died in the year 1519, it's very unlikely Leonardo Da Vinci would have visited the Louvre in 1793. And, of course, there's also the inconvenient fact that the Mona Lisa actually did have eyebrows; it’s just that those eyebrows are no longer visible, either because they've simply faded over time or because they were erased by someone attempting to clean the painting.

 

Minor quibbles like those aside, however, the lesson of the story remains as valuable today as it was in 1793: everyone, even a genius like Leonardo Da Vinci, sometimes looks back on his creation and thinks, "You know, given the chance, I would have done things a little differently." Like they say, hindsight is foresight.

 

Note. To the best of our knowledge, Leonardo Da Vinci was not the one who said, "Hindsight is foresight." However, he did say "Although nature commences with reason and ends in experience it is necessary for us to do the opposite, that is to commence with experience and from this to proceed to investigate the reason."

 

And no, we're not totally sure what he meant by that, either. But, then again, we don't speak Italian.

 

So what does any of that have to do with Microsoft Lync Server 2010? Well, for one thing, we like to consider Lync Server the Mona Lisa of the unified communications world. For another, and to be perfectly honest, after we released Lync Server to manufacturing we took a closer look at it and exclaimed, "Mamma mia! We forgot to paint the eyebrows!"

 

Of course, after we calmed down we realized that we were never supposed to paint eyebrows on Lync Server 2010 in the first place. But then we realized yet another thing. As you probably know, in Lync Server's implementation of Windows PowerShell there are two different cmdlets that return user information: Get-CsUser and Get-CsAdUser. We won't go into much detail about the differences between these two cmdlets; for that, see the article Get-CsUser: The Real Story. What we will do, however, is note that, although there is some overlap between the two cmdlets, Get-CsAdUser primarily brings back information for generic Active Directory attributes such as department, job title, and home phone number; in other words, attributes shared by all Active Directory user accounts regardless of whether or not those accounts have been enabled for Lync Server. In comparison, Get-CsUser returns information about Lync Server-specific attributes, like the dial plan or the external access policy that has been assigned to the user. These are attributes which are only part of user accounts that have been enabled for Lync Server. East is east, west is west, and ne'er the twain meet.

 

Note. Yes, it would be useful to be able to see a table that compares the attribute values returned by the two cmdlets, wouldn't it? Wonder where we could find one of those ….

 

If you've ever seen the Mona Lisa in person (and you should: it's actually pretty cool) then you know that the fact that she doesn't have eyebrows is no big deal; to be honest, you don't even notice that she doesn't have any eyebrows. And, in general, the same thing is true of Get-CsAdUser and Get-CsUser: most of the time you won't even notice that you have to use two different cmdlets to return user information. Two cmdlets? Big deal.

 

Like we said, most of the time you won't even notice that you have to use two different cmdlets to return user information. However, there are exceptions to this general rule. For example, suppose you'd like to return the following information for each user who's been enabled for Lync Server:

 

·         Display name

·         Department

·         Job title

·         Registrar pool

·         Dial plan

 

So how are we going to do that? Well, the display name is easy: after all, both Get-CsUser and Get-CsAdUser can return the display name. The other four attributes pose a problem, however, and a big problem at that. Get-CsAdUser can return the department and job title, but not the Registrar pool or dial plan. Meanwhile, Get-CsUser can return the Registrar pool and dial plan, but not the department or job title. Uh-oh ….

 

So do we have a problem here? Sure looks that way, doesn't it? But, as Leonardo Da Vinci once said, "Marriage is like putting your hand into a bag of snakes in the hope of pulling out an eel." With that in mind, let's reach into our bag of Windows PowerShell tricks and see if we can pull out an eel.

 

Before we go searching for that eel, however, let's stop to clarify the problem we're having here, just to make sure we're all on the same page. (You should be on one of the Lync Server PowerShell blog pages right now, the one that keeps going on and on about Leonardo Da Vinci.) Suppose we had a hypothetical cmdlet named Get-FavoriteColor and suppose that cmdlet returned information like this:

 

Identity      : Ken Myer

FavoriteColor : Orange

 

Suppose we also have a second hypothetical cmdlet – Get-FavoriteNumber – that returns information like this:

 

Identity       : Ken Myer

FavoriteNumber : 13

 

What we'd like to have is way to combine the output of these two cmdlets, like so:

 

Identity       : Ken Myer

FavoriteColor  : Orange

FavoriteNumber : 13

 

Can we do that? You bet we can. We'll have to write a little script to do it, but it will definitely be a little script, and it won't be hard to do at all. So does that mean we can also find a way to combine the output of Get-CsUser and Get-CsAdUser? See for yourself:

 

$userIDs = Get-CsUser | Select-Object Identity, RegistrarPool, DialPlan

 

foreach ($user in $userIDs)

    {

        $x = Get-CsAdUser -Identity $user.Identity

        $x | Add-Member -MemberType NoteProperty -Name RegistrarPool -Value $user.RegistrarPool

        $x | Add-Member -MemberType NoteProperty -Name DialPlan -Value $user.DialPlan

        $x | Select-Object DisplayName, Department, Title, RegistrarPool, DialPlan

    }

 

Let's take a look at this script line-by-line and see if we can explain how it works. As you no doubt recall, we want to run a command that returns the following data for each user who's been enabled for Lync Server:

 

·         Display name

·         Department

·         Job title

·         Registrar pool

·         Dial plan

 

With that in mind, our first line of code uses the Get-CsUser cmdlet to return the Identity, RegistrarPool, and DialPlan attributes for each Lync Server-enabled user, then stores all that information in a variable named $userIDs:

 

$userIDs = Get-CsUser | Select-Object Identity, RegistrarPool, DialPlan

 

Why did we limit the returned data to the attributes Identity, RegistrarPool, and DialPlan? Well, to begin with, we need the Identity to help us differentiate one user from another. Second, we need the RegistrarPool and DialPlan attributes because only Get-CsUser returns that information for us. The other attributes we're interested in – DisplayName, Department, and Title – can all be retrieved using Get-CsAdUser.

 

Let's pretend that we have only two users who have been enabled for Lync Server. That means that $userIDs is going to contain information similar to this:

 

Identity

RegistrarPool

DialPlan

CN=Ken Myer,OU=Finance,DC=litwareinc,DC=com

atl-cs-001.litwareinc.com

Redmond

CN=Pilar Ackerman,OU=Finance,DC=litwareinc,DC=com

atl-cs-001.litwareinc.com

Paris

 

Got that? Good. In that case, our next step is to set up a foreach loop that loops through each of the user accounts stored in $userIDs. That's what this line of code is for:

 

foreach ($user in $userIDs)

 

Now it's time to roll up our sleeves and get to work. Inside our foreach loop, the first thing we do is execute this line of code:

 

$x = Get-CsAdUser -Identity $user.Identity

 

What are we doing here? Well, what we're doing here is calling the Get-CsAdUser cmdlet in order to return information for a user. Which user? Well, because this is our first time through the loop, we're retrieving information for the first user (Ken Myer) whose account information is stored in $userIDs; that's why we use $user.Identity as the parameter value for the Identity parameter. When this command finishes running, we'll have two sets of information for Ken Myer: we'll have his Identity, RegistrarPool, and DialPlan tucked away in the variable $user, and we'll have all his "generic" Active Directory data tucked away in the variable $x.

 

Now that's pretty impressive, except for one thing: that's not the least bit impressive at all. Why not? Well, we currently have Ken Myer's data in two different places. But whoop-dee-doo: we've always had Ken Myer's data in two different places. What we really want to do is combine all that data into a single data source. And guess what? That's what these two lines of code are for:

 

$x | Add-Member -MemberType NoteProperty -Name RegistrarPool -Value $user.RegistrarPool

$x | Add-Member -MemberType NoteProperty -Name DialPlan -Value $user.DialPlan

 

Let's take a closer look at what these two lines of code do. In the first line, we're taking the variable $x (which contains Ken Myer's generic Active Directory information) and piping that object to the Add-Member cmdlet. If you aren't familiar with the Add-Member cmdlet, well, you should be: it's pretty dang cool. Among other things, Add-Member enables you to add a property of your own (any property you want) to an object. As we've already determined (even if we haven't spelled it out quite this technically), the Microsoft.Rtc.Management.ADConnect.Schema.ADUser object returned by Get-CsAdUser does not include the attributes RegistrarPool or DialPlan. We can't return the Registrar pool or dial plan for a user by using Get-CsAdUser; that's because Get-CsAdUser doesn't support either of those attributes.

 

Note. In case you're wondering, Get-CsUser returns instances of the Microsoft.Rtc.Management.ADConnect.Schema.OCSADUser object.

 

In case y

Skip to main content