Sunday, February 9, 2020

Migrating SfB Enterprise Voice Configs to MS Teams

I'm flyyyyyyiiiing through the cloud.....get it? Cloud? Teams is in the cloud. Got it? Good.

The UCDialplans.com Winter of Code continues at a torrid pace!

Hot on the heels of the MS Teams EV Backup/Restore scripts is another script that I can't believe someone hasn't already cooked up.

Say that you have a well-oiled Skype for Business Enterprise Voice deployment that you carefully cultivated over the years into a perfectly tuned beast. You're considering migrating to Teams, but aren't terribly enthusiastic about having to recreate all that magic in Teams.

Well, worry no more! I've created a script that will copy all of your Skype for Business Enterprise Voice settings into MS Teams. It will handle the following:
  • SfB dialplans into Teams dialplans (along with all normalization rules, of course)
  • SfB PSTN usages into Teams PSTN usages
  • SfB voice routes into Teams voice routes
  • SfB voice policies into Teams voice routing policies
  • SfB trunk translation rules into Teams translation rules
  • SfB PSTN gateways into Teams PSTN gateways
Most of this is completely automatic, except for the PSTN gateway part. Teams PSTN gateways have to be publicly accessible via FQDN, and that FQDN's domain name has to be registered in the O365 tenant. Chances are pretty good that the SfB PSTN gateways won't conform to those requirements. The script will first look for an existing Teams gateway that matches each SfB gateway. If one isn't found, the user will be prompted to do one of the following:
  • match the SfB gateway to an existing Teams gateway with a different FQDN
  • create a new Teams gateway with the same FQDN as the SfB gateway (only works if the SfB gateway already meets the Teams FQDN requirements)
  • create an entirely new Teams gateway
  • don't match the SfB gateway
All this is presented to the user in a straightforward menu. Once all the selections are complete, the selected matches are shown before erasing any existing Teams EV configs and migrating the SfB EV configuration:

The usual trained monkeys will then migrate all the SfB EV settings to Teams, with a few caveats:
  • Since Teams doesn't have "sites", site-level SfB dialplans will be converted to user-level dialplans
  • Same deal with site-level voice policies
  • If there are duplicate translation rules, they will be merged into one, but translation rules with the same name, but different patterns/translations will be changed to include the PSTN gateway name to avoid conflicts.
If all goes well, the script should progress like this:

If you want to see more details around how the secret sauce is made, run the script with the -Verbose switch:


Once complete, your SfB Enterprise Voice config should be completely migrated to Teams. Check things over to be sure. If you elected to create Teams PSTN gateways, you will have to revisit them using Set-CsOnlinePSTNGateway to make sure they're configured correctly. 

Hopefully, this script will help make your transition to Teams that much simpler!

To get the script, go on over to my Github page to get the latest version. If you experience any issues, you can create an bug report through there.

https://github.com/kenlasko/SfBEV2TeamsEV


Tuesday, February 4, 2020

Backing up and restoring Teams Enterprise Voice configs

In the cloud....get it?

Whenever I made changes to a Skype for Business Enterprise Voice configuration, either via a UCDialplans.com script or manually, I got into the habit of making a backup of the Enterprise Voice configuration. Skype for Business made this easy with an option directly in the UI that allowed you to backup and, more importantly, restore. I've made use of both options on more occasions than I can think of.

In the MS Teams world, there are a few scripts out there that help you backup your Teams Enterprise Voice configuration as part of a larger backup of the entire environment. However, I haven't found one that will let you restore that backup. Now there is!

I've published two PowerShell scripts that allow you to backup and then restore an MS Teams Enterprise Voice configuration in a few minutes. Slide on over to my Github page for the latest version.

https://github.com/kenlasko/BackupRestoreTeamsEV



BackupRestoreTeamsEV

PowerShell scripts that allow you to easily backup and restore your Microsoft Teams Enterprise Voice configuration

Getting Started

Download the scripts onto your local Windows machine where you normally connect to your MS Teams tenant.

Prerequisites

Requires that you have the Office 365 PowerShell module installed, and that you have a Microsoft Teams Enterprise Voice configuration that you are interested in backing up/restoring. You may have to set your execution policy to unrestricted to run this script:
Set-ExecutionPolicy Unrestricted

Running a backup

Simply run .\Backup-TeamsEV.ps1 from a PowerShell prompt. If you are not already connected to your Teams tenant, the script will prompt for authentication. If your admin account is not a @tenantname.onmicrosoft.com account, then you should use the -OverrideAdminDomain switch.

Restoring a backup

