Use Azure Functions to Remove Unauthorized Role Assignments


Recently, one of my customers faced a challenge regarding the assignment of Role Definitions to workload teams on their Subscriptions. Their current configuration uses Entitlement Management, in combination with Privileged Identity Management (PIM), to grant a set of standing and eligible Role Assignments to workload teams. In this way, individual users would be able to elevate to the Contributor Role Definition on the scope of their own Subscription.

Even though the Contributor Role Definition grants users a lot of rights, in some cases it is not sufficient. For instance, it does not allow for the creation of a key, secret or in an Azure Key Vault. Instead, Access Policies or another Role Definition (e.g. Key Vault Administrator) need to be used.

In many cases, the assignment of for example the Owner or User Access Administrator Role Definition would do the trick as it enables workload teams to create their own Role Assignments. Unfortunately, this defeats the purpose of the above-mentioned identity solutions since PIM can be circumvented by assigning Role Assignments on a permanent basis. For that reason, the customer agreed to grant users the ability to create Role Assignments, but only on the scope of an individual Azure resource (e.g. Azure Key Vault).

In this blogpost, I will elaborate on the solution that was built to enforce the creation of Role Assignments at the resource scope only. At the end of this article, you can also find a link to the GitHub repository containing all the artifacts that I used to build the solution.

Solution Overview

To prevent the creation of Role Assignments at the Resource Group scope or above, different Azure services are used. In Figure 1, these Azure services, and the role these play in the overall solution, are visualized in more detail.

Figure 1: Solution OverviewFigure 1: Solution Overview

As you can see in Figure 1, the solution comprises different Azure services, denoted by a number.

  1. First, a Log Search Alert Rule is being used to create an Alert when a Role Assignment is created on the scope of a Resource Group or a Subscription. Apart from that, the Log Search Alert Rule is linked to an Action Group that triggers the Function App.
  2. The Function App is hosted on an App Service plan.
  3. The Function App is linked to the above-mentioned Action Group and contains one Function running PowerShell.
  4. To execute the PowerShell code in the Function, a System-assigned Managed Identity is linked to the Function App.
  5. The Account is used by the Function App for operations such as managing triggers and logging function executions.
  6. Finally, Application Insights is used to monitor the Functions in your Function App.

The execution flow is also visualized in Figure 1, this time with the use of letters.

  1. When an identity creates a Role Assignment on the scope of either a Subscription or Resource Group, the Log Search Alert Rule creates an Alert.
  2. If the Alert is triggered, the Action Group makes sure that it subsequently triggers the Function that is part of the Function App.
  3. With the use of the JSON payload that is sent to the Function, a PowerShell script is executed to remove the unauthorized Role Assignment.
  4. To do so, the Function App uses a System-assigned Managed Identity that has the permissions to remove Role Assignments.

With the solution overview out of the way, let's have a look at how the solution works in practice.

Putting Theory into Practice

As you can see in Figure 2, I am starting the flow with the creation of a Role Assignment on the scope of a Subscription.

Figure 2: Create a Role AssignmentFigure 2: Create a Role Assignment

I have configured my Log Search Alert Rule in such a way that it will run a KQL query every 5 minutes while using a 5-minute timeframe. The KQL query is formatted so that it detects Role Assignments created on a Resource Group or Subscription scope.

On top of that, the KQL query excludes Role Assignments created by PIM since these are only valid for a certain amount of time and fit the Role Based Access Control (RBAC) model of the customer. In Figure 3, you can see that the KQL query, associated with the Log Search Alert Rule, found the Role Assignment I just created.

Figure 3: Check the KQL Query ResultsFigure 3: Check the KQL Query Results

If you want to create a similar Log Search Alert Rule, use the KQL query contained in the code block below. In order to account for other use cases, such as the exclusion of certain Service Principals, you can use the KQL query as a starting point and subsequently extend it.

| where CategoryValue =~ “Administrative”
and OperationNameValue =~ “Microsoft.Authorization/roleAssignments/write”
and ActivityStatusValue =~ “Success” // Only select the creation of Role Assignments that succeeded
| where _ResourceId matches regex “^/subscriptions/[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}/providers/microsoft.authorization/roleassignments/[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$” or _ResourceId matches regex “^/subscriptions/[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}/resourcegroups/[a-zA-Z0-9._-()]+/providers/microsoft.authorization/roleassignments/[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$” // Only select the creation of Role Assignments on the Subscription or Resource Group scope
| where Caller != “87794bfc-4bf9-4695-b010-154387cedcc3″// Only select the creation of Role Assignments not conducted Privileged Identity Management (87794bfc-4bf9-4695-b010-154387cedcc3)
| project TimeGenerated, Caller, _SubscriptionId, _ResourceId // Only project information on when the Role Assignment is created, by whom, and on what Azure resource

Be aware, you need to export the Activity Logs of all relevant Subscriptions to the Log Analytics Workspace that is targeted by the Log Search Alert Rule. If you have not done so, the KQL query will not provide any results and thus not result in the creation of an Alert. Next to that, you can adapt the KQL query to your liking by for instance excluding the creation of Role Assignments by certain, centrally managed Service Principals as well.

As the Log Search Alert Rule is triggered, an Alert is created in as you can see in Figure 4.

Figure 4: Check the Alert ResultsFigure 4: Check the Alert Results

In this solution, the Action Group that is associated with the Log Search Alert Rule triggers an Azure Function upon the creation of the Alert. In Figure 5, this configuration is visualized in more detail.

Figure 5: Check the Action Group ConfigurationFigure 5: Check the Action Group Configuration

After the Alert has triggered the Azure Function, the Monitor tab should provide details on the invocations and logs. Especially once you have integrated your Function App with Application Insights, the logs can be very detailed. In Figure 6, you can see that the Alert has just triggered the Azure Function.

Figure 6: Check the Azure Function LogsFigure 6: Check the Azure Function Logs

Based on the JSON payload of the Alert, the PowerShell script retrieves the Role Assignment and subsequently removes it. Even though the example only focuses on one single Role Assignment, the solution can handle large amounts of Role Assignments simultaneously.

After the Azure Function has run successfully, the unauthorized Role Assignment has been removed as is displayed by the Activity Logs in Figure 7. As you can see, the removal was conducted by the System-assigned Managed Identity, associated with the Azure Function.

Figure 7: Check the Activity LogsFigure 7: Check the Activity Logs

How can you use this solution?

Since the blogpost does not provide detailed information on the configuration of the solution, I have uploaded all code in a public GitHub repository. If more information on the configuration of the solution, and the use of the different artifacts in the GitHub repository is needed, please let me know so that I can then create a follow-up blogpost.


The sample are not supported by any Microsoft standard support program or service. The sample are provided AS IS without a warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.


This article was originally published by Microsoft's Secure Blog. You can find the original article here.