diff --git a/infrastructure/templates/public-api/application/public-api/publicApiApp.bicep b/infrastructure/templates/public-api/application/public-api/publicApiApp.bicep index 2b62ca27c1..72a2120777 100644 --- a/infrastructure/templates/public-api/application/public-api/publicApiApp.bicep +++ b/infrastructure/templates/public-api/application/public-api/publicApiApp.bicep @@ -154,15 +154,11 @@ module apiContainerAppModule '../../components/containerApp.bicep' = { maxReplicas: resourceAndScalingConfig.maxReplicas scaleAtConcurrentHttpRequests: resourceAndScalingConfig.scaleAtConcurrentHttpRequests workloadProfileName: resourceAndScalingConfig.workloadProfileName - tagValues: tagValues - } -} - -module containerAppRestartsAlert '../../components/alerts/containerApps/restarts.bicep' = if (deployAlerts) { - name: '${resourceNames.publicApi.apiApp}RestartsDeploy' - params: { - resourceNames: [resourceNames.publicApi.apiApp] - alertsGroupName: resourceNames.existingResources.alertsGroup + alerts: deployAlerts ? { + restarts: true + responseTime: true + alertsGroupName: resourceNames.existingResources.alertsGroup + } : null tagValues: tagValues } } diff --git a/infrastructure/templates/public-api/application/public-api/publicApiDataProcessor.bicep b/infrastructure/templates/public-api/application/public-api/publicApiDataProcessor.bicep index 9e7719ab20..1e214dc312 100644 --- a/infrastructure/templates/public-api/application/public-api/publicApiDataProcessor.bicep +++ b/infrastructure/templates/public-api/application/public-api/publicApiDataProcessor.bicep @@ -112,41 +112,14 @@ module dataProcessorFunctionAppModule '../../components/functionApp.bicep' = { mountPath: publicApiDataFileShareMountPath }] storageFirewallRules: storageFirewallRules - tagValues: tagValues - } -} - -module functionAppHealthAlert '../../components/alerts/sites/healthAlert.bicep' = if (deployAlerts) { - name: '${resourceNames.publicApi.dataProcessor}HealthDeploy' - params: { - resourceNames: [resourceNames.publicApi.dataProcessor] - alertsGroupName: resourceNames.existingResources.alertsGroup - tagValues: tagValues - } -} - -module storageAccountAvailabilityAlerts '../../components/alerts/storageAccounts/availabilityAlert.bicep' = if (deployAlerts) { - name: '${resourceNames.publicApi.dataProcessor}StorageAvailabilityDeploy' - params: { - resourceNames: [ - dataProcessorFunctionAppModule.outputs.managementStorageAccountName - dataProcessorFunctionAppModule.outputs.slot1StorageAccountName - dataProcessorFunctionAppModule.outputs.slot2StorageAccountName - ] - alertsGroupName: resourceNames.existingResources.alertsGroup - tagValues: tagValues - } -} - -module fileServiceAvailabilityAlerts '../../components/alerts/fileServices/availabilityAlert.bicep' = if (deployAlerts) { - name: '${resourceNames.publicApi.dataProcessor}FsAvailabilityDeploy' - params: { - resourceNames: [ - dataProcessorFunctionAppModule.outputs.managementStorageAccountName - dataProcessorFunctionAppModule.outputs.slot1StorageAccountName - dataProcessorFunctionAppModule.outputs.slot2StorageAccountName - ] - alertsGroupName: resourceNames.existingResources.alertsGroup + alerts: deployAlerts ? { + functionAppHealth: true + storageAccountAvailability: true + storageLatency: true + fileServiceAvailability: true + fileServiceLatency: true + alertsGroupName: resourceNames.existingResources.alertsGroup + } : null tagValues: tagValues } } diff --git a/infrastructure/templates/public-api/application/public-api/publicApiStorage.bicep b/infrastructure/templates/public-api/application/public-api/publicApiStorage.bicep index 1207e1a971..edf536d79f 100644 --- a/infrastructure/templates/public-api/application/public-api/publicApiStorage.bicep +++ b/infrastructure/templates/public-api/application/public-api/publicApiStorage.bicep @@ -51,6 +51,11 @@ module publicApiStorageAccountModule '../../components/storageAccount.bicep' = { firewallRules: storageFirewallRules skuStorageResource: 'Standard_LRS' keyVaultName: resourceNames.existingResources.keyVault + alerts: deployAlerts ? { + availability: true + latency: true + alertsGroupName: resourceNames.existingResources.alertsGroup + } : null tagValues: tagValues } } @@ -62,23 +67,11 @@ module dataFilesFileShareModule '../../components/fileShare.bicep' = { fileShareQuota: publicApiDataFileShareQuota storageAccountName: publicApiStorageAccountModule.outputs.storageAccountName fileShareAccessTier: 'TransactionOptimized' - } -} - -module storageAccountAvailabilityAlert '../../components/alerts/storageAccounts/availabilityAlert.bicep' = if (deployAlerts) { - name: '${resourceNames.publicApi.publicApiStorageAccount}AvailabilityDeploy' - params: { - resourceNames: [resourceNames.publicApi.publicApiStorageAccount] - alertsGroupName: resourceNames.existingResources.alertsGroup - tagValues: tagValues - } -} - -module fileServiceAvailabilityAlert '../../components/alerts/fileServices/availabilityAlert.bicep' = if (deployAlerts) { - name: '${resourceNames.publicApi.publicApiStorageAccount}FsAvailabilityDeploy' - params: { - resourceNames: [resourceNames.publicApi.publicApiStorageAccount] - alertsGroupName: resourceNames.existingResources.alertsGroup + alerts: deployAlerts ? { + availability: true + latency: true + alertsGroupName: resourceNames.existingResources.alertsGroup + } : null tagValues: tagValues } } diff --git a/infrastructure/templates/public-api/application/shared/appGateway.bicep b/infrastructure/templates/public-api/application/shared/appGateway.bicep index d93258d140..73fc3fed4a 100644 --- a/infrastructure/templates/public-api/application/shared/appGateway.bicep +++ b/infrastructure/templates/public-api/application/shared/appGateway.bicep @@ -51,15 +51,11 @@ module appGatewayModule '../../components/appGateway.bicep' = { backends: backends routes: routes rewrites: rewrites - tagValues: tagValues - } -} - -module backendPoolsHealthAlert '../../components/alerts/appGateways/backendPoolHealth.bicep' = if (deployAlerts) { - name: '${resourceNames.sharedResources.appGateway}BackendPoolsHealthDeploy' - params: { - resourceNames: [resourceNames.sharedResources.appGateway] - alertsGroupName: resourceNames.existingResources.alertsGroup + alerts: deployAlerts ? { + health: true + responseTime: true + alertsGroupName: resourceNames.existingResources.alertsGroup + } : null tagValues: tagValues } } diff --git a/infrastructure/templates/public-api/application/shared/postgreSqlFlexibleServer.bicep b/infrastructure/templates/public-api/application/shared/postgreSqlFlexibleServer.bicep index a5a9660913..ecca8569a1 100644 --- a/infrastructure/templates/public-api/application/shared/postgreSqlFlexibleServer.bicep +++ b/infrastructure/templates/public-api/application/shared/postgreSqlFlexibleServer.bicep @@ -58,6 +58,12 @@ module postgreSqlServerModule '../../components/postgresqlDatabase.bicep' = { firewallRules: formattedFirewallRules databaseNames: ['public_data'] privateEndpointSubnetId: privateEndpointSubnetId + alerts: deployAlerts ? { + availability: true + queryTime: true + transactionTime: true + alertGroupName: resourceNames.existingResources.alertsGroup + } : null tagValues: tagValues } } @@ -73,15 +79,6 @@ resource maxPreparedTransactionsConfig 'Microsoft.DBforPostgreSQL/flexibleServer ] } -module databaseAliveAlert '../../components/alerts/flexibleServers/databaseAlive.bicep' = if (deployAlerts) { - name: '${resourceNames.sharedResources.postgreSqlFlexibleServer}DbAliveDeploy' - params: { - resourceNames: [resourceNames.sharedResources.postgreSqlFlexibleServer] - alertsGroupName: resourceNames.existingResources.alertsGroup - tagValues: tagValues - } -} - var managedIdentityConnectionStringTemplate = postgreSqlServerModule.outputs.managedIdentityConnectionStringTemplate var dataProcessorPsqlConnectionStringSecretKey = 'ees-publicapi-data-processor-connectionstring-publicdatadb' diff --git a/infrastructure/templates/public-api/components/alerts/appGateways/responseTimeAlert.bicep b/infrastructure/templates/public-api/components/alerts/appGateways/responseTimeAlert.bicep new file mode 100644 index 0000000000..fa5fc24c9f --- /dev/null +++ b/infrastructure/templates/public-api/components/alerts/appGateways/responseTimeAlert.bicep @@ -0,0 +1,32 @@ +import { Severity } from '../types.bicep' + +@description('Names of the resources that these alerts are being applied to.') +param resourceNames string[] + +@description('The alert severity.') +param severity Severity = 'Warning' + +@description('Name of the Alerts Group used to send alert messages.') +param alertsGroupName string + +@description('Tags with which to tag the resource in Azure.') +param tagValues object + +module alerts '../dynamicMetricAlert.bicep' = [for name in resourceNames: { + name: '${name}ResponseTimeAlertModule' + params: { + alertName: '${name}-response-time' + resourceIds: [resourceId('Microsoft.Network/applicationGateways', name)] + resourceType: 'Microsoft.Network/applicationGateways' + query: { + metric: 'ApplicationGatewayTotalTime' + aggregation: 'Average' + operator: 'GreaterThan' + } + evaluationFrequency: 'PT1M' + windowSize: 'PT5M' + severity: severity + alertsGroupName: alertsGroupName + tagValues: tagValues + } +}] diff --git a/infrastructure/templates/public-api/components/alerts/containerApps/responseTimeAlert.bicep b/infrastructure/templates/public-api/components/alerts/containerApps/responseTimeAlert.bicep new file mode 100644 index 0000000000..7957c351fd --- /dev/null +++ b/infrastructure/templates/public-api/components/alerts/containerApps/responseTimeAlert.bicep @@ -0,0 +1,32 @@ +import { Severity } from '../types.bicep' + +@description('Names of the resources that these alerts are being applied to.') +param resourceNames string[] + +@description('The alert severity.') +param severity Severity = 'Warning' + +@description('Name of the Alerts Group used to send alert messages.') +param alertsGroupName string + +@description('Tags with which to tag the resource in Azure.') +param tagValues object + +module alerts '../dynamicMetricAlert.bicep' = [for name in resourceNames: { + name: '${name}LatencyAlertModule' + params: { + alertName: '${name}-latency' + resourceIds: [resourceId('Microsoft.App/containerApps', name)] + resourceType: 'Microsoft.App/containerApps' + query: { + metric: 'ResponseTime' + aggregation: 'Average' + operator: 'GreaterThan' + } + evaluationFrequency: 'PT1M' + windowSize: 'PT5M' + severity: severity + alertsGroupName: alertsGroupName + tagValues: tagValues + } +}] diff --git a/infrastructure/templates/public-api/components/alerts/dynamicMetricAlert.bicep b/infrastructure/templates/public-api/components/alerts/dynamicMetricAlert.bicep new file mode 100644 index 0000000000..35b09b032c --- /dev/null +++ b/infrastructure/templates/public-api/components/alerts/dynamicMetricAlert.bicep @@ -0,0 +1,123 @@ +import { + EvaluationFrequency + MetricName + DynamicMetricOperator + ResourceType + TimeAggregation + WindowSize + Severity + Sensitivity + severityMapping +} from 'types.bicep' + +@description('Name of the alert.') +param alertName string + +@description('Ids of the resources that this alert is being applied to.') +param resourceIds string[] + +@description('Type of the resource that this alert is being applied to.') +param resourceType ResourceType + +@description('The query being used to test if the alert should be fired.') +param query { + metric: MetricName + aggregation: TimeAggregation + operator: DynamicMetricOperator +} + +@description(''' +The frequency with which this alert rule evaluates the metrics against the dynamic thresholds. +For instance, PT1M with a window size of PT5M will evaluate the past 5 minutes' worth of metric data +against the dynamic threshold every minute. +''') +param evaluationFrequency EvaluationFrequency = 'PT1M' + +@description(''' +The timespan that is used to calculate the metric's value against the specified time aggregation. +For instance, PT5M with a time aggregation of "Average" will use 5 minutes of metric data to calculate +the average value, which is then compared to the dynamic threshold. +''') +param windowSize WindowSize = 'PT5M' + +@description(''' +How sensitive the alert is if a metric exceeds its dynamic threshold. +Low sensitivity means that this alert will fire only if the metric exceeds the threshold by a high degree. +High sensitivity means that this alert will fire if the metric exceeds the threshold to a much lower degree. +''') +param sensitivity Sensitivity = 'Low' + +@description(''' +How many periods to look back over to count failing periods. Used in conjunction with "minFailingPeriodsToAlert". +As an example, if "numberOfEvaluationPeriods" is set to 5 and "evaluationFrequency" is set to every minute, the past +5 alerts (one for each of the last 5 minutes) is looked at and each failure is counted up. +''') +param numberOfEvaluationPeriods int = 5 + +@description(''' +How many of the "numberOfEvaluationPeriods" results need to have failed in order for this rule to fire. +For instance, if this rule is using the past 5 calculations (with "numberOfEvaluationPeriods" set to 5) to evaluate +whether or not to fire, "minFailingPeriodsToAlert" determines how many of those past 5 periods have to have failed +in order for this rule to fire. If this was set to 3, 3 out of the 5 past calculations will have had to fail in +order for this rule to fire. +''') +param minFailingPeriodsToAlert int = 5 + +@description('The alert severity.') +param severity Severity = 'Error' + +@description(''' +An optional date that prevents machine learning algorithms from using metric data prior to this date in order to +calculate its dynamic threshold. +''') +param ignoreDataBefore string? + +@description('Name of the Alerts Group used to send alert messages.') +param alertsGroupName string + +@description('Tags with which to tag the resource in Azure.') +param tagValues object + +var severityLevel = severityMapping[severity] + +resource alertsActionGroup 'Microsoft.Insights/actionGroups@2023-01-01' existing = { + name: alertsGroupName +} + +resource metricAlertRule 'Microsoft.Insights/metricAlerts@2018-03-01' = { + name: alertName + location: 'Global' + properties: { + enabled: true + scopes: resourceIds + severity: severityLevel + evaluationFrequency: evaluationFrequency + windowSize: windowSize + criteria: { + 'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + allOf: [ + { + criterionType: 'DynamicThresholdCriterion' + name: 'Metric1' + metricName: query.metric + metricNamespace: resourceType + timeAggregation: query.aggregation + operator: query.operator + alertSensitivity: sensitivity + skipMetricValidation: false + failingPeriods: { + minFailingPeriodsToAlert: minFailingPeriodsToAlert + numberOfEvaluationPeriods: numberOfEvaluationPeriods + } + ignoreDataBefore: ignoreDataBefore + } + ] + } + actions: [ + { + actionGroupId: alertsActionGroup.id + } + ] + } + tags: tagValues +} diff --git a/infrastructure/templates/public-api/components/alerts/fileServices/latencyAlert.bicep b/infrastructure/templates/public-api/components/alerts/fileServices/latencyAlert.bicep new file mode 100644 index 0000000000..160cb49974 --- /dev/null +++ b/infrastructure/templates/public-api/components/alerts/fileServices/latencyAlert.bicep @@ -0,0 +1,32 @@ +import { Severity } from '../types.bicep' + +@description('Names of the resources that these alerts are being applied to.') +param resourceNames string[] + +@description('The alert severity.') +param severity Severity = 'Warning' + +@description('Name of the Alerts Group used to send alert messages.') +param alertsGroupName string + +@description('Tags with which to tag the resource in Azure.') +param tagValues object + +module alerts '../dynamicMetricAlert.bicep' = [for name in resourceNames: { + name: '${name}FsLatencyAlertModule' + params: { + alertName: '${name}-fileservice-latency' + resourceIds: [resourceId('Microsoft.Storage/storageAccounts/fileServices', name, 'default')] + resourceType: 'Microsoft.Storage/storageAccounts/fileServices' + query: { + metric: 'SuccessE2ELatency' + aggregation: 'Average' + operator: 'GreaterThan' + } + evaluationFrequency: 'PT1M' + windowSize: 'PT5M' + severity: severity + alertsGroupName: alertsGroupName + tagValues: tagValues + } +}] diff --git a/infrastructure/templates/public-api/components/alerts/flexibleServers/databaseAlive.bicep b/infrastructure/templates/public-api/components/alerts/postgreSqlFlexibleServers/databaseAlive.bicep similarity index 100% rename from infrastructure/templates/public-api/components/alerts/flexibleServers/databaseAlive.bicep rename to infrastructure/templates/public-api/components/alerts/postgreSqlFlexibleServers/databaseAlive.bicep diff --git a/infrastructure/templates/public-api/components/alerts/postgreSqlFlexibleServers/queryTimeAlert.bicep b/infrastructure/templates/public-api/components/alerts/postgreSqlFlexibleServers/queryTimeAlert.bicep new file mode 100644 index 0000000000..c9e51a2e8c --- /dev/null +++ b/infrastructure/templates/public-api/components/alerts/postgreSqlFlexibleServers/queryTimeAlert.bicep @@ -0,0 +1,32 @@ +import { Severity } from '../types.bicep' + +@description('Names of the resources that these alerts are being applied to.') +param resourceNames string[] + +@description('The alert severity.') +param severity Severity = 'Warning' + +@description('Name of the Alerts Group used to send alert messages.') +param alertsGroupName string + +@description('Tags with which to tag the resource in Azure.') +param tagValues object + +module alerts '../dynamicMetricAlert.bicep' = [for name in resourceNames: { + name: '${name}QueryTimeAlertModule' + params: { + alertName: '${name}-query-time' + resourceIds: [resourceId('Microsoft.DBforPostgreSQL/flexibleServers', name)] + resourceType: 'Microsoft.DBforPostgreSQL/flexibleServers' + query: { + metric: 'longest_query_time_sec' + aggregation: 'Maximum' + operator: 'GreaterThan' + } + evaluationFrequency: 'PT1M' + windowSize: 'PT5M' + severity: severity + alertsGroupName: alertsGroupName + tagValues: tagValues + } +}] diff --git a/infrastructure/templates/public-api/components/alerts/postgreSqlFlexibleServers/transactionTimeAlert.bicep b/infrastructure/templates/public-api/components/alerts/postgreSqlFlexibleServers/transactionTimeAlert.bicep new file mode 100644 index 0000000000..487313a7ee --- /dev/null +++ b/infrastructure/templates/public-api/components/alerts/postgreSqlFlexibleServers/transactionTimeAlert.bicep @@ -0,0 +1,32 @@ +import { Severity } from '../types.bicep' + +@description('Names of the resources that these alerts are being applied to.') +param resourceNames string[] + +@description('The alert severity.') +param severity Severity = 'Warning' + +@description('Name of the Alerts Group used to send alert messages.') +param alertsGroupName string + +@description('Tags with which to tag the resource in Azure.') +param tagValues object + +module alerts '../dynamicMetricAlert.bicep' = [for name in resourceNames: { + name: '${name}TransactionTimeAlertModule' + params: { + alertName: '${name}-query-time' + resourceIds: [resourceId('Microsoft.DBforPostgreSQL/flexibleServers', name)] + resourceType: 'Microsoft.DBforPostgreSQL/flexibleServers' + query: { + metric: 'longest_transaction_time_sec' + aggregation: 'Maximum' + operator: 'GreaterThan' + } + evaluationFrequency: 'PT1M' + windowSize: 'PT5M' + severity: severity + alertsGroupName: alertsGroupName + tagValues: tagValues + } +}] diff --git a/infrastructure/templates/public-api/components/alerts/staticMetricAlert.bicep b/infrastructure/templates/public-api/components/alerts/staticMetricAlert.bicep index 324df78ee6..86d3447652 100644 --- a/infrastructure/templates/public-api/components/alerts/staticMetricAlert.bicep +++ b/infrastructure/templates/public-api/components/alerts/staticMetricAlert.bicep @@ -38,7 +38,7 @@ resource alertsActionGroup 'Microsoft.Insights/actionGroups@2023-01-01' existing name: alertsGroupName } -resource metricAlertRule 'Microsoft.Insights/metricAlerts@2018-03-01' = { +resource metricAlertRule 'Microsoft.Insights/metricAlerts@2018-03-01' = if (length(resourceIds) == 1) { name: alertName location: 'Global' properties: { @@ -48,7 +48,7 @@ resource metricAlertRule 'Microsoft.Insights/metricAlerts@2018-03-01' = { evaluationFrequency: evaluationFrequency windowSize: windowSize criteria: { - 'odata.type': length(resourceIds) > 1 ? 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' : 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' + 'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' allOf: [{ criterionType: 'StaticThresholdCriterion' name: 'Metric1' diff --git a/infrastructure/templates/public-api/components/alerts/storageAccounts/latencyAlert.bicep b/infrastructure/templates/public-api/components/alerts/storageAccounts/latencyAlert.bicep new file mode 100644 index 0000000000..164c717cff --- /dev/null +++ b/infrastructure/templates/public-api/components/alerts/storageAccounts/latencyAlert.bicep @@ -0,0 +1,32 @@ +import { Severity } from '../types.bicep' + +@description('Names of the resources that these alerts are being applied to.') +param resourceNames string[] + +@description('The alert severity.') +param severity Severity = 'Warning' + +@description('Name of the Alerts Group used to send alert messages.') +param alertsGroupName string + +@description('Tags with which to tag the resource in Azure.') +param tagValues object + +module alerts '../dynamicMetricAlert.bicep' = [for name in resourceNames: { + name: '${name}LatencyAlertModule' + params: { + alertName: '${name}-latency' + resourceIds: [resourceId('Microsoft.Storage/storageAccounts', name)] + resourceType: 'Microsoft.Storage/storageAccounts' + query: { + metric: 'SuccessE2ELatency' + aggregation: 'Average' + operator: 'GreaterThan' + } + evaluationFrequency: 'PT1M' + windowSize: 'PT5M' + severity: severity + alertsGroupName: alertsGroupName + tagValues: tagValues + } +}] diff --git a/infrastructure/templates/public-api/components/alerts/types.bicep b/infrastructure/templates/public-api/components/alerts/types.bicep index c767d9d5f0..b262448386 100644 --- a/infrastructure/templates/public-api/components/alerts/types.bicep +++ b/infrastructure/templates/public-api/components/alerts/types.bicep @@ -35,6 +35,12 @@ type Severity = | 'Informational' | 'Verbose' +@export() +type Sensitivity = + | 'High' + | 'Medium' + | 'Low' + @export() var severityMapping = { Critical: 0 @@ -57,6 +63,7 @@ type ResourceType = @export() type MetricName = + | 'ApplicationGatewayTotalTime' | 'availability' | 'blocked_by_firewall' | 'connection_failed' @@ -64,5 +71,9 @@ type MetricName = | 'cpu_percent' | 'HealthCheckStatus' | 'is_db_alive' + | 'longest_query_time_sec' + | 'longest_transaction_time_sec' + | 'ResponseTime' | 'RestartCount' + | 'SuccessE2ELatency' | 'UnhealthyHostCount' diff --git a/infrastructure/templates/public-api/components/appGateway.bicep b/infrastructure/templates/public-api/components/appGateway.bicep index 1e04740d0f..31a9742a4a 100644 --- a/infrastructure/templates/public-api/components/appGateway.bicep +++ b/infrastructure/templates/public-api/components/appGateway.bicep @@ -39,6 +39,13 @@ param availabilityZones ('1' | '2' | '3') [] = [ '3' ] +@description('Whether to create or update Azure Monitor alerts during this deploy') +param alerts { + health: bool + responseTime: bool + alertsGroupName: string +}? + @description('Tags for the resources') param tagValues object @@ -275,3 +282,21 @@ resource appGateway 'Microsoft.Network/applicationGateways@2024-01-01' = { keyVaultAccessPolicyModule ] } + +module backendPoolsHealthAlert 'alerts/appGateways/backendPoolHealth.bicep' = if (alerts != null && alerts!.health) { + name: '${appGatewayName}BackendPoolsHealthDeploy' + params: { + resourceNames: [appGatewayName] + alertsGroupName: alerts!.alertsGroupName + tagValues: tagValues + } +} + +module responseTimeAlert 'alerts/appGateways/responseTimeAlert.bicep' = if (alerts != null && alerts!.responseTime) { + name: '${appGatewayName}ResponseTimeDeploy' + params: { + resourceNames: [appGatewayName] + alertsGroupName: alerts!.alertsGroupName + tagValues: tagValues + } +} diff --git a/infrastructure/templates/public-api/components/containerApp.bicep b/infrastructure/templates/public-api/components/containerApp.bicep index 331ef0c013..43539600d2 100644 --- a/infrastructure/templates/public-api/components/containerApp.bicep +++ b/infrastructure/templates/public-api/components/containerApp.bicep @@ -102,6 +102,13 @@ param volumeMounts { @description('An existing App Registration registered with Entra ID that will be used to control access to this Container App') param entraIdAuthentication EntraIdAuthentication? +@description('Whether to create or update Azure Monitor alerts during this deploy') +param alerts { + restarts: bool + responseTime: bool + alertsGroupName: string +}? + @description('A set of tags with which to tag the resource in Azure') param tagValues object @@ -204,6 +211,24 @@ module privateDns 'containerAppPrivateDns.bicep' = if (deployPrivateDns) { } } +module containerAppRestartsAlert 'alerts/containerApps/restarts.bicep' = if (alerts != null && alerts!.restarts) { + name: '${containerAppName}RestartsDeploy' + params: { + resourceNames: [containerAppName] + alertsGroupName: alerts!.alertsGroupName + tagValues: tagValues + } +} + +module responseTimeAlert 'alerts/containerApps/responseTimeAlert.bicep' = if (alerts != null && alerts!.responseTime) { + name: '${containerAppName}ResponseTimeDeploy' + params: { + resourceNames: [containerAppName] + alertsGroupName: alerts!.alertsGroupName + tagValues: tagValues + } +} + output containerAppFqdn string = containerAppFqdn output containerImage string = containerImageName output containerAppName string = containerApp.name diff --git a/infrastructure/templates/public-api/components/fileShare.bicep b/infrastructure/templates/public-api/components/fileShare.bicep index 37ff9ff08d..9011c2a5e9 100644 --- a/infrastructure/templates/public-api/components/fileShare.bicep +++ b/infrastructure/templates/public-api/components/fileShare.bicep @@ -11,6 +11,16 @@ param fileShareAccessTier string = 'Hot' @description('Name of the Storage Account') param storageAccountName string +@description('Whether to create or update Azure Monitor alerts during this deploy') +param alerts { + availability: bool + latency: bool + alertsGroupName: string +}? + +@description('A set of tags with which to tag the resource in Azure') +param tagValues object + // Reference an existing Storage Account. resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = { name: storageAccountName @@ -30,4 +40,22 @@ resource fileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2023-0 } } +module availabilityAlerts 'alerts/fileServices/availabilityAlert.bicep' = if (alerts != null && alerts!.availability) { + name: '${storageAccountName}FsAvailabilityDeploy' + params: { + resourceNames: [storageAccountName] + alertsGroupName: alerts!.alertsGroupName + tagValues: tagValues + } +} + +module latencyAlert 'alerts/fileServices/latencyAlert.bicep' = if (alerts != null && alerts!.latency) { + name: '${storageAccountName}FsLatencyDeploy' + params: { + resourceNames: [storageAccountName] + alertsGroupName: alerts!.alertsGroupName + tagValues: tagValues + } +} + output fileShareName string = fileShare.name diff --git a/infrastructure/templates/public-api/components/functionApp.bicep b/infrastructure/templates/public-api/components/functionApp.bicep index 12d55ea2dc..ca20c4827b 100644 --- a/infrastructure/templates/public-api/components/functionApp.bicep +++ b/infrastructure/templates/public-api/components/functionApp.bicep @@ -73,6 +73,16 @@ param azureFileShares AzureFileShareMount[] = [] @description('Specifies firewall rules for the various storage accounts in use by the Function App') param storageFirewallRules IpRange[] = [] +@description('Whether to create or update Azure Monitor alerts during this deploy') +param alerts { + functionAppHealth: bool + storageAccountAvailability: bool + storageLatency: bool + fileServiceAvailability: bool + fileServiceLatency: bool + alertsGroupName: string +}? + var reserved = appServicePlanOS == 'Linux' var identity = userAssignedManagedIdentityParams != null @@ -105,6 +115,18 @@ var slot2StorageAccountName = '${storageAccountsNamePrefix}s2' var functionAppCodeFileShareName = '${functionAppName}-fs' var keyVaultReferenceIdentity = userAssignedManagedIdentityParams != null ? userAssignedManagedIdentityParams!.id : null +var storageAlerts = alerts != null ? { + availability: alerts!.storageAccountAvailability + latency: alerts!.storageLatency + alertsGroupName: alerts!.alertsGroupName +} : null + +var fileServiceAlerts = alerts != null ? { + availability: alerts!.fileServiceAvailability + latency: alerts!.fileServiceLatency + alertsGroupName: alerts!.alertsGroupName +} : null + // This is the shared Storage Account for this Durable Function App that is used for key management, timer trigger // management etc. // @@ -123,6 +145,7 @@ module sharedStorageAccountModule 'storageAccount.bicep' = { skuStorageResource: 'Standard_LRS' keyVaultName: keyVaultName firewallRules: storageFirewallRules + alerts: storageAlerts tagValues: tagValues } } @@ -139,13 +162,20 @@ module slot1StorageAccountModule 'storageAccount.bicep' = { skuStorageResource: 'Standard_LRS' keyVaultName: keyVaultName firewallRules: storageFirewallRules + alerts: storageAlerts tagValues: tagValues } } // This is the file share for slot 1 to use for its code storage. -resource slot1FileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2023-05-01' = { - name: '${slot1StorageAccountName}/default/${functionAppCodeFileShareName}' +module slot1FileShareModule 'fileShare.bicep' = { + name: '${slot1StorageAccountName}${functionAppCodeFileShareName}Deploy' + params: { + storageAccountName: slot1StorageAccountName + fileShareName: functionAppCodeFileShareName + alerts: fileServiceAlerts + tagValues: tagValues + } dependsOn: [ slot1StorageAccountModule ] @@ -163,13 +193,20 @@ module slot2StorageAccountModule 'storageAccount.bicep' = { skuStorageResource: 'Standard_LRS' keyVaultName: keyVaultName firewallRules: storageFirewallRules + alerts: storageAlerts tagValues: tagValues } } // This is the file share for slot 2 to use for its code storage. -resource slot2FileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2023-05-01' = { - name: '${slot2StorageAccountName}/default/${functionAppCodeFileShareName}' +module slot2FileShareModule 'fileShare.bicep' = { + name: '${slot2StorageAccountName}${functionAppCodeFileShareName}Deploy' + params: { + storageAccountName: slot2StorageAccountName + fileShareName: functionAppCodeFileShareName + alerts: fileServiceAlerts + tagValues: tagValues + } dependsOn: [ slot2StorageAccountModule ] @@ -370,8 +407,8 @@ module functionAppSlotSettings 'appServiceSlotConfig.bicep' = { } dependsOn: [ functionAppKeyVaultRoleAssignments - slot1FileShare - slot2FileShare + slot1FileShareModule + slot2FileShareModule ] } @@ -387,6 +424,15 @@ module privateEndpointModule 'privateEndpoint.bicep' = if (privateEndpointSubnet } } +module healthAlert 'alerts/sites/healthAlert.bicep' = if (alerts != null && alerts!.functionAppHealth) { + name: '${functionAppName}HealthDeploy' + params: { + resourceNames: [functionAppName] + alertsGroupName: alerts!.alertsGroupName + tagValues: tagValues + } +} + output functionAppName string = functionApp.name output url string = 'https://${functionApp.name}.azurewebsites.net' output stagingUrl string = 'https://${functionApp.name}-staging.azurewebsites.net' diff --git a/infrastructure/templates/public-api/components/postgresqlDatabase.bicep b/infrastructure/templates/public-api/components/postgresqlDatabase.bicep index 006c67627b..b02b328b58 100644 --- a/infrastructure/templates/public-api/components/postgresqlDatabase.bicep +++ b/infrastructure/templates/public-api/components/postgresqlDatabase.bicep @@ -45,6 +45,14 @@ param firewallRules IpRange[] = [] @description('An array of Entra ID admin principal names for this resource') param entraIdAdminPrincipals PrincipalNameAndId[] = [] +@description('Whether to create or update Azure Monitor alerts during this deploy') +param alerts { + availability: bool + queryTime: bool + transactionTime: bool + alertGroupName: string +}? + @description('A set of tags with which to tag the resource in Azure') param tagValues object @@ -136,6 +144,33 @@ resource adminRoleAssignments 'Microsoft.DBforPostgreSQL/flexibleServers/adminis ] }] +module databaseAliveAlert 'alerts/postgreSqlFlexibleServers/databaseAlive.bicep' = if (alerts != null && alerts!.availability) { + name: '${databaseServerName}DbAliveDeploy' + params: { + resourceNames: [databaseServerName] + alertsGroupName: alerts!.alertGroupName + tagValues: tagValues + } +} + +module queryTimeAlert 'alerts/postgreSqlFlexibleServers/queryTimeAlert.bicep' = if (alerts != null && alerts!.queryTime) { + name: '${databaseServerName}QueryTimeDeploy' + params: { + resourceNames: [databaseServerName] + alertsGroupName: alerts!.alertGroupName + tagValues: tagValues + } +} + +module transactionTimeAlert 'alerts/postgreSqlFlexibleServers/transactionTimeAlert.bicep' = if (alerts != null && alerts!.transactionTime) { + name: '${databaseServerName}TransactionTimeDeploy' + params: { + resourceNames: [databaseServerName] + alertsGroupName: alerts!.alertGroupName + tagValues: tagValues + } +} + @description('The fully qualified Azure resource ID of the Database Server.') output databaseRef string = resourceId('Microsoft.DBforPostgreSQL/flexibleServers', databaseServerName) diff --git a/infrastructure/templates/public-api/components/storageAccount.bicep b/infrastructure/templates/public-api/components/storageAccount.bicep index a3e865d0b1..d938bc37ba 100644 --- a/infrastructure/templates/public-api/components/storageAccount.bicep +++ b/infrastructure/templates/public-api/components/storageAccount.bicep @@ -18,6 +18,13 @@ param skuStorageResource 'Standard_LRS' | 'Standard_GRS' | 'Standard_RAGRS' | 'S @description('Storage Account Name') param keyVaultName string +@description('Whether to create or update Azure Monitor alerts during this deploy') +param alerts { + availability: bool + latency: bool + alertsGroupName: string +}? + @description('A set of tags with which to tag the resource in Azure') param tagValues object @@ -50,6 +57,24 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = { tags: tagValues } +module availabilityAlerts 'alerts/storageAccounts/availabilityAlert.bicep' = if (alerts != null && alerts!.availability) { + name: '${storageAccountName}StorageAvailabilityDeploy' + params: { + resourceNames: [storageAccountName] + alertsGroupName: alerts!.alertsGroupName + tagValues: tagValues + } +} + +module latencyAlert 'alerts/storageAccounts/latencyAlert.bicep' = if (alerts != null && alerts!.latency) { + name: '${storageAccountName}LatencyDeploy' + params: { + resourceNames: [storageAccountName] + alertsGroupName: alerts!.alertsGroupName + tagValues: tagValues + } +} + var key = storageAccount.listKeys().keys[0].value var storageAccountConnectionString = 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=${endpointSuffix};AccountKey=${key}'