If you were to tunnel deep enough under the Microsoft campus (note: please don't tunnel under the Microsoft campus, or at least don't tell anyone we suggested it), you would eventually run into the concrete bunker that serves as the Lync PowerShell Fortress of Solitude.
Note . You'll also find thousands of copies of Windows ME buried down there as well. But that's another story.
Inside the Fortress of Solitude, teams of dedicated Lync Server PowerShell blog writers monitor the world of Lync Server PowerShell, 24 hours a day, 7 days a week. Whenever they hear about someone who has a problem or a question involving Lync Server PowerShell, they immediately dispatch a highly-trained Lync PowerShell SWAT team, a SWAT team that can be airlifted and parachuted into your facility within hours. Once your facility has been secured, the SWAT team will fix your problem or answer your question, then disappear into the night, awaiting their next assignment.
Note . OK, it's possible that the preceding description could be a bit of an exaggeration. But you have to consider things from our point of view: it's mid-year review time here at Microsoft, and the things that we really do do – write a daily Lync Server PowerShell haiku and ask you which PowerShell cmdlet is not like the others – don't stack up very well against the things other technical writers do around here.
Oh, and in case you're wondering, no, we haven't been able to talk Microsoft into buying us parachutes. However, the company doesn’t seem to have any problem with us jumping out of airplanes without those parachutes.
But while we may not have a Fortress of Solitude, from time-to-time we do get questions about Lync Server PowerShell, and while we might not be a real SWAT team, whenever we do get a question we sit down and try to come up with an answer. (And, before you ask, yes, we usually try to come up with the right answer.)
For example, just the other day we received this email:
"Do you have or can you post a PowerShell script that will go through AD and find all enabled Lync users and then set their LineURI as a normalized version of their Office Phone number? … This would help us out, otherwise we are going to have to sit and do this by hand, not to mention when the Office Number changes it will have to be manually changed in Lync. I’m sure I’m not the only one who could use something like this."
So can we do that? Can we write a script that will find each user who has been enabled for Lync Server, grab that user's phone number (as currently configured in Active Directory), and then assign that phone number to their LineUri attribute? Beats us. But we'll definitely give it the old college try.
Note . Which sounds pretty good, unless you know how hard we actually tried when we were in college.
Let's start with a very simple scenario. Suppose we have two user accounts that have been enabled for Lync Server. Those two users, and the Active Directory phone numbers that have been assigned to them, are shown below:
Display Name |
Phone Number |
Ken Myer |
|
Pilar Ackerman |
|
What we want to do is take those existing phone numbers, convert them to the line URI format, and then assign those line URIs to their respective users. For example, Ken Myer has the phone number 1-(206)-555-1219 . When our script finishes running, he should also have the LineURI TEL:+12065551219. What kind of script can do that for us? This kind of script:
$enabledUsers = Get-CsAdUser -Filter {Enabled -ne $Null}
foreach ($user in $enabledUsers)
{
$phoneNumber = $user.Phone
$phoneNumber = $phoneNumber -replace "[^0-9]"
$phonenumber = "TEL:+" + $phoneNumber
Set-CsUser -Identity $user.Identity -LineUri $phoneNumber
}
Let's see if we can figure out what's going on here. To begin with, we use the following command to return information about all the users who have been enabled for Lync Server:
$enabledUsers = Get-CsAdUser -Filter {Enabled -ne $Null}
Now, you might be looking at the preceding command and wondering: a) why did they use Get-CsAdUser instead of Get-CsUser; and, b) why did they use the filter {Enabled –ne $Null} ? After all, if all we want to get back are the users who have been enabled for Lync Server, shouldn't we use this filter instead: {Enabled –eq $True} ?
Believe it or not, we have good reasons for using Get-CsAdUser and for using the filter we chose. (And yes, that might be the first time we ever had a good reason for doing something.) Because we're interested only in users who have been enabled for Lync Server, you might have expected us to use the Get-CsUser cmdlet; after all, those are the only kind of users Get-CsUser can return. However, if we want this script to actually work (which we do), then we need to retrieve each user's current Active Directory phone number. Get-CsAdUser returns that phone number for us; Get-CsUser doesn't. That's why we need to use Get-CsAdUser.
As for the filter we chose, well, the Enabled attribute tells whether or not a user has been enabled for Lync Server. The Enabled attribute can have one of three values:
· $True , which means the user has been enabled for Lync Server.
· $False , which means the user has been enabled for Lync Server, but his or her Lync account is temporarily disabled.
· $Null , which means that the user has not been enabled for Lync Server.
As you can see, if we looked for users where the Enabled attribute is equal to $True, we wouldn't get back users whose accounts have been enabled for Lync Server but are temporarily disabled. Therefore, we decided to look for all the users where the Enabled attribute is equal to either $True or $False; in other words, where the attribute is not equal to (-ne) $Null.
Note . Confused? We don't blame you. For more information on how the Enabled attribute works take a look at the article When is a Boolean Not a Boolean?
After we execute the first line in our script we should have information about all the users who have been enabled for Lync Server; that collection of data then gets safely tucked away in the variable $enabledUsers. In the next line of code, we set up a foreach loop that loops through all the users in that collection. Inside that loop, the first thing we do is grab the user's Active Directory phone number (for example, 1-(206)-555-1219 ) and stash that in a variable named $phoneNumber:
$phoneNumber = $user.Phone
This is where it gets interesting. (Or, should we say, this is where it gets even more interesting.) As you know, line URIs are finicky little things: they have to be formatted just right or Lync Server will reject them. In particularly, line URIs must start with the prefix TEL: + and then be followed by a phone number consisting of the country code, area code, and the actual phone number. Oh, and that country code, area code, and phone number? Digits only. No blank spaces, no parentheses, no hyphens, nothing but numbers.
Which means we have two problems here. First, Ken Myer's Active Directory phone number – 1-(206)-555-1219 – doesn't start with the TEL:+ prefix. Second, that phone number contains some invalid characters: (, ), and -. Before we can turn this phone number into a line URI we need to fix both of these problems.
Let's start off by tackling the second problem: the presence of invalid characters. What we need to do here is take our Active Directory phone number and remove everything that isn't a number. That sounds like a pretty daunting task; the truth is, it requires just one line of code:
$phoneNumber = $phoneNumber -replace "[^0-9]"
What we're doing here is using the –replace operator and the regular expression [^0-9] to locate everything that isn't a number and replace it with, well, nothing. We know that we're searching for everything that isn't a number because of the regular expression [^0-9] ; that value tells PowerShell to use the numeric range 0 through 9. The caret symbol ( ^ ) tells PowerShell to look for characters that aren't in the range 0-9. If we wanted to look for characters that are in the range 0-9 (which we don't) we'd simply omit the caret:
"[0-9]"
And to make sure that non-digit characters are deleted (that is, replaced by nothing) we don't include a replacement parameter. Suppose, for some reason, we wanted to replace non-digit characters with the letter X . In that case, we'd use this syntax:
-replace "[^0-9]", "X"
Which is probably more than you really need to know at the moment.
And so what do we end up with when we remove all the non-digit characters? This:
12065551219
As you can see, that's almost a line URI; the only problem is that it doesn't start with the TEL:+ prefix. But that's easy enough to fix:
$phonenumber = "TEL:+" + $phoneNumber
Needless to say, there's nothing too terribly complicated about that command. All we're doing is adding the prefix TEL:+ to the value stored in the variable $phoneNumber. That means $phoneNumber now looks like this:
TEL:+12065551219
And that value does look like a line URI, doesn't it? Which, in turn, means that all we have to do now is assign this value to the LineUri attribute. You know, like this:
Set-CsUser -Identity $user.Identity -LineUri $phoneNumber
If we take a peek at Active Directory right now we should see something that looks like this:
Display Name |
Phone Number |
Line URI |
|