Exploring Azure AD users and Security groups via the SCC PowerShell cmdlets

I’ve blogged previously about the fact that “plain” Azure AD Security groups are synced to EXODS and can in fact be used to assign permissions or added as members of Exchange groups. Similar observations were then made about “non-recipient” Azure AD user objects, although these were easily discoverable in the output of Get-User. What I failed to cover back in the day is the way these object types are exposed/utilized by the corresponding cmdlets within the Security and Compliance PowerShell module, so let’s take a look at them now.

Let’s start with user objects, where we should not expect to see a big difference. Or should we? Here’s the result we get out of the Get-User cmdlet when using the SCC variant:

Get-User | group RecipientType | select Count,Name

Count Name
----- ----
55 User
1 MailUser

and here is the same via the ExO one:

Get-User | group RecipientType | select Count,Name

Count Name
----- ----
35 UserMailbox
16 MailUser
8 User

OK, that’s a bit strange. Not only the total count is off, but the breakdown by recipient type looks suspicious. Let’s take a look at the breakdown by RecipientTypeDetails instead:

Get-User | group RecipientTypeDetails | select Count,Name

Count Name
----- ----
55 User
1 RemoteUserMailbox

Hm… So all of them fall in the same type according to the SCC cmdlet, with a single exception (a user mailbox of all things). Here’s what the ExO cmdlet reported in turn:

Get-User | group RecipientTypeDetails | select Count,Name

Count Name
----- ----
2 DiscoveryMailbox
15 UserMailbox
3 RoomMailbox
11 SharedMailbox
1 TeamMailbox
12 GuestMailUser
8 User
2 SchedulingMailbox
1 EquipmentMailbox
4 MailUser

So which are the missing objects? In my particular scenario, the two DiscoveryMailbox objects as well as one of the Scheduling mailboxes. While one might expect this for the case of Discovery mailboxes, which are not represented in Azure AD and thus have no value for the ExternalDirectoryObjectId attribute, why only a single one of my Scheduling mailboxes is missing is a bit of a mystery. A closer look at its attributes reveals some empty values, on parameters such as MicrosoftOnlineServicesID, whereas they are populated on the other Scheduling mailbox entry. Incidentally, the former one is older, created two years earlier, so the provisioning workflow for such objects seems to have changed in the meantime (remember back when Microsoft changed the object type for Group mailboxes?).

But enough about users. It’s the group object type that’s showing bigger discrepancies, as shown below:

Get-Group | group RecipientType | select Count,Name

Count Name
----- ----
116 Group
17 MailUniversalSecurityGroup

Get-Group | group RecipientTypeDetails | select Count,Name

Count Name
----- ----
55 UniversalSecurityGroup
61 RoleGroup
5 MailUniversalSecurityGroup
12 RemoteGroupMailbox

Now compare this against the output of Get-Group in ExO:

Get-Group | group RecipientType | select Count,Name

Count Name
----- ----
21 Group
26 MailUniversalDistributionGroup
5 MailUniversalSecurityGroup

Get-Group | group RecipientTypeDetails | select Count,Name

Count Name
----- ----
21 RoleGroup
9 MailUniversalDistributionGroup
5 MailUniversalSecurityGroup
2 RoomList
15 GroupMailbox

That’s more than twice the number of objects returned from the SCC cmdlet (133) vs the ExO one (52). Now, the title of the post has probably already given away the answer here – the difference is that the SCC cmdlet returns “plain” Azure AD Security group objects, whereas the ExO cmdlet does not. Even though some ExO cmdlet will happily accept such objects, as shown in previous articles.

Let’s focus on the 55 UniversalSecurityGroup objects. None of these should be returned in the output of Get-Group within ExO, as such objects correspond to Azure AD Security groups and/or Azure AD Role groups (or their representation within the SCC). In my tenant, 37 out of the 55 objects map to Azure AD security groups (as checked via Get-MsolGroup/Get-AzureADGroup) and the remaining represent admin roles such as “SharePoint Service Administrator” or the legacy “Partner Tier1 Support”. One interesting observation here is that the latter set of groups compliment the list of “default” admin role “mapped” groups returned by Get-RoleGroup, such as the TenantAdmins one, or GlobalReaders. While we can see some of these “default” entries within ExO as well, roles such as “SharePoint Service Administrator” do not have a representation there. This of course makes sense, as the SCC is a cross-workload console.

The important part to note here is that such groups are actually supported for assigning permissions to different functionalities within the SCC. As an example, we can assign a Azure AD security group to the Records Management Role Group by using:

Add-RoleGroupMember RecordsManagement -Member 2903dd42-9643-40ee-9df6-88f18c155fe1

