Bug in the Office 365 admin portal can result in removal of admin roles

We often hear stories about the wonders of the DevOps model and how it has revolutionized the software industry. It’s really great in theory, bringing the processes of development, testing and running the service closer together, and providing benefits such as faster release cycles, improved reliability, and whatnot.

That’s the theory alright, but as its often the case, in the real world things look a bit different. More often than not, DevOps has turned into an excuse to ship a minimum-viable version of a product, and to force customers to wait for years until all the promised features are delivered. Another thing that has become painfully obvious over the past few years is the fact that the role of QA or “tester” has been continuously neglected, to a point where software is launched with blindingly obvious bugs or issues. We’ve seen it with recent Windows releases, we’ve seen it with Office releases, we see it every day with Office 365 and Azure. Slowly but surely the customers are turning into beta testers, and it’s no wonder that DevOps is sometimes mockingly referred to as “testing in production” (not in a good way).

Anyway, after this rant-y preamble, let me introduce you to yet another obvious bug in the Office 365 Admin Center, which could have been easily avoided if the engineers simply bothered to test their code before releasing it in production. Actually, there are series of few interlinked bugs, and it all boils down to the way the Office 365 Admin Center UI handles admin role assignments.

Bug #1 was originally reported on the Azure AD forums over at MSDN, and can be summarized as “Office 365 Admin Center UI doesn’t recognize any additional Azure AD roles”. The steps to reproduce it are simple enough: go to the Azure AD blade in the Azure portal, select a user and assign any non-Office 365 role to him, for example “Application administrator”. Confirm that the role was successfully assigned and head to the Office 365 Admin Center, select the user and check the list of Roles on the user details pane. The Application administrator role will not be visible there, nor when you press the Edit button next to Roles. One can say that this is the expected behavior, as the role doesn’t “exist” in the context of Office 365. After all, we don’t see any Azure roles in the Office 365 Admin Center, right? I might actually let that one slide, even though Azure AD is vital part of Office 365, and so are any roles that give permissions on Azure AD objects and entities are of great importance.

That’s not all there is to the issue though. If you happen to press the Save button now, even though you haven’t made any changes, the role assignments for the given user will be overwritten with what’s currently displayed in the UI. And as the UI doesn’t list any of the Azure AD roles, in our scenario this means effectively removing the Application administration role assignment from the user. You can confirm this by switching back to the Azure AD blade and refreshing the roles page. This is essentially bug #2, and the short video below illustrates this behavior:

Very similar issues can be observed when you have more than one “standard” Office 365 role assigned. Although in most cases the Admin Center UI should handle such scenarios, one important exception is the Global administrator role. If you have assigned the GA role to a user, the Office 365 UI will not let you assign another role, despite the fact that such scenario is supported and can be enabled via the Azure AD portal or the Azure AD/MSOnline PowerShell module. If you use any of these endpoints to assign another role to a given GA user, say the Billing administrator role, and head back to the Office 365 Admin Center, the role assignments will be correctly displayed in the user pane. However, when you press the Edit button, once again the UI will not reflect the fact that the user has the GA role assigned. More importantly, if you click the Save button, the GA role will be stripped! Video below:

As illustrated above, there are obvious deficiencies in the way the Office 365 Admin Center handles role assignments. Whether this was a deliberate design decision in the quest to simplify management, or something that was overlooked, we can only guess. The good news is that the relevant teams at Microsoft are aware of the bugs described above and are already working on fixing the experience. Given the scale at which Office 365 runs, it might take few days before the Admin Center UI is updated with those fixes.

In all fairness, Microsoft’s reaction was very fast, as it should be in a DevOps world. However, this does not excuse the fact that the engineers neglected to thoroughly test the relevant UI controls in the first place, before releasing them to millions of Office 365 customers. When even critically important functionality such as assigning admin roles is clearly not given enough attention, one can only wonder whether a fundamental change in the way code is developed, tested and released might be necessary.

P. S. In case you are wondering, the same behavior was present in both the old and the new Office 365 Admin Center, as in fact the same UI bits are utilized in both.

