Counting Retrieved Data

Why Can't I Get a Count of My Dial Plans?

Not too long ago we got an email from someone who had encountered a strange problem when working with Lync Server PowerShell:

"I was just trying to get a count of things," he wrote. "Sometimes I could do that and sometimes I couldn't. For example, this command returns all my conferencing policies:

(Get-CsConferencingPolicy).Count

"No problem there. But then I tried doing the same thing for dial plans, using this command:

(Get-CsDialPlan).Count

"When I ran that command, I got nothing. I could count some things, like users, but I couldn't count other things, like Address Book settings. Have I discovered a bug in Lync Server?"

As it turns out, the answer is no: this isn't a bug. (As if there could be any bugs in Lync Server!) We have to admit that it took us a little while to figure out what the problem was, but once we did we thought it might be worth passing this information along to all of you. After all, you never know when you might run into the same problem.

Besides, we promised we'd write something for today, and we hadn't actually gotten around to doing that yet. Mahjong doesn't play itself, you know?

To explain the problem, and offer up a solution, let's start out by running the command that actually worked:

(Get-CsConferencingPolicy).Count

If you're new to Windows PowerShell, we need to explain that what we're doing here is first running the Get-CsConferencingPolicy cmdlet in order to return a collection consisting of all the conferencing policies available in our organization. That command is enclosed in parentheses in order to make sure PowerShell completes the command – that is, returns all the conferencing policies – before doing anything else. After all the policies have been returned, we then use the Count property to figure out how many policies are in the collection. Onscreen, the whole thing looks something like this:

PS C:\> (Get-CsConferencingPolicy).Count

13

PS: C:\>

We got back 13 because we have 13 conferencing policies in our test domain. Makes sense, right?

Now let's try a command that didn't work:

(Get-CsDialPlan).Count

When we tried returning a count of all the dial plans, we got back, well, nothing:

PS C:\> (Get-CsDialPlan).Count

PS C:\>

That would be fine, except for one thing: we know for sure that we have at least one dial plan; you always have at least one dial plan. (You can't delete the global dial plan.) Hmmm, maybe we have discovered a bug after all ….

No, wait, scratch that: it turns out that it’s not a bug. Here's the problem: the problem is that we have only one dial plan. (Why is that a problem? We'll get to that in a second.) To verify our suspicions, we removed all our conferencing policies except for the global policy; that left us with just one conferencing policy. When we tried to get a count of our conferencing policies, we – well, see for yourself what happened:

PS C:\> (Get-CsConferencingPolicy).Count

PS C:\>

Good heavens! Did we break something, or does PowerShell just have problems counting to 1?

As it turns out, the answer is: neither. (If you got that wrong, don't feel bad; we would have bet on the authors of this article having broken something, too.) No, the problem is this: the Count property is a property that is valid on collections and arrays. If you have a collection (or an array) then the Count property tells you the number of items in that collection. It doesn't matter whether we have a collection of conferencing policies or dial plans or whatever; the Count property is there to tell us how many items are in the collection.

So why is that a problem? Be patient; we're getting to that. As you may or may not know, Windows PowerShell objects typically have a GetType() parameter that provides information about the datatype of the object you're working with. For example, back when we had 13 conferencing policies we ran this command:

(Get-CsConferencingPolicy).GetType()

In turn, PowerShell reported back the following:

IsPublic IsSerial Name BaseType

-------- -------- ---- --------

True True Object[] System.Array

As you can see we're dealing with an array of objects (System.Array). And, as we've already determined, arrays support the Count property.

Trivia Note. Arrays also support the Length property, which returns the exact same information as the Count property. Arrays also support the LongLength property, which returns the exact same information as the Length and Count properties. And – well, I guess we decided three properties that all return the exact same information was enough.

Actually, that's true only up to a point: although Length and Count are identical the LongLength property, as the name implies, supports really big arrays, arrays with more than 2 billion items. We're going to assume that won't be a problem for most of you. A quick, off-the-top-our-heads calculation suggests that, if you created a new dial plan every minute, it would take around 4,000 years to create 2 billion dial plans. But hey, if anyone actually does create 2 billion dial plans let us know and we'll talk more about the LongLength property.

Now, where were we? Ah, yes: if we have multiple conferencing policies and we run Get-CsConferencingPolicy we get back an array of conferencing policies.

Note. Wow: that's the loudest "Well, duh" we've ever heard.

So what happens after we delete 12 of our conferencing policies, leaving us with just one conferencing policy? Let's try the same command and see for ourselves:

(Get-CsConferencingPolicy).GetType()

Well, whatta you know: look what we got back when we ran that command:

IsPublic IsSerial Name BaseType

-------- -------- ---- --------

True False MeetingPolicy Microsoft.Rtc.Management.Writab…

This time we didn't back an array; instead, we got back a singleton, a single instance of the Microsoft.Rtc.Management.WritableConfig.Policy.Meeting.MeetingPolicy object. That's why the Count property doesn't do us any good: the MeetingPolicy object doesn't actually have a Count property. By contrast, if we have more than one object we get back an array (an array of MeetingPolicy objects), and then we can use the Count property. If we only have one object we get back a singleton, and we can't use the Count property. Case closed!

Oh, wait: case not closed, not yet anyway. We now understand why we're getting unexpected results when we use the Count property: that's because we're getting back different objects that support different property sets. We still have a problem, though. Remember this command:

PS C:\> (Get-CsDialPlan).Count

PS C:\>

That leaves one question still open: do we have 1 dial plan or do we have 0 dial plans? There's really no way of knowing, at least not by using the Count property.

So how can we tell, for sure, how many somethings we have? The best way is to use the Measure-Object cmdlet. For example:

Get-CsDialPlan | Measure-Object

That's going to return data that looks like this:

Count : 1

Average :

Sum :

Maximum :

Minimum :

Property :

Or, if you don’t want to get back all those blank values (for things like Average, Maximum, and Minimum, which don't make any sense in this case anyway), use this command:

(Get-CsDialPlan | Measure-Object).Count

And what if you have a deep, personal attachment to the Count property? Well, to be perfectly honest, in that case you would seem to have problems that we can't really help you with. But if you're dead set on using Count instead of Measure-Object, well, you can always do this:

[array] $x = Get-CsDialPlan

$x.Count

If you retrieve an object (like a dial plan) and store it in a variable that has been cast as an array (in other words, we're forcing the variable $x to be an array), that variable will, well, always be configured as an array, even if that array contains just 1 item. That means that, in this example, $x is an array that contains a single object. And, because it is an array, that means $x supports the Count property.

But wait: there's a catch here, a catch that crops up if you have zero instances of something. For example, suppose you don't have any dial-in conferencing access numbers. Onscreen you're going to see something like this:

PS C:\> [array] $x = Get-CsDialInConferencingAccessNumber

PS C:\> $x.Count

PS C:\>

By contrast, using Measure-Object returns an actual value of 0:

PS C:\> Get- CsDialInConferencingAccessNumber | Measure-Object | Select-Object Count | Format-List

Count : 0

PS C:\>

With that in mind, we recommend that you use Measure-Object any time you need to get a count of something. But hey, to each his own, right?