Confirming the changes are in effect can be a big confusing, as the Get-RoleGroupMember will display the ExchangeObjectId/Guid value instead of the ExternalDirectoryObjectId:

Get-RoleGroupMember RecordsManagement

Name                                 RecipientType
----                                 -------------
47dc4a7a-c85d-4eaa-ac87-11501200f928 Group

The real confusing part is that the Get-Group cmdlet will only show the relevant entry when used with the ExternalDirectoryObjectId value:

Get-Group 2903dd42-9643-40ee-9df6-88f18c155fe1

Name                                 DisplayName SamAccountName GroupType
----                                 ----------- -------------- ---------
47dc4a7a-c85d-4eaa-ac87-11501200f928 new                        Universal, SecurityEnabled

If you try passing the ExchangeObjectId or any other non-existing GUID value for that matter, the full set of group objects will be returned!? The Get-User cmdlet seems to exhibit the same behavior within the SCC, whereas their ExO equivalents behave “as expected” and return an error message instead.

Putting the output discrepancies aside, the important thing is that one can indeed use Azure AD security groups to assign permissions to SCC features and more importantly, these actually work. After a 30 mins or so replication time, the members of the group we added to the RecordsManagement Role Group above were able to access the corresponding pages within the SCC.

Another thing to note is that the Permissions UI still does not recognize Azure AD security groups as valid object for assigning permissions. If you assign the permissions via the PowerShell cmdlet though, the corresponding entries will be visible and can be removed via the UI as well. Adding an Azure AD security group will not be possible however. And while we are on the subject of limitations, the SCC (and its sister Compliance/Security centers) still lacks proper RBAC controls, such as managing assignments directly, configuring scopes, and so on. Role Groups remain the only RBAC bit available.

OK, so what about the remaining objects? 61 of those represent Role Groups within the SCC, versus 21 for the EAC ones. There are zero (0) MailUniversalDistributionGroup returned by the Get-Group cmdlet in the SCC, as it doesnt seem to particularly care about non-security enabled objects. Interestingly, such entries are readily returned when queried for by name. Closer examination shows that such objects show GroupType value of “Universal”, whereas all the other groups have a value of “Universal, SecurityEnabled”. In effect, the SCC Get-Group cmdlet filters out non-security enabled groups it seems.

And whereas GroupMailbox objects are returned as MailUniversalDistributionGroup type within the EAC, the SCC cmdlet designates them as MailUniversalSecurityGroup.  What’s even more interesting is that some Microsoft 365 groups aren’t returned by default, suggesting some discrepancy in their properties. In my tenant two such objects were found, both with GroupType value set to “Universal”, similarly to the case of “traditional” distribution groups described above. The conclusion we can draw here is that the Get-Group cmdlet behaves quite differently in the case of SCC, so you probably shouldn’t be comparing its output directly with the ExO counterpart.

To close this article, I want to re-iterate that Azure AD security groups can successfully be used to assign (some types of) permissions across Exchange Online and the Security and Compliance Center functionalities. The only bit that’s new here is the fact that such groups are now readily returned in the output of Get-Group cmdlet as part of the SCC module, which in turn allows you to more easily enumerate and assign them as needed.

Posted in Azure AD, Exchange Online, Microsoft 365, Office 365, PowerShell | Leave a comment

Exchange Online module now supports PowerShell 7/Core

Yesterday, Microsoft officially released the 2.0.4 version of the Exchange Online V2 PowerShell module. The biggest improvement this version brings is support for running the module on PowerShell 7+ instances, as announced back at Ignite. In other words, we now have cross-platform support for running Exchange Online PowerShell cmdlets!

Since the announcement at Ignite, quite few public preview versions of the module have been made available, so people that were interested in PowerShell 7 support have most likely already tested it and I will not go over many details here. I do want to point few facts that I personally find interesting though. Let’s start with the new “browser-based SSO” method.

If you have used some of the “cloud” PowerShell modules on a PSCore install, you might have experienced the annoyance of having to go over the device code flow in order to complete the authentication process. While this flow is designed to be used on devices with limited input capabilities, it’s also the default method for some PowerShell modules running in “core” environments, even if the device is capable to use the browser control. The “browser-based SSO” method alleviates some of the annoyances by leveraging a clever workaround of sorts, redirecting the response (and the token) to a local HTTPS listener.

In effect, when you issue the Connect-ExchangeOnline cmdlet without any parameters, the default browser will be launched and the familiar Azure AD login page will be displayed. You can then proceed with typing your credentials, meeting the MFA challenge or performing passwordless authentication, depending on what’s configured for your account/organization. Finally, a new page will be displayed in the browser with a simple message designating either success or failure, as shown below:

