diff --git a/IMPLEMENTATION_NOTES.md b/IMPLEMENTATION_NOTES.md index a60c90f..7b32b28 100644 --- a/IMPLEMENTATION_NOTES.md +++ b/IMPLEMENTATION_NOTES.md @@ -309,6 +309,46 @@ Each model registration includes: The deployment name, not the backing catalog model name, is the OpenAI `model` value sent through AI Gateway. +## AI Gateway monitoring + +AI Gateway monitoring uses the native OTLP/OpenTelemetry exporter contract, not +the legacy `applicationInsights` exporter kind. The Bicep template creates the +workspace-based Application Insights component with +`AzureMonitorWorkspaceIngestionMode: Enabled`, which causes Azure Monitor to +generate a managed Data Collection Rule (DCR), Data Collection Endpoint (DCE), +and OTLP metrics/logs/traces ingestion endpoints for that component. Those +endpoints are generated asynchronously after the component is created, so +Bicep cannot create the exporter itself in the same deployment. + +Instead, the `postprovision` hook +(`infra/scripts/configure-ai-gateway.sh` / `.ps1`): + +- Polls the Application Insights component until its OTLP metrics and logs + ingestion endpoints are populated (the traces endpoint may lag behind and is + not required; metrics and logs are sufficient for the portal dashboard + eligibility check). +- Creates the `telemetryExporters` resource with `kind: OpenTelemetry`, + referencing the Application Insights resource ID, the metrics/logs/traces + OTLP endpoints, and managed-identity authentication with audience + `https://monitor.azure.com`. +- Assigns the AI Gateway system-assigned identity the **Monitoring Metrics + Publisher** role (`3913510d-42f4-4e42-8a64-420c390055eb`) at the generated + DCR scope. This assignment is made with a direct `az role assignment + create` call from the hook rather than a nested Bicep role assignment: the + DCR is generated inside a system-managed resource group that carries a deny + assignment blocking role-assignment writes performed as part of a nested + ARM/Bicep deployment, while a direct role-assignment call from the + postprovision hook succeeds. + +The `--prepare-bicep` step (run from `preprovision`) also: + +- Registers the `Microsoft.Monitor` resource provider, which is required for + Application Insights to generate the managed DCR/DCE and OTLP endpoints. +- Deletes any existing legacy `applicationInsights` exporter before Bicep + provisions again. The exporter `kind` is immutable once created, so + migrating an environment that still has the legacy exporter requires + deleting and recreating it; this cannot be done as an in-place update. + ## Regions and model defaults The default split is: diff --git a/infra/ai-gateway/main.bicep b/infra/ai-gateway/main.bicep index 01e7068..8d98075 100644 --- a/infra/ai-gateway/main.bicep +++ b/infra/ai-gateway/main.bicep @@ -92,19 +92,16 @@ resource appInsights 'Microsoft.Insights/components@2020-02-02' = if (enableMoni properties: { Application_Type: 'web' WorkspaceResourceId: logAnalyticsWorkspace.id - } -} - -resource telemetryExporter 'Microsoft.ApiManagement/service/workspaces/telemetryExporters@2025-09-01-preview' = if (enableMonitoring && deployApimGatewayViaBicep) { - parent: defaultWorkspace - name: telemetryExporterName - properties: { - kind: 'applicationInsights' - payloadCapture: false - applicationInsights: { - connectionString: appInsights!.properties.ConnectionString - resourceId: appInsights!.id - } + // Enables the Azure Monitor Workspace-based OTLP ingestion endpoints (metrics, + // logs, traces) that the AI Gateway OpenTelemetry exporter requires. The + // managed DCR/DCE and endpoints are generated asynchronously after this + // resource is created, so the telemetry exporter itself cannot be created + // in the same Bicep deployment; the postprovision hook configures it once + // the endpoints are available (see infra/scripts/configure-ai-gateway.sh). + // This property is valid at runtime but is not yet reflected in the + // Bicep/ARM type definitions for this API version, hence the suppression. + #disable-next-line BCP037 + AzureMonitorWorkspaceIngestionMode: 'Enabled' } } @@ -175,7 +172,7 @@ output appInsightsId string = enableMonitoring ? appInsights.id : '' output appInsightsName string = enableMonitoring ? appInsights.name : '' output logAnalyticsWorkspaceId string = enableMonitoring ? logAnalyticsWorkspace.id : '' output logAnalyticsWorkspaceName string = enableMonitoring ? logAnalyticsWorkspace.name : '' -output telemetryExporterId string = enableMonitoring && deployApimGatewayViaBicep ? telemetryExporter.id : '' +output telemetryExporterName string = telemetryExporterName output runtimeHostname string = deployApimGatewayViaBicep ? replace(aiGateway!.properties.gatewayUrl, 'https://', '') : '' output runtimeApiKeyId string = deployApimGatewayViaBicep ? runtimeApiKey.id : '' output gatewayPrincipalId string = deployApimGatewayViaBicep ? aiGateway!.identity.principalId : '' diff --git a/infra/main.bicep b/infra/main.bicep index a09e599..d843e59 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -268,6 +268,9 @@ output AI_GATEWAY_CONNECTOR_NAMESPACE_RESOURCE_ID string = aiGateway.outputs.con output AI_GATEWAY_FOUNDRY_ROLE_ASSIGNMENT_NAME string = aiGateway.outputs.foundryUserRoleAssignmentName output AI_GATEWAY_FOUNDRY_ROLE_ASSIGNMENT_PRINCIPAL_ID string = aiGateway.outputs.gatewayPrincipalId output APPLICATION_INSIGHTS_NAME string = aiGateway.outputs.appInsightsName +output AI_GATEWAY_APPLICATION_INSIGHTS_ID string = aiGateway.outputs.appInsightsId +output AI_GATEWAY_PRINCIPAL_ID string = aiGateway.outputs.gatewayPrincipalId +output AI_GATEWAY_TELEMETRY_EXPORTER_NAME string = aiGateway.outputs.telemetryExporterName output LOG_ANALYTICS_WORKSPACE_ID string = aiGateway.outputs.logAnalyticsWorkspaceId output LOG_ANALYTICS_WORKSPACE_NAME string = aiGateway.outputs.logAnalyticsWorkspaceName output AZURE_AI_GATEWAY_ENDPOINT string = 'https://${aiGateway.outputs.runtimeHostname}/' diff --git a/infra/scripts/configure-ai-gateway.ps1 b/infra/scripts/configure-ai-gateway.ps1 index 0db0b19..c32bbe8 100644 --- a/infra/scripts/configure-ai-gateway.ps1 +++ b/infra/scripts/configure-ai-gateway.ps1 @@ -4,6 +4,11 @@ $PSNativeCommandUseErrorActionPreference = $true $aiGatewayApiVersion = "2025-09-01-preview" $legacyAiGatewayApiVersion = "2026-05-01" $foundryUserRoleId = "53ca6127-db72-4b80-b1b0-d745d6d5456d" +$monitoringMetricsPublisherRoleId = "3913510d-42f4-4e42-8a64-420c390055eb" +$monitorManagedIdentityAudience = "https://monitor.azure.com" +$appInsightsOtlpApiVersion = "2024-02-01" +$otlpPollMaxAttempts = 30 +$otlpPollIntervalSeconds = 10 $defaultRepository = "microsoft/agent-framework" $githubMcpServer = "https://api.githubcopilot.com/mcp/" $githubMcpTools = "list_pull_requests,list_issues,actions_list" @@ -197,10 +202,140 @@ function Prepare-BicepRbac { } } +function Register-MonitorProvider { + $registrationState = [string](az provider show --namespace Microsoft.Monitor --query registrationState -o tsv 2>$null) + if ($registrationState -eq "Registered") { + return + } + Write-Host "Registering the Microsoft.Monitor resource provider for AI Gateway OpenTelemetry monitoring." + az provider register --namespace Microsoft.Monitor --wait +} + +function Remove-LegacyTelemetryExporter { + $subscriptionId = First-Value @($env:AZURE_SUBSCRIPTION_ID, (Get-AzdValue "AZURE_SUBSCRIPTION_ID"), (az account show --query id -o tsv 2>$null)) + $resourceGroup = First-Value @($env:AI_GATEWAY_RESOURCE_GROUP, (Get-AzdValue "AI_GATEWAY_RESOURCE_GROUP"), $env:RESOURCE_GROUP, $env:AZURE_RESOURCE_GROUP, (Get-AzdValue "RESOURCE_GROUP"), (Get-AzdValue "AZURE_RESOURCE_GROUP")) + $gatewayName = First-Value @($env:AI_GATEWAY_NAME, (Get-AzdValue "AI_GATEWAY_NAME")) + $workspaceName = First-Value @($env:AI_GATEWAY_WORKSPACE_NAME, "default") + $exporterName = First-Value @($env:AI_GATEWAY_TELEMETRY_EXPORTER_NAME, (Get-AzdValue "AI_GATEWAY_TELEMETRY_EXPORTER_NAME"), "appinsights") + if ([string]::IsNullOrWhiteSpace($subscriptionId) -or + [string]::IsNullOrWhiteSpace($resourceGroup) -or + [string]::IsNullOrWhiteSpace($gatewayName)) { + return + } + + $exporterUri = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.ApiManagement/service/$gatewayName/workspaces/$workspaceName/telemetryExporters/${exporterName}?api-version=$aiGatewayApiVersion" + $existingKind = "" + try { + $existingKind = [string](az rest --method get --uri $exporterUri --query properties.kind -o tsv 2>$null) + } catch { + $existingKind = "" + } + if ($existingKind -eq "applicationInsights") { + # The exporter "kind" is immutable, so an existing legacy applicationInsights + # exporter must be deleted before it can be recreated as OpenTelemetry. + Write-Host "Deleting the legacy applicationInsights telemetry exporter; exporter kind is immutable and requires delete/recreate to migrate to OpenTelemetry." + az rest --method delete --uri $exporterUri -o none + } +} + +function Set-TelemetryExporter($WorkspaceResourceId) { + $appInsightsId = First-Value @($env:AI_GATEWAY_APPLICATION_INSIGHTS_ID, (Get-AzdValue "AI_GATEWAY_APPLICATION_INSIGHTS_ID")) + if ([string]::IsNullOrWhiteSpace($appInsightsId)) { + Write-Host "AI Gateway monitoring is disabled; skipping OpenTelemetry exporter configuration." + return + } + if ([string]::IsNullOrWhiteSpace($WorkspaceResourceId)) { + Write-Warning "AI Gateway workspace resource ID is unavailable; skipping OpenTelemetry exporter configuration." + return + } + + $exporterName = First-Value @($env:AI_GATEWAY_TELEMETRY_EXPORTER_NAME, (Get-AzdValue "AI_GATEWAY_TELEMETRY_EXPORTER_NAME"), "appinsights") + $principalId = First-Value @($env:AI_GATEWAY_PRINCIPAL_ID, (Get-AzdValue "AI_GATEWAY_PRINCIPAL_ID")) + $appInsightsUri = "https://management.azure.com${appInsightsId}?api-version=$appInsightsOtlpApiVersion" + + Write-Host "Waiting for Application Insights to generate its managed DCR/DCE and OTLP ingestion endpoints." + $metricsEndpoint = "" + $logsEndpoint = "" + $tracesEndpoint = "" + $dcrId = "" + for ($attempt = 1; $attempt -le $otlpPollMaxAttempts; $attempt++) { + $appInsightsJson = [string](az rest --method get --uri $appInsightsUri -o json 2>$null) + if (-not [string]::IsNullOrWhiteSpace($appInsightsJson)) { + $props = ($appInsightsJson | ConvertFrom-Json).properties + $metricsEndpoint = First-Value @($props.MetricsIngestionEndpoint, $props.metricsIngestionEndpoint) + $logsEndpoint = First-Value @($props.LogsIngestionEndpoint, $props.logsIngestionEndpoint) + $tracesEndpoint = First-Value @($props.TracesIngestionEndpoint, $props.tracesIngestionEndpoint) + $dcrId = First-Value @($props.MetricsIngestionDataCollectionRuleId, $props.metricsIngestionDataCollectionRuleId) + } + if (-not [string]::IsNullOrWhiteSpace($metricsEndpoint) -and -not [string]::IsNullOrWhiteSpace($logsEndpoint) -and -not [string]::IsNullOrWhiteSpace($dcrId)) { + break + } + Write-Host "Waiting for Application Insights managed DCR/DCE and OTLP endpoints, attempt=$attempt" + Start-Sleep -Seconds $otlpPollIntervalSeconds + } + + if ([string]::IsNullOrWhiteSpace($metricsEndpoint) -or [string]::IsNullOrWhiteSpace($logsEndpoint)) { + Write-Warning "Application Insights did not expose OTLP ingestion endpoints in time; leaving the AI Gateway telemetry exporter unconfigured." + return + } + + $openTelemetry = [ordered]@{ + resourceId = $appInsightsId + metricsEndpoint = $metricsEndpoint + logsEndpoint = $logsEndpoint + authentication = @{ + kind = "ManagedIdentity" + managedIdentity = @{ resource = $monitorManagedIdentityAudience } + } + } + if (-not [string]::IsNullOrWhiteSpace($tracesEndpoint)) { + $openTelemetry["tracesEndpoint"] = $tracesEndpoint + } + $exporterBody = @{ + properties = @{ + kind = "OpenTelemetry" + payloadCapture = $false + openTelemetry = $openTelemetry + } + } + + Write-Host "Configuring the AI Gateway OpenTelemetry exporter." + $exporterUri = "https://management.azure.com${WorkspaceResourceId}/telemetryExporters/${exporterName}?api-version=$aiGatewayApiVersion" + Invoke-AzRestPutJson $exporterUri $exporterBody + + if (-not [string]::IsNullOrWhiteSpace($principalId) -and -not [string]::IsNullOrWhiteSpace($dcrId)) { + Write-Host "Assigning the Monitoring Metrics Publisher role to the AI Gateway identity at the generated Data Collection Rule scope." + # The generated DCR lives in a managed resource group with a deny + # assignment that blocks a nested ARM/Bicep role assignment; a direct + # role-assignment call from this postprovision hook works instead. + # A non-zero exit from az becomes a terminating error here because of + # the script-level $ErrorActionPreference / $PSNativeCommandUseErrorActionPreference. + try { + az role assignment create ` + --assignee-object-id $principalId ` + --assignee-principal-type ServicePrincipal ` + --role $monitoringMetricsPublisherRoleId ` + --scope $dcrId ` + -o none + } catch { + $errorText = $_.Exception.Message + if ($errorText -match "RoleAssignmentExists" -or $errorText -match "already exists") { + Write-Host "The Monitoring Metrics Publisher role assignment already exists; continuing." + } else { + throw "Failed to assign the Monitoring Metrics Publisher role: $errorText" + } + } + } else { + Write-Warning "Application Insights did not report the generated Data Collection Rule ID; skipping the Monitoring Metrics Publisher role assignment." + } +} + $mode = $args.Count -gt 0 ? $args[0] : "" if ($mode -eq "--prepare-bicep") { & (Join-Path $PSScriptRoot "manage-ai-gateway-lifecycle.ps1") prepare + Register-MonitorProvider Prepare-BicepRbac + Remove-LegacyTelemetryExporter exit 0 } if (-not [string]::IsNullOrWhiteSpace($mode)) { @@ -267,6 +402,8 @@ if ($providerAuth -ne "ManagedIdentity") { throw "Bicep did not configure the Foundry provider for managed identity." } +Set-TelemetryExporter $workspaceResourceId + Remove-AzdEnvValues @("GITHUB_MCP_TOKEN", "GITHUB_TOKEN") $githubToken = "" if ($null -ne (Get-Command gh -ErrorAction SilentlyContinue)) { diff --git a/infra/scripts/configure-ai-gateway.sh b/infra/scripts/configure-ai-gateway.sh index 930929a..d24138b 100755 --- a/infra/scripts/configure-ai-gateway.sh +++ b/infra/scripts/configure-ai-gateway.sh @@ -5,6 +5,11 @@ umask 077 AI_GATEWAY_API_VERSION="2025-09-01-preview" LEGACY_AI_GATEWAY_API_VERSION="2026-05-01" FOUNDRY_USER_ROLE_ID="53ca6127-db72-4b80-b1b0-d745d6d5456d" +MONITORING_METRICS_PUBLISHER_ROLE_ID="3913510d-42f4-4e42-8a64-420c390055eb" +MONITOR_MANAGED_IDENTITY_AUDIENCE="https://monitor.azure.com" +APP_INSIGHTS_OTLP_API_VERSION="2024-02-01" +OTLP_POLL_MAX_ATTEMPTS=30 +OTLP_POLL_INTERVAL_SECONDS=10 DEFAULT_REPOSITORY="microsoft/agent-framework" GITHUB_MCP_SERVER="https://api.githubcopilot.com/mcp/" GITHUB_MCP_TOOLS="list_pull_requests,list_issues,actions_list" @@ -197,9 +202,170 @@ prepare_bicep_rbac() { -o tsv) } +register_monitor_provider() { + local registration_state + registration_state="$(az provider show --namespace Microsoft.Monitor --query registrationState -o tsv 2>/dev/null || true)" + if [ "$registration_state" = "Registered" ]; then + return 0 + fi + echo "Registering the Microsoft.Monitor resource provider for AI Gateway OpenTelemetry monitoring." + az provider register --namespace Microsoft.Monitor --wait +} + +migrate_legacy_telemetry_exporter() { + local subscription_id + local resource_group + local gateway_name + local workspace_name + local exporter_name + local exporter_uri + local existing_kind + + subscription_id="$(first_value "${AZURE_SUBSCRIPTION_ID:-}" "$(azd_value AZURE_SUBSCRIPTION_ID)" "$(az account show --query id -o tsv 2>/dev/null || true)")" + resource_group="$(first_value "${AI_GATEWAY_RESOURCE_GROUP:-}" "$(azd_value AI_GATEWAY_RESOURCE_GROUP)" "${RESOURCE_GROUP:-}" "${AZURE_RESOURCE_GROUP:-}" "$(azd_value RESOURCE_GROUP)" "$(azd_value AZURE_RESOURCE_GROUP)")" + gateway_name="$(first_value "${AI_GATEWAY_NAME:-}" "$(azd_value AI_GATEWAY_NAME)")" + workspace_name="${AI_GATEWAY_WORKSPACE_NAME:-default}" + exporter_name="$(first_value "${AI_GATEWAY_TELEMETRY_EXPORTER_NAME:-}" "$(azd_value AI_GATEWAY_TELEMETRY_EXPORTER_NAME)" "appinsights")" + if [ -z "$subscription_id" ] || [ -z "$resource_group" ] || [ -z "$gateway_name" ]; then + return 0 + fi + + exporter_uri="https://management.azure.com/subscriptions/${subscription_id}/resourceGroups/${resource_group}/providers/Microsoft.ApiManagement/service/${gateway_name}/workspaces/${workspace_name}/telemetryExporters/${exporter_name}?api-version=${AI_GATEWAY_API_VERSION}" + existing_kind="$(az rest --method get --uri "$exporter_uri" --query properties.kind -o tsv 2>/dev/null || true)" + if [ "$existing_kind" = "applicationInsights" ]; then + # The exporter "kind" is immutable, so an existing legacy applicationInsights + # exporter must be deleted before it can be recreated as OpenTelemetry. + echo "Deleting the legacy applicationInsights telemetry exporter; exporter kind is immutable and requires delete/recreate to migrate to OpenTelemetry." + az rest --method delete --uri "$exporter_uri" -o none + fi +} + +configure_telemetry_exporter() { + local workspace_resource_id="$1" + local app_insights_id + local app_insights_uri + local app_insights_json + local parsed_endpoints + local exporter_name + local principal_id + local metrics_endpoint="" + local logs_endpoint="" + local traces_endpoint="" + local dcr_id="" + local attempt + + app_insights_id="$(first_value "${AI_GATEWAY_APPLICATION_INSIGHTS_ID:-}" "$(azd_value AI_GATEWAY_APPLICATION_INSIGHTS_ID)")" + if [ -z "$app_insights_id" ]; then + echo "AI Gateway monitoring is disabled; skipping OpenTelemetry exporter configuration." + return 0 + fi + if [ -z "$workspace_resource_id" ]; then + echo "AI Gateway workspace resource ID is unavailable; skipping OpenTelemetry exporter configuration." >&2 + return 0 + fi + + exporter_name="$(first_value "${AI_GATEWAY_TELEMETRY_EXPORTER_NAME:-}" "$(azd_value AI_GATEWAY_TELEMETRY_EXPORTER_NAME)" "appinsights")" + principal_id="$(first_value "${AI_GATEWAY_PRINCIPAL_ID:-}" "$(azd_value AI_GATEWAY_PRINCIPAL_ID)")" + app_insights_uri="https://management.azure.com${app_insights_id}?api-version=${APP_INSIGHTS_OTLP_API_VERSION}" + + echo "Waiting for Application Insights to generate its managed DCR/DCE and OTLP ingestion endpoints." + for attempt in $(seq 1 "$OTLP_POLL_MAX_ATTEMPTS"); do + app_insights_json="$(az rest --method get --uri "$app_insights_uri" -o json 2>/dev/null || true)" + if [ -n "$app_insights_json" ]; then + parsed_endpoints="$(python3 -c ' +import json, sys +p = json.load(sys.stdin).get("properties", {}) +fields = [ + p.get("MetricsIngestionEndpoint") or p.get("metricsIngestionEndpoint") or "", + p.get("LogsIngestionEndpoint") or p.get("logsIngestionEndpoint") or "", + p.get("TracesIngestionEndpoint") or p.get("tracesIngestionEndpoint") or "", + p.get("MetricsIngestionDataCollectionRuleId") or p.get("metricsIngestionDataCollectionRuleId") or "", +] +print("\t".join(fields)) +' <<< "$app_insights_json")" + IFS=$'\t' read -r metrics_endpoint logs_endpoint traces_endpoint dcr_id <<< "$parsed_endpoints" + fi + if [ -n "$metrics_endpoint" ] && [ -n "$logs_endpoint" ] && [ -n "$dcr_id" ]; then + break + fi + echo "Waiting for Application Insights managed DCR/DCE and OTLP endpoints, attempt=${attempt}" + sleep "$OTLP_POLL_INTERVAL_SECONDS" + done + + if [ -z "$metrics_endpoint" ] || [ -z "$logs_endpoint" ]; then + echo "Application Insights did not expose OTLP ingestion endpoints in time; leaving the AI Gateway telemetry exporter unconfigured." >&2 + return 0 + fi + + exporter_body_file="$(mktemp)" + python3 - "$app_insights_id" "$metrics_endpoint" "$logs_endpoint" "$traces_endpoint" "$MONITOR_MANAGED_IDENTITY_AUDIENCE" > "$exporter_body_file" <<'PY' +import json +import sys + +resource_id, metrics_endpoint, logs_endpoint, traces_endpoint, audience = sys.argv[1:6] +open_telemetry = { + "resourceId": resource_id, + "metricsEndpoint": metrics_endpoint, + "logsEndpoint": logs_endpoint, + "authentication": { + "kind": "ManagedIdentity", + "managedIdentity": {"resource": audience}, + }, +} +if traces_endpoint: + open_telemetry["tracesEndpoint"] = traces_endpoint + +print(json.dumps({ + "properties": { + "kind": "OpenTelemetry", + "payloadCapture": False, + "openTelemetry": open_telemetry, + } +})) +PY + + echo "Configuring the AI Gateway OpenTelemetry exporter." + az rest \ + --method put \ + --uri "https://management.azure.com${workspace_resource_id}/telemetryExporters/${exporter_name}?api-version=${AI_GATEWAY_API_VERSION}" \ + --body @"$exporter_body_file" \ + -o none + rm -f "$exporter_body_file" + exporter_body_file="" + + if [ -n "$principal_id" ] && [ -n "$dcr_id" ]; then + echo "Assigning the Monitoring Metrics Publisher role to the AI Gateway identity at the generated Data Collection Rule scope." + # The generated DCR lives in a managed resource group with a deny + # assignment that blocks a nested ARM/Bicep role assignment; a direct + # role-assignment call from this postprovision hook works instead. + local role_assignment_error + role_assignment_error="$(az role assignment create \ + --assignee-object-id "$principal_id" \ + --assignee-principal-type ServicePrincipal \ + --role "$MONITORING_METRICS_PUBLISHER_ROLE_ID" \ + --scope "$dcr_id" \ + -o none 2>&1)" && role_assignment_error="" + if [ -n "$role_assignment_error" ]; then + case "$role_assignment_error" in + *RoleAssignmentExists*|*"already exists"*) + echo "The Monitoring Metrics Publisher role assignment already exists; continuing." + ;; + *) + echo "Failed to assign the Monitoring Metrics Publisher role: $role_assignment_error" >&2 + return 1 + ;; + esac + fi + else + echo "Application Insights did not report the generated Data Collection Rule ID; skipping the Monitoring Metrics Publisher role assignment." >&2 + fi +} + if [ "${1:-}" = "--prepare-bicep" ]; then bash "$REPO_ROOT/infra/scripts/manage-ai-gateway-lifecycle.sh" prepare + register_monitor_provider prepare_bicep_rbac + migrate_legacy_telemetry_exporter exit 0 elif [ "$#" -gt 0 ]; then echo "Usage: $0 [--prepare-bicep]" >&2 @@ -207,8 +373,9 @@ elif [ "$#" -gt 0 ]; then fi tool_server_body="" +exporter_body_file="" cleanup() { - rm -f "$tool_server_body" + rm -f "$tool_server_body" "$exporter_body_file" } trap cleanup EXIT @@ -283,6 +450,8 @@ if [ "$provider_auth" != "ManagedIdentity" ]; then exit 1 fi +configure_telemetry_exporter "$workspace_resource_id" + remove_azd_env_values GITHUB_MCP_TOKEN GITHUB_TOKEN github_token="" if command -v gh >/dev/null 2>&1; then