Skip to content
Merged
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).

[**📘 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 |
| **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
17 changes: 15 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) |
| 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 PostgreSQL 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,19 @@ 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 PostgreSQL Flexible Server 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. Route PostgreSQL diagnostic logs and metrics to your workspace (when PostgreSQL is deployed by the wrapper).
2. Create an Application Insights component in the deployment resource group, linked to your existing workspace — only when Application Insights deployment is enabled and the deployment is not creating a new Log Analytics workspace (i.e. `deployAppInsights = true` and `deployLogAnalytics = false`, which are the wrapper defaults).

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 (including App Insights values when that component is deployed) and notes on deployment-history exposure of those values.
Comment thread
Saswato-Microsoft marked this conversation as resolved.

</details>

Expand Down
48 changes: 48 additions & 0 deletions docs/parameter_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,54 @@ 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 PostgreSQL Flexible Server 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` — **only when `deployAppInsights = true` and `deployLogAnalytics = false`** (the wrapper defaults). Its name follows the same `appInsightsName` convention (`appi-<resourceToken>`).
2. **PostgreSQL Flexible Server** diagnostic settings (all logs + AllMetrics) are routed to your workspace (only when PostgreSQL is deployed by the wrapper).
3. The connection string and instrumentation key are exposed as deployment outputs (when the Application Insights component is created) 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/<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 that emits diagnostics is wrapper-managed in the deployment RG, so no additional cross-resource permissions are required.
Comment thread
Saswato-Microsoft marked this conversation as resolved.

---

## 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 PostgreSQL. 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 PostgreSQL flexible server 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
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 : ''
#disable-next-line outputs-should-not-contain-secrets
output byoApplicationInsightsInstrumentationKey string = byoCreateAppInsights ? byoAppInsights.properties.InstrumentationKey : ''
Comment thread
Saswato-Microsoft marked this conversation as resolved.
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 PostgreSQL resources.
// When provided, diagnostic settings on the wrapper-managed PostgreSQL
// 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 = []

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": "8519040683451216502"
},
"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 PostgreSQL. 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 PostgreSQL flexible server 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