Skip to the main content.

ScriptRunner Blog

Exchange Online – Part 2 of our Graph PowerShell series

Table of contents

Post Featured Image

Explore the evolving landscape of managing Exchange Online with Microsoft's Graph PowerShell module versus the traditional Exchange Online Management module. Discover practical insights and examples of using Graph PowerShell for mailbox operations and its current limitations in administrative functionality. 

 

 

An introduction

Exchange and PowerShell go hand in hand and for the longest time those who administer Exchange Online used the Exchange Online Management PowerShell module. Microsoft's Graph PowerShell module provides access to Exchange Online as well and this can be seen in cmdlets present in sub-modules, however this coverage does not provide complete management capabilities. So why use it?

 

Sub-modules

The Microsoft Graph PowerShell module currently has 38 different sub-modules and thousands of cmdlets to choose from. In terms of Exchange management, there is no one module that includes all cmdlets for Exchange in Graph. One sub-module stands out and the is the Microsoft.Graph.Mail module and has 75 cmdlets as of the writing of this article. For reference, the Exchange Online PowerShell module has over 800 cmdlets as of the writing of this article. What other sub-modules may be of service when managing Exchange?


Microsoft.Graph.Compliance
Microsoft.Graph.Security
Microsoft.Graph.Reports
Microsoft.Graph.Contacts

These four sub-modules contain useful cmdlets for interacting with Exchange Online and administrators will find utilitarian cmdlets for tasks that focus on users’ mailboxes versus the macro level of configuring Exchange Online. Let’s dive into what Exchange Online PowerShell can be used for and then dive into these modules to explore what practical tasks we can accomplish with them. 

 

Exchange Online (ExO) management tasks

Exchange is a complex product that contains many manageable features. Below is a sample of what we can manage using the Exchange Online PowerShell module (v3): 

  • Anti-phishing
  • Anti-spam
  • Availability address space
  • Clutter
  • Contacts
  • Distribution groups (dynamic as well)
  • DKIM
  • Email address policies
  • Global address list
  • Inbox rules
  • Journaling
  • Mail flow connectors
  • Mailboxes – calendars, folders, permissions, imports, migrations, OOF
  • Message encryption
  • Message traces
  • Mobile device / OWA policies
  • Public folders
  • Quarantine policy
  • RBAC roles
  • Retention policy
  • Safe attachment policies
  • Safe link policies
  • Sharing policy
  • Transport rules
  • …and more.

Exchange and Graph PowerShell

Below is a short list of what we can do in Graph. It is vastly oversimplified as there are numerous cmdlets that we can use in Graph for Exchange Online, however they are relegated to user / mailbox level options.

  • Send mail messages
  • Low level mailbox integration – emails, folders, invites, etc.
  • Mailbox contacts
  • User photos

Note: The above cmdlets are very user focus, with little to no Exchange Online administrative options.

As with any task in Graph, we cannot assume that Exchange related cmdlets exist only in the Microsoft.Graph.Mail sub-module, so let’s run a more general query to see what cmdlets with the noun -email exist outside the Mail sub-module:


Get-Command *email*

01_email

As we can see above, cmdlets with the -email noun exist in Reports, Identity and Teams sub-modules of Graph

 

Current capabilities in Graph 

The desire to use Graph PowerShell instead of Exchange by Administrators would seem to have had a small effect on Microsoft as the capabilities present are not all encompassing.  Exchange Online management has a lot of facets that are sufficiently covered in the Exchange Online PowerShell module but a lot of the backend management is missing in Graph. This doesn't mean that Graph does not have its place when it comes to managing Exchange but we need to be aware of limitations present in Graph. In this section of the article we will dive into some excellent uses of Graph PowerShell for the Exchange workload. 

 

Mailbox operations

The majority of cmdlets focus on users and their mailboxes and can be quite handy in queries and troubleshooting issues with mailboxes.

 

Mail folders

