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. ✌

Office 365 – License Reporting using PowerShell – Updated

Reporting on O365 licenses is crucial and is necessary to keep track. I’m sure just like me most administrators get asked to generate reports on how the current license state on the tenant. In this post, I have this script I’ve put together just for this purpose.

The O365 portal does provide few options to export the data but this script generates the below reports,

  • O365 license usage
  • All enabled and licensed users report
  • All unlicensed users report
  • All disabled and licensed users report

This report can also be scheduled to run if you already use a mechanism to store your credentials securely and pass it on to your PS scripts.

I use the ImportExcel PowerShell module for this script,

Install-Module -Name ImportExcel

Before proceeding further, you must first connect to your MS online service. To do so, run the cmdlet Connect-MsolService at the Windows PowerShell command prompt. You will then be prompted for your credentials.

$Msolcred = Get-credential
Connect-MsolService -Credential $MsolCred

$xlsxPath = ".\Office365LicenseReport_$((Get-Date).ToString("MMddyyyy")).xlsx"
$LicNames = Get-Content -raw ".\FriendlyLicenseName.txt" | ConvertFrom-StringData

Get-MsolAccountSku | foreach {
            $ActiveUnits = $_.ActiveUnits
            $ConsumedUnits = $_.ConsumedUnits
            $LicenseItem = $_.AccountSkuId -Split ":" | Select-Object -Last 1
            $FriendlyName = $LicNames[$LicenseItem]

            [PSCustomObject]@{
                    'License' = $FriendlyName;
                    'Active Units' = $ActiveUnits;
                    'Consumed Units' = $ConsumedUnits
            }
} | Export-Excel -Path $xlsxPath -WorksheetName "Licensed_State" -TableStyle Medium16 -AutoSize -Append

Get-MsolUser -All -EnabledFilter EnabledOnly | where {$_.IsLicensed -eq $true} | Select-Object -ExpandProperty Licenses DisplayName,UserPrincipalName,Title,Department,UsageLocation | Select DisplayName,UserPrincipalName,Title,Department,UsageLocation,AccountSkuId | foreach {
            $DisplayName = $_.DisplayName
            $UPN = $_.UserPrincipalName
            $JobTitle = $_.Title
            $Department = $_.Department
            $UsageLoc = $_.UsageLocation
            $LicenseItem = $_.AccountSkuId -Split ":" | Select-Object -Last 1
            $FriendlyName = $LicNames[$LicenseItem]

            [PSCustomObject]@{
                    'Display Name' = $DisplayName;
                    'User Principal Name' = $UPN;
                    'License Plans' = $FriendlyName;
                    'JobTitle' = $JobTitle;
                    'Department' = $Department;
                    'Usage Location' = $UsageLoc
            }
}  | Export-Excel -Path $xlsxPath -WorksheetName "Enabled_Licensed_Users" -TableStyle Medium16 -AutoSize -Append

Get-MsolUser -All -UnlicensedUsersOnly | foreach {
            $DisplayName = $_.DisplayName
            $UPN = $_.UserPrincipalName
            $JobTitle = $_.Title
            $Department = $_.Department

            [PSCustomObject]@{
                    'Display Name' = $DisplayName;
                    'User Principal Name' = $UPN;
                    'JobTitle' = $JobTitle;
                    'Department' = $Department
            }
} | Export-Excel -Path $xlsxPath -WorksheetName "UnLicensed_Users" -TableStyle Medium16 -AutoSize -Append