If you take a closer look at the URL during the authentication phase however, you will notice some familiar elements as well as few differences compared to the “standard” process. The client_id property is still using the same value of fb78d390-0c51-40cd-8e17-fdbfab77341b or the appID corresponding to the Microsoft Exchange REST API Based PowerShell application. Similarly, the same resource and scopes are utilized. The redirect_uri value will look a bit strange however, for example https://localhost:59618. This is in fact the local “listener”, which is only active during the authentication phase and to which the response will be redirected as the last step of the authentication process. As the response itself contains the token, the PowerShell process now has all the needed building blocks to establish a remote session.

Now, some questions might arise from the above description, such as “is PowerShell really spinning up an HTTP server on my device every time I run the cmdlet”? Well, yeah, and not really. OK, maybe 😀 One can argue on that front I suppose, but the reality is that it will only listed to “local” addresses, only on a specific port (incremental) and only for the validity of the authentication flow. If you close the tab or cancel the flow in any other way, or if you try to access the local address directly, it will immediately shut down.

TCP              LISTENING
TCP    [::]:63670             [::]:0                 LISTENING

The “browser-based SSO” described above is the default method and the one used when you run the Connect-ExchangeOnline cmdlet without any parameters. You can still use the device code flow when needed, by specifying the -Device parameter. Lastly, you can also use the newly introduced -InlineCredential parameter to enter credentials directly in the console:

Remember that there is no way to perform MFA when using this method, so it might inapplicable in most scenarios. Also, there is no –Credential parameter when using the PowerShell 7+ version of the module, meaning you need to use –InlineCredential instead. Last but not least, you can also connect via certificate, which remains the preferred method for automation scenarios, with the added benefit that it now works on core.

Other things worth mentioning about the new version of the module include support for Continuous Access Evaluation, which is one of my favorite new feature on Azure AD side of things, and the fact that it now powers the experience within Azure Cloud Shell (which got support for browser based SSO a while back).

Posted in Exchange Online, Microsoft 365, Office 365, PowerShell | Leave a comment

Microsoft finally adds some meaningful controls to the Power platform

One of Microsoft’s current goals is the expansion and accelerated adoption of its Power platform, so by now most of us are used to getting bombarded with marketing posts and sales pitches going on and on about the benefits of the platform, its ease of use, the greatness of the “citizen developer” and so on. Yet for years some very obvious governance, compliance and security concerns remain unaddressed or are being swept under the rug with half baked features that were meant to address them. I’ve ranted about this previously over at Practical 365, where we discussed how hard it is to prevent users from accessing Flow/Power Automate even when you have a valid reason for doing so, as well as how easy it is to bypass some of the “traditional” data exfiltration controls.

While for years we’ve had the DLP feature as part of the platform, yet it failed to address important scenarios such as using a single connector to both fetch and export data out of the tenant via a multiple connections. In addition some high-profile connectors weren’t covered by the DLP controls (still true, although the list is shorter now). And here is where the good news comes – Microsoft is now releasing a new set of controls to address some of these gaps. Dubbed Cross-tenant inbound and outbound restrictions, these new controls allow you to restrict any attempt at creating a connection via accounts belonging to other organizations, as well as restrict your own users from creating such connections outside of their “home” tenant. The bad news is that you currently need to open a support request in order to enable this functionality, so I’m not able to cover the process in details just yet.

Nevertheless, I believe this is a good step forward and one that should address at least some of the concerns of enterprise customers. And who knows, going forward we might actually see a shift in behavior on this front, with features being released with compliance and security in mind from the get go.

Posted in Microsoft 365 | Leave a comment

Self-service PowerShell throttling policy relaxation for Exchange Online

Many vendors, Microsoft included, enforce strict throttling policies on their services in order to make sure that resources are not being hogged by a single user/process. In the Exchange world, throttling policies were first introduced in Exchange 2010 and have been present in each server version since. With the move to the cloud, they become even more vital, given the multi-tenant nature of the service.

It’s understandable why Microsoft wants to enforce such limitations, and one can even argue that the decision to remove the ability to even see the current throttling policy values that are in effect in Exchange Online was justified as well. However, it’s not that uncommon for organizations to have a legitimate need to go beyond the limitations enforced by throttling policies, for example when performing a migration from on-premises or third-party solution. In such scenarios, a process was made available for requesting temporary relaxation of the throttling controls, although Microsoft still had the last word in terms of the duration and actual throttling values.

Few months back, Microsoft decided to make this process a bit easier, by releasing a self-service experience for checking the current settings and updating the configuration to a more relaxed set of values. At the time, I decided to skip covering this new experience, as there’s not much you can say about it and even less in terms of actual configuration, and frankly I’m not a fan of that stupid “AI” support assistant. Since then, Microsoft has added additional scenarios, so I guess I have to play along 🙂

One new scenario in particular is relaxing the throttling controls for PowerShell cmdlet execution. To request this, you need to again open the Microsoft 365 Admin Center, then go to Support > New service request. In the “Need help?” pane, enter “PowerShell throttling” or similar keywords, and you will be presented with the following:

Here, you are given the option to Temporarily update throttling policies for a migration or Update throttling policies to align with your tenant size. Pressing the Run Tests button for either of these will initiate a (not so) short check and once the results are back, you will be presented with the option to Update settings, provided the automated diagnostic tool determined there is room for improvement. All you need to do is select the consent checkbox and press the button.

Overall, the process is pretty much identical with the EWS throttling scenario, and fairly straightforward, though I would much prefer to have this exposed on an actual page instead of having to go through the “Need help?” pane. Anyway, the end result is what’s important here, and being able to perform this without having to open a support case (or provide justification) is a good step forward. Nowhere in the process you will be presented with the actual values though, so the experience remains as convoluted as ever.

Do note that this is not a “golden ticket” and you should still add appropriate anti-throttling handling within your migration and reporting scripts. Same old recommendations still apply, at least until Microsoft releases a full set of REST-based cmdlets or their corresponding Graph API endpoints.

Posted in Exchange Online, Microsoft 365, Office 365 | 3 Comments

Common issues when sending mail via PowerShell in Office 365

Lately I seem to run into such questions quite often, so I figured I’d put a short article outlining the most common issues one might run into when using PowerShell to send email messages via SMTP AUTH in Office 365. The article is by no means intended to be an exhaustive resource for all possible errors, just some of the common things I see over at the different communities.

One issue that seems to be overlooked in particular is the fact that PowerShell by default uses on older/insecure protocols. More correctly, it uses the system default values, which even for modern (desktop) OS versions are a bit relaxed. By now, you should probably be well aware that within Office 365, Microsoft has more strict requirements, and generally speaking you should be using TLS 1.2. This in turn creates problems with PowerShell, as connections negotiated via older/less secure protocols will get blocked. The error message received simply says something like “the server requires a secure connection” but doesn’t directly tell you what to do:

Send-MailMessage: The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.7.57 SMTP; Client was not authenticated to send anonymous mail during MAIL FROM [VI1PR08CA0220.eurprd08.prod.outlook.com]

So, one thing to try, and make habit of is to configure the Security Protocol value and set it to TLS1.2. This can be done by invoking the corresponding method, as follows:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

Once this is done, you can retry running the Send-MailMessage cmdlet and if the credentials specified are correct, it should work OK. Granted, there are several other things you might need to check on or configure before you can make it work, which we will cover next. But first, let’s answer another question that pops up – how can we make sure the changes to the Security Protocol value persist (they’re per-session ones). An easy way to do this is to add the line above to your PowerShell profile. Remember that profiles can depend on the host as well, so consult the above article to pick the best location for your scenario.

If the above suggestion doesn’t solve the issue, one other thing to check is whether SMTP Auth is disabled for your organization or the particular user. As the article explains, per-mailbox settings take precedence over the organizational config, so if needed you can add exceptions, either by using the UI or PowerShell:

Set-CASMailbox -Identity user@domain.com -SmtpClientAuthenticationDisabled $true

Another thing to remember is that the current implementation of SMTP Auth uses basic authentication, and thus is considered insecure. Newer PowerShell versions might even warn you about this when using the Send-MailMessage cmdlet:

WARNING: The command 'Send-MailMessage' is obsolete. This cmdlet does not guarantee secure connections to SMTP servers. While there is no immediate replacement available in PowerShell, we recommend you do not use Send-MailMessage at this time. See https://aka.ms/SendMailMessage for more information.

Microsoft has already announced support for using OAuth with SMTP, but that doesn’t mean the already existing clients/libraries will be able to leverage this automatically. They still rely on and will try to use Basic authentication, which by now should be blocked across most Office 365 tenants. The Security defaults feature is one example on how this block can be enforced, and depending on the SKU you might be able to leverage Conditional Access policies or Exchange Authenticated policies for more granular control over this. So make sure to check your appropriate controls and exclude any accounts that need to leverage outdated methods to send email, such as using the Send-MailMessage cmdlet in PowerShell.

Of course one also must make sure that the credentials are correct, and for best results, use an account that has a corresponding Exchange Online license assigned. Should you need to impersonate other users when sending messages, the Send-MailMessage cmdlet is probably not the best approach (use EWS instead), but it might work in some cases, granted you make sure the relevant Send As permissions have been assigned.

Posted in Exchange Online, Microsoft 365, Office 365, PowerShell | Leave a comment