PowerShell can be used to explore parts of mailboxes, with one of these being Folders. Folders are where people have files and organize their emails for business or personal use. Within these folders we can query things like size and counts of total and unread emails. Why would we care? Outlook has limitations that can be exposed when folders get to big. The below query might expose this to help troubleshooting Outlooking sync issues:


Get-MgUserMailFolder -UserId 22561a78-a72e-4d39-898d-cd7c57c84ca6 |
Format-Table DisplayName,*ItemCount,AdditionalProperties

02_screenshot of folders

Screenshot of folders

In the above screenshot we see that while a couple folders have thousands of items, the numbers do not exceed Outlook limits. 

The previous example is a simple query, but what if wanted to look at all mailboxes and see if any TotalItemCounts were over a certain threshold like 10,000 as this could cause poor Outlook performance. How would we do this?


foreach ($Mailbox in $Mailboxes) {
try {
$Folders = Get-MgUserMailFolder -UserId $Mailbox.Id -ErrorAction Stop |
Select-Object DisplayName, TotalItemCount
}
catch
{}
foreach ($Folder in $Folders) {
if ($Folder.TotalItemCount -gt 10000) {
$FolderName = $Folder.DisplayName
$MailboxName = $Mailbox.DisplayName
Write-Host "$MailboxName - $FolderName has over 10k mail items."
}
}
}

