Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Comment thread
Saswato-Microsoft marked this conversation as resolved.

[**📘 Click here to launch the Deployment Guide**](./docs/deploymentguide.md)

<br/>
Expand Down Expand Up @@ -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 |
Comment thread
Saswato-Microsoft marked this conversation as resolved.
| **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`.
Expand Down
18 changes: 16 additions & 2 deletions docs/deploymentguide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Comment thread
Saswato-Microsoft marked this conversation as resolved.
| Git | Latest | [Install Git](https://git-scm.com/downloads) |
| PowerShell | 7.0+ | [Install PowerShell](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) |

Expand Down Expand Up @@ -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 |

Expand Down Expand Up @@ -267,8 +269,20 @@ To check and adjust quota settings, follow the [Quota Check Guide](./quota_check
<details>
<summary><b>Reusing Existing Resources</b></summary>

**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/<sub-id>/resourceGroups/<rg>/providers/Microsoft.OperationalInsights/workspaces/<workspace-name>"
```

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.
Comment on lines +281 to +285

</details>

Expand Down
49 changes: 49 additions & 0 deletions docs/parameter_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-<resourceToken>`).
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.
Comment on lines +87 to +98

### Setting it via azd env

```powershell
azd env set EXISTING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID "/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.OperationalInsights/workspaces/<workspace-name>"
```

Or set it directly in `infra/main.bicepparam`:

```bicep
param existingLogAnalyticsWorkspaceResourceId = '/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.OperationalInsights/workspaces/<workspace-name>'
```

### 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)
Expand Down
58 changes: 58 additions & 0 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ''

Comment on lines +114 to +116
@description('Identity options.')
param useUAI bool = false
param useCAppAPIKey bool = false
Expand Down Expand Up @@ -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: {
Expand All @@ -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
}
}
Expand Down Expand Up @@ -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 : ''
Comment thread
Saswato-Microsoft marked this conversation as resolved.
Comment thread
Saswato-Microsoft marked this conversation as resolved.
#disable-next-line outputs-should-not-contain-secrets
output byoApplicationInsightsInstrumentationKey string = byoCreateAppInsights ? byoAppInsights.properties.InstrumentationKey : ''
10 changes: 10 additions & 0 deletions infra/main.bicepparam
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment on lines +27 to +32
// 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 = []

Expand Down
57 changes: 53 additions & 4 deletions infra/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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'))))]",
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -2491,6 +2517,9 @@
"privateEndpoints": {
"value": "[variables('postgreSqlPrivateEndpoints')]"
},
"diagnosticSettings": {
"value": "[variables('postgreSqlDiagnosticSettings')]"
},
"tags": {
"value": "[parameters('deploymentTags')]"
}
Expand Down Expand Up @@ -5114,8 +5143,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.42.1.51946",
"templateHash": "12325469206187179338"
"version": "0.43.8.12551",
"templateHash": "11293123033508500089"
}
},
"parameters": {
Expand Down Expand Up @@ -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, '')]"
}
}
}
Loading