Author: Tony Redmond
The New Outlook Gains Colored Folder Icons
Outlook Users Never Realized the Desperate Need for Colored Folder Icons
The announcement in message center notification MC993229 (31 January, 2025), Microsoft 365 roadmap item 472921) that the new Outlook (or as it’s referred to in the announcement, “the new Microsoft Outlook for Windows desktop” and OWA are allowing users to personalize folder icon colors is in the category of “why” features. Apparently, the idea is to make it easier for people “to visually differentiate and personalize folders.” The feature is available in targeted release tenants and will be generally available worldwide during March 2025.
I don’t want to pour cold water on innovation, but the thought did go through my mind that the Outlook classic desktop client has survived and prospered since 1997 without different colored folder icons. The same is true for OWA, introduced around the same time, and seemingly unaffected by monocolor folder icons.
Using Outlook Colored Folder Icons
But now we have colored folder icons and the world is a better place. At least, it might be if you’re not color blind (like me) and have difficulties differentiating between nuanced shades. In the spirit of adventure, I resolved to bring a dash of color into my email life and set out to update some folders.
The first thing to note is that you can leave Outlook alone and it will use automatic colors. In other words, Outlook chooses how to present the folder icon. I’m not quite sure what color is used, but it’s functional and never caused me a moment’s worry until now, mostly because I never thought about choosing a new color for folder icons.
In Figure 1, the Archive folder is selected, and its folder icon is colored silver, one of the options in the folder icon palette. Some of the other folders have new colors too. Whether this makes those folders more recognizable or visually differentiated is in the eyes of the beholder.
To reveal the option to choose a new color for a folder icon, click the […] folder menu alongside its name. To produce the screen shot shown in Figure 1, I selected the folder menu for the Sent Items folder. As you can see, Sent Items still uses the automatic default chosen by Outlook. To update the folder icon color, choose one of the available selection like cranberry, light teal, or lime (note to self, who would have thought that I would ever write about applying lime as a color to any Outlook component?).

In any case, it all works, and you can spend a few minutes colorizing your folder icons.
Filers versus Pilers
I don’t know what impetus pushed the Outlook team to introduce colored folder icons at this point in the product’s development. It seems like many users eschew the use of folders apart from the default set because they depend on search to find items of interest when necessary. Piling items into a small set of folders is a habit encouraged by reliable search, something that took Outlook a long time to acquire.
I’m a filer in that I use folders to organize information. I’m not as diligent about filing as I once was in the days when search worked intermittently. Smaller mailbox quotas meant that it was sometimes necessary to clear out lots of items to make space for new email. Large mailbox quotas and retention processing have largely taken care of the need to delete items from mailboxes manually. I guess we need to fill the time once spent removing unwanted debris from mailboxes with other activities, like choosing colors for folder icons.
But Seriously
Some will criticize the Outlook developers for spending valuable engineering time implementing features like folder icon colors. If Microsoft is really serious about convincing the curmudgeons who use Outlook classic to move to the new client before support ceases for Outlook classic in 2029, shouldn’t they be solving the major pain points that stop people switching? Of course, Microsoft should deliver solutions like solid PST support (due imminently according to MC966639), but assigning a bunch of extra engineers to work on the pain points might not create solutions any faster. Which is why the engineers need to be kept occupied by pushing forward the frontiers of information technology with colored folder icons.
Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.
Using iOS Build Numbers in Exchange ActiveSync Device Access Rules
Now Possible to Include iOS Build Numbers in ActiveSync Device Access Rules to Allow Access for Devices Running Specific Builds
I last looked at Exchange Online Mobile Device management in June 2023, when I wrote about reporting devices that synchronize with Exchange mailboxes using ActiveSync. At the time, I said that not many changes had recently occurred in Exchange Mobile Device Management. After all, Microsoft wants customers to use Intune, and Exchange Mobile Device Management is very much the runt in the Microsoft device management litter.
Which brings me to message center notification MC916298 (23 October 2024) and now fully available in tenants worldwide. It’s the first change in ActiveSync Device Access rules that I can remember since Microsoft updated rules to support Outlook for iOS and Android after its Acompli acquisition in late 2014. The best articles about how to configure device access rules still date from that period. Exchange ActiveSync is not an area of high change.
Query Strings and iOS Build Numbers (or Build Strings)
The change is that Apple iOS build information is now supported in the query string used to check the O/S version on mobile devices attempting to connect to Exchange Online mailboxes. MC916298 says “build number,” but Apple uses alphabetic identifiers like 22D72 (Figure 1).

Leaving semantics aside, the point is that organizations can create ActiveSync device access rules based on the information reported by iOS devices. For example, this code creates an access rule that allows IOS devices running iOS 18.3.1 22D72:
New-ActiveSyncDeviceAccessRule -AccessLevel Allow -Characteristic DeviceOS -QueryString "iOS 18.3.1 22D72"
To block iOS devices with a specific build, change the access level to Block.
I guess that the new capability exists to allow tenants to insist that iOS devices use a specific build for whatever reason that they might have. It’s just another level of granularity to detect devices.
Testing a Block Using iOS Build Numbers
The documentation for the New-ActiveSyncDeviceAccessRule cmdlet hasn’t been updated recently (it features examples blocking iOS devices running 6.1.1), so don’t expect much additional information from that source. However, I can guarantee that the access rule shown above works. I know this because I removed all the other access rules from my tenant and created one that was slightly different to the one shown above (IOS 18.4.1 rather than iOS 18.3.1). After a pause of about 15 minutes for the rule changes to replicate and become effective, the access rule blocked any attempt by Outlook for iOS to synchronize with mailboxes (Figure 2).

Users of non-compliant iOS devices also received email to tell them that their devices couldn’t connect and was blocked from synchronizing with Exchange Online (Figure 3). Interestingly, the blocked devices have never shown up in the quarantined device list in the Exchange admin center.

After being shouted at by several users who were unhappy that their email wouldn’t synchronize, I deleted the incorrect access rule and replaced it with the proper version. Within 15 minutes, email flowed again and all was well.
Time to Check Device Access Rules
Apart from playing with IOS build numbers, the exercise in testing device access rules was useful because it forced me to clean out the old and obsolete device access rules that had accumulated in my tenant. There was a time when these rules were critical. Given the dominance of Outlook for iOS and Android, I suspect that many tenants have just one rule (to allow access to those clients. Defining more sophisticated access rules are only needed to control clients that use the Exchange ActiveSync protocol for everything, like the native Apple mail app. Oh well, on to the next thing.
Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.
How to Create and Send an Outlook Newsletter
Outlook Newsletters App for Outlook and OWA
Message center notifications MC1009916 (19 February 2025, Microsoft 365 roadmap item 328282) describes the new Outlook Newsletters solution, designed to create and send high-quality internal newsletters. The app is rolling out now in preview to targeted tenants. Standard tenants are likely to see Outlook Newsletters before the end of March 2025. General availability is scheduled for August 2025.
Outlook Newsletters is an app constructed from components drawn from the Microsoft 365 software toolbox like SharePoint Embedded, Microsoft Designer, Outlook reactions, comments, and so on. It’s a good example of how to combine available components with new code to create new apps.
Enabling Outlook Newsletters
Outlook Newsletters is an opt-in solution, meaning that it must be enabled before it appears in the menu bar for the new Outlook for Windows or OWA. Enablement is through settings in the OWA mailbox policies applied to mailboxes. Three policy settings are available (the administrator documentation is sparse and likely to be overhauled before general release):
- OutlookNewslettersAccessLevel: defines the access level a mailbox has to Outlook Newsletters. To create and send newsletters, this setting must be ReadWrite. Users with ReadOnly access can open the Newsletters app but can’t create or send newsletters. The default is no value, which equates to NoAccess.
- OutlookNewslettersReactions: Set to DefaultOn to make it the default that newsletters allow recipients to react in the same way as they react to normal Outlook email. Reactions can only be posted by internal recipients.
- OutlookNewslettersShowMore: Set to DefaultOn to make it the default for newsletters to display other newsletters at the bottom of a message. The idea is that recipients might find newsletters to subscribe to.
For example, this command allows any mailbox with the scope of the OWAFullAccess policy to have read write access to the Newsletters app with the other features enabled by default.
Set-OwaMailboxPolicy -Identity OWAFullAccess -OutlookNewslettersReactions DefaultOn -OutlookNewslettersAccessLevel ReadWrite -OutlookNewslettersShowMore DefaultOn
After updating the mailbox policy, it will take between 15 and 30 minutes before the Newsletters app becomes available to users in the Outlook menu bar. Alternatively, users can open the app using the direct link.
Quick Tour of Outlook Newsletters
The user documentation for Outlook Newsletters is available online and doesn’t need to be repeated here. Instead, I’ll describe how I created and sent a newsletter in just a few minutes.
After opening the app, you can choose to create a newsletter or group page. A group page is recommended when a newsletter has multiple contributors and multiple newsletters will be created with common branding, so that’s what I created (Figure 1). I added a heading and some common settings shared by all the newsletters associated with the group page.

Next, create a newsletter by selecting one of the out-of-the-box templates or a blank template. I used the basic template, which seemed like a good starting point to create a newsletter to circulate details of blog posts published over the last month.

Creating the content of a newsletter is a matter of editing the elements contained in sections. A template contains prepopulated sections to make the task easier, but you can add or remove elements as you like to create the desired effect. In my case, I extracted snippets and links for blog posts and combined them with images to highlight each article. Suitable images can be uploaded or generated using Microsoft Designer.
Draft newsletters and comments are stored in SharePoint Embedded containers and are visible through the SharePoint admin center (Figure 3) and PowerShell. Unhappily, an application name isn’t registered for the containers used by Outlook Newsletters. No doubt this is a detail that Microsoft will clean up before GA.

When the newsletter is complete, it’s ready for sending. This process involves creating a HTML format body part and combining it with message properties like the message title and recipients (Figure 4) before sending the message from the author’s mailbox, much like you’d do with Graph APIs.

Only known recipients can receive newsletters. A known recipient is an emailable object known to Exchange Online, including distribution lists, Microsoft 365 groups, individual mailboxes, mail user objects for guest accounts, and mail contacts.
You can enter an SMTP address, but Outlook drops these addresses if they don’t match with a known recipient when it sends the message. The golden rule is that to send a newsletter to an external address, the address must belong to a known recipient. This isn’t a big deal because it’s easy to create a mail contact for an external recipient, even for something like the email address for a team channel.
When sent, copies of the newsletter are normal messages in recipient mailboxes. After it is sent, the newsletter remains available for editing in the Newsletters app. If you make some changes and send another version, everyone in the recipient list receives a new copy.
Lots to Like
There’s lots to like about the Outlook newsletters app. The output generated looks well, basic analytics are included, newsletters support subscribe and unsubscribe options, messages can be sent from one mailbox with replies going back to a different mailbox, and so on. It’s definitely an app that people who send internal communications can find value.
Although it’s possible to send newsletters externally, restrictions like the new tenant-wide external recipient limit in Exchange Online constrain sending high volume communications, even if you add all the external email address as mail contacts. Used properly for internal communications, Outlook newsletters have the potential to be very successful.
So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across the Microsoft 365 ecosystem. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.
Microsoft Graph PowerShell SDK V2.26.1 Remains Flawed
Microsoft Graph PowerShell SDK Problems Means that Reputation Won’t be Easily Fixed
On February 25, 2025, I described how bugs in V2.26 of the Microsoft Graph PowerShell SDK made the software unusable. Late the same day, Microsoft pushed out V2.26.1. After a week’s testing, the signs are that the new version solved some of the problems seen in V2.26. However, bugs are still present in V2.26.1, including licensing failires and a nasty “invalid JWT access token” issue encountered when running the Connect-MgGraph cmdlet in an Azure Automation runbook. This error means that Connect-MgGraph failed to authenticate with Entra ID to secure an access token to allow the runbook to access data. It’s a pretty fundamental problem that’s accompanied by other issues reported on the Microsoft Graph PowerShell SDK GitHub issues page.

Given the current state, my advice is to stay on V2.25 until we know that a solid newer version of the Microsoft Graph PowerShell SDK is available.
On the surface, progress in squashing bugs is happening, and we can carry on using the SDK to generate automation solutions for Entra ID and Microsoft 365 as normal. Alas, that’s not a great plan At least, if Microsoft continues to develop the Microsoft Graph PowerShell SDK as before, exactly the same problems will appear in future versions. Lack of testing and poor communication will lead to more bugs and heightened customer dissatisfaction as Microsoft replays what happened with V2.14, V2.17/18, and now V2.26.
The Question of Testing to find Microsoft Graph PowerShell SDK Problems
Customers who experienced problems after installing V2.26 can justifiably ask what testing happens during the release cycle for a new version of the Microsoft Graph PowerShell SDK. I’m sure that some testing happens. The problem is that the testing has proven ineffective at picking up problems with heavily-used cmdlets like New-MgGroupMember and Send-MgUserMail, both of which were obviously flawed in V2.26.
It’s hard to test PowerShell cmdlets. Your use of a cmdlet might not be the way I use a cmdlet (the flexibility of PowerShell can be its own worst enemy at times). One method might be to test each cmdlet using the examples in the documentation. This sounds feasible, but it’s not. First, not all cmdlets have documented examples. Second, when examples exist, the code invariably reflects the simplest use of the cmdlet. For instance, the Send-MgUserMail cmdlet would have passed a test in V2.26 in terms of being able to send a simple message; its problems were revealed with moderately complex HTML body parts and attachments.
Third, there are just too many cmdlets to test. For V2.26.1, the number is 39,878 cmdlets (11,445 production (V1.0) and 28,433 beta). The SDK spans 38 sub-modules for the production cmdlets and 43 for beta cmdlets. These numbers grow over time as new Graph APIs appear.
Remember that the Microsoft Graph PowerShell SDK cmdlets are created by the Autorest process, which reads the Graph API schema and metadata to discover the resources and methods used to access data. The result is a cmdlet for every API. Some cmdlets or their documentation are flawed due to errors in the Graph metadata.
The challenge is to improve the quality of SDK cmdlets and documentation by making sure that the foundation (metadata) is right, and the cmdlets work the way that they should for every release. Developers don’t like surprises, and they especially don’t like when code that works with one version fails in another.
The Need for Crystal Clear Communication about Microsoft Graph PowerShell SDK Problems
The engineering group for the Microsoft Graph PowerShell SDK needs to improve its communication dramatically. Apart from some brief release notes (probably not read by many people), the bomb that lay within V2.26 was not discussed. The bomb was the impact on Azure Automation runbook due to the retirement of support for .NET6 and .NET7.
The release notes for V2.26.1 are equally terse. It’s possible to understand that the SDK rolled back to support .NET6 to address the Azure Automation issue, but not even the full changelog adds much value for anyone who’s not a professional developer, familiar with the SDK structure, and knows how to manage GitHub repositories.
The thing that seems to be forgotten is that many Microsoft 365 tenant administrators use the Microsoft Graph PowerShell SDK. Usage of the SDK is going up due to the imminent retirement of the MSOL and Azure AD modules.
V2.25 of the Microsoft Graph PowerShell SDK clocked up 3.6 million downloads compared to 1.18 million for V2.24. These are higher download numbers than for the Exchange Online management, Teams, SharePoint Online, or Entra modules and should be enough to prove the popularity of the SDK.
Tenant administrators are not developers. Most will have a passing knowledge of GitHub, and few will be able to trace the development of a new version through a GitHub changelog. What’s needed is clear, concise, and explicit explanations of what has changed in a new version, what impact this might have on existing code (if any), and any steps a tenant administrator must take to ensure that their code will continue to work.
It’s not too much to ask for people who create code that’s used by millions of people to communicate clearly with their audience. The temptation to use GitHub to generate release notes from change logs might be overpowering, but it’s simply not good enough. Context is everything, and bald statements that such and such a component was updated to fix bug number 3133 is never going to be an example of good communication.
Learning from Coping with Many Microsoft Graph PowerShell SDK Problems
I hope Microsoft learns from the V2.26 fiasco. It was a debacle created from their own making due to perfectly avoidable circumstances. To their credit, the SDK developers scrambled quickly to fix problems and get V2.26.1 out the door, but that’s still no reason for inflicting so much heartache on what should be their most fervent admirers.
There’s no doubt that the Microsoft Graph PowerShell SDK is a great tool for Microsoft 365 automation, like creating SharePoint pages from an RSS feed (just one example). Being able to interact with multiple workloads through a single SDK makes the pain somewhat bearable, except when it happens frequently. Trust in the PowerShell SDK was degraded by the V2.26 experience. I hope we see progress to allow SDK fans to build that trust back again.
Need some assistance to write great PowerShell scripts for Microsoft 365? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.
Office 365 for IT Pros March 2025 Update
Subscribers Can Download Files for Monthly Update #117 Now

The Office 365 for IT Pros author team is delighted to announce the availability of the March 2025 update for the Office 365 for IT Pros (2025 edition) eBook. This is monthly update #117. Readers can check the update number of the book on the inside front cover. Details of the update are available in our change log.
This update also includes update #9 for the Automating Microsoft 365 with PowerShell book, the most complete and comprehensive coverage of using PowerShell to create solutions based on Microsoft 365 data. Despite the recent quality problems for the Microsoft Graph PowerShell SDK, we still have strong faith in the usefulness of the PowerShell SDK and the ability it gives tenant administrators to access and use data across a wide range of workloads.
Subscribers can download the updated files from their Gumroad account or by using the download link in the receipt they received by email following their original purchase. The link in the receipt always accesses the latest files. See our FAQ for more information about how to download updates.
The Demise of Skype
The flux within the Microsoft ecosystem was dramatically illustrated by last Friday’s announcement that Microsoft will close down the Skype service on May 5, 2025. While the only impact on Microsoft 365 tenants will be the retirement of Skype interoperability with Teams (MC1019985, 28 February 2025), the demise of Skype is a sign of the ruthlessness that can happen when large corporations seek savings to invest elsewhere. Anything that doesn’t generate a good return is likely to be canned, which is what happened with Viva Goals late last year.
On a personal level, I don’t think I shall miss Skype too much. The app is installed on my PC and phone, but I don’t think I have made a Skype call in a year or more. Microsoft wants people to move to Teams personal (aka Teams for free), which I also have. However, I don’t use Teams personal for calling either because of the ubiquity of WhatsApp and the easy availability of low-cost data eSIMs around the world.
Some have pointed to the $8.5 billion price paid for Skype in 2011 and wondered if Microsoft got value for the purchase. I think that they did. The challenges of scaling up Teams to deal with the demand for online meetings when the pandemic happened were huge, and the experience of running the Skype backend was invaluable (according to Rish Tandon, who ran Teams engineering at the time). It can be argued that the pandemic made Teams and propelled its development to a point where it now supports 320 million monthly active users (the latest figure from October 2023). Without the success of Teams, would the Microsoft cloud now be at an annualized run rate of $163.6 billion? In that light, the investment in Skype seems like a rounding error.
Microsoft 365 Security for IT Pros
We are often asked about updates for the Microsoft 365 Security for IT Pros eBook. The facts are that we have nothing to do with this book. After sharing some of our processes and procedures to help the author team get up and running for the original edition in 2020, we have had no interaction with the book since. The latest edition appeared in 2023 and it seems like some difficulties have occurred in keeping the material updated.
We understand the book update challenge very well because publishing a monthly update takes a lot of time and effort. Muscle memory makes the task a little easier after 117 updates, but it’s still not as easy as some might think.
On to Update #118
Heading into March, we have started work on update #118. This will be the ninth update for Office 365 for IT Pros (2025 Edition), so it’s time to start thinking about the next edition. We typically publish a new edition on July 1 each year and I don’t anticipate any change to that cadence. But the writing team will get together at the Microsoft HQ in late March for the annual MVP summit and I am sure we’ll have a chat about the next version then. Stay tuned!
Stay updated with developments across the Microsoft 365 ecosystem by subscribing to the Office 365 for IT Pros eBook. We do the research to make sure that our readers understand the technology. The Office 365 book package includes the Automating Microsoft 365 with PowerShell eBook.
New PAYG Service to Classify Historical SharePoint Data
On Demand Classification Processes Cold Files to Find Sensitive or Confidential Information
Message center notification MC1013459 (21 February 2025) announces that Microsoft will introduce a new Information Protection service to find and classify “historical data in SharePoint Online and OneDrive for Business,” or as Microsoft 365 roadmap item 475062 says “scan cold files.”
The idea seems to be that tenants might have a bunch of files in SharePoint and OneDrive that have never been scanned or not been scanned in some time. Purview solutions like Data Loss Prevention (DLP) and Information Protection (sensitivity labels) tend to operate against active files created and edited by users. Cold files gather the digital equivalent of dust and might never be accessed, and it’s possible that those files contain many types of sensitive data.
Closing the gap by finding and classifying the information discovered in “cold files” is what the new On Demand Classification service is all about. By processing historical data, an organization can make sure that the files present in the tenant are classified appropriately. Once classified, policies can be automatically invoked to apply actions like assigning sensitivity or retention labels to files.
Preview of On Demand Classification in March 2025
Perhaps because trainable classifiers are an important method for tenants to find information that’s specific to their business, On Demand Classification is available through the classifiers section of the Purview DLP solution.
Microsoft expects to begin the deployment of a preview version of the code to targeted release tenants in late March 2025 with the goal of attaining general availability in mid-June 2025. Dates have been known to slip in the past, especially with new services, so treat these dates with caution.
Pay As You Go On Demand Classification
The On Demand Classification service is an example of the kind of new functionality that Microsoft bundles into its Office 365 E5 and Microsoft 365 E5 products to tempt customers to upgrade. That’s not the situation in this case. On Demand Classification is the first Purview Pay-as-you-go (PAYG) service.
PAYG is not unknown in Microsoft 365. SharePoint Online has PAYG services like Microsoft 365 Backup, Microsoft 365 Archive, and Document Translation, all of which require customers to have an Azure subscription attached to a valid credit card to pay for metered resource consumption. Other examples include the Graph API to apply sensitivity labels programmatically to Office documents and PDF files in SharePoint Online and OneDrive for Business and the export APIs for Teams and Exchange Online.
The case can be made that all the PAYG offerings are optional and not mainline services. They fill niches that not every Microsoft 365 customer wants or needs. It would be possible to include these services in the Microsoft 365 E5 product, but likely at an increased cost for everyone. Overall, it seems fairer for Microsoft to do the software engineering to create the solutions and realize a return by selling metered services to customers that need these capabilities.
No Details about Service Limitations
Microsoft hasn’t released any details about potential service limitations like the 100,000 items per day maximum an auto-label policy (sensitivity labels) can process within a tenant. We don’t know what kind of cold file types can be processed or if specific policy-invoked actions are restricted to certain types. The screenshot released by Microsoft (Figure 1) doesn’t give any insight into how long a scan might take or how long the subsequent processing might require.

All will be revealed when we get the software to test sometime in March 2025. Of course, some historic data must be found for On Demand Classification to process, but I’m sure that I have some old articles or book files hanging around that deserve to be heated up.
So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across the Microsoft 365 ecosystem. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.
SharePoint Online Adds Support for Sensitivity Labels with User Defined Permissions
Opens Access to UDP-Protected Files to Search, eDiscovery, and DLP – but not Copilot
Originally announced in preview in an August 1, 2023 technical community post, message center notification MC1013467 (21 February 2025) contains the good news that SharePoint Online will deploy support for sensitivity labels with user-defined permissions (UDP) in mid-March 2025. The reason why this development is important is that SharePoint Online support for UDP enables support for these files in content searches, Purview eDiscovery, and Purview Data Loss Prevention (DLP).
Configuring Permissions for Sensitivity Labels
Most sensitivity labels that protect files with rights-management based encryption use permissions configured by administrators. Permissions are formed by a set of usage rights that dictate what level of access an authenticated user has to a file. The same permissions apply to all files that receive a label with preconfigured access.
User-defined permissions allow file owners to assign different permissions for different files. To allow this to happen, administrators must configure a sensitivity label to support UDP (Figure 1).

After the label is published to make it available to users, they can assign the label and configure permissions for files (Figure 2). UDP labels are visible in Office web applications but can only be set by Office desktop applications.

Clicking more options reveals additional controls for a user to assign to protect a file, including an expiration date (which doesn’t pick up the date format configured for the workstation) for the permissions, a contact email address to request additional permissions, and whether a user must be online to validate their permission before they can open a file. The last option, to access content programmatically, allows Word and Excel to run code within a protected document.

Support for Microsoft Search
The initial SharePoint support for UDP-protected files previewed in August 2023 was limited. The big issue remained that files with UDP labels stored in SharePoint Online or OneDrive for Business couldn’t be indexed by Microsoft Search because Search had no way to gain access to file content (metadata for UDP-protected files is always indexed). This is important because Microsoft Search is an essential component for other services such as eDiscovery. In a nutshell, no indexing meant that UDP-protected files were invisible outside SharePoint Online.
The news announced in MC1013467 addresses the problem, but in a very focused manner. Although the number of UDP-protected files stored in SharePoint Online is likely a very small percentage of the billions of new files created daily, there’s no way that a trawl across all sites to find and process UDP-protected files could work in a practical sense.
To solve the problem, SharePoint Online processes newly-created UDP-protected files from mid-March 2025 to make their content accessible to Microsoft Search. Once indexed by Search, the file content is available to other Microsoft 365 workloads like eDiscovery. During the indexing process, SharePoint interprets the permissions assigned to a file by the author to ensure that those with relevant permissions can engage in co-authoring. In addition, SharePoint Online and the Office apps need permission to access the file before the autosave feature can work. It takes a little time to process a new file after it is uploaded to SharePoint Online. Microsoft reckons on ten minutes, but I have experienced longer delays before features like autosave work.
Older files stored in sites remain inaccessible to SharePoint Online until the next time they are edited. At this point, SharePoint processes the file content to make it searchable. Over time, the idea is that the number of inaccessible UDP-protected files will gradually decrease, and the problem will go away. Once a file is processed by Search, it becomes available to content searches, eDiscovery, and DLP.
Even when UDP-protected files are processed by Microsoft Search, MC1013467 says that “files with labels configured for user-defined permissions will continue to not be available for Microsoft 365 Copilot processing.” In other words, although Search can find UDP-protected files, Copilot still does not have the necessary permissions to load content from those files to use when generating responses to user prompts.
No Big Change for Users in the Immediate Future
From a user perspective, the update for how SharePoint Online processes UDP-protected files won’t mean dramatic change in the immediate future. UDP sensitivity labels might become more popular and widespread, but that’s a process that needs time because it must be factored in the organization’s information protection policy, which is probably currently based on preconfigured permissions. Administrators will need time to absorb the news and figure out how and if UDP-protected files bring value to the business before they create and publish UPD labels.
Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.
Microsoft Removes Reactivation Fee for Archived SharePoint Sites
No Microsoft 365 Archive Fee to Reactivate Sites After March 31, 2025
In a February 20, 2025 announcement, Microsoft said that they will remove reactivation fees for archived SharePoint Online sites. Some tenants will see the reduction in fees in early March and the change will roll out gradually worldwide for completion by the end of March 2025.
When Microsoft launched Microsoft 365 Archive, they charged $0.60 per GB to reactivate a site by moving its content from “cold” (long-term, archived) storage to “hot” (online, immediately-accessible) storage. Reactivation is immediate for sites archived within the last week, while sites archived for longer take approximately 24 hours to come back online.
Following the removal of the site reactivation fee, Microsoft will only charge the ongoing monthly storage cost of $0.05/GB. Storage fees don’t apply when they can be offset against the tenant’s unused SharePoint Online storage quota, so depending on how many sites they archive and how much content exists in those sites, some organizations might be able to use Microsoft 365 Archive for free.
A restriction does apply in that reactivated sites cannot be moved back into the archive for four months after reactivation. Microsoft says that the restriction is there to stop constant movement in and out of archive storage.
Keep Material Online but Prevent Copilot Access
One of the nice things about archiving sites is that it makes site content inaccessible for Microsoft 365 Copilot. There’s nothing worse than having AI-generated results being polluted by old, obsolete, and probably misleading information, and even if steps are taken to stop Copilot using content in its responses, Copilot can still find and use document library metadata because it exists in Microsoft Search.
I can’t think of a downside to moving old sites into the archive if you want to keep the material stored in the sites. Archived sites are still accessible for eDiscovery, the storage costs are a lot lower than hot online SharePoint storage, and now you can reactivate archived sites free when necessary.
Archived OneDrive Accounts
But you won’t be able to reactivate archived OneDrive for Business accounts free of charge because Microsoft excludes these objects from the removal of reactivation fees. The big idea behind automatically archiving unlicensed OneDrive for Business accounts is to force organizations to do something with accounts that might have been around for ten years or more. The unlicensed OneDrive accounts occupy valuable online storage and because Microsoft encourages the use of OneDrive for Business to hold all manner of files from Teams meeting recordings to PowerShell modules, a significant amount of storage can be occupied.
Microsoft released a report to help tenant administrators decide how to deal with unlicensed OneDrive for Business accounts in August 2024. By now, administrators should have a good handle on the unlicensed accounts within the tenant and know whether they will let automatic archiving happen (and be willing to pay the ongoing storage fees) or take action to remove the unlicensed accounts.
According to recently-revised Microsoft documentation, unlicensed accounts fall into two categories: those unlicensed before February 17, 2025, and those unlicensed afterward. The first batch includes all the historically unlicensed accounts. By April 25, 2025, these accounts will be in read-only mode to prepare them to move into the archive. This process will happen in the background and the unlicensed accounts will be archived by May 16, 2025, including the set shown in Figure 1. Once archived, tenants must pay to reactivate unlicensed OneDrive for Business accounts.

OneDrive for Business accounts that become unlicensed now are placed into read-only mode sixty days after they become unlicensed (for instance, the owning user account is deleted but the OneDrive data is kept by a retention policy, or the user account loses its license to allow them to use OneDrive for Business).
Thirty-three days afterward (93 days after the removal of the license), SharePoint Online will either move to the OneDrive account into the archive or into the recycle bin. Movement into the archive happens when a retention policy applies to the owner’s account.
Lower Fees are Always Appreciated
It’s good that Microsoft has removed the site reactivation fee. While not a lot in the overall scheme of things, getting rid of fees always encourage more use of facilities, and using Microsoft 365 Archive to store old material that the organization cannot remove is a good tactic. Some might question why the same logic doesn’t apply to archived OneDrive for Business accounts. That’s for Microsoft to answer, but I bet that they just want to get people used to the idea of paying to keep old OneDrive content online before they move on charges.
Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.
Microsoft Graph PowerShell SDK Runs into Choppy Waters
Graph SDK V2.26 Issues Make the Software Unusable
The Microsoft Graph PowerShell SDK is becoming increasingly popular. With over 3.5 million downloads of the previous version, a new release was bound to be a major event., especially after a three-month delay since Microsoft released V2.25 on November 21, 2024 (the SDK usually follows a monthly release cadence). V2.26 duly arrived five days ago.
Alas, you should avoid and not install V2.26. The release is buggy, exhibits little evidence of being tested before launch, and creates huge operational problems for Azure Automation-based runbooks.
The Cracks Appear
I installed V2.26 soon after it appeared in the PowerShell Gallery. I have a large number of PowerShell scripts and runbooks based on the Microsoft Graph PowerShell SDK and it didn’t take long before cracks appeared. For example, HTML messages generated by the Send-MgUserMail cmdlet didn’t display property in multiple email clients, including Outlook classic, the new Outlook, OWA, and Proton (Figure 1).

Another issue was that the Send-MgUserMail cmdlet failed to process attachments. The script I published last week to show how to add and send multiple attachments with Outlook messages failed spectacularly. Many production scripts use Send-MgUserMail to generate and send HTML formatted messages, so this issue was a big problem for a new release.
To be fair to Microsoft, they quickly fixed the two Send-MgUserMail issues. However, these weren’t the only problems. For instance, the New-MgGroupMember cmdlet failed because of an “invalid URL format” generated by an odd value appended to the request URI created for the POST request to add a new group member.
As is my norm, I reported the issues as I encountered problems (anyone who finds a problem with the Microsoft Graph PowerShell SDK, can report the issue online). After a couple of hours, it was evident that V2.26 was in bad shape and practically unusable.
The Big Issue
Discovering several Priority 1 issues in a short period is bad enough; finding that V2.26 had dropped support for .NET6 and .NET7 without warning delivered the coup de grace. This news is a bullet point in the Microsoft Graph PowerShell SDK release notes, which is nice if you read that information and understand the full consequences of the removal. Microsoft delivered no context for the change and no commentary as to what might be the effect.
Issue #3151 was the first report that came in to advise that Azure Automation runbooks had stopped working with V2.26. As you might recall, before Microsoft Graph PowerShell SDK cmdlets can be used with Azure Automation, the modules containing the cmdlets must be loaded as resources into the automation account. You can then create runbooks based on a specific PowerShell version and use the cmdlets in those runbooks.
Azure Automation supports runbooks based on PowerShell V5.1, V7.1. and V7.2. Sometimes a module only supports V5.1 (the SharePoint Online management module is an example), but usually it’s recommended to use PowerShell 7. The problem introduced with the new version of the SDK is that PowerShell V7.1 and V7.2 don’t use .NET 8. Because the SDK dropped support for .NET6 (which reached end of support in November 2024) and .NET7 (which reached end of support in May 2024), Azure Automation and the Microsoft Graph PowerShell SDK are now at odds with each other.
To demonstrate, I created a new automation account and loaded the Microsoft.Graph.Authentication module into the account (Figure 2). This module is the foundation of the Microsoft Graph PowerShell SDK because all other modules have a dependency on it. Note that the runtime version is PowerShell 7.2.

Next, I created a very simple runbook to sign into the Graph using a managed identity (Figure 3). The only processing performed by the runbook is to sign in and report the authentication context.

When I ran the runbook, it failed because the runbook couldn’t load an assembly it needed to run the Connect-MgGraph cmdlet. This isn’t surprising because PowerShell 7.2 is looking for a file that it doesn’t support or isn’t available.

Stay with V2.25
You won’t have a problem if automation accounts keep on using V2.25. You will have problems if you upgrade automation accounts to use the V2.26 modules or create new automation accounts and load the V2.26 modules (which is what Azure Automation offers) as account resources. Some workarounds have been suggested, such as using the Azure Runtime Environment, but that’s a preview feature and I don’t recommend using it in production. You’re safer by leaving the V2.25 modules in place and not attempting to upgrade.
A Big Mess
It’s obvious that V2.26 of the Microsoft Graph PowerShell SDK is a big mess. It’s not the only fiasco in SDK history as previous problems were experienced in V2.14 and V2.17/V2.18 in 2024. It’s obvious that Microsoft didn’t test this release thoroughly before pushing it out the door. It’s also clear that Microsoft failed to appreciate or communicate the impact of removing support for .NET6 and .NET7 on Azure Automation. I don’t recall seeing a support article that clearly outlines how V2.26 affects Azure Automation that might convince customers to pause upgrading to V2.26.
Although the two .NET releases are retired, PowerShell in Azure Automation hasn’t moved on to support PowerShell V7.5. Perhaps that’s because the Azure Automation development team know that dropping support for PowerShell V7.1 and V7.2 might break many production runbooks. Still, that’s an internal issue for Microsoft to work out between the development groups. Customers shouldn’t have to experience the downside of the lack of coordination and planning that obviously exists within the world’s largest software company.
If you’re one of the 150,000 who downloaded V2.26 SDK, it’s time to bin it and revert to V2.25. At least that version works.
Need some assistance to write and manage PowerShell scripts for Microsoft 365? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.
Tracking Down Bootleg Copies of Office 365 for IT Pros
Free Downloads of Office 365 for IT Pros Pop Up Around the Internet
Ever since electronic copies of books have existed, people have posted illegal copies of books on websites across the internet. The copies are illegal because they infringe the copyright holder’s rights and are not paid for. In the past, publishers took most of the loss caused by unpaid copies. With the gradual spread of self-publication, which is what we do for the Office 365 for IT Pros and the Automating Microsoft 365 with PowerShell eBooks, the loss falls directly on authors.
Figure 1 shows a typical example of an illegal copy of the PDF for the 2023 edition available for download from a site called Dokumen.Pub, which is apparently based in Canada.

To some publishers, tolerating some illegal copies is part of the cost of doing business. To others, it’s an affront to the work done to create the content. We’re in the middle. We don’t have the time to expend a lot of effort searching for and attempting to remove illegal copies, but when we do, we like the administrators of the hosting sites to remove the illegal copies quickly.
Tracking Down Free Downloads of Office 365 for IT Pros
Most of the time, we are told about illegal copies. Occasionally, we search for illegal copies. Either way, when we find an illegal copy, we contact the site administrator and try to make a Digital Millennium Copyright Act (DMCA) notification to let the administrators know that infringing material exists on their site. Usually, sites make this process easy by providing an online form to allow a copyright holder to assert their ownership of content.
After that, it’s a matter of waiting. Some sites take copyright infringement very seriously. They respond within a day and take down the illegal copy without a quibble. In other cases, sites like Scribd.com refer the DMCA notification for review by an internal copyright abuse team, and the process takes much longer. It’s right and fair that complaints should be reviewed and dealt with fairly, but from our perspective, it seems like reviews could be completed faster. In any case, it is what it is. Figure 2 shows an illegal copy of the 2023 edition that was available for download on Scribd.com (now removed).

Just a word of warning: some download sites are vectors for malware. If you download files from these sites, you might receive more than you expect.
What Can Be Done About Illegal Downloads of Office 365 for IT Pros
The answer is precisely nothing. The internet has some dark corners where people do stuff that they shouldn’t. This is just a mild example. The best defense that we have is our monthly updates. Take the illegal PDF shown in Figure 2. The downloadable file is for update #86. The current version of the book is #116 and we are working on monthly update #117. A bunch of stuff has changed in Microsoft 365 over the last 30 months, none of which is covered in the illegal material. Another thing that’s noticeable is that none of the PDFs available online have the stamped email address of the original purchaser. This could be because the person who uploaded the file removed the stamped address, or it could be because Gumroad failed to stamp some PDFs in the past. As we noted last month, we believe that the issues with PDF stamping are now resolved. Whether the stamps will make any difference remains to be seen.
All we can do is ask anyone who’s considering uploading a PDF is to please reconsider and ask yourself why you propose to share intellectual material that doesn’t belong to you. It’s not like you help anyone. Some might learn something from outdated material, but the content will eventually age out or be taken down.
Keep Authors Interested
What those who upload book PDFs really do is remove the desire of the authors to continue working on a project. If our work is not respected and deemed essentially to be worth nothing, then we’ll stop producing our eBooks. If something is worthwhile, you should pay for it. And we think that there’s enough value in our books to justify a fair payment, especially considering our commitment to continually update the books to add new content, remove old material, and address any issues that people find.
We publish free information for all to learn from on this site and we share a bunch of PowerShell scripts in our GitHub repository to help people master PowerShell for Microsoft 365. But the site and the repository exist to support the books. If the book publishing project ceases, everything stops, and that would be a bad thing.
Don’t be a bad guy. Keep authors interested in generating great content by respecting copyright and intellectual property. Support us by subscribing to Office 365 for IT Pros and get two great books that are updated monthly! And if you come across any illegal downloads of Office 365 for IT Pros, please let us know (post a comment here), and we’ll take care of the paperwork.
Another Nail in the Exchange Web Services Coffin
Microsoft Clamps Down on EWSEnabled Tenant Setting to Enable Exchange Web Services
Exchange Web Services (EWS) is scheduled for retirement on October 1, 2026. Although October 2026 is still 19 months away, retiring an API that was heavily used at one time requires time and patience to expunge every trace of the API. Microsoft has already removed EWS from clients like Outlook for Mac and is working through the steps to remove EWS from its other applications.
Independent software developers have received clear directions that they should replace EWS with the Microsoft Graph. The acknowledged gaps in functionality that currently exist in the Microsoft Graph are being closed, with the latest example being the introduction of the Exchange mailbox import-export API (see MVP Glen Scales’ commentary on the new API).
Now Microsoft is preparing for the final removal by clamping down on the organization setting which controls whether EWS is enabled or disabled within a tenant. In a February 20, 2025 post, Microsoft says that the organization-level EWSEnabled flag will play a more significant role than it has done in the past. The change is being made to make it easier for Microsoft to disable EWS across Microsoft 365.
Enabling EWS for a Mailbox
In the past, it was sufficient for administrators to set the EWSEnabled flag for a mailbox to $true to allow the mailbox to use EWS in apps and clients. This condition existed because the mailbox setting has precedence over the setting in the Exchange Online organization configuration and, by default, the organization setting is null.
Get-OrganizationConfig | Select-Object EWSEnabled EwsConfig ---------
The only time administrators set the organization-level EWSEnabled setting is if they wanted to block EWS throughout the tenant. Usually, this need didn’t arise because it was sufficient to set EWSEnabled to $true on the mailboxes that needed access to EWS. For instance, because EWSEnabled is $true for the James Ryan mailbox, that mailbox can use EWS even though the organization setting is null.
Get-CasMailbox -Identity James.Ryan | Select-Object EWSEnabled EwsEnabled ---------- True
The Big Change
What’s changing is that Exchange Online will only permit a mailbox to use EWS if both the organization and mailbox settings are $true. The old situation where the default null value at the organization level is sufficient to allow access is no longer in force. Tenants that want to use EWS to the bitter end must now explicitly enable EWS in the Exchange Online organization configuration:
Set-OrganizationConfig -EWSEnabled $true
You can see where Microsoft is going. By forcing the relatively small number of tenants to explicitly allow EWS by updating the organization configuration, Microsoft is preparing for the big turn-off when they will update the organization configuration to set EWSEnabled to $False and block any further changes to the setting. It’s an elegant and effective way of making sure that the turnoff happens on October 1, 2026.
Problems might arise for tenants that have the organization configuration set to $false already and still have a small number of mailboxes enabled for EWS. The precedence enjoyed by the mailbox setting allows these mailboxes to access EWS, but once Microsoft removes the precedence, those mailboxes will lose the ability to use EWS.
The solution is to update the organizational setting back to $true. It might seem counterintuitive to allow EWS for the tenant, but the existing access setting for mailboxes will then kick in and only those mailboxes enabled for EWS can continue making EWS API requests.
Check Mailboxes Enabled for EWS
Given that we’re on the final glidepath to the retirement of EWS, it’s a good idea to validate that the set of mailboxes enabled for EWS declines over time. That way you’ll know if the dependency on EWS within the organization is reducing and understand why some mailboxes continue to need EWS. To help, I put together an Azure automation runbook that looks for EWS-enabled mailboxes and emails the details using Exchange High Volume Email (HVE). The email (Figure 1) serves as a nagging reminder for tenant administrators to minimize the set of mailboxes enabled for EWS. Using Azure Automation means that it’s easy to schedule the job to run weekly (or whatever period makes sense) as the clock ticks down to October 1, 2026.

You can download the code from the Office 365 for IT Pros GitHub repository. See this article for more details about running Exchange Online PowerShell in Azure Automation, and this article about using HVE in Azure Automation.
I chose HVE rather than using the Send-MgUserMail cmdlet from the Microsoft Graph PowerShell SDK because there’s an odd bug that stops the latest version of Exchange Online PowerShell module working in version 7.2 runbooks. My Graph setup is based on PowerShell 7, so mixing and matching Exchange Online and the Graph SDK doesn’t work as smoothly as it should for now. Microsoft is aware of the issue. I believe it will be fixed in the next release.
You don’t need to use Azure Automation as a standard script will do the job too. It just seems to make sense to me to have Azure Automation run the job without human intervention. After all, I might forget to check…
Why Microsoft 365 Copilot Works for Some and Not for Others
I Can’t Wait for Agentic Experiences to Make Copilot Useful
We’re all on a journey to understand how to use artificial intelligence effectively to improve systems, lives, and human existence. If you pay for the necessary licenses, Copilot is everywhere within the Microsoft 365 ecosystem, both as helpers deployed in desktop apps like Word, Teams, and PowerPoint, and the possibility of custom agents for tenants to develop and deploy, albeit without the necessary tools to manage potentially thousands of agents created by citizen developers.
According to Microsoft CEO Satya Nadella, Microsoft wants to make it as simple for people to create agents than it is to create an Excel worksheet, which might mean the creation of the “highly customized agentic experiences” referred to in Microsoft 365 center notification MC985480 (January 22). I don’t quite know that phrase means, and the clarifying text that said it “means you can design unique prompts, connect to any LLM, and integrate these custom agents with Microsoft 365 Copilot” wasn’t much help either. When I asked Copilot, it struggled with the concept too (Figure 1). In any case, I’m sure that we’ll all be happy in our highly customized agentic world when it arrives.

Why Today’s AI Falls Short of its Hype
All of which brings me to a thoughtful article in the Tomorrow’s Blueprint blog entitled “Why Others Think AI Is a Miracle But You Think It’s Useless.” The author is Microsoft product manager Abram Jackson, now deeply involved in the development of Microsoft 365 Copilot. The core of the article is an assertion that:
“Today’s AI falls short of its hype for many due to three big reasons:
- It often doesn’t have the data it needs to work with
- Defining tasks precisely is very difficult
- There’s little AI can do other than give you text or images.”
Abram knows much more about AI than I do. I reckon that he has captured the problems faced by many organizations as they consider how to extract value from a potentially massive investment in Copilot licenses.
Without access to data, Copilot can do nothing. The magic of Microsoft 365 Copilot, if some exists, is the Microsoft Graph, or access to the documents, emails, and Teams messages stored within Microsoft 365. Yet the legacy of some older Microsoft decisions around collaboration strategy forced organizations to restrict SharePoint Search to stop Copilot revealing information to anyone who asked. As it turns out, it is hard to stop Copilot using data because even document metadata can reveal secrets.
I like the way Abram discusses the issue of defining tasks. Math works because the answer is either right or wrong. Copilot works very well when given well-defined tasks to do, like summarizing a meeting transcript or extracting tasks for people to consider. The same goes for scanning an email thread or summarizing a Word document. Generating text is less satisfactory unless the user is very precise in their prompt and grounds Copilot with some suitable input, like documents to work from. The promise of early demos where Copilot generated project reports and other material in the blink of an eye is never attained where loose prompting gives the AI free rein to indulge itself.
How People Need to Use AI
The summary is that to extract value from AI (and Microsoft 365 Copilot in particular), users must:
Understand if a task is valuable and not prone to hallucinations. Asking Copilot for Word to scan a document and decide if it is well-structured and how make improvements is valuable for many people who aren’t natural writers. Asking Copilot for Word to generate the initial document introduces the possibility of hallucinations.
Work to define the task precisely: Asking Copilot to do something very precisely with clear boundaries and guidelines will generate much better results than dashing off a quick prompt.
Translate the result generated by the AI into the form you need it to be. For chat, the introduction of Copilot pages has proven useful because it allows users to easily capture the output generated by Copilot for reuse. But will the slides generated by Copilot for PowerPoint be the type you need? Or can Copilot for Excel really perform the computations you want? Of course, they can, but only with practice and perseverance on the part of the human.
As Abram says, this approach “isn’t natural and it is time-consuming.” It comes about because Copilot is essentially an eager assistant that wants to work but will do stupid things unless you tell it precisely what to do and how to do it. Expanding on the example shown in Figure 1, adding context and direction to the prompt gives Copilot the chance to deliver a much better answer. Prompts can now be up to 128,000 characters, so there’s lots of room for comprehensive instructions.

The Bing Conundrum
One last point about data being available for Copilot to work with. I’m not sure about Abram’s statement that “hallucination is largely a solved problem for Microsoft Copilot.” I see odd stuff generated all the time. Abram justifies his claim by saying that “Copilot is trained to only respond with information it has been able to find through search.”
Copilot depends on Bing and Bing isn’t very good at searching. Take this website. Despite the ease in which Google has indexed and searched all my articles for years, Bing stubbornly refused to touch the site. I only discovered this fact when creating some declarative agents that used office365itpros.com as a source. Since then, the best efforts of WordPress support and my own attempts to navigate the online Bing webmaster advice have only just persuaded Bing to start indexing some pages. Some of the blocks are quite silly. One problem that caused Bing to refuse to index pages was the lack of an alt tag for a graphic in a sidebar.
If Copilot had better search facilities, it could generate better answers because it has better data to work with.
So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across the Microsoft 365 ecosystem. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.
Processing Multiple Message Attachments with the Microsoft Graph PowerShell SDK
When You Want to Add Attachments to Email
Many examples exist to show how to send a single attachment with an email using the Send-MgUserMail cmdlet from the Microsoft Graph PowerShell SDK. Microsoft’s documentation covers how to send a single attachment but is mute on how to process a batch of attachments. This is understandable because the need to send multiple attachments for a single message from PowerShell isn’t probably of huge importance when it comes to programmatically creating and sending email.
With that thought in mind, I set out to create some sample code to illustrate the principle behind adding multiple attachments to a message sent with the Send-MgUserMail cmdlet.
An Array of Attachments
The essential points to remember are:
- To include one or more attachments in a message, the attachment key must be present in the hash table that describes the message. The associated value is an array of attachments.
- Each attachment is represented by a hash table in the attachments array.
- The hash table for an attachment describes its odata type, file name, content type, and the base64-encoded content for the file.
Thus, the hash table for an attachment looks like this:
$AttachmentDetails = @{ "@odata.type" = "#microsoft.graph.fileAttachment" Name = $File ContentType = $ContentType ContentBytes = $ConvertedContent }
Adding Multiple Attachments
The first step is to find some files to attach. This code looks for files in a specified folder and checks the total file size to make sure that adding all the files as attachments won’t exceed 140 MB. The documented maximum message size for Exchange Online is 150 MB, but there’s always some overhead incurred from encoding, the message body, message properties, and so on.
$AttachmentsFolder = "c:TempAttachments" [array]$InputAttachments = Get-ChildItem -Path $AttachmentsFolder If (!($InputAttachments)) { Write-Host "No attachments found in $AttachmentsFolder" Break } $FileSizeThreshold = 146800640 # 140 MB in bytes $TotalFileSize = ($InputAttachments | Measure-Object -Sum Length).Sum $FoundSizeMB = [math]::Round($TotalFileSize / 1MB, 2) If ($TotalFileSize -gt $FileSizeThreshold) { Write-Host ("Total size of attachments is {1} MB. Maximum size for an Outlook message is 140 MB. Please remove some attachments and try again." -f $TotalFileSize, $FoundSizeMB) Break }
To prevent problems, the code won’t process the attachments if their total size is more than 140 MB and will report an error like this:
Total size of attachments is 182.14 MB. Maximum size for an Outlook message is 140 MB. Please remove some attachments and try again.
This avoids the problem when an attempt is made to send a message with oversized attachments, the Send-MgUserMail cmdlet will report:
Error sending message: [ErrorMessageSizeExceeded] : The message exceeds the maximum supported size., Cannot save changes made to an item to store.
The failure could occur because the mailbox that’s sending the message isn’t capable of handling such a large email. By default, Exchange Online enterprise mailboxes can send messages of up to 150 MB and receive messages of up to 125 MB (why the two values are different is debatable). To change these values for a mailbox, run the Set-Mailbox cmdlet:
Set-Mailbox -Identity Jane.Smith@office365itpros.com -MaxReceiveSize 150MB -MaxSendSize 150MB
Populating the Attachments Array
To populate the attachments array, the code creates a base64-encoded form of the file content and attempts to figure out the most appropriate content type. This is an optional property and Microsoft 365 can decide which format is best if you omit the property.
$FullFileName = $AttachmentsFolder + "" + $File $ConvertedContent = [Convert]::ToBase64String([IO.File]::ReadAllBytes($FullFileName)) $FileExtension = [System.IO.Path]::GetExtension($FullFileName) Switch ($FileExtension) { ".pdf" { $ContentType = "application/pdf" } ".docx" { $ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" } ".xlsx" { $ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" } ".pptx" { $ContentType = "application/vnd.openxmlformats-officedocument.presentationml.presentation" } ".jpg" { $ContentType = "image/jpeg" } ".png" { $ContentType = "image/png" } default { $ContentType = "application/octet-stream" } }
After processing an attachment, the code creates the hash table referred to earlier and adds it to the attachment array:
$MsgAttachments += $AttachmentDetails
The attachment array then becomes part of the message structure:
$Message = @{} $Message.Add('subject', $MsgSubject) $Message.Add('toRecipients', $MsgTo) $Message.Add('body', $MsgBody) $Message.Add('attachments', $MsgAttachments)
The final step is to call Send-MgUserMail to send the message. If everything works, it will arrive at its destination complete with a set of attachments (Figure 1).

Get the Structure Right and Everything Flows
Dealing with attachments through the Microsoft Graph PowerShell SDK is straightforward once you understand the structures used and how they are populated. It would be nice if the SDK cmdlet documentation covered this kind of thing better, but they don’t, so here we are.
You can download the script I used from the Office 365 for IT Pros GitHub repository. This article describes another example of using the Send-MgUserMail cmdlet.
Need some assistance to write and manage PowerShell scripts for Microsoft 365? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.
Update #9 for Automating Microsoft 365 with PowerShell eBook
Updated EPUB and PDF Files Available for Download
The Automating Microsoft 365 with PowerShell eBook is now at update #9. This is the March 2025 update. We release monthly updates for the PowerShell eBook around the middle of the preceding month to allow us the time to concentrate on preparing the monthly update for Office 365 for IT Pros.
The updated EPUB and PDF files are available to:
- People who bought Automating Microsoft 365 with PowerShell on its own.
- Subscribers to the Office 365 for IT Pros (2025 edition) eBook.
Please use the download link in the receipt emailed after your purchase to access the updated files. Alternatively, you can get the updated files through your Gumroad.com account. The update number (and month) is shown at the bottom of each page.
Continual Expansion of Content
The original version of Automating Microsoft 365 with PowerShell spanned about 120 pages. The book is now 300 pages (more in the paperback edition because it includes an index). When we removed the PowerShell chapter from the Office 365 for IT Pros eBook, we always knew that there was much more to say about using PowerShell with Microsoft 365. Over the last eight updates, we’ve added a ton of examples, mostly covering the use of Microsoft Graph PowerShell SDK cmdlets with workloads like Entra ID, Exchange Online, SharePoint Online, and Teams.
Update #9 continues the trend with new content covering topics like using the Sites.Selected Graph permission to control access to SharePoint Online sites, how to upload files to SharePoint Online, sending multiple attachments with Exchange Online, and using an upload session to process very large attachments. There are many other changes, rewrites, and enhancements scattered across the book, including a complete rewrite of our coverage of using Microsoft 365 PowerShell with Azure Automation.
Price, Price, Price
To reflect the increased value of the content included in Automating Microsoft 365 with PowerShell, we’ve increased the price from $12.95 to $14.95. Other books covering the use of PowerShell with Microsoft 365 are priced significantly higher, so we think that even the new price represents incredible value. We’re confident that no other book covers the number and variety of fully-worked out examples of how to use PowerShell to get work done with Microsoft 365.
We also increased the price of the paperback edition to $19.95. This is simply a function of the increased page count driving the cost we pay Amazon to print each copy on an on-demand. There’s nothing to stop anyone printing off the PDF version if you want a paper copy. The only issue you’ll run into is that the many hyperlinks (over 200 at the last count) we include in the book become unusable when printed. To get around the issue, we substitute plain-text links in the content of the paperback edition.
Subscribers of Office 365 for IT Pros don’t have to pay any extra for their copies of Automating Microsoft 365 with PowerShell.
Onto Update #10
Work has already started on update #10. We’re waiting for Microsoft to release a new version of the Microsoft Graph PowerShell SDK. V2.25 has been around for about three months now, which is much longer than the usual monthly release cadence (Figure 1).

I don’t know why Microsoft has delayed the release of V2.26. It’s certainly not to deal with the problem related to plain-text passwords reported last week. No doubt we will hear in time. In the meanwhile, the interesting thing about the information shown in Figure 1 is the dramatic usage growth for the SDK from 1.18 million downloads of V2.24 to 3.49 million downloads for V2.25. That’s probably indicative of an uptick in interest as tenants work to get off the soon-to-retire MSOL and Azure AD modules. Maybe all those folks upgrading scripts to use the Graph SDK could do with a good book?
Purview Retires Events Alert Capability from Unified Audit Log
Audit-Based Alerts Retired from March 25, 2025
Publish bad news on Friday is the advice for anyone who wants the news to create less of a fuss. Publishing the news late on Friday before a holiday the following Monday (U.S. Presidents’ Day) might do an even better job of suppressing criticism. Some will consider the announcement in Message center notification MC1006620 (15 February 2025) that “Purview will retire the event alerts capability within the Purview Audit solution on March 24, 2025” to be bad news. It all depends on whether you use alerts based on audit events to learn when something happens in a tenant.
Activity Alerts
Microsoft introduced activity alerts and alert policies soon after the introduction of the unified audit log in July 2015. The idea is simple. The audit log holds some extraordinarily valuable information about what happens in a tenant, but a busy tenant can generate hundreds of thousands of audit events daily. Human administrators don’t have the time to keep on checking if events of interest (for whatever reason) show up in the audit log. Computers are very good at checking data, and activity alerts are predefined checks against new audit events as workloads ingest data into the audit log. If an event of interest is found, Purview sends email to administrators to tell them about the event (Figure 1).

Better Tools to Analyze Audit Events Exist
Although useful (and still used), a case can be made that activity alerts have passed their sell-by-date. The unified audit log holds an increasing amount of data generated by workloads from Entra ID to SharePoint Online to Teams to Purview. Better tools exist to allow tenant administrators to monitor events of interest, including connecting Office 365 data to Microsoft Sentinel where the data can be analyzed along with information gleaned from other sources. Many organizations run background jobs to extract audit events from the unified audit log for ingestion into an external SIEM. There’s even a Splunk add-on to extract audit data for Microsoft 365. And if you want to involve AI, there’s Security Copilot to consider.
And if off-the-shelf software isn’t available, PowerShell can be used to extract and analyze audit events using either the Search-UnifiedAuditLog cmdlet or the AuditLogQuery Graph API. The signs are that Microsoft wants customers to use asynchronous Graph-based audit searches because these searches absorb fewer resources. Removing the monitoring of new audit events to be able to generate audit alerts seems to be another attempt to restrict the resources consumed by audit activity.
Using either the cmdlet or Graph query, the same kind of processing to find and email alerts for audit events of interest is easily done using a combination of PowerShell and scheduled Azure Automation jobs.
Confusion with DLP Alerts
Data Loss Prevention (DLP) policies can also signal alerts when policy rules detect violations. MC1006620 confuses the issue slightly by reassuring tenants that DLP alerts are unaffected by the retirement of audit-based activity alerts.
Further confusion comes about in the assertion that customers who want to retain audit-based activity policies should recreate them using DLP policies. Outside of scenarios like unusual file downloads, I’m not sure if this is even possible for some of the activities audit-based alert policies highlight. Microsoft says that they will invest their development resources on the alerts functionality within DLP, which is fine even if it might not cover everything that might be exposed in an audit event.
In any case, as noted above, a range of options exist to monitor audit events and signal alerts if something of interest is discovered.
Make sure that you’re not surprised about changes that appear inside Microsoft 365 applications by subscribing to the Office 365 for IT Pros eBook. Our monthly updates make sure that our subscribers stay informed.
Microsoft Graph PowerShell SDK Needs to Fix Its Password Problem
Graph SDK Plain Text Passwords are Unacceptable in Today’s Threat Climate

Many PowerShell developers are all too aware that time is running out for the Azure AD and MSOL modules. Microsoft will retire the MSOL module in April 2025 and the Azure AD module in Q3 2025. The result is that a lot of work is going on to upgrade scripts to replace MSOL and Azure AD cmdlets with equivalents from the Microsoft Graph PowerShell SDK or Entra module.
Microsoft launched the Entra module in June 2024 and made its V1.0 release generally available in January 2025. The Entra module is built on top of the Microsoft Graph PowerShell SDK. The major difference is that the Entra module comes with a set of hand-crafted cmdlets intended to mimic how Azure AD cmdlets work. I say hand-crafted because Microsoft engineers upgrade the automatically-generated versions created for the SDK to add support for features like piping.
The AutoRest process, which generates the SDK cmdlets, uses the metadata of the underlying Graph APIs to guide what it generates. That metadata is not constructed with PowerShell in mind, which is why the SDK cmdlets can be difficult to work with at times. For instance, the SDK cmdlets don’t support piping, which is a fundamental PowerShell feature. Output from SDK cmdlets is often a set of identifiers rather than human-understandable objects, and so on.
You could ask why Microsoft doesn’t intervene to add support for piping, make cmdlet output more useful, and address the other SDK foibles. One reason is the sheer number of Graph APIs that end up as SDK cmdlets.
[array]$Command = Get-Command -Module Microsoft.Graph* $Command.count 42474
The 42,474 cmdlets are broken down into 15,259 V1.0 and 27,215 beta cmdlets. Updating all the cmdlets in V2.25 of the Microsoft Graph PowerShell SDK would take enormous effort. The number of cmdlets grows with each version of the SDK to reflect newly-added Graph APIs. The automatic generation process would need to change (and testing of the generated cmdlets). Whether the world’s largest software company should do this is an argument that’s been going on for years.
All of which brings me to issue 3119 reported in the SDK’s GitHub repository reported by MVP Aleksandar Nikolić, a well-known PowerShell expert. The problem report is terse and accurate:
The Update-MgUserPassword command’s parameters, -CurrentPassword and -NewPassword, expect a string value instead of a SecureString value.
Using Graph SDK Plain Text Passwords to Update User Accounts
The Update-MgUserPassword cmdlet is designed to allow users to change their own password. For instance, this code updates the user who’s signed into an SDK interactive session.
$User = Get-MgUser -UserId (Get-MgContext).Account Update-MgUserPassword -UserId $User.Id -NewPassword "P@sswOrD" -CurrentPassword "Galway2020!!!"
If an administrator is changing someone’s password, they should use the Update-MgUser cmdlet.
$NewPasswordProfile = @{} $NewPasswordProfile["Password"]= "$!FDGmso13@" Update-MgUser -UserId $User.Id -PasswordProfile $NewPassword
Notice that in both cases, the password is in clear text. I don’t know how many tenants have coded solutions to allow users to change their own passwords and use the Update-MgUserPassword cmdlet, but I know that many have processes to change user passwords with Update-MgUser, so that’s the more serious problem.
By comparison, the equivalent cmdlets from the now-deprecated modules both take SecureString values when changing passwords.
$NewPassword | ConvertTo-SecureString -AsPlainText -Force Set-MsolUserPassword -UserPrincipalName $userPrincipalName -NewPassword $NewPassword -ForceChangePassword $true Set-AzureADUserPassword -ObjectId $User.Id" -Password $NewPassword
Using Secure Strings for Password Changes
Microsoft defines a SecureString as “text that should be kept confidential.” In terms of security, Microsoft says that A SecureString “provides more data protection than a string.” It seems like the right kind of protection passwords should have.
You might consider that there’s nothing much wrong here because the passwords are available to those who set them. However, it’s possible to have a script generate a password for an account and store it as a SecureString in a repository like Azure Key Vault, and have a different script read the password and use it to update an account. This method means that no password ever appears in plain text.
The Solution to Graph SDK Plain Text Passwords
The initial response from the SDK development team was that they follow the documentation for the underlying user: ChangePassword Graph API, which defines the input values in the request body strings. Referring back to the Graph API seems like a dereliction of duty. If everyone did that, we’d never see any improvements in software. Switching to support SecureString input for the SDK cmdlets is the right thing to do, even if it might break some existing scripts. That’s an acceptable cost to pay for better security.
Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.
How to Index and Search SharePoint Online Custom Columns
Custom Columns are One Part of the Mystery of SharePoint Search
Understanding how SharePoint Online search works is one of the learning curves faced by many Microsoft 365 tenant administrators. Because search is so important to SharePoint, this topic is well-covered ground for people who worked with SharePoint Server (on-premises). The magic that melds managed properties, crawled properties, mapping, custom cpolumns, indexes, and so on into search doesn’t hold any mysteries.
Things are different for those who come to SharePoint because of its core role within Microsoft 365. SharePoint Online is not the center of an ecosystem like SharePoint Server is. Like Exchange Online delivers email services, the cloud version of SharePoint takes on a completely different role as the provider of document management services to other workloads, like Teams. The different role doesn’t make search any less important. People still want to find files quickly and easily but competing demands from across Microsoft 365 mean that administrators sometimes pay less attention to the finer details of search. After all, search just works in the cloud…
Generally, SharePoint search does just work. Sometimes complexities do exist, like finding out how to find files with a specific sensitivity label. Although users mightn’t want to look for labelled files, administrators might need this knowledge to find labelled files in eDiscovery searches, and that’s why some knowledge about how search works is a good skill to acquire, even for non-SharePoint people. In my case, it helped me to maximize the advantage of creating a custom column for a site.
Creating a Custom Column
SharePoint Online is basically a big Azure SQL application based on a database holding many lists (tables). The lists hold data in items with the information for each item stored in columns (fields). SharePoint allows site administrators to define new custom columns at a site or library level. I use this feature to track the topic areas for articles I publish on different sites.
In general, defining custom columns at site level is recommended because the custom columns can then be used in any library. Figure 1 shows the properties of a custom site column called RAInfo.

After creating the site custom column, use the Add from existing site columns option in Library settings to add the column to a document library. Once the column is added to a library, it can be added to the view that exposes file metadata and edited there (or updated programmatically using a Graph API or SDK cmdlet).
Remaining in library settings, go to Indexed columns and add the new custom column as an index. SharePoint libraries support up to 20 indices.
Using Custom Columns
One of the nice things about custom columns is that SharePoint supports their use in filters. In Figure 2 we see that the filter picker shows the values entered for files in the RAInfo custom column. Selecting the desired values for the RAInfo column helps SharePoint to find and display files that meet the filter criteria. Of course, filtering only works if users remember to enter the necessary information for files.

Users can also input a custom column value (for instance, “RA001”) into the search box to search the library for matching files. Because the custom column is part of the document metadata, SharePoint search can use it to find files.
Making Custom Columns More Searchable
The search against document metadata finds matches against any property containing the value. A further step is required to allow searches against the custom column be more specific.
When you create a custom site column, SharePoint Online creates a managed property named using the form CustomColumnName + “OWSText.” In this case, the managed property is called “RAInfoOWSText.” SharePoint also creates a crawled property with the name OWS_Q_TEXT_CustomColumnName, or OWS_Q_TEXT_RAInfo in our case. The crawled property is what SharePoint search extracts from a site.
To allow search to use the managed property, it’s critical to map the managed property to the crawled property and wait for indexing to complete. Once indexing is complete, you can input RAInfoOWSText:RA001 into the search box. This command instructs SharePoint to search against the RAInfoOWSText managed property.
Not everyone will appreciate working with what seems to be odd column names. To solve the problem, go to Site Information, then View all site settings, and select the Search schema. You can now create an alias for the custom column, hopefully giving the column a name that makes more sense to regular users. In Figure 3, I’ve assigned RAInfo as the alias for the RAInforOWSText managed property.

Adding an alias to disguise some of the complexities of SharePoint search is a small but useful step to take. Now users can input search terms like RAInfo:RA003 (Figure 4) into the search box instead of RAInfoOWSText:RA003 and find the same information.

Take Your Time
One thing that you’ll discover when tweaking SharePoint search is that it takes hours and sometimes days for changes to become active in a site. Users need to populate values in the custom column and search needs to index those values, including respecting the changes that you might have made like adding an alias for a managed property. You can force the issue somewhat by requesting a reindex of the document library (in library advanced settings), but SharePoint Online can’t be rushed too much.
Take your time and everything will work in the end. At least, it did for me.
How to Use Bulk User Operations in Entra Admin Center
Update Multiple Entra ID Accounts in a Single Action
It’s perhaps a natural assumption that administrative consoles like the Entra admin center perform actions against singular objects. However, that’s not the case because the Entra admin center now boasts an upgraded edit menu which supports operations against multiple user accounts (Figure 1). As indicated by the admin center, the update is currently in preview.

The older bulk operations menu has options for bulk create, bulk invite, and bulk delete.
No Notification from Entra
The disappointing thing is that Microsoft 365 administrators might struggle to discover interesting news like this because the Entra development group don’t post notifications to the Microsoft 365 message center. Hearing about changes might depend on fortuitously seeing a message in a social media feed or reading an article like this. It’s odd that Entra doesn’t take advantage of posting notifications in the Microsoft 365 message center because Microsoft 365 is a significant workload for Entra ID that generates large amounts of revenue through premium licenses.
The only documentation for bulk Entra ID updates that I can find refer to the bulk operations menu and says “Bulk operations in the Microsoft Entra admin portal could time out and fail on large tenants. This limitation is a known issue due to scaling limitations.”
The recommended workaround is to use the Microsoft Graph PowerShell SDK. That’s certainly a good idea if you want to process large numbers of accounts. It takes a little while to master user account management with the Graph SDK, but once you understand the basic mechanism, everything clicks into place and scripting account management isn’t a challenge.
What You Can do to Update Multiple Entra ID Accounts
Using the options in the edit menu is easy. Select some accounts (which can be a mixture of member and guest accounts), and choose one of the supported actions to update multiple Entra ID accounts:
- Edit properties (Figure 2). Only certain properties can be edited.
- Add manager. Every account should have a manager
- Add sponsors. Account sponsorship is really intended for guest accounts. A flaw in the implementation means that the UI doesn’t reveal if the chosen accounts already have sponsors. There also doesn’t seem to be a way to cancel sponsor assignment if you decide not to select a sponsor. The perils of preview software…
- Add as members of a group.
- Add to administrative unit.
- Edit account status. This option changes the accountEnabled property for the selected accounts from Enabled to Disabled or vice versa.
- Revoke sessions with a forced sign-out. Affected user accounts must reauthenticate to reconnect.

As you might expect, any change made to a user account is captured in an individual audit record and is discoverable by searching the Entra ID audit log (Figure 3) or the unified audit log (after ingestion).

Update Multiple Entra ID Accounts is Goodness
The new edit menu option is an example of a change that’s surprising because it hasn’t appeared before now. Making changes to multiple accounts at one time is a great way to speed up administration. It avoids the need to use PowerShell to process one-off changes for small groups of users. However, I’d always use PowerShell for anything more complex because of the extra control it affords.
After all, the nice thing about PowerShell is that you get to choose how to implement functionality without waiting for Microsoft to add options to an admin center. Then again, good things come to those who wait…
Need some assistance to write and manage PowerShell scripts for Microsoft 365? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.
Use Protected Actions to Stop Attackers Hard-Deleting Entra ID Accounts
Enforcing Strong MFA Through Protected Actions Might Block Bad Actors
A January 25, 2025 blog about how attackers leverage the User.DeleteRestore.All Graph permission attracted my attention. The idea advanced is that if attackers can wreak havoc if they can secure the User.DeleteRestore.All permission. There’s no doubt that this assertion is correct. Attackers being able to permanently remove soft-deleted user accounts from the Entra ID recycle bin is enough to give any tenant administrator a severe headache.
Protected actions might offer some protection against attacker destruction. When I first covered this capability in May 2023, it was early days for the feature. Essentially, a protected action is associated with a conditional access policy through an authentication context so that any attempt to perform the protected access invokes the conditions set in the policy. For instance, the condition could be that the user must authenticate themselves with a strong phishing-resistant authentication method like a FIDO2 key or passkey.
The idea here is that the attacker might not be able to satisfy the authentication challenge with the required strong method and is therefore blocked from performing the protected action. That theory falls down if the attacker has gained sufficient control over the tenant to update conditional access policies (unless you block update access to conditional access policies with a protected action).
Creating a CA Policy for Protected Actions
Microsoft’s documentation for adding a protected action is straightforward. To add a protected action to guard against unexpected removal of soft-deleted user accounts, create a conditional access policy for Entra ID to invoke when a connection attempts to us an authentication context (basically, when something happens – like accessing a sensitive SharePoint Online site). The CA policy (Figure 1) is very simple and associates an authentication context with strong authentication. You could add other policy requirements to grant access to make it harder for attackers. For instance, require the use of a compliant device.

The magic comes when you link the authentication context with one or more permissions. The set of supported permissions for protected actions has grown over time and includes microsoft.directory/deletedItems/delete, the permission to permanently delete objects from Entra ID. Deleted user accounts normally remain in a soft-deleted state in the Entra ID recycle bin for 30 days following deletion. Soft-deleted means that the accounts are recoverable. However, if the account is removed from the recycle bin, it is hard-deleted and irrecoverable.
To link the authentication context, go to the Roles & admins section under Identity in the Entra admin center and select Protected actions. Find the authentication context to link, select the permission, and save (Figure 2).

You can’t link a protected action to multiple authentication contexts. If you make a mistake and link the protected action to the wrong authentication context, you must remove the protected action and add it to the correct authentication context.
Testing the Protected Action
To test the effectiveness of the protected action, you need an account that holds an administrative role that would normally allow the holder to permanently remove soft-deleted user accounts that doesn’t meet the requirements of the CA policy. I used an account holding the Global administrator role that used SMS to satisfy an MFA challenge. Entra ID doesn’t consider SMS to be an authentication strength that meets the criteria of passwordless MFA, so any attempt by this global administrator to remove a soft-deleted account fails (Figure 3).

The block also works if an attempt is made to remove an account using the Graph Permanently delete an item API or the Remove-MgDirectoryDeletedItem cmdlet from the Microsoft Graph PowerShell SDK.
Get-MgDirectoryDeletedItemAsUser | Format-Table Id, displayname Id DisplayName -- ----------- 63699f2f-a46a-4e99-a068-47a773f9af11 Annie Colonna f13e62ff-b43c-44e7-a821-48db196b84d9 Cathy Lin 889bad5f-d7f7-4731-bf07-af2894f345b2 Joanne Crispa Remove-MgDirectoryDeletedItem -DirectoryObjectId f13e62ff-b43c-44e7-a821-48db196b84d9 Remove-MgDirectoryDeletedItem_Delete: Operation requires conditional access and client does not support it. Client must be configured to support conditional access claims challenges to proceed.
Insisting on Stringent Conditions is Sometimes Good
Any organization that has deployed conditional access policies should consider using protected actions. The necessary policies are easy to implement, and it makes sense to insist on stringent conditions before destructive actions like permanent removal of user accounts are possible.
The only issue I encountered during testing was that Entra ID didn’t respect the disabling of the CA policy used for protected actions. No matter what, the policy remained in force until I removed the protected action. It’s possible that the underlying cause was a timing issue generated by multiple changes to settings over a short period and that everything would have worked more smoothly if I was more patient. In any case, the issue shouldn’t be a problem in practical terms because it’s highly unlikely that anyone will disable and enable a CA policy to turn protected actions off and on several times in quick succession, but it’s definitely something that the developers should investigate.
Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.
Primer: Using Exchange Online PowerShell in Azure Automation Runbooks
Exchange Online PowerShell Assumes Administrators Run Its Cmdlets
My last primer article in the Azure Automation series covered how to send email using the Exchange Online High-Volume Email (HVE) facility. HVE is still in preview (Microsoft is targeting September 2025 for general availability) but it still does a nice job of sending email from scheduled automation jobs.
This article discusses how to create and execute Azure Automation Exchange runbooks using PowerShell cmdlets from the Exchange Online management module. Unlike HVE, which doesn’t require any Exchange cmdlets, Automation accounts that use the Exchange module in their jobs need some special configuration. This is because the Exchange module assumes that anyone running its cmdlets is an Exchange administrator. There’s no concept of least privilege implemented in the module: once a process loads the module, it can act like a human administrator.
Loading Exchange Online PowerShell into an Automation Account
At least, an app can be all-powerful for Exchange if it meets three conditions. First, it can load the Exchange Online management module. For Azure automation accounts, this means that module is loaded as a resource into the account (Figure 1).

At the time of writing, Exchange Online PowerShell only supports PowerShell V5.1 for automation runbooks, so be sure to install that version of the module. Due to module dependencies, you must install the PackageManagement and PowerShellGet modules (loaded jn that order) before installing the Exchange Online module.
Assigning Exchange Online Permissions and Roles for the Automation Account
Second, the service principal for the app must be assigned the Exchange administrator RBAC role. For Azure Automation, this means the service principal for the automation account. The assignment can be done through the Entra admin center (Figure 2) or with PowerShell. Make sure that you select the correct automation account from the set of enterprise applications listed in the picker.

Third, the app must be assigned the Exchange.ManageAsApp permission. This is not a Microsoft Graph permission. It is an Office 365 Exchange Online permission designed to allow apps to act as administrators. The assignment can only be made through PowerShell. Here’s how to do the job with the Microsoft Graph PowerShell SDK:
$ExoApp = Get-MgServicePrincipal -Filter "AppId eq '00000002-0000-0ff1-ce00-000000000000'" $TargetSP = Get-MgServicePrincipal -filter "displayname eq 'M365Automation'" $Role = $ExoApp.AppRoles | Where-Object {$_.DisplayName -eq "Manage Exchange As Application"} $AppRoleAssignment = @{} $AppRoleAssignment.Add("PrincipalId",$TargetSP.Id) $AppRoleAssignment.Add("ResourceId",$ExoApp.Id) $AppRoleAssignment.Add("AppRoleId",$Role.Id) $RoleAssignment = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $TargetSP.Id -BodyParameter $AppRoleAssignment If ($RoleAssignment.AppRoleId) { Write-Host ("{0} permission granted to {1}" -f $Role.Value, $TargetSP.DisplayName) }
Creating a Runbook to use Exchange Online Cmdlets
With the three prerequisites in place, you can create a runbook. To test that everything works as expected, create a V5.1 PowerShell runbook with the following code (replace the organization name with your tenant):
Connect-ExchangeOnline -ManagedIdentity -Organization Office365itpros.com (Get-OrganizationConfig).DisplayName
Save the runbook and use the test pane to execute it. The output should be the display name for your organization. If that’s all you see, you can go ahead and build out the runbook with code to do more useful work.
As a demonstration, I took the script to report missing properties for user mailboxes and copied it into the runbook. The only changes that I made were:
- Remove the code that checks for an active connection to Exchange Online at the start of the script and replace it with the Connect-ExchangeOnline -ManagedIdentity command.
- Remove the Clear-Host cmdlet (Azure Automation doesn’t have a host to clear).
- Replace the Write-Host cmdlet with Write-Output (Azure Automation outputs everything together (a stream) at the end of a job).
- Remove the code to output the results as an CSV file at the end of the script.
Figure 3 shows the output of the runbook in the test pane. Everything works and we know that there are some mailboxes with missing properties that should be addressed.

Azure Automation can create an output file on the headless server where the runbook executes, but the question is then how to copy the file to somewhere more accessible later. The easy answer is to use HVE to send the file as an email attachment or to include the data in the body of a message. Something more complicated, like creating a file in a SharePoint Online site, will require more effort.
Not So Difficult
Running Exchange Online scripts in Azure Automation isn’t difficult once the initial setup for the automation account is in place. Some tweaking of the script code is probably necessary, but it’s not difficult to make the changes and will become second nature after a while. If you need to run jobs that process large numbers of Exchange objects (like mailboxes), Azure Automation is an excellent platform choice.
Need some assistance to write and manage PowerShell scripts for Microsoft 365? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.