or Write-Host like so: 


   foreach ($Folder in $Folders) {
if ($Folder.TotalItemCount -gt 10000) {
$FolderName = $Folder.DisplayName
$MailboxName = $Mailbox.DisplayName
Write-Host "$($Folder.DisplayName) - $($Mailbox.DisplayName) has over 10k mail items."
}

 

Expected results from this code if mailboxes have over 10,000 items in a folder:

03_10000 items

10,000 items exceeded

Four mailboxes were discovered with over 10,000 items stored in the Inbox Folder. We can proactively ask users to clean this up to prevent future issues.

Caveat: If we use delegate permissions, then we will only retrieve information about the local user and instead we need to connect to Graph using an application. 

 

Example: 



$Thumbprint = '77643669B10C64A23B35DA761FEEF4222039F439'
$AppId = 'dad165ff-4Fa8-4d0f-bbf9-5F67876be441'
$TenantId = '5d0DD54e-0082-4eb8-a311-ce17a036f3f4'
Connect-MgGraph -CertificateThumbprint $Thumbprint -ClientId $AppId -TenantId $TenantId

 

Mailbox message cleanup

There are a couple of scenarios here, one would be if an unintended email was sent, we could search and remove these messages, or perhaps we enabled the Microsoft Quarantine email notification by accident, without telling users and we need to remove those emails. Either scenario can be carried out with some Graph PowerShell. 

Make sure again to connect with the Azure App to Graph as this will allow us to access all mailboxes. First, a sample run:



$UserID = '5fc8abf1-2178-4043-b601-4802e0a703cd'
Get-MgUserMessage -UserId $UserID -Filter "Subject eq 'Emails Quarantined for your mailbox.'"

 

04_quarantined emails

A list of the first returned Quarantine emails found in the source mailbox

One off to remove the top message:
(testing for expected results)



Remove-MgUserMessage -UserId $UserID -MessageId
'AAMkADNhNWI1ODRiLTVmNjQtNGU4Mi1hYmRlLWFjMzJkYTY2Nzk2NQBGAAAAAABoWADN1g6YRpgk4ssxd16qBwCAEw_FeYpCSKoYCRXHiLVBAS0F7CqPAABpkGQwsqIcSYSWFGyKXc7bAAI4DSpIAAA='

 

This does indeed remove one email message. Next, we want to remove all of these for one user:


$msgFilter = "Subject eq 'Emails Quarantined for your mailbox.'"
$EmailToRemove = (Get-MgUserMessage -UserId $UserID -Filter $msgFilter).Id
foreach ($Email in $EmailToRemove) {
Remove-MgUserMessage -UserId $UserID -MessageId $Email -WhatIf
}

 

-WhatIf used to validate the cmdlet is correct and it is:

05_whatif switch

The -WhatIf switch reports the emails that would be removed from the mailbox

Remove the -WhatIf switch:


foreach ($Email in $EmailToRemove) {
Remove-MgUserMessage -UserId $UserID -MessageId $Email
}

Then we find out that, well, there are more emails that match this criterion. Why is that? Graph and paging. When running the cmdlet, we only pull the top 10 results. If we want all, we need to tell Graph that with the -All switch.


$EmailToRemove = Get-MgUserMessage -UserId $UserID -Filter "Subject eq 'Emails Quarantined for your mailbox.'" -All

Now we have them all:

06_count

First run – 248 emails as the result

We see from the .Count property that there are an additional 248 emails to remove. We see this because the Get‑MgUserMessage does not return all results by default.

It is recommended to run -WhatIf one more time and then run the real thing. Once complete, check for any remaining messages, but there should be zero:

07_second run

Second run – zero

Now that we’ve run the code again, all emails were removed and can no longer be discovered.

 

Reviewing emails in a user's mailbox

An additional cmdlet exists, which will shrink coding needed, that will pull email counts for a user’s folders IF we have the folder ID.


Get-MgUserMailFolder -UserId $UserId |
Select-Object DisplayName,ID |
ForEach-Object {
Write-Host "$($_.DisplayName) - " -NoNewLine
Get-MgUserMailFolderMessageCount -UserId $UserId -MailFolderId $_.Id
}

08_Get-MgUserMailFolderMessageCount

Folder totals

Get-MgUserMailFolderMessageCount can also be used to get folder totals.

 

User photos 

User Photos were at a time a function of Exchange Online PowerShell, and this was done historically because Exchange was the workload organizations used to enter Microsoft 365. Now that Exchange is no longer the center of the Microsoft 365 ecosystem, Microsoft has deprecated this and moved forward with a solution in Graph PowerShell instead. Indeed, from within Graph we see that there are four User Photo oriented cmdlets:


Get-MgUserPhoto
Get-MgUserPhotoContent
Remove-MgUserPhoto
Set-MgUserPhotoContent

To add a photo to an existing user, we can run code like this:


Set-MgUserPhotoContent -UserId Eric@powershellgeek.com -InFile C:\Data\SampleUserPhoto.jpg

To prove it works, we can check that Eric does indeed have a photo:


Get-MgUserPhoto -UserId Eric@powershellgeek.com

09_size of photo

Height and width of the user photo

We now have the size of the user’s profile photo, 91x100 pixels. And we can even easily download the photo:


Get-MgUserPhotoContent -UserId Eric@powershellgeek.com -OutFile c:\data\SamplePhoto-Ronald.jpg

samplePhoto-Eric

Exported and imported photo

Above are the two photos, on the left is the one we exported and, on the right, the one we imported (view from 'C:\Data' directory).

Very easy to use and it could potentially be done in bulk. To find out who has a photo and who is missing their photo, we can run this one-liner:


Get-MgUser -All |
Select-Object DisplayName, Id |
ForEach-Object {
try {
$Photo = Get-MgUserPhoto -UserId $_.Id -ErrorAction Stop
Write-Host "$($_.DisplayName) has a photo." -ForegroundColor Green
}
catch {
Write-Host "$($_.DisplayName) is missing photo." -ForegroundColor Yellow
}
}

Which yields this:

11_who has a photo like eric

Who has a photo like Eric?

Some users have a photo, some do not. Now we can go about adding photos to users with Graph PowerShell.

 

Discovering cmdlets

This is where using a consolidated module is easier when managing one Workload and by this I mean Exchange Online Management. Graph is a model based on access to API URLs and Graph PowerShell cmdlets are wrappers for these API calls. The problem exists when you are looking for cmdlets to manage a workload and they are spread among quite a few other modules. Try this search and you will see a lot of different modules referenced:


Get-Command '*user*mail*' | Format-Table Name, Source

These sub-modules are the unique values we find in this search:

12_mail related cmdlets

Mail related cmdlets

We see that there are indeed quite a few functions that have mail related PowerShell cmdlets. For those new to managing some aspects of Exchange from Graph, there will be a learning curve. Don't think of this as a barrier, but an exploratory adventure into the many options that Graph offers for you. In fact, some of the cmdlets in Graph, with respect to Exchange, are quite useful and make some administrative tasks easier to work with. The delving into various aspects of a user's mailbox means that we can use Graph PowerShell instead of EWS, which is good as Microsoft announced the deprecation of EWS recently.

 

Conclusion – Final thoughts

Microsoft Graph is like a treasure hunt where cmdlets that are relevant to a workload, may be stored in different sub-modules because of how Graph is organized. Add to this the permissions levels, Delegated vs Application permissions as well as PowerShell cmdlets vs Invoke0-RestMethod an administrator may be forgiven for feeling like the entire Graph PowerShell SDK is cumbersome. However, it is a good alternative for the functionality it provides and in terms of Exchange Online, Graph provides a much-needed interconnect into the inner parts of user’s mailboxes that may not be possible with Exchange Online PowerShell. In terms of overall fitness for managing Exchange Online, Graph simply is not there yet, and administrators will still need to lean on both the Exchange Online PowerShell module and the Security and Compliance PowerShell module along with Graph to effectively manage this workload. Microsoft will probably get to a point in the future where this may not be true, but that is not today.

 

 

Good2know

Eric takes over our office!

Are you' wondering who Eric is and why we chose this dog picture for the blogpost?

Eric-1

 Watch the video with adorable Eric down below or here on LinkedIn!   LinkedIn.svg

 

🐾 Eric takes over the office! 🐾

Guess who is our employee of the month (and last month, and next)? Eric, our Labrador!
He sometimes takes over the office and with the help of ScriptRunner,
he automated everything – from sending 'important' emails to scheduling tasks effortlessly.
He even found time for a nap under the desk.
✨🐾 Spoiler: He’s a pro at both automation and relaxation! 🐾✨

🐶 Check out Eric’s hilarious office adventure in the full video!🐶 And comment here on LinkedIn!    LinkedIn.svg

 

And if you're looking for more serious help, we offer cheat sheets to help scripting:

Your must-have PowerShell Cheat Sheet
to automate Microsoft Exchange

Streamline your Microsoft Exchange administration tasks with our 5-page PowerShell cheat sheet for Microsoft Exchange. This indispensable guide compiles essential PowerShell commandlets and best practices to automate repetitive tasks, manage users, and configure settings with ease. Whether you're new to Exchange or an experienced admin, this cheat sheet is designed to make your job simpler and more efficient.

Exchange Cheat Sheet for PowerShell scripting

Get your Exchange Cheat Sheet here!

 

 

 

Your ultimate PowerShell Cheat Sheet

Unleash the full potential of PowerShell with our handy poster. Whether you're a beginner or a seasoned pro, this cheat sheet is designed to be your go-to resource for the most important and commonly used cmdlets.

The poster is available for download and in paper form.

PowerShell Poster 2023

Get your poster here!

 

 

Related links

Related posts

12 min read

Licensing with Microsoft Graph PowerShell

The Microsoft Graph SDK PowerShell module is replacing two other modules. Learn more about connecting to Graph, finding...

12 min read

Are you ready for Destination Graph? – Azure AD and MSOnline module are deprecated by March 2024

With MS Online and Azure AD module deprecation, it's time to map previous tasks to new methods. We hope the following...

11 min read

Graph PowerShell SDK – Part 1 of our Graph series

Users will encounter one or two hurdles when they start using Graph. Damian Scoles wrote three articles provides help...

About the author: