Teams – Outbound Calling policies for Audio conferencing and user PSTN calls

This one took me a while to understand what is happening and how to resolve it. The Teams audio and calling functions are not something I deal with on a daily basis but it was good learning experience.

One of the Teams tenant I manage kept running out of pooled minutes really quickly. I wasn’t actively monitoring the usage but I did notice the emails from Microsoft that were warning us that the minutes are almost used up and users won’t be able to use the PSTN conferencing services for the rest of the calendar month unless more communication credits were added.

What is the deal here exactly?

Each user assigned an ‘Audio Conferencing’ license which provides a dial-in phone number when they schedule a meeting, is given 60 minutes per month of pooled minutes that can be used for inbound or outbound PSTN dialing for meetings. This ‘Audio Conferencing’ license is only needed for users scheduling the meetings. Meeting attendees who dial in don’t need this license. Depending on how you have deployed Teams in your organization, whether as a full blown phone system or used specifically for meetings, this pooling minutes is only for meetings and is separate from other dial plans(domestic or international) that you may have.

Users with ‘Audio Conferencing’ license are given a default invite number in the same country as what their O365 account’s usage location is set to.

In a example scenario where an organization has 100 users that have ‘Audio Conferencing’ license, 100 X 60 minutes and you get 6000 pooled minutes for your tenant to PSTN dial-in and dial-out of meetings. One the 6000 minutes are consumed users will no longer be able to dial-in or dial-out of the meetings using the number provided in the meetings.

Note: Users can still participate using the Teams meeting clients(Desktop or phone app).

Latest Update: Microsoft is extending the audio conferencing capabilities by removing the cost involved to the SKU.

Why users use the PSTN dial-in numbers for Teams online meetings

Personally I love the Teams app, both on the desktop and the phone. And I can tell Microsoft is adding new functionalities over time. I also like it how I can use the phone app for voice and also launch Teams on my desktop and I get an option to ‘add the device’ to the ongoing call on the phone. This way, I can present using the desktop.

Using this VOIP features via the Teams app(phone or desktop) doesn’t incur additional cost to the organization but there are some scenarios where including a dial-in number to meetings makes sense,

  • Poor or limited internet connectivity
  • Users moving in and out of limited data coverage where voice quality may be better
  • Users having issues with VOIP on their PC and phone app
  • Users are used to the old ways
    • Users lack training in Teams clients(Desktop or phone app)

Determine pooled minutes and usage

To check your PSTN pooled minutes, you can run usage reports in the Teams admin center,

Teams Admin Center –> Analytics & reports –> Usage reports

  • PSTN minute and SMS(preview) pools
  • PSTN and SMS(preview) usage
PSTN minute and SMS(preview) pools
PSTN and SMS(preview) usage

What is burning precious PSTN minutes

Based on the reports and further reading I realized the ‘Call me‘ feature in Teams is apparently a well known and heavily used user loved feature which seems to be a behavior that followed users from using other conferencing tools.

Users can join a meeting and have the meeting call and join them or dial in manually to the meeting.

Screen shot of the Phone audio option.
Screen shot of the Call me option on the Use phone for audio screen.

As I mentioned earlier, the users using this feature didn’t realize they can use their computer audio or the Teams app on their phone. There were others who thought this was a handy feature and more convenient. Well, what they didn’t know or didn’t care is, the outbound calls were eating those pooled PSTN minutes and affecting the entire organization for users who would really need it.

What is the fix?

This can be fixed by educating the users about the VOIP options, using the computer or phone Teams app. And yes, obviously by putting policies in the tenant.

In the Teams admin center, the dial-out from meeting can be controlled on a per-user basis

  1. In the left navigation, select Users, and then select the display name of the user from the list of available users
  2. Under Audio Conferencing, select Edit
  3. Under Dial-out from meetings, select the dial-out restriction option you desire
  4. Select Save

This may resolve the issue by assigning ‘Don’t allow’ policy for the users who are the heavy hitters of the ‘Call me’ feature but more users might start using this feature and you’ll have to constantly monitor the usage.

To prevent this, you can set a tenant level policy based on your requirements and organizational needs. Once the global policy is in place, you can assign a policy on a per-user level.

The following table provides an overview of each policy.

PowerShell cmdlet Description
DialoutCPCandPSTNInternationalUser in the conference can dial out to international and domestic numbers, and this user can also make outbound calls to international and domestic numbers.
DialoutCPCDomesticPSTNInternationalUser in the conference can only dial out to domestic numbers, and this user can make outbound calls to international and domestic numbers.
DialoutCPCDisabledPSTNInternationalUser in the conference can’t dial out. This user can make outbound calls to international and domestic numbers.
DialoutCPCInternationalPSTNDomesticUser in the conference can dial out to international and domestic numbers, and this user can only make outbound calls to domestic PSTN number.
DialoutCPCInternationalPSTNDisabledUser in the conference can dial out to international and domestic numbers, and this user cannot make any outbound calls to PSTN number besides emergency numbers.
DialoutCPCandPSTNDomesticUser in the conference can only dial out to domestic numbers, and this user can only make outbound call to domestic PSTN numbers.
DialoutCPCDomesticPSTNDisabledUser in the conference can only dial out to domestic numbers, and this user cannot make any outbound calls to PSTN number besides emergency numbers.
DialoutCPCDisabledPSTNDomesticUser in the conference can’t dial out, and this user can only make outbound call to domestic PSTN numbers.
DialoutCPCandPSTNDisabledUser in the conference can’t dial out, and this user can’t make any outbound calls to PSTN number besides emergency numbers.
DialoutCPCZoneAPSTNInternationalUser in the conference can only dial out to Zone A countries and regions, and this user can make outbound calls to international and domestic numbers.
DialoutCPCZoneAPSTNDomesticUser in the conference can only dial out to Zone A countries and regions, and this user can only make outbound calls to domestic PSTN number.
DialoutCPCZoneAPSTNDisabledUser in the conference can only dial out to Zone A countries and regions, and this user can’t make any outbound calls to PSTN number besides emergency numbers.

To set the policy on the tenant level, use following cmdlet. Use the pre-defined policy from the table above for the ‘policy name’

Grant-CsDialoutPolicy -PolicyName <policy name> -Global

To check the current policy at the tenant level,

Get-CSOnlineDialOutPolicy -Identity Global

In my scenario, my plan is to set the global dial-out from meetings policy to DialoutCPCandPSTNDisabled. And assign per-user policy based on their needs.

Tenant-level policy

Note: All users of the tenant who don’t have any dial-out policy assigned will get the global policy.

To set the policy on a per-user level,

Grant-CsDialoutPolicy -Identity <username> -PolicyName <policy name>

You can set what is allowed per-user using the Teams admin center as covered earlier or you can use PowerShell to assign a policy to a list of users using this below script.

#Connect-MicrosoftTeams
$list=import-csv "c:\tmp\user.csv"

Foreach($user in $list){

Grant-CsDialoutPolicy -identity $user.UserId -PolicyName "DialoutCPCandPSTNDomestic" #Setting 'DialoutCPCandPSTNDomestic' policy to the users
}

You can also export a report of users and their Dial-Out policy assigned to them,

Get-CsOnlineUser | Select-Object UserPrincipalName,OnlineDialOutPolicy | Export-CSV "C:\tmp\userCallingPolicyReport.csv" -NoTypeInformation

Teams and its audio services are a much more detailed topic and I covered what applied to the issue I faced. Hope you’d be able to as well if you encounter this.

Thank you for stopping by.✌

M365 – Manage Group Creation Permission

All users can create M365 groups, this is the option enabled by default. Microsoft probably took this approach so as to make sure users can collaborate without any IT assistance.

This is good but when it comes to start managing Teams and the related resources that get created, it can easily become an IT data governance nightmare. If your organization is in its initial phases of Teams rollout, IMO it is better to disable group creation ability for the masses and preferable do a phased approach.

When we disable M365 group creation, it affects all services that rely on groups for access, including:

  • Outlook
  • SharePoint
  • Microsoft Teams
  • Microsoft Stream
  • Yammer
  • Planner
  • Power BI (classic)
  • Project for the web

To have a solution that is sort of a best of both worlds scenario, we can designate an Azure AD group with specific users who have the permissions to create M365 groups.

Create an Azure AD Group

To create a new Azure AD group, the New-AzureADGroup cmdlet can be used or can also be created from the Azure AD admin portal. I’m naming the group ‘M365 – Group Creators’

New-AzureADGroup -DisplayName "M365 - Group Creators" -Description "Group that allows users to create M365 groups" -MailEnabled $false -SecurityEnabled $true -MailNickName "NotSet"
New Azure AD group

