Microsoft Teams Self-Service Auto Attendants (Without Premium Connectors)

Hi everybody.

Hi everybody.

Every week more customers are moving to a Microsoft Teams based telephone solution. With that, customers also want more flexibility in managing their auto attendants and call queues. Many of my customers also want their team managers or members to be able to change certain settings of their auto attendants and call queues by themselves. The problem is that all these changes currently require an administrator role for Microsoft Teams. Even though most of the things can be configured in an easy-to-understand GUI in TAC (Teams Admin Center), a user could still potentially break stuff if they’re not 100% aware of what they’re doing. Furthermore, some changes are not as straight forward as they could be. As an example: There’s no switch to manually “open” or “close” an auto attendant since the default and after hours call flows are linked to a weekly recurrent schedule.

While Teams is certainly lacking in this area, there’s no denying that the advantages of a Teams Phone solution far outweigh the disadvantages. Microsoft also offers numerous awesome tools to cope with this very issue by allowing us to automate nearly everything. And that ladies and gentlemen, is one of the most awesome and fascinating things to me!

Let’s dive in.

Credits and Inspiration

This article is inspired by the work of two other fantastic community members and bloggers: MVPs Luca Vitali and Alexander Holmeset. Both have written articles on self-service auto attendant and call queue management using Microsoft automation tools.

I don’t think that I would have been able to come up with a solution of my own without their exceptional blog posts. At least not this fast.

To fully understand this blog post, please make sure you read their publications first.

The Ever-Present Licensing Dilemma

Alex’s solution uses a combination of Microsoft Forms, Power Automate and an Azure Runbook. However, to use an HTTP request either as a trigger or an action in a Flow, you need a premium license for Power Automate. I can understand that requirement from Microsofts business perspective, but such a basic feature shouldn’t require a premium license. Since I don’t have any premium licenses at the moment anyway, I looked for other ways.

Luca’s method uses an outgoing webhook in Teams. It’s a great way to invoke a Bot or script directly from Teams. There’s no need to install any apps and you don’t even need to open a website or an App to change some settings on an auto attendant or a call queue.

The problem is that Teams can’t really handle the response sent back by the Runbook. This means that a user will always get an error message, even if the Runbook was triggered successfully. This might be very confusing for end users and just doesn’t look nice.

Azure Functions

Instead of calling the Azure Runbook directly from the outgoing Teams webhook, I tried to call an Azure Function first. Azure Functions are serverless web services which can execute code once they’re called by a trigger. Much like the Runbook in Luca’s blog, my Function is also triggered by a web request by the outgoing webhook.

From what I could find on the internet, the webhook expects a reply in JSON format containing "type": "message" and "text": "some message". I didn’t find a way to customize the response of the Runbook but it’s easy with an Azure Function. I will show you how later in this post.

Override Auto Attendant Business Hours

Let’s start with the example from the introduction of this article. Let’s assume that we have a small company where the users who answer the phone need to be able to manually open or close an auto attendant.

Note: This part assumes that you already have the following components set up. If you don’t have them already, please refer to the external websites linked in this post in order to set them up.

  • A Microsoft Teams Auto Attendant
  • A Microsoft Teams Team
  • An Azure Automation Account
  • An Azure Function App

Also, it might be a little confusing at first, because we’re creating each component from the opposite direction in which they are chained together. This way, we only have to touch each component once though.

Prepare the Auto Attendant

The easiest way I could think of is to create a special holiday for this scenario. I called it “Manual Override”. The reason I did it in this way is that the actual business hours don’t get changed. If there were multiple hours per day or different hours each day, it would be quite an effort to revert all the changes once the manual override should no longer be active.

To start configuring it, head over to TAC and switch to Voice\Holidays. Now create a new holiday and set the dates to anywhere in the past.

Now go to your auto attendant and link the holiday schedule.

Make sure you click “Submit” after you have saved the holiday. Otherwise, the auto attendant won’t be updated.

That’s all you need to do in order to prepare your auto attendant for this self-service solution.

Later in this post, I will provide an example script which will log into Teams PowerShell and change the dates of the “Manual Override” Holiday. When an auto attendant should be open, the holiday schedule will be set to past dates. Past dates won’t have any effect on the auto attendants routing decisions and therefore the auto attendant’s business hours schedule will be active.

When the auto attendant should be manually closed before the configured business hours end, the holiday schedule will be updated to start at today’s date and end at the start of tomorrow’s date.

This means that it’s possible to close the attendant if the office closes before the regular business hours on that day. If it’s not manually opened again, it will resume to normal business hours as soon as the next day starts.

While it’s possible to close or open the attendant during business hours (e.g., extended lunch break), it’s not possible to extend the configured business hours using the method used by the provided example script.

Prepare your Teams Channel

First, we will need an incoming webhook in a channel to receive messages in Teams. We will use this webhook to receive message cards sent by an Azure Function or by a Runbook.

Go to your desired channel and click on “Connectors”.

Now add an Incoming Webhook.

After clicking “Add” the second time, the window will close. Open “Connectors” again and click “Configure”.

Give your Connector a name and upload an Icon (if you want to) and then click “Create”.

After you have created the webhook, its URL for your connector will be shown. Copy the URL and save it for later.