Posted in Azure AD, Office 365 | Leave a comment

Azure AD PowerShell module with support for PowerShell Core

Few months back, I did a quick review of the freshly GA’d PowerShell Core in the context of running your day-to-day Office 365 related tasks. As expected, it didn’t perform that well. Due to the various dependencies on the underlying .NET binaries, every Office 365 workload that required a module was a no-go when run in Core. Only the good old Remote PowerShell for Exchange Online worked, and only when using Basic authentication.

Well, things are slowly starting to change now. Although still not officially released, there is a new version of the AzureAD PowerShell module which adds support for PowerShell Core. How do I know that? Because this is what Azure Cloud Shell uses:

Yes, it’s a Preview version, as the name implies. But it most definitely isn’t a Private Preview version, or only available via the PSGallery Internal repository, as everyone using Azure Cloud Shell has access to it. And since you have the module installed in ACS, you can just grab it and start using it on any other endpoints utilizing PowerShell Core.

To get the module, simply copy it from the path shown above to your clouddrive directory, which is exposed into the Cloud storage File structure. Once the module is copied there, you can use any number of methods to export it, for example by clicking the Connect button from the Azure portal and copying the PowerShell cmdlet. Then, just start using it:

If you examine the module code, or simply browse the directory, you will notice that two versions of the binaries are provided, one for the “full” version of PowerShell, so .Net 4.7.1, and another one using .Net Core. We are more interested in the latter, which is automatically selected when you load the module in PowerShell Core host.

One of the improvements of this version is the option to trigger the OAuth Device code flow, which is a nice workaround to use on devices that cannot utilize the ADAL dialog for interactive login. In case you are not familiar with this flow, in a nutshell it allows you to use another device to complete the auth process. In fact, this is the default method used by Connect-AzureAD in this version of the module, as shown on the screenshot below:

Connecting via other methods, such as directly providing an Access token or using a certificate-based authentication should still be possible as well. Regardless of which method you use to connect, the most important part is that cmdlets actually run OK now, compared to the JSON exceptions that were being generated when using the GA version of the module. Thus, you can now perform pretty much any operation via the Azure AD module, with some examples shown below:

While this *is* still a preview, judging from the amount of debug information spilled by the module and the fact that you cannot actually find it yet in the PSGallery, the method used in this article should get you a viable solution for scenarios where you want to manage Azure AD outside of a “traditional” PowerShell console. Or you can just head over to https://shell.azure.com and use the module directly 🙂

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

Graph API adds support for transitive membership queries

In other words, you no longer need to run recursive queries and enumerate the membership of all groups in order to display a list of groups for which a particular user is a member of. Similarly, you can use a simple query to get a “flattened” list of members for a particular group, with the membership of all nested groups expanded. Let’s check some examples.

First of all, in order to use the examples below, you must have an Azure AD application registered with the Directory.Read.All, User.Read.All permissions at minimum. If you don’t know how to do that, I suggest you start by reviewing the documentation on how to get an access token for the Graph, or use the Graph explorer tool instead to run the examples from this article.

List all Groups a given user is a member of

In this example, we will be using the transitiveMemberOf method to query the Graph for all group objects a given user is a member of. The request is very simple, all you need to provide is the user identifier (UPN will do just fine) and the Access Token you’ve obtained via the application we mentioned above.

$MemberOf = Invoke-WebRequest -Headers $AuthHeader -Uri "https://graph.microsoft.com/beta/users/vasil@michev.info/transitiveMemberOf"

The query will return a JSON object, which you can convert to an array via the following:

$result = ($MemberOf.Content | ConvertFrom-Json).Value

Then, you can simply review the results, filter them out, export them to CSV or whatever. For example, I can list only the Admin groups I’m a member of by filtering on the “#microsoft.graph.directoryRole” entity:

$result | ? {$_.'@odata.type' -eq '#microsoft.graph.directoryRole'} | select DisplayName,id

displayName                   id                                  
-----------                   --                                  
Privileged Role Administrator 81705206-0da5-4ae7-a615-277fad156754
Company Administrator         c25d133f-4944-481a-84d2-6e41d6a101f4
Security Administrator        9eae26d0-29b0-49e8-834d-5025c8542618

