Microsoft Teams Speed Dial Contacts Provisioning

Have you ever thought that it might be handy to provision a curated speed dial list for your end-users to get started with when they are onboarded to Microsoft Teams? Or another scenario, don’t have to manually add speed dial contacts on your common area phones for all the commonly used numbers like the reception, helpdesk, cafeteria, travel booking etc….

The issue is that there is no official way to administrate this from Teams Admin Center, PowerShell, or Graph API. But what about unofficial ways? Yes, there is! So what do we need to be able to do this?

If you are logged in to the Teams web client, and open dev mode (F12) of your browser you are able to see what is happening in the background. All actions in the web client are if you look closely, API calls!

Here we can see the request URL, method, and body/payload. Take a look closely at the request URL, as this will be different from where your tenant is hosed. My is hosted in Europe, and therefore you see EMEA in the URL. An example from the US is “amer-03”. We also see a token we can use to authenticate with. The issue though, this is only valid for this single user and expires in about an hour. This does not scale when doing a lot of provisioning and adding many contacts to the speed dial list. So how do we get the user’s token in a more automatic way? I came over a genius PowerShell module from Dr. Nestori Syynimaa that’s called AADInternals. You can install it by running “Install-Module AADInternals” in PowerShell. This module has a cmdlet called Get-AADIntAccessTokenForTeams, which contains a parameter called -Credentials. This parameter accepts username/password. Now we can before setting MFA or you could maybe have a conditional access rule from where you do the provisioning, then we can use basic-auth to authenticate with the user’s first-time password. You could even reset the first-time password after using it. By having a CSV file containing the headers password and UPN, we can feed that into a script I have created. The script loops through the users and adds the speed dial contacts we define in the script.

#Import the Azure AD Internals module.
Import-Module AADInternals
#Users to process
$Users = import-csv c:\temp\users.csv
foreach($user in $users){
$password = ConvertTo-SecureString $user.password -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList ($($user.upn), $password)
#Get the Teams token for user curently beeing processed.
$token = Get-AADIntAccessTokenForTeams -Credentials $cred
#URL to buddylists.
#The url is difrent depending on theregion your tenant is hosted in. This URL is from Europe, therefore EMEA in the URL.
#Example from the US is amer-03. See blogpost above for detqils on fidning this url.
$URL = "https://teams.microsoft.com/api/mt/emea/beta/contacts/buddylist/"
$header = @{Authorization = "Bearer $token"}
#Get id of the Speed Dial list.
$ListID = ((Invoke-RestMethod -Uri $URL -Method GET -Headers $header -ContentType "application/json").value | Where-Object{$_.displayname -like "Favorites"}).id
#URL for the Speed Dial list.
$URL = "https://teams.microsoft.com/api/mt/emea/beta/contacts/buddylist/$ListID/managebuddies?migrationRequested=true&federatedContactsSupported=true"
#Number to Add. MRI is the phonenumber of the contact, same as phone property. If contact to be added is a internal user, add the request like this:
#{"add":[{"mri":"8:orgid:18c4a91b-4fa9-4fbd-ab1c-b39465d81baf","displayName":"Admin","isFederated":false,"email":"[email protected]"}]}
#To find the org id, run this command and look for it in a users buddies list:
#(Invoke-RestMethod -Uri $URL -Method GET -Headers $header -ContentType "application/json").value
$payload1 = @'
{"add":[{"mri":"4:123456789","displayName":"Helpdesk","phone":"123456789","companyName":"Contoso","jobTitle":""}]}
'@
#Add phonenumber 1 to Speed Dial list.
Invoke-RestMethod -Uri $URL -Body $payload1 -Method POST -Headers $header -ContentType "application/json"
$payload2 = @'
{"add":[{"mri":"4:987654321","displayName":"Reception","phone":"987654321","companyName":"Contoso","jobTitle":""}]}
'@
#Add phonenumber 2 to Speed Dial list.
Invoke-RestMethod -Uri $URL -Body $payload2 -Method POST -Headers $header -ContentType "application/json"
}

16 thoughts on “Microsoft Teams Speed Dial Contacts Provisioning

      • Havent tested in a while, going to do it soon as I’m prepping a speaker session on automating things like this.
        The CSV contains .UPN, so create a csv with the UPN header and put users upn under.

        Like

      • Also the password in the CSV is that what we get from Get-AADIntAccessTokenForTeams or the UPN’s password?
        I click run and I get prompted to login to my microsoft account, then im getting the following error

        “New-Object : Exception calling “.ctor” with “2” argument(s): “Cannot process argument because the value of argument “userName” is not valid. Change the value of the “userName” argument and
        run the operation again.””

        Like

    • Have you read the comment in the script that mentions this?
      #Number to Add. MRI is the phonenumber of the contact, same as phone property. If contact to be added is a internal user, add the request like this:
      #{“add”:[{“mri”:”8:orgid:18c4a91b-4fa9-4fbd-ab1c-b39465d81baf”,”displayName”:”Admin”,”isFederated”:false,”email”:”[email protected]”}]}
      #To find the org id, run this command and look for it in a users buddies list:
      #(Invoke-RestMethod -Uri $URL -Method GET -Headers $header -ContentType “application/json”).value

      Like

      • What $URL variable should this be run against?
        I’ve tried the last one but get a 404 error.
        The first one spits out some info but the id format is different and seems to be related to the CAP user rather than an org.

        Thanks,
        Rob

        Like

      • Both, the first one gets the ID of the Favorites list, the second is a url directly to the favorite list.
        I probably coudl named the variable a little better.
        I see the URLs contain the word EMEA, so could be you need to change that to NA or something if your located in America.
        You can see what the URL is if you open the dev mode /f12 when browsing the favourite list in Teams through your browser.

        Like

  1. Actually ignore my previous post above this solution is amazing! I missed the Buddies list area and was inputting our tenant ID for users for reference the format is simply the GUID of each internal contact you want to add! easy!

    Like

  2. Just for checking, i must know everyone’s password to add it to their speeddial list? I want to push it to 300+ users to add 5 default speeddialnumbers.

    Like

  3. Hi Alexander ~ just to query this further lets say we want to remove a contact from a bunch of Teams accounts how would we do this – I tried to replace the -Method “POST” to DELETE but this fails be awesome to get an example of a bulk contact remove.

    I’ll just say I’m getting ready to prep 40 + Teams phones which would equal to more than 1600 contacts I would have added manually logging into each phone account, absolute life saver.

    Liked by 1 person

    • Tested now, it sends a POST request to the same url with this payload when you delete a favourite:
      {“delete”:[{“mri”:”8:orgid:1f6ba91f-8307-2342-b307-asdf234sdf”,”displayName”:”Admin”,”email”:”[email protected]”}]}

      Like

Leave a comment