I’ve seen several online discussions about the steps which need to be taken in case of a ‘red button’ action, for example when a user account is compromised or employee termination.
Of course, it differs depending on the environment which actions need to be taken to block access to corporate data as soon as possible in such a case. I assume we can all agree the actions at least include disabling the user account in Entra, revoking the Entra ID refresh tokens, disable devices in Entra and maybe even wipe or retire the user’s devices.
Unfortunately, there is no red button, single click option available in Entra ID to perform all those actions. Maybe because a part of these actions relies on Microsoft Intune (and Defender if you’re using that as well).
Therefor I started building some low-code automation with Azure Logic Apps, to create a sort one-click red button solution to automate this whole revoke access process. This first version is triggered by entering the User Principal Name in a SharePoint List. But triggering such a flow can be done from several third-party solutions as well, via a direct integration in Logic Apps or via an HTTP request. Be creative and use this as a starting point I would say.
Another option to trigger the flow could be creating a PowerApp, so the security officer could even start the revocation process using a mobile device in an easy way. If I ever find some spare time, I might share these steps as well 😊
The solution in short
The whole solution relies on Azure Logic Apps, a cloud-based platform that allows you to create and run automated workflows with little to no code. And calling the Microsoft Graph API via actions in the Logic Apps flow.
It is very easy to trigger such an automation solution via the creation of a new item in a SharePoint List. Employees with access to the SP List can add a new item to the SP List which includes the User Principal Name (UPN) of the account for which access needs to be revoked.
Adding a new item to the list starts the flow and its actions. Via Microsoft Graph all kinds of information is retrieved using the UPN so that the following actions can take the actions to block user access, such as disabling the user account and resetting the password. The outcome (success or failure) of these actions is written back to the SharePoint List so the employee who initiated the flow knows if all the actions taken are executed successfully.
All these actions are taken in only a couple of seconds after the flow is triggered.
Actions taken by the flow
These are the several steps to the flow takes after it is getting triggered;
- Disable user account
- Reset password
- Revoke sign in sessions
- Delete authentication methods
- Disable Windows devices in Entra ID
- Reboot Windows device
- Retire Android, iOS and macOS devices
An additional version is available on my GitHub repository that isolates the Windows device via Defender, instead of rebooting the device.
Requirements
We have some requirements that need to be in-place before we can use our Logic Apps flow.
In my example we need to have a SharePoint List, so that’s the first requirement. The list should have these columns; User Principal Name, User account disabled, Password reset, Sign out of all sessions, Auth methods deleted, Devices disabled, Device action taken, Other.
To access the SharePoint List from the flow, we need to have a (service) account with permission to the List.
To take all these actions via Graph API calls we make use of a Managed Identity (MI):
A Managed Identity in Azure is a feature that provides an automatically managed identity in Entra ID for applications to use when connecting to resources that support Azure AD authentication.
We have the option to use a system-assigned and user-assigned managed identity, as you can read in the documentation. In my case I make use of a system-assigned MI, as that is considered the most secure.
This Managed Identity needs to have (application) permission to execute all the Graph calls we make. Unfortunately, this change password option only supports delegated permission, which I actually want to avoid as I want to make use of one identity to perform the Graph calls.
Fortunately, we also have another option to reset a password which does support applications permissions, but the documentation describes that besides the application permissions, also an Entra ID role needs to be assigned;
In application-only access, the calling app must be assigned the User.ReadWrite.All (least privilege) or Directory.ReadWrite.All (higher privilege) application permission and at least the User Administrator Microsoft Entra role.
These are the application permissions needed for our flow, besides the User administrator role:
Device.ReadWrite.All
DeviceManagementManagedDevices.PrivilegedOperations.All
DeviceManagementManagedDevices.Read.All
User.ReadWrite.All
UserAuthenticationMethod.ReadWrite.All
In case that you want to use Defender to isolate your (Windows) devices, these application permissions are also needed;
Machine.Read.All
Machine.Isolate
The last requirement is having an Entra ID group. We use that group to control who is allowed to start the flow. Everybody with access to the SharePoint List could add an item to the list, but if you’re not member of the group, you are not able to initiate the whole flow and its actions.
On my GitHub repo you can find a script that assigns these permissions and Entra ID role to the Managed Identity.
Setting up the flow – part 1
Usually, I share every trigger and action which I add to the flow separate in my blog posts. But this time I do this a little differently as this would mean that I would share a lot of actions multiple times where the only difference is for example the OS type or authentication method in the Graph query. But don’t worry that you’re not able to rebuild the flow, at the end of this post I’ll share a direct link to deploy the flow to Azure using an ARM template. Besides that, I share a PowerShell script to assign the needed Entra ID role and application permissions to the Managed Identity on my GitHub repo.
In case you want to start building the Logic App yourself, sign in to the Azure portal and open the Logic App service. Create a Logic App of type Consumption.
When the Logic App is created, we first assign the Managed Identity. This is done on the Identity tab, under Settings.
In case you also want to use a system assigned Managed Identity switch the status to On, on the System assigned tab.
If you want to use a user assigned identity, add your identity on the User assigned tab.
Browse to the Overview tab and click on Edit.
The first thing we need to add to the flow is a Trigger. Click on Add a trigger and search for When an item is created. In the list you’ll find that trigger under SharePoint.
First sign in with your service account that has access to the SP List.
Next, select the Site Address where the SP List is located and select the List Name.
In case nothing is shown in the list, click on Enter custom value and paste the needed information.
Click on How often do you want to check for items. Here we select how often the flow checks for new items. It is up to you to configure this to your needs.
Next, we add our first action to the flow. We add an Initialize variable action. In this action we store the object ID of the Entra ID group for later usage.
Provide a Name for the variable, select string as type and enter the object ID of the Entra ID group in the value field.
We add three more Initialize variable actions to the flow. We use this variable later in the flow to store the outcome of some of the actions. I take this approach of storing a success or failed outcome of actions in a variable. Because for some actions, like disabling the users’ devices, we could have multiple outcomes when the user has multiple devices. But that will be shown later in this post.
Add the three Initialize variable actions to the flow. Enter a name and select string as type. Leave the Value box empty.
We add a Compose action to the flow, to grab the Author of the SharePoint item, to later check if the author is member of the Entra ID group.
When you set the cursor in the Inputs field, you see two options. Select fx to be able to enter a Function (expression).
Enter this expression and click Add;
Last(split(triggerBody()[‘Author#Claims’],’|’))
Next, we add our first HTTP action to the flow, to query Microsoft Graph for information. We run a GET query to check if the author is member of the Entra ID group.
Enter this URI:
https://graph.microsoft.com/v1.0/users/[outputs]/memberOf/[varGroupObjectID]
[outputs] and [varGroupObjectID] are data values from previous steps which you can add by hitting the lightning symbol (to get dynamic content available).
[outputs] contains the author from the Compose action and [varGroupObjectID] should contain the object ID from the Entra ID.
Make sure that you add the Advanced parameter option Authentication. Select Managed Identity as authentication type to make use of the (system) assigned managed identity.
We add a Condition action to our flow. We use the status code of the HTTP action in the condition to determine if the author is member of the Entra ID group. When the status code is equal to 200, it means the author is member of the group and the condition results in true. Otherwise, it is false, and the flow is terminated.
On the Settings tab of the Condition action, under Run after select Has failed. This makes sure the flow moves on, even when the HTTP action fails (which it does when the user is not member of a group).
Under False of the Condition action, we add an Update item action, which is a SharePoint action.
Select the Site address and List again.
As ID we use the ID from the SharePoint trigger found via the dynamic content (lightning button). We do the same for UserPrincipleName. Under Advanced parameters we add other and we fill in Action forbidden.
The Action forbidden message is written back to the SP list by this action.
As last action of the first part of our flow we add a Terminate action. With this action we terminate the whole flow.
This is how our flow looks like until now.
Setting up the flow – part 2 (disable account and reset password)
In this part of the flow, we add HTTP actions to disable the user account and reset the password of that account. The result of these actions is written back to the SP List.
The upcoming parts of our flow are all added under True of the condition.
Add an HTTP action under the True section to disable the user account.
We run a PATCH query with this URI;
https://graph.microsoft.com/v1.0/users/[User Principal Name]
[User Principal Name] is a value from the SharePoint trigger found via the dynamic content.
In the Body field add:
{
"accountEnabled": false
}
We add a Condition action under the HTTP action. We use the statuscode of the HTTP action to determine if disabling the user account succeeded or not. When the status code is equal to 204, it succeeded.
On the Settings tab of the Condition action, under Run after select Has failed again.
Under both True and False we add an Update item (SharePoint) action to write the status to the SharePoint List.
Add User Account Disabled under Advanced parameters and fill in Success or Failed (which is the only difference between the two SharePoint actions).
Disablement of the user account is handled. We now move to the part to reset the password and revoke the sign in sessions. To run this part of the flow at the same time as disabling the user account we make use of a parallel branch.
Under True, hit the plus button and select Add a parallel branch (instead of Add an action).
Add a HTTP action with Method PATCH and the same URI as in the HTTP action that disables the user account:
https://graph.microsoft.com/v1.0/users/[User Principal Name]
In the body field add:
{
"passwordProfile": {
"forceChangePasswordNextSignIn": false,
"password": "NewSecurePassword123!"
}
}
Add your own password instead of the default one. Or you can add the below expression on the place of the password to generate a random password of 17 characters;
concat(toUpper(substring(guid(),0,8)),’!’,substring(guid(),0,8))
Like under the disable user account action, we add a Condition action that checks the status code (204 is succeeded), including changing the Run after to also run after a failed HTTP action.
Don’t forget to configure the Run after again, which should be done for all the conditions.
And two Update item actions to write back the result of the HTTP action to SharePoint.
After the Condition we add an HTTP action to revoke the user sign in sessions. This time it’s a POST action with this URI:
https://graph.microsoft.com/v1.0/users/[User Principal Name]/revokeSignInSessions
Make sure the Run after is configured like on the Condition actions.
Add a Condition action (checking status code is equal to 200) and two Update items actions again.
Yes, don’t forget the Run after setting (last time I mention it ;).
And this is the part of the flow that disables the user account, reset the user’s password and revokes the sign in sessions.
Setting up the flow – part 3 (delete authentication methods)
Next to the disablement of the user account and resetting the user’s password, I also want to delete the user’s authentication methods.
We add another parallel branch under True. Ensure the branches are all located next to each other so these run simultaneously!
For that, we first query Microsoft Graph for all authentication methods registered by the end user, before we delete all the methods (besides the password, as that method can’t be deleted). Add an HTTP action that runs a GET query with this URI:
https://graph.microsoft.com/v1.0/users/[UserPrincipalName]authentication/methods/?$select=id
I use $Select in the URI to only select the value I’m interested in, which is the ID of the authentication method. The type of the method is also needed, but that’s already included in the odata.type and returned by default.
Next, we add a Parse JSON action again, followed by a Select action.
I use a Select action as I can’t just grab the odata.type value directly from the Parse JSON action, therefore I use this additional action to pick out the information I need. Under map, in the left fields add OdateType and ID (just text) In the right fields we add expressions:
item()?['@odata.type']
item()?['id']
Add another Parse JSON action with Outputs of the select action as Content and below as Schema:
{
"type": "array",
"items": {
"type": "object",
"properties": {
"odataType": {
"type": "string"
},
"Id": {
"type": "string"
}
},
"required": [
"odataType",
"Id"
]
}
}
As the authentication methods and how it is provided to use via Graph don’t follow a naming convention, we have the need to split up every authentication method ourselves. That’s because we don’t have one default URI to delete all methods, but a URI per method 🙁
To split up all the methods we use a Switch action. The switch is done on odate.type.
Note, you will see the Switch action is automatically added to a For each action to loop through all authentication methods.
Next, we create 6 cases, for every authentication method one:
Windows Hello for Business equals #microsoft.graph.windowsHelloForBusinessAuthenticationMethod
Microsoft Authenticator equals #microsoft.graph.microsoftAuthenticatorAuthenticationMethod
FIDO2 (passkeys) equals #microsoft.graph.fido2AuthenticationMethod
Phone authentication equals #microsoft.graph.phoneAuthenticationMethod
Temporary Access Pass equals #microsoft.graph.temporaryAccessPassAuthenticationMethod
Email authentication equals #microsoft.graph.emailAuthenticationMethod
Under every case we add an Until action. This is an action that loops the action inside the Until action. The reason for this Until action is that we can’t delete the default sign-in method. This would result in one of the following HTTP actions failing. If we loop a couple of times through the delete actions, after a couple of tries only one authentication method is left and at that time, we can delete the authentication method.
In the until action (under Do) we add a Delay action. We delay the loop by a couple of seconds, otherwise, the following HTTP action will run constantly, which makes no sense in my opinion and could in practice might run through the loop without deleting the last authentication method.
After the Until action, add an HTTP action that runs a DELETE query. This is the example URI for WHfB:
https://graph.microsoft.com/v1.0/users/[UserPrincipalName/authentication/windowsHelloForBusinessMethods/[ID]
The ID is from the last PARSE JSON action.
These are the URIs for all methods:
https://graph.microsoft.com/v1.0/users/[UserPrincipalName/authentication/windowsHelloForBusinessMethods/[ID]
https://graph.microsoft.com/v1.0/users/[UserPrincipalName/authentication/microsoftAuthenticatorMethods/[ID]
https://graph.microsoft.com/v1.0/users/[UserPrincipalName/authentication/fido2Methods/[ID]
https://graph.microsoft.com/v1.0/users/[UserPrincipalName/authentication/phoneMethods/[ID]
https://graph.microsoft.com/v1.0/users/[UserPrincipalName/authentication/temporaryAccessPassMethods/[ID]
https://graph.microsoft.com/v1.0/users/[UserPrincipalName/authentication/emailMethods/[ID]
Now that we have added the HTTP action in the Until action, we can add the Status code in the Loop until filter. It needs to loop until the status code is equal to 204.
Like with the previous described actions I would like to write the result of the delete authentication methods back to the SharePoint List. But in this case, we could have multiple authentication methods of one type, which would mean the success or failure status would be overwritten by the latest executed action, there we here use the variables initialized at the beginning of the flow.
We first add a Condition action in every case, under the Until action. The condition checks if the status code of the delete HTTP action is equal to 204.
Under both True and False of the condition we add a Append to string variable action.
As Name in select the name of the variable we initialized for the delete Authentication method variable.
As value, add Success in the action under True and Failed as value under False.
6 cases including their actions.
Next, we add a Compose action under the Append to string action, outside of the For each action. Close the Switch action to get a better overview of where to add the Compose action.
With this compose action we collect all results from the variables.
We add another Condition action. This condition action checks if the Outputs from the Compose action doesn’t contain Failed. If it contains failed, it would mean at least one of the delete authentication methods actions failed.
As last, we add an Update item action under True and False to write back the result back to the SharePoint List.
Again a part of the flow is finished!
Setting up the flow – part 4 (disable Windows devices)
In the fourth part of the flow, we disable the Windows devices in Entra ID. You may wonder why only Windows devices. The Graph call used to disable devices in Entra ID, only supports Windows devices when using application permissions: Update device – Microsoft Graph v1.0 | Microsoft Learn In my situation that shouldn’t be a real issue as in the next part of the flow we retire macOS, iOS and Android devices, but we don’t do that for Windows devices.
We scroll back to the top of the flow under True to add a parallel branch. We first query Entra ID for all registered devices of the user. There we add an HTTP GET action with the below URI:
https://graph.microsoft.com/v1.0/users/[UserPrincipalName]/registeredDevices?$select=id,displayName,deviceId,operatingSystem
The HTTP action is followed by a Parse JSON action.
We add a Filter array action (type Data operations) to the flow. With this action we filter out any device with an operating system other than Windows.
For that, we add value from the Parse JSON action to the From field and operatingSystem in the right field under Filter query. We filter on is equals to Windows.
We use an HTTP PATCH action to disable the Windows devices in Entra ID. The URI for this is:
https://graph.microsoft.com/v1.0/devices/[ID]
And we need to add the below in the Body field:
{
"accountEnabled": false
}
The HTTP action is automatically added to a For each action.
To write back the results to SharePoint, we add a Condition action (checks status code is equal to 204) and Append to string variables actions, like we did for the delete authentication methods part of the flow. Followed by a Compose action.
And this part of the flow is ended with a Condition and Update item action.
The flow is becoming quite large already 😊
Setting up the flow – part 5 (take device actions)
In the fifth part of the flow, we perform device actions.
In the default flow, we restart the Windows devices to speed up blocking access to corporate data as the sign in sessions are deleted. In case you have Defender for Endpoint licenses, the reboot action could (should) be replaced by an Isolate action.
For macOS, iOS and Android we trigger a retire action with Microsoft Intune.
We add a parallel branch again to add a HTTP GET action. With that we query all Intune managed devices from the user. The URI we use:
https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?$filter=userPrincipalName%20eq%20'[UserPrincipalName]'&$select=id,azureADDeviceId,deviceName,operatingSystem,userPrincipalName
The HTTP action is followed by a Parse JSON action.
I make use of another Switch action. I split up using operating system to be able to take a different action per OS.
Add cases for every OS you support; Windows, macOS, iOS, Android. Under every case we add an HTTP POST action, but with different URIs.
For Windows we perform a rebootnow action:
https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/[ID]/rebootNow
For macOS, iOS and Android we perform a retire action:
https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/[ID]/retire
As we did for other actions, we add a Condition (status code equals 204) with Append to string variable actions, followed by a Compose action to the flow to collect the results of the HTTP POST actions.
And we end the flow with a Condition and Update item action.
As very last, at the end of the flow, I add another Update item action and a Terminate action. The Update item action runs after a Has timed out or has failed.
The Update item action writes back an Error to the Other field in the SP List in case an (unknown) error occurs.
And the Terminate action terminates the flow and puts it in an error status.
With all these steps, we have come to one big flow performing a lot of actions in a couple of seconds to block end user access as soon as possible.
Deploy the Logic Apps flow via an ARM template
The flow is available on my GitHub repository.
I have made two versions available for easy deployment.
Hitting one of the two buttons below brings you to the Azure portal to directly deploy the template to your tenant.
I have the ‘default’ one:
And this is the one not performing a reboot for Windows devices but initiate an isolate action via Defender:
Select your subscription and resource group to which the flow should be deployed. Enter a name for the flow and follow the wizard to deploy the flow under Logic Apps service.
To assign the User administrator role and the needed application permissions needed to run the flow you can make use of the script I provide on GitHub.
Make sure to add your own tenant ID and ID of the Managed Identity to the script before running it.
One location where you can find the needed object ID of the system assigned managed identity is under Settings, Identity of the Logic App flow.
Authenticate the SharePoint connection:
Browse Development tools, API Connections, sharepointonline, General. Edit API connection click Authorize twice. Authenticate and click Save.
Open the flow via Development tools, Log app designer.
Here we need to add the object ID of the Entra ID group (that contains the employees allowed to perform this action).
And run through the SharePoint actions (both the When an item is created and Update item actions) to select the Site Address and List Name.
In case you try to save the flow when not all SP actions are configured correctly it shows an error. When you click on Open operation, it shows the action with an issue.
Don’t forget to hit the save button and we are ready to initiate the Revoke user access in case of an emergency flow!
Initiate a red button action
To start a red button action to revoke the user access you add a new item to the SharePoint List that contains the user principal name of the user.
And soon the flow runs.
When the flow is finished, the result is written back to the SP List.
The user account is disabled.
The registered authentication options of the user before the red button action.
Which are deleted soon after the flow is finished.
The Windows devices are disabled in Entra ID.
In case you implemented the version including the Defender isolation action, the isolation is triggered.
When switch to the user part of the action, we see that the user is signed out of an application like Microsoft Teams pretty fast.
When the user is signed in to for example Office.com via a web browser, the user is signed out of the session or when switching between online apps got a message the account is disabled.
And when the isolate action is triggered, the device is disconnected from the network.
What’s next
Which steps need to be taken in case of a user termination or compromised user account differs organization and on the available licenses. The isolate action is for example something you always would execute for your Windows devices, but if you don’t make use of the proper Defender license, you don’t have that option.
Blocking access to local data on Windows devices is still challenging. By resetting the password and revoking user sessions you will (almost) immediately block access to online portals and access to applications like Teams. But for a period of time the user is still able to sign into the device by using Windows Hello. A next step in this could be pushing a (remediation) script that deletes the Windows Hello container on the local device by running certutil /deletehellocontainer. Check https://github.com/imabdk/PowerShell/blob/master/Detect-WindowsHelloEnrollment.ps1 to get you started with creating a remediations script for that.
A next step in this solution could be creating a PowerApp which we can provide to our security officers. That way they could easily trigger a red button action from their mobile device. If I find some spare time, I’ll share that in part 2 of this post.
I hope this post gets you started in creating your own automation regarding this topic.
Thanks for reading!
1 Comment
How about privileged accounts such as admins? Least privileged access ive found is directory.readwrite.all for the SP.
Ive build a similar solution (not with logic apps) that uses powershell -> into a small GUI where the SOC can simply launch it, select the user, all users and then what action. However, for privileged accounts (which are usually more critical to address quickly) the directory.readwrite.all was required.
Also, revoking all MFA methods will not always work when some methods are set as preferred and no else is present, same for old entries (if they registered their methods long time ago). I have an error rate of 2-5% on a tenant with those (i usually roll out MFA to 15k+ environment and in many cases they cant use CA in the process, only registration campaign or registration policy, the registration policy however only looks for “true” or “false” under MFA-methods registered for user so therefor i revoke them very often). Have you encountered the same and is that solved with logic app?