Retrieving Active Directory and Microsoft Lync Server 2010 User Accounts
After you get Microsoft Communications Server up and running, you probably won’t find yourself having to manage several hundred sets of Address Book server configurations or thousands of conferencing policies. Consequently, the fact that the cmdlets used to manage these objects don’t offer a lot of ways to filter the returned information (beyond simple wildcards) probably isn’t that big of a deal.
Note. And if it is a problem you can always pipe the returned data to Where-Object and thus apply some very complex and sophisticated filters to the information.
This is definitely not the case when it comes to users, however. Depending on the size of your organization you might have hundreds of users enabled for Microsoft Communications Server; heck, you might have tens of thousands of users enabled for Microsoft Communications Server. Furthermore, you will undoubtedly want to “slice and dice” these user accounts in a myriad of ways: you might want to retrieve a collection of users who have offices in a certain location, users who work for a specified department, users who have a designated job title, etc. Needless to say, simple wildcards won’t do the trick here.
But that’s OK. As it turns out, the two cmdlets used to return user account information – Get-CsAdUser and Get-CsUser –include several ways to slice and dice user information. For example, the two cmdlets each have a parameter ( -OU) that limits retrieved data to user accounts found in a specific Active Directory organizational unit, including any child OUs in that organizational unit. These two cmdlets also feature a pair of filter parameters:
• -Filter, which enables you to filter user accounts based on Microsoft Communications Server-specific attributes, such as voice policy or dial plan.
• -LdapFilter, which enables you to filter user accounts based on “generic” Active Directory attributes, attributes such as department and job title.
In this article we’ll explain these parameters. We’ll also take a moment to talk about the –Identity parameter for user account cmdlets.
Incidentally, why two cmdlets for retrieving user account information? Well, Get-CsUser returns information only for users who have been enabled for Microsoft Communications Server; if you don’t have any users who have been enabled for Microsoft Communications Server then Get-CsUser won’t return any data whatsoever. By contrast, Get-CsAdUser returns account information for all your users, whether they have been enabled for Microsoft Communications Server or not.
User Identities
To begin with, you don’t have to retrieve user accounts in bulk; instead, you can always use the –Identity parameter to zero in on a specific user. As you might expect, the Identity for a user differs from the Identity used for a policy or configuration. As such, you can retrieve a specific user account by using any of the following:
• The user’s Active Directory display name (for example, Pilar Ackerman).
• The user’s Universal Principal Name (UPN). The UPN will look something like this: pilar@fabrikam.com.
• The user’s SIP address; for example, sip:pilar@fabrikam.com.
• The user’s domain name and logon name. For example: fabrikam\pilar.
Or, to put it into PowerShell terms, any of these commands will return Pilar Ackerman’s user account:
Get-CsUser –Identity "Pilar Ackerman"
Get-CsUser –Identity "pilar@fabrikam.com"
Get-CsUser –Identity "sip:pilar@fabrikam.com"
Get-CsUser –Identity "fabrikam\pilar"
Unlike other cmdlets, Get-CsUser and Get-CsAdUser let you use wildcards when specifying the Identity. This command returns Pilar Ackerman’s account, as well as the account for any other user named Pilar:
Get-CsUser –Identity "Pilar *"
Note. We know what you’re thinking: why can’t you just use the person’s SamAccountName (logon name) as an Identity? There’s actually a good reason for that: SamAccountNames do not have to be unique in a forest. That means you could have more than one user with the same SamAccountName. And that makes the SamAccountName unsuitable for use as an Identity.
So then why can you use a user’s display name? Display names don’t have to be unique. Well, … just because.
OU Parameter
Let’s turn our attention to the –OU parameter. If you use the –OU parameter along with the Get-CsAdUser cmdlet, Get-CsAdUser will return a collection of all the user accounts found in the specified Active Directory OU. For example, this command returns all the user accounts found in the North America OU:
Get-CsAdUser -OU "ou=North America,dc=fabrikam,dc=com"
The –OU parameter works pretty much the same way when used with Get-CsUser; the only difference, of course, is that Get-CsUser returns only the users in the North America OU who have been enabled for Microsoft Communications Server:
Get-CsUser -OU "ou=North America,dc=fabrikam,dc=com"
Wow: that did seem easy, didn’t it?
And it was, as long as you remember to set the value of the –OU parameter to the distinguished name of the OU. For example, this is how we get to the Redmond OU, a child OU of North America:
Get-CsAdUser -OU "ou=Redmond,ou=North America,dc=fabrikam,dc=com"
That’s how you do it.
Before you ask, yes, you can retrieve a collection of users in an OU and all its child OUs; in fact, that’s exactly how the -OU parameter works. However, you can’t supply multiple OU names to the –OU parameter. If you try that, your command will fail. Period.
But don’t despair: you can always write a custom script to do this for you. You know, a custom script that returns all your users, grouped by the OU where their user account resides.
Like, you know, this somewhat-rudimentary custom script:
$strFilter = "(objectCategory=organizationalUnit)"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "Subtree"
$colPropList = "DisplayName"
foreach($i in $colPropList)
{[void] $objSearcher.PropertiesToLoad.Add($i)}
$colResults = $objSearcher.FindAll()
foreach($objResult in $colResults)
{$x = $objResult.Properties.distinguishedName
$objResult.Path
Get-CsAdUser -OU "$x" | Select-Object DisplayName, CsEnabled}
And no, we won’t bother explaining how this works, at least not today. We’ll save that explanation for a different article .
The –Filter Parameter
The –Filter parameter enables you to “filter” the user accounts returned by Get-CsAdUser or Get-CsUser; that means that you can specify criteria of some kind and only the user accounts that meet that criteria will be returned. And what kind of criteria can you specify? In the case of –Filter, you can create filters based on the Active Directory user account attributes that are directly related to Microsoft Communications Server.
Before we go much further we should note that, at the present time, there are a few Microsoft Communications Server attributes that you can’t filter on; if you put those attributes in a filter all you’ll get back is an error message. However, here are some of the attributes that we were able to successfully use as filters:
• Enabled
• LineServerUri
• LineUri
• SipAddress
• PrivateLine
We were also able to use commands like the following to determine which users had been assigned a per-user policy, using any of the attributes that include Policy in the attribute name. For example, here we’ve used the VoicePolicy attribute:
Get-CsUser -Filter {VoicePolicy -eq "*"}
Even better is the fact that we can retrieve the list of users who had been assigned a particular per-user policy by using a command similar to this one, which returns all the users who have been assigned the voice policy RedmondPolicy:
Get-CsUser -Filter {VoicePolicy -eq "RedmondPolicy"}
Makes sense, right? Let’s run some more commands and explain how they work. Suppose you’d like a list of all the Active Directory users who have not yet been enabled for Microsoft Communications Server. How are you going to do that? Here’s one way to do that:
Get-CsAdUser -Filter {Enabled -eq $False}
As you can see, all we’re doing here is calling Get-CsAdUser along with the –Filter parameter. And what exactly is our filter? This is our filter:
{Enabled -eq $False}
Doesn’t make much sense? Gee; it made sense to us. OK, maybe we should take a moment to walk you through the different parts of the filter.
To begin with, filters must be enclosed in curly braces. (In PowerShell terminology, that makes the filter a “script block.”) Inside the curly braces the filter must adhere to the following format:
Attribute |
Operator |
Filter Value |
Enabled |
-eq |
$False |
So why does PowerShell uses –eq rather than the equals sign? That was a decision made by the PowerShell team many years ago, and, to their credit, they’ve stuck to their guns even though this probably wasn’t the most popular decision they ever made. Here are some of the more commonly-used comparison operators found in Windows PowerShell:
Operator |
Definition |
-lt |
Less than. |
-le |
Less than or equal to. |
-gt |
Greater than. |
-ge |
Greater than or equal to. |
-eq |
Equal to. |
-ne |
Not equal to. |
-contains |
Determines whether an item can be found in a group. This always returns Boolean $True or $False. |
-notcontains |
Determines whether an item cannot be found in a group. This always returns Boolean $True or $False. |
-like |
Like - uses wildcards for pattern matching. |
-notlike |
Not Like - uses wildcards for pattern matching. |
-match |
Match - uses regular expressions for pattern matching. |
-notmatch |
Not Match - uses regular expressions for pattern matching. |
For more information, type the following command at the Windows PowerShell prompt and then press ENTER:
Get-Help about_comparison_operators | More
Last, but surely not least, comes the value for the filter. We’re interested in user accounts where the Enabled attribute is False; thus we use the Windows PowerShell constant $False. (Other Windows PowerShell constants of interest include $True for True and $Null for null values.) If we were working with a string value rather than a Boolean (true/false) value then that value would be enclosed in double quote marks. For example:
Get-CsUser -Filter {LineUri –eq "TEL:+14255551234"}
And if we were working with a numeric value then we would simply specify the number.
Now that’s pretty good but, like they say on TV, “Wait: there’s more.” With the –Filter parameter you can include multiple criteria in a single filter. For example, suppose you’d like to return all the users who have a line Uri that starts with TEL:+1425555 (area code 425, prefix 555) and have been assigned the voice policy RedmondPolicy. Here’s how you write an AND query to return that information:
Get-CsUser -Filter {(LineUri –like "TEL:+1425555*") –and (VoicePolicy –eq "RedmondPolicy")}
As you can see, this time around our filter includes two parts. (We’ve enclosed the two parts in parentheses to make them easier to pick out.) The first part uses the –like operator to determine whether or not the LineUri property starts with the string value TEL+1:425555; in other words:
(LineUri –like "TEL:+1425555*")
Following our first filter clause is the –and operator. This tells PowerShell that we want to write an AND query; with an AND query a user account must meet both criteria (it must have a LineUri starting with TEL:+1425555 and it must have been assigned the voice policy RedmondPolicy) in order to pass through the filter.
After the –and operator comes the second filter clause, this one stating that the value of the VoicePolicy property must be equal to RedmondPolicy:
(VoicePolicy –eq "RedmondPolicy")
And there you have it. We use the same approach to write an OR filter; with this type of filter a user account must meet just one of the specified criteria in order to pass through the filter. For example, the following OR clause – which, like all good OR clauses, uses the –or operator – returns user accounts that have a line Uri that starts with TEL:+1425555 or have been assigned the voice policy RedmondPolicy. Here’s the command:
Get-CsUser -Filter {(LineUri –like "TEL:+1425555*") –or (VoicePolicy –eq "RedmondPolicy")}
Child’s play, huh?
The –LdapFilter Parameter
So then what about –LdapFilter? After all, if –Filter lets us filter user accounts on Microsoft Communications Server-specific properties then what’s the point of this parameter? Well, as usual, you’re way ahead of us: -LdapFilter lets us filter on Active Directory properties that are not specific to Communications Server. For example, Department is not a Communications Server attribute; however, there’s a good chance you’d like to be able to filter on Department anyway. (For example, you might want to return a collection of all the users who work in the Finance department.) That’s where -LdapFilter comes in:
Get-CsAdUser -LdapFilter "(Department=Finance)"
Before you ask, no, we didn’t make a mistake here (hard as that might be to believe). You’re right in pointing out that we didn’t enclose our filter in curly braces, and you’re right that we used the equals sign (=) rather than the –eq operator. Nevertheless, that’s a not a mistake; that’s the way you have to do it.
At the risk of making your head spin, when you use the –Filter parameter you need to use the Windows PowerShell filter format (the same format you use with the Where-Object cmdlet). That’s why we need the curly braces and the –eq operator. When you use –LdapFilter, however, you don’t use the Windows PowerShell filter format; instead, you have to use the LDAP filter format. And that’s a very different animal.
But hey, don’t panic; we were just about to explain the LDAP filter format.
The LDAP Filter Format
To begin with, you might have noticed that we enclosed our filter (Department=Finance) inside a set of parentheses. That’s not really necessary, at least not when you’re writing a filter that has just one component. On the other hand, parentheses are very important when you write AND queries and OR queries. Because of that, we decided to go ahead and start with a set of parentheses.
Inside the parentheses we specify the attribute we want to filter on. In our example we’re filtering on the Department attribute; it should come as no surprise, then, that we put the name of that attribute in our filter:
"(Department)"
Next comes the operator. When writing an LDAP filter we can use any of the following operators:
Operator |
Description |
< |
Less than |
<= |
Less than or equal to |
= |
Equal to |
> |
Greater than |
>= |
Greater than or equal to |
Note. You know you’re right: there isn’t a “not equal to” operator (e.g., <>), is there? OK, tell you what: before we go we’ll explain how to write a “not equal to” filter.
We promise.
As we noted, we’re looking for all the user accounts where the Department is equal to Finance; that means that, after we insert the operator, out filter should look like this:
"(Department=)"
And that can mean only one thing: it’s now time to add the filter value:
"(Department=Finance)"
As you can see, there’s no need to put double quotes around the value (Finance). That’s true even if the value includes blank spaces. For example, suppose the department you’re interested in is the Human Resources department. Is that going to be a problem? Heck no:
"(Department=Human Resources)"
By the way, you can also use the asterisk as a wildcard character when specifying the value. Want to search for all the departments that have a name that starts with Finance? This filter should do the trick:
"(Department=Finance*)"
Meanwhile, this filter returns all the users who have a department of some kind:
"(Department=*)"
Granted, these LDAP filters look weird. And to be honest, they are weird. But at least they’re easy enough to write.
“Special” Filters
Before we call it a day let’s take a quick look at three special types of filters:
• AND filters
• OR filters
• NOT filters
You should already have a rough idea of what we’re talking about when we mention AND filters and OR filters. Let’s take a look at an AND filter and see if we can explain how it works:
"(&(Title=Accountant)(Department=Finance))"
What we’ve done here is create a filter that returns objects that meet two criteria: the Title must be equal to Accountant and the Department must be equal to Finance. (In other words, we want to return all the accountants who work in the Finance department.) Note the syntax used to create this filter. For starters, the entire filter is enclosed in parentheses. We then have an ampersand ( & ); in the world of LDAP filters, the ampersand indicates that we want to create an AND filter. After that we simply have the two criteria, with each item enclosed in a set of parentheses. To make this a little easier to visualize, picture the query as being written like this:
(&
(Title=Accountant)
(Department=Finance)
)
See how that works? What if we wanted returned objects to meet three criteria? That’s no problem; we just need to add the third item to the query:
(&
(l=Redmond)
(Department=Finance)
(Title=Accountant)
)
Or, for the purpose of using this with –LdapFilter (including the double quotes around the entire filter):
"(&(l=Redmond)(Title=Accountant)(Department=Finance))"
Admittedly, this a bit intimidating just to look at. But if you break the filter down part-by-part, like we did above, well, it’s not so bad.
The OR filter is similar to the AND filter; all we have to do there is substitute the pipe separator ( | ) for the ampersand:
"(|(Department=Finance)(Department=Research))"
In this case, we get back all the user accounts where the Department is equal to Finance or where the Department is equal to Research. See the difference? In an AND filter we need to meet all the criteria; in an OR filter we need to meet only one of the specified criteria.
We can even combine these filter types to create a finely-targeted filter. For example, this filter returns all the user accounts where the user is a Manager, provided that the user is a member of either the Finance or the Research department:
"(&(Title=Manager)(|(Department=Finance) (Department=Research)))”"
Or, to again put it a little more visually:
(&
(Title=Manager)
(| (Department=Finance)
(Department=Research)
)
)
As you can see, with this filter we have both an AND filter and an OR filter. The AND filter looks like this, and specifies that we must have a user who is a manager:
& (Title=Manager)()
Inside the second half of the AND filter we have the OR filter, which indicates whichdepartment the manager must be in:
| (Department=Finance)(Department=Research)
Don’t feel bad; it is a little confusing. But eventually you’ll learn to read and write these LDAP filters almost as easily as you read and write Windows PowerShell filters.
Note. Well, assuming you’ve mastered the Windows PowerShell syntax, that is. But you’ll eventually learn how to do that, too, especially if you take a few moments to study the examples found in these articles .
Last, but definitely not least, we have the NOT filter. What do you suppose the following filter does?
"(!(Department=Finance))"
We won’t keep you in suspense: this filter returns all users who do not work for the Finance department. (The exclamation point – ! – indicates a NOT filter.) Here’s another example. Need a list of users who do not have a home telephone number? This filter should do the trick:
"(!(HomePhone=*))"
After you’ve played around with this a little it will all begin to make sense.
Trust us.