Keep in mind this doesn’t prevent users with Azure AD admin roles which has group creation capabilities from creating new groups.

Set Group Creators

The following needs to be run from PowerShell. Make sure AzureADPreview is installed and connected.

Install-module AzureADPreview
Import-Module AzureADPreview
Connect-AzureAD

Run the following commands,

$Template = Get-AzureADDirectorySettingTemplate | where {$_.DisplayName -eq 'Group.Unified'}
$Setting = $Template.CreateDirectorySetting()
New-AzureADDirectorySetting -DirectorySetting $Setting
‘..already exists’ error

If you get an ‘..already exists’ error, that means your tenant already this setting defined. Proceed with the next steps below,

$Setting = Get-AzureADDirectorySetting -Id (Get-AzureADDirectorySetting | where -Property DisplayName -Value "Group.Unified" -EQ).id
$Setting["EnableGroupCreation"] = $False
$Setting["GroupCreationAllowedGroupId"] = (Get-AzureADGroup -SearchString "<Name of your security group>").objectid
EnableGroupCreation and GroupCreationAllowedGroupId

Use the Set-AzureADDirectorySetting below to set the value in the $Setting variable which has the object ID of the Azure AD group.

Set-AzureADDirectorySetting -Id (Get-AzureADDirectorySetting | where -Property DisplayName -Value "Group.Unified" -EQ).id -DirectorySetting $Setting

To determine if the group is allowed to create to groups,

(Get-AzureADDirectorySetting).Values
verify settings

Only one group can be used to control the ability to create Microsoft 365 Groups. But, other groups can be nested as members of this group.

In case your organization wants to revert back this setting in the future, you can do so by changing $AllowGroupCreation to “True” and the group value to “”

$Setting = Get-AzureADDirectorySetting -Id (Get-AzureADDirectorySetting | where -Property DisplayName -Value "Group.Unified" -EQ).id
$Setting["EnableGroupCreation"] = $true
$Setting["GroupCreationAllowedGroupId"] = ""
Set-AzureADDirectorySetting -Id (Get-AzureADDirectorySetting | where -Property DisplayName -Value "Group.Unified" -EQ).id -DirectorySetting $Setting
(Get-AzureADDirectorySetting).Values
Enable group creation

Usually the settings takes 30ish minutes to take effect. You can verify this by trying to create a group with a user who is a non-member of the allowed Azure AD group.

If a user who is part of the group creators can’t create a M365 group, it’s worth checking the OWA policy. The Get-OwaMailboxPolicy can be used to check this,

Get-OwaMailboxPolicy | Select GroupCreationEnabled

If the above output shows ‘False’, you can enable this by using the Set-OwaMailboxPolicy cmdlet,

Set-OwaMailboxPolicy -Identity "Name of your OWA Policy" -GroupCreationEnabled $true

Hope this helped you in setting up the policies to disable M365 group creation.

Thank you for stopping by.✌

O365 – Create Distribution Groups using PowerShell

In this post, I’ll go through the steps to create distribution groups in O365 using PowerShell.

Before proceeding further make sure you are connected to Exchange Online,

$o365cred = Get-Credential
Connect-ExchangeOnline -credential $o365cred

To create a mail-enabled security group named Managers without specifying any members:

$Name = Read-Host "Enter a name for the DistributionGroup"
New-DistributionGroup -Type "Security" -Name $Name -DisplayName $Name -Alias $Name

To create a mail-enabled security group named Managers with members:

Note: -Member is a ‘MultiValuedProperty’ and as we input users comma seperated, we need to split the (comma-separated) string to get an actual array.

$Name = Read-Host "Enter a name for the DistributionGroup"
$Members = Read-Host "Enter email addresses seperated by comma"
$members = $members -split ' *, *'
New-DistributionGroup -Type "Security" -Name $Name -DisplayName $Name -Alias $Name -Members $Members

To add multiple members to an existing Distribution Group:

$Name = Read-Host "Enter DistributionGroup name to add members"
$Members = "user01@domain.onmicrosoft.com","user01@domain.onmicrosoft.com"
$Members | ForEach-Object { Add-DistributionGroupMember -Identity $Name -Member $_}

To import members from a csv and add to an existing Distribution Group:

