How to Script Call Forwarding in Microsoft Teams Voice

Have you ever had to deal with call forwarding for a former employee? I often find there are two reasons I will write a PowerShell script to solve a problem. The first is for anything repeatable that I perform regularly. The other is anything that is repeatable, that I do so rarely, that I forget the process. Whenever I have to process an employee leaving the company, my script sends an email to the manager asking if they want calls forwarded, and 9 times out of 10, they don’t. However, on those rare occasions they do, I have to find the document and walk through the steps to make it happen again.

So that got me thinking – why not just script the entire thing. I can make it so that all I need is a username, and then it’ll go do it all for me. Easy Peezy. Well, that got me scripting and here we are! I noticed a few things and I will break it down as I go along, but since I was successful, I realized I wanted to share it with others who may be in the same situation. Keep reading to find out what it is all about, and maybe it’ll save us both some time. 🙂


Please understand that the content herein is for informational purposes only. This existence and contents shall not create an obligation, liability or suggest a consultancy relationship. In further, such shall be considered as is without any express or implied warranties including, but not limited to express and implied warranties of merchantability, fitness for a particular purpose and non-infringement. There is no commitment about the content within the services that the specific functions of the services or its reliability, applicability or ability to meet your needs, whether unique or standard. Please be sure to test this process fully before deploying in ANY production capacity, and ensure you understand that you are doing so at your own risk.

Table of Contents

When to Use this Script
Prerequisites for the Script
Create Call Forwarding Account
Remove Call Forwarding Account

When to Use this Script

I wrote this script as a way for me to not have to remember all the steps for creating a call forwarding account. At my job, I only use this script when an employee leaves the company, and the manager has requested call forwarding of the old number. That means I wrote this to support this use case.

It shouldn’t be hard to modify this to fit similar use cases, as the steps are essentially the same.

Don’t take any script you find on the internet and just run it without understanding what it does, and what settings it has configured!

That means though that you have to pay attention to some of the settings. Make sure you read the original docs and make sure it has the settings you want.

Prerequisites for the Call Forwarding Script

As usual, there are some things to understand and consider for this script. These are just some considerations that I want anyone who finds this to be aware of. If you find something isn’t working, check back to this section and make sure you understand the assumptions I took with these scripts.

The script needs Microsoft.Graph.Users and MicrosoftTeams modules

Since this script is looking up information about the former employee account, as well as assigning a license to the resource account it will be creating in the process. Everything else happens in Teams PowerShell. This means that you’re going to have to sign in twice. I know, it’s a pain, but I haven’t looked into whether or not a token approach would work where we could save the connection variables in a key vault, and sign in once to access them. This is blog is about version 1.0.0 of the script, so make sure you check the GitHub page for more info. I may fix this later and make this paragraph a waste of space :).

However, I did include a function that I use that will check for updates on the module, and ensure it is imported properly. It helps with keeping the modules up to date and ensuring it works correctly.

Assumptions about the UPN & DisplayName

I chose something that I thought would be generic and general enough that it might work for anyone. Review this section before you commit to using any part of the script. If you have your own preferences, you can change it to match what you need. Part of this includes you needing to specify the UPN domain for the employee. If you have multiple domains, you’ll need to work out a way to determine the correct one, or just choose one for these types of resource account to make it the same. It looks like this:

[String]$callQueueDN = "CallForwarding_" + $formerEmployeeDetails.GivenName + "_" + $formerEmployeeDetails.Surname

[String]$callQueueUPN = $callQueueDN + $emailDomain

Write-Host "DisplayName is:" $callQueueDN -ForegroundColor Cyan
Write-Host "UserPrincipalName is:" $callQueueUPN -ForegroundColor Cyan

If you like it like this, cool. Otherwise, this will be an area for you to review and make some decisions about.

Teams Voice Calling Policy & Resource Licensing

This script will need to ensure that the resource account it is creating has access to a voice calling policy. This is required if you want this call forwarding account to be able to receive calls from external callers. Line 177 of the v1 script contains the switch regarding DirectRouting voice calling, and having access to a voice policy is required for this to work. Be sure to specify one here. If you need to switch between more than one, then you’ll have to modify that part yourself.

Additionally, this account will need to have a free Microsoft Teams Phone Resource Account license. Here are the Microsoft listed requirements for a Direct Routing Call Queue to work. (Which is what our call forwarding account really is).

Where does the phone number come from?

I made the assumption that the original account for the former employee would have the work number saved to their user profile as an account attribute. This script pulls it from the former employee’s account to add it there. If you would like something different, or the ability to input your own number, it’s an easy switch.

Just create a new variable with a ‘Read-Host’ cmdlet and request input for the number. Ensure you include some formatting or data validation to make sure it matches the number format for your region. As I am located in the U.S., here is how the current script validates the number:

#specify Former employee business phone then convert it to the 10 digit number
[String]$formerEmployeePhone = $formerEmployeeDetails.BusinessPhones
$formerEmployeePhone = "+1" + $formerEmployeePhone.Replace("-", "")

Timeouts are a Real Thing with this script

As you read through the script itself, you’ll notice several ‘Start-Sleep’ cmdlets in the script. You may have to adjust these times for your environment if you find it is failing with errors like “Account is missing appropriate license” or other general errors like that. What I found is that the script will run too quickly at times. When it assigns a license, the next step in the script will occur before it has registered that the license was assigned, causing it to fail.

I added these ‘Start-Sleep’ cmdlets in there to slow it down and give Microsoft’s systems times to register some of these changes. Feel free to extend the times if you are seeing issues; I tried to get these ones down to the lowest time I could still get it to work. You may have to adjust accordingly.

Create Call Forwarding Account

Now we get to the script portion. I’m going to provide a link to full script in GitHub, as that will ensure you get the full version if you need it. It’s rather long, so rather than make this post a million times longer than it needs to, you can go to get it there.

Create-CallForwardingAccount.ps1 · SeeSmitty/Powershell (

One thing I will note is that I’m a fan of nesting functions in a way that allows me to create natural loops. This function in particular is one I use frequently for interactive scripts that may require it run through more than once. This allows me to run through multiple times without having to sign in each time.

function Test-Finished {
    $finished = Read-Host "Do Need to forward any more phone numbers? Y or N" 
    IF ($finished -like "Y") {
    ELSE {
        Write-Host "Disconnecting from Teams Powershell" -ForegroundColor Blue

In this function, it will ask if you want to run through again and will call the main function if you enter ‘Y’. If you enter anything else, it will exit. I first used this on my script for processing employee accounts for termination and have used it many times since. All that is needed is to drop this in at the beginning, call the main part of the script as a function, and insert this function at the end of the main function.

call forwarding test-finished loop

Remove Call Forwarding Account

I personally have found that if you are going to make a change programmatically through a script, it’s good to have another script that will reverse the changes. In this case, this script is designed to be used during the cleanup of these call forwarding accounts when they are no longer needed.

Remove-CallForwardingAccount.ps1 · SeeSmitty/Powershell (

Same as the script above, it leverages the ‘Test-Finished’ Function to allow you to loop through multiple accounts if needed. This script does not include any time outs as there aren’t time concerns here. I also left it open ended enough that it should technically work with any direct routing call queue account, though I would test it to make sure, as I didn’t test it with anything other than the accounts I created with the original script.


Well, there you have it. Another call PowerShell script to make my life easier. I hope that if you use it, it will make your life your life easier too. It isn’t very often that I have to do, but the script helps make sure I do it the same every time. I’m hoping to find more things that can be done in Teams PowerShell to make repetitive tasks even easier, and if I find something useful, I will definitely pass it along.

Like I mentioned above, this could be rewritten to incorporate additional features or even be used for creating a regular call queue or auto-attendant account. It just takes some practice and a good understanding of what you are trying to accomplish. Exercises like this are a great way to sharpen your PowerShell skills and make your work more efficient.

I hope you found some value in this post. As always, let me know what you think and if this worked for you. Hit me up on the ‘site formerly known as Twitter’ @SeeSmittyIT to let me know what you thought of this post. Or if you are avoiding the X-bird site, I’m also posted up on Mastodon @[email protected]. Thanks for reading!


Curtis Smith works in IT with a primary focus on Mobile Device Management, M365 Apps, and Azure AD. He has certifications from CompTIA and Microsoft, and writes as a hobby.

View all posts by Smitty →