To list all Security, Office 365, and Mail Enabled Security and Distribution groups, you can use the following filter:

$result | ? {$_.'@odata.type' -eq '#microsoft.graph.group'} | select DisplayName,Mail,GroupTypes,MailEnabled,SecurityEnabled

displayName   mail                                 groupTypes          mailEnabled securityEnabled
-----------   ----                                 ----------          ----------- ---------------
domaintest    testdomain@michev.onmicrosoft.com    {Unified}                  True           False
DG            DG@michev.onmicrosoft.com            {}                         True           False
Enabled Users                                      {DynamicMembership}       False            True
Default       default2@michev.info                 {Unified}                  True           False
USG           USG@michev.info                      {}                         True            True
Unified       Unified@michev.info                  {Unified}                  True           False

Since there might be a lot of groups returned, you can just export the results to CSV file and review/filter them in Excel:

$result | ? {$_.'@odata.type' -eq '#microsoft.graph.group'} | Export-CSV -nti MemberOf.csv

The list will also include any groups with Dynamic membership rules, however it will *not* include Exchange Dynamic Distribution Groups and other object types not recognized by Azure AD. So, here’s the place to remind you about the PowerShell script I released recently to generate a report of given users’ group membership via the Exchange cmdlets, which you can download from the TechNet Gallery.

And that’s pretty much all there is to it. For additional details and code samples, review the official documentation articles on users, groups, devices and service principals (once they become available that is).

In case you are having a hard time with the process of registering an application, obtaining a token and so on, you can simply open the Graph explorer tool instead and paste the URI I user in the first example above. Here’s how it will look like:

List all members of a given group recursively

The other new method allows us to perform the “opposite” operation: given a group, expand all its membership, including the membership of any nested groups, and return a “flattened” list of members. The method in question is transitiveMembers, and the usage is very similar to the transitiveMemberOf method described above. This time we simply need to use the /groups endpoint and provide a valid identifier for the group in question, say its objectID.

$Members = Invoke-WebRequest -Headers $AuthHeader -Uri "https://graph.microsoft.com/beta/groups/c91cd116-a8a5-443b-9ae1-e1f0bade4a23/transitiveMembers"
$result = ($Members.Content | ConvertFrom-Json).Value

$result | group '@odata.type' | select Name,Count

Name                        Count
----                        -----
#microsoft.graph.user           5
#microsoft.graph.group          2
#microsoft.graph.orgContact     1

So, the group we run this query for has a total of 8 members, 5 of which are Users, 1 is a Contact, and there are 2 nested groups. Personally, when I run such a query I don’t care at all about any group entries, I simply want to know how many users a given change or email will affect. Thus, we can filter out any groups from the results obtained above, which also has the added benefit of making the output a bit easier to handle.

$result | ? {$_.'@odata.type' -ne '#microsoft.graph.group'} | select DisplayName,userPrincipalName,Mail,AccountEnabled

displayName   userPrincipalName                               mail                        accountEnabled
-----------   -----------------                               ----                        --------------
HuKu          HuKu@michev.onmicrosoft.com                     HuKu@michev.onmicrosoft.com           True
Marian Marian marian_ski_outlook.com#EXT#@michev.info                                               True
Vasil Michev  vasil@michev.info                               vasil@michev.info                     True
Vasil Michev  vasil.michev_quadrotech-it.com#EXT#@michev.info                                       True
pesh0
SfBUser       SfBUserSatya@michev.onmicrosoft.com                                                   True

As with the transitiveMemberOf method, we cannot use this against Dynamic distribution groups or any other type of group not recognized by Azure Ad. For additional details and examples, refer to the official documentation.

And in case you want to take the easy road and run this in the Graph explorer, here’s how the query should look like there:

Posted in Graph API, Office 365 | 1 Comment

An extended view of the Privileged Access Management feature in Office 365

