diff --git a/.github/instructions/bicep.instructions.md b/.github/instructions/bicep.instructions.md new file mode 100644 index 0000000000..1a878e979b --- /dev/null +++ b/.github/instructions/bicep.instructions.md @@ -0,0 +1,34 @@ +--- +description: 'Infrastructure as Code with Bicep' +applyTo: '**/*.bicep' +--- + +# Bicep best-practices +This list of best-practices builds on top of information available at https://learn.microsoft.com/azure/azure-resource-manager/bicep. It provides a more opinionated and up-to-date set of rules for generating high-quality Bicep code. You should aim to follow these rules whenever generating or modifying Bicep code. + +## Rules +### General +1. Avoid setting the `name` field for `module` statements - it is no longer required. +1. If you need to input or output a set of logically-grouped values, generate a single `param` or `output` statement with a User-defined type instead of emitting a `param` or `output` statement for each value. +1. If generating parameters, default to generating Bicep parameters files (`*.bicepparam`), instead of ARM parameters files (`*.json`). + +### Resources +1. Do not add references from child resources to parent resources by using `/` characters in the child resource `name` property. Instead, use the `parent` property with a symbolic reference to the parent resource. +1. If you are generating a child resource type, sometimes this may require you to add an `existing` resource for the parent if the parent is not already present in the file. +1. If you see diagnostic codes `BCP036`, `BCP037` or `BCP081`, this may indicate you have hallucinated resource types or resource type properties. You may need to double-check against available resource type schema to tune your output. +1. Avoid using multiple `resourceId()` functions and `reference()` function where possible. Instead use symbolic names to refer to ids or properties, creating `existing` resources if needed. For example, write `foo.id` or `foo.properties.id`, instead of `resourceId('...')` or `reference('...').id`. + +### Types +1. Avoid using open types such as `array` or `object` when referencing types where possible (e.g. in `output` or `param` statements). Instead, use User-defined types to define a more precise type. +1. Use typed variables instead of untyped variables when exporting values with the `@export()` decorator. For example, use `var foo string = 'blah'` instead of `var foo = bar`. +1. When using User-defined types, aim to avoid repetition, and comment properties with `@description()` where the context is unclear. +1. If you are passing data directly to or from a resource body via a `param` or `output` statement, try to use existing Resource-derived types (`resourceInput<'type@version'>` and `resourceOutput<'type@version'>`) instead of writing User-defined types. + +### Security +1. When generating `param` or `output` statements, ALWAYS use the `@secure()` decorator if sensitive data is present. + +### Syntax +1. If you hit warnings or errors with null properties, prefer solving them with the safe-dereference (`.?`) operator, in conjunction with the coalesce (`??`) operator. For example, `a.?b ?? c` is better than `a!.b` which may cause runtime errors, or `a != null ? a.b : c` which is unnecessarily verbose. + +## Glossary +* Child resource: an Azure resource type with type name consisting of more than 1 `/` characters. For example, `Microsoft.Network/virtualNetworks/subnets` is a child resource. `Microsoft.Network/virtualNetworks` is not. diff --git a/docs/deploy_existing.md b/docs/deploy_existing.md index 38c602c34d..8282df2cf5 100644 --- a/docs/deploy_existing.md +++ b/docs/deploy_existing.md @@ -8,7 +8,6 @@ You should set these values before running `azd up`. Once you've set them, retur * [OpenAI resource](#openai-resource) * [Azure AI Search resource](#azure-ai-search-resource) * [Azure App Service Plan and App Service resources](#azure-app-service-plan-and-app-service-resources) -* [Azure Application Insights and related resources](#azure-application-insights-and-related-resources) * [Azure AI Vision resources](#azure-ai-vision-resources) * [Azure Document Intelligence resource](#azure-document-intelligence-resource) * [Azure Speech resource](#azure-speech-resource) @@ -72,12 +71,6 @@ You can also customize the search service (new or existing) for non-English sear 1. Run `azd env set AZURE_APP_SERVICE {Name of existing Azure App Service}`. 1. Run `azd env set AZURE_APP_SERVICE_SKU {SKU of Azure App Service, defaults to B1}`. -## Azure Application Insights and related resources - -1. Run `azd env set AZURE_APPLICATION_INSIGHTS {Name of existing Azure App Insights}`. -1. Run `azd env set AZURE_APPLICATION_INSIGHTS_DASHBOARD {Name of existing Azure App Insights Dashboard}`. -1. Run `azd env set AZURE_LOG_ANALYTICS {Name of existing Azure Log Analytics Workspace Name}`. - ## Azure AI Vision resources 1. Run `azd env set AZURE_VISION_SERVICE {Name of existing Azure AI Vision Service Name}` diff --git a/infra/backend-dashboard.bicep b/infra/backend-dashboard.bicep index 50683f740c..659fb545b1 100644 --- a/infra/backend-dashboard.bicep +++ b/infra/backend-dashboard.bicep @@ -4,7 +4,6 @@ param applicationInsightsName string param location string = resourceGroup().location param tags object = {} -// 2020-09-01-preview because that is the latest valid version resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { name: name location: location @@ -21,7 +20,9 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr colSpan: 2 rowSpan: 1 } - metadata: { + // The Bicep schema only defines MarkdownPart, so we use any() to bypass + // type checking for other valid extension types (AppInsightsExtension, MonitorChartPart) + metadata: any({ inputs: [ { name: 'id' @@ -32,14 +33,13 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr value: '1.0' } ] - #disable-next-line BCP036 type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' asset: { idInputName: 'id' type: 'ApplicationInsights' } defaultMenuItemId: 'overview' - } + }) } { position: { @@ -48,7 +48,7 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr colSpan: 1 rowSpan: 1 } - metadata: { + metadata: any({ inputs: [ { name: 'ComponentId' @@ -63,14 +63,13 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr value: '1.0' } ] - #disable-next-line BCP036 type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' asset: { idInputName: 'ComponentId' type: 'ApplicationInsights' } defaultMenuItemId: 'ProactiveDetection' - } + }) } { position: { @@ -79,7 +78,7 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr colSpan: 1 rowSpan: 1 } - metadata: { + metadata: any({ inputs: [ { name: 'ComponentId' @@ -105,13 +104,12 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr value: '78ce933e-e864-4b05-a27b-71fd55a6afad' } ] - #disable-next-line BCP036 type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' asset: { idInputName: 'ComponentId' type: 'ApplicationInsights' } - } + }) } { position: { @@ -141,7 +139,7 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr colSpan: 1 rowSpan: 1 } - metadata: { + metadata: any({ inputs: [ { name: 'ResourceId' @@ -167,7 +165,6 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr isOptional: true } ] - #disable-next-line BCP036 type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' isAdapter: true asset: { @@ -175,7 +172,7 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr type: 'ApplicationInsights' } defaultMenuItemId: 'failures' - } + }) } { position: { @@ -205,7 +202,7 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr colSpan: 1 rowSpan: 1 } - metadata: { + metadata: any({ inputs: [ { name: 'ResourceId' @@ -231,7 +228,6 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr isOptional: true } ] - #disable-next-line BCP036 type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' isAdapter: true asset: { @@ -239,7 +235,7 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr type: 'ApplicationInsights' } defaultMenuItemId: 'performance' - } + }) } { position: { @@ -248,7 +244,7 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr colSpan: 4 rowSpan: 3 } - metadata: { + metadata: any({ inputs: [ { name: 'options' @@ -306,10 +302,9 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr isOptional: true } ] - #disable-next-line BCP036 type: 'Extension/HubsExtension/PartType/MonitorChartPart' settings: {} - } + }) } { position: { @@ -318,7 +313,7 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr colSpan: 4 rowSpan: 3 } - metadata: { + metadata: any({ inputs: [ { name: 'options' @@ -376,10 +371,9 @@ resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-pr isOptional: true } ] - #disable-next-line BCP036 type: 'Extension/HubsExtension/PartType/MonitorChartPart' settings: {} - } + }) } ] } diff --git a/infra/core/ai/hub.bicep b/infra/core/ai/hub.bicep index 7c82f3f85c..4821bd853b 100644 --- a/infra/core/ai/hub.bicep +++ b/infra/core/ai/hub.bicep @@ -60,9 +60,9 @@ resource hub 'Microsoft.MachineLearningServices/workspaces@2024-07-01-preview' = category: 'CognitiveSearch' authType: 'ApiKey' isSharedToAll: true - target: 'https://${search.name}.search.windows.net/' + target: 'https://${search!.name}.search.windows.net/' credentials: { - key: !empty(aiSearchName) ? search.listAdminKeys().primaryKey : '' + key: !empty(aiSearchName) ? search!.listAdminKeys().primaryKey : '' } } } diff --git a/infra/core/host/appservice.bicep b/infra/core/host/appservice.bicep index 4bf13b3cb3..5eec160714 100644 --- a/infra/core/host/appservice.bicep +++ b/infra/core/host/appservice.bicep @@ -99,8 +99,8 @@ resource appService 'Microsoft.Web/sites@2022-03-01' = { ENABLE_ORYX_BUILD: string(enableOryxBuild) }, runtimeName == 'python' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true' } : {}, - !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, - !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights!.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault!.properties.vaultUri } : {}) } resource configLogs 'config' = { diff --git a/infra/core/host/container-app-upsert.bicep b/infra/core/host/container-app-upsert.bicep index c8fc7d5bcd..535929f483 100644 --- a/infra/core/host/container-app-upsert.bicep +++ b/infra/core/host/container-app-upsert.bicep @@ -116,7 +116,7 @@ module app 'container-app.bicep' = { allowedOrigins: allowedOrigins external: external env: concat(envAsArray, envSecrets) - imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' + imageName: !empty(imageName) ? imageName : exists ? existingApp!.properties.template.containers[0].image : '' targetPort: targetPort serviceBinds: serviceBinds } diff --git a/infra/core/host/container-app.bicep b/infra/core/host/container-app.bicep index dc103e7ead..865430d5e2 100644 --- a/infra/core/host/container-app.bicep +++ b/infra/core/host/container-app.bicep @@ -104,7 +104,7 @@ module containerRegistryAccess '../security/registry-access.bicep' = if (usePriv name: '${deployment().name}-registry-access' params: { containerRegistryName: containerRegistryName - principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' + principalId: usePrivateRegistry ? userIdentity!.properties.principalId : '' } } @@ -174,7 +174,7 @@ resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' } output defaultDomain string = containerAppsEnvironment.properties.defaultDomain -output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) +output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity!.properties.principalId) output identityResourceId string = normalizedIdentityType == 'UserAssigned' ? resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', userIdentity.name) : '' output imageName string = imageName output name string = app.name diff --git a/infra/core/host/container-apps-environment.bicep b/infra/core/host/container-apps-environment.bicep index 2a35daceae..fd40175640 100644 --- a/infra/core/host/container-apps-environment.bicep +++ b/infra/core/host/container-apps-environment.bicep @@ -28,11 +28,11 @@ resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2025-02-02- appLogsConfiguration: { destination: 'log-analytics' logAnalyticsConfiguration: { - customerId: logAnalyticsWorkspace.properties.customerId - sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + customerId: logAnalyticsWorkspace!.properties.customerId + sharedKey: logAnalyticsWorkspace!.listKeys().primarySharedKey } } - daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights!.properties.InstrumentationKey : '' publicNetworkAccess: usePrivateIngress ? 'Disabled' : 'Enabled' vnetConfiguration: usePrivateIngress ? { infrastructureSubnetId: subnetResourceId diff --git a/infra/core/monitor/applicationinsights-dashboard.bicep b/infra/core/monitor/applicationinsights-dashboard.bicep deleted file mode 100644 index d082e668ed..0000000000 --- a/infra/core/monitor/applicationinsights-dashboard.bicep +++ /dev/null @@ -1,1236 +0,0 @@ -metadata description = 'Creates a dashboard for an Application Insights instance.' -param name string -param applicationInsightsName string -param location string = resourceGroup().location -param tags object = {} - -// 2020-09-01-preview because that is the latest valid version -resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { - name: name - location: location - tags: tags - properties: { - lenses: [ - { - order: 0 - parts: [ - { - position: { - x: 0 - y: 0 - colSpan: 2 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'id' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' - asset: { - idInputName: 'id' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'overview' - } - } - { - position: { - x: 2 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'ProactiveDetection' - } - } - { - position: { - x: 3 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 4 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-04T01:20:33.345Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 5 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-08T18:47:35.237Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'ConfigurationId' - value: '78ce933e-e864-4b05-a27b-71fd55a6afad' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 0 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Usage' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 3 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-04T01:22:35.782Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 4 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Reliability' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 7 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'DataModel' - value: { - version: '1.0.0' - timeContext: { - durationMs: 86400000 - createdTime: '2018-05-04T23:42:40.072Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - isOptional: true - } - { - name: 'ConfigurationId' - value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' - isAdapter: true - asset: { - idInputName: 'ResourceId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'failures' - } - } - { - position: { - x: 8 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Responsiveness\r\n' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 11 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'DataModel' - value: { - version: '1.0.0' - timeContext: { - durationMs: 86400000 - createdTime: '2018-05-04T23:43:37.804Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - isOptional: true - } - { - name: 'ConfigurationId' - value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' - isAdapter: true - asset: { - idInputName: 'ResourceId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'performance' - } - } - { - position: { - x: 12 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Browser' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 15 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'MetricsExplorerJsonDefinitionId' - value: 'BrowserPerformanceTimelineMetrics' - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - createdTime: '2018-05-08T12:16:27.534Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'CurrentFilter' - value: { - eventTypes: [ - 4 - 1 - 3 - 5 - 2 - 6 - 13 - ] - typeFacets: {} - isPermissive: false - } - } - { - name: 'id' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'browser' - } - } - { - position: { - x: 0 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'sessions/count' - aggregationType: 5 - namespace: 'microsoft.insights/components/kusto' - metricVisualization: { - displayName: 'Sessions' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'users/count' - aggregationType: 5 - namespace: 'microsoft.insights/components/kusto' - metricVisualization: { - displayName: 'Users' - color: '#7E58FF' - } - } - ] - title: 'Unique sessions and users' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'segmentationUsers' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'requests/failed' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Failed requests' - color: '#EC008C' - } - } - ] - title: 'Failed requests' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'failures' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'requests/duration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Server response time' - color: '#00BCF2' - } - } - ] - title: 'Server response time' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'performance' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 12 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/networkDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Page load network connect time' - color: '#7E58FF' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/processingDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Client processing time' - color: '#44F1C8' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/sendDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Send request time' - color: '#EB9371' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/receiveDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Receiving response time' - color: '#0672F1' - } - } - ] - title: 'Average page load time breakdown' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 0 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'availabilityResults/availabilityPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Availability' - color: '#47BDF5' - } - } - ] - title: 'Average availability' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'availability' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'exceptions/server' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Server exceptions' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'dependencies/failed' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Dependency failures' - color: '#7E58FF' - } - } - ] - title: 'Server exceptions and Dependency failures' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processorCpuPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Processor time' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processCpuPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Process CPU' - color: '#7E58FF' - } - } - ] - title: 'Average processor and process CPU utilization' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 12 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'exceptions/browser' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Browser exceptions' - color: '#47BDF5' - } - } - ] - title: 'Browser exceptions' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 0 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'availabilityResults/count' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Availability test results count' - color: '#47BDF5' - } - } - ] - title: 'Availability test results count' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processIOBytesPerSecond' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Process IO rate' - color: '#47BDF5' - } - } - ] - title: 'Average process I/O rate' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/memoryAvailableBytes' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Available memory' - color: '#47BDF5' - } - } - ] - title: 'Average available memory' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - ] - } - ] - } -} - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { - name: applicationInsightsName -} diff --git a/infra/core/monitor/monitoring.bicep b/infra/core/monitor/monitoring.bicep index 0bc6c002da..6c4131b954 100644 --- a/infra/core/monitor/monitoring.bicep +++ b/infra/core/monitor/monitoring.bicep @@ -1,7 +1,6 @@ metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' param logAnalyticsName string param applicationInsightsName string -param applicationInsightsDashboardName string = '' param location string = resourceGroup().location param tags object = {} @allowed([ 'Enabled', 'Disabled' ]) @@ -33,15 +32,6 @@ module applicationInsights 'br/public:avm/res/insights/component:0.4.1' = { } } -module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(applicationInsightsDashboardName)) { - name: 'application-insights-dashboard' - params: { - name: applicationInsightsDashboardName - location: location - applicationInsightsName: applicationInsights.name - } -} - output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString output applicationInsightsId string = applicationInsights.outputs.resourceId output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey diff --git a/infra/core/search/search-services.bicep b/infra/core/search/search-services.bicep index 9d5f887aa5..99fbf5d783 100644 --- a/infra/core/search/search-services.bicep +++ b/infra/core/search/search-services.bicep @@ -13,10 +13,10 @@ param encryptionWithCmk object = { enforcement: 'Unspecified' } @allowed([ - 'default' - 'highDensity' + 'Default' + 'HighDensity' ]) -param hostingMode string = 'default' +param hostingMode string = 'Default' @allowed([ 'enabled' 'disabled' diff --git a/infra/main.bicep b/infra/main.bicep index 1fbcd9db49..ac8e97f311 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -377,7 +377,7 @@ param containerRegistryName string = deploymentTarget == 'containerapps' // For more information please see https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS var msftAllowedOrigins = [ 'https://portal.azure.com', 'https://ms.portal.azure.com' ] var loginEndpoint = environment().authentication.loginEndpoint -var loginEndpointFixed = lastIndexOf(loginEndpoint, '/') == length(loginEndpoint) - 1 ? substring(loginEndpoint, 0, length(loginEndpoint) - 1) : loginEndpoint +var loginEndpointFixed = lastIndexOf(loginEndpoint, '/') == length(loginEndpoint) - 1 ? substring(loginEndpoint, 0, max(length(loginEndpoint) - 1, 0)) : loginEndpoint var allMsftAllowedOrigins = !(empty(clientAppId)) ? union(msftAllowedOrigins, [ loginEndpointFixed ]) : msftAllowedOrigins // Combine custom origins with Microsoft origins, remove any empty origin strings and remove any duplicate origins var allowedOrigins = reduce(filter(union(split(allowedOrigin, ';'), allMsftAllowedOrigins), o => length(trim(o)) > 0), [], (cur, next) => union(cur, [next])) @@ -446,7 +446,7 @@ module applicationInsightsDashboard 'backend-dashboard.bicep' = if (useApplicati ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' location: location - applicationInsightsName: useApplicationInsights ? monitoring.outputs.applicationInsightsName : '' + applicationInsightsName: useApplicationInsights ? monitoring!.outputs.applicationInsightsName : '' } } @@ -474,15 +474,15 @@ var appEnvVariables = { AZURE_SEARCH_SERVICE: searchService.outputs.name AZURE_SEARCH_SEMANTIC_RANKER: actualSearchServiceSemanticRankerLevel AZURE_SEARCH_QUERY_REWRITING: searchServiceQueryRewriting - AZURE_VISION_ENDPOINT: useMultimodal ? vision.outputs.endpoint : '' + AZURE_VISION_ENDPOINT: useMultimodal ? vision!.outputs.endpoint : '' AZURE_SEARCH_QUERY_LANGUAGE: searchQueryLanguage AZURE_SEARCH_QUERY_SPELLER: searchQuerySpeller AZURE_SEARCH_FIELD_NAME_EMBEDDING: searchFieldNameEmbedding APPLICATIONINSIGHTS_CONNECTION_STRING: useApplicationInsights - ? monitoring.outputs.applicationInsightsConnectionString + ? monitoring!.outputs.applicationInsightsConnectionString : '' - AZURE_SPEECH_SERVICE_ID: useSpeechOutputAzure ? speech.outputs.resourceId : '' - AZURE_SPEECH_SERVICE_LOCATION: useSpeechOutputAzure ? speech.outputs.location : '' + AZURE_SPEECH_SERVICE_ID: useSpeechOutputAzure ? speech!.outputs.resourceId : '' + AZURE_SPEECH_SERVICE_LOCATION: useSpeechOutputAzure ? speech!.outputs.location : '' AZURE_SPEECH_SERVICE_VOICE: useSpeechOutputAzure ? speechServiceVoice : '' ENABLE_LANGUAGE_PICKER: enableLanguagePicker USE_SPEECH_INPUT_BROWSER: useSpeechInputBrowser @@ -492,7 +492,7 @@ var appEnvVariables = { // Chat history settings USE_CHAT_HISTORY_BROWSER: useChatHistoryBrowser USE_CHAT_HISTORY_COSMOS: useChatHistoryCosmos - AZURE_COSMOSDB_ACCOUNT: (useAuthentication && useChatHistoryCosmos) ? cosmosDb.outputs.name : '' + AZURE_COSMOSDB_ACCOUNT: (useAuthentication && useChatHistoryCosmos) ? cosmosDb!.outputs.name : '' AZURE_CHAT_HISTORY_DATABASE: chatHistoryDatabaseName AZURE_CHAT_HISTORY_CONTAINER: chatHistoryContainerName AZURE_CHAT_HISTORY_VERSION: chatHistoryVersion @@ -504,7 +504,7 @@ var appEnvVariables = { AZURE_OPENAI_REASONING_EFFORT: defaultReasoningEffort AGENTIC_KNOWLEDGEBASE_REASONING_EFFORT: defaultRetrievalReasoningEffort // Specific to Azure OpenAI - AZURE_OPENAI_SERVICE: isAzureOpenAiHost && deployAzureOpenAi ? openAi.outputs.name : '' + AZURE_OPENAI_SERVICE: isAzureOpenAiHost && deployAzureOpenAi ? openAi!.outputs.name : '' AZURE_OPENAI_CHATGPT_DEPLOYMENT: chatGpt.deploymentName AZURE_OPENAI_EMB_DEPLOYMENT: embedding.deploymentName AZURE_OPENAI_knowledgeBase_MODEL: knowledgeBase.modelName @@ -529,14 +529,14 @@ var appEnvVariables = { USE_VECTORS: useVectors USE_MULTIMODAL: useMultimodal USE_USER_UPLOAD: useUserUpload - AZURE_USERSTORAGE_ACCOUNT: useUserUpload ? userStorage.outputs.name : '' + AZURE_USERSTORAGE_ACCOUNT: useUserUpload ? userStorage!.outputs.name : '' AZURE_USERSTORAGE_CONTAINER: useUserUpload ? userStorageContainerName : '' AZURE_IMAGESTORAGE_CONTAINER: useMultimodal ? imageStorageContainerName : '' AZURE_DOCUMENTINTELLIGENCE_SERVICE: documentIntelligence.outputs.name USE_LOCAL_PDF_PARSER: useLocalPdfParser USE_LOCAL_HTML_PARSER: useLocalHtmlParser USE_MEDIA_DESCRIBER_AZURE_CU: useMediaDescriberAzureCU - AZURE_CONTENTUNDERSTANDING_ENDPOINT: useMediaDescriberAzureCU ? contentUnderstanding.outputs.endpoint : '' + AZURE_CONTENTUNDERSTANDING_ENDPOINT: useMediaDescriberAzureCU ? contentUnderstanding!.outputs.endpoint : '' RUNNING_IN_PRODUCTION: 'true' // RAG Configuration RAG_SEARCH_TEXT_EMBEDDINGS: ragSearchTextEmbeddings @@ -556,13 +556,13 @@ module backend 'core/host/appservice.bicep' = if (deploymentTarget == 'appservic location: location tags: union(tags, { 'azd-service-name': 'backend' }) // Need to check deploymentTarget again due to https://github.com/Azure/bicep/issues/3990 - appServicePlanId: deploymentTarget == 'appservice' ? appServicePlan.outputs.id : '' + appServicePlanId: deploymentTarget == 'appservice' ? appServicePlan!.outputs.id : '' runtimeName: 'python' runtimeVersion: '3.11' appCommandLine: 'python3 -m gunicorn main:app' scmDoBuildDuringDeployment: true managedIdentity: true - virtualNetworkSubnetId: usePrivateEndpoint ? isolation.outputs.appSubnetId : '' + virtualNetworkSubnetId: usePrivateEndpoint ? isolation!.outputs.appSubnetId : '' publicNetworkAccess: publicNetworkAccess allowedOrigins: allowedOrigins clientAppId: clientAppId @@ -601,8 +601,8 @@ module containerApps 'core/host/container-apps.bicep' = if (deploymentTarget == location: location containerAppsEnvironmentName: acaManagedEnvironmentName containerRegistryName: '${containerRegistryName}${resourceToken}' - logAnalyticsWorkspaceName: useApplicationInsights ? monitoring.outputs.logAnalyticsWorkspaceName : '' - subnetResourceId: usePrivateEndpoint ? isolation.outputs.appSubnetId : '' + logAnalyticsWorkspaceName: useApplicationInsights ? monitoring!.outputs.logAnalyticsWorkspaceName : '' + subnetResourceId: usePrivateEndpoint ? isolation!.outputs.appSubnetId : '' usePrivateIngress: usePrivateEndpoint workloadProfile: azureContainerAppsWorkloadProfile } @@ -612,17 +612,13 @@ module containerApps 'core/host/container-apps.bicep' = if (deploymentTarget == module acaBackend 'core/host/container-app-upsert.bicep' = if (deploymentTarget == 'containerapps') { name: 'aca-web' scope: resourceGroup - dependsOn: [ - containerApps - acaIdentity - ] params: { name: !empty(backendServiceName) ? backendServiceName : '${abbrs.webSitesContainerApps}backend-${resourceToken}' location: location identityName: (deploymentTarget == 'containerapps') ? acaIdentityName : '' exists: webAppExists - containerRegistryName: (deploymentTarget == 'containerapps') ? containerApps.outputs.registryName : '' - containerAppsEnvironmentName: (deploymentTarget == 'containerapps') ? containerApps.outputs.environmentName : '' + containerRegistryName: (deploymentTarget == 'containerapps') ? containerApps!.outputs.registryName : '' + containerAppsEnvironmentName: (deploymentTarget == 'containerapps') ? containerApps!.outputs.environmentName : '' tags: union(tags, { 'azd-service-name': 'backend' }) targetPort: 8000 containerCpuCoreCount: '1.0' @@ -631,7 +627,7 @@ module acaBackend 'core/host/container-app-upsert.bicep' = if (deploymentTarget allowedOrigins: allowedOrigins env: union(appEnvVariables, { // For using managed identity to access Azure resources. See https://github.com/microsoft/azure-container-apps/issues/442 - AZURE_CLIENT_ID: (deploymentTarget == 'containerapps') ? acaIdentity.outputs.clientId : '' + AZURE_CLIENT_ID: (deploymentTarget == 'containerapps') ? acaIdentity!.outputs.clientId : '' }) secrets: useAuthentication ? { azureclientappsecret: clientAppSecret @@ -654,14 +650,14 @@ module acaAuth 'core/host/container-apps-auth.bicep' = if (deploymentTarget == ' name: 'aca-auth' scope: resourceGroup params: { - name: acaBackend.outputs.name + name: acaBackend!.outputs.name clientAppId: clientAppId serverAppId: serverAppId clientSecretSettingName: !empty(clientAppSecret) ? 'azureclientappsecret' : '' authenticationIssuerUri: authenticationIssuerUri enableUnauthenticatedAccess: enableUnauthenticatedAccess blobContainerUri: 'https://${storageAccountName}.blob.${environment().suffixes.storage}/${tokenStorageContainerName}' - appIdentityResourceId: (deploymentTarget == 'appservice') ? '' : acaBackend.outputs.identityResourceId + appIdentityResourceId: (deploymentTarget == 'appservice') ? '' : acaBackend!.outputs.identityResourceId } } @@ -881,7 +877,7 @@ module searchDiagnostics 'core/search/search-diagnostics.bicep' = if (useApplica scope: searchServiceResourceGroup params: { searchServiceName: searchService.outputs.name - workspaceId: useApplicationInsights ? monitoring.outputs.logAnalyticsWorkspaceId : '' + workspaceId: useApplicationInsights ? monitoring!.outputs.logAnalyticsWorkspaceId : '' } } @@ -1019,7 +1015,7 @@ module ai 'core/ai/ai-environment.bicep' = if (useAiProject) { hubName: 'aihub-${resourceToken}' projectName: 'aiproj-${resourceToken}' storageAccountId: storage.outputs.id - applicationInsightsId: !useApplicationInsights ? '' : monitoring.outputs.applicationInsightsId + applicationInsightsId: !useApplicationInsights ? '' : monitoring!.outputs.applicationInsightsId } } @@ -1134,11 +1130,11 @@ module cosmosDbDataContribRoleUser 'core/security/documentdb-sql-role.bicep' = i scope: cosmosDbResourceGroup name: 'cosmosdb-data-contrib-role-user' params: { - databaseAccountName: (useAuthentication && useChatHistoryCosmos) ? cosmosDb.outputs.name : '' + databaseAccountName: (useAuthentication && useChatHistoryCosmos) ? cosmosDb!.outputs.name : '' principalId: principalId // Cosmos DB Built-in Data Contributor role roleDefinitionId: (useAuthentication && useChatHistoryCosmos) - ? '/${subscription().id}/resourceGroups/${cosmosDb.outputs.resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosDb.outputs.name}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002' + ? '/${subscription().id}/resourceGroups/${cosmosDb!.outputs.resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosDb!.outputs.name}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002' : '' } } @@ -1149,8 +1145,8 @@ module openAiRoleBackend 'core/security/role.bicep' = if (isAzureOpenAiHost && d name: 'openai-role-backend' params: { principalId: (deploymentTarget == 'appservice') - ? backend.outputs.identityPrincipalId - : acaBackend.outputs.identityPrincipalId + ? backend!.outputs.identityPrincipalId + : acaBackend!.outputs.identityPrincipalId roleDefinitionId: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' principalType: 'ServicePrincipal' } @@ -1181,8 +1177,8 @@ module storageRoleBackend 'core/security/role.bicep' = { name: 'storage-role-backend' params: { principalId: (deploymentTarget == 'appservice') - ? backend.outputs.identityPrincipalId - : acaBackend.outputs.identityPrincipalId + ? backend!.outputs.identityPrincipalId + : acaBackend!.outputs.identityPrincipalId roleDefinitionId: '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1' // Storage Blob Data Reader principalType: 'ServicePrincipal' } @@ -1193,8 +1189,8 @@ module storageOwnerRoleBackend 'core/security/role.bicep' = if (useUserUpload) { name: 'storage-owner-role-backend' params: { principalId: (deploymentTarget == 'appservice') - ? backend.outputs.identityPrincipalId - : acaBackend.outputs.identityPrincipalId + ? backend!.outputs.identityPrincipalId + : acaBackend!.outputs.identityPrincipalId roleDefinitionId: 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b' // Storage Blob Data Owner principalType: 'ServicePrincipal' } @@ -1226,7 +1222,7 @@ module storageRoleContributorBackend 'core/security/role.bicep' = if (deployment scope: storageResourceGroup name: 'storage-role-contributor-aca-backend' params: { - principalId: acaBackend.outputs.identityPrincipalId + principalId: acaBackend!.outputs.identityPrincipalId roleDefinitionId: 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' // Storage Blob Data Contributor principalType: 'ServicePrincipal' } @@ -1239,8 +1235,8 @@ module searchRoleBackend 'core/security/role.bicep' = { name: 'search-role-backend' params: { principalId: (deploymentTarget == 'appservice') - ? backend.outputs.identityPrincipalId - : acaBackend.outputs.identityPrincipalId + ? backend!.outputs.identityPrincipalId + : acaBackend!.outputs.identityPrincipalId roleDefinitionId: '1407120a-92aa-4202-b7e9-c0e197c71c8f' principalType: 'ServicePrincipal' } @@ -1251,8 +1247,8 @@ module speechRoleBackend 'core/security/role.bicep' = { name: 'speech-role-backend' params: { principalId: (deploymentTarget == 'appservice') - ? backend.outputs.identityPrincipalId - : acaBackend.outputs.identityPrincipalId + ? backend!.outputs.identityPrincipalId + : acaBackend!.outputs.identityPrincipalId roleDefinitionId: 'f2dc8367-1007-4938-bd23-fe263f013447' principalType: 'ServicePrincipal' } @@ -1264,13 +1260,13 @@ module cosmosDbRoleBackend 'core/security/documentdb-sql-role.bicep' = if (useAu scope: cosmosDbResourceGroup name: 'cosmosdb-role-backend' params: { - databaseAccountName: (useAuthentication && useChatHistoryCosmos) ? cosmosDb.outputs.name : '' + databaseAccountName: (useAuthentication && useChatHistoryCosmos) ? cosmosDb!.outputs.name : '' principalId: (deploymentTarget == 'appservice') - ? backend.outputs.identityPrincipalId - : acaBackend.outputs.identityPrincipalId + ? backend!.outputs.identityPrincipalId + : acaBackend!.outputs.identityPrincipalId // Cosmos DB Built-in Data Contributor role roleDefinitionId: (useAuthentication && useChatHistoryCosmos) - ? '/${subscription().id}/resourceGroups/${cosmosDb.outputs.resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosDb.outputs.name}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002' + ? '/${subscription().id}/resourceGroups/${cosmosDb!.outputs.resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosDb!.outputs.name}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002' : '' } } @@ -1296,7 +1292,7 @@ var openAiPrivateEndpointConnection = (usePrivateEndpoint && isAzureOpenAiHost & { groupId: 'account' dnsZoneName: 'privatelink.openai.azure.com' - resourceIds: [openAi.outputs.resourceId] + resourceIds: [openAi!.outputs.resourceId] } ] : [] @@ -1310,8 +1306,8 @@ var cognitiveServicesPrivateEndpointConnection = (usePrivateEndpoint && (!useLoc // Azure OpenAI uses its own privatelink.openai.azure.com zone and already has a separate private endpoint above. resourceIds: concat( !useLocalPdfParser ? [documentIntelligence.outputs.resourceId] : [], - useMultimodal ? [vision.outputs.resourceId] : [], - useMediaDescriberAzureCU ? [contentUnderstanding.outputs.resourceId] : [] + useMultimodal ? [vision!.outputs.resourceId] : [], + useMediaDescriberAzureCU ? [contentUnderstanding!.outputs.resourceId] : [] ) } ] @@ -1322,7 +1318,7 @@ var containerAppsPrivateEndpointConnection = (usePrivateEndpoint && deploymentTa { groupId: 'managedEnvironments' dnsZoneName: 'privatelink.${location}.azurecontainerapps.io' - resourceIds: [containerApps.outputs.environmentId] + resourceIds: [containerApps!.outputs.environmentId] } ] : [] @@ -1332,7 +1328,7 @@ var appServicePrivateEndpointConnection = (usePrivateEndpoint && deploymentTarge { groupId: 'sites' dnsZoneName: 'privatelink.azurewebsites.net' - resourceIds: [backend.outputs.id] + resourceIds: [backend!.outputs.id] } ] : [] @@ -1341,7 +1337,7 @@ var otherPrivateEndpointConnections = (usePrivateEndpoint) { groupId: 'blob' dnsZoneName: 'privatelink.blob.${environmentData.suffixes.storage}' - resourceIds: concat([storage.outputs.id], useUserUpload ? [userStorage.outputs.id] : []) + resourceIds: concat([storage.outputs.id], useUserUpload ? [userStorage!.outputs.id] : []) } { groupId: 'searchService' @@ -1351,7 +1347,7 @@ var otherPrivateEndpointConnections = (usePrivateEndpoint) { groupId: 'sql' dnsZoneName: 'privatelink.documents.azure.com' - resourceIds: (useAuthentication && useChatHistoryCosmos) ? [cosmosDb.outputs.resourceId] : [] + resourceIds: (useAuthentication && useChatHistoryCosmos) ? [cosmosDb!.outputs.resourceId] : [] } ] : [] @@ -1366,10 +1362,10 @@ module privateEndpoints 'private-endpoints.bicep' = if (usePrivateEndpoint) { tags: tags resourceToken: resourceToken privateEndpointConnections: privateEndpointConnections - applicationInsightsId: useApplicationInsights ? monitoring.outputs.applicationInsightsId : '' - logAnalyticsWorkspaceId: useApplicationInsights ? monitoring.outputs.logAnalyticsWorkspaceId : '' - vnetName: isolation.outputs.vnetName - vnetPeSubnetId: isolation.outputs.backendSubnetId + applicationInsightsId: useApplicationInsights ? monitoring!.outputs.applicationInsightsId : '' + logAnalyticsWorkspaceId: useApplicationInsights ? monitoring!.outputs.logAnalyticsWorkspaceId : '' + vnetName: isolation!.outputs.vnetName + vnetPeSubnetId: isolation!.outputs.backendSubnetId } } @@ -1380,8 +1376,8 @@ module searchReaderRoleBackend 'core/security/role.bicep' = if (useAuthenticatio name: 'search-reader-role-backend' params: { principalId: (deploymentTarget == 'appservice') - ? backend.outputs.identityPrincipalId - : acaBackend.outputs.identityPrincipalId + ? backend!.outputs.identityPrincipalId + : acaBackend!.outputs.identityPrincipalId roleDefinitionId: 'acdd72a7-3385-48ef-bd42-f606fba81ae7' principalType: 'ServicePrincipal' } @@ -1393,8 +1389,8 @@ module searchContribRoleBackend 'core/security/role.bicep' = if (useUserUpload) name: 'search-contrib-role-backend' params: { principalId: (deploymentTarget == 'appservice') - ? backend.outputs.identityPrincipalId - : acaBackend.outputs.identityPrincipalId + ? backend!.outputs.identityPrincipalId + : acaBackend!.outputs.identityPrincipalId roleDefinitionId: '8ebe5a00-799e-43f5-93ac-243d3dce84a7' principalType: 'ServicePrincipal' } @@ -1406,8 +1402,8 @@ module visionRoleBackend 'core/security/role.bicep' = if (useMultimodal) { name: 'vision-role-backend' params: { principalId: (deploymentTarget == 'appservice') - ? backend.outputs.identityPrincipalId - : acaBackend.outputs.identityPrincipalId + ? backend!.outputs.identityPrincipalId + : acaBackend!.outputs.identityPrincipalId roleDefinitionId: 'a97b65f3-24c7-4388-baec-2e87135dc908' principalType: 'ServicePrincipal' } @@ -1419,8 +1415,8 @@ module documentIntelligenceRoleBackend 'core/security/role.bicep' = if (useUserU name: 'documentintelligence-role-backend' params: { principalId: (deploymentTarget == 'appservice') - ? backend.outputs.identityPrincipalId - : acaBackend.outputs.identityPrincipalId + ? backend!.outputs.identityPrincipalId + : acaBackend!.outputs.identityPrincipalId roleDefinitionId: 'a97b65f3-24c7-4388-baec-2e87135dc908' principalType: 'ServicePrincipal' } @@ -1438,8 +1434,8 @@ output AZURE_OPENAI_EMB_DIMENSIONS int = embedding.dimensions output AZURE_OPENAI_CHATGPT_MODEL string = chatGpt.modelName // Specific to Azure OpenAI -output AZURE_OPENAI_SERVICE string = isAzureOpenAiHost && deployAzureOpenAi ? openAi.outputs.name : '' -output AZURE_OPENAI_ENDPOINT string = isAzureOpenAiHost && deployAzureOpenAi ? openAi.outputs.endpoint : '' +output AZURE_OPENAI_SERVICE string = isAzureOpenAiHost && deployAzureOpenAi ? openAi!.outputs.name : '' +output AZURE_OPENAI_ENDPOINT string = isAzureOpenAiHost && deployAzureOpenAi ? openAi!.outputs.endpoint : '' output AZURE_OPENAI_RESOURCE_GROUP string = isAzureOpenAiHost ? openAiResourceGroup.name : '' output AZURE_OPENAI_CHATGPT_DEPLOYMENT string = isAzureOpenAiHost ? chatGpt.deploymentName : '' output AZURE_OPENAI_CHATGPT_DEPLOYMENT_VERSION string = isAzureOpenAiHost ? chatGpt.deploymentVersion : '' @@ -1455,11 +1451,11 @@ output AZURE_OPENAI_KNOWLEDGEBASE_DEPLOYMENT string = isAzureOpenAiHost && useAg output AZURE_OPENAI_KNOWLEDGEBASE_MODEL string = isAzureOpenAiHost && useAgenticKnowledgeBase ? knowledgeBase.modelName : '' output AZURE_OPENAI_REASONING_EFFORT string = defaultReasoningEffort output AZURE_SEARCH_KNOWLEDGEBASE_RETRIEVAL_REASONING_EFFORT string = defaultRetrievalReasoningEffort -output AZURE_SPEECH_SERVICE_ID string = useSpeechOutputAzure ? speech.outputs.resourceId : '' -output AZURE_SPEECH_SERVICE_LOCATION string = useSpeechOutputAzure ? speech.outputs.location : '' +output AZURE_SPEECH_SERVICE_ID string = useSpeechOutputAzure ? speech!.outputs.resourceId : '' +output AZURE_SPEECH_SERVICE_LOCATION string = useSpeechOutputAzure ? speech!.outputs.location : '' -output AZURE_VISION_ENDPOINT string = useMultimodal ? vision.outputs.endpoint : '' -output AZURE_CONTENTUNDERSTANDING_ENDPOINT string = useMediaDescriberAzureCU ? contentUnderstanding.outputs.endpoint : '' +output AZURE_VISION_ENDPOINT string = useMultimodal ? vision!.outputs.endpoint : '' +output AZURE_CONTENTUNDERSTANDING_ENDPOINT string = useMediaDescriberAzureCU ? contentUnderstanding!.outputs.endpoint : '' output AZURE_DOCUMENTINTELLIGENCE_SERVICE string = documentIntelligence.outputs.name output AZURE_DOCUMENTINTELLIGENCE_RESOURCE_GROUP string = documentIntelligenceResourceGroup.name @@ -1472,7 +1468,7 @@ output AZURE_SEARCH_SEMANTIC_RANKER string = actualSearchServiceSemanticRankerLe output AZURE_SEARCH_FIELD_NAME_EMBEDDING string = searchFieldNameEmbedding output AZURE_SEARCH_USER_ASSIGNED_IDENTITY_RESOURCE_ID string = searchService.outputs.userAssignedIdentityResourceId -output AZURE_COSMOSDB_ACCOUNT string = (useAuthentication && useChatHistoryCosmos) ? cosmosDb.outputs.name : '' +output AZURE_COSMOSDB_ACCOUNT string = (useAuthentication && useChatHistoryCosmos) ? cosmosDb!.outputs.name : '' output AZURE_CHAT_HISTORY_DATABASE string = chatHistoryDatabaseName output AZURE_CHAT_HISTORY_CONTAINER string = chatHistoryContainerName output AZURE_CHAT_HISTORY_VERSION string = chatHistoryVersion @@ -1481,7 +1477,7 @@ output AZURE_STORAGE_ACCOUNT string = storage.outputs.name output AZURE_STORAGE_CONTAINER string = storageContainerName output AZURE_STORAGE_RESOURCE_GROUP string = storageResourceGroup.name -output AZURE_USERSTORAGE_ACCOUNT string = useUserUpload ? userStorage.outputs.name : '' +output AZURE_USERSTORAGE_ACCOUNT string = useUserUpload ? userStorage!.outputs.name : '' output AZURE_USERSTORAGE_CONTAINER string = userStorageContainerName output AZURE_USERSTORAGE_RESOURCE_GROUP string = storageResourceGroup.name @@ -1496,13 +1492,13 @@ output DOCUMENT_EXTRACTOR_SKILL_AUTH_RESOURCE_ID string = useCloudIngestion ? fu output FIGURE_PROCESSOR_SKILL_AUTH_RESOURCE_ID string = useCloudIngestion ? functions!.outputs.figureProcessorAuthIdentifierUri : '' output TEXT_PROCESSOR_SKILL_AUTH_RESOURCE_ID string = useCloudIngestion ? functions!.outputs.textProcessorAuthIdentifierUri : '' -output AZURE_AI_PROJECT string = useAiProject ? ai.outputs.projectName : '' +output AZURE_AI_PROJECT string = useAiProject ? ai!.outputs.projectName : '' output AZURE_USE_AUTHENTICATION bool = useAuthentication -output BACKEND_URI string = deploymentTarget == 'appservice' ? backend.outputs.uri : acaBackend.outputs.uri +output BACKEND_URI string = deploymentTarget == 'appservice' ? backend!.outputs.uri : acaBackend!.outputs.uri output AZURE_CONTAINER_REGISTRY_ENDPOINT string = deploymentTarget == 'containerapps' - ? containerApps.outputs.registryLoginServer + ? containerApps!.outputs.registryLoginServer : '' -output AZURE_VPN_CONFIG_DOWNLOAD_LINK string = useVpnGateway ? 'https://portal.azure.com/#@${tenant().tenantId}/resource${isolation.outputs.virtualNetworkGatewayId}/pointtositeconfiguration' : '' +output AZURE_VPN_CONFIG_DOWNLOAD_LINK string = useVpnGateway ? 'https://portal.azure.com/#@${tenant().tenantId}/resource${isolation!.outputs.virtualNetworkGatewayId}/pointtositeconfiguration' : '' diff --git a/infra/main.test.bicep b/infra/main.test.bicep index c79ddcfcf5..351a4960f7 100644 --- a/infra/main.test.bicep +++ b/infra/main.test.bicep @@ -14,23 +14,30 @@ module main 'main.bicep' = { environmentName: environmentName location: location appServiceSkuName: 'B1' + azureContainerAppsWorkloadProfile: 'Consumption' + cosmosDbSkuName: 'serverless' + defaultReasoningEffort: 'medium' documentIntelligenceResourceGroupLocation: location documentIntelligenceSkuName: 'S0' openAiHost: 'azure' openAiLocation: location + searchFieldNameEmbedding: 'embedding' searchIndexName: 'gptkbindex' searchQueryLanguage: 'en-us' searchQuerySpeller: 'lexicon' + searchServiceQueryRewriting: 'none' searchServiceSemanticRankerLevel: 'free' searchServiceSkuName: 'standard' speechServiceSkuName: 'S0' storageSkuName: 'Standard_LRS' + useAgenticKnowledgeBase: false useApplicationInsights: false useVectors: true useMultimodal: true enableLanguagePicker: false useSpeechInputBrowser: false useSpeechOutputBrowser: false + webAppExists: false // Test the secure configuration enableUnauthenticatedAccess: false