How to audit Key Vault Access Policy using Azure Resource Graph

If you didn't know it yet, I am a big fan of Azure Resource Graph. It gives me the ability to quickly answer business inquiries / people showing at my desk with questions regarding Azure resources. I got one last week: "What is all the Key Vault where "UserX" has access". Seems complicated? Not at all, follow me.

If you need a quick intro or refresh about what is Azure Resource Graph, jump here.

The requirement

Like stated above, the need was, please tell me all the Azure Key Vault instances where "UserX" (who is an admin) forgot to clean his access/remove itself after his maintenance ticket.

How I solved it

What I first did is query Azure Resource Graph to see the definition/properties I get for an Azure Key Vault instance, just to see if its definition contains the information I needed (in this case Access Policy)

where type =~ "Microsoft.KeyVault/vaults"
| limit 1

it returned me the following properties:

{
    "provisioningState": "RegisteringDns",
    "tenantId": "4f9ff0ff-bbb7-4cd0-ac4d-f8aea3f93bf6",
    "sku": {
        "name": "Standard",
        "family": "A"
    },
    "enabledForTemplateDeployment": false,
    "enabledForDiskEncryption": false,
    "enabledForDeployment": false,
    "accessPolicies": [
        {
            "permissions": {
                "secrets": [
                    "Get",
                    "List",
                    "Set",
                    "Delete",
                    "Recover",
                    "Backup",
                    "Restore"
                ],
                "certificates": [
                    "Get",
                    "List",
                    "Update",
                    "Create",
                    "Import",
                    "Delete",
                    "Recover",
                    "Backup",
                    "Restore",
                    "ManageContacts",
                    "ManageIssuers",
                    "GetIssuers",
                    "ListIssuers",
                    "SetIssuers",
                    "DeleteIssuers"
                ],
                "keys": [
                    "Get",
                    "List",
                    "Update",
                    "Create",
                    "Import",
                    "Delete",
                    "Recover",
                    "Backup",
                    "Restore"
                ]
            },
            "tenantId": "4f9ff0ff-bbb7-4cd0-ac4d-f8aea3f93bf6",
            "objectId": "5eaecd00-b76e-47ab-8645-16d9a75f5757"
        },
        {
            "permissions": {
                "secrets": [
                    "Get",
                    "List",
                    "Set",
                    "Delete",
                    "Recover",
                    "Backup",
                    "Restore"
                ],
                "certificates": [],
                "keys": []
            },
            "tenantId": "4f9ff0ff-bbb7-4cd0-ac4d-f8aea3f93bf6",
            "objectId": "5a99e6db-4ec7-43ac-9585-25eee5b479df"
        }
    ],
    "vaultUri": "https://key-vault-resource-graph.vault.azure.net/"
}

I found the information I need under the property accessPolicies. Now I needed to find the objectId of "UserX", this is easy, using PowerShell or Azure CLI, I can look it up.

# Azure CLI
az ad user show --upn "userx@mycompany.com" --query objectId

#PowerShell
Get-AzADUser -UserPrincipalName 'userx@mycompany.com' | Select-Object -ExpandProperty Id

Once I have this information, I can now build up the Resource Graph query to ask about all Key Vault where this objectId is present in the accessPolicies.

where type =~ "Microsoft.KeyVault/vaults"
| mvexpand policy = properties.accessPolicies
| where policy.objectId == '5a99e6db-4ec7-43ac-9585-25eee5b479df'
| project name, resourceGroup

Let's Understand What This Query Does

The secret sauce here is mvexpand. It will expand a multi-value array or property bag into separate results, allowing you to query each item in the accessPolicies array.

  1. It will first filter to only return types Microsoft.KeyVault/vaults (Key Vault).
  2. It will extend the result set by creating a separate row for each accessPolicies in that Key Vault.
  3. It will filter results to the objectId we looked up earlier.

Use it as often as possible

I invite you to play more and more with Azure Resource Graph, it is a very valuable tool in your tool belt. In all transparency, this is the original query I use to lookup the information I needed last week:

where type =~ "Microsoft.Keyvault/vaults"
| extend p=todynamic(properties)
| extend policies=todynamic(tostring(p.accessPolicies)) 
| mvexpand policy = policies 
| where tostring(policy.objectId) == '5a99e6db-4ec7-43ac-9585-25eee5b479df'
| project name, resourceGroup

I originally took pieces here and there from Log Analytics query I had for policies and compliance . Taking a step back after, I was able to trim it to be more effective. We learn every day!

Resources