Every now and then, there comes a feature that promises to forever change the security landscape of the service. Privileged Access Management, or PAM, is one such feature, designed to allow customers to enforce the principle of Zero Standing Access in Office 365.

The feature is built on the same technology Microsoft has used for year in order to run operations in their datacenters. It combines Just In-Time Access (JIT), Just Enough Administration (JEA), Approval workflow and robust Auditing to enable admins to still perform their tasks, while having zero users with standing access to highly privileged roles overall.

Sounds interesting, right? Go check the full two-part review over at the Practical365 site. Here are the links:

Privileged Access Management in Office 365

Privileged Access Management – Part Two

 

Posted in Office 365 | Leave a comment

GAL segmentation and Ethical walls in Microsoft Teams

It’s not uncommon for various types of organizations to want to restrict one or more of the form of communication between their users. For example, in the EDU sector students are often limited to seeing and communicating with just their peers. Another example are financial institutions which can get into a lot of trouble for any documented case of exchanging “insider” information, and so on.

“Ethical wall” or “ethical firewall” is a term used to designate a solution, or set of settings that prevent users from communicating with each other. In the Exchange world, this is usually enforced by transport rules. Another feature commonly used in such scenarios is GAL segmentation (also known as GAL segregation), or the process of splitting users into specific “groups”, limiting their visibility into users from the rest of the organization.

Now, Microsoft has released a feature that allows Teams to use the same controls in order to enforce a basic form of ethical wall. More specifically, this is a setting that instructs the Teams client to honor any Exchange Address Book policy configured for the user, instead of using the default company-wide search. You can find the corresponding setting in the Microsoft Teams & Skype for Business Admin Center, under Org-wide Settings, Teams Settings, Search, or directly via: https://admin.teams.microsoft.com/company-wide-settings/client-settings. The setting itself is called Scope directory search in Teams using an Exchange address book policy (ABP), as shown below:

As the name suggest, the setting relies on the configuration you’ve already made in Exchange Online. In my case, I have created the following custom ABPs:

Get-AddressBookPolicy

Name            GlobalAddressList AddressLists             OfflineAddressBook RoomList
----            ----------------- ------------             ------------------ --------
All Contoso ABP \New GAL          {\All Users, \All Rooms} \New OAB           \All Rooms
Teams           \New GAL          {\CustomAttribute15}     \New OAB           \Rooms15

The Teams policy includes only users for which the value of CustomAttribute15 is set to “Teams“. Yes, I’m simple like that.

Get-AddressList CustomAttribute15

Name                      DisplayName               RecipientFilter
----                      -----------               ---------------
CustomAttribute15         CustomAttribute15         CustomAttribute15 -eq 'Teams'

So, a user that has this ABP assigned will only be able to see limited set of users in the GAL, as shown below:

If the setting works as expected, it should prevent this user from searching for any users outside of the list shown above in any of the Teams clients or contacting them via the New chat box. So let’s try this. If I log in with the same user to Teams, and head out to the Chats section to initiate a new private chat with a user not included in the ABP, I get the following:

The same experience is observed if I try to search for a user not in scope of the ABP I’m assigned to via the Search box on top. In contrast, objects that are part of the ABP are returned just fine. The same behavior is observed when trying to schedule a new meeting, so the setting does seem to have effect.

The good news ends here though. As evident from the above screenshot, Teams is happy to suggest that I contact “Vasil Michev”, even though this user is NOT part of any address list I’m allowed to look at. And since there no actual message delivery restrictions enforced, I am able to contact this user just fine. Any user that is able to see my account in any address list he has access to is of course able to initiate a chat as well, as well as use any other type of communication method with me. And, I’m able to search for and initiate private forms of communication with any user we share membership of any Team with.

So in a nutshell, while the functionality can somewhat limit who a given user can see and initiate communication with from the Teams clients, it works by simply trimming the list of entries presented at few UI elements. There are multiple exceptions and no server-side functionality is added to actually block users from reaching each other, unlike the transport rules we can use for Exchange Online. Overall, a good first step, but it leaves a lot to be desired.

Posted in Exchange Online, Microsoft Teams, Office 365 | 1 Comment