Skip to content

Commit b6ce39e

Browse files
authored
Merge pull request #5783 from dfe-analytical-services/dev
Merge Dev into Master
2 parents d21b556 + 3f899a6 commit b6ce39e

File tree

286 files changed

+11690
-5619
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

286 files changed

+11690
-5619
lines changed

infrastructure/parameters/pre-prod.parameters.json

+3
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@
9191
},
9292
"immediatePublicationOfScheduledReleaseVersionsEnabled": {
9393
"value": true
94+
},
95+
"eventGridTopicsExist": {
96+
"value": false
9497
}
9598
}
9699
}

infrastructure/parameters/prod.parameters.json

+3
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@
9494
},
9595
"maxStatsDbSizeBytes": {
9696
"value": 2199023255552
97+
},
98+
"eventGridTopicsExist": {
99+
"value": false
97100
}
98101
}
99102
}

infrastructure/parameters/test.parameters.json

+3
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@
9494
},
9595
"immediatePublicationOfScheduledReleaseVersionsEnabled": {
9696
"value": true
97+
},
98+
"eventGridTopicsExist": {
99+
"value": false
97100
}
98101
}
99102
}

infrastructure/templates/analytics/application/analyticsFunctionApp.bicep

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ param analyticsFileShareName string
2626
@description('The Application Insights connection string that is associated with this resource.')
2727
param applicationInsightsConnectionString string = ''
2828

29-
@description('The cron schedule for the Public API queries consumer Function. Defaults to every half hour.')
30-
param publicApiQueryConsumerCron string
29+
@description('The cron schedule for the analytics consumer Function.')
30+
param analyticsRequestFilesConsumerCron string
3131

3232
@description('Specifies whether or not the Analytics Function App already exists.')
3333
param functionAppExists bool
@@ -71,8 +71,8 @@ module functionAppModule '../../common/components/functionApp.bicep' = {
7171
value: fileShareMountPath
7272
}
7373
{
74-
name: 'App__ConsumePublicApiQueriesCronSchedule'
75-
value: publicApiQueryConsumerCron
74+
name: 'App__ConsumeAnalyticsRequestFilesCronSchedule'
75+
value: analyticsRequestFilesConsumerCron
7676
}
7777
]
7878
functionAppExists: functionAppExists

infrastructure/templates/analytics/ci/jobs/deploy-infrastructure.yml

+2-7
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,8 @@ jobs:
2121
steps:
2222
- checkout: self
2323

24-
- task: AzureCLI@2
25-
displayName: Install Bicep
26-
inputs:
27-
azureSubscription: ${{ parameters.serviceConnection }}
28-
scriptType: bash
29-
scriptLocation: inlineScript
30-
inlineScript: az bicep install
24+
- script: az upgrade --yes
25+
displayName: Upgrade Azure CLI and extensions
3126

3227
- template: ../tasks/deploy-bicep.yml
3328
parameters:

infrastructure/templates/analytics/ci/tasks/deploy-bicep.yml

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ steps:
2626
inlineScript: |
2727
set -e
2828
29+
# Workaround for AZ CLI 2.71.x breaks bicep deployments when using Azure Devops Agent
30+
# See https://github.com/Azure/azure-cli/issues/31189
31+
az config set bicep.use_binary_from_path=false
32+
2933
az deployment group ${{ parameters.action }} \
3034
--name $(infraDeployName) \
3135
--resource-group $(resourceGroupName) \

infrastructure/templates/analytics/main.bicep

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ param analyticsFunctionAppExists bool = true
3030
param maintenanceIpRanges IpRange[] = []
3131

3232
@description('The cron schedule for the Public API queries consumer Function. Defaults to every hour.')
33-
param publicApiQueryConsumerCron string = '0 0 * * * *'
33+
param analyticsRequestFilesConsumerCron string = '0 0 * * * *'
3434

3535
var tagValues = union(resourceTags ?? {}, {
3636
Environment: environmentName
@@ -105,7 +105,7 @@ module analyticsFunctionAppModule 'application/analyticsFunctionApp.bicep' = {
105105
applicationInsightsConnectionString: applicationInsightsModule.outputs.applicationInsightsConnectionString
106106
analyticsStorageAccountName: analyticsStorageModule.outputs.storageAccountName
107107
analyticsFileShareName: analyticsStorageModule.outputs.fileShareName
108-
publicApiQueryConsumerCron: publicApiQueryConsumerCron
108+
analyticsRequestFilesConsumerCron: analyticsRequestFilesConsumerCron
109109
tagValues: tagValues
110110
}
111111
}

infrastructure/templates/analytics/parameters/main-dev.bicepparam

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ using '../main.bicep'
44
param environmentName = 'Development'
55

66
// On Dev, we will run the Function App every 10 minutes.
7-
param publicApiQueryConsumerCron = '0 */10 * * * *'
7+
param analyticsRequestFilesConsumerCron = '0 */10 * * * *'

infrastructure/templates/common/abbreviations.bicep

+3-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ var abbreviations = {
4242
devicesProvisioningServicesCertificates: 'pcert'
4343
documentDBDatabaseAccounts: 'cosmos'
4444
eventGridDomains: 'evgd'
45-
eventGridDomainsTopics: 'evgt'
46-
eventGridEventSubscriptions: 'evgs'
45+
eventGridSubscriptions: 'evgs'
46+
eventGridTopics: 'evgt'
47+
eventGridSystemTopics: 'evst'
4748
eventHubNamespaces: 'evhns'
4849
eventHubNamespacesEventHubs: 'evh'
4950
fileShare: 'share'

infrastructure/templates/common/builtInRoles.bicep

+683
Large diffs are not rendered by default.

infrastructure/templates/common/components/blobService.bicep

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ param storageAccountName string
1313
param containerNames array = []
1414

1515
// Reference an existing Storage Account.
16-
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = {
16+
resource storageAccount 'Microsoft.Storage/storageAccounts@2024-01-01' existing = {
1717
name: storageAccountName
1818
}
1919

20-
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-05-01' = {
20+
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2024-01-01' = {
2121
name: blobServiceName
2222
parent: storageAccount
2323
properties: {
@@ -31,7 +31,7 @@ resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-05-01'
3131
}
3232
}
3333

34-
resource blobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-05-01' = [for containerName in containerNames: {
34+
resource containers 'Microsoft.Storage/storageAccounts/blobServices/containers@2024-01-01' = [for containerName in containerNames: {
3535
name: containerName
3636
parent: blobService
3737
}]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { IpRange } from '../../types.bicep'
2+
3+
@description('The resource name.')
4+
@minLength(3)
5+
@maxLength(50)
6+
param name string
7+
8+
@description('Location for all resources.')
9+
param location string
10+
11+
@description('A list IP network rules to allow access to the Search Service from specific public internet IP address ranges. These rules are applied only when \'publicNetworkAccess\' is \'Enabled\'.')
12+
param ipRules IpRange[] = []
13+
14+
@description('Specifies whether to enable local authentication. Microsoft Entra access authentication is always enabled.')
15+
param localAuthenticationEnabled bool = false
16+
17+
@description('Specifies whether traffic is allowed over the public interface.')
18+
@allowed([
19+
'Disabled'
20+
'Enabled'
21+
])
22+
param publicNetworkAccess string = 'Disabled'
23+
24+
@description('Indicates whether the resource should have a system-assigned managed identity.')
25+
param systemAssignedIdentity bool = false
26+
27+
@description('The name of a user-assigned managed identity to assign to the resource.')
28+
param userAssignedIdentityName string = ''
29+
30+
@description('A set of tags with which to tag the resource in Azure')
31+
param tagValues object
32+
33+
var identityType = systemAssignedIdentity
34+
? (!empty(userAssignedIdentityName) ? 'SystemAssigned, UserAssigned' : 'SystemAssigned')
35+
: (!empty(userAssignedIdentityName) ? 'UserAssigned' : 'None')
36+
37+
resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' existing = if (!empty(userAssignedIdentityName)) {
38+
name: userAssignedIdentityName
39+
}
40+
41+
resource topic 'Microsoft.EventGrid/topics@2025-02-15' = {
42+
name: name
43+
location: location
44+
identity: {
45+
type: identityType
46+
userAssignedIdentities: !empty(userAssignedIdentityName) ? { '${userAssignedIdentity.id}': {} } : null
47+
}
48+
properties: {
49+
minimumTlsVersionAllowed: '1.2'
50+
inputSchema: 'EventGridSchema'
51+
publicNetworkAccess: publicNetworkAccess
52+
inboundIpRules: [
53+
for ipRule in ipRules: {
54+
action: 'Allow'
55+
ipMask: ipRule.cidr
56+
}
57+
]
58+
disableLocalAuth: !localAuthenticationEnabled
59+
dataResidencyBoundary: 'WithinRegion'
60+
}
61+
tags: tagValues
62+
}
63+
64+
output name string = topic.name
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
@description('The resource name.')
2+
@minLength(3)
3+
@maxLength(64)
4+
param name string
5+
6+
@description('The Event Grid topic name associated with the subscription.')
7+
param topicName string
8+
9+
@description('The name of the storage account that contains the queue that is the destination of the subscription.')
10+
param storageAccountName string
11+
12+
@description('The name of the storage queue under the storage account that is the destination of the subscription.')
13+
param queueName string
14+
15+
@description('A list of event types to include in the subscription. If not specified, all event types are included.')
16+
param includedEventTypes array = []
17+
18+
resource topic 'Microsoft.EventGrid/topics@2025-02-15' existing = {
19+
name: topicName
20+
}
21+
22+
resource storageAccount 'Microsoft.Storage/storageAccounts@2024-01-01' existing = {
23+
name: storageAccountName
24+
}
25+
26+
resource eventSubscription 'Microsoft.EventGrid/topics/eventSubscriptions@2025-02-15' = {
27+
parent: topic
28+
name: name
29+
properties: {
30+
destination: {
31+
properties: {
32+
resourceId: storageAccount.id
33+
queueName: queueName
34+
queueMessageTimeToLiveInSeconds: 604800
35+
}
36+
endpointType: 'StorageQueue'
37+
}
38+
filter: {
39+
enableAdvancedFilteringOnArrays: true
40+
includedEventTypes: length(includedEventTypes) > 0 ? includedEventTypes : null
41+
}
42+
labels: []
43+
eventDeliverySchema: 'EventGridSchema'
44+
retryPolicy: {
45+
maxDeliveryAttempts: 30
46+
eventTimeToLiveInMinutes: 1440
47+
}
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { abbreviations } from '../../abbreviations.bicep'
2+
import { buildFullyQualifiedTopicName } from '../../functions.bicep'
3+
import { IpRange } from '../../types.bicep'
4+
5+
@description('Resource prefix for all resources.')
6+
param resourcePrefix string
7+
8+
@description('Location for all resources.')
9+
param location string
10+
11+
@description('A list of IP network rules to allow access to the resource from specific public internet IP address ranges.')
12+
param ipRules IpRange[]
13+
14+
@description('Specifies a set of tags with which to tag the resource in Azure.')
15+
param tagValues object
16+
17+
@description('A list of custom topic names to create.')
18+
@minLength(1)
19+
param customTopicNames string[]
20+
21+
module eventGridCustomTopicModule 'eventGridCustomTopic.bicep' = [
22+
for (topicName, index) in customTopicNames: {
23+
name: 'eventGridCustomTopicModuleDeploy-${index}'
24+
params: {
25+
name: buildFullyQualifiedTopicName(resourcePrefix, topicName)
26+
location: location
27+
ipRules: ipRules
28+
publicNetworkAccess: 'Enabled'
29+
systemAssignedIdentity: true
30+
tagValues: tagValues
31+
}
32+
}
33+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
@description('Specifies the id of the principal to which the role is assigned.')
2+
param principalId string
3+
4+
@description('Specifies whether the principal is a user, a group, or a service principal.')
5+
@allowed([
6+
'User'
7+
'Group'
8+
'ServicePrincipal'
9+
])
10+
param principalType string = 'ServicePrincipal'
11+
12+
@description('Specifies the role definition id\'s to assign to the principal.')
13+
param roleDefinitionId string
14+
15+
@description('Specifies the name of the Event Grid topic to which the role assignment is scoped.')
16+
param topicName string
17+
18+
resource topic 'Microsoft.EventGrid/topics@2025-02-15' existing = {
19+
name: topicName
20+
}
21+
22+
resource roleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
23+
scope: subscription()
24+
name: roleDefinitionId
25+
}
26+
27+
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
28+
scope: topic
29+
name: guid(topic.id, principalId, roleDefinition.id)
30+
properties: {
31+
principalId: principalId
32+
principalType: principalType
33+
roleDefinitionId: roleDefinition.id
34+
}
35+
}

infrastructure/templates/common/components/functionApp.bicep

+15-19
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,8 @@ param allowedOrigins array = []
7777
@description('Specifies an optional URL for Azure to use to monitor the health of this resource')
7878
param healthCheckPath string?
7979

80-
@description('An existing Managed Identity\'s Resource Id with which to associate this Function App.')
81-
param userAssignedManagedIdentityParams {
82-
id: string
83-
name: string
84-
principalId: string
85-
}?
80+
@description('The name of a user-assigned managed identity to assign to the resource.')
81+
param userAssignedIdentityName string = ''
8682

8783
@description('Specifies the SKU for the Function App hosting plan.')
8884
param sku object
@@ -126,17 +122,6 @@ var firewallRules = [
126122
}
127123
]
128124

129-
var identity = userAssignedManagedIdentityParams != null
130-
? {
131-
type: 'UserAssigned'
132-
userAssignedIdentities: {
133-
'${userAssignedManagedIdentityParams!.id}': {}
134-
}
135-
}
136-
: {
137-
type: 'SystemAssigned'
138-
}
139-
140125
var storageAlerts = alerts != null
141126
? {
142127
availability: alerts!.storageAccountAvailability
@@ -158,6 +143,12 @@ resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
158143
name: keyVaultName
159144
}
160145

146+
var identityType = !empty(userAssignedIdentityName) ? 'SystemAssigned, UserAssigned' : 'SystemAssigned'
147+
148+
resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' existing = if (!empty(userAssignedIdentityName)) {
149+
name: userAssignedIdentityName
150+
}
151+
161152
module appServicePlanModule '../../public-api/components/appServicePlan.bicep' = {
162153
name: '${appServicePlanName}ModuleDeploy'
163154
params: {
@@ -210,7 +201,10 @@ resource functionApp 'Microsoft.Web/sites@2024-04-01' = {
210201
name: functionAppName
211202
location: location
212203
kind: (operatingSystem == 'Linux' ? 'functionapp,linux' : 'functionapp')
213-
identity: identity
204+
identity: {
205+
type: identityType
206+
userAssignedIdentities: !empty(userAssignedIdentityName) ? { '${userAssignedIdentity.id}': {} } : null
207+
}
214208
properties: {
215209
containerSize: instanceMemoryMB
216210
reserved: operatingSystem == 'Linux'
@@ -313,7 +307,7 @@ resource azureStorageAccountsConfig 'Microsoft.Web/sites/config@2023-12-01' = if
313307
module keyVaultRoleAssignmentModule '../../public-api/components/keyVaultRoleAssignment.bicep' = {
314308
name: '${functionAppName}KeyVaultRoleAssignmentModuleDeploy'
315309
params: {
316-
principalIds: userAssignedManagedIdentityParams != null ? [userAssignedManagedIdentityParams!.principalId] : [functionApp.identity.principalId]
310+
principalIds: [functionApp.identity.principalId]
317311
keyVaultName: keyVault.name
318312
role: 'Secrets User'
319313
}
@@ -411,3 +405,5 @@ module expectedHttpStatusCodeAlerts '../../public-api/components/alerts/dynamicM
411405

412406
output name string = functionApp.name
413407
output url string = 'https://${functionApp.name}.azurewebsites.net'
408+
output functionAppIdentityPrincipalId string = functionApp.identity.principalId
409+
output storageAccountName string = storageAccountModule.outputs.storageAccountName

0 commit comments

Comments
 (0)