Create an Azure Runbook

Let’s create the Runbook which will change the settings on our auto attendant. This script will log into Teams PowerShell and manipulate the holiday schedule called “Manual Override”. Because only the schedule is changed, there’s not even a need to specify an auto attendant explicitly. You could even add the “Manual Override” holiday schedule to all your auto attendants (which are in the same time zone) and open or close them all at once.

If you don’t have an Azure Automation Account already, you can read how to create one here or in Luca Vitali’s article under chapter 1.

From within your Automation Account, click “Create a runbook”.

Name your Runbook and choose the following settings.

Paste the following code in the editing pane. Make sure to also insert your webhook URL on line 7 between the double quotes.

For this to work, we obviously need credentials which have the Teams Administrator Role. These are imported on line 17. If you need guidance on how to add them to your automation account, please refer to Luca Vitali’s article under chapter 3.

Once you have pasted the script and inserted your webhook URL, save and publish the Runbook.

Next, we will add a webhook to the Runbook as well.

You can also read in detail how to configure the webhook on Luca’s blog.

Copy the webhook URL of the Runbook and save it for later.

Create the Refresh Card Function

Next, we will create a new Azure Function. This Function will be called when a user clicks on the “Submit Config” button on a card which was sent by the first Function (which will be created later). Besides triggering the Runbook, it will also update the card which was sent by the first Function.

For this, go to and select Function App. If you don’t have an Azure Function App already, you need to create one first.

Once the Function App has been deployed you can create the function.

Choose “HTTP trigger” as the template, name your function and leave the Authorization level at “Function”.

In the editing pane, paste the following code. Insert the URL from the Runbook webhook on line 8 in between the double quotes.

Save the Function and copy the Function URL by clicking “Get function URL”.

Note: the URL will include a key at the end. Because we’ve chosen “Function” as the Authorization level when we created the function, it will only be possible to call the function by including the key in the URL.

Create the Request Card Function

Now we create the second Function. This is the Function which will be called from the outgoing webhook in Teams and sends the “Config Change” Card to the channel. It’s actually the Function which will be called first.

Paste the following code in the Azure Portal. Again, make sure to populate the variables on line 6 and 7. $uri represents the URL of your Teams incoming webhook and $refreshURI represents the URL of your “RefreshCard” Azure Function.

Don’t forget to save the Function.

Create an Outgoing Webhook in Teams

Finally, we need to create an outgoing webhook in Teams. Users will be able to mention the webhook in the channel. Once the “Bot” is mentioned, a web request will be made to the “RequestCard” Function.

To set it up, go to your Team’s settings and switch to the Apps tab. From there, click on “Create an outgoing webhook” on the bottom right.

Name your outgoing webhook and paste the Function URL of the “RequestCard” Function in the “Callback URL” field before you click “Create”.

You can ignore the security token which will be shown after the webhook has been created. If you like you can still save it somewhere safe.

See it in Action

Now that we have everything set up, any member of the channel can request a card to manually open or close an auto attendant.

Here’s the “Manual Override” Holiday Schedule before the config change has been submitted. Today, as of writing this article it’s the 17th of May 2022. The holiday schedule is set to 15.05.2022–16.052022 so it’s non-effective.

And here’s the chronological process:

A user requests a card by mentioning the outgoing webhook.

This will call our first “RequestCard” Azure Function. The Function will then reply with a message that the config change request has been received.

At the same time, the Function will also send a new card to the channel which contains the options to open or close the auto attendant and a “Submit Config” button.

Note: Unfortunately, it’s not possible to send the card as an answer to the invocation of the webhook. This answer can only support text.

Once a user has chosen an option and submitted the config change, the “RefreshCard” Function will be called.

This Function will update the “Config Change Request” card, trigger the Runbook, and lets the user know that their config change request has been received.

Once the Runbook has finished, it will also post a new card to the channel, informing the user that the auto attendant has been opened or closed.

If we check the holiday schedule again, we can see that it has been updated and set from 17.05.2022 to 18.05.2022. This means that any calls which are received by the auto attendant today, go into the holiday call handling and not into the default call flow. Thus, this auto attendant is now closed.


This is a great proof of concept on how to build a self-service auto attendant or call queue solution which doesn’t require any Premium Licenses for Power Automate or Power Apps. Of course, Azure Functions and Runbooks are not free either but I’m almost certain that it will still be much cheaper at scale.

From my point of view this is a great showcase of what one can achieve with Azure Automation. I also like that everything stays in Teams from an end user perspective. There’s no need to open any other website or App and even Teams mobile App experiences are supported.

This article only scatched the surface by covering only one example of endless possible scenarios. For example, it’s also possible to write an Azure Function in a way in which it can receive parameters from the outgoing webhook. Instead of just mentioning the webhook by “@RequestCard” we could also mention it like this: “@RequestCard CQ ” or “@RequestCard AA” and then respond with a different config change card, one for each type of voice app or even different change scenarios. Or we could even do stuff like check the username from which the outgoing webhook was invoked and do a security filtering on it by only allowing certain users to submit changes to auto attendants or call queues. The sky really is the limit.

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Hosted on GitHub Pages
Built with Hugo
Theme Stack designed by Jimmy