$Name = Read-Host "Enter DistributionGroup name to add members"
Import-csv "C:\tmp\members.csv" | ForEach-Object {
Add-DistributionGroupMember -Identity $Name -Member $_.member
}

To determine existing distribution group members for a distribution group:

To set distribution group to accept messages from authenticated (internal) and unauthenticated (external) senders.

Note: If you don’t specify this parameter while creating the distribution group, the default value is set to ‘true’ meaning messages from unauthenticated (external) senders are rejected.

$Name = Read-Host "Enter DistributionGroup's name to allow external senders"
Set-DistributionGroup -Identity $Name -RequireSenderAuthenticationEnabled $false

To change an existing distribution group’s name:

$Name = Read-Host "Enter name of existing group to be renamed" 
$NewName = Read-Host "Enter new name" 
Set-DistributionGroup -Identity $Name -Name $NewName -DisplayName $NewName -Alias $NewName

Thank you for stopping by. ✌

O365 – Determine Mailbox Folder Size from Exchange Online using PowerShell

I’m sure all Exchange Online environments have users who are data hoarders in their email environment. Most tenants have policies to limit how big the mailbox can be for various reasons. Users may come back with increasing mailbox sizes so they can hoard more of those outdated data. It is good practice to maintain the folder items. Get-MailboxFolderStatistics helps retrieve information about folders in mailboxes, including number and size of items in the folder and other information.

Before proceeding, make sure you are connected to Exchange Online module,

$credential = Get-Credential
Connect-ExchangeOnline -credential $credential

To determine a folders from a user’s mailbox, the folder size and the number of items in the folders,

$EmailId = Read-Host "Enter user's email address"
Get-MailboxFolderStatistics -Identity $EmailId | Select Name,FolderSize,ItemsinFolder

To determine individual folders and subfolder sizes of a specific user:

$EmailId = Read-Host "Enter user's email address"
Get-MailboxFolderStatistics -Identity $EmailId | Select Name,FolderAndSubfolderSize,ItemsInFolderAndSubfolders

To determine inbox folders statistics of a specific user:

$EmailId = Read-Host "Enter user's email address"
Get-MailboxFolderStatistics -Identity $EmailId -FolderScope Inbox | Format-Table Identity,ItemsInFolderAndSubfolders,FolderAndSubfolderSize -AutoSize

To determine and display inbox folder sizes for all mailboxes in the organization

$All = Get-Mailbox -ResultSize Unlimited
$All | foreach {Get-MailboxFolderStatistics -Identity $_.Identity -FolderScope Inbox} | Select Identity,ItemsInFolderAndSubfolders,FolderAndSubfolderSize | ft -AutoSize

and export to CSV file:

$Allmbx = Get-Mailbox -ResultSize Unlimited
$Allmbx | foreach {Get-MailboxFolderStatistics -Identity $_.Identity -FolderScope Inbox | Select Identity,ItemsInFolderAndSubfolders,FolderAndSubfolderSize | Export-Csv "C:\tmp\Inbox_data.csv" -NoTypeInformation -Append}

Get-MailboxFolderStatistics returns IPM subtree folders. This folder structure consists of messages between recipients(Inbox, Sent Items). In the Exchange Online, the Non-IMP subtree is quite larger, as different O365 applications have been using mailboxes to store and process data. Teams, Delve, MyAnalytics, all have their own folders or folder trees inside the Non-IPM root.

To determine Non-IPM subtree folders and their sizes:

$EmailId = Read-Host "Enter user's email address"
Get-MailboxFolderStatistics -Identity $EmailId -FolderScope NonIpmRoot | Format-Table Identity,ItemsInFolderAndSubfolders,FolderAndSubfolderSize -AutoSize

Thank you for stopping by. ✌

O365 – Limit App Only Permissions to Specific mailboxes

I was recently tasked with providing access to a SaaS Business Spend Management applications access to specific mailboxes in an O365 tenant. This lead me in reading and understanding how this can be achieved and I’ve detailed it in this post.

Before we go further, it is important to understand the differences between Application permissions and Delegated permissions supported by the Microsoft identity platform:

  • Delegated permissions allow an Azure AD application perform actions on behalf of the signed-in user. The user or an administrator consents to the permissions that the app requests. The app has permission to act as the signed-in user when it makes API calls to the target resource.
  • Application permissions allow an Azure AD application run as background services or daemon apps without the presence of a signed-in user.

