Haiku #172

The sky rumbles and

Roars. We should talk about voice



Well, it looks like another nice day here in Park City: there are a few clouds in the sky, but the sun is shining and the temperature is expected to creep very close to – if not vault right over – the 80-degree mark. To be honest, there's nothing unusual about that: the author of today's haiku has now been gone for a week, and he has yet to see a day below 82 degrees, pretty nice when you compare it to the temperatures in Seattle for the past week: 68, 70, 73, 75, 70, and 70. For the first time all summer, the author of today's haiku is no longer a pale, pasty shade of white. He's still short and fat, but no longer pale and pasty!


Now, if you happened to drive through Park City at 2:00 AM you might not have expected decent weather today: last night we had thunderstorms, we had lightning, we had torrential rain, and we had gusts of wind blowing so hard that the author and his wife had to get up and close all the windows before their condo blew away. (Remember the tornado scene in The Wizard of Oz? The author of today's haiku half-expected to glance out his window and see his wife furiously peddling through the sky on her mountain bike.)


Note. No comparison to wives and wicked witches intended.


At any rate, some people might think last night's storm was a sign from heaven, a warning that the author of today's haiku should spend a little less time riding bikes and hiking through the mountains and a little more time actually doing his work. To be honest, the author of today's haikus isn't particularly superstitious (except when he's playing baseball), and he doesn't believe in signs from heaven. Will a little thunder and lightning keep him from enjoying himself while he's out of town? No way.


By amazing coincidence, however, it turns out that what the author of today's haiku enjoys doing is writing about the CsVoiceNormalizationRule cmdlets: Get- CsVoiceNormalizationRule, New-CsVoiceNormalizationRule, Remove-CsVoiceNormalizationRule, Set-CsVoiceNormalizationRule, and Test-CsVoiceNormalizationRule.


As most of you probably know, normalization rules convert the phone number dialed by a user into the E.164 phone number format used by Lync server. For example, suppose you need to make a quick phone call to Ken Myer, whose office is just down the hall. Using the E.164 format, you would need to dial a number similar to this:




Good question: setting aside the fact that you have to include the country code and the area code, how do you dial a + as part of a phone number? And that's the whole point of normalization rules. If you want to call Ken Myer, you shouldn't have to dial his official E.164 phone number; instead, you should be able to just dial his extension: 1219. The job of a voice normalization rule is to take the "simple" phone number dialed by a user (1219) and convert it to something Lync Server can work with (+12065551219).


So how does all this happen? Well, we'd like to tell you that it's magic; then we wouldn't have to try to explain how this all happens. As it turns out, though, there's nothing magical about it: instead, you use regular expressions to tell Lync Server how to convert a certain type of phone number into the E.164 format. Why do we say "certain type of phone number?" Well, if someone dials 1219 we know that's an internal extension number, and we can go ahead and convert that number to +12065551219. How do we convert a four-digit number like that? That's easy: we just tack on +1206555:




But suppose someone else dials this:




A number like that probably is not an internal extension. That means we don't want to just tack on +1206555:




That's not going to work.


Which simply means that a voice normalization rule really needs to do two things: 1) it needs to identify the type of phone number being dialed; and, 2) it needs to convert that specific type of number to the E.164 format. And, best of all, you get to tell the voice normalization rule how to do all that!


You can see why we find this so much more fun that hiking or mountain biking.


What we’ll do today is show you one example of a voice normalization rule and how you can create that rule using the New-CsVoiceNormalizationRule cmdlet. And, in yet another amazing coincidence, here's that example right now:


New-CsVoiceNormalizationRule -Parent site:Redmond -Name InternalExtensionRule -Description "Dialing with internal four-digit extension" -Pattern '^(\d{4})$' -Translation '+1206555$1'


Let's see if we can explain how this works. To begin with, notice that we didn't use the Identity parameter here. (We could have, but we didn't.) Instead, we used two parameters: Parent and Name. Technically, the Parent parameter specifies the scope at which our new rule is to be created; more practically, the Parent parameter specifies the dial plan where the new rule will be stored. As it turns out, Lync Server won't let you create a voice normalization rule that simply exists on its own. Instead, all voice normalization rules must be associated with an existing dial plan (in this case, the site:Redmond dial plan). Suppose we tried to create this new rule and the site:Redmond dial plan didn't exist. In that case, our command would fail and we'd get back this error message:


New-CsVoiceNormalizationRule : Invalid identity "site:Redmond/InternalExtensionRule": "Cannot add a normalization rule to missing dial plan "site:Redmond". You must create the dial plan first."


The name is simply the unique identifier for that rule. When we say "unique identifier," we mean within that particular dial plan. We just added a rule named InternalExtensionRule to the site:Redmond dial plan. Could we add a rule named InternalExtensionRule to the global dial plan? Sure we could. That's because the Identity for a normalization rule is composed by combining the Parent and the Name properties. Thus these two rules have the same Name, but different Identities:


· site:Redmond/InternalExtensionRule

· global/InternalExtensionRule


Note. Yes, you can use blank spaces in a rule name:


-Name "Internal Extension Rule"


If you do this, you just have to remember to always surround the name with double quote marks. That's too hard for us to remember, so we typically leave out the blank spaces. But that's up to you.


Next up we have the Pattern parameter (we probably don't need to explain that the Description is simply a description of our new rule):


-Pattern '^(\d{4})$'


The pattern is a regular expression that specifies the number dialed by the user. Now, to be honest, we aren't going to explain regular expressions in today's haiku; that's way too big of a job. (However, for a nice little introduction to regular expressions and voice normalization rules, you might take a peek at this article: http://theucguy.wordpress.com/2010/09/30/ocs-lync-server-normalization-rules). What we will explain, without going into a lot of detail, is that our pattern here looks for dialed numbers that start with 4 digits. The ^ means "starts with;" the \d means digits (numbers); and the {4} means that we're looking for exactly 4 digits. And the $ character means "the end of the phone number." In other words, we're looking for a phone number that consists of just 4 digits: 1219.


And suppose we find a 4-digit number; what then? Well, that's what the Translation pattern is for:


-Translation '+1206555$1'


The Translation pattern tells Lync Server how to translate that 4-digit number into an E.164 number. In this case, we're saying take the 4-digit number (represented by $1) and tack on +1206555. In other words, turn this:




Into this:




Hmmm, maybe it is magic after all.


Oh, and here's something very important: when specifying the Pattern and the Translation parameter values, make sure you surround those values with single quote marks. Why? Well, here's what the Translation property looks like if we use single quotes:


Translation : +1206555$1


What's wrong with that? Nothing; that's exactly what we want it to look like. But suppose we used double quotes with the Translation parameter, like this:


-Translation "+1206555$1"




If we do that (and we shouldn't do that) our Translation property will look like this:


Translation : +1206555


If you look closely, you'll notice that $1 has disappeared. (And yes, that is a bad thing.) Why did it disappear? Well, when a parameter value is enclosed in single quotes, PowerShell uses that value exactly as typed:




However, if you surround a value in double quotes, PowerShell attempts to "interpolate" any variables within that value; in this case, that means it treats $1 as a variable (remember that all variables in PowerShell start with $) and tries to substitute in the value of the variable $1. Because $1 doesn't actually have a value $1 thus gets replace by, well, nothing:




Here's a way you can test that: in this example, we're going to set $1 to a value (bob) and then create a new normalization rule:


$1 = "bob"


New-CsVoiceNormalizationRule -Parent site:Redmond -Name InternalExtensionRule -Description "Dialing with internal four-digit extension" -Pattern '^(\d{4})$' -Translation "+1206555$1"


And here are the properties for our new, virtual rule. Note how, in the Translation, $1 has been replaced with bob:


Identity : site:Redmond/InternalExtensionRule

Priority : 4

Description : Dialing with internal four-digit extension

Pattern : ^(\d{4})$

Translation : +1206555bob

Name : InternalExtensionRule

IsInternalExtension : False


The moral of the story? Use single quotes, not double quotes.


Note. Which, as we recall, is also the moral of the story for Hansel and Gretel. Yet another coincidence!


We won't spend any time today talking about Get-CsVoiceNormalizationRule, Remove-CsVoiceNormalizationRule, and Set-CsVoiceNormalizationRule; you can probably figure those out for yourself. (Take a look at the help documentation if you can't figure them out for yourself.) However, we will take just a moment to look at Test-CsVoiceNormalizationRule, a handy little cmdlet that lets you verify that your voice normalization rules are working as expected. Here's an example of how to use Test-CsVoiceNormalizationRule:




Get-CsVoiceNormalizationRule -Identity "site:Redmond/InternalExtensionRule" | Test-CsVoiceNormalizationRule -DialedNumber 1219


As you can see, what we've done here is used the Get-CsVoiceNormalizationRule cmdlet to retrieve our InternalExtensionRule rule. We've then piped that rule to Test-CsVoiceNormalizationRule, essentially saying, "Hey, what's going to happen if we use this rule and someone dialed the number 1219?" (Note the DialedNumber parameter.) In turn, we should get output similar to this:






Is that good? No, that's bad: our normalization rule gives us a translated number of +1206555bob. (Remember how we intentionally messed up the value of $1?) If we fix our rule (using the original statement to create the Translation with single quotes), we should get back something a little better:






Needless to say, something a lot better.


Anyway, that's all we have time for today; after all, we have a lot more work to do, and we want to get started on all that work right now, right away.


Just in case any of the weather gods are listening.


See you tomorrow.