Get-MsolUser -All -EnabledFilter DisabledOnly | where {$_.IsLicensed -eq $true} | Select-Object -ExpandProperty Licenses DisplayName,UserPrincipalName,Title,Department,UsageLocation | Select DisplayName,UserPrincipalName,Title,Department,UsageLocation,AccountSkuId | foreach {
            $DisplayName = $_.DisplayName
            $UPN = $_.UserPrincipalName
            $JobTitle = $_.Title
            $Department = $_.Department
            $UsageLoc = $_.UsageLocation
            $LicenseItem = $_.AccountSkuId -Split ":" | Select-Object -Last 1
            $FriendlyName = $LicNames[$LicenseItem]

            [PSCustomObject]@{
                    'Display Name' = $DisplayName;
                    'User Principal Name' = $UPN;
                    'License Plans' = $FriendlyName;
                    'JobTitle' = $JobTitle;
                    'Department' = $Department;
                    'Usage Location' = $UsageLoc
            }
} | Export-Excel -Path $xlsxPath -WorksheetName "Disabled_Licensed_Users" -TableStyle Medium16 -AutoSize -Append

I used this link to create a file to do a lookup of the ID which is the output from the AccountSkuId and convert it into a friendly name. This list is subject to change but you can download the ‘FriendlyLicenseName.txt‘ from this below link.

Place this file in the same location as you have the PS script or modify script accordingly.

I found it useful that this script’s output can be readily used to generate Pivot tables or charts. If you wish, you can also generate it straight from the script using -IncludePivotTable

Hope this script was helpful in determining the current license state in your O365 environment.

Thank you for stopping by. ✌

Office 365 – Convert User Mailbox to Shared Mailbox

Scenarios are plenty when O365 admins are requested to convert a user mailbox to a shared mailbox.

Here is one that comes up often,

  • User/Employee leaves the organization and others in the team need access to the user’s mailbox to keep track of the project they were working on

When we convert a user mailbox to a shared mailbox, the mailbox must have a license assigned and after the conversion is complete, we can remove the license.

One little caveat to the licensing part is, without license assigned to the shared mailbox, its size is limited to 50GB. So, before converting, make sure to check the mailbox’s size and to increase the shared mailbox’s size to 100GB, assign a Exchange Online Plan 2 license.

Note: If you are on Exchange hybrid environment, you’ll have to manage your mailboxes using on-premises Exchange management tools.

To convert a user mailbox to Shared mailbox using PowerShell

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

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

To convert to shared mailbox:

$Mbx = Read-Host "Enter user's email address for Shared Mailbox conversion"
Set-Mailbox -Identity $Mbx -Type Shared
Set-Mailbox

The -Type parameter also supports the below values:

  • Equipment
  • Regular
  • Room
  • Workspace (cloud-only)

Determine if it worked

To make sure the mailbox has been successfully converted:

$sMbx = Read-Host "Enter email address"
Get-Mailbox -Identity $sMbx | fl DisplayName,RecipientTypeDetails
Get-Mailbox

To convert a user mailbox to Shared mailbox using EAC

Exchange admin center also allows converting a user’s mailbox to shared mailbox.

  1. Login to Exchange admin center, in the left navigation menu, click recipients
  2. Click Mailboxes
  3. Search for the mailbox and select it
  4. In the right side details window, Click Convert under Convert to Shared Mailbox
  5. Click Yes in the warning window
Convert to Shared Mailbox
successfully converted to shared mailbox

Determine if it worked

  1. Login to Exchange admin center, in the left navigation menu, click recipients
  2. Click shared
  3. Search for the mailbox
Shared tab in recipients

Hope this post helped you out.

Thank you for stopping by.✌

O365 – Create and Manage Public folders using PowerShell

Public folders are meant for shared access and to enable an easy way to collect and share information with other users in an organization. Public folders makes it easier to browse content and users will see the full hierarchy in Outlook using which they can find the content they are looking for.

Public folders can also be used to archive email sent to distribution groups. When a public folder is mail-enabled, and added as a member of the distribution group, email sent to this group is automatically added to the public folder to be referenced later.

Public folders use mailbox infrastructure for high availability and mailbox database storage technologies. This architecture uses specially designed mailboxes to store both public folder hierarchy and the content.