Run .\Restore-TeamsEV.ps1 with the path to the backup file to restore. If you are not already connected to your Teams tenant, the script will prompt for authentication. If your admin account is not a @tenantname.onmicrosoft.com account, then you should use the -OverrideAdminDomain switch.
By default, the script will clean out any existing config, including dialplans, voice routes, voice routing policies, PSTN usages and translation rules. You can override this behaviour by using the -KeepExisting switch.

Monday, February 3, 2020

Undocumented behaviour with dialplans on MS Teams

Who am I talking to? Eartha Kitt? Sorry, wrong number. I was trying to call my car.
I've been spending a lot of time recently messing around with MS Teams dialplans thanks to the recent release of SBC-specific translation rules. For the most part, the overall structure is the same as with Skype for Business on-prem. Some PowerShell commands are different, but most of what you already know is usable in Teams.

The biggest issue with Teams is that there hasn't been a UI for managing your Teams Enterprise Voice layout. This can be an issue if you are big into Direct Routing.  Thankfully, Microsoft recently made available what is hopefully the first round of UI enhancements to the Teams Admin Center. You can now create and manage dialplans in the UI. Microsoft has dutifully updated their documentation to help people navigate these new features.

However, there are some undocumented features that open up some interesting scenarios. Along with these are some extremely frustrating bugs that you should be aware of.

The Hidden Feature

In Skype for Business on-prem, there is a global dialplan that applies to users when there isn't an appropriate site or user-level dialplan. In the Teams documentation, the tenant Global dialplan serves a similar purpose, but there are additional features outside of what can be done on-prem. 

In Teams, Microsoft has published a series of very simple pre-defined dialplans called service country dialplans that apply to nearly every country in the world. You can see these by running Get-CsDialPlan and Get-CsVoiceNormalizationRule. They do the bare minimum, and don't do any real validation. These dialplans cannot be modified. 

The documentation goes on to say that you can define normalization rules in the tenant global dialplan or any number of tenant user dialplans. If a user tenant dialplan isn't assigned to a user, they will be assigned an effective dialplan of whatever is in the tenant global dialplan and the default service country dialplan for that user. Similarly, if a user is assigned to a tenant user dialplan, their effective dialplan will consist of the contents of the assigned tenant user dialplan along with the user's service country dialplan. The tenant dialplans have higher priority than the service country ones. 

The hidden feature is that if you have defined any normalization rules in the tenant global dialplan, those normalization rules will appear at the top of the list of any new tenant user dialplans created afterwards. This can be very useful in large organizations with many sites, but have a common set of extension dial rules. You simply have to define the rules once in the tenant global dialplan, and they will show up in every new tenant user dialplan created from that point forward. This applies to dialplans created in both PowerShell and the Teams Admin Center.

Unfortunately, there isn't a real link between the tenant global and tenant user dialplans. They are simply copies of the original. If you make a change to the tenant global dialplan, it won't be reflected in all your other dialplans. As with Skype for Business, you're still stuck with using PowerShell to manage bulk changes. Overall, it might make deploying new dialplans just a little bit simpler if you choose to take advantage of this undocumented feature.

The Bugs

Unfortunately, there are also a number of annoying bugs/oversights with the new UI. Some are cosmetic, but some will force you to re-create your dialplan if you accidentally encounter one.

Description Discrepancies

If you've used UCDialplans.com to create your dialplans, you may have noticed that I make extensive use of the Description field to let people know where the rules came along with a copyright notice. I even put in linebreaks to make it readable in the SfB Control Panel UI. These descriptions are rather lengthy and go well beyond 255 characters (sorry, not sorry).

Here are my grievances with the Description field in the MAIN dialplan UI:
  1. The Teams admin center dialplan UI doesn't respect the linebreaks and just renders a wall of text.
  2. The UI throws an error that you can only have up to 255 characters, when PowerShell allows a much higher number.
  3. The UI also throws errors about "invalid characters", such as !@#$%^*()=/\[]{}:;?<>+' Again, PowerShell has no issue with any of these characters.



What makes this even more odd, is that the UI for the individual normalization rules exhibits NONE of these issues. It renders the linebreaks properly, and doesn't complain when the description is longer than 255 characters and includes "special" characters.

The External Dialing Prefix Bug

This is a big bug, and am quite surprised it made it through QA. For a bit of background, you can configure Teams so that users can dial an external access prefix (like 9), before dialing an external number. For users coming from a legacy PBX, this is undoubtedly a boon and doesn't force them to retrain years of muscle memory. It also makes off-hook dialing more reliable. For an extended explanation of this feature, go waaay back to one of my posts from 2011 on the topic. I haven't tested it myself, but the details should still apply to Teams.

Most customers don't bother with this feature, and have been happily creating dialplans in Teams/Skype for Business Online for years via PowerShell without defining an external dialing prefix. However, if you create a new dialplan or edit an existing one in the UI, you will not be able to save your changes unless you set an external dialing prefix.

Also, this seems to have found itself into the PowerShell commands themselves, but not completely. You can create a new dialplan with New-CsTenantDialplan without an external dialing prefix, and you can make changes to an existing dialplan with Set-CsTenantDialplan, but if you try to remove an external dialing prefix via PowerShell by setting it to either $NULL or "", you will be denied.

As long as you don't set a prefix, you should be fine. Don't do what I did and set a prefix in the UI to see what happens, and expect to be able to remove it in PowerShell. This forced me to redo my entire dialplan from scratch. Thankfully, UCDialplans.com took care of that in a minute.

Spelling Mistakes

A minor quibble, but one that will force me to keep a close eye on (boo hoo me). The New/Get/Set-CsOnlinePSTNGateway command has options for setting inbound and outbound translation rules:

  • InboundPstnNumberTranslationRules
  • InboundTeamsNumberTranslationRules
  • OutboundPstnNumberTranslationRules
  • OutbundTeamsNumberTranslationRules

Do you see the problem?

Someone's spellcheck didn't work.

Conclusion


I'm trying to get this in front of the right people at Microsoft to get them to fix. Hopefully, this half of my post will have a limited shelf life. Have any of you experienced any of these issues or come across any others? Let me know in the comments.

Wednesday, May 22, 2019

Useful Teams PowerShell Commands

Back in the day, I compiled a shortish list of Lync/Skype for Business PowerShell commands that did some reasonably useful stuff. With my shift to Teams, I figure now is a good time to start compiling a list of semi-useful PowerShell scripts for Teams. I will add to this list as I think of them.

Getting Started

Before you run any of these scripts, you have to install the Teams PowerShell module. Thankfully, its very easy to do nowadays, as long as you're running PowerShell version 5.0 or higher (run $PSVersionTable.PSVersion to check). If you're not running PS v5.0 or higher, I recommend installing it. Once PS 5.x is installed, you simply run the following in an elevated PowerShell prompt to add the Teams module:
Install-Module -Name MicrosoftTeams -Repository PSGallery      

You then connect to your Teams tenant by running:
Connect-MicrosoftTeams

You will be prompted for admin credentials. Enter those, and you'll be all set!

Count All Teams Users in your Tenant

There isn't a direct way to count every Teams user across your tenant. However, you can get a list of users in a specific team. Thanks to PowerShell pipelining, you can string multiple commands together to get the desired result.
(Get-Team | Get-TeamUser | Sort-Object User -Unique).Count
If you want to exclude external accounts who you've granted guest access to Teams, modify the command accordingly:
(Get-Team | Get-TeamUser | Where {$_.User -notlike "*#EXT#*"} | Sort-Object User -Unique).Count
You can get a count of all Skype for Business (on-prem) and Teams users, excluding duplicates by running this from a Skype for Business PS prompt:

$SfBUsers = (Get-CsUser).SipAddress -Replace "sip:", "" 
Connect-MicrosoftTeams 
$TeamsUsers = (Get-Team | Get-TeamUser | Where {$_.User -notlike "*#EXT#*"} | Sort-Object User -Unique).User 
$CombinedUsers = $SfBUsers + $TeamsUsers | Sort-Object -Unique
$CombinedUsers.Count

Thursday, February 21, 2019

Can You Install an SDN Listener in a SfB CCE Environment?

As part of my job at Nectar Corp, I'm always looking at ways to get call detail information from various Microsoft-based UC telephony components. It's easy to get a wealth of detail from an on-prem deployment of Skype for Business, using either direct access to the SfB monitoring databases (for historical info) or via the Skype for Business SDN dialog listeners (for realtime call data).  As mentioned in my recent blog post, the same cannot be said for Office 365 UC workloads such as SfB Online and Teams.

Nectar has a tool that relies on knowing when a call starts and stops in realtime so we can gather information from various network components as the call is happening. Nectar uses the SfB SDN interface for this realtime info. Since both SfBO and Teams lack ANY sort of call data API, we have to resort to other methods to learn about realtime calls.

A Nectar customer has deployed several Skype for Business Cloud Connector Edition (CCE) instances to provide on-prem PSTN call routing for SfB Online users. CCE is the precursor of Teams Direct Routing, and it does essentially the same thing but in a WAAAAAAY more complex manner. It consists of several pre-packaged VMs running a vastly slimmed down Skype for Business environment.

We were in discussions with the customer to see if there was any way we could get realtime call info from the CCE instances, by installing the Microsoft SDN Dialog Listener on any of the CCE VMs. Nobody seemed to know for certain, so I elected to setup a CCE instance of my own to run some tests. The question I was trying to answer was:

Can you get call data out of a CCE instance using the MS SDN Listener service??? 

TL;DR:  No.

I had built myself a fancy new work PC at home that I figured would be ideal for this test. I put in 32GB of RAM so I could spin up VMs as needed for my tests, and this would be an excellent test OR SO I THOUGHT!

My first hurdle was that I needed a virtual machine host that would host the VMs required for CCE. In other words, I needed to run VMs from within a VM.
Just like the movie, except much more boring.
CCE only supports Windows 2012 R2 as the VM host OS, so I started with that as my VM host for my VMs, but it blocked me from installing the Hyper-V components, since it was already a VM. A bit of research showed that I could use Windows Server 2019, which had no such limitation. I spun up my Hyper-V host VM, gave it 24 GB of RAM, all the processor cores I had, and off I went.

...OR SO I THOUGHT (AGAIN). I ran into issues right off the bat when installing the CCE components, where most of the CCE PowerShell commands were available. Thanks to a workaround from Shawn Harry, I was soon off to the races and installing CCE.

...BUT NO! FOILED AGAIN!!! There are a number of steps to deploy a CCE instance, most of which require running various CCE PowerShell commands. One of these commands performed a hardware check to make sure the Hyper-V host had enough resources to run the required VMs. I was rudely informed that my super-awesome, brand-new workstation PC did not have enough memory, processor cores or disk space to run CCE.

Keanu is sad, and so am I
Time to implement my hacker skilz.

The big red, failure message that PowerShell spit out at me helpfully included the path to the script that did the hardware check:

C:\Program Files\WindowsPowerShell\Modules\CloudConnector\Internal\MtCommon.ps1

A quick review of that script, and a few edits to the $logicalProcessorsCount, TotalPhysicalMemory, FreePhysicalMemory, and FreeDiskSize variables and the script was convinced that I had a super-server with 999999999999 MB of memory, diskspace and processor cores.

With a few more tweaks here and there, I was finally able to get a full Skype for Business Cloud Connector Edition instance running in my modest home lab. Since the CCE environment consists of several standard Windows VMs, I was able to login to them and attempt to install the SDN Listener on what I hoped would be possible candidates: the CMS or the Mediation VM.

Neither of those were running as a full front-end server, but I still held out hope. The CMS VM was running the Centralized Logging Service Agent, File Transfer Agent and Master/Replica Replicator Agents. The Mediation VM was running the Centralized Logging Service Agent, Mediation service and Replica Replicator Agents.

I attempted to install the SDN Listener on both those VMs and got the following message:

And there you go. The answer to a question that nobody else was looking for. No, you can't run SDN on any CCE VM role.

Hope you're happy @ucomsgeek.

Wednesday, February 20, 2019

The Current State of Microsoft Cloud-Based Call Analytics

Microsoft Teams continues to evolve at a rapid pace, and nearly every company who currently runs on-prem Microsoft Skype for Business deployments are investigating the feasibility of moving their UC infrastructure to the cloud.

While moving UC workloads to the cloud eliminates the need for most (if not all) customer-owned server hardware, UC media still must traverse the customer network. Since the network is one of the major causes of poor call quality, customers still require advanced troubleshooting capabilities.
Microsoft does provide some basic call analytics in Office 365, but there is much room for improvement. As such, many 3rd party vendors (including Nectar, the company I work for) are keen to fill the gaps. Unfortunately, there is a lot of confusion and misdirection in the marketplace about what is possible with 3rd party call analytics in Office 365 UC platforms.

Skype for Business Online

Even though Skype for Business Online is being deprecated in favour of Microsoft Teams, there are still many customers who have not made the transition. As mentioned previously, Microsoft does provide some limited call analytics and reporting capabilities for both SfBO and Teams at no extra cost, but the available tools only show a subset of the available information, presented in limited ways. A dedicated person can customize the tools to meet some of their needs, but this takes time, effort and deep knowledge.

This is the ideal place for other companies to provide additional value. Unfortunately, the only way to access detailed call analytics is to use the Get-CsUserSession PowerShell command (included as part of the Skype forBusiness Online PS Module).

The output of the Get-CsUserSession command provides detailed CDR and quality information for a specified user over a specified timeframe in JSON format. Much of the detail is not available in the existing O365 dashboards. At first blush, this appears to be a fantastic way to import detailed call data into 3rd party call analytics platforms, but there are some significant limitations:
  • The Get-CsUserSession command only returns data for a single user
  • No built-in delta mechanism, so no way to return only new/updated information since the last time the command was run
  • Data is only available for 30 days

Theoretically, a tool could constantly run the Get-CsUserSession command for every user in the enterprise, but this is not an approach that scales to the enterprise-level.  Nectar looked at this but discarded it after early tests showed inherent scalability issues.

