Most Microsoft 365 admins have become accustomed to using PowerShell in their daily life, and some have even learned to love it. Others are not so thrilled by the lack of alternatives (especially in UI form) and see PowerShell as yet another tool they need to learn to use and keep track of changes in the corresponding modules.
Luckily, the vibrant PowerShell community has shared a multitude of sample scripts, snippets, and even modules that can be utilized even by inexperienced users (not without exercising some caution first!). CoreView has also stepped in to fill in the gaps left by Microsoft and their products with alternatives to running PowerShell scripts.
We at CoreView, of course, also have to keep up with all the changes within Microsoft 365, especially on the PowerShell and Graph API front. Thus, when Microsoft releases a new module version, a new set of APIs, or changes in existing endpoint and PowerShell modules, we often have to review how those affect our existing solution, evaluate if any changes are needed, and plan to add newly released cmdlets, endpoints and reports, and so on.
A recent example of this is the announced deprecation of the MSOnline and Azure AD PowerShell modules.
While the deadline is still a few months away, we have been hard at work addressing those incoming changes. Specifically, we have been hard at work creating resources that make use of supported alternatives to the MSOnline and Azure AD PowerShell modules, such as Microsoft Graph.
It’s not just about our own products though. While we do need to make sure our customers do not get impacted by such changes, as a company we strongly believe in the value of the community and so we want to share some of the work we’ve been doing with the general public.
Without further ado, in this article we will introduce you to a sample PowerShell script, designed to address one specific need – generate a list of all the users with admin roles within your Microsoft 365 environment via fully supported endpoints that can serve as alternatives to the soon to be deprecated MSOnline and Azure AD PowerShell modules.
The example script we’re sharing today comes in two flavors, both of which have been updated to work with the latest and greatest version of the Microsoft Graph SDK for PowerShell and the corresponding Graph API endpoints respectively and can be used as a replacement for similar scripts based on the MSOnline and/or Azure AD PowerShell modules.
The first script is based on direct web requests against the Graph API endpoints and uses application permissions, thus is suitable for automation scenarios. Do make sure to replace the authentication variables, which you can find on lines 11-13. Better yet, replace the whole authentication block (lines 7-36) with your preferred “connect to Graph” function. Also, make sure that sufficient permissions are granted to the service principal under which you will be running the script. Those include the Directory.Read.All scope for fetching regular role assignments and performing directory-wide queries, and the RoleManagement.Read.Directory for PIM roles.
The second flavor is based on the cmdlets included as part of the Microsoft Graph SDK for PowerShell. As authentication is handled via the built-in Connect-MGGraph cmdlet, the script is half the size of the first one. And it would’ve been even smaller were it not for a few annoying bugs Microsoft is yet to address. Let’s walk you over what the scripts do and introduce their building blocks.
While the “old” cmdlets are more user-friendly, switching to the Graph does offer some improvements, such as being able to use a single call to list all role assignments. This is made possible thanks to the /roleManagement/directory/roleAssignmentsendpoint endpoint (or calling the Get-MgRoleManagementDirectoryRoleAssignment cmdlet). Previously, we had to iterate over each admin role and list its members, which is not exactly optimal, and given the fact that the list of built-in roles has now grown to over 90, it does add up. On the negative side, we now have a bunch of GUIDs in the output, most of which we will want to translate to human-readable values, as they designate the user, group, or service principle to which a given role has been assigned, as well as the actual role. One way to go about this is to use the $expand operator (or the –ExpandProperty parameter if using the SDK) to request the full object.
While this is the quickest method, the lack of support for the $select operator inside an $expand query means we will be fetching a lot more data than what we need for the report. In addition, there seems to be an issue with the definition of the expandable properties for this specific endpoint, as trying to use the handy $expand=* value will result in an error (“Could not find a property named ‘appScope’ on type ‘Microsoft.DirectoryServices.RoleAssignment'”). In effect, to fetch both the expanded principal object and the expanded roleDefinition object, we need to run two separate queries and merge the results. Hopefully, Microsoft will address this issue in the future (the /roleManagement/directory/roleEligibilitySchedules we will use to fetch PIM eligible role assignments do support $expand=* query).
Another option is to collect all the principalIDs and issue a POST request against the /directoryObjects/getByIds endpoint (or the corresponding Get-MgDirectoryObjectById cmdlet), which does have a proper support for $select. A single query can be used to “translate” up to 1000 principal values, which should be sufficient for most scenarios. With the information gathered from the query, we can construct a hash-table and use it to look up the property values we want to expose in our report. Lastly, you can also query each principalID individually, but that’s the messiest option available.
Apart from role assignments obtained via the /roleManagement/directory/roleAssignments call, the scripts can also include any PIM eligible role assignments. To fetch those, invoke the script with the –IncludePIMEligibleAssignments switch. It will then call the /v1.0/roleManagement/directory/roleEligibilitySchedules endpoint, or similarly, use the Get-MgRoleManagementDirectoryRoleEligibilitySchedule cmdlet. Some minor adjustments are needed to ensure the output between the two is uniform, which includes the aforementioned issue with expanding the navigation properties. But hey, it wouldn’t be a Microsoft product if everything worked out of the box.
And with that, we’re ready to build the output. Thanks to the $expand operator and the workarounds used above, we should be able to present sufficient information about each role assignment while minimizing the number of calls made. The output is automatically exported to a CSV in the script folder, and includes the following fields:
Now, it’s very important to understand that this script only covers Azure AD admin roles, either default or custom ones, and optionally eligible PIM-assignable roles. Apart from these, there are numerous workload-specific roles that can be granted across Office 365, such as the Exchange Online Roles and assignments, Roles in the Security and Compliance Center, site collection permissions in SharePoint Online, and so on. Just because a given user doesn’t appear in the admin role report, it doesn’t mean that he cannot have other permissions granted!
You might want to add some additional properties to the output, such as whether the user account is enabled and protected by MFA, what licenses are assigned, whether the user has been active recently, and so on. In addition, you might want to consider running this script periodically.
Here’s where the full power of CoreSuite comes into play, as it’s now only quite easy to surface additional properties and correlate between different data sources within Microsoft 365, but scheduling a report is also a breeze. To top it all off, you can even configure a workflow to automatically execute an action against each entry. With CoreSuite you’re certainly in control!
Lastly, one should make sure to cover any applications (service principals) that have been granted permissions to execute operations against your tenant. Such permissions can range from being able to read directory data to full access to users’ messages and files, so it’s very important to keep track of them.
M365 is a constantly evolving platform, and as such there are continually new features added, and older features being deprecated. CoreView pays close attention to such changes, and we are happy to share our discoveries with the larger M365 administrator community. That said, there are specific benefits, such as simple automation for the regular execution of such resources that can only be achieved within the CoreView product itself. If you aren’t yet using CoreView, and you would like to streamline these sorts of transitions for your team in the future, reach out to a sales engineer at CoreView to learn more about our powerful, M365 management platform.