Skip to content

Commit c19969f

Browse files
committed
Modernizing Bicep for FlexConsumption, updated AI resources, and MI
Signed-off-by: Paul Yuknewicz <[email protected]>
1 parent 02ac92e commit c19969f

37 files changed

+706
-2249
lines changed

infra/app/ai-Cog-Service-Access.bicep

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
param principalID string
2+
param principalType string = 'ServicePrincipal' // Workaround for https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-template#new-service-principal
3+
param roleDefinitionID string
4+
param aiResourceName string
5+
6+
resource cognitiveService 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = {
7+
name: aiResourceName
8+
}
9+
10+
// Allow access from API to this resource using a managed identity and least priv role grants
11+
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
12+
name: guid(cognitiveService.id, principalID, roleDefinitionID)
13+
scope: cognitiveService
14+
properties: {
15+
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionID)
16+
principalId: principalID
17+
principalType: principalType
18+
}
19+
}
20+
21+
output ROLE_ASSIGNMENT_NAME string = roleAssignment.name

infra/app/ai.bicep

Lines changed: 0 additions & 38 deletions
This file was deleted.

infra/app/api.bicep

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,50 @@
11
param name string
22
param location string = resourceGroup().location
33
param tags object = {}
4-
5-
param allowedOrigins array = []
64
param applicationInsightsName string = ''
75
param appServicePlanId string
86
param appSettings object = {}
9-
param keyVaultName string
7+
param runtimeName string
8+
param runtimeVersion string
109
param serviceName string = 'api'
1110
param storageAccountName string
12-
param openAiAccountName string
13-
param openAiResourceGroupName string
11+
param deploymentStorageContainerName string
12+
param virtualNetworkSubnetId string = ''
13+
param instanceMemoryMB int = 2048
14+
param maximumInstanceCount int = 100
15+
param identityId string = ''
16+
param identityClientId string = ''
17+
param aiServiceUrl string = ''
18+
19+
var applicationInsightsIdentity = 'ClientId=${identityClientId};Authorization=AAD'
1420

