The DevOps Jedi

Taking the cloud by storm one line of code at a time....

Exporting Azure Management Group Activity Logs

2024-03-124 min readIaCDarren Johnson

I’ve been working with Management Groups a lot recently in my day job and have really seen the benefit of being able to enforce governance by assigning RBAC roles and policies once, and have all new subscriptions inherit them. However, I soon realised there was no way in the Azure Portal or the native Azure RM Terraform provider to export the Activity Logs to a Log Analytics Workspace for use with Sentinel, so I decided to figure out a solution to this.

I had created a Management Group named platform-management under the Tenant Root Group in line with Microsoft’s recommendation and this Management Group would be used to assign platform wide policies and role assignments.

If you look in the portal at your Management Group, you’ll notice there is an ‘Export Activity Logs’ button, however upon clicking this it just shows you the resources within a selected subscription, and whether they have diagnostics enabled or not.

Export Activity Logs Button

Resource Diagnostics

This felt a like a big gap in Azure’s security monitoring capability, so I checked to see if this was possible using the Azure APIs, and discovered that exporting the logs was supported.

Experience told me from working with APIs previously I would need the Management Group Resource ID, so I grabbed it from an activity log entry as there was no Properties section in the Resource Menu. I could have easily have retrieved it programatically via PowerShell using the command:

Get-AzManagementGroup -GroupId 'platform-management'

Microsoft had documented how to use the AzAPI provider for diagnostic settings , so I decided to pull this info together and dive straight into Terraform.

The first thing I wanted to know was what options could be selected, and as there was no GUI to tell me, I had to look at the example from the API documentation which showed there were in fact 2 categories, Administrative and Policy. Armed with this information, I figured I had enough info to write the Terraform configuration.

Firstly I added a datasource for the Management Group as I knew I would need to dynamically retrieve the Resource ID.

Next, I took the sample code from the Microsoft Example and cross referenced the arguments to the HTTP API example. Anything that wasn’t listed or required was removed.

I ran a terraform apply and it was successful first time!

I validated this by assigning a policy that wouldn’t make any changes to my subscription at the ‘platform-management’ scope, and then waited (about 45 minutes in total) for the Azure Activity Logs to appear in Log Analytics which they did.

This was a pretty simple Terraform configuration to write which used the AzAPI provider and hopefully it will help you onboard your Management Groups.

A few things to note before I share a sample of my code:

  • I dynamically generate all resource names to ensure consistency across my deployments so the name of the diagnostic setting here is simply platform-management-diag
  • The Log Analytics Workspace resource is also part of the same configuration, so I reference it’s ID directly but you could use a data source for this purpose
  • I wanted to see the JSON response body of the API command, so I added the response_export_values = ["*"] argument and created an output to do this
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.94"
    }
    azapi = {
      source  = "Azure/azapi"
      version = "~> 1.12"
    }
  }
  required_version = "~> 1.7.4"
}

data "azurerm_management_group" "platform_management" {
  display_name = "platform-management"
}

resource "azapi_resource" "management_group_export_activity_logs" {
  name      = join("-", [data.azurerm_management_group.platform_management.name, "diag"])
  parent_id = data.azurerm_management_group.platform_management.id
  type      = "Microsoft.Insights/diagnosticSettings@2021-05-01-preview"
  body = jsonencode({
    properties = {
      logs = [
        {
          category = "Administrative"
          enabled  = true
        },
        {
          category = "Policy"
          enabled  = true
        }
      ]
      workspaceId = azurerm_log_analytics_workspace.monitoring.id
    }
  })
  response_export_values = ["*"]
}

output "management_group_export_activity_logs_api_response" {
  value = jsondecode(azapi_resource.management_group_export_activity_logs.output)
}

When using the AzAPI provider be mindful that when you run a terraform destroy command it doesn’t remove the Azure resource that had been created, so I added an output to remind me of the az cli command to do this.

output "azapi_delete_command" {
  value = <<EOT

                         _____ _____ 
     /\            /\   |  __ \_   _|
    /  \    ____  /  \  | |__) || |  
   / /\ \  |_  / / /\ \ |  ___/ | |  
  / ____ \  / / / ____ \| |    _| |_ 
 /_/    \_\/___/_/    \_\_|   |_____|

This configuration has used the AzAPI Provider.  A limitation of this provider is it cannot remove the configuration deployed.

Be sure to run the command below before destroying this configuration:

az rest --method delete --uri "https://management.azure.com${data.azurerm_management_group.platform_management.id}/providers/Microsoft.Insights/diagnosticSettings/${azapi_resource.management_group_export_activity_logs.name}?api-version=2020-01-01-preview"

Have a nice day!
EOT
}