This SaaS application needed to access emails from a shared mailbox in the tenant. This permission was needed by the SaaS application to read invoices and bills sent to a shared mailbox.

This would be application permissions. Applications like these use OAuth 2.0 Client credentials grant flow to authenticate. So, creating an Azure AD application and granting application permissions as mail.read should solve what we are trying to achieve..right? but wait, there this more. Adding mail.read application permissions allows this app, ability to read mail in all mailboxes in an organization in Exchange Online.

In my above statement, ‘ability to read mail in all mailboxes’ should make any mail administrator scream. Well, that is the problem statement and what we have to solve here.

OAuth 2.0 Client credentials grant flow

In this scenario, I have to limit an Azure AD app to only access specific mailboxes and not all mailboxes in the tenant. Below diagram has a high-level overview of the thought process on how I’m planning to implement this.

For sake of explanation, I have a mailbox named ‘U.S – Marketing’ and I have to grant mail.read permission to the SaaS BSM application. I’ll create a new Azure AD application, add mail.read permission. Next, create a mail-enabled security group named ‘US.Marketing.Mailbox.Access’, add ‘U.S – Marketing’ to the group and then apply the application access policy to restrict access to the mail-enabled security group.

First step is to create a new Azure AD application, add mail.read API permissions and grant admin consent. Yes. we can do this in the Azure AD portal in the App registrations blade but where is the fun in that. If you are in a hurry and need to get this done, the Azure AD portal is the best way as there is lot more information you need to determine for the Add-AzADAppPermission cmdlet’s parameters.

Before proceeding further, make sure you are connected to Azure AD PowerShell with a global admin account.

You can use the below lines in PS to achieve this. The az ad app is part of Azure CLI and not a PS cmdlet. You’ll need to have Azure CLI installed and do az login as well before running this.

$appname = Read-Host "Enter your Azure AD Application's Display Name"
$ObjID = New-AzureADApplication -DisplayName $appname | Select ObjectId
Add-AzADAppPermission -ObjectId $ObjID.ObjectId -ApiId 00000002-0000-0ff1-ce00-000000000000 -PermissionId 810c84a8-4a9e-49e6-bf7d-12d183f40d01 -Type Role
Start-Sleep -Seconds 60
az ad app permission admin-consent --id $ObjID.ObjectId

For the Add-AzADAppPermission cmdlet above, How I determined and arrived with the ApiId and PermissionId is covered in a different blogpost here.

Checking the result in Azure AD portal –> App Registration blade,

Second step is to create an ApplicationAccessPolicy with the policy scope set to the mail-enabled security group,

$appname = Read-Host "Enter your Azure AD Application's Display Name"
$mailbox = Read-Host "Enter mail-enabled security group's address"
$Desc = Read-Host "Enter Description"
$id = Get-AzureADApplication -Filter "DisplayName eq '$appname'"
New-ApplicationAccessPolicy -AppId $id.AppId -PolicyScopeGroupId $mailbox -AccessRight RestrictAccess -Description $Desc

To view the list of all application access policies, Get-ApplicationAccessPolicy cmdlet can be used:

Get-ApplicationAccessPolicy | Format-Table -Auto ScopeName, AccessRight, IsValid, Description

What we’ve done so far is, provided an application permissions to read all emails in a specific mailbox. As we applied the scope to a mail-enabled security group, we add this ‘specific mailbox’ I mentioned in my earlier statement to this mail-enabled security group. To test access right of an application to a specific mailbox or a user, Test-ApplicationAccessPolicy cmdlet can be used:

$appname = Read-Host "Enter your Azure AD Application's Display Name"
$mailbox = Read-Host "Enter email address to test access"
$id = Get-AzureADApplication -Filter "DisplayName eq '$appname'"
Test-ApplicationAccessPolicy -AppID $id.AppId -Identity $mailbox

In the below examples with screenshots, the ‘U.S – Marketing’ mailbox is part of the mail-enabled security group named ‘US.Marketing.Mailbox.Access’. Whereas ‘teams-admin’ mailbox is not, you can see the AccessCheckResult output.

While I was experimenting with the application access policies, I noticed that the changes made to it can take some time to show results. So, if you are following the steps and it still didn’t work..give it some time.

Hope this helped you in limiting application permissions to specific mailboxes in your tenant.

Thank you for stopping by. ✌