15-
module api '../core/host/functions.bicep' = {
16-
name: '${serviceName}-functions-python-module'
21+
module api '../core/host/functions-flexconsumption.bicep' = {
22+
name: '${serviceName}-functions-module'
1723
params: {
1824
name: name
1925
location: location
2026
tags: union(tags, { 'azd-service-name': serviceName })
21-
allowedOrigins: allowedOrigins
22-
alwaysOn: false
23-
appSettings: union(appSettings, {
24-
AZURE_OPENAI_KEY: openai.listKeys().key1
27+
identityType: 'UserAssigned'
28+
identityId: identityId
29+
appSettings: union(appSettings,
30+
{
31+
AzureWebJobsStorage__clientId : identityClientId
32+
APPLICATIONINSIGHTS_AUTHENTICATION_STRING: applicationInsightsIdentity
33+
AZURE_OPENAI_ENDPOINT: aiServiceUrl
34+
AZURE_CLIENT_ID: identityClientId
2535
})
2636
applicationInsightsName: applicationInsightsName
2737
appServicePlanId: appServicePlanId
28-
keyVaultName: keyVaultName
29-
//py
30-
numberOfWorkers: 1
31-
minimumElasticInstanceCount: 0
32-
//--py
33-
runtimeName: 'python'
34-
runtimeVersion: '3.9'
38+
runtimeName: runtimeName
39+
runtimeVersion: runtimeVersion
3540
storageAccountName: storageAccountName
36-
scmDoBuildDuringDeployment: false
41+
deploymentStorageContainerName: deploymentStorageContainerName
42+
virtualNetworkSubnetId: virtualNetworkSubnetId
43+
instanceMemoryMB: instanceMemoryMB
44+
maximumInstanceCount: maximumInstanceCount
3745
}
3846
}
3947

40-
resource openai 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = {
41-
name: openAiAccountName
42-
scope: resourceGroup(openAiResourceGroupName)
43-
}
44-
45-
output SERVICE_API_IDENTITY_PRINCIPAL_ID string = api.outputs.identityPrincipalId
4648
output SERVICE_API_NAME string = api.outputs.name
4749
output SERVICE_API_URI string = api.outputs.uri
50+
output SERVICE_API_IDENTITY_PRINCIPAL_ID string = api.outputs.identityPrincipalId

infra/app/db.bicep

Lines changed: 0 additions & 31 deletions
This file was deleted.

infra/app/eventgrid.bicep

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
param location string = resourceGroup().location
2+
param tags object = {}
3+
param storageAccountId string
4+
5+
resource unprocessedPdfSystemTopic 'Microsoft.EventGrid/systemTopics@2024-06-01-preview' = {
6+
name: 'unprocessed-pdf-topic'
7+
location: location
8+
tags: tags
9+
properties: {
10+
source: storageAccountId
11+
topicType: 'Microsoft.Storage.StorageAccounts'
12+
}
13+
}
14+
15+
// The actual event grid subscription will be created in the post deployment script as it needs the function to be deployed first
16+
17+
// resource unprocessedPdfSystemTopicSubscription 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2024-06-01-preview' = {
18+
// parent: unprocessedPdfSystemTopic
19+
// name: 'unprocessed-pdf-topic-subscription'
20+
// properties: {
21+
// destination: {
22+
// endpointType: 'WebHook'
23+
// properties: {
24+
// //Will be set on post-deployment script once the function is created and the blobs extension code is available
25+
// //endpointUrl: 'https://${function_app_blob_event_grid_name}.azurewebsites.net/runtime/webhooks/blobs?functionName=Host.Functions.Trigger_BlobEventGrid&code=${blobs_extension}'
26+
// }
27+
// }
28+
// filter: {
29+
// includedEventTypes: [
30+
// 'Microsoft.Storage.BlobCreated'
31+
// ]
32+
// subjectBeginsWith: '/blobServices/default/containers/${unprocessedPdfContainerName}/'
33+
// }
34+
// }
35+
// }
36+
37+
output unprocessedPdfSystemTopicId string = unprocessedPdfSystemTopic.id
38+
output unprocessedPdfSystemTopicName string = unprocessedPdfSystemTopic.name

infra/app/storage-Access.bicep

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
param principalID string
2+
param principalType string = 'ServicePrincipal' // Workaround for https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-template#new-service-principal
3+
param roleDefinitionID string
4+
param storageAccountName string
5+
6+
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = {
7+
name: storageAccountName
8+
}
9+
10+
// Allow access from API to storage account using a managed identity and least priv Storage roles
11+
resource storageRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
12+
name: guid(storageAccount.id, principalID, roleDefinitionID)
13+
scope: storageAccount
14+
properties: {
15+
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionID)
16+
principalId: principalID
17+
principalType: principalType
18+
}
19+
}
20+
21+
output ROLE_ASSIGNMENT_NAME string = storageRoleAssignment.name
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Parameters
2+
@description('Specifies the name of the virtual network.')
3+
param virtualNetworkName string
4+
5+
@description('Specifies the name of the subnet which contains the virtual machine.')
6+
param subnetName string
7+
8+
@description('Specifies the resource name of the Storage resource with an endpoint.')
9+
param resourceName string
10+
11+
@description('Specifies the location.')
12+
param location string = resourceGroup().location
13+
14+
param tags object = {}
15+
16+
// Virtual Network
17+
resource vnet 'Microsoft.Network/virtualNetworks@2021-08-01' existing = {
18+
name: virtualNetworkName
19+
}
20+
21+
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' existing = {
22+
name: resourceName
23+
}
24+
25+
var blobPrivateDNSZoneName = format('privatelink.blob.{0}', environment().suffixes.storage)
26+
var blobPrivateDnsZoneVirtualNetworkLinkName = format('{0}-link-{1}', resourceName, take(toLower(uniqueString(resourceName, virtualNetworkName)), 4))
27+
28+
// Private DNS Zones
29+
resource blobPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
30+
name: blobPrivateDNSZoneName
31+
location: 'global'
32+
tags: tags
33+
properties: {}
34+
dependsOn: [
35+
vnet
36+
]
37+
}
38+
39+
// Virtual Network Links
40+
resource blobPrivateDnsZoneVirtualNetworkLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
41+
parent: blobPrivateDnsZone
42+
name: blobPrivateDnsZoneVirtualNetworkLinkName
43+
location: 'global'
44+
tags: tags
45+
properties: {
46+
registrationEnabled: false
47+
virtualNetwork: {
48+
id: vnet.id
49+
}
50+
}
51+
}
52+
53+
// Private Endpoints
54+
resource blobPrivateEndpoint 'Microsoft.Network/privateEndpoints@2021-08-01' = {
55+
name: 'blob-private-endpoint'
56+
location: location
57+
tags: tags
58+
properties: {
59+
privateLinkServiceConnections: [
60+
{
61+
name: 'blobPrivateLinkConnection'
62+
properties: {
63+
privateLinkServiceId: storageAccount.id
64+
groupIds: [
65+
'blob'
66+
]
67+
}
68+
}
69+
]
70+
subnet: {
71+
id: '${vnet.id}/subnets/${subnetName}'
72+
}
73+
}
74+
}
75+
76+
resource blobPrivateDnsZoneGroupName 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-01-01' = {
77+
parent: blobPrivateEndpoint
78+
name: 'blobPrivateDnsZoneGroup'
79+
properties: {
80+
privateDnsZoneConfigs: [
81+
{
82+
name: 'storageBlobARecord'
83+
properties: {
84+
privateDnsZoneId: blobPrivateDnsZone.id
85+
}
86+
}
87+
]
88+
}
89+
}

infra/app/vnet.bicep

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
@description('Specifies the name of the virtual network.')
2+
param vNetName string
3+
4+
@description('Specifies the location.')
5+
param location string = resourceGroup().location
6+
7+
@description('Specifies the name of the subnet for the Service Bus private endpoint.')
8+
param peSubnetName string = 'private-endpoints-subnet'
9+
10+
@description('Specifies the name of the subnet for Function App virtual network integration.')
11+
param appSubnetName string = 'app'
12+
13+
param tags object = {}
14+
15+
resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-05-01' = {
16+
name: vNetName
17+
location: location
18+
tags: tags
19+
properties: {
20+
addressSpace: {
21+
addressPrefixes: [
22+
'10.0.0.0/16'
23+
]
24+
}
25+
encryption: {
26+
enabled: false
27+
enforcement: 'AllowUnencrypted'
28+
}
29+
subnets: [
30+
{
31+
name: peSubnetName
32+
id: resourceId('Microsoft.Network/virtualNetworks/subnets', vNetName, 'private-endpoints-subnet')
33+
properties: {
34+
addressPrefixes: [
35+
'10.0.1.0/24'
36+
]
37+
delegations: []
38+
privateEndpointNetworkPolicies: 'Disabled'
39+
privateLinkServiceNetworkPolicies: 'Enabled'
40+
}
41+
type: 'Microsoft.Network/virtualNetworks/subnets'
42+
}
43+
{
44+
name: appSubnetName
45+
id: resourceId('Microsoft.Network/virtualNetworks/subnets', vNetName, 'app')
46+
properties: {
47+
addressPrefixes: [
48+
'10.0.2.0/24'
49+
]
50+
delegations: [
51+
{
52+
name: 'delegation'
53+
id: resourceId('Microsoft.Network/virtualNetworks/subnets/delegations', vNetName, 'app', 'delegation')
54+
properties: {
55+
//Microsoft.App/environments is the correct delegation for Flex Consumption VNet integration
56+
serviceName: 'Microsoft.App/environments'
57+
}
58+
type: 'Microsoft.Network/virtualNetworks/subnets/delegations'
59+
}
60+
]
61+
privateEndpointNetworkPolicies: 'Disabled'
62+
privateLinkServiceNetworkPolicies: 'Enabled'
63+
}
64+
type: 'Microsoft.Network/virtualNetworks/subnets'
65+
}
66+
]
67+
virtualNetworkPeerings: []
68+
enableDdosProtection: false
69+
}
70+
}
71+
72+
output peSubnetName string = virtualNetwork.properties.subnets[0].name
73+
output peSubnetID string = virtualNetwork.properties.subnets[0].id
74+
output appSubnetName string = virtualNetwork.properties.subnets[1].name
75+
output appSubnetID string = virtualNetwork.properties.subnets[1].id

0 commit comments

Comments
 (0)