I believe assigning phone numbers in Microsoft Teams can waste hours for an organization with multiple ranges and locations. What if you could run a PowerShell routine to find the next available number in a number range and at the same time know how many numbers you have left?😱 Now you can, with Get-TeamsNumbers.ps1. Watch my full session from Commsverse21 at the bottom of this post where I demo this script routine.
Fun fact: This script is based on my very popular phone number management routine I crated for Skype for Business Server 2015 called Get-SfBNumbers.ps1.
Disclaimer: This tool is provided as is and you are free to re-use the routines found in the script for your own use, but should not be incorporated in commercial products without author’s consent. If you got any feedback on the script let me know on Twitter.
Get started
- Download the script from GithHub.
- I prefer to open it in PowerShell ISE.
- Add your number ranges and numbers in retention.

- Before you run the script, make sure you Connect-MicrosoftTeams with the latest version.
- You need to be either Teams Communications Administrator, Teams Administrator or Global Admin to run the script.
- The numbers cannot be discovered via GraphAPI, only via the Teams PowerShell module.
- Save the script and run it directly in PowerShell ISE.
- Use examples to see how it can be run as normal ps1 script.
- I prefer to run it in ISE because then you can play with the $Reports variable which I will show you later.
- Keep in mind that it may run for a while if you have thousands of users.
- Already after first run, you should get some good results as shown below.
- The routine works for number used through Calling Plans, Operator Connect and Direct Routing.

What it does
- Uses Get-CsOnlineUser to fetch all users and accounts with a populated LineURI.
- This includes Auto Attendands, Call Queues, Meeting Rooms, Common Area Phones and all other objects with a phone number.
- Creates a complete overview of all numbers and number ranges.
- Exports to csv C:\_Report\PhoneNumbers<Date>.csv so that you can import it to Excel.
- Prints a GridView so that you can do a direct sort and search.
- Prints the summary of all ranges to console.
- What the script does can be controlled in the parameters section at the top of the script.

Find all available numbers for a range
The advantage of running the script in ISE is that you get access to all the variables the script uses. A very useful variable is the $Report variable. Notice that all number ranges has an Identity and has and attribute called AllAvailableNumbers. You can use these to get all numbers and then start assigning them in bulk to users. In Figure 2 we see the number range has Identity set to 2 which we can use like this:

Automatic retention of numbers
There might be numbers you don’t want to give to normal users, but you want to reserve for Auto Attendants, Call Queues or VIP users. The script automatically classifies Gold and Silver numbers based on regex. I have not created this regex myself, it is based on the script routine Paul Valiant provided for me back in 2015. It still works well :) My slide from my talk at Microsoft Ignite 2015 is still valid, when explaining Gold and Silver numbers.

You can reserve your own numbers at the top section of the script, and notice that you can add your own comment and it wont be offered as the next number

Attend my session at Commsverse to learn more
I am speaking at Commsverse 15-16th of September 2021 about phone number management. In that session I will explain this script routine, but go even further and show how it can be used in automation and how to troubleshoot phone numbers for users and explain why country code is key for Microsoft Teams Phone System. Read more here.
Watch my full session from Commsverse21 where I demo this script routine
What you will learn
– How to automate finding available numbers for new users
– How to reserve numbers for future use
– Assign and troubleshoot phone numbers for users.
Hi there,
Not sure if you are aware – this is a great script – but with later versions of the Teams PS module it throws an error:
Microsoft.Teams.ConfigAPI.Cmdlets.internal\Search-CsUser : Filter couldn’t be parsed
At C:\Program Files\WindowsPowerShell\Modules\MicrosoftTeams\3.1.1\net472\custom\PsExt\Get-CsUserSearch.ps1:52 char:13
+ Microsoft.Teams.ConfigAPI.Cmdlets.internal\Search-CsUser …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ({ PageSize = , …olicy = False }:f__AnonymousType74`9) [Search-CsUser_Search], Exception
+ FullyQualifiedErrorId : BadArgument,Microsoft.Teams.ConfigAPI.Cmdlets.Generated.Cmdlets.SearchCsUser_Search
Done!
Determining what range to work with… Done!
Finding all numbers in ranges… Done!
Generating complete internal database of used and available numbers…Cannot index into a null array.
At C:\TeamsDDIs\Get-TeamsNumbersManchester.ps1:601 char:17
+ … if ($number.NumberInRange -eq $FunctionAllUSed[$Count].did){
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
I can’t quite work out where the issue actually is, but as MS seem intent on knobbling get/set cs-user, I would bet related to that?
Found the issue, the current Teams #PowerShell module fails on Get-CsOnlineUser -Filter {LineURI -ne $Null}. Changed to Get-CsOnlineUser | Where-Object {$_.LineUri -ne $Null} Verified OK. Thanks for letting me know. Download the latest version of the script to get the update👍