Skip to content

Automate Teams voice assignment for users (1:2)

In order to manage voice and phone number assignments in Microsoft Teams, you need at least Teams Communications Administrator role. This role does however have more privileges than most organizations want to assign to their first line staff. This blog post will cover a way for first line to automate voice activation of users with the granularity necessary to cover several technologies such as Direct Routing and Operator Connect.

The main idea is to let first line operators use the tools they have access to when managing users without the demand of acquiring extra privileges.

By adding the Teams phone number in E.164 format to the users telephoneNumber field in AD/AAD and assigning the user as member of a defined security group, I have enough information to automate the Teams voice assignment for the user. This could also include license assignment through the group membership.

There will be a follow up on this post showing how I do reporting to PowerBI as part of the same routine.

Azure Automation Account

To run the automation, I am using an Azure Automation account where I add the MicrosoftTeams module from the gallery.

The MicrosoftTeams module was loaded because the operations to enable enterprise voice and setting routing policies can’t be performed through Graph API at this point in time. Since the CS commands in the Microsoft Teams module don’t support app tokens or certs for authentication, I need to use a resource account. This account will be secured accordingly before the actual roles “Teams Communications Administrator” and “Directory Readers” are assigned.

The credentials are defined in the automation account for secure use in the coming runbook.


Now it’s time to create the Runbook to hold the PowerShell routine.

Once the Runbook is created, we can do an Edit to get into the location where the PowerShell script can be added.


The PowerShell script can be found on my GitHub area. The first part of the script relates to getting the credentials and connecting to Microsoft Teams.

# Get the credential from Automation  
$credential = Get-AutomationPSCredential -Name 'TeamsPhoneEnablingScriptUser'
$userName = $credential.UserName
$securePassword = $credential.Password

$psCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $userName, $securePassword
# Connect to Microsoft Teams  
Connect-MicrosoftTeams -Credential $psCredential
Connect-AzureAd -Credential $psCredential

The next part of the script is defining the groups and variables used pr. location. In this example the locations in Norway and Vietnam are set up with direct routing, while the location in Singapore is configured for operator connect.