The main component of public folders are the public folder mailboxes. Before creating public folders, we must first create public folder mailboxes. There are two types of public folder mailboxes,

  • Primary hierarchy mailbox: This is the one writable copy of the public folder hierarch and is copied to all other public folder mailboxes. The first public folder mailbox created will be the primary hierarchy mailbox in the organization
  • Secondary hierarchy mailbox: Additional public folder mailboxes created are secondary hierarchy mailboxes and can also contain content. Is a read-only copy of the public folder hierarchy

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

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

To create new public folder mailbox

To create first mailbox that will be the Primary hierarchy mailbox:

$Name = Read-Host "Enter a name for the Public Folder"
New-Mailbox -PublicFolder -Name $Name

To create additional public folder mailboxes that will be Secondary hierarchy mailboxes:

$Name = Read-Host "Enter a name for the Public Folder"
New-Mailbox -PublicFolder -Name $Name

To create public folders

To create a new public folder:

$Name = Read-Host "Enter a name for the Public Folder"
New-PublicFolder -Name $Name

To create a public folder under and existing folder:

$Name = Read-Host "Enter a name for the Public Folder"
$Path = Read-Host "Enter a existing folder name"
New-PublicFolder -Name $Name -Path $Path
example shows ‘Reports’ folder created under Sales
example shows ‘Monthly’ folder under ‘Reports’ folder created under Sales

To mail enable a public folder:

$Name = Read-Host "Enter Public Folder which you wish to mail enable"
Enable-MailPublicFolder $Name

To add permissions to a specific user on a public folder:

$Name = Read-Host "Enter name of public folder that you wish to add permissions"
$user = Read-Host "Enter email address of user"
$AccessRights = Read-Host "Enter permissions separated by comma"
$AccessRights = $AccessRights -split ' *, *'
Add-PublicFolderClientPermission -Identity $Name -User $user -AccessRights $AccessRights

To determine permissions on a specific folder:

$Name = Read-Host "Enter a name for the Public Folder to query permissions"
Get-PublicFolder -Identity $Name | Get-PublicFolderClientPermission | Select Identity, User, AccessRights

Hope this post helped you out.

Thank you for stopping by. ✌

O365 – Determine Licensed users

Who are the licensed users in our tenant and what licenses are assigned to them? This question comes up way too often in several scenarios and there are a few methods to determine this. I will go over those in this post. I’ve updated this post with some newer information about exporting from the admin portal when I learned them.

Using PowerShell

There are two versions of the PowerShell module that you can use to connect to Microsoft 365 and administer user accounts, groups, and licenses:

  • Microsoft Azure AD Module for Windows PowerShell, whose cmdlets include Msol in their name
  • Azure AD PowerShell for Graph, whose cmdlets include AzureAD in their name

Please make sure you have the MSOnline Module for PowerShell installed and loaded

The Get-MsolUser is a powerful cmdlet which provides a lot of details and I’m going to use it for determining the user’s license.

To connect to the service,

$credential = Get-Credential -credential "adminuser@tenant.onmicrosoft.com"
Connect-MsolService -credential $credential

To get all (licensed and unlicensed) users,

Get-MsolUser -All
Get-MsolUser output

To list only licensed users,

Get-MsolUser -All | Where {$_.isLicensed -eq $true}

To list unlicensed users,

Get-MsolUser -All -UnlicensedUsersOnly

To export all users to a csv with their user name, license status and license assigned,

Get-MsolUser -All | Where {$_.isLicensed -eq $true} | Select Displayname,userprincipalname,islicensed,{$_.Licenses.AccountSkuId} | Export-csv "C:\tmp\userlist.csv" -NoTypeInformation

The exported csv will look like this,

csv data

Using the O365 admin portal

Microsoft has enabled exporting licensing information from the portal.

  1. Login to O365 admin center
  2. Users –> Active Users
  3. Click Export Users
  4. Click Continue
admin portal licensing information

This is how the csv output looks like,

csv export from admin center

Thank you for stopping by. ✌