If you use Azure PowerShell it is easy to conduct a “who created what”-style audit of your subscription.

Simply run
Get-AzureRMLog -ResourceGroup "ResourceGroupName" -StartTime Start Date and Time (local formatting) -EndTime End Date and Time -DetailedOutput

Passing the -DetailedOutput switch will make the call surface a list of claims for each call that was recorded against the resource group.
You can use “OperationName” to easily isolate any write activity and the claims information to display the caller details in clear text.

After a bit of trial and error you might end up with a script like this
$results = Get-AzureRMLog -StartTime 17/08/2017 -DetailedOutput | Where-Object { $_.OperationName.EndsWith("write") }

Write-Host "Event Name | Resource Group Name | Resource Name | Operation Name | Caller | Identifier " -ForegroundColor Yellow

foreach($result in $results) {
Write-Host $result.EventName "|" $result.ResourceGroupName "|" $result.ResourceId.Substring($result.ResourceId.LastIndexOf("/") + 1) "|" $result.OperationName "|" $result.Caller "|" $result.Claims.Content.'http://schemas.microsoft.com/identity/claims/objectidentifier'

This exposes the event, the resource group, and the resource in each transaction.
When running the piece of PowerShell, you will notice that you do not always get a caller or an object id back.

If you review the areas marked in red, it becomes clear that this is a system action. (Azure Security Centre generating a new recommendation based on a resource we recently created)
The area marked in yellow is a deployment made by a service account or service principal. These accounts do not have a caller identifier.

You will also notice that there are actions that are not assigned to a particular resource group. In our case this is the cascading effect of an RBAC assignment made at subscription level.
As you develop your audit job, you will get an understanding of what actions you care about and which ones are safe to ignore in your organisation. Role Assignments might be one of the things you may want to audit separately from resource creation.

To add more context, we can query the object IDs that we get back to make sure that they are indeed service principals. We can also display a message if we have clearly identified a platform action.
The following code does this.

$results = Get-AzureRMLog -StartTime 17/08/2017 -DetailedOutput | Where-Object { $_.OperationName.EndsWith("write") }

Write-Host "Event Name | Resource Group Name | Resource Name | Operation Name | Caller | Identifier " -ForegroundColor Yellow

foreach($result in $results) {

Write-Host $result.EventName "|" $result.ResourceGroupName "|" $result.ResourceId.Substring($result.ResourceId.LastIndexOf("/") + 1) "|" $result.OperationName "|" $result.Caller "|" $result.Claims.Content.'http://schemas.microsoft.com/identity/claims/objectidentifier'

if($result.Claims.Content.'http://schemas.microsoft.com/identity/claims/objectidentifier' -eq $null) {
Write-Host "System action" -ForegroundColor Yellow
}

if(($result.Caller -eq $null) -or ($result.Caller -eq "")) {

$caller = $null
# Making sure it is definitely not a user before querying for the service principal name
if(-not($caller = Get-AzureRmADUser -ObjectId $result.Claims.Content.'http://schemas.microsoft.com/identity/claims/objectidentifier')) {

$caller = Get-AzureRmADServicePrincipal -ObjectId $result.Claims.Content.'http://schemas.microsoft.com/identity/claims/objectidentifier'

}

Write-Host "Actual caller: " $caller.DisplayName -ForegroundColor Cyan

}

}

The result is a clearer representation of what is going on in the subscription:

Once you are happy with your audit job you can run it as a runbook in Azure Automation, to regularly pull this data. You will need to dump it to a type of storage, (table storage for example) and work out a way of deduplicating your data.

Another important thing to consider is that Azure Automation uses a service principal to run automation runbooks. Even if this service principal is an owner of the target subscription, it will still need additional read privileges for the Graph API to look up service principals and other identities. These are:

  • Read directory data
  • Read user profiles
  • Read group profiles

If you are a directory administrator you can grant your service principal these privileges through Azure AD.
If you are a normal user, you can set the privileges up, before having to get an administrator to grant them.

More information on consuming APIs with a Service Principal

This article is part of the Azure Governance series

One thought on “Azure Governance | Using Azure Logs to tie resources to owners

Comments are closed.