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...
ScriptRunner Blog
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.
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?
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 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):
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.
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*
As we can see above, cmdlets with the -email noun exist in Reports, Identity and Teams sub-modules of 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.
The majority of cmdlets focus on users and their mailboxes and can be quite handy in queries and troubleshooting issues with mailboxes.
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
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:
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.
$Thumbprint = '77643669B10C64A23B35DA761FEEF4222039F439'
$AppId = 'dad165ff-4Fa8-4d0f-bbf9-5F67876be441'
$TenantId = '5d0DD54e-0082-4eb8-a311-ce17a036f3f4'
Connect-MgGraph -CertificateThumbprint $Thumbprint -ClientId $AppId -TenantId $TenantId
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.'"
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:
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:
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:
Second run – zero
Now that we’ve run the code again, all emails were removed and can no longer be discovered.
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
}
Folder totals
Get-MgUserMailFolderMessageCount can also be used to get folder totals.
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
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
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:
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.
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:
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.
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.
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! 🐾✨
And if you're looking for more serious help, we offer cheat sheets to help scripting:
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.
Get your Exchange Cheat Sheet here!
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.
Sep 14, 2023 by Damian Scoles
The Microsoft Graph SDK PowerShell module is replacing two other modules. Learn more about connecting to Graph, finding...
Mar 6, 2024 by Damian Scoles
With MS Online and Azure AD module deprecation, it's time to map previous tasks to new methods. We hope the following...
Oct 8, 2024 by Damian Scoles
Users will encounter one or two hurdles when they start using Graph. Damian Scoles wrote three articles provides help...
Damian Scoles is a ten-time Microsoft MVP specializing in Exchange, Office 365 and PowerShell who has 25 years of IT industry experience. He is based in the Chicago area and started out managing Exchange 5.5 and Windows NT. Over the years he has worked with Office 365 since BPOS and his experience has grown to include Azure AD, Security and Compliance Admin Centers, and Exchange Online. His community outreach includes contributing to TechNet forums, creating PowerShell scripts that can be found on his blogs, writing in-depth PowerShell / Office365 / Exchange blog articles, tweeting, and creating PowerShell videos on YouTube. He has written five PowerShell books and is also actively working on the book "Microsoft 365 Security for IT Pros".