This article builds on the following article.
Teams Phone Number Management on a Budget | by martin heusser | Mar, 2023 | Medium
Please make sure you read that one first.
After I built the list initially, I quickly realized that there’s still room for improvement. Especially a feature to assign and unassign phone numbers through the list would be incredibly handy. This would allow us to delegate this task to users without Teams Admin Center access.
The repository has been updated and already includes the code required for number assignment. If you’ve already deployed it, just delete the resource group in Azure and re-deploy. Of course, you can also replace the existing files with the new scripts and variables by hand if you know what you’re doing. But since there’s a deployment script available, I recommend deleting and re-deploying.
Let’s check out the new features first and go over the manual changes required later.
New Features
Country
All Calling Plan and Operator Connect phone numbers already include a country attribute. Therefore, I decided to use the property which is already known to Teams. The Country column now includes the two-digit ISO code instead of the country’s full name.
For consistency, the CountryLookupTable.json has been updated to include the ISO codes as well. Please see the Changes in V2 section of this article if the Country does not always match your direct routing numbers.
City
The purpose of this list is to make the management of phone numbers easier. The more information you have, the better you can filter. That’s why I added a City column as well.
Number Reservation
The Status column is now a drop-down list. Setting a number to Reserved won’t do anything yet, except letting other people which use the list know that this number should not be assigned to any user or resource account.
Because this is a SharePoint list, we can add a comment on that entry.
The runbook will not make any changes or updates to that entry until the number is assigned or the Status is changed by hand again.
Assign a Number
To assign a number, simply set a number to Reserved and choose a User Profile from the people picker.
The list will then look like this. There’s a User Profile linked but the User Principal Name is still unassigned.
The next time the runbook runs, it will try to assign the number. If the assignment fails for any reason, the Staus will change to Assignment Error but the linked User Profile will be retained.
Currently, the list only reflects a generic error. The runbook output, however, does include more information.
The script will even assign a voice routing policy for direct routing numbers if you specify one in the CountryLookupTable.json file. I will show you how to do that at the end of this article.
If you really want to be on top of your game, you can also create a rule to notify you of failed assignments.
Unassign a Number
To unassign a number, simply change the Status to Remove Pending.
The number will then be removed from the user, the next time the runbook is running.
Performance Improvements
I’ve also made some changes which will improve the performance of the script dramatically. I discovered a couple of redundant Graph requests which have now been reduced to the minimum required.
Changes in V2
Changes to the List
Edit the Status column of your list.
Change the Type from Single line of Text to Choice and add the following choices.
Save the changes to the Status column.
Now add a new Text column.
Name it City and choose Type Single line of text.
Changes to the Flow
Update Trigger Condition
In your flow’s trigger condition, delete the existing condition and paste the following expression.
Update 08.08.2023
Thanks to a community member of my Teams Phone Admin Discord Server I was made aware that the trigger condition can fail if the UPN and the email address case did not match. Below is the corrected trigger condition which includes a
toLower()
expression as well.
/Update
@or(
and(
not(equals(triggerOutputs()?[‘body/User_x0020_Principal_x0020_Name’], ‘Unassigned’)),
not(contains(triggerBody(), ‘UserProfile’))
),
not(equals(toLower(triggerOutputs()?[‘body/User_x0020_Principal_x0020_Name’]), toLower(triggerOutputs()?[‘body/UserProfile’][‘Email’]))),
and(
contains(triggerBody(), ‘UserProfile’),
not(equals(triggerOutputs()?[‘body/User_x0020_Principal_x0020_Name’], ‘Unassigned’)),
not(contains(triggerBody(), ‘TeamsAdminCenter’))
)
)
The expression is now also included in the repository for better readability. The line breaks will be automatically removed once you paste it in Power Automate.
This will also trigger the flow, when a User Profile was added manually through the list (not by the flow) which is what happens when a list user wants to assign a number to a new Teams User.
Add Another Condition
I don’t know why it didn’t occur to me initially but instead of using the runbook to recreate items which have been deleted by the flow, we can just use the flow to do that as well. The reason why we need to delete and re-create is because setting the User Profile to null did not work in Power Automate.
In the first If yes action, we have to add another condition which checks if the Status Value is equal to Unassigned.
The existing Delete item action has to be copied to the clipboard, deleted, and then pasted inside the If yes action of the second condition again.
Add Create Item Action
In the second If yes action, add a Create item action after Delete item with the following dynamic values.
Here’s a new screenshot of the complete flow with collapsed boxes.
Changes to the CountryLookupTable.json
Add More Precise Prefixes
There are some countries which use the same phone prefix. If multiple matches are found, the function always uses the last match which might be wrong in many cases. To solve this, just edit the Json file yourself.
For example, if you have Australian +61 numbers, but don’t have any numbers from Christmas Island or Cocos Islands (which also start with +61), just delete these entries from the Json.
In case you have e.g. numbers from Canada and USA, which both start with +1, you can also extend the prefix to include all digits which are common in all your number ranges from left to right.
Original Entry
{
“Prefix”: “+1”,
“CountryName”: “United States”,
“Country”: “US”
}
Possible Entry Change
{
“Prefix”: “+1206”,
“CountryName”: “United States”,
“Country”: “US”
}
This example assumes that you have Canadian direct routing numbers starting with +1 and have US direct routing numbers which all start with +1206.
If you have more prefixes from the same country in your number ranges, just add more entries with different prefixes but with the same country.
{
“Prefix”: “+1206”,
“CountryName”: “United States”,
“Country”: “US”
},
{
“Prefix”: “+1323”,
“CountryName”: “United States”,
“Country”: “US”
}
Add Voice Routing Policy Names
If you want to use the list for assignment of direct routing numbers, you’ll have to add the name of your voice routing policy for each country to the CountryLookupTable.json file.
{
“Prefix”: “+41”,
“CountryName”: “Switzerland”,
“Country”: “CH”,
“VoiceRoutingPolicy”: “Switzerland”
}
Currently, this solution only supports one voice routing policy per country entry in the CountryLookupTable.json file.
If you have different voice routing policies for various locations within the same country, you’ll have to try to find a way to match them via their prefixes and add multiple entries in the Json.
{
“Prefix”: “+4144”,
“CountryName”: “Switzerland”,
“Country”: “CH”,
“VoiceRoutingPolicy”: “Switzerland-Zurich”
},
{
“Prefix”: “+4131”,
“CountryName”: “Switzerland”,
“Country”: “CH”,
“VoiceRoutingPolicy”: “Switzerland-Bern”
}
Changes to TeamsPhoneNumberOverview.ps1
Update 11.04.2023
Sorry, I totally forgot to mention that you’ll also need to specify the name of the SharePoint User Information List in your language.
# Set your language or add it to the switch statement
# To find the name of your user information list, go to:
# https://
$spoLanguage = “German”
switch ($spoLanguage) {
German {
$localizedUserInformationList = "Benutzerinformationsliste"
}
English {
$localizedUserInformationList = "User Information List"
}
Default {
$localizedUserInformationList = "User Information List"
}
}
I hope that you like the V2 upgrade of my budget number management list.