Limiting access to Office 365 by country

Quietly, Microsoft has released (a preview version of the) country-based controls for Conditional Access. While this is technically¬†a minor addition, the ability to block logins to Office 365 or other cloud applications based on the location of the user has been a common request for years. Office 365 being a public SaaS offering is by default accessible from anywhere, anytime and this can be problematic for some organizations. Previously, AD FS claims rules were the only method that allowed restrictions to be configured based on the IP of the user/client. With the advent of Azure AD Conditional Access and Multi-factor authentication, we now have more robust and easier to use alternatives. Let’s do a quick test of the new feature.

Since this feature is part of Conditional Access policies, to configure it you need to browse to the corresponding blade in the Azure AD portal. Then, select the Named locations tab or click directly on this link. You will be presented with the same old interface used to define trusted IPs/ranges for both Conditional Access and Azure MFA. However, when you press the New location button, you are now given the option to define the location via Countries/Region as shown on the below screenshot:

To create a new country-based location, all you need to do is to give it a Name, and then select one or more of the countries from the dropdown control. In the example above, I have already created a location that includes my country, Bulgaria, and another one that includes the Netherlands, which happens to be the country in which my Azure VMs are hosted. In effect, I’m preparing a list of “known” or “good” locations which I can then whitelist in any CA policies. It’s important to note that you cannot designate any country or group of countries as a “trusted location” directly in the settings, as one can do for IPs/ranges.

You can of course approach this from the opposite angle – create a list of countries that are “bad” and any potential login attempt from those should either be blocked, or be a subject to more strict CA policy. In such scenarios, you will probably also want to check the Include unknown areas option, which will apply to any IP address that cannot be mapped to a given country. Whichever approach you select, the steps to create the named location are almost identical, and very easy to follow.

Once you have described all the “good” or “bad” locations, it’s time to put them in use in your Conditional Access policies. To do so, create a new policy or edit any existing one, then navigate to the Conditions tab, and under Locations, toggle the Configure slider, then select the relevant locations to include or exclude. Adjust any additional conditions as needed and decide on which controls to use. In the following example, I have created a policy that will require MFA for any login attempt, unless it’s coming from Bulgaria, where any such attempts are designated by the exclusion of the “BG” named location:

As you can see from the above screenshot, any Named locations you defined will appear in the list and you can select one or more of them for each of your policies, either as included or excluded location. You can of course still create a policy that does not depend on the network location, or a policy that applies to any “uncategorized” locations as we discussed above. After selecting the appropriate controls for your policy, it’s strongly recommended to test it via the WhatIf tool and also via some real login attempts. Be warned that the IP-to-country mappings are not always correct and can actually change over time, so have that in mind when configuring and troubleshooting country-based policies.

On to testing then. In my case, the policy should enforce MFA on any attempt not coming from Bulgaria, so lets check that. Connecting to a VM in Azure and trying to open any Office 365 application results either an MFA prompt, or triggering the “proof up” process if the user hasn’t registered their MFA methods already, as expected:

Just to be on the safe side, it’s a good idea to perform the same test from any location that is excluded from the policy. In my case, opening the Office 365 portal from my home PC with the same account resulted in no MFA prompts, so the policy seems to be working as expected. In effect, now I have a better control over who can access my Office 365 tenant, and from which country. Take that, random Chinese guy trying to brute force my account! ūüôā

Posted in Azure AD, Office 365 | 1 Comment

Adding and reporting on permissions for Azure AD Security groups in Exchange Online

Some of you are probably well aware of the fact that Azure AD is the directory behind Office 365. Azure AD stores all the user and group objects and their relevant properties. Some of the different workloads constituting Office 365 have their own directory stores, such as EXODS for Exchange Online, SPODS for SharePoint Online and so on. A copy of any Azure AD object relevant to Exchange Online is kept inside EXODS, and its properties are “extended” with many Exchange-only attributes. It simply doesn’t make sense to store all this information in the “general” Azure AD directory, as no other workload can consume it. Similarly, some object types only exist in EXODS and are never synced to Azure AD.

The opposite scenario is also observed, for examples (non mail-enabled) Azure AD security groups do not have any representation in EXODS. This includes security groups you have synchronized from on-premises or the ones created directly in the cloud. As they are not mail-enabled and take no part in the mail flow process, Exchange doesn’t care about them. Thus, when you try to use any of the familiar Exchange cmdlets against such a security group, you will run into the “object could not be found” exception. As it should be, right?

Let’s dig a bit deeper. It’s easy enough to list all such non mail-enabled, Azure AD only security groups. For example, to do this via the MSOnline module, one can use:

Get-MsolGroup -GroupType Security | select DisplayName,GroupType,ProxyAddresses,ObjectId

DisplayName                            GroupType ProxyAddresses ObjectId
-----------                            --------- -------------- --------
test                                    Security {}             a1813eff-a80b-4ac9-bbdc-8e0821b76809
Group                                   Security {}             80cc841e-e4fd-4351-94d6-bd2f4398191b
FIMSyncAdmins                           Security {}             8595ff15-45fa-4fef-bf51-cf3878a085ef
WinRMRemoteWMIUsers__                   Security {}             5a3cdc21-85c0-41b2-8f29-a8b4850ddff2

In the above output, we can clearly see some groups¬†that have been synchronized from on-premises AD (who in their right mind will intentionally¬†name a group “WinRMRemoteWMIUsers__”), as well as some cloud-created ones.¬†The newer Azure AD PowerShell module can also be used:

Get-AzureADMSGroup -Filter "SecurityEnabled eq true and MailEnabled eq false" | select DisplayName,MailNickName,MailEnabled,ProxyAddresses

DisplayName                     MailNickname                    MailEnabled ProxyAddresses
-----------                     ------------                    ----------- --------------
ADSyncBrowse                    ADSyncBrowse                          False {}
ADSyncOperators                 ADSyncOperators                       False {}
Exchange Install Domain Servers Exchange_Install_Domain_Servers       False {}
Exchange Trusted Subsystem      Exchange_Trusted_Subsystem            False {}
ADSyncPasswordSet               ADSyncPasswordSet                     False {}

The above example is from a different tenant, one that has Exchange on-premises installed, and so some of the default Exchange groups are visible in the output. It’s interesting to note that although all these groups are not mail-enabled, all of them have their alias/MailNickName attribute populated.

Another option to report on such groups is the Get-AzureADMSGroup cmdlet. To spice up things a bit, this example uses yet another tenant, one in which the synchronization process has been initiated from several different on-premises ADs:

Get-AzureADMSGroup -SearchString ADSync | ft DisplayName,MailNickName,*Enabled,Id

DisplayName       MailNickname      OnPremisesSyncEnabled MailEnabled SecurityEnabled Id
-----------       ------------      --------------------- ----------- --------------- --
ADSyncAdmins      ADSyncAdmins                                  False            True bf526ee3-f6c7-4960-bd9b-cc90bbed718c
ADSyncAdmins      ADSyncAdmins                                  False            True 445e01a0-b7e4-41ca-b269-311e473c7e6f
ADSyncAdmins      ADSyncAdmins      True                        False            True 7abcceed-2e6a-48c2-9601-d281fd567e77
ADSyncBrowse      ADSyncBrowse                                  False            True c56aff52-d7e5-4a54-93d7-ffce3ad40f98
ADSyncBrowse      ADSyncBrowse                                  False            True d0f96187-cce8-4666-9bb7-5a167a4ed914
ADSyncBrowse      ADSyncBrowse      True                        False            True e063d094-65f8-4aa9-a89b-30bf6e30aef3

As a result, we have several copies of the same security group object, each with different ObjectID, but with matching names and alias/MailNickName attributes. Now, if you look at cloud-created groups, another interesting observation can be made for the MailNickName attribute. Namely, for all such groups, the value of this attribute is set to “BposMailNickName”, regardless of which method they were created by (PowerShell, O365 portal, Azure AD portal). In turn, this allows us to easily filter them out:

Get-AzureADMSGroup -SearchString BposMailNickName

Id                                   DisplayName           Description
--                                   -----------           -----------
a1813eff-a80b-4ac9-bbdc-8e0821b76809 test
a32a3a02-095a-4a7d-86e8-98f63fc002b2 CreatedFromO365Portal
84b18857-3c01-48be-b707-492019c57142 CloudSecGrp
b6b27af5-7b64-4bd5-9dc5-8886974dcb51 All Users
5641f280-8cf7-4a3b-a559-470b16451730 AllGuests
492075a7-3b42-41b6-884e-f19966134422 TestEXO

So, why am I showing you all this? It turns out, we can actually use such groups to delegate permissions in Exchange Online. Or at least we can try to, the permissions don’t actually work as we will see later on. To demonstrate this, we select a random, cloud-created, non mail-enabled security group, say “TestEXO” from the above list. We have the group’s objectID from the above example, so lets check what Exchange Online knows about it:

Get-Recipient 492075a7-3b42-41b6-884e-f19966134422
The operation couldn't be performed because object '492075a7-3b42-41b6-884e-f19966134422' couldn't be found on ''.

Get-Group 492075a7-3b42-41b6-884e-f19966134422
The operation couldn't be performed because object '492075a7-3b42-41b6-884e-f19966134422' couldn't be found on ''.

Get-SecurityPrincipal 492075a7-3b42-41b6-884e-f19966134422
The operation couldn't be performed because object '492075a7-3b42-41b6-884e-f19966134422' couldn't be found on ''.

Judging by the above, Exchange Online is totally oblivious about the existence of this group, as far as its concerned it does not exist and definitely cannot be used to delegate permissions (not recognized as security principal). Yet, any permissions-related cmdlet happily accepts the group as a valid delegate:

Add-MailboxPermission sharednew -User 492075a7-3b42-41b6-884e-f19966134422 -AccessRights FullAccess -AutoMapping $false

Identity             User                 AccessRights
--------             ----                 ------------
sharednew            EURPR03A001\Bpos5... {FullAccess}

Yes, the Full Access permissions were successfully granted, which we can confirm via:

Get-MailboxPermission sharednew -User 492075a7-3b42-41b6-884e-f19966134422 | fl

AccessRights    : {FullAccess}
Deny            : False
InheritanceType : All
User            : BposMailNickName_4343c935f8
Identity        : sharednew
IsInherited     : False
IsValid         : True

So, contrary to our initial expectations, the Azure AD security group does seem to exist in EXODS and can be used to delegate permissions. Some permissions at least: Full Access and Send As can be granted, while folder-level permissions cannot and neither can Send on behalf of permissions. Even though Full Access permissions can be granted, they do not actually work, as can be confirmed by trying to access the shared mailbox from the above example via the account of any member of the TestEXO group:

What is the explanation behind all this? There is no official documentation from Microsoft that says Azure AD Security groups can be used to delegate permissions. At least not yet. So two options exists here: either the observed behavior is a bug, or we are simply seeing the initial stages of a feature that is intended to provide support for such permissions. Some additional facts increase the possibility that the latter scenario is valid – recently a new recipient type was introduced in Exchange Online. More specifically, the Get-Recipient cmdlet lists “ExchangeSecurityGroup” as valid recipient type (RecipientTypeDetails), which is a nice fit for the above scenario.

The actual parameter doesn’t work yet, and it might never work for us as end-users, but we have already seen similar behavior on several other occasions. Most recently, such behavior was observed with the Supervisory mailboxes used by the Supervisory review feature, which are very well hidden from the admin tools. Their corresponding recipient type, SupervisoryReviewPolicyMailbox, is also listed as available for Get-Recipient, however only the permissions-related cmdlets work with such objects.

Another example of similar behavior is the use of built-in administrative groups and security principals. When you review the permissions of any mailbox in Office 365, you will note entries such as “EURPRD03\Domain Admins” or “NT AUTHORITY\SYSTEM”. Those entries are usually ignored by us, and there’s hardly any information we can get on them, but they are of vital importance to Microsoft’s ability to run the service. If you are adventurous enough, there are some workarounds that allow you to play with such objects, as described here.

While Supervisory mailboxes can be thought of “backend” for a particular feature and in general are not very interesting to work with, the situation with Azure AD security groups is different. We can create, synchronize and manage such groups and in most organizations the number of such objects can be comparable to or even surpassing the number of users. Thus, if some part of the service has support for security groups, it’s natural to expect that all the familiar tools work as expected. More importantly, permissions are usually a sensitive matter for which often times periodic reports are being generated end examined. For example, it’s a common practice to review which resources a give user has prior to him leaving the company, etc.

And therein lies the problem – we simply cannot rely on any known method to report on permission entries granted via such groups. If you run the Get-MailboxPermission cmdlet to get the list of permissions for a given object, such as in the above example, the only bit of information you can get about the delegate is a string value containing something like “BposMailNickName_4343c935f8”. None of the cmdlets in either Exchange Online or Azure AD work against this entry however, as shown on the below image:

The above example illustrates the scenario for cloud-created Azure AD security groups, which get the generic “BposMailNickName” value. For synchronized ones, the MailNickName attribute matches the display name and is easier to reuse. None of the Exchange cmdlets will accept this value, but you can use it against Get-MsolGroup or Get-AzureADGroup to find a match. However, remember that example we show above with several groups having the same display name? Here’s how permissions looks like when all three instances of the ADSyncAdmins group are present, and how the User entry is mapped across the different cmdlets:

In other words, there is no reliable way to report on such permissions unless you check against each Azure AD Security group. Here’s an example on how to generate such report:

foreach ($group in (Get-MsolGroup -GroupType Security)) {Get-MailboxPermission shared -User $group.ObjectId.Guid | select @{n="AADGroup";e={$Group.ObjectId.Guid}},User,AccessRights }

AADGroup                             User                    AccessRights
--------                             ----                    ------------
bf526ee3-f6c7-4960-bd9b-cc90bbed718c ADSyncAdmins_034fadd801 {FullAccess}
445e01a0-b7e4-41ca-b269-311e473c7e6f ADSyncAdmins            {FullAccess}
7abcceed-2e6a-48c2-9601-d281fd567e77 ADSyncAdmins_e4e371a4b9 {FullAccess}

Of course, if you want to cover all mailboxes, a proper full-blown script should be used instead.

In summary, when you report on (or grant) permissions in Exchange Online, you should be aware that Azure AD Security group entries can eventually pop up. To handle such entries, you cannot use the Exchange cmdlets, as the corresponding objects are “hidden” from them. The only reliable method I’ve found is to use the objectID of the group against the User parameter of the Get-MailboxPermission cmdlet, which unfortunately means you will have to cycle over each security group in your Azure AD instance. Hopefully, Microsoft will improve the handling of such entries in the future!

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

Renewed as MVP and the Office 365 for IT Pros 5th edition is out

I’m happy to share that I have been renewed as Microsoft MVP for the Office Servers and Services award category for another year. Being part of the program has definitely been one of the highlights of my IT career and the incredible people that are part of it continue to be a source of inspiration. I hope I will continue to be useful to others via this blog and my contributions on the different technical communities.

Speaking of contributions, the latest, 5th edition of the Office 365 for IT Pros eBook has been released today. Hands down the best source of information about Office 365 you can find on the marked, the book has grown immensely over the last few years so some topics needed to be moved to a separate “companion” volume. A lot of new content has been added in their place and the author team has been revamped, due to the fact that few of the original authors had to step down in order to focus on different challenges. Which in turn meant more pain for the poor technical editor, but I believe we managed to deliver yet another outstanding volume. Read Tony’s detailed announcement here to get all the details.

You can get the new edition here, or here if you prefer Amazon.

Posted in Office 365 | Leave a comment

AzureAD PowerShell brings support for federation in version (Preview)

It took Microsoft several years since the release of the Azure AD PowerShell module to address the issue with the missing federation-related cmdlets. Now, with the version of the Azure AD Preview PowerShell module we can finally manage federation. It is important however to understand that this module only exposes the analog of the Set-MsolDomainFederationSettings cmdlet, and not the Convert-MsolDomainToFederated one. Meaning that you cannot run it directly on your AD FS servers and expect the corresponding RPT and claims to be created, you will have to take care of this part manually.

Additionally, I haven’t been able to successfully run the cmdlet yet, as any combination I try seems to result in an error. Of course, the documentation is non-existent at this point, but things should get better once the cmdlet makes it to the GA module. Just in case, here’s the only example I managed to dig up:

$federationSettings = New-Object Microsoft.Open.AzureAD.Model.DomainFederationSettings
$federationSettings.IssuerUri = ""
$federationSettings.LogOffUri = $federationSettings.ActiveLogOnUri
$federationSettings.FederationBrandName = "Contoso Misa1 US"
$federationSettings.SigningCertificate="X509 signing public key"

New-AzureADExternalFederationDomain -ExternalDomainName "" -FederationSettings $federationSettings

I will make sure to update the article once I have more info.

Posted in Azure AD, Office 365 | Leave a comment

Script to remove user(s) from all groups in Office 365

Often times when a user leaves the company, or for whatever other reason, you are tasked with making sure said user is removed as member from any and all groups. In the Office 365 world, this means going over each Security, Distribution, Mail-Enabled Security and Office 365 (Modern) group and removing the user. If you are a fan of the UI approach, you can simply navigate to the Office 365 Admin Center, find and click on the user in the list of Active users, then click the Edit button next to Group membership and press the small X next to each group object.

While this approach works and is easy enough to follow, having to perform the same task over and over again is definitely not something I enjoy, so prepared a short script for this scenario. We have already discussed the quickest way to list all groups given user is a member of in a previous article, so half of the script was already done. After obtaining the list, all that’s left is to iterate over each group and depending on the group type, use the relevant cmdlet to remove the user. Now, as we deal with few very different group types, different cmdlets and even modules will be needed. For any Exchange-related groups, we can of course use the Remove-DistributionGroupMember cmdlet. Exchange remote PowerShell also exposes the cmdlet needed to handle Office 365 Groups: Remove-UnifiedGroupLinks. To handle Azure AD Security groups however, we need the Remove-AzureADGroupMember cmdlet and thus the AzureAD PowerShell module.

To add some flexibility to the script, few parameters have been introduced to handle the different group types. By default, only Exchange-related groups will be handled¬†(DGs and MESGs). To include Office 365 Groups, use the –IncludeOffice365Groups switch. To include Azure AD Security groups, use the –IncludeAADSecurityGroups switch. The -Identity parameter has been coded to accept multiple user objects as an array, and to also handle pipeline input. Any valid user identifier will be accepted, but it’s strongly recommended to use unique-valued properties such as UPN or PrimarySmtpAddress.

To simulate the script action without making any actual changes, which is a very good idea when trying to use it against a large number of objects, use the –WhatIf switch. Lastly, for troubleshooting purpose you can specify the –Verbose switch and get additional information about each step of the script execution. While the script includes some basic code to handle connectivity to Exchange Online and/or Azure AD PowerShell, this will not work in all scenarios. Make sure to execute your “connect to Office 365” script first and establish any needed sessions, otherwise the script will halt.

Combining all of the above, the script does the following:

  • Checks for connectivity to ExO and/or AzureAD
  • Builds a list of all the users to process, removing any invalid entries
  • For each user, obtains the list of all Exchange groups
    • If the –IncludeOffice365Groups switch is used, the list will include Office 365 Groups as well
  • For each group in the list, the relevant cmdlet is issued to remove the user as member
  • If the switch –IncludeAADSecurityGroups ¬†is used, Azure AD groups are enumerated next
  • For any Azure AD group returned, the Remove-AzureADGroupMember cmdlet is run to remove the user as member

If you are running the script against a large number of users, or if the user is member of to many groups, potential throttling issues might arise. To address those, a small artificial delay is added on lines 104 and 117, feel free to adjust it as needed. Additional information about the script usage and parameters can be found in the built-in help.

You can find the script on the TechNet Gallery here:

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