Opt In or Opt Out Teams Call Queue Agents Remotely

This is an interesting one. First of all, let’s give credits where they’re due. This blog post is inspired by this post by the great…

This is an interesting one. First of all, let’s give credits where they’re due. This blog post is inspired by this post by the great Alexander Holmeset.

The PowerShell code to generate the TOTP is forked from this repo. And the code from this repo is from this gist.

The code to create the interactive sign in window and to get the token is based of this example.


Before we get into it, I should probably add a disclaimer that this is a proof of concept, at best. You should not use it in production as it uses some bad security practices (like sharing a user’s password). For real. Don’t say I didn’t warn you.

What’s this about again?

However, being able to remotely change the opt in status of call queue agents seems to be quite a big ask from several Microsoft Teams admins and customers, so I decided to publish this piece anyway.

Changing opt in status as a user

Can the opt in status of a user be changed as an admin? The answer is both yes and no. You can’t do it in any delegated way if that’s what you’re asking. You will need the user’s credentials.

If you’re a Teams User which is also a call queue agent, there are numerous ways to change your own opt in status for call queues. That is, if your admin allows opting out of queues of course. Let’s go through them.

  • From Teams Desktop Client Settings
  • From a Teams Voice Enabled Channel
  • From Teams Web Client Settings
  • From Teams Certified Desk Phones
  • From Teams Mobile Apps (Android / iOS)
  • From the good old https://aka.ms/cqsettings

I’m an Admin, why can’t I change the damn opt in status?

From a Teams PowerShell session, we can only view the opt in status of a queue’s agents but not change it.

(Get-CsCallQueue -Identity xxxxxxxx-f74b-46bb-9743-xxxxxxxxxxxx).Agents

This will spit out something like this.

ObjectId OptIn
-——- —–
xxxxxxxx-4d28-4246-9c08-xxxxxxxxxxxx True
xxxxxxxx-8bae-419d-a4eb-xxxxxxxxxxxx True
xxxxxxxx-49db-40ee-9d05-xxxxxxxxxxxx False

Every attempt to change the returned PowerShell object failed. Miserably.

How can we at least do it as the user, but remotely?

Similar to Alex’s approach of creating Teams Contacts from CLI we also have to acquire a user token to be able to change the opt in status of a user by CLI.

In his scenario, he’s mainly thinking about new users or even accounts which are used to sign into common area phones which might not be MFA enabled yet. In our case this might look different because we want to be able to alter the opt in state of already working staff.

At first, I also tried to use the AAD Internals PowerShell Module to receive a user token. In this case, however, it turned out to be much more complicated. When a user opens the Calls settings page in Teams, another token is requested from a different issuing service than the one which is used to access all the other features in Teams.

After a long night of googling and binging (yes, I was this desperate) I finally was able to get a working token by modifying the example from the Docs Q&A which is linked in the intro of this article.


Let’s get one thing straight. This approach ONLY works if you know the password of the user and / or the user agrees to share their password or even an MFA secret with you.

I’m thinking of the following scenario: we want to be able to automatically and remotely opt in and opt out agents of call queues based on various criteria such as working schedules etc.

To have at least some kind of security, my script is encrypting the password and the MFA secret before it’s saved to the disk. Once they’re encrypted, they can only be decrypted by the same user account and the same Windows system which was used to encrypt them.

Why do we need to save the password to disk, when it’s an interactive login process anyway? Well, if you want to automate it by any means, we can use SendKeys to emulate a user’s keystrokes. To be completely honest, I only went so far because I wanted to see for myself if I can get it working…

How the script works

The script needs a few parameters to work. We need to provide a user principal name, a user object Id and a call queue Id.

It will then check the current directory for two or three files depending on whether the -MFA switch is used.

You will be prompted to enter the password and the MFA secret if these files do not already exist. The token will be saved to a file once you’ve successfully logged in and acquired one. If the token is expired (happens every few hours or so), you will be prompted for credentials again and the token inside the file will be replaced with the new one. Since we also saved the password to disk, you do not need to enter the password again. The script will decrypt and enter the password by emulating automated keystrokes automatically.

Script modes

The script features a -QueryStatusOnly switch, which will only check if the user is opted in or out of a queue. This will not change anything.

For the sake of simplicity, I’ve already stored the values of $UserId, $CallQueueId and $UserName as default values for the parameters. Of course, you can pass your own values if you call the script yourself.

In this video, you can see how the username and password are automatically entered, which will ultimately get us the token we need to call the API and query the user’s opt in status.

If we run the same thing again, we will already have a valid token.

Now let’s take a look at an MFA enabled user. For this, we use the optional -MFA switch. Besides the password, this will also prompt us for the MFA secret. The script then pulls down the PowerShell Script from GitHub and uses it’s code to do some magic and generate the TOTP from the secret.

Ok — But how do we opt a user out!?

Since we can already see the opt in status of members either in Voice Enabled Channels or via PowerShell this is nothing new. Let’s opt a user out already!


Here’s the script. Please notice that sometimes, activating the AAD Login Window does not always work, so you might have to cancel and run it again. For me, it usually worked the second time I ran the script.

Here’s an example of running the script with parameters.

.\CallQueueOptInOptOutGist.ps1 -UserId "xxxxxxxx-8bae-419d-a4eb-xxxxxxxxxxxx" -UserName "user@domain.com" -CallQueueId "xxxxxxxx-f74b-46bb-9743-xxxxxxxxxxxx" -Action OptOut


Even though this method uses some real dirty tricks it still get’s the job done. Well, kind of, at least. I really hope that Microsoft will add the functionality to allow Queue managers to remotely opt in or opt out their agents in a more practical way. It’s a real pity that not even the new Set-CsUserCallingSettings or the Boss/Admin feature can do this!

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