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 certificate 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 blog post, 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.
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.
As you can see in Figure 1, the solution comprises different Azure services, denoted by a number.
The execution flow is also visualized in Figure 1, this time with the use of letters.
With the solution overview out of the way, let’s have a look at how the solution works in 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.
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.
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.
AzureActivity
| 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 Azure Monitor as you can see in Figure 4.
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.
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.
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.
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 scripts are not supported by any Microsoft standard support program or service. The sample scripts 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 scripts 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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.