Another approach would be to provide a GUI front-end that simply runs the Get-CsUserSession in the background on demand for a specified user. This avoids any scalability issues and can provide some useful information on a given user’s call quality. However, this approach doesn’t allow for any enterprise-level reporting or trending data, since data for all users is never downloaded.

Microsoft Teams

As mentioned in the previous section, customers have access to a limited portfolio of Teams call analytics tools through their Office 365 subscription. There is room for improvement and vendors are anxious to show value in this area.

At the time of writing, there is absolutely no way to access detailed call analytics about Microsoft Teams calls or conferences. The Get-CsUserSession command discussed in the previous section only returns call data for SfBO calls.  Some Teams PSTN calls may show up in Get-CsUserSession results, but that’s because it appears PSTN calls are routed through SfBO infrastructure. Detailed call data on P2P calls or conferences are not available.

Some vendors provide some great analytics about Teams usage (channel members, activity etc). These analytics are provided through the Office365 Graph API and does not provide any detailed call analytics. Through misunderstanding or miscommunication, some customers end up confusing this point and think they can get the same level of call quality troubleshooting as they currently can from on-prem Skype for Business deployments.

The Future of Microsoft’s Cloud-Based Analytics

Microsoft has not shared any information about when or if they will release an API that will allow vendors to incorporate Teams call data into their existing call analytics platforms. Rest assured that Nectar is keeping a close eye on the situation and will be able to incorporate Office365 call analytics into our call analytics platform as soon as something is available.

In the meantime, if a vendor is promising they can do advanced analytics for Microsoft’s cloud platform, think of this and ask the right questions:
  • Exactly what kind of call data can the tool retrieve? (MOS scores, jitter, packet loss etc)
  • How does the tool get data from Office365?
  • Does the tool provide detailed call quality reports for the entire enterprise?

If you get appropriate answers, then you can make a more informed decision on purchasing a call analytics platform for your cloud-based UC platforms.

This article was also posted on Nectar's website and Telecom Reseller

Monday, October 1, 2018

Run PowerShell Core in Docker on Raspberry Pi

I've recently started playing around with the latest Raspberry Pi 3 B+ along with a PoE HAT, which is an amazing little piece of kit. I've been trying to offload a bunch of workloads that are currently being served by an aging Windows 10 PC whose primary role is a media PC. This very same PC used to run Windows Media Center on Windows XP, before I turned to XBMC/Kodi and then Plex. It's well over a decade old, but still going strong. Although, now that I think of it, the only thing that's probably still original is the case, since I've replaced the motherboard, videocard, disk drives, and power supply over the years.

Since I've worked with Docker in the past (most recently with a failed attempt to move the Skype Optimizer to a Docker container), I thought I'd try running Docker on the Raspberry Pi.

My home networking setup in the basement furnace room. My Raspberry Pi is circled in red.
I've been pretty successful in getting things working efficiently on Docker.  At the time of writing this post, I've got the following Docker containers running on my little Pi:
  • Traefik - an easy-to-use reverse proxy solution so that I can access the different container UIs by using https://containername.skypeoptimizer.com, instead of http://192.168.1.x:somerandomport. It also simplifies certificate management, since I only have to do it once in Traefik with a wildcard cert, rather than on every container.
  • Portainer - a GUI for container management, so I don't have to remember specific commands
  • Pihole - a DNS blackhole for ads. Blocks ads for all devices on my internal network. Tidbit: its currently blocking almost 50% of my total DNS queries!
  • Radarr/Sonarr/Jackett - *cough cough* media management
  • Unifi Controller - manages my amazing-how-did-I-live-before-this wifi infrastructure
  • NOIP - publishes my ever-changing public IP address to Noip.com.
  • PowerShellPi - Runs PowerShell scripts
It was the last one that took me the most work to get going. While there are several published PowerShell Docker containers for Linux, there didn't seem to be any that would work with the ARM processors inside the Raspberry Pi. 

After much trial-and-error (story of my life), I finally managed to build a working Docker image that works with the Raspberry Pi. I used some of the bits from this TechNet blog to get the right commands, but had to make some modifications for it to work on the latest Raspberry Pi version. 

I'm using this base image to build other images that run simple scripts on a schedule. For example, I've got one that checks my public IP, and updates an Azure DNS record when it changes. It does the same thing as the NOIP one, but its something that's fully under my control.

For those who are interested, the base image is available on https://hub.docker.com under kenlasko/powershellpi. The DockerFile used to build this image is shown below (since I haven't figured out how to display it on Docker Hub yet).

# PowerShell on Raspberry Pi
#
# Version 1.0

FROM raspbian/stretch

LABEL maintainer="Ken.Lasko@gmail.com"

RUN sudo apt-get update
# Install libraries necessary for PowerShell
RUN sudo apt-get install --no-install-recommends -y libunwind8 libicu57 libcurl4-openssl-dev cron
# Get the latest released PS module
RUN wget https://github.com/PowerShell/PowerShell/releases/download/v6.0.4/powershell-6.0.4-linux-arm32.tar.gz
# Make PowerShell directory and install PS module
RUN mkdir ~/powershell && tar -xvf ./powershell-6.0.4-linux-arm32.tar.gz -C ~/powershell && sudo ln -s ~/powershell/pwsh /usr/bin/pwsh && sudo ln -s ~/powershell/pwsh /usr/local/bin/powershell
# Remove install files after completion
RUN sudo rm -rf /var/lib/apt/lists && rm powershell-6.0.4-linux-arm32.tar.gz
# Install PS modules (Azure auth and DNS modules shown as examples)
#RUN ~/powershell/pwsh -Command Install-Module -Name AzureRM.Profile.Netcore -Force && ~/powershell/pwsh -Command Install-Module -Name AzureRM.Dns.Netcore -Force


My next move is to get a few more Raspberry Pis to create a Docker Swarm to automatically load-balance my containers, just like a wee little datacenter!

This is for you, Pat!

Tuesday, September 18, 2018

Skype Optimizer JSON Interface Now Available

For people who like to have more control over how dial rules are implemented but want to leverage my vast database of worldwide dial rules, I have implemented a JSON interface for the Skype Optimizer.

The interface will return all the dial rules for a given country in JSON format, which can then be used in your own scripts or application. 

To access the API, you simply construct the URL to correspond to the correct representation for the desired country. The base URL for the JSON interface starts with:

https://www.ucdialplans.com/queryapi/

You then add specific options as query strings. For NANPA countries like US, Canada and many Caribbean countries, use the following:
Query String
Data Value
Required?
countrycode
1
Yes
npa
3-digit integer
Yes
nxx
3-digit integer
Yes
natscope
national/uscan/all
No (defaults to national)
sevendigit
yes/no;1/0;true/false
No (defaults to no)
apikey
16-character alphanumeric
Yes

For an example location in Chicago, where we want to treat both US and Canada numbers as national numbers, use the following:
https://www.ucdialplans.com/queryapi/?countrycode=1&npa=312&nxx=226&natscope=uscan&apikey=yourapikeyhere

For any other country, the available query strings are as follows:
Query String
Data Value
Required?
countrycode
1-3 digit integer value
Yes
areacode
1-6 digit integer value
Most countries
carriercode
1-3 digit integer valueSome countries
apikey
16-character alphanumeric
Yes

For example, to return the dial rules for London, UK use the following URL:
https://www.ucdialplans.com/queryapi/?countrycode=44&areacode=20&apikey=yourapikeyhere
The results will look something like this, when you parse it through a JSON formatter:

One way to use this data is in your own personal PowerShell script. Below is an example that will pull down the dial rules for 312-226 in Chicago:
$DialPlan = "https://www.ucdialplans.com/queryapi/?countrycode=1&npa=312&nxx=226&apikey=abc1234567890xyz"
$JSON = Invoke-RestMethod -Method Get -Uri $DialPlan
Once you've got the JSON data, you can return specific elements simply by "following the path", as it were:
PS C:\> $JSON.data.routes

local         : {@{pattern=^\+1((872([^01]\d\d))|(847([^01]\d\d))...
tollfree      : @{pattern=^\+18(00|8\d|77|66|55|44|33|22)\d{7}$}premium       : @{pattern=^\+1(900|976)[2-9]\d{6}$}national      : @{pattern=^\+1(?!(24[26]|26[48]|284|345|441|473|649|664|721|[67]58|767|784|8[024]9|86[89]|876|900|976))                [2-9]\d\d[2-9]\d{6}$}international : @{pattern=^\+((1(?!(900|976))[2-9]\d\d[2-9]\d{6})|([2-9]\d{6,14}))}service       : @{pattern=^\+?([2-9]11)$}

You can get as specific as you want:
PS C:\> $JSON.data.normrules.national.pattern^1?([2-9]\d\d[2-9]\d{6})\d*(\D+\d+)?$PS C:\> $JSON.data.routes.international.pattern^\+((1(?!(900|976))[2-9]\d\d[2-9]\d{6})|([2-9]\d{6,14}))
For elements that could have multiple values, such as local routes, you have to write it a little bit differently (note the square brackets):
PS C:\> $JSON.data.routes.local[0].pattern^\+1((872([^01]\d\d))|(847([^01]\d\d))|(815(20[^189]|21[^1378]|22[01348]|23[067]|24[125]|25[02458]|26[^2469]|27[^035]|28[07]|29[0345]|30[^39]|31[03478]|32[^39]|33[^2569]|34[^0]|35[^089]|36[1346]|37[^5689]|38[23568]|40[^026]|41[^149]|42[^089....
You must also include a valid API key. For testing purposes, I've created a demo API key that anybody can use.
abc1234567890xyz
The output will be obfuscated to deter abuse, but is otherwise valid JSON.  Please give it a try, and if you feel there is a place for this in your company or application, please contact me through the usual channels.

The APIs have been documented on SwaggerHub, where you can check them out and even run tests to see what the output looks like.

I have to give thanks to longtime SfB/Teams MVP Jonathan McKinney for pushing me to do this, and to help test it along the way. Beers are inbound!

Wednesday, June 6, 2018

Teams Direct Routing Now Supported in the Skype Optimizer

Since Microsoft made Teams Direct Routing available as a preview, I've been working to modify the Skype Optimizer codebase to support it. Finally, after weeks of coding and testing, I'm pleased to announce that the Skype Optimizer now fully supports Teams Direct Routing.



O365-native PSTN calling is only available for a small number of countries. A lot of companies have held off from migrating to the cloud until there was some way of allowing PSTN calling for users in the other countries that aren't supported in O365. Direct Routing allows customers basically anywhere in the world to move their users to Office 365 while allowing telephony connections to their provider of choice via on-premises PSTN gateways.

Direct Routing in Teams works almost exactly the same as it does in Skype for Business on-premises. The only differences are in how normalization rules are handled (which I talked about in an earlier post), and there is no support for trunk translation rules. This means that if you have to do any manipulation of numbers before sending to another PBX or the PSTN, you will have to do it at the PSTN gateway level.

There are still voice policies, routes and PSTN usages that are combined together to provide nearly limitless ways to control how phone calls are routed in your company. The difference is really just in the name of the PowerShell commands, as shown below.

Skype for Business
Teams Direct Routing
Get-CsVoicePolicy
Get-CsOnlineVoiceRoutingPolicy
Get-CsVoiceRoute
Get-CsOnlineVoiceRoute
Get-CsPstnUsage
Get-CsOnlinePstnUsage

Fortunately, since the underlying concepts are still the same, that means the Skype Optimizer can still provide many of the same features to Teams as with on-prem deployments.  This includes:

  • Class of Service
  • Least-cost/failover routing
  • Extension ranges
Skype Optimizer features not currently supported in Teams include:
  • Location-based routing
  • Selective caller-ID blocking
  • Premium number blocking via Announcement service
  • Call Park
The PowerShell script generated by the Skype Optimizer builds on the same codebase used for Skype for Business Online scripts. The only prerequisite is that you have already paired one or more PSTN gateways to your Office 365 tenant using the New-CsOnlinePSTNGateway command.  If you haven't done this step, the Skype Optimizer script will only create normalization rules that will apply to both Skype for Business Online and Teams. 

Please try out the new functionality and let me know if you experience any issues.

Monday, March 12, 2018

The Evolution of the Skype Optimizer: From Locally-Run VBScript to Azure Web App

I was recently amazed when I realized the seeds of what is now the Skype Optimizer was created 10 YEARS AGO, back when Office Communications Server 2007 R2 was starting to make headway in the unified communications space.

The Beginning

The genesis of the program grew out of a need to know when to strip the +1 from a North American local number and when not to. Rather than rehash the creation myth, you can read all about it from one of my earliest blog posts where I announced the Dialing Rule Optimizer to the world (at that time, the 30-odd subscribers to my blog).

The very first version was a straight VBScript that I had to manually input the proper variables and run it by hand. Rather than give the code away, I told people to email me the phone numbers they wanted to get optimized dial rules for.  I would run the script and send them the results, which was a simple text file with either a bunch of regex, or text formatted to be applied to AudioCodes or Dialogic gateways.  Word got around within Microsoft, and I found myself busy sending stuff to various Microsoft consultants.

When Lync 2010 came around, which was in its earliest days known as Communications Server "14", I added the capability to create simple routing rules that consisted of a few lines of PowerShell code.  I also wrapped the code around a simple UI in something called an HTA (short for HTML application).  It made generating rulesets easier for me, but it was still something that I was running from my local machine.
The earliest known copy of the original Dialing Rule Optimizer. I obtained this from the Smithsonian Museum. The text-only v1.0 has been lost to the sands of time.
I soon figured out that it would be relatively straightforward to move the HTA into an actual web page.  I put the code on a web server hosted by the company I was working for at the time and opened up the tool to the entire world. I actually put this code on the computer that was running our OCS 2007 R2 server!

The very first web-based iteration of the Dialing Rule Optimizer. Note the Communications Server "14" logo on the top-right.

Once Communications Server "14" became Lync 2010, I realized that I could go beyond simple optimized route creation, and modified the Optimizer to create everything required for a simple Enterprise Voice setup for US and Canada deployments.

Shortly after, I realized that I could do the same for other countries as well. The Optimizer interface grew somewhat to accommodate the requirements for different countries.
Dramatic differences abound! Communications Server "14" has changed to "Microsoft Lync". Also, UK dial plans!
I slowly added other countries to the Optimizer. I also added other features such as extension dialing rules, least-cost/failover routing, among many others.

Over time, the back-end code base was starting to become difficult to support. I was using a series of XML files to deal with languages and country-specific dialrules, and the sheer number of them was becoming cumbersome to manage.  I decided to move everything from the company-hosted platform to Amazon Web Services. I built a single Windows VM with SQL Express and ported the XML files to a database. It worked well, but AWS was starting to cost a fair bit to run for a free service. Donations were not keeping pace with costs.

I then discovered that Microsoft MVPs got a monthly allotment of funds in Azure. I immediately moved my infrastructure to Azure, where it ran mostly trouble-free for the next several years.

The Optimizer featureset grew and grew, but the interface was still as ugly as the day I first created it.  One person even suggested it looked like a GeoCities page. Hey, my argument was always that I was not (and am still not) a web developer.


The Modern Era

I decided to try to give the Optimizer a more modern look. I completely re-wrote the front-end code using Notepad++ as my trusty editor.  I replaced the clunky extension builder with a better Javascript framework that emulated an Excel spreadsheet, and made other significant under-the-hood improvements. After much trial and error, I was pleased to unveil the new look.

The website looked modern, clean and easy-to-use.  However, it bugged me that while the site was running in Azure, it was still just a single Windows VM with a local SQL Server instance. It was also costing most of my monthly Azure MVP credits with not a lot of headroom. I decided to try to make the Skype Optimizer simpler and cheaper to manage, figuring that there would probably come a day when I would not be a Microsoft MVP (the horror!!!) and I'd be expected to pay a monthly bill (Oh the humanity!!!).

My first step was to dump the local SQL and move to Azure SQL Database. First, I had to copy the gigabytes of data to my SQL instance.  I opted to use transactional replication, which would allow me to keep both my local and Azure-based SQL instances up-to-date while I tested things out.  It turned out to be ridiculously easy. I had to modernize my code a bit to allow it to still read/write data to Azure SQL, but this was pretty straightforward as well.

With that hurdle out of the way, I looked at a few different ways to further reduce my costs and administrative burden. 

Docker Containers

I'd heard about Docker containers and how it was an easy way to reduce the overall complexity and costs over a traditional virtual machine.  I installed Docker on my home machine and started messing around with it. I used the Image2Docker tool to make a copy of my Windows VM-based website and installed it locally.  I had to do quite a bit of modifications to my dockerfile to support some of the added features that Image2Docker didn't capture, but after a while, I managed to make the Skype Optimizer work in a Docker container. 

Moving my newly-created container to Azure wasn't too much work, but there was definitely a learning curve involved. It started up fine, and I pointed my DNS entries to the container and away we went!  However, all was not perfect:
  1. My container stopped working a few times over the span of a few weeks. Troubleshooting this proved to be nearly impossible due to the nature of Docker containers and how you lose the previous state every time you restart it. Either that or I just don't know how these things really work.
  2. Making code modifications wasn't simple either. I'd have to make the change in my local Docker image and publish that image to Azure. The startup process took 5-10 minutes, which was probably due to how I built my container.  I looked at ways to improve the startup time, but it was already eating up lots of my time.
  3. The costs to run the container wasn't much cheaper than a full VM
Because of those reasons, I decided that Docker containers weren't well-suited to my needs and I finally turned to....

Azure Web App

All the back-end code changes I made to the Optimizer to support both Azure SQL Database and Docker containers actually had an unexpected side benefit: it allowed me to easily move the Optimizer to Azure Web App, which is Azure's web hosting framework.

To make the process of managing this easier, I finally moved away from Notepad++ as a development environment and embraced Visual Studio. Visual Studio made it trivial to take my entire website and migrate it to Azure. I had a few challenges with making my code-signing certificate work, but in the end it all worked flawlessly. 

The Skype Optimizer has been running as an Azure Web App for several months now. Its extremely reliable, simple to manage, and about 3 times cheaper to run than the original Windows VM. 

The Future

So, there you have it. The entire history of the Skype Optimizer posted here for posterity. Where do things go from here? Well, with Microsoft Teams eventually taking over the Enterprise Voice role from Skype for Business, I may decide to look into what it would take to turn the Skype Optimizer into a Teams Direct Routing and Calling Plans management platform. 

Until then, I will continue to keep updating the Skype Optimizer to make sure Skype and Teams administrators worldwide have a single place to get accurate, up-to-date dialing rules for every country in the world.