diff --git a/README.md b/README.md
index f9e6dba..d0d1ed8 100644
--- a/README.md
+++ b/README.md
@@ -82,6 +82,8 @@ Follow the deployment guide to deploy this solution to your own Azure subscripti
> **Note:** This solution accelerator requires **Azure Developer CLI (azd) version 1.15.0 or higher**. [Download azd here](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd).
+> **Note:** This solution accelerator also requires **Bicep CLI version 0.33.0 or higher** for compiling infrastructure templates. [Install Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/install).
+
[**📘 Click here to launch the Deployment Guide**](./docs/deploymentguide.md)
@@ -130,6 +132,7 @@ Follow the deployment guide to deploy this solution to your own Azure subscripti
| **Microsoft Purview** | Existing tenant-level Purview account (or ability to create one) |
| **Azure CLI** | Version 2.61.0 or later |
| **Azure Developer CLI** | Version 1.15.0 or later |
+ | **Bicep CLI** | Version 0.33.0 or later |
| **Quota** | Sufficient Azure OpenAI quota ([check here](./docs/quota_check.md)) |
> **Note:** Fabric automation is optional. To disable all Fabric automation, set `fabricCapacityPreset = 'none'` and `fabricWorkspacePreset = 'none'` in `infra/main.bicepparam`.
diff --git a/docs/deploymentguide.md b/docs/deploymentguide.md
index e987506..596e479 100644
--- a/docs/deploymentguide.md
+++ b/docs/deploymentguide.md
@@ -25,6 +25,7 @@ To deploy this solution accelerator, ensure you have access to an [Azure subscri
|------|----------------|--------------|
| Azure CLI | 2.61.0+ | [Install Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) |
| Azure Developer CLI (azd) | 1.15.0+ | [Install azd](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd) |
+| Bicep CLI | 0.33.0+ | [Install Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/install) |
| Git | Latest | [Install Git](https://git-scm.com/downloads) |
| PowerShell | 7.0+ | [Install PowerShell](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) |
@@ -238,6 +239,7 @@ After setting these variables, run `azd up` normally. The deployment will attach
| `postgreSqlNetworkIsolation` | PostgreSQL private networking toggle (defaults to `networkIsolation`) | `networkIsolation` |
| `useExistingVNet` | Reuse an existing VNet | `false` |
| `existingVnetResourceId` | Existing VNet resource ID (when `useExistingVNet=true`) | `` |
+| `existingLogAnalyticsWorkspaceResourceId` | Existing Log Analytics workspace to receive Foundry app + PostgreSQL + Fabric capacity diagnostics. May live in another subscription within the same tenant. | `` |
| `vmUserName` | Jump box VM admin username | `VM_ADMIN_USERNAME` env var or `testvmuser` |
| `vmAdminPassword` | Jump box VM admin password | `VM_ADMIN_PASSWORD` env var |
@@ -267,8 +269,20 @@ To check and adjust quota settings, follow the [Quota Check Guide](./quota_check
Reusing Existing Resources
-**Log Analytics Workspace:**
-See [Parameter Guide](./parameter_guide.md) for Log Analytics reuse guidance.
+**Log Analytics Workspace (BYO observability):**
+
+By default the wrapper does not create a Log Analytics workspace or Application Insights, so the deployed Foundry application and wrapper-managed resources (PostgreSQL Flexible Server, Fabric capacity) have no telemetry sink. If you already have a centralized workspace, point the deployment at it:
+
+```powershell
+azd env set EXISTING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID "/subscriptions//resourceGroups//providers/Microsoft.OperationalInsights/workspaces/"
+```
+
+When set, the deployment will:
+1. Create an Application Insights component in the deployment resource group, linked to your existing workspace.
+2. Route PostgreSQL diagnostic logs and metrics to your workspace.
+3. Route Fabric capacity diagnostic logs and metrics to your workspace.
+
+The workspace may live in a different resource group or subscription within the same tenant. The identity running `azd up` needs **`Microsoft.Insights/diagnosticSettings/write`** on the workspace itself (covered by the built-in **Log Analytics Contributor** role scoped to the workspace or its resource group — subscription-wide rights are not required). See the **Observability — Bring Your Own Log Analytics Workspace** section in the [Parameter Guide](./parameter_guide.md) for the full output reference (App Insights resource ID, connection string, instrumentation key) and notes on deployment-history exposure of those values.
diff --git a/docs/parameter_guide.md b/docs/parameter_guide.md
index b62882f..2220286 100644
--- a/docs/parameter_guide.md
+++ b/docs/parameter_guide.md
@@ -82,6 +82,55 @@ param fabricCapacitySku = 'F2' // adjust SKU as needed
---
+## Observability — Bring Your Own Log Analytics Workspace
+
+By default the wrapper sets `deployLogAnalytics = false`, so the AI Landing Zone does not create a new Log Analytics workspace and Application Insights is not provisioned. If you already have a centralized Log Analytics workspace (for example one shared across the platform), you can wire the deployed Foundry application and the wrapper-managed resources (PostgreSQL Flexible Server, Fabric capacity) to it.
+
+### How it works
+
+When you set `existingLogAnalyticsWorkspaceResourceId`:
+
+1. An **Application Insights** component is created in the deployment resource group and linked to your existing workspace via `WorkspaceResourceId`. Its name follows the same `appInsightsName` convention (`appi-`).
+2. **PostgreSQL Flexible Server** diagnostic settings (all logs + AllMetrics) are routed to your workspace.
+3. **Fabric capacity** diagnostic settings (all logs + AllMetrics) are routed to your workspace.
+4. The connection string and instrumentation key are exposed as deployment outputs so post-provision automation (or your application configuration) can pick them up.
+
+> **Note:** This is wrapper-side wiring. The upstream AI Landing Zone submodule does not natively support a BYO Log Analytics workspace, so leave `deployLogAnalytics = false` and `deployAppInsights = true` (the defaults) when using BYO so the LAZ does not create its own workspace + Application Insights pair.
+
+### Setting it via azd env
+
+```powershell
+azd env set EXISTING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID "/subscriptions//resourceGroups//providers/Microsoft.OperationalInsights/workspaces/"
+```
+
+Or set it directly in `infra/main.bicepparam`:
+
+```bicep
+param existingLogAnalyticsWorkspaceResourceId = '/subscriptions//resourceGroups//providers/Microsoft.OperationalInsights/workspaces/'
+```
+
+### Outputs
+
+| Output | Description |
+|--------|-------------|
+| `existingLogAnalyticsWorkspaceResourceIdOut` | Echo of the supplied workspace resource ID |
+| `byoApplicationInsightsResourceId` | Resource ID of the App Insights component created against the BYO workspace |
+| `byoApplicationInsightsName` | Name of the App Insights component |
+| `byoApplicationInsightsConnectionString` | Connection string for app instrumentation |
+| `byoApplicationInsightsInstrumentationKey` | Instrumentation key for legacy SDKs |
+
+> **Sensitive outputs:** The connection string and instrumentation key are bootstrap credentials for sending telemetry to your Application Insights resource. They are emitted as deployment outputs so post-provision scripts and `azd` env can wire them into application configuration. Anyone with read access to the deployment history (subscription/RG `Microsoft.Resources/deployments/read`) can retrieve these values — keep that access scoped appropriately.
+
+### Permissions
+
+The identity running the deployment needs permission to attach diagnostic settings to the workspace and to create the Application Insights component:
+
+- **`Microsoft.Insights/diagnosticSettings/write`** on the BYO Log Analytics workspace (or its resource group). The built-in **Log Analytics Contributor** role on the workspace (or its RG) covers this — there is no need to grant subscription-wide rights.
+- **`Microsoft.Insights/components/write`** on the deployment resource group (covered by **Contributor** on the deployment RG, which the deployment identity already needs to provision the rest of the stack).
+- The PostgreSQL Flexible Server and Fabric capacity that emit diagnostics are wrapper-managed in the deployment RG, so no additional cross-resource permissions are required.
+
+---
+
## Table of Contents
1. [Basic Parameters](#basic-parameters)
2. [Deployment Toggles](#deployment-toggles)
diff --git a/infra/main.bicep b/infra/main.bicep
index cf81bf7..64dc47d 100644
--- a/infra/main.bicep
+++ b/infra/main.bicep
@@ -111,6 +111,9 @@ param aiFoundryStorageAccountResourceId string = ''
param aiFoundryCosmosDBAccountResourceId string = ''
param keyVaultResourceId string = ''
+@description('Optional. Full ARM resource ID of an existing Log Analytics workspace to use for observability of the deployed Foundry application and wrapper-managed resources (PostgreSQL, Fabric capacity). When provided, an Application Insights component is created in the deployment resource group and linked to this workspace, and diagnostic settings on the wrapper-managed resources are routed to it. Leave empty to skip BYO behavior. Format: /subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.OperationalInsights/workspaces/{name}.')
+param existingLogAnalyticsWorkspaceResourceId string = ''
+
@description('Identity options.')
param useUAI bool = false
param useCAppAPIKey bool = false
@@ -405,6 +408,51 @@ var postgreSqlPrivateEndpoints = postgreSqlNetworkIsolation ? [
}
] : []
+// ----------------------------------------------------------------------
+// BYO Log Analytics Workspace (observability for the Foundry application
+// and wrapper-managed resources). When existingLogAnalyticsWorkspaceResourceId
+// is provided, diagnostic settings on wrapper-managed resources
+// (currently PostgreSQL) are routed to that workspace. An
+// Application Insights component is created in this resource group and
+// linked to that workspace only when BYO Log Analytics is enabled,
+// deployAppInsights is true, and deployLogAnalytics is false.
+// ----------------------------------------------------------------------
+var byoLogAnalyticsEnabled = !empty(existingLogAnalyticsWorkspaceResourceId)
+var byoCreateAppInsights = byoLogAnalyticsEnabled && deployAppInsights && !deployLogAnalytics
+
+resource byoAppInsights 'Microsoft.Insights/components@2020-02-02' = if (byoCreateAppInsights) {
+ name: appInsightsName
+ location: effectiveLocation
+ kind: 'web'
+ tags: deploymentTags
+ properties: {
+ Application_Type: 'web'
+ WorkspaceResourceId: existingLogAnalyticsWorkspaceResourceId
+ DisableIpMasking: false
+ publicNetworkAccessForIngestion: 'Enabled'
+ publicNetworkAccessForQuery: 'Enabled'
+ }
+}
+
+var postgreSqlDiagnosticSettings = (deployPostgreSql && byoLogAnalyticsEnabled) ? [
+ {
+ name: 'send-to-byo-law'
+ workspaceResourceId: existingLogAnalyticsWorkspaceResourceId
+ logCategoriesAndGroups: [
+ {
+ categoryGroup: 'allLogs'
+ enabled: true
+ }
+ ]
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ enabled: true
+ }
+ ]
+ }
+] : []
+
module postgreSqlFlexibleServer 'br/public:avm/res/db-for-postgre-sql/flexible-server:0.15.2' = if (deployPostgreSql) {
name: 'postgresql-flexible'
params: {
@@ -424,6 +472,7 @@ module postgreSqlFlexibleServer 'br/public:avm/res/db-for-postgre-sql/flexible-s
version: postgreSqlVersion
storageSizeGB: postgreSqlStorageSizeGB
privateEndpoints: postgreSqlPrivateEndpoints
+ diagnosticSettings: postgreSqlDiagnosticSettings
tags: deploymentTags
}
}
@@ -532,3 +581,12 @@ output desiredFabricWorkspaceName string = effectiveFabricWorkspaceName
// Purview outputs (for post-provision scripts)
output purviewAccountResourceId string = purviewAccountResourceId
output purviewCollectionName string = !empty(purviewCollectionName) ? purviewCollectionName : (!empty(environmentName) ? 'collection-${environmentName}' : 'collection-${baseName}')
+
+// Observability outputs (BYO Log Analytics Workspace)
+output existingLogAnalyticsWorkspaceResourceIdOut string = existingLogAnalyticsWorkspaceResourceId
+output byoApplicationInsightsResourceId string = byoCreateAppInsights ? byoAppInsights.id : ''
+output byoApplicationInsightsName string = byoCreateAppInsights ? byoAppInsights.name : ''
+#disable-next-line outputs-should-not-contain-secrets
+output byoApplicationInsightsConnectionString string = byoCreateAppInsights ? byoAppInsights.properties.ConnectionString : ''
+#disable-next-line outputs-should-not-contain-secrets
+output byoApplicationInsightsInstrumentationKey string = byoCreateAppInsights ? byoAppInsights.properties.InstrumentationKey : ''
diff --git a/infra/main.bicepparam b/infra/main.bicepparam
index 88e6bb0..300cd0b 100644
--- a/infra/main.bicepparam
+++ b/infra/main.bicepparam
@@ -23,6 +23,16 @@ param keyVaultResourceId = ''
param useExistingVNet = false
param existingVnetResourceId = readEnvironmentVariable('EXISTING_VNET_RESOURCE_ID', '')
+// BYO Log Analytics Workspace for observability of the deployed Foundry
+// application and wrapper-managed resources (PostgreSQL, Fabric capacity).
+// When provided, diagnostic settings on the wrapper-managed resources are
+// routed to this workspace. An Application Insights component is also
+// created in this RG and linked to the workspace, but only when
+// deployAppInsights is true and deployLogAnalytics is false (the wrapper
+// defaults). Leave empty to skip BYO behavior.
+// Format: /subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.OperationalInsights/workspaces/{name}
+param existingLogAnalyticsWorkspaceResourceId = readEnvironmentVariable('EXISTING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID', '')
+
// Optional additional Entra object IDs to grant Search roles.
param aiSearchAdditionalAccessObjectIds = []
diff --git a/infra/main.json b/infra/main.json
index ffd08db..6004a18 100644
--- a/infra/main.json
+++ b/infra/main.json
@@ -5,8 +5,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.42.1.51946",
- "templateHash": "10203842773715246176"
+ "version": "0.43.8.12551",
+ "templateHash": "1222454929578026913"
},
"description": "Deploys AI Landing Zone with Fabric capacity extension"
},
@@ -354,6 +354,13 @@
"type": "string",
"defaultValue": ""
},
+ "existingLogAnalyticsWorkspaceResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Full ARM resource ID of an existing Log Analytics workspace to use for observability of the deployed Foundry application and wrapper-managed resources (PostgreSQL, Fabric capacity). When provided, an Application Insights component is created in the deployment resource group and linked to this workspace, and diagnostic settings on the wrapper-managed resources are routed to it. Leave empty to skip BYO behavior. Format: /subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.OperationalInsights/workspaces/{name}."
+ }
+ },
"useUAI": {
"type": "bool",
"defaultValue": false,
@@ -825,6 +832,9 @@
"effectiveKeyVaultResourceId": "[if(not(empty(parameters('keyVaultResourceId'))), parameters('keyVaultResourceId'), resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')))]",
"effectivePostgreSqlAdminPassword": "[if(equals(parameters('postgreSqlAdminPassword'), '$(secretOrRandomPassword)'), format('{0}!{1}', uniqueString(subscription().id, resourceGroup().id, parameters('postgreSqlServerName')), replace(parameters('generatedPostgreSqlAdminPassword'), '-', '')), parameters('postgreSqlAdminPassword'))]",
"postgreSqlPrivateEndpoints": "[if(parameters('postgreSqlNetworkIsolation'), createArray(createObject('name', variables('postgreSqlPrivateEndpointName'), 'subnetResourceId', format('{0}/subnets/{1}', variables('effectiveVnetResourceId'), parameters('peSubnetName')), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', resourceId('Microsoft.Network/privateDnsZones', variables('postgreSqlPrivateDnsZoneName'))))))), createArray())]",
+ "byoLogAnalyticsEnabled": "[not(empty(parameters('existingLogAnalyticsWorkspaceResourceId')))]",
+ "byoCreateAppInsights": "[and(and(variables('byoLogAnalyticsEnabled'), parameters('deployAppInsights')), not(parameters('deployLogAnalytics')))]",
+ "postgreSqlDiagnosticSettings": "[if(and(parameters('deployPostgreSql'), variables('byoLogAnalyticsEnabled')), createArray(createObject('name', 'send-to-byo-law', 'workspaceResourceId', parameters('existingLogAnalyticsWorkspaceResourceId'), 'logCategoriesAndGroups', createArray(createObject('categoryGroup', 'allLogs', 'enabled', true())), 'metricCategories', createArray(createObject('category', 'AllMetrics', 'enabled', true())))), createArray())]",
"effectiveAiSearchResourceId": "[if(not(empty(parameters('aiSearchResourceId'))), parameters('aiSearchResourceId'), resourceId('Microsoft.Search/searchServices', parameters('searchServiceName')))]",
"effectiveStorageAccountResourceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
"effectiveFabricWorkspaceName": "[if(equals(variables('effectiveFabricWorkspaceMode'), 'byo'), if(not(empty(parameters('fabricWorkspaceName'))), parameters('fabricWorkspaceName'), if(not(empty(parameters('environmentName'))), format('workspace-{0}', parameters('environmentName')), format('workspace-{0}', parameters('baseName')))), if(not(empty(parameters('environmentName'))), format('workspace-{0}', parameters('environmentName')), format('workspace-{0}', parameters('baseName'))))]",
@@ -2409,6 +2419,22 @@
"postgreSqlPrivateDnsZone"
]
},
+ "byoAppInsights": {
+ "condition": "[variables('byoCreateAppInsights')]",
+ "type": "Microsoft.Insights/components",
+ "apiVersion": "2020-02-02",
+ "name": "[parameters('appInsightsName')]",
+ "location": "[variables('effectiveLocation')]",
+ "kind": "web",
+ "tags": "[parameters('deploymentTags')]",
+ "properties": {
+ "Application_Type": "web",
+ "WorkspaceResourceId": "[parameters('existingLogAnalyticsWorkspaceResourceId')]",
+ "DisableIpMasking": false,
+ "publicNetworkAccessForIngestion": "Enabled",
+ "publicNetworkAccessForQuery": "Enabled"
+ }
+ },
"postgreSqlFlexibleServerResource": {
"condition": "[parameters('deployPostgreSql')]",
"existing": true,
@@ -2491,6 +2517,9 @@
"privateEndpoints": {
"value": "[variables('postgreSqlPrivateEndpoints')]"
},
+ "diagnosticSettings": {
+ "value": "[variables('postgreSqlDiagnosticSettings')]"
+ },
"tags": {
"value": "[parameters('deploymentTags')]"
}
@@ -5114,8 +5143,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.42.1.51946",
- "templateHash": "12325469206187179338"
+ "version": "0.43.8.12551",
+ "templateHash": "11293123033508500089"
}
},
"parameters": {
@@ -5353,6 +5382,26 @@
"purviewCollectionName": {
"type": "string",
"value": "[if(not(empty(parameters('purviewCollectionName'))), parameters('purviewCollectionName'), if(not(empty(parameters('environmentName'))), format('collection-{0}', parameters('environmentName')), format('collection-{0}', parameters('baseName'))))]"
+ },
+ "existingLogAnalyticsWorkspaceResourceIdOut": {
+ "type": "string",
+ "value": "[parameters('existingLogAnalyticsWorkspaceResourceId')]"
+ },
+ "byoApplicationInsightsResourceId": {
+ "type": "string",
+ "value": "[if(variables('byoCreateAppInsights'), resourceId('Microsoft.Insights/components', parameters('appInsightsName')), '')]"
+ },
+ "byoApplicationInsightsName": {
+ "type": "string",
+ "value": "[if(variables('byoCreateAppInsights'), parameters('appInsightsName'), '')]"
+ },
+ "byoApplicationInsightsConnectionString": {
+ "type": "string",
+ "value": "[if(variables('byoCreateAppInsights'), reference('byoAppInsights').ConnectionString, '')]"
+ },
+ "byoApplicationInsightsInstrumentationKey": {
+ "type": "string",
+ "value": "[if(variables('byoCreateAppInsights'), reference('byoAppInsights').InstrumentationKey, '')]"
}
}
}
\ No newline at end of file