#Settings for Norway
$SecGroupObjectIdRTNO = @(
    '12345678-abcd-abcd-abcd-123456789abc'  # Display Name: Microsoft 365 - Phone System - Bergen
    '12345678-efgh-abcd-abcd-123456789abc'  # Display Name: Microsoft 365 - Phone System - Trondheim
$RoutingPolicyNO = "routingpolicy-no"
$CallingPolicyNO = "CallingPolicy_NO_DVM"
# Settings for Vietnam
$SecGroupObjectIdRTVN = @(
    '12345678-ijkl-abcd-abcd-123456789abc'  # Display Name: Microsoft 365 - Phone System - VungTau
    '12345678-mnop-abcd-abcd-123456789abc'  # Display Name: Microsoft 365 - Phone System - Hanoi
$RoutingPolicyVN = "routingpolicy-vn"
# Settings for Singapore
$SecGroupObjectIdRTSingapore = @(
    '12345678-qrst-abcd-abcd-123456789abc'  # Display Name: Microsoft 365 - Phone System - Jurong

Some variables and hash tables are now defined to support the iteration of groups and users.

# Declare Hash table for users and variables
    $UsersToEnable = @{}
    $ExistintTeamsUsersWithoutGroupActivation = @()
    $Number = 1
    $ExistingUsers = (Get-CsOnlineUser | ? {$_.EnterpriseVoiceEnabled}).UserPrincipalName

The following section in the script will now iterate the defined groups pr. location. This is how the Norwegian groups will be handled. The users found in the groups are all added to the hashtable declared for users to be enabled.

# Iterating all groups in Norway
ForEach ($GroupID in $SecGroupObjectIdRTNO) {
        # Reading information from Azure AD and Microsoft Teams
        $Group = Get-AzureADGroup -ObjectId $GroupID
        $Users = Get-AzureADGroupMember -ObjectId $Group.ObjectId -Top 10000
        # Add all users from $Group to $UsersToEnable
        $Users | ForEach-Object {
            $UserPrincipalName = $_.UserPrincipalName
            $DisplayName = $_.DisplayName
            $Country = "Norway"
            $UserPrincipalNameAndCountry = $UserPrincipalName+";"+$Country

After running through all locations, I have all users which should have Teams Voice activated in the hash table. I will now cycle through the hash table with existing voice activated users and remove these. This should leave only the users that needs configuration.

# Iterate ExistingUsers and remove existing active teams phone system users from $UsersToEnable hash
$ExistingUsers | ForEach-Object {
    $user = $_
    if ($UsersToEnable.ContainsKey($user)) {
    else {
        # Creates a table of Existing users in order to find voice enabled users not in the groups
        $ExistintTeamsUsersWithoutGroupActivation += $user

With a table containing users to be configured, I will do the actual configuration of the user. The hash table with users to enable will be iterated and each user will have its configuration set based on the associated country. Usage location will be set in order to have the correct number handling in the Teams client.

# Iterate UsersToEnable hash table and activate new users for Teams phone system
$UsersToEnable.GetEnumerator() | ForEach-Object {
    $useridentity = '{0}' -f $_.key
    $country = ('{0}' -f $_.value).split(";")[1]
    if ($country -eq "Norway") {
        $UsageLocation = "NO"
        $telephonenumber = ((Get-AzureADUser -Filter "UserPrincipalName eq '$useridentity'").telephoneNumber).replace(' ', '')
        Set-AzureADUser -ObjectId $useridentity -UsageLocation $UsageLocation
        Set-CsPhoneNumberAssignment -identity $useridentity -PhoneNumber $telephoneNumber -PhoneNumberType DirectRouting
        Set-CsPhoneNumberAssignment -identity $useridentity -EnterpriseVoiceEnabled $true
        Grant-CsOnlineVoiceRoutingPolicy -Identity $useridentity -PolicyName $RoutingPolicyNO
        Grant-CsTeamsCallingPolicy -Identity $useridentity -PolicyName $CallingPolicyNO
    elseif ($country -eq "Vietnam") {
        $UsageLocation = "VN"
        $telephonenumber = ((Get-AzureADUser -Filter "UserPrincipalName eq '$useridentity'").telephoneNumber).replace(' ', '')
        Set-AzureADUser -ObjectId $useridentity -UsageLocation $UsageLocation
        Set-CsPhoneNumberAssignment -identity $useridentity -PhoneNumber $telephoneNumber -PhoneNumberType DirectRouting
        Set-CsPhoneNumberAssignment -identity $useridentity -EnterpriseVoiceEnabled $true
        Grant-CsOnlineVoiceRoutingPolicy -Identity $useridentity -PolicyName $RoutingPolicyVN
        Grant-CsTeamsCallingPolicy -Identity $useridentity -PolicyName $CallingPolicyNO
    elseif ($country -eq "Singapore") {
        $UsageLocation = "SG"
        $telephonenumber = ((Get-AzureADUser -Filter "UserPrincipalName eq '$useridentity'").telephoneNumber).replace(' ', '')
        Set-AzureADUser -ObjectId $useridentity -UsageLocation $UsageLocation
        Set-CsPhoneNumberAssignment -Identity $useridentity -PhoneNumber $telephonenumber -PhoneNumberType OperatorConnect

The script in the PowerShell Runbook is now ready to be published.

The Runbook can now be linked to a schedule to have the routine run as often as necessary.

The end result

This routine will now enable Teams telephony on the users automatically. When creating new users, a free number should be acquired in the number plan and assigned to users telephoneNumber attribute together with the corresponding group assignment.

If the user later on is removed from the group, the license for Teams Telephony will be removed which in turn removes the configuration and functionality.

This is working ok in the environments where it has been implemented. Feel free to imitate these thoughts and routines in your environment at your own risk.

Published inAutomationAzureMicrosoft 365PowershellTeams

One Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

The reCAPTCHA verification period has expired. Please reload the page.