Auditing the use of Managed Service Identity in Azure

Managed Identity in Azure quite simply provides an AAD backed identity for your Web App or Virtual Machine, in order to communicate with other Azure services without explicitly providing credentials.

The range of Azure services that you can communicate with is growing, for the sake of this blog post we’re not going to focus on a specific service – instead querying the control plane to find all applicable RBAC assignments that have been set up for our Managed Identity. Please note that the script and example is all focussed around App Service, not a VM.

Switching it on

Turning on Managed Identity for a Web App you’ve published to Azure is easy. Navigate to the Web App, under settings you’ll finding Managed Service Identity, then flip the toggle box on before hitting Save.

This is what happens under the covers;

The App Gets a nice GUID assigned, this should be familiar to those working with ApplicationId’s and ServicePrincipals.

Toggling it

If you remove the Managed Identity from the app, and then set it back on again then a new PrincipalId is generated and any permissions you’d set up for this identity onto other Azure services will have been removed.

Auditing the Identity permissions

In an ideal world you’ll have a deployment script that sets up permissions for your Web App or VM on it’s dependant services with the least privilege required, however having a way of auditing a deployed applications permissions is going to be helpful in getting to that state. The script I’ve made looks at;

  • All Web Apps in your Azure subscription
  • Reports RBAC assignments for the Web Apps Identity
  • Checks all Keyvaults for Access Policies that the Identity has been allowed to use

The script: https://github.com/Gordonby/Snippets/blob/master/Powershell/Get-ManagedIdentityAssignments.ps1

#Gordon.byers@microsoft.com
#Powershell script is provided as-is and without any warranty of any kind

$subscriptionId = ""

#See Verbose messages, if you like.
#$VerbosePreference = "continue"

#Connect to the right Azure subscription
Connect-AzureRmAccount
Select-AzureRmSubscription -subscriptionId $subscriptionId

#An array of keyvault strings to search.  You can be explicit about the Vault if you don't want to search all of them
$KeyVaults = Get-AzureRmKeyVault | Select-Object  -ExpandProperty VaultName
#$KeyVaults = "msidemo"
#$KeyVaults = @("msidemo", "NeVault")

#Get all the WebApps used in the subscription which use Managed Identity
$WebApps = Get-AzureRmWebApp
$webAppsWithMI = $WebApps | ? {$_.Identity.Type -eq 'SystemAssigned'}

$RbacSummary = @()
$kvSummary = @()

$webAppsWithMI | foreach-object {
    $WebAppName = $_.Name
    $PrincipalId = $_.Identity.PrincipalId

    Write-Verbose "Investigating Managed Identity on $WebAppName"

    Write-Verbose "Searching for $PrincipalId across Azure Resources in Subscription"
    $RbacAssignments = Get-AzureRmRoleAssignment -ObjectId $PrincipalId
    $RbacSummary += $RbacAssignments | Select-Object RoleDefinitionName, Scope | %{ Add-Member -inputObject $_ -passThru -type NoteProperty -name WebApp -Value $WebAppName}

    Write-Verbose "Searching for Key Vault assignments"
    $KeyVaults | % { 
        Write-Verbose "Searching access policies in Key Vault : $_"
        $kv = Get-AzureRmKeyVault -VaultName $_

        $AccessPoliciesForMI = $kv.AccessPolicies | ? {$_.ObjectId -eq $PrincipalId} | Select-Object PermissionsToSecretsStr, PermissionsToCertificatesStr | %{ Add-Member -inputObject $_ -passThru -type NoteProperty -name WebApp -Value $WebAppName} | %{ Add-Member -inputObject $_ -passThru -type NoteProperty -name KeyVault -Value $kv.VaultName}

        $kvSummary += $AccessPoliciesForMI
    }
}

Write-Output $RbacSummary | Select-Object WebApp, RoleDefinitionName, Scope;
Write-Output $kvSummary | Select-Object WebApp, KeyVault, PermissionsToSecretsStr, PermissionsToCertificatesStr;

The script populates two arrays with the pertinent information that you want to capture. From these arrays you can then start building a script that would restore the permissions to be used in a failure scenario.
Here’s what the they look like;