Summary: This is a bicep module that deploys a dashboard to monitor Azure App Configurations.
Date: 9 January 2025
Check here for a previous post for modules on deploying and configuring an Azure App Configuration Store.
The module below is the bicep module to deploy a dashboard to monitor an Azure App Configuration. The dashboard will display the following panels:
/*
DESCRIPTION
Deploys a dashboard for monitoring app configuration
LINKS:
- Resource: https://learn.microsoft.com/en-us/azure/templates/microsoft.portal/dashboards
*/
targetScope = 'resourceGroup'
// parameters
param location string
param environment string
param appConfigurationName string
param appConfigurationRg string
// existing
resource configStore 'Microsoft.AppConfiguration/configurationStores@2023-03-01' existing = {
scope: resourceGroup(appConfigurationRg)
name: appConfigurationName
}
// defined parameters
param dashboardName string = 'AppConfig'
resource dashboard 'Microsoft.Portal/dashboards@2019-01-01-preview' = {
name: 'dashboard-${environment}-${dashboardName}'
location: location
tags: {
'hidden-title': 'dashboard-${environment}-${dashboardName}'
}
properties: {
lenses: {
'0': {
order: 0
parts: {
'0': {
position: {
x: 0
y: 0
colSpan: 12
rowSpan: 2
}
metadata: {
inputs: []
type: 'Extension/HubsExtension/PartType/MarkdownPart'
settings: {
content: {
content: '__Useful information__\n\n<a href=\'https://learn.microsoft.com/en-us/azure/azure-app-configuration/\' target=\'_blank\'>Azure App Configuration documentation</a><br>\n<a href=\'https://learn.microsoft.com/en-us/azure/azure-app-configuration/monitor-app-configuration?tabs=portal\' target=\'_blank\'>Monitoring App Configuration</a>\n\n'
title: 'App Configuration Dashboard'
subtitle: 'A managed service that helps developers centralize their application and feature settings simply and securely'
markdownSource: 1
markdownUri: ''
}
}
}
}
'1': {
position: {
x: 0
y: 2
colSpan: 12
rowSpan: 4
}
metadata: {
inputs: [
{
name: 'partTitle'
value: 'Query 1'
isOptional: true
}
{
name: 'query'
value: 'resources\r\n| where type == "microsoft.appconfiguration/configurationstores"\r\n| extend sku = sku.name\r\n| extend creationDate = todatetime(properties.creationDate)\r\n| project name, resourceGroup, creationDate, location, sku, subscriptionId'
isOptional: true
}
{
name: 'chartType'
isOptional: true
}
{
name: 'isShared'
isOptional: true
}
{
name: 'queryId'
value: ''
isOptional: true
}
{
name: 'formatResults'
value: true
isOptional: true
}
{
name: 'queryScope'
value: {
scope: 0
values: []
}
isOptional: true
}
]
type: 'Extension/HubsExtension/PartType/ArgQueryGridTile'
settings: {}
partHeader: {
title: 'Overview App Configurations'
subtitle: 'Resource Graph Explorer'
}
}
}
'2': {
position: {
x: 0
y: 6
colSpan: 12
rowSpan: 4
}
metadata: {
inputs: [
{
name: 'resourceTypeMode'
isOptional: true
}
{
name: 'ComponentId'
isOptional: true
}
{
name: 'Scope'
value: {
resourceIds: [
configStore.id
]
}
isOptional: true
}
{
name: 'PartId'
value: '1158470d-5c03-431a-859f-6309b00a054e'
isOptional: true
}
{
name: 'Version'
value: '2.0'
isOptional: true
}
{
name: 'TimeRange'
isOptional: true
}
{
name: 'DashboardId'
isOptional: true
}
{
name: 'DraftRequestParameters'
value: {
scope: 'hierarchy'
}
isOptional: true
}
{
name: 'Query'
value: 'AACHttpRequest\n | where TimeGenerated > ago(14d)\n | extend Day = startofday(TimeGenerated)\n | summarize requestcount=sum(HitCount) by Day\n | render columnchart \n'
isOptional: true
}
{
name: 'ControlType'
value: 'FrameControlChart'
isOptional: true
}
{
name: 'SpecificChart'
value: 'StackedColumn'
isOptional: true
}
{
name: 'PartTitle'
value: 'Analytics'
isOptional: true
}
{
name: 'PartSubTitle'
value: appConfigurationName
isOptional: true
}
{
name: 'Dimensions'
value: {
xAxis: {
name: 'Day'
type: 'datetime'
}
yAxis: [
{
name: 'requestcount'
type: 'long'
}
]
splitBy: []
aggregation: 'Sum'
}
isOptional: true
}
{
name: 'LegendOptions'
value: {
isEnabled: true
position: 'Bottom'
}
isOptional: true
}
{
name: 'IsQueryContainTimeRange'
value: true
isOptional: true
}
]
type: 'Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart'
settings: {
content: {
Query: 'AACHttpRequest\n | extend Day = startofday(TimeGenerated)\n | summarize requestcount=sum(HitCount) by Day\n | render columnchart \n\n'
IsQueryContainTimeRange: false
}
}
filters: {
MsPortalFx_TimeRange: {
model: {
format: 'local'
granularity: 'auto'
relative: '30d'
}
}
}
partHeader: {
title: 'Usage'
subtitle: appConfigurationName
}
}
}
'3': {
position: {
x: 0
y: 10
colSpan: 12
rowSpan: 8
}
metadata: {
inputs: [
{
name: 'resourceTypeMode'
isOptional: true
}
{
name: 'ComponentId'
isOptional: true
}
{
name: 'Scope'
value: {
resourceIds: [
configStore.id
]
}
isOptional: true
}
{
name: 'PartId'
value: '12373b3a-2894-4b2b-ae71-6ea9a910aa19'
isOptional: true
}
{
name: 'Version'
value: '2.0'
isOptional: true
}
{
name: 'TimeRange'
value: 'P1D'
isOptional: true
}
{
name: 'DashboardId'
isOptional: true
}
{
name: 'DraftRequestParameters'
value: {
scope: 'hierarchy'
}
isOptional: true
}
{
name: 'Query'
value: '// Most recent delete key-value operations \n// List the most recent deleting key-value operations in App Config data plane. \n// This query helps retrieve the most recent 10 audit logs for deleting key-value operations in App Configuration data plane.\nAACAudit\n| where EventCategory == "ApplicationManagement" // and OperationName == "delete-keyvalue"\n| where Status == "Finished"\n| extend Target = tostring(split(TargetResource.TargetResourceName, "/")[4])\n| extend CallerType = iif( isempty(CallerIdentity[3])\n , CallerIdentity[0].callerIdentityType\n , CallerIdentity[3].callerIdentityType\n )\n| extend CallerId = iif( isempty( CallerIdentity[3])\n , CallerIdentity[0].callerIdentity\n , CallerIdentity[3].callerIdentity\n )\n| sort by TimeGenerated desc\n| project TimeGenerated, CallerType, CallerId, Target, OperationName\n\n'
isOptional: true
}
{
name: 'ControlType'
value: 'AnalyticsGrid'
isOptional: true
}
{
name: 'SpecificChart'
isOptional: true
}
{
name: 'PartTitle'
value: 'Analytics'
isOptional: true
}
{
name: 'PartSubTitle'
value: appConfigurationName
isOptional: true
}
{
name: 'Dimensions'
isOptional: true
}
{
name: 'LegendOptions'
isOptional: true
}
{
name: 'IsQueryContainTimeRange'
value: false
isOptional: true
}
]
type: 'Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart'
settings: {
content: {
GridColumnsWidth: {
CallerId: '276px'
Target: '336px'
CallerType: '128px'
}
}
}
partHeader: {
title: 'Last Changes'
subtitle: appConfigurationName
}
}
}
'4': {
position: {
x: 0
y: 18
colSpan: 12
rowSpan: 4
}
metadata: {
inputs: [
{
name: 'resourceTypeMode'
isOptional: true
}
{
name: 'ComponentId'
isOptional: true
}
{
name: 'Scope'
value: {
resourceIds: [
configStore.id
]
}
isOptional: true
}
{
name: 'PartId'
value: '9e8320bf-09ea-4a19-949d-d5c0a30ad2b0'
isOptional: true
}
{
name: 'Version'
value: '2.0'
isOptional: true
}
{
name: 'TimeRange'
value: 'P7D'
isOptional: true
}
{
name: 'DashboardId'
isOptional: true
}
{
name: 'DraftRequestParameters'
value: {
scope: 'hierarchy'
}
isOptional: true
}
{
name: 'Query'
value: 'AACHttpRequest\n| where StatusCode != 200\n| summarize ErrorCount=count() by bin(TimeGenerated, 1d), StatusCode\n| extend StatusCode = tostring(StatusCode)\n| render columnchart with (kind=stacked, series=StatusCode)\n\n'
isOptional: true
}
{
name: 'ControlType'
value: 'FrameControlChart'
isOptional: true
}
{
name: 'SpecificChart'
value: 'StackedColumn'
isOptional: true
}
{
name: 'PartTitle'
value: 'Analytics'
isOptional: true
}
{
name: 'PartSubTitle'
value: appConfigurationName
isOptional: true
}
{
name: 'Dimensions'
value: {
xAxis: {
name: 'TimeGenerated'
type: 'datetime'
}
yAxis: [
{
name: 'ErrorCount'
type: 'long'
}
]
splitBy: [
{
name: 'StatusCode'
type: 'string'
}
]
aggregation: 'Sum'
}
isOptional: true
}
{
name: 'LegendOptions'
value: {
isEnabled: true
position: 'Bottom'
}
isOptional: true
}
{
name: 'IsQueryContainTimeRange'
value: false
isOptional: true
}
]
type: 'Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart'
settings: {}
filters: {
MsPortalFx_TimeRange: {
model: {
format: 'local'
granularity: 'auto'
relative: '7d'
}
}
}
partHeader: {
title: 'HTTP error codes'
subtitle: appConfigurationName
}
}
}
}
}
}
metadata: {
model: {
timeRange: {
value: {
relative: {
duration: 24
timeUnit: 1
}
}
type: 'MsPortalFx.Composition.Configuration.ValueTypes.TimeRange'
}
filterLocale: {
value: 'en-us'
}
filters: {
value: {
MsPortalFx_TimeRange: {
model: {
format: 'local'
granularity: 'auto'
relative: '24h'
}
displayCache: {
name: 'Local Time'
value: 'Past 24 hours'
}
}
}
}
}
}
}
}
This wiki has been made possible by: