From 5a4b47863326d42ce3320244e8bca343324bde88 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Thu, 5 Mar 2026 11:52:56 -0500 Subject: [PATCH 01/22] Update submodule integration flow --- .gitmodules | 2 +- azure.yaml | 15 +- infra/main.bicep | 258 +++++++++++++++++--------- infra/main.bicepparam | 263 +++++++++++++++++++------- scripts/preprovision-integrated.ps1 | 275 +++++++++++++++++----------- submodules/ai-landing-zone | 2 +- 6 files changed, 534 insertions(+), 281 deletions(-) diff --git a/.gitmodules b/.gitmodules index cfdec76..6a06097 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "submodules/ai-landing-zone"] path = submodules/ai-landing-zone - url = https://github.com/Azure/AI-Landing-Zones.git + url = https://github.com/Azure/bicep-ptn-aiml-landing-zone diff --git a/azure.yaml b/azure.yaml index fb0ad0c..42c26a8 100644 --- a/azure.yaml +++ b/azure.yaml @@ -16,14 +16,8 @@ metadata: hooks: preprovision: # Integrated preprovision: - # - Runs AI Landing Zone preprovision to generate deploy/ files and Template Specs - # - Ensures our wrapper points to deploy/main.bicep (Template Spec-based) to avoid ARM 4MB template limit - # On Windows, `shell: sh` may not be available; the PowerShell script is a fallback. - - shell: sh - run: ./scripts/preprovision-integrated.sh - interactive: false - continueOnError: true - + # - Deploys the AI Landing Zone submodule separately to avoid ARM 4MB template limit + # PowerShell is the supported entrypoint in this repo. - shell: pwsh run: ./scripts/preprovision-integrated.ps1 interactive: false @@ -144,8 +138,3 @@ hooks: shell: pwsh continueOnError: false - # Stage 19: Clean up AI Landing Zone template specs - - run: ./submodules/ai-landing-zone/bicep/scripts/postprovision.ps1 - interactive: false - shell: pwsh - continueOnError: false diff --git a/infra/main.bicep b/infra/main.bicep index c7433e4..e0cacb1 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -8,65 +8,155 @@ targetScope = 'resourceGroup' metadata description = 'Deploys AI Landing Zone with Fabric capacity extension' -import * as types from '../submodules/ai-landing-zone/bicep/infra/common/types.bicep' +import * as const from '../submodules/ai-landing-zone/constants/constants.bicep' // ======================================== -// PARAMETERS - AI LANDING ZONE (Required) +// PARAMETERS - AI LANDING ZONE (Pass-through) // ======================================== -@description('Per-service deployment toggles for the AI Landing Zone submodule.') -param deployToggles object = {} +@description('Name of the Azure Developer CLI environment.') +param environmentName string -@description('Optional. Enable platform landing zone integration.') -param flagPlatformLandingZone bool = false - -@description('Optional. Existing resource IDs to reuse.') -param resourceIds types.resourceIdsType = {} - -@description('Optional. Azure region for resources.') +@description('Azure region for resources.') param location string = resourceGroup().location -@description('Optional. Environment name for resource naming.') -param environmentName string = '' +@description('Azure region for Cosmos DB.') +param cosmosLocation string = resourceGroup().location -@description('Optional. Resource naming token.') -param resourceToken string = toLower(uniqueString(subscription().id, resourceGroup().name, location)) +@description('Principal ID for role assignments.') +param principalId string -@description('Optional. Base name for resources.') +@description('Principal type for role assignments.') +@allowed([ + 'User' + 'ServicePrincipal' + 'Group' +]) +param principalType string = 'User' + +@description('Tags for all resources.') +param deploymentTags object = {} + +@description('App Configuration label.') +param appConfigLabel string = 'ai-lz' + +@description('Enable network isolation.') +param networkIsolation bool = false + +@description('Use an existing VNet.') +param useExistingVNet bool = false + +@description('Existing VNet resource ID.') +param existingVnetResourceId string = '' + +@description('Subnet names.') +param agentSubnetName string = 'agent-subnet' +param peSubnetName string = 'pe-subnet' +param gatewaySubnetName string = 'gateway-subnet' +param azureBastionSubnetName string = 'AzureBastionSubnet' +param azureFirewallSubnetName string = 'AzureFirewallSubnet' +param azureAppGatewaySubnetName string = 'AppGatewaySubnet' +param jumpboxSubnetName string = 'jumpbox-subnet' +param apiManagementSubnetName string = 'api-management-subnet' +param acaEnvironmentSubnetName string = 'aca-environment-subnet' +param devopsBuildAgentsSubnetName string = 'devops-build-agents-subnet' + +@description('VNet address prefixes.') +param vnetAddressPrefixes array = [ + '192.168.0.0/21' +] + +@description('Subnet address prefixes.') +param agentSubnetPrefix string = '192.168.0.0/24' +param acaEnvironmentSubnetPrefix string = '192.168.1.0/24' +param peSubnetPrefix string = '192.168.2.0/26' +param azureBastionSubnetPrefix string = '192.168.2.64/26' +param azureFirewallSubnetPrefix string = '192.168.2.128/26' +param gatewaySubnetPrefix string = '192.168.2.192/26' +param azureAppGatewaySubnetPrefix string = '192.168.3.0/27' +param apimSubnetPrefix string = '192.168.3.32/27' +param jumpboxSubnetPrefix string = '192.168.3.64/27' +param devopsBuildAgentsSubnetPrefix string = '192.168.3.96/27' + +@description('Feature flags.') +param deployGroundingWithBing bool = true +param deployAiFoundry bool = true +param deployAiFoundrySubnet bool = true +param deployAppConfig bool = true +param deployKeyVault bool = true +param deployVmKeyVault bool = true +param deployLogAnalytics bool = false +param deployAppInsights bool = true +param deploySearchService bool = true +param deployStorageAccount bool = true +param deployCosmosDb bool = true +param deployContainerApps bool = true +param deployContainerRegistry bool = true +param deployContainerEnv bool = true +param deployVM bool = true +param deploySubnets bool = true +param deployNsgs bool = true +param sideBySideDeploy bool = true +param deploySoftware bool = true +param deployApim bool = false +param deployAfProject bool = true +param deployAAfAgentSvc bool = true +param enableAgenticRetrieval bool = false + +@description('Existing resource IDs to reuse.') +param aiSearchResourceId string = '' +param aiFoundryStorageAccountResourceId string = '' +param aiFoundryCosmosDBAccountResourceId string = '' +param keyVaultResourceId string = '' + +@description('Identity options.') +param useUAI bool = false +param useCAppAPIKey bool = false +param useZoneRedundancy bool = false + +@description('Resource naming token.') +param resourceToken string = toLower(uniqueString(subscription().id, environmentName, location)) + +@description('Short base name for resource naming.') param baseName string = substring(resourceToken, 0, 12) -@description('Optional. AI Search settings.') -param aiSearchDefinition types.kSAISearchDefinitionType? - -@description('Optional. Additional Entra object IDs (users or groups) granted AI Search contributor roles.') -param aiSearchAdditionalAccessObjectIds array = [] - -@description('Optional. Enable telemetry.') -param enableTelemetry bool = true - -@description('Optional. Tags for all resources.') -param tags object = {} - -// All other optional parameters from AI Landing Zone - pass as needed -@description('Optional. Private DNS Zone configuration.') -param privateDnsZonesDefinition types.privateDnsZonesDefinitionType = {} - -@description('Optional. Enable Defender for AI.') -param enableDefenderForAI bool = true - -@description('Optional. NSG definitions per subnet.') -param nsgDefinitions types.nsgPerSubnetDefinitionsType? - -@description('Optional. Virtual Network configuration.') -param vNetDefinition types.vNetDefinitionType? - -@description('Optional. AI Foundry configuration.') -param aiFoundryDefinition types.aiFoundryDefinitionType = {} - -@description('Optional. API Management configuration.') -param apimDefinition types.apimDefinitionType? - -// Add more parameters as needed from AI Landing Zone... +@description('Resource names.') +param aiFoundryAccountName string = '${const.abbrs.ai.aiFoundry}${resourceToken}' +param aiFoundryProjectName string = '${const.abbrs.ai.aiFoundryProject}${resourceToken}' +param aiFoundryStorageAccountName string = replace('${const.abbrs.storage.storageAccount}${const.abbrs.ai.aiFoundry}${resourceToken}', '-', '') +param aiFoundrySearchServiceName string = '${const.abbrs.ai.aiSearch}${const.abbrs.ai.aiFoundry}${resourceToken}' +param aiFoundryCosmosDbName string = '${const.abbrs.databases.cosmosDBDatabase}${const.abbrs.ai.aiFoundry}${resourceToken}' +param bingSearchName string = '${const.abbrs.ai.bing}${resourceToken}' +param appConfigName string = '${const.abbrs.configuration.appConfiguration}${resourceToken}' +param appInsightsName string = '${const.abbrs.managementGovernance.applicationInsights}${resourceToken}' +param containerEnvName string = '${const.abbrs.containers.containerAppsEnvironment}${resourceToken}' +param containerRegistryName string = '${const.abbrs.containers.containerRegistry}${resourceToken}' +param dbAccountName string = '${const.abbrs.databases.cosmosDBDatabase}${resourceToken}' +param dbDatabaseName string = '${const.abbrs.databases.cosmosDBDatabase}db${resourceToken}' +param keyVaultName string = '${const.abbrs.security.keyVault}${resourceToken}' +param logAnalyticsWorkspaceName string = '${const.abbrs.managementGovernance.logAnalyticsWorkspace}${resourceToken}' +param searchServiceName string = '${const.abbrs.ai.aiSearch}${resourceToken}' +param storageAccountName string = '${const.abbrs.storage.storageAccount}${resourceToken}' +param vnetName string = '${const.abbrs.networking.virtualNetwork}${resourceToken}' + +@description('Model deployments and container app configuration.') +param modelDeploymentList array +param containerAppsList array +param workloadProfiles array = [] + +@description('Miscellaneous settings.') +param acrDnsSuffix string = (environment().name == 'AzureUSGovernment' ? 'azurecr.us' : environment().name == 'AzureChinaCloud' ? 'azurecr.cn' : 'azurecr.io') +param databaseContainersList array +param vmName string = '' +param vmUserName string = '' +@secure() +param vmAdminPassword string +param vmSize string = 'Standard_D8s_v5' +param vmImageSku string = 'win11-25h2-ent' +param vmImagePublisher string = 'MicrosoftWindowsDesktop' +param vmImageOffer string = 'windows-11' +param vmImageVersion string = 'latest' +param storageAccountContainersList array // ======================================== // PARAMETERS - FABRIC EXTENSION @@ -113,32 +203,6 @@ param purviewAccountResourceId string = '' @description('Optional. Existing Purview collection name') param purviewCollectionName string = '' -// ======================================== -// AI LANDING ZONE DEPLOYMENT -// ======================================== - -module aiLandingZone '../submodules/ai-landing-zone/bicep/deploy/main.bicep' = { - name: 'ai-landing-zone' - params: { - deployToggles: deployToggles - flagPlatformLandingZone: flagPlatformLandingZone - resourceIds: resourceIds - location: location - resourceToken: resourceToken - baseName: baseName - enableTelemetry: enableTelemetry - tags: tags - privateDnsZonesDefinition: privateDnsZonesDefinition - enableDefenderForAI: enableDefenderForAI - nsgDefinitions: nsgDefinitions - vNetDefinition: vNetDefinition - aiFoundryDefinition: aiFoundryDefinition - apimDefinition: apimDefinition - aiSearchDefinition: aiSearchDefinition - // Add more parameters as needed... - } -} - // ======================================== // FABRIC CAPACITY DEPLOYMENT // ======================================== @@ -159,30 +223,42 @@ module fabricCapacity 'modules/fabric-capacity.bicep' = if (effectiveFabricCapac location: location sku: fabricCapacitySku adminMembers: fabricCapacityAdmins - tags: tags + tags: deploymentTags } - dependsOn: [ - aiLandingZone - ] } // ======================================== // OUTPUTS - Pass through from AI Landing Zone // ======================================== -output virtualNetworkResourceId string = aiLandingZone.outputs.virtualNetworkResourceId -output keyVaultResourceId string = aiLandingZone.outputs.keyVaultResourceId -output storageAccountResourceId string = aiLandingZone.outputs.storageAccountResourceId -output aiFoundryProjectName string = aiLandingZone.outputs.aiFoundryProjectName -output logAnalyticsWorkspaceResourceId string = aiLandingZone.outputs.logAnalyticsWorkspaceResourceId -output aiSearchResourceId string = aiLandingZone.outputs.aiSearchResourceId -output aiSearchName string = aiLandingZone.outputs.aiSearchName -output aiSearchAdditionalAccessObjectIds array = aiSearchAdditionalAccessObjectIds - -// Subnet IDs (constructed from VNet ID using AI Landing Zone naming convention) -output peSubnetResourceId string = '${aiLandingZone.outputs.virtualNetworkResourceId}/subnets/pe-subnet' -output jumpboxSubnetResourceId string = '${aiLandingZone.outputs.virtualNetworkResourceId}/subnets/jumpbox-subnet' -output agentSubnetResourceId string = '${aiLandingZone.outputs.virtualNetworkResourceId}/subnets/agent-subnet' +var effectiveVnetResourceId = useExistingVNet && !empty(existingVnetResourceId) + ? existingVnetResourceId + : resourceId('Microsoft.Network/virtualNetworks', vnetName) + +var effectiveKeyVaultResourceId = !empty(keyVaultResourceId) + ? keyVaultResourceId + : resourceId('Microsoft.KeyVault/vaults', keyVaultName) + +var effectiveAiSearchResourceId = !empty(aiSearchResourceId) + ? aiSearchResourceId + : resourceId('Microsoft.Search/searchServices', searchServiceName) + +var effectiveStorageAccountResourceId = resourceId('Microsoft.Storage/storageAccounts', storageAccountName) +var effectiveLogAnalyticsWorkspaceResourceId = resourceId('Microsoft.OperationalInsights/workspaces', logAnalyticsWorkspaceName) + +output virtualNetworkResourceId string = effectiveVnetResourceId +output keyVaultResourceId string = effectiveKeyVaultResourceId +output storageAccountResourceId string = effectiveStorageAccountResourceId +output aiFoundryProjectName string = aiFoundryProjectName +output logAnalyticsWorkspaceResourceId string = effectiveLogAnalyticsWorkspaceResourceId +output aiSearchResourceId string = effectiveAiSearchResourceId +output aiSearchName string = searchServiceName +output aiSearchAdditionalAccessObjectIds array = [] + +// Subnet IDs (constructed from VNet ID and subnet names) +output peSubnetResourceId string = '${effectiveVnetResourceId}/subnets/${peSubnetName}' +output jumpboxSubnetResourceId string = '${effectiveVnetResourceId}/subnets/${jumpboxSubnetName}' +output agentSubnetResourceId string = '${effectiveVnetResourceId}/subnets/${agentSubnetName}' // Fabric outputs output fabricCapacityModeOut string = effectiveFabricCapacityMode diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 9d68690..0ccc044 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -4,80 +4,208 @@ using './main.bicep' // AI LANDING ZONE PARAMETERS // ======================================== -// Per-service deployment toggles. -param deployToggles = { - acaEnvironmentNsg: true - agentNsg: true - apiManagement: false - apiManagementNsg: false - appConfig: true - appInsights: true - applicationGateway: true - applicationGatewayNsg: true - applicationGatewayPublicIp: true - bastionHost: true - bastionNsg: true - buildVm: true - containerApps: true - containerEnv: true - containerRegistry: true - cosmosDb: true - devopsBuildAgentsNsg: true - firewall: false - groundingWithBingSearch: true - jumpVm: true - jumpboxNsg: true - keyVault: true - logAnalytics: true - peNsg: true - searchService: true - storageAccount: true - virtualNetwork: true - wafPolicy: true -} - -// Existing resource IDs (empty means create new) Add any resource ID separated by a comma to utilize existing items like Keyvault, Storage, etc.. -param resourceIds = {} - -// Enable platform landing zone integration. When true, private DNS zones and private endpoints are managed by the platform landing zone. -param flagPlatformLandingZone = false - -// Environment name for resource naming (uses AZURE_ENV_NAME from azd). param environmentName = readEnvironmentVariable('AZURE_ENV_NAME', '') +param location = readEnvironmentVariable('AZURE_LOCATION', '') +param cosmosLocation = readEnvironmentVariable('AZURE_COSMOS_LOCATION', '') +// Set this to your Entra object ID if Graph lookup is blocked. +param principalId = '0d60355b-dcae-4331-b55f-283d80aabde5' +param principalType = 'User' +param deploymentTags = {} +param appConfigLabel = 'ai-lz' -// Collapse the environment name into an Azure-safe token. -var foundryEnvName = empty(environmentName) - ? 'default' - : toLower(replace(replace(replace(environmentName, ' ', '-'), '_', '-'), '.', '-')) - -param aiFoundryDefinition = { - aiFoundryConfiguration: { - accountName: 'ai-${foundryEnvName}' - allowProjectManagement: true - createCapabilityHosts: false - disableLocalAuth: false - project: { - name: 'project-${foundryEnvName}' - displayName: 'AI Foundry project (${environmentName})' - description: 'Environment-scoped project created by the AI Landing Zone deployment.' +param networkIsolation = true +param useExistingVNet = false +param existingVnetResourceId = readEnvironmentVariable('EXISTING_VNET_RESOURCE_ID', '') + +param deployGroundingWithBing = false +param deployAiFoundry = true +param deployAiFoundrySubnet = true +param deployAppConfig = true +param deployKeyVault = true +param deployVmKeyVault = readEnvironmentVariable('DEPLOY_VM_KEY_VAULT', 'true') == 'true' +param deployLogAnalytics = true +param deployAppInsights = true +param deploySearchService = true +param deployStorageAccount = true +param deployCosmosDb = true +param deployContainerApps = true +param deployContainerRegistry = true +param deployContainerEnv = true +param deployVM = true +param deploySubnets = readEnvironmentVariable('DEPLOY_SUBNETS', 'true') == 'true' +param deployNsgs = true +param sideBySideDeploy = readEnvironmentVariable('SIDE_BY_SIDE', 'true') == 'true' +param deploySoftware = true +param deployApim = false +param deployAfProject = true +param deployAAfAgentSvc = true +param enableAgenticRetrieval = readEnvironmentVariable('ENABLE_AGENTIC_RETRIEVAL', 'false') == 'true' + +param aiSearchResourceId = '' +param aiFoundryStorageAccountResourceId = '' +param aiFoundryCosmosDBAccountResourceId = '' +param keyVaultResourceId = '' + +param useUAI = readEnvironmentVariable('USE_UAI', 'false') == 'true' +param useCAppAPIKey = readEnvironmentVariable('USE_CAPP_API_KEY', 'false') == 'true' +param useZoneRedundancy = false + +param modelDeploymentList = [ + { + name: 'chat' + model: { + format: 'OpenAI' + name: 'gpt-4.1-mini' + version: '2025-04-14' + } + sku: { + name: 'GlobalStandard' + capacity: 40 } + canonical_name: 'CHAT_DEPLOYMENT_NAME' + apiVersion: '2025-01-01-preview' + } + { + name: 'text-embedding' + model: { + format: 'OpenAI' + name: 'text-embedding-3-large' + version: '1' + } + sku: { + name: 'Standard' + capacity: 40 + } + canonical_name: 'EMBEDDING_DEPLOYMENT_NAME' + apiVersion: '2025-01-01-preview' + } +] + +param workloadProfiles = [ + { + name: 'Consumption' + workloadProfileType: 'Consumption' + } + { + workloadProfileType: 'D4' + name: 'main' + minimumCount: 0 + maximumCount: 1 } -} +] +param storageAccountContainersList = [ + { + name: 'documents-images' + canonical_name: 'DOCUMENTS_IMAGES_STORAGE_CONTAINER' + } + { + name: 'documents' + canonical_name: 'DOCUMENTS_STORAGE_CONTAINER' + } + { + name: 'nl2sql' + canonical_name: 'NL2SQL_STORAGE_CONTAINER' + } +] +param databaseContainersList = [ + { + name: 'conversations' + canonical_name: 'CONVERSATIONS_DATABASE_CONTAINER' + } + { + name: 'datasources' + canonical_name: 'DATASOURCES_DATABASE_CONTAINER' + } + { + name: 'prompts' + canonical_name: 'PROMPTS_CONTAINER' + } + { + name: 'mcp' + canonical_name: 'MCP_CONTAINER' + } +] -// AI Search settings for the default deployment. -param aiSearchDefinition = { - name: toLower('search-${empty(environmentName) ? 'default' : replace(replace(environmentName, '_', '-'), ' ', '-')}') - sku: 'standard' - semanticSearch: 'free' - managedIdentities: { - systemAssigned: true +param containerAppsList = [ + { + name: null + external: true + service_name: 'orchestrator' + profile_name: 'main' + min_replicas: 1 + max_replicas: 1 + canonical_name: 'ORCHESTRATOR_APP' + roles: [ + 'AppConfigurationDataReader' + 'CognitiveServicesUser' + 'CognitiveServicesOpenAIUser' + 'AcrPull' + 'CosmosDBBuiltInDataContributor' + 'SearchIndexDataReader' + 'StorageBlobDataReader' + 'KeyVaultSecretsUser' + ] + } + { + name: null + external: true + service_name: 'frontend' + profile_name: 'main' + min_replicas: 1 + max_replicas: 1 + canonical_name: 'FRONTEND_APP' + roles: [ + 'AppConfigurationDataReader' + 'AcrPull' + 'StorageBlobDataReader' + 'StorageBlobDelegator' + 'KeyVaultSecretsUser' + ] + } + { + name: null + external: false + service_name: 'dataingest' + profile_name: 'main' + min_replicas: 1 + max_replicas: 1 + canonical_name: 'DATAINGEST_APP' + roles: [ + 'AppConfigurationDataReader' + 'CognitiveServicesUser' + 'CognitiveServicesOpenAIUser' + 'AcrPull' + 'CosmosDBBuiltInDataContributor' + 'SearchIndexDataContributor' + 'StorageBlobDataContributor' + 'KeyVaultSecretsUser' + ] } - disableLocalAuth: true -} + { + name: null + external: false + service_name: 'mcp' + profile_name: 'main' + min_replicas: 1 + max_replicas: 1 + canonical_name: 'MCP_APP' + roles: [ + 'AppConfigurationDataReader' + 'CognitiveServicesUser' + 'CognitiveServicesOpenAIUser' + 'AcrPull' + 'CosmosDBBuiltInDataContributor' + 'SearchIndexDataContributor' + 'StorageBlobDataContributor' + 'KeyVaultSecretsUser' + ] + } +] -param aiSearchAdditionalAccessObjectIds = [] +param vmAdminPassword = readEnvironmentVariable('VM_ADMIN_PASSWORD', '$(secretOrRandomPassword)') +param vmSize = 'Standard_D8s_v5' // ======================================== // FABRIC CAPACITY PARAMETERS @@ -114,18 +242,17 @@ param fabricWorkspaceId = '' // required when fabricWorkspacePreset='byo' param fabricWorkspaceName = '' // optional (helpful for naming/UX) // Fabric capacity SKU. - param fabricCapacitySku = 'F8' // Fabric capacity admin members (email addresses or object IDs). -param fabricCapacityAdmins = [] +param fabricCapacityAdmins = ['admin@MngEnv282784.onmicrosoft.com'] // ======================================== // PURVIEW PARAMETERS (Optional) // ======================================== // Existing Purview account resource ID (in different subscription if needed). -param purviewAccountResourceId = '' +param purviewAccountResourceId = '/subscriptions/48ab3756-f962-40a8-b0cf-b33ddae744bb/resourceGroups/Governance/providers/Microsoft.Purview/accounts/swantekPurview' // Purview collection name (leave empty to auto-generate from environment name). param purviewCollectionName = '' diff --git a/scripts/preprovision-integrated.ps1 b/scripts/preprovision-integrated.ps1 index b4a0c94..9023792 100644 --- a/scripts/preprovision-integrated.ps1 +++ b/scripts/preprovision-integrated.ps1 @@ -17,73 +17,7 @@ Write-Host " AI Landing Zone - Integrated Preprovision" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan Write-Host "" -function Get-PreprovisionMarkerPath { - param( - [string]$RepoRoot - ) - - $envName = $env:AZURE_ENV_NAME - if ([string]::IsNullOrWhiteSpace($envName)) { - try { $envName = (& azd env get-value AZURE_ENV_NAME 2>$null).ToString().Trim() } catch { $envName = $null } - } - if ([string]::IsNullOrWhiteSpace($envName)) { $envName = 'default' } - - $azureDir = Join-Path $RepoRoot '.azure' - return Join-Path $azureDir ("preprovision-integrated.$envName.ok") -} - -function Test-PreprovisionAlreadyComplete { - param( - [string]$RepoRoot - ) - - $markerPath = Get-PreprovisionMarkerPath -RepoRoot $RepoRoot - if (-not (Test-Path $markerPath)) { return $false } - - $deployDir = Join-Path $RepoRoot 'submodules' 'ai-landing-zone' 'bicep' 'deploy' - if (-not (Test-Path $deployDir)) { return $false } - - $wrapperPath = Join-Path $RepoRoot 'infra' 'main.bicep' - if (-not (Test-Path $wrapperPath)) { return $false } - - try { - $wrapperContent = Get-Content $wrapperPath -Raw - if ($wrapperContent -notmatch '/bicep/deploy/main\.bicep') { return $false } - } catch { - return $false - } - - return $true -} - -function Write-PreprovisionMarker { - param( - [string]$RepoRoot, - [string]$Location, - [string]$ResourceGroup, - [string]$SubscriptionId - ) - - $markerPath = Get-PreprovisionMarkerPath -RepoRoot $RepoRoot - $markerDir = Split-Path -Parent $markerPath - if (-not (Test-Path $markerDir)) { - New-Item -ItemType Directory -Path $markerDir -Force | Out-Null - } - - $stamp = (Get-Date).ToString('s') - @( - "timestamp=$stamp", - "location=$Location", - "resourceGroup=$ResourceGroup", - "subscriptionId=$SubscriptionId" - ) | Set-Content -Path $markerPath -Encoding UTF8 -} - -$repoRootResolved = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path -if (Test-PreprovisionAlreadyComplete -RepoRoot $repoRootResolved) { - Write-Host "[i] Preprovision already completed by prior step; skipping PowerShell fallback." -ForegroundColor Yellow - exit 0 -} + $repoRootResolved = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path function Resolve-AzdEnvironmentValues { param( @@ -138,6 +72,90 @@ $Location = $resolved.Location $ResourceGroup = $resolved.ResourceGroup $SubscriptionId = $resolved.SubscriptionId +if ([string]::IsNullOrWhiteSpace($env:AZURE_LOCATION) -and -not [string]::IsNullOrWhiteSpace($Location)) { + $env:AZURE_LOCATION = $Location +} + +if ([string]::IsNullOrWhiteSpace($env:AZURE_COSMOS_LOCATION) -and -not [string]::IsNullOrWhiteSpace($Location)) { + $env:AZURE_COSMOS_LOCATION = $Location +} + +if ([string]::IsNullOrWhiteSpace($env:AZURE_PRINCIPAL_ID)) { + try { + $fromAzd = (& azd env get-value AZURE_PRINCIPAL_ID 2>$null).ToString().Trim() + if (-not [string]::IsNullOrWhiteSpace($fromAzd)) { + $env:AZURE_PRINCIPAL_ID = $fromAzd + } + } catch { + # Ignore and fall back to other methods. + } +} + +$isGuid = $false +if (-not [string]::IsNullOrWhiteSpace($env:AZURE_PRINCIPAL_ID)) { + $isGuid = $env:AZURE_PRINCIPAL_ID -match '^[0-9a-fA-F-]{36}$' +} + +if (-not $isGuid) { + try { + $acctType = (& az account show --query user.type -o tsv 2>$null).Trim() + $acctName = (& az account show --query user.name -o tsv 2>$null).Trim() + + if ($acctType -eq 'user') { + $principal = (& az ad signed-in-user show --query id -o tsv 2>$null) + if ([string]::IsNullOrWhiteSpace($principal) -and -not [string]::IsNullOrWhiteSpace($acctName)) { + $principal = (& az ad user show --id $acctName --query id -o tsv 2>$null) + } + } elseif ($acctType -eq 'servicePrincipal') { + if (-not [string]::IsNullOrWhiteSpace($acctName)) { + $principal = (& az ad sp show --id $acctName --query id -o tsv 2>$null) + } + } + + if (-not [string]::IsNullOrWhiteSpace($principal) -and ($principal -match '^[0-9a-fA-F-]{36}$')) { + $env:AZURE_PRINCIPAL_ID = $principal.Trim() + $isGuid = $true + } + } catch { + # Ignore and fall back to provided values. + } +} + +if ([string]::IsNullOrWhiteSpace($env:AZURE_PRINCIPAL_ID)) { + try { + $acctType = (& az account show --query user.type -o tsv 2>$null).Trim() + $acctName = (& az account show --query user.name -o tsv 2>$null).Trim() + + if ($acctType -eq 'user') { + $principal = (& az ad signed-in-user show --query id -o tsv 2>$null) + if ([string]::IsNullOrWhiteSpace($principal) -and -not [string]::IsNullOrWhiteSpace($acctName)) { + $principal = (& az ad user show --id $acctName --query id -o tsv 2>$null) + } + } elseif ($acctType -eq 'servicePrincipal') { + if (-not [string]::IsNullOrWhiteSpace($acctName)) { + $principal = (& az ad sp show --id $acctName --query id -o tsv 2>$null) + } + } + + if (-not [string]::IsNullOrWhiteSpace($principal)) { + $env:AZURE_PRINCIPAL_ID = $principal.Trim() + } + } catch { + # Ignore and fall back to provided values. + } +} + +if ([string]::IsNullOrWhiteSpace($env:NETWORK_ISOLATION)) { + try { + $ni = (& azd env get-value NETWORK_ISOLATION 2>$null).ToString().Trim() + if (-not [string]::IsNullOrWhiteSpace($ni)) { + $env:NETWORK_ISOLATION = $ni + } + } catch { + # Ignore and fall back to defaults. + } +} + # In non-interactive hook execution (azure.yaml sets interactive:false), Read-Host prompts are not usable. # If the resource group is missing, derive a deterministic default from AZURE_ENV_NAME. if ([string]::IsNullOrWhiteSpace($ResourceGroup)) { @@ -174,7 +192,7 @@ if ([string]::IsNullOrWhiteSpace($Location) -or [string]::IsNullOrWhiteSpace($Re } # Navigate to AI Landing Zone submodule -$aiLandingZonePath = Join-Path $PSScriptRoot ".." "submodules" "ai-landing-zone" "bicep" +$aiLandingZonePath = Join-Path $PSScriptRoot ".." "submodules" "ai-landing-zone" if (-not (Test-Path $aiLandingZonePath)) { Write-Host "[!] AI Landing Zone submodule not initialized" -ForegroundColor Yellow @@ -203,61 +221,104 @@ if (-not (Test-Path $aiLandingZonePath)) { } } -Write-Host "[1] Running AI Landing Zone preprovision..." -ForegroundColor Cyan +Write-Host "[1] Deploying AI Landing Zone submodule..." -ForegroundColor Cyan Write-Host "" -# Run the AI Landing Zone preprovision script -$preprovisionScript = Join-Path $aiLandingZonePath "scripts" "preprovision.ps1" +$submoduleMain = Join-Path $aiLandingZonePath "main.bicep" +if (-not (Test-Path $submoduleMain)) { + Write-Host "[X] AI Landing Zone main.bicep not found!" -ForegroundColor Red + Write-Host " Expected: $submoduleMain" -ForegroundColor Yellow + exit 1 +} + +$parentParamsFile = Join-Path $PSScriptRoot ".." "infra" "main.bicepparam" +if (-not (Test-Path $parentParamsFile)) { + Write-Host "[X] Parent parameters file not found!" -ForegroundColor Red + Write-Host " Expected: $parentParamsFile" -ForegroundColor Yellow + exit 1 +} + +$az = Get-Command az -ErrorAction SilentlyContinue +if ($null -eq $az) { + Write-Host "[X] Azure CLI (az) not found in PATH." -ForegroundColor Red + exit 1 +} + +Write-Host " [+] Submodule template: $submoduleMain" -ForegroundColor Green +Write-Host " [+] Parent params file: $parentParamsFile" -ForegroundColor Green + +if (-not [string]::IsNullOrWhiteSpace($SubscriptionId)) { + & az account set --subscription $SubscriptionId | Out-Null +} + +$envNameForDeployment = $env:AZURE_ENV_NAME +if ([string]::IsNullOrWhiteSpace($envNameForDeployment)) { $envNameForDeployment = 'default' } +$deploymentName = "ai-landing-zone-$envNameForDeployment-$(Get-Date -Format 'yyyyMMddHHmmss')" + +Write-Host " [+] Deployment name: $deploymentName" -ForegroundColor Green + +$compiledParent = Join-Path $env:TEMP ("parent.$deploymentName.parameters.json") -if (-not (Test-Path $preprovisionScript)) { - Write-Host "[X] AI Landing Zone preprovision script not found!" -ForegroundColor Red - Write-Host " Expected: $preprovisionScript" -ForegroundColor Yellow +& az bicep build-params --file $parentParamsFile --outfile $compiledParent | Out-Null +if ($LASTEXITCODE -ne 0 -or -not (Test-Path $compiledParent)) { + Write-Host "[X] Failed to compile parent bicepparam to JSON: $compiledParent" -ForegroundColor Red exit 1 } -# Call AI Landing Zone preprovision with current directory context -Push-Location $aiLandingZonePath +$allowedParamNames = Select-String -Path $submoduleMain -Pattern '^param\s+(\w+)' | ForEach-Object { + $_.Matches[0].Groups[1].Value +} | Sort-Object -Unique + +$parentJson = Get-Content $compiledParent -Raw | ConvertFrom-Json +$parentPrincipal = $null try { - & $preprovisionScript -Location $Location -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId - if ($LASTEXITCODE -ne 0) { - Write-Host "[X] AI Landing Zone preprovision failed" -ForegroundColor Red - exit 1 - } -} finally { - Pop-Location + $parentPrincipal = [string]$parentJson.parameters.principalId.value +} catch { + $parentPrincipal = $null } -Write-Host "" -Write-Host "[2] Verifying deploy directory..." -ForegroundColor Cyan +if ([string]::IsNullOrWhiteSpace($parentPrincipal)) { + Write-Host "[X] principalId is empty in infra/main.bicepparam. Set it to your Entra Object ID (GUID)." -ForegroundColor Red + exit 1 +} -$deployDir = Join-Path $aiLandingZonePath "deploy" -if (-not (Test-Path $deployDir)) { - Write-Host "[X] Deploy directory not created: $deployDir" -ForegroundColor Red +$parentPrincipal = $parentPrincipal.Trim() +if ($parentPrincipal -notmatch '^[0-9a-fA-F-]{36}$') { + Write-Host "[X] principalId must be a GUID. Current value: '$parentPrincipal'" -ForegroundColor Red exit 1 } -Write-Host " [+] Deploy directory ready: $deployDir" -ForegroundColor Green +$env:AZURE_PRINCIPAL_ID = $parentPrincipal +try { + & azd env set AZURE_PRINCIPAL_ID $env:AZURE_PRINCIPAL_ID 2>$null | Out-Null +} catch { + # Ignore and proceed. +} +$filtered = [ordered]@{ + '$schema' = $parentJson.'$schema' + contentVersion = $parentJson.contentVersion + parameters = @{} +} -Write-Host "" -Write-Host "[3] Updating wrapper to use deploy directory..." -ForegroundColor Cyan - -# Update our wrapper to reference deploy/ instead of infra/ -$wrapperPath = Join-Path $PSScriptRoot ".." "infra" "main.bicep" -$wrapperContent = Get-Content $wrapperPath -Raw - -# Replace infra/main.bicep reference with deploy/main.bicep -$pattern = '/bicep/infra/main\.bicep' -$replacement = '/bicep/deploy/main.bicep' - -if ($wrapperContent -match $pattern) { - $updatedContent = $wrapperContent -replace $pattern, $replacement - Set-Content -Path $wrapperPath -Value $updatedContent -NoNewline - Write-Host " [+] Wrapper updated to use Template Spec deployment" -ForegroundColor Green -} else { - Write-Host " [!] Warning: Could not update wrapper reference" -ForegroundColor Yellow - Write-Host " Expected pattern: $pattern" -ForegroundColor Gray +foreach ($name in $allowedParamNames) { + $value = $parentJson.parameters.$name + if ($null -ne $value) { + $filtered.parameters[$name] = $value + } } +$filteredParams = Join-Path $env:TEMP ("ai-landing-zone.$deploymentName.parameters.json") +$filtered | ConvertTo-Json -Depth 50 | Set-Content -Path $filteredParams -Encoding UTF8 + +& az deployment group create --name $deploymentName --resource-group $ResourceGroup --template-file $submoduleMain --parameters ("@" + $filteredParams) +if ($LASTEXITCODE -ne 0) { + Write-Host "[X] AI Landing Zone submodule deployment failed" -ForegroundColor Red + exit 1 +} + +Write-Host " [+] AI Landing Zone deployment complete" -ForegroundColor Green + + Write-Host "" Write-Host "[OK] Preprovision complete!" -ForegroundColor Green diff --git a/submodules/ai-landing-zone b/submodules/ai-landing-zone index 46fc4c9..05adc5b 160000 --- a/submodules/ai-landing-zone +++ b/submodules/ai-landing-zone @@ -1 +1 @@ -Subproject commit 46fc4c9516bd4568aa444169faa01a95f2990a5f +Subproject commit 05adc5b0407686ce33793c0bc21420fb599222d6 From 5467ae3aa06e5bb26d37a650229ccecaf32372a9 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Fri, 6 Mar 2026 17:09:12 -0500 Subject: [PATCH 02/22] Pin ai-landing-zone submodule to v1.0.1 --- submodules/ai-landing-zone | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/ai-landing-zone b/submodules/ai-landing-zone index 05adc5b..37b856b 160000 --- a/submodules/ai-landing-zone +++ b/submodules/ai-landing-zone @@ -1 +1 @@ -Subproject commit 05adc5b0407686ce33793c0bc21420fb599222d6 +Subproject commit 37b856bc3113814187dc588c3cca5da34e9ca4c7 From 8c76feb453c70405e2310f2adc8197630453fa4b Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Fri, 6 Mar 2026 17:15:30 -0500 Subject: [PATCH 03/22] Swap in Azure AI landing zone submodule and pin to v1.0.1 --- CHANGELOG.md | 14 + README.md | 4 +- azure.yaml | 11 +- docs/DeploymentGuide.md | 38 +-- docs/PARAMETER_GUIDE.md | 12 +- docs/automation-outputs-mapping.md | 4 +- docs/post_deployment_steps.md | 2 +- docs/quota_check.md | 4 +- infra/main.bicep | 6 +- infra/main.bicepparam | 40 ++- .../connect_log_analytics.ps1 | 90 ------ .../create_purview_collection.ps1 | 6 + ...gger_purview_scan_for_fabric_workspace.ps1 | 178 +++++++++-- .../register_fabric_datasource.ps1 | 6 + .../OneLakeIndex/01_setup_rbac.ps1 | 31 +- .../02_create_onelake_skillsets.ps1 | 240 +++++++++++++-- .../OneLakeIndex/03_create_onelake_index.ps1 | 238 ++++++++++++++- .../04_create_onelake_datasource.ps1 | 242 +++++++++++++-- .../05_create_onelake_indexer.ps1 | 288 ++++++++++++++++-- .../06_setup_ai_foundry_search_rbac.ps1 | 73 ++++- .../OneLakeIndex/setup_ai_services_rbac.ps1 | 24 ++ scripts/automationScripts/SecurityModule.ps1 | 22 +- scripts/preprovision-integrated.ps1 | 142 ++++++++- 23 files changed, 1461 insertions(+), 254 deletions(-) delete mode 100644 scripts/automationScripts/FabricPurviewAutomation/connect_log_analytics.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c818e0..89b5219 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ All notable changes to this project will be documented in this file. +## [Unreleased] - 2026-03-06 +### Added +- Parameter to override Log Analytics workspace resource ID and output mapping for automation scripts +- Optional `SKIP_PURVIEW_INTEGRATION` guard for Purview automation scripts (used by hooks when Purview is disabled) +- Retry/timeout handling for AI Search public network access toggles in OneLake indexing scripts + +### Changed +- Preprovision error output simplified with concise failure reason and optional verbose diagnostics +- Main parameter file reordered into required/optional/defaulted sections with clearer comments +- OneLake indexing scripts prefer outputs, include AAD-only auth, and handle transient 409 run conflicts + +### Fixed +- Power BI headers initialization in Log Analytics linkage script to resolve workspace ID lookups + ## [1.3] - 2025-12-09 ### Added - Microsoft Fabric integration with automatic capacity creation and management diff --git a/README.md b/README.md index 97c5cb6..bd096ce 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ This accelerator extends the [AI Landing Zone](https://github.com/Azure/ai-landi ### Additional Resources -- [AI Landing Zone Documentation](https://github.com/Azure/ai-landing-zone) +- [AI Landing Zone Documentation](https://github.com/Azure/bicep-ptn-aiml-landing-zone) - [Azure AI Foundry Documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/) - [Microsoft Fabric Documentation](https://learn.microsoft.com/en-us/fabric/) @@ -104,7 +104,7 @@ Follow the deployment guide to deploy this solution to your own Azure subscripti > **GitHub Codespaces and Dev Containers handle this automatically.** > **Windows shell note** ->
Preprovision uses `shell: sh`. Run `azd` from Git Bash/WSL so `bash` is available, or switch the `preprovision` hook in `azure.yaml` to the provided PowerShell script if you want to stay in PowerShell. +>
Preprovision runs with PowerShell (`pwsh`) by default. Run `azd` from PowerShell 7+ (or any terminal that can invoke `pwsh`).
diff --git a/azure.yaml b/azure.yaml index 42c26a8..2c5c43e 100644 --- a/azure.yaml +++ b/azure.yaml @@ -55,13 +55,13 @@ hooks: continueOnError: false # Stage 5: Purview Collection Creation - - run: ./scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1 + - run: "$env:SKIP_PURVIEW_INTEGRATION='true'; ./scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1" interactive: false shell: pwsh continueOnError: false # Stage 6: Register Fabric as Purview Data Source - - run: ./scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1 + - run: "$env:SKIP_PURVIEW_INTEGRATION='true'; ./scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1" interactive: false shell: pwsh continueOnError: false @@ -127,14 +127,9 @@ hooks: continueOnError: false # Stage 17: Trigger Purview Scan (if Purview enabled) - - run: ./scripts/automationScripts/FabricPurviewAutomation/trigger_purview_scan_for_fabric_workspace.ps1 + - run: "$env:SKIP_PURVIEW_INTEGRATION='true'; ./scripts/automationScripts/FabricPurviewAutomation/trigger_purview_scan_for_fabric_workspace.ps1" interactive: false shell: pwsh continueOnError: false - # Stage 18: Connect Log Analytics (placeholder) - - run: ./scripts/automationScripts/FabricPurviewAutomation/connect_log_analytics.ps1 - interactive: false - shell: pwsh - continueOnError: false diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index 7de2f86..6216b3a 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -28,7 +28,7 @@ To deploy this solution accelerator, ensure you have access to an [Azure subscri | Git | Latest | [Install Git](https://git-scm.com/downloads) | | PowerShell | 7.0+ | [Install PowerShell](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) | -> **Windows-specific shell requirement:** Preprovision hooks run with `shell: sh`. Install Git for Windows (includes Git Bash) **or** run `azd` from WSL/Ubuntu so `bash/sh` is on PATH. If you prefer pure PowerShell, update `azure.yaml` to point `preprovision` to the provided `preprovision.ps1`. +> **Windows shell requirement:** Preprovision runs with PowerShell (`pwsh`). Use PowerShell 7+ so `pwsh` is on PATH. ### External Resources @@ -106,7 +106,7 @@ If you're not using Codespaces or Dev Containers: 4. Continue with [Deployment Steps](#deployment-steps) below -> **Note (Windows):** Run `azd up` from Git Bash or WSL so the `preprovision` hook can execute. If you want to stay in PowerShell, edit `azure.yaml` to use `preprovision.ps1` instead of the `.sh` script. +> **Note (Windows):** Run `azd up` from PowerShell 7+ so the `pwsh` preprovision hook can execute. @@ -152,22 +152,23 @@ Edit `infra/main.bicepparam` or set environment variables: | Parameter | Description | Example | |-----------|-------------|---------| | `purviewAccountResourceId` | Resource ID of existing Purview account | `/subscriptions/.../Microsoft.Purview/accounts/...` | -| `aiSearchAdditionalAccessObjectIds` | Array of Entra object IDs to grant Search roles | `["00000000-0000-0000-0000-000000000000"]` | -| `fabricCapacityMode` | Fabric capacity mode: `create`, `byo`, or `none` | `create` | -| `fabricWorkspaceMode` | Fabric workspace mode: `create`, `byo`, or `none` | `create` | -| `fabricCapacitySku` | Fabric capacity SKU (only used when `fabricCapacityMode=create`) | `F8` (default) | -| `fabricCapacityAdmins` | Fabric capacity admin principals (UPN emails or Entra object IDs) (required when `fabricCapacityMode=create`) | `["user@contoso.com"]` | -| `fabricCapacityResourceId` | Existing Fabric capacity ARM resource ID (required when `fabricCapacityMode=byo`) | `/subscriptions/.../providers/Microsoft.Fabric/capacities/...` | -| `fabricWorkspaceId` | Existing Fabric workspace ID (GUID) (required when `fabricWorkspaceMode=byo`) | `00000000-0000-0000-0000-000000000000` | -| `fabricWorkspaceName` | Existing Fabric workspace name (used when `fabricWorkspaceMode=byo`) | `my-existing-workspace` | +| `fabricCapacityPreset` | Fabric capacity preset: `create`, `byo`, or `none` | `create` | +| `fabricWorkspacePreset` | Fabric workspace preset: `create`, `byo`, or `none` | `create` | +| `fabricCapacitySku` | Fabric capacity SKU (only used when `fabricCapacityPreset=create`) | `F8` (default) | +| `fabricCapacityAdmins` | Fabric capacity admin principals (UPN emails or Entra object IDs) (required when `fabricCapacityPreset=create`) | `["user@contoso.com"]` | +| `fabricCapacityResourceId` | Existing Fabric capacity ARM resource ID (required when `fabricCapacityPreset=byo`) | `/subscriptions/.../providers/Microsoft.Fabric/capacities/...` | +| `fabricWorkspaceId` | Existing Fabric workspace ID (GUID) (required when `fabricWorkspacePreset=byo`) | `00000000-0000-0000-0000-000000000000` | +| `fabricWorkspaceName` | Existing Fabric workspace name (used when `fabricWorkspacePreset=byo`) | `my-existing-workspace` | ```bash # Example: Set Purview account -azd env set purviewAccountResourceId "/subscriptions//resourceGroups//providers/Microsoft.Purview/accounts/" +# (Edit infra/main.bicepparam) +# param purviewAccountResourceId = "/subscriptions//resourceGroups//providers/Microsoft.Purview/accounts/" # Example: Disable all Fabric automation -azd env set fabricCapacityMode none -azd env set fabricWorkspaceMode none +# (Edit infra/main.bicepparam) +# var fabricCapacityPreset = 'none' +# var fabricWorkspacePreset = 'none' ``` @@ -177,9 +178,10 @@ azd env set fabricWorkspaceMode none | Parameter | Description | Default | |-----------|-------------|---------| -| `aiSearchAdditionalAccessObjectIds` | Entra ID object IDs for additional Search access | `[]` | -| `networkIsolationMode` | Network isolation level | `AllowInternetOutbound` | -| `vmAdminUsername` | Jump box VM admin username | `azureuser` | +| `networkIsolation` | Enable network isolation | `false` | +| `useExistingVNet` | Reuse an existing VNet | `false` | +| `existingVnetResourceId` | Existing VNet resource ID (when `useExistingVNet=true`) | `` | +| `vmUserName` | Jump box VM admin username | `` | | `vmAdminPassword` | Jump box VM admin password | (prompted) | @@ -214,8 +216,8 @@ azd up ``` This command will: -1. Run pre-provision hooks (validate environment) -2. Deploy all Azure infrastructure (~30-40 minutes) +1. Run pre-provision hooks (deploy AI Landing Zone submodule) +2. Deploy Fabric capacity and supporting infrastructure (~30-40 minutes) 3. Run post-provision hooks (configure Fabric, Purview, Search RBAC) > **Note:** The entire deployment typically takes 45-60 minutes. diff --git a/docs/PARAMETER_GUIDE.md b/docs/PARAMETER_GUIDE.md index a02131f..667ccf0 100644 --- a/docs/PARAMETER_GUIDE.md +++ b/docs/PARAMETER_GUIDE.md @@ -5,13 +5,15 @@ This guide focuses on configuration concepts for the **AI Landing Zone**. > **Important**: This repository deploys using Bicep parameter files, not `infra/main.parameters.json`. > > - Primary parameters file: `infra/main.bicepparam` -> - AI Landing Zone submodule parameters file (if you deploy it directly): `submodules/ai-landing-zone/bicep/infra/main.bicepparam` +> - AI Landing Zone submodule parameters file (if you deploy it directly): `submodules/ai-landing-zone/main.parameters.json` > > **Fabric options in this repo** are configured in `infra/main.bicepparam` via: > - `fabricCapacityPreset` (`create` | `byo` | `none`) > - `fabricWorkspacePreset` (`create` | `byo` | `none`) > - BYO inputs: `fabricCapacityResourceId`, `fabricWorkspaceId`, `fabricWorkspaceName` +> **Deployment flow**: This repo deploys the AI Landing Zone submodule from `submodules/ai-landing-zone/main.bicep` during the preprovision hook. The single source of truth for parameters is `infra/main.bicepparam`. + ## Table of Contents 1. [Basic Parameters](#basic-parameters) 2. [Deployment Toggles](#deployment-toggles) @@ -151,6 +153,14 @@ Each toggle controls whether a service is created. Set to `true` to deploy, `fal - `buildVm: true` - For CI/CD build agents - `jumpVm: true` - For Windows-based management +### Log Analytics (Optional) + +If you are using an existing Log Analytics workspace, set the resource ID in `infra/main.bicepparam`: + +```bicep-params +param logAnalyticsWorkspaceResourceId = '/subscriptions//resourceGroups//providers/Microsoft.OperationalInsights/workspaces/' +``` + ### Network Security Groups ```json diff --git a/docs/automation-outputs-mapping.md b/docs/automation-outputs-mapping.md index 9fe8a81..6bbdbb3 100644 --- a/docs/automation-outputs-mapping.md +++ b/docs/automation-outputs-mapping.md @@ -20,8 +20,8 @@ The postprovision automation scripts consume deployment outputs via the `AZURE_O | Bicep Output | Script Variable | Used By | Purpose | |-------------|-----------------|---------|---------| -| `fabricCapacityModeOut` | `fabricCapacityMode` | Multiple Fabric scripts | Whether capacity is `create`, `byo`, or `none` | -| `fabricWorkspaceModeOut` | `fabricWorkspaceMode` | Multiple Fabric scripts | Whether workspace is `create`, `byo`, or `none` | +| `fabricCapacityModeOut` | `fabricCapacityMode` | Multiple Fabric scripts | Resolved mode from `fabricCapacityPreset` (`create`, `byo`, `none`) | +| `fabricWorkspaceModeOut` | `fabricWorkspaceMode` | Multiple Fabric scripts | Resolved mode from `fabricWorkspacePreset` (`create`, `byo`, `none`) | | `fabricCapacityId` | `FABRIC_CAPACITY_ID` | `ensure_active_capacity.ps1` | ARM resource ID of Fabric capacity | | `fabricCapacityResourceIdOut` | `fabricCapacityId` | `create_fabric_workspace.ps1` | Resource ID for capacity assignment | | `fabricWorkspaceIdOut` | `FABRIC_WORKSPACE_ID` | Multiple Fabric scripts | Existing or created Fabric workspace ID | diff --git a/docs/post_deployment_steps.md b/docs/post_deployment_steps.md index 647af6a..9239d6e 100644 --- a/docs/post_deployment_steps.md +++ b/docs/post_deployment_steps.md @@ -106,7 +106,7 @@ If no documents appear, check: ## 6. Verify Network Isolation (if enabled) -When `networkIsolationMode` is set to isolate resources: +When `networkIsolation` is set to `true`: ### Check AI Foundry Network Settings diff --git a/docs/quota_check.md b/docs/quota_check.md index 46ee0e6..d809a32 100644 --- a/docs/quota_check.md +++ b/docs/quota_check.md @@ -78,8 +78,8 @@ The final table lists regions with available quota. You can select any of these ## **If using VS Code or Codespaces** 1. Open the terminal in VS Code or Codespaces. -2. If you're using VS Code, click the dropdown on the right side of the terminal window, and select `Git Bash`. - ![git_bash](../img/provisioning/git_bash.png) +2. Use a terminal that can run bash. This is only for the quota check script; deployment uses PowerShell. + ![git_bash](../img/provisioning/git_bash.png) 3. Navigate to the `scripts` folder where the script files are located and make the script as executable: ```sh cd scripts diff --git a/infra/main.bicep b/infra/main.bicep index e0cacb1..96cfd9b 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -105,6 +105,8 @@ param enableAgenticRetrieval bool = false @description('Existing resource IDs to reuse.') param aiSearchResourceId string = '' +@description('Optional additional Entra object IDs to grant Search roles.') +param aiSearchAdditionalAccessObjectIds array = [] param aiFoundryStorageAccountResourceId string = '' param aiFoundryCosmosDBAccountResourceId string = '' param keyVaultResourceId string = '' @@ -244,16 +246,14 @@ var effectiveAiSearchResourceId = !empty(aiSearchResourceId) : resourceId('Microsoft.Search/searchServices', searchServiceName) var effectiveStorageAccountResourceId = resourceId('Microsoft.Storage/storageAccounts', storageAccountName) -var effectiveLogAnalyticsWorkspaceResourceId = resourceId('Microsoft.OperationalInsights/workspaces', logAnalyticsWorkspaceName) output virtualNetworkResourceId string = effectiveVnetResourceId output keyVaultResourceId string = effectiveKeyVaultResourceId output storageAccountResourceId string = effectiveStorageAccountResourceId output aiFoundryProjectName string = aiFoundryProjectName -output logAnalyticsWorkspaceResourceId string = effectiveLogAnalyticsWorkspaceResourceId output aiSearchResourceId string = effectiveAiSearchResourceId output aiSearchName string = searchServiceName -output aiSearchAdditionalAccessObjectIds array = [] +output aiSearchAdditionalAccessObjectIds array = aiSearchAdditionalAccessObjectIds // Subnet IDs (constructed from VNet ID and subnet names) output peSubnetResourceId string = '${effectiveVnetResourceId}/subnets/${peSubnetName}' diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 0ccc044..12861a7 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -1,22 +1,43 @@ using './main.bicep' // ======================================== -// AI LANDING ZONE PARAMETERS +// REQUIRED INPUTS // ======================================== param environmentName = readEnvironmentVariable('AZURE_ENV_NAME', '') param location = readEnvironmentVariable('AZURE_LOCATION', '') param cosmosLocation = readEnvironmentVariable('AZURE_COSMOS_LOCATION', '') -// Set this to your Entra object ID if Graph lookup is blocked. +// Entra object ID of the identity to grant RBAC (user, group, service principal, or UAI). Set this if Graph lookup is blocked. param principalId = '0d60355b-dcae-4331-b55f-283d80aabde5' param principalType = 'User' -param deploymentTags = {} -param appConfigLabel = 'ai-lz' -param networkIsolation = true +// ======================================== +// OPTIONAL INPUTS (Existing Resources) +// ======================================== +// Use these to reuse existing resources instead of creating new ones. + +param aiSearchResourceId = '' +param aiFoundryStorageAccountResourceId = '' +param aiFoundryCosmosDBAccountResourceId = '' +param keyVaultResourceId = '' param useExistingVNet = false param existingVnetResourceId = readEnvironmentVariable('EXISTING_VNET_RESOURCE_ID', '') +// Optional additional Entra object IDs to grant Search roles. +param aiSearchAdditionalAccessObjectIds = ['0d60355b-dcae-4331-b55f-283d80aabde5'] + +// ======================================== +// OPTIONAL INPUTS (Configuration) +// ======================================== + +param deploymentTags = {} +param appConfigLabel = 'ai-lz' +param networkIsolation = true + +// ======================================== +// FEATURE TOGGLES +// ======================================== + param deployGroundingWithBing = false param deployAiFoundry = true param deployAiFoundrySubnet = true @@ -35,16 +56,15 @@ param deployVM = true param deploySubnets = readEnvironmentVariable('DEPLOY_SUBNETS', 'true') == 'true' param deployNsgs = true param sideBySideDeploy = readEnvironmentVariable('SIDE_BY_SIDE', 'true') == 'true' -param deploySoftware = true +param deploySoftware = false param deployApim = false param deployAfProject = true param deployAAfAgentSvc = true param enableAgenticRetrieval = readEnvironmentVariable('ENABLE_AGENTIC_RETRIEVAL', 'false') == 'true' -param aiSearchResourceId = '' -param aiFoundryStorageAccountResourceId = '' -param aiFoundryCosmosDBAccountResourceId = '' -param keyVaultResourceId = '' +// ======================================== +// ADVANCED SETTINGS (Defaults) +// ======================================== param useUAI = readEnvironmentVariable('USE_UAI', 'false') == 'true' param useCAppAPIKey = readEnvironmentVariable('USE_CAPP_API_KEY', 'false') == 'true' diff --git a/scripts/automationScripts/FabricPurviewAutomation/connect_log_analytics.ps1 b/scripts/automationScripts/FabricPurviewAutomation/connect_log_analytics.ps1 deleted file mode 100644 index d6ed33e..0000000 --- a/scripts/automationScripts/FabricPurviewAutomation/connect_log_analytics.ps1 +++ /dev/null @@ -1,90 +0,0 @@ -<# -.SYNOPSIS - Placeholder: Connect a Fabric workspace to an Azure Log Analytics workspace (if API exists). -.DESCRIPTION - This PowerShell script replicates the placeholder behavior of the original shell script. -#> - -[CmdletBinding()] -param( - [string]$FabricWorkspaceName = $env:FABRIC_WORKSPACE_NAME, - [string]$LogAnalyticsWorkspaceId = $env:LOG_ANALYTICS_WORKSPACE_ID -) - -Set-StrictMode -Version Latest - -# Import security module -$SecurityModulePath = Join-Path $PSScriptRoot "../SecurityModule.ps1" -. $SecurityModulePath -$ErrorActionPreference = 'Stop' - -function Log([string]$m){ Write-Host "[fabric-loganalytics] $m" } -function Warn([string]$m){ Write-Warning "[fabric-loganalytics] $m" } - -# Skip when Fabric workspace automation is disabled -$fabricWorkspaceMode = $env:fabricWorkspaceMode -if (-not $fabricWorkspaceMode -and $env:AZURE_OUTPUTS_JSON) { - try { - $out0 = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop - if ($out0.fabricWorkspaceModeOut -and $out0.fabricWorkspaceModeOut.value) { $fabricWorkspaceMode = $out0.fabricWorkspaceModeOut.value } - elseif ($out0.fabricWorkspaceMode -and $out0.fabricWorkspaceMode.value) { $fabricWorkspaceMode = $out0.fabricWorkspaceMode.value } - } catch { } -} -if (-not $fabricWorkspaceMode) { - try { - $azdMode = & azd env get-value fabricWorkspaceModeOut 2>$null - if ($LASTEXITCODE -eq 0 -and $azdMode) { $fabricWorkspaceMode = $azdMode.Trim() } - if (-not $fabricWorkspaceMode) { - $azdMode = & azd env get-value fabricWorkspaceMode 2>$null - if ($LASTEXITCODE -eq 0 -and $azdMode) { $fabricWorkspaceMode = $azdMode.Trim() } - } - } catch { } -} -if ($fabricWorkspaceMode -and $fabricWorkspaceMode.ToString().Trim().ToLowerInvariant() -eq 'none') { - Warn "Fabric workspace mode is 'none'; skipping Log Analytics linkage." - exit 0 -} - -if (-not $FabricWorkspaceName) { - # try .azure env - $envDir = $env:AZURE_ENV_NAME - if (-not $envDir -and (Test-Path '.azure')) { $envDir = (Get-ChildItem -Path .azure -Name -ErrorAction SilentlyContinue | Select-Object -First 1) } - if ($envDir) { - $envPath = Join-Path -Path '.azure' -ChildPath "$envDir/.env" - if (Test-Path $envPath) { - Get-Content $envPath | ForEach-Object { - if ($_ -match '^desiredFabricWorkspaceName=(?:"|")?(.+?)(?:"|")?$') { $FabricWorkspaceName = $Matches[1] } - } - } - } -} - -if (-not $FabricWorkspaceName) { Warn 'No FABRIC_WORKSPACE_NAME determined; skipping Log Analytics linkage.'; exit 0 } - -# Acquire token -try { $accessToken = Get-SecureApiToken -Resource $SecureApiResources.PowerBI -Description "Power BI" } catch { $accessToken = $null } -if (-not $accessToken) { Warn 'Cannot acquire token; skip LA linkage.'; exit 0 } - -$apiRoot = 'https://api.powerbi.com/v1.0/myorg' -$workspaceId = $env:WORKSPACE_ID -if (-not $workspaceId) { - try { - $groups = Invoke-SecureRestMethod -Uri "$apiRoot/groups?%24top=5000" -Headers $powerBIHeaders -Method Get -ErrorAction Stop - $g = $groups.value | Where-Object { $_.name -eq $FabricWorkspaceName } - if ($g) { $workspaceId = $g.id } - } catch { - Warn "Unable to resolve workspace ID for '$FabricWorkspaceName'; skipping."; # Clean up sensitive variables -Clear-SensitiveVariables -VariableNames @("accessToken", "fabricToken", "purviewToken", "powerBIToken", "storageToken") -exit 0 - } -} - -if (-not $workspaceId) { Warn "Unable to resolve workspace ID for '$FabricWorkspaceName'; skipping."; exit 0 } - -if (-not $LogAnalyticsWorkspaceId) { Warn "LOG_ANALYTICS_WORKSPACE_ID not provided; skipping."; exit 0 } - -Log "(PLACEHOLDER) Would link Fabric workspace $FabricWorkspaceName ($workspaceId) to Log Analytics workspace $LogAnalyticsWorkspaceId" -Log "No public API yet; skipping." -# Clean up sensitive variables -Clear-SensitiveVariables -VariableNames @("accessToken", "fabricToken", "purviewToken", "powerBIToken", "storageToken") -exit 0 diff --git a/scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1 b/scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1 index 00b7526..fc42a30 100644 --- a/scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1 +++ b/scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1 @@ -17,6 +17,12 @@ function Log([string]$m){ Write-Host "[purview-collection] $m" } function Warn([string]$m){ Write-Warning "[purview-collection] $m" } function Fail([string]$m){ Write-Error "[script] $m"; Clear-SensitiveVariables -VariableNames @("accessToken", "fabricToken", "purviewToken", "powerBIToken", "storageToken"); exit 1 } +if ($env:SKIP_PURVIEW_INTEGRATION -and $env:SKIP_PURVIEW_INTEGRATION.ToLowerInvariant() -eq 'true') { + Warn "SKIP_PURVIEW_INTEGRATION=true; skipping Purview collection setup." + Clear-SensitiveVariables -VariableNames @("accessToken", "fabricToken", "purviewToken", "powerBIToken", "storageToken") + exit 0 +} + # Skip when Fabric workspace automation is disabled $fabricWorkspaceMode = $env:fabricWorkspaceMode if (-not $fabricWorkspaceMode) { $fabricWorkspaceMode = $env:fabricWorkspaceModeOut } diff --git a/scripts/automationScripts/FabricPurviewAutomation/trigger_purview_scan_for_fabric_workspace.ps1 b/scripts/automationScripts/FabricPurviewAutomation/trigger_purview_scan_for_fabric_workspace.ps1 index 989f1c4..2979857 100644 --- a/scripts/automationScripts/FabricPurviewAutomation/trigger_purview_scan_for_fabric_workspace.ps1 +++ b/scripts/automationScripts/FabricPurviewAutomation/trigger_purview_scan_for_fabric_workspace.ps1 @@ -26,6 +26,12 @@ function Log([string]$m){ Write-Host "[purview-scan] $m" } function Warn([string]$m){ Write-Warning "[purview-scan] $m" } function Fail([string]$m){ Write-Error "[script] $m"; Clear-SensitiveVariables -VariableNames @("accessToken", "fabricToken", "purviewToken", "powerBIToken", "storageToken"); exit 1 } +if ($env:SKIP_PURVIEW_INTEGRATION -and $env:SKIP_PURVIEW_INTEGRATION.ToLowerInvariant() -eq 'true') { + Warn "SKIP_PURVIEW_INTEGRATION=true; skipping Purview scan trigger." + Clear-SensitiveVariables -VariableNames @("accessToken", "fabricToken", "purviewToken", "powerBIToken", "storageToken") + exit 0 +} + # Skip when Fabric workspace automation is disabled $fabricWorkspaceMode = $env:fabricWorkspaceMode if (-not $fabricWorkspaceMode) { $fabricWorkspaceMode = $env:fabricWorkspaceModeOut } @@ -231,34 +237,160 @@ if ($collectionId) { $bodyJson = $payload | ConvertTo-Json -Depth 10 -# Create or update scan -$createUrl = "$endpoint/scan/datasources/$datasourceName/scans/${scanName}?api-version=2022-07-01-preview" -try { - $resp = Invoke-SecureWebRequest -Uri $createUrl -Method Put -Headers (New-SecureHeaders -Token $purviewToken -AdditionalHeaders @{'Content-Type' = 'application/json'}) -Body $bodyJson -ErrorAction Stop - $code = $resp.StatusCode - $respBody = $resp.Content -} catch [System.Net.WebException] { - $resp = $_.Exception.Response - if ($resp) { - $reader = New-Object System.IO.StreamReader($resp.GetResponseStream()) - $respBody = $reader.ReadToEnd() +function Invoke-PurviewWebRequest { + param( + [string]$Uri, + [string]$Method, + [hashtable]$Headers, + [string]$Body + ) + + try { + $resp = Invoke-WebRequest -Uri $Uri -Method $Method -Headers $Headers -Body $Body -ErrorAction Stop + return [PSCustomObject]@{ + StatusCode = $resp.StatusCode + Content = $resp.Content + } + } catch { + $resp = $null + try { $resp = $_.Exception.Response } catch { $resp = $null } + if (-not $resp -and $_.Exception.InnerException) { + try { $resp = $_.Exception.InnerException.Response } catch { $resp = $null } + } + if ($resp) { + $content = $null + try { + $reader = New-Object System.IO.StreamReader($resp.GetResponseStream()) + $content = $reader.ReadToEnd() + } catch { $content = $null } + $status = $null + try { $status = $resp.StatusCode } catch { $status = $null } + return [PSCustomObject]@{ + StatusCode = $status + Content = $content + } + } + + throw + } +} + +# Create or update scan with retries +$createUrl = "$endpoint/scan/datasources/${datasourceName}/scans/${scanName}?api-version=2022-07-01-preview" +$maxCreateAttempts = 10 +if ($env:PURVIEW_SCAN_CREATE_MAX_RETRIES) { + [int]::TryParse($env:PURVIEW_SCAN_CREATE_MAX_RETRIES, [ref]$maxCreateAttempts) | Out-Null +} +$createDelaySeconds = 20 +if ($env:PURVIEW_SCAN_CREATE_DELAY_SECONDS) { + [int]::TryParse($env:PURVIEW_SCAN_CREATE_DELAY_SECONDS, [ref]$createDelaySeconds) | Out-Null +} + +$scanExists = $false +$createSucceeded = $false +$lastCreateStatus = $null +$lastCreateBody = $null +for ($attempt = 1; $attempt -le $maxCreateAttempts; $attempt++) { + $code = $null + $respBody = $null + try { + $headers = New-SecureHeaders -Token $purviewToken -AdditionalHeaders @{'Content-Type' = 'application/json'} + $resp = Invoke-PurviewWebRequest -Uri $createUrl -Method Put -Headers $headers -Body $bodyJson $code = $resp.StatusCode - } else { - Fail "Scan create/update failed: $_" + $respBody = $resp.Content + $lastCreateStatus = $code + $lastCreateBody = $respBody + } catch { + Warn "Scan create/update failed (attempt $attempt of $maxCreateAttempts): $($_.Exception.Message)" + } + + if ($code -ge 200 -and $code -lt 300) { + Log "Scan definition created/updated (HTTP $code)" + $createSucceeded = $true + break + } + + Warn "Scan create/update failed (HTTP $code): $respBody" + if ($collectionId) { + try { + Warn "Retrying scan create/update without collection assignment..." + $payloadNoCollection = $payload.PSObject.Copy() + if ($payloadNoCollection.properties -and $payloadNoCollection.properties.PSObject.Properties.Name -contains 'collection') { + $payloadNoCollection.properties.PSObject.Properties.Remove('collection') + } + $bodyJsonNoCollection = $payloadNoCollection | ConvertTo-Json -Depth 10 + $headers = New-SecureHeaders -Token $purviewToken -AdditionalHeaders @{'Content-Type' = 'application/json'} + $retryResp = Invoke-PurviewWebRequest -Uri $createUrl -Method Put -Headers $headers -Body $bodyJsonNoCollection + $lastCreateStatus = $retryResp.StatusCode + $lastCreateBody = $retryResp.Content + if ($retryResp.StatusCode -ge 200 -and $retryResp.StatusCode -lt 300) { + $createSucceeded = $true + Log "Scan definition created/updated without collection (HTTP $($retryResp.StatusCode))" + break + } + } catch { + Warn "Retry without collection failed: $($_.Exception.Message)" + } + } + + try { + $getUrl = "$endpoint/scan/datasources/${datasourceName}/scans/${scanName}?api-version=2022-07-01-preview" + $getHeaders = New-SecureHeaders -Token $purviewToken -AdditionalHeaders @{'Content-Type' = 'application/json'} + $getResp = Invoke-PurviewWebRequest -Uri $getUrl -Method Get -Headers $getHeaders -Body $null + if ($getResp.StatusCode -ge 200 -and $getResp.StatusCode -lt 300) { + $scanExists = $true + Log "Existing scan definition found. Continuing with scan run." + break + } + } catch { + Warn "Unable to retrieve existing scan definition: $($_.Exception.Message)" + } + + if ($attempt -lt $maxCreateAttempts) { + Write-Warning "Scan definition not ready. Waiting ${createDelaySeconds}s before retry..." + Start-Sleep -Seconds $createDelaySeconds } } -if ($code -ge 200 -and $code -lt 300) { Log "Scan definition created/updated (HTTP $code)" } else { Warn "Scan create/update failed (HTTP $code): $respBody"; Fail "Could not create/update scan" } +if (-not $createSucceeded -and -not $scanExists) { + if ($lastCreateStatus -or $lastCreateBody) { + Warn "Final scan create/update response (HTTP $lastCreateStatus): $lastCreateBody" + } + Fail "Could not create or retrieve scan definition after $maxCreateAttempts attempts." +} -# Trigger a run -$runUrl = "$endpoint/scan/datasources/$datasourceName/scans/$scanName/run?api-version=2022-07-01-preview" -try { - $runResp = Invoke-SecureWebRequest -Uri $runUrl -Method Post -Headers (New-SecureHeaders -Token $purviewToken -AdditionalHeaders @{'Content-Type' = 'application/json'}) -Body '{}' -ErrorAction Stop - $runBody = $runResp.Content - $runCode = $runResp.StatusCode -} catch [System.Net.WebException] { - $resp = $_.Exception.Response - if ($resp) { $reader = New-Object System.IO.StreamReader($resp.GetResponseStream()); $runBody = $reader.ReadToEnd(); $runCode = $resp.StatusCode } else { Fail "Scan run request failed: $_" } +# Trigger a run with retries +$runUrl = "$endpoint/scan/datasources/${datasourceName}/scans/${scanName}/run?api-version=2022-07-01-preview" +$maxRunAttempts = 3 +if ($env:PURVIEW_SCAN_RUN_MAX_RETRIES) { + [int]::TryParse($env:PURVIEW_SCAN_RUN_MAX_RETRIES, [ref]$maxRunAttempts) | Out-Null +} +$runDelaySeconds = 15 +if ($env:PURVIEW_SCAN_RUN_DELAY_SECONDS) { + [int]::TryParse($env:PURVIEW_SCAN_RUN_DELAY_SECONDS, [ref]$runDelaySeconds) | Out-Null +} + +$runCode = $null +$runBody = $null +for ($attempt = 1; $attempt -le $maxRunAttempts; $attempt++) { + try { + $runHeaders = New-SecureHeaders -Token $purviewToken -AdditionalHeaders @{'Content-Type' = 'application/json'} + $runResp = Invoke-PurviewWebRequest -Uri $runUrl -Method Post -Headers $runHeaders -Body '{}' + $runBody = $runResp.Content + $runCode = $runResp.StatusCode + } catch { + Warn "Scan run request failed (attempt $attempt of $maxRunAttempts): $($_.Exception.Message)" + } + + if ($runCode -eq 200 -or $runCode -eq 202) { break } + if ($attempt -lt $maxRunAttempts) { + Write-Warning "Scan run not accepted yet (HTTP $runCode). Waiting ${runDelaySeconds}s before retry..." + Start-Sleep -Seconds $runDelaySeconds + } +} + +if (-not ($runCode -eq 200 -or $runCode -eq 202)) { + Fail "Scan run request failed after $maxRunAttempts attempts (HTTP $runCode)" } if ($runCode -ne 200 -and $runCode -ne 202) { diff --git a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1 b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1 index 7bb1c4a..9156436 100644 --- a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1 +++ b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1 @@ -17,6 +17,12 @@ function Log([string]$m){ Write-Host "[register-datasource] $m" } function Warn([string]$m){ Write-Warning "[register-datasource] $m" } function Fail([string]$m){ Write-Error "[register-datasource] $m"; Clear-SensitiveVariables -VariableNames @('purviewToken'); exit 1 } +if ($env:SKIP_PURVIEW_INTEGRATION -and $env:SKIP_PURVIEW_INTEGRATION.ToLowerInvariant() -eq 'true') { + Warn "SKIP_PURVIEW_INTEGRATION=true; skipping Purview datasource registration." + Clear-SensitiveVariables -VariableNames @('purviewToken') + exit 0 +} + # Skip when Fabric workspace automation is disabled $fabricWorkspaceMode = $env:fabricWorkspaceMode if (-not $fabricWorkspaceMode) { $fabricWorkspaceMode = $env:fabricWorkspaceModeOut } diff --git a/scripts/automationScripts/OneLakeIndex/01_setup_rbac.ps1 b/scripts/automationScripts/OneLakeIndex/01_setup_rbac.ps1 index 3887ce2..535d01f 100644 --- a/scripts/automationScripts/OneLakeIndex/01_setup_rbac.ps1 +++ b/scripts/automationScripts/OneLakeIndex/01_setup_rbac.ps1 @@ -44,6 +44,19 @@ Log "==================================================================" try { Log "Checking for AI Search deployment outputs..." + $aiSearchName = '' + $aiSearchResourceGroup = '' + $aiSearchSubscriptionId = '' + $aiFoundryName = '' + $aiFoundryResourceGroup = '' + $fabricWorkspaceName = '' + $aiSearchResourceId = '' + + $outputs = $null + if ($env:AZURE_OUTPUTS_JSON) { + try { $outputs = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop } catch { $outputs = $null } + } + # Get azd environment values $azdEnvValues = azd env get-values 2>$null if (-not $azdEnvValues) { @@ -61,13 +74,19 @@ try { } # Extract required values - $aiSearchName = $env_vars['aiSearchName'] + if (-not $aiSearchName -and $outputs -and $outputs.aiSearchName -and $outputs.aiSearchName.value) { $aiSearchName = $outputs.aiSearchName.value } + if (-not $aiSearchName) { $aiSearchName = $env_vars['aiSearchName'] } if (-not $aiSearchName) { $aiSearchName = $env_vars['AZURE_AI_SEARCH_NAME'] } - $aiSearchResourceGroup = $env_vars['aiSearchResourceGroup'] - $aiSearchSubscriptionId = $env_vars['aiSearchSubscriptionId'] - $aiFoundryName = $env_vars['aiFoundryName'] - $fabricWorkspaceName = $env_vars['desiredFabricWorkspaceName'] - $aiSearchResourceId = $env_vars['aiSearchResourceId'] + if (-not $aiSearchResourceGroup -and $outputs -and $outputs.aiSearchResourceGroup -and $outputs.aiSearchResourceGroup.value) { $aiSearchResourceGroup = $outputs.aiSearchResourceGroup.value } + if (-not $aiSearchResourceGroup) { $aiSearchResourceGroup = $env_vars['aiSearchResourceGroup'] } + if (-not $aiSearchSubscriptionId -and $outputs -and $outputs.aiSearchSubscriptionId -and $outputs.aiSearchSubscriptionId.value) { $aiSearchSubscriptionId = $outputs.aiSearchSubscriptionId.value } + if (-not $aiSearchSubscriptionId) { $aiSearchSubscriptionId = $env_vars['aiSearchSubscriptionId'] } + if (-not $aiFoundryName -and $outputs -and $outputs.aiFoundryName -and $outputs.aiFoundryName.value) { $aiFoundryName = $outputs.aiFoundryName.value } + if (-not $aiFoundryName) { $aiFoundryName = $env_vars['aiFoundryName'] } + if (-not $fabricWorkspaceName -and $outputs -and $outputs.desiredFabricWorkspaceName -and $outputs.desiredFabricWorkspaceName.value) { $fabricWorkspaceName = $outputs.desiredFabricWorkspaceName.value } + if (-not $fabricWorkspaceName) { $fabricWorkspaceName = $env_vars['desiredFabricWorkspaceName'] } + if (-not $aiSearchResourceId -and $outputs -and $outputs.aiSearchResourceId -and $outputs.aiSearchResourceId.value) { $aiSearchResourceId = $outputs.aiSearchResourceId.value } + if (-not $aiSearchResourceId) { $aiSearchResourceId = $env_vars['aiSearchResourceId'] } if (-not $aiSearchResourceGroup -and $aiSearchResourceId -and $aiSearchResourceId -match '/resourceGroups/([^/]+)/') { $aiSearchResourceGroup = $matches[1] diff --git a/scripts/automationScripts/OneLakeIndex/02_create_onelake_skillsets.ps1 b/scripts/automationScripts/OneLakeIndex/02_create_onelake_skillsets.ps1 index bb9a43b..a6bf208 100644 --- a/scripts/automationScripts/OneLakeIndex/02_create_onelake_skillsets.ps1 +++ b/scripts/automationScripts/OneLakeIndex/02_create_onelake_skillsets.ps1 @@ -28,12 +28,20 @@ if ($fabricWorkspaceMode -and $fabricWorkspaceMode.ToString().Trim().ToLowerInva exit 0 } +$outputs = $null +if ($env:AZURE_OUTPUTS_JSON) { + try { $outputs = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop } catch { $outputs = $null } +} + # Resolve parameters from environment +if (-not $aiSearchName -and $outputs -and $outputs.aiSearchName -and $outputs.aiSearchName.value) { $aiSearchName = $outputs.aiSearchName.value } if (-not $aiSearchName) { $aiSearchName = $env:aiSearchName } if (-not $aiSearchName) { $aiSearchName = $env:AZURE_AI_SEARCH_NAME } +if (-not $resourceGroup -and $outputs -and $outputs.aiSearchResourceGroup -and $outputs.aiSearchResourceGroup.value) { $resourceGroup = $outputs.aiSearchResourceGroup.value } if (-not $resourceGroup) { $resourceGroup = $env:aiSearchResourceGroup } if (-not $resourceGroup) { $resourceGroup = $env:AZURE_RESOURCE_GROUP_NAME } if (-not $resourceGroup) { $resourceGroup = $env:AZURE_RESOURCE_GROUP } +if (-not $subscription -and $outputs -and $outputs.aiSearchSubscriptionId -and $outputs.aiSearchSubscriptionId.value) { $subscription = $outputs.aiSearchSubscriptionId.value } if (-not $subscription) { $subscription = $env:aiSearchSubscriptionId } if (-not $subscription) { $subscription = $env:AZURE_SUBSCRIPTION_ID } @@ -45,25 +53,220 @@ if (-not $aiSearchName -or -not $resourceGroup -or -not $subscription) { exit 1 } -# Acquire Entra ID access token for Azure AI Search data plane -try { - $accessToken = az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv -} catch { - $accessToken = $null +function Get-SearchPublicNetworkAccess { + try { + return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "publicNetworkAccess" -o tsv + } catch { + return $null + } } -if (-not $accessToken) { - Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" - exit 1 +function Get-SearchResourceId { + try { + return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "id" -o tsv + } catch { + return $null + } +} + +function Get-ArmAccessToken { + try { + return az account get-access-token --resource https://management.azure.com/ --subscription $subscription --query accessToken -o tsv + } catch { + return $null + } +} + +function Invoke-AzCliWithTimeout { + param( + [string[]]$Args, + [int]$TimeoutSeconds = 120 + ) + + $escapedArgs = $Args | ForEach-Object { + if ($_ -match '\s|"') { '"' + ($_ -replace '"', '\"') + '"' } else { $_ } + } + + $azPath = (Get-Command az -ErrorAction SilentlyContinue).Source + if (-not $azPath) { + throw "Azure CLI (az) not found on PATH." + } + + $psi = New-Object System.Diagnostics.ProcessStartInfo + $psi.FileName = $azPath + $psi.Arguments = ($escapedArgs -join ' ') + $psi.RedirectStandardOutput = $true + $psi.RedirectStandardError = $true + $psi.UseShellExecute = $false + $psi.CreateNoWindow = $true + + $process = [System.Diagnostics.Process]::Start($psi) + if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { + try { $process.Kill() } catch { } + throw "Azure CLI command timed out after $TimeoutSeconds seconds: az $($psi.Arguments)" + } + + $stdout = $process.StandardOutput.ReadToEnd() + $stderr = $process.StandardError.ReadToEnd() + + if ($process.ExitCode -ne 0) { + throw "Azure CLI failed with exit code $($process.ExitCode): $stderr" + } + + return $stdout +} + +function Set-SearchPublicNetworkAccess { + param([string]$Mode) + + $timeoutSeconds = 120 + if ($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS, [ref]$timeoutSeconds) | Out-Null + } + + $maxRetries = 3 + if ($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES, [ref]$maxRetries) | Out-Null + } + + $waitSeconds = 120 + if ($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS, [ref]$waitSeconds) | Out-Null + } + + for ($attempt = 1; $attempt -le $maxRetries; $attempt++) { + try { + $resourceId = Get-SearchResourceId + if (-not $resourceId) { + throw "Unable to resolve AI Search resource ID." + } + $armToken = Get-ArmAccessToken + if (-not $armToken) { + throw "Unable to acquire ARM access token." + } + $body = @{ properties = @{ publicNetworkAccess = $Mode } } | ConvertTo-Json -Compress + $url = "https://management.azure.com${resourceId}?api-version=2023-11-01" + $headers = @{ Authorization = "Bearer $armToken"; 'Content-Type' = 'application/json' } + Invoke-RestMethod -Method Patch -Uri $url -Headers $headers -Body $body | Out-Null + $deadline = (Get-Date).AddSeconds($waitSeconds) + do { + $current = Get-SearchPublicNetworkAccess + if ($current -eq $Mode) { return } + Start-Sleep -Seconds 5 + } while ((Get-Date) -lt $deadline) + + throw "Timed out waiting for AI Search public network access to become '$Mode'." + } catch { + if ($attempt -lt $maxRetries) { + Write-Warning "Failed to set AI Search public network access (attempt $attempt of $maxRetries): $($_.Exception.Message)" + Start-Sleep -Seconds 10 + continue + } + throw + } + } +} + +function Ensure-SearchPublicAccess { + if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { + Write-Host "Skipping temporary public network access toggle for AI Search." + return $null + } + + $current = Get-SearchPublicNetworkAccess + if (-not $current) { return $null } + + if ($current -eq 'Disabled') { + Write-Warning "AI Search public network access is Disabled. Enabling temporarily for OneLake setup." + Set-SearchPublicNetworkAccess -Mode 'Enabled' + } + + return $current } -$headers = @{ - 'Authorization' = "Bearer $accessToken" - 'Content-Type' = 'application/json' +function Restore-SearchPublicAccess { + param([string]$OriginalAccess) + + if (-not $OriginalAccess) { return } + if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { return } + + $current = Get-SearchPublicNetworkAccess + if ($current -and $current -ne $OriginalAccess) { + Write-Host "Restoring AI Search public network access to '$OriginalAccess'." + try { + Set-SearchPublicNetworkAccess -Mode $OriginalAccess + } catch { + Write-Warning "Failed to restore AI Search public network access: $($_.Exception.Message)" + } + } } -# Use preview API version required for OneLake -$apiVersion = '2024-05-01-preview' +function Get-SearchAccessToken { + try { + return az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv + } catch { + return $null + } +} + +function New-SearchHeaders { + param( + [string]$AccessToken + ) + + if ($AccessToken) { + return @{ + 'Authorization' = "Bearer $AccessToken" + 'Content-Type' = 'application/json' + } + } + + return $null +} + +function Invoke-SearchRequest { + param( + [string]$Method, + [string]$Uri, + [string]$Body + ) + + $maxAttempts = 6 + $delaySeconds = 30 + + for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { + $accessToken = Get-SearchAccessToken + $headers = New-SearchHeaders -AccessToken $accessToken + + if (-not $headers) { + Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" + exit 1 + } + + try { + if ($Body) { + return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method -Body $Body + } + return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method + } catch { + $statusCode = $null + try { $statusCode = $_.Exception.Response.StatusCode.value__ } catch { } + + if (($statusCode -eq 401 -or $statusCode -eq 403) -and $attempt -lt $maxAttempts) { + Write-Warning "Search request denied (HTTP $statusCode). Waiting ${delaySeconds}s for RBAC propagation (attempt $attempt of $maxAttempts)." + Start-Sleep -Seconds $delaySeconds + continue + } + + throw + } + } +} + +$originalPublicAccess = Ensure-SearchPublicAccess +try { + # Use preview API version required for OneLake + $apiVersion = '2024-05-01-preview' # Create text-only skillset for OneLake documents Write-Host "Creating onelake-textonly-skillset..." @@ -101,7 +304,7 @@ $skillsetBody = @{ # Delete existing skillset if present try { $deleteUrl = "https://$aiSearchName.search.windows.net/skillsets/onelake-textonly-skillset?api-version=$apiVersion" - Invoke-RestMethod -Uri $deleteUrl -Headers $headers -Method DELETE + Invoke-SearchRequest -Method 'DELETE' -Uri $deleteUrl Write-Host "Deleted existing skillset" } catch { Write-Host "No existing skillset to delete" @@ -111,12 +314,15 @@ try { $createUrl = "https://$aiSearchName.search.windows.net/skillsets?api-version=$apiVersion" try { - $response = Invoke-RestMethod -Uri $createUrl -Headers $headers -Method POST -Body $skillsetBody + $response = Invoke-SearchRequest -Method 'POST' -Uri $createUrl -Body $skillsetBody Write-Host "✅ Successfully created skillset: $($response.name)" } catch { Write-Error "Failed to create skillset: $($_.Exception.Message)" exit 1 } -Write-Host "" -Write-Host "OneLake skillsets created successfully!" + Write-Host "" + Write-Host "OneLake skillsets created successfully!" +} finally { + Restore-SearchPublicAccess -OriginalAccess $originalPublicAccess +} diff --git a/scripts/automationScripts/OneLakeIndex/03_create_onelake_index.ps1 b/scripts/automationScripts/OneLakeIndex/03_create_onelake_index.ps1 index ee5038d..ed4cf43 100644 --- a/scripts/automationScripts/OneLakeIndex/03_create_onelake_index.ps1 +++ b/scripts/automationScripts/OneLakeIndex/03_create_onelake_index.ps1 @@ -31,6 +31,11 @@ if ($fabricWorkspaceMode -and $fabricWorkspaceMode.ToString().Trim().ToLowerInva exit 0 } +$outputs = $null +if ($env:AZURE_OUTPUTS_JSON) { + try { $outputs = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop } catch { $outputs = $null } +} + # Import security module . "$PSScriptRoot/../SecurityModule.ps1" @@ -66,11 +71,14 @@ if ($indexName -eq 'onelake-documents-index') { } # Resolve parameters from environment +if (-not $aiSearchName -and $outputs -and $outputs.aiSearchName -and $outputs.aiSearchName.value) { $aiSearchName = $outputs.aiSearchName.value } if (-not $aiSearchName) { $aiSearchName = $env:aiSearchName } if (-not $aiSearchName) { $aiSearchName = $env:AZURE_AI_SEARCH_NAME } +if (-not $resourceGroup -and $outputs -and $outputs.aiSearchResourceGroup -and $outputs.aiSearchResourceGroup.value) { $resourceGroup = $outputs.aiSearchResourceGroup.value } if (-not $resourceGroup) { $resourceGroup = $env:aiSearchResourceGroup } if (-not $resourceGroup) { $resourceGroup = $env:AZURE_RESOURCE_GROUP_NAME } if (-not $resourceGroup) { $resourceGroup = $env:AZURE_RESOURCE_GROUP } +if (-not $subscription -and $outputs -and $outputs.aiSearchSubscriptionId -and $outputs.aiSearchSubscriptionId.value) { $subscription = $outputs.aiSearchSubscriptionId.value } if (-not $subscription) { $subscription = $env:aiSearchSubscriptionId } if (-not $subscription) { $subscription = $env:AZURE_SUBSCRIPTION_ID } @@ -87,25 +95,220 @@ if ($workspaceName) { Write-Host "Derived Fabric Workspace Name: $workspaceName" if ($domainName) { Write-Host "Derived Fabric Domain Name: $domainName" } Write-Host "" -# Acquire Entra ID access token for Azure AI Search data plane -try { - $accessToken = az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv -} catch { - $accessToken = $null +function Get-SearchPublicNetworkAccess { + try { + return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "publicNetworkAccess" -o tsv + } catch { + return $null + } } -if (-not $accessToken) { - Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" - exit 1 +function Get-SearchResourceId { + try { + return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "id" -o tsv + } catch { + return $null + } +} + +function Get-ArmAccessToken { + try { + return az account get-access-token --resource https://management.azure.com/ --subscription $subscription --query accessToken -o tsv + } catch { + return $null + } +} + +function Invoke-AzCliWithTimeout { + param( + [string[]]$Args, + [int]$TimeoutSeconds = 120 + ) + + $escapedArgs = $Args | ForEach-Object { + if ($_ -match '\s|"') { '"' + ($_ -replace '"', '\"') + '"' } else { $_ } + } + + $azPath = (Get-Command az -ErrorAction SilentlyContinue).Source + if (-not $azPath) { + throw "Azure CLI (az) not found on PATH." + } + + $psi = New-Object System.Diagnostics.ProcessStartInfo + $psi.FileName = $azPath + $psi.Arguments = ($escapedArgs -join ' ') + $psi.RedirectStandardOutput = $true + $psi.RedirectStandardError = $true + $psi.UseShellExecute = $false + $psi.CreateNoWindow = $true + + $process = [System.Diagnostics.Process]::Start($psi) + if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { + try { $process.Kill() } catch { } + throw "Azure CLI command timed out after $TimeoutSeconds seconds: az $($psi.Arguments)" + } + + $stdout = $process.StandardOutput.ReadToEnd() + $stderr = $process.StandardError.ReadToEnd() + + if ($process.ExitCode -ne 0) { + throw "Azure CLI failed with exit code $($process.ExitCode): $stderr" + } + + return $stdout } -$headers = @{ - 'Authorization' = "Bearer $accessToken" - 'Content-Type' = 'application/json' +function Set-SearchPublicNetworkAccess { + param([string]$Mode) + + $timeoutSeconds = 120 + if ($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS, [ref]$timeoutSeconds) | Out-Null + } + + $maxRetries = 3 + if ($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES, [ref]$maxRetries) | Out-Null + } + + $waitSeconds = 120 + if ($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS, [ref]$waitSeconds) | Out-Null + } + + for ($attempt = 1; $attempt -le $maxRetries; $attempt++) { + try { + $resourceId = Get-SearchResourceId + if (-not $resourceId) { + throw "Unable to resolve AI Search resource ID." + } + $armToken = Get-ArmAccessToken + if (-not $armToken) { + throw "Unable to acquire ARM access token." + } + $body = @{ properties = @{ publicNetworkAccess = $Mode } } | ConvertTo-Json -Compress + $url = "https://management.azure.com${resourceId}?api-version=2023-11-01" + $headers = @{ Authorization = "Bearer $armToken"; 'Content-Type' = 'application/json' } + Invoke-RestMethod -Method Patch -Uri $url -Headers $headers -Body $body | Out-Null + $deadline = (Get-Date).AddSeconds($waitSeconds) + do { + $current = Get-SearchPublicNetworkAccess + if ($current -eq $Mode) { return } + Start-Sleep -Seconds 5 + } while ((Get-Date) -lt $deadline) + + throw "Timed out waiting for AI Search public network access to become '$Mode'." + } catch { + if ($attempt -lt $maxRetries) { + Write-Warning "Failed to set AI Search public network access (attempt $attempt of $maxRetries): $($_.Exception.Message)" + Start-Sleep -Seconds 10 + continue + } + throw + } + } } -# Use preview API version required for OneLake -$apiVersion = '2024-05-01-preview' +function Ensure-SearchPublicAccess { + if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { + Write-Host "Skipping temporary public network access toggle for AI Search." + return $null + } + + $current = Get-SearchPublicNetworkAccess + if (-not $current) { return $null } + + if ($current -eq 'Disabled') { + Write-Warning "AI Search public network access is Disabled. Enabling temporarily for OneLake setup." + Set-SearchPublicNetworkAccess -Mode 'Enabled' + } + + return $current +} + +function Restore-SearchPublicAccess { + param([string]$OriginalAccess) + + if (-not $OriginalAccess) { return } + if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { return } + + $current = Get-SearchPublicNetworkAccess + if ($current -and $current -ne $OriginalAccess) { + Write-Host "Restoring AI Search public network access to '$OriginalAccess'." + try { + Set-SearchPublicNetworkAccess -Mode $OriginalAccess + } catch { + Write-Warning "Failed to restore AI Search public network access: $($_.Exception.Message)" + } + } +} + +function Get-SearchAccessToken { + try { + return az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv + } catch { + return $null + } +} + +function New-SearchHeaders { + param( + [string]$AccessToken + ) + + if ($AccessToken) { + return @{ + 'Authorization' = "Bearer $AccessToken" + 'Content-Type' = 'application/json' + } + } + + return $null +} + +function Invoke-SearchRequest { + param( + [string]$Method, + [string]$Uri, + [string]$Body + ) + + $maxAttempts = 6 + $delaySeconds = 30 + + for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { + $accessToken = Get-SearchAccessToken + $headers = New-SearchHeaders -AccessToken $accessToken + + if (-not $headers) { + Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" + exit 1 + } + + try { + if ($Body) { + return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method -Body $Body + } + return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method + } catch { + $statusCode = $null + try { $statusCode = $_.Exception.Response.StatusCode.value__ } catch { } + + if (($statusCode -eq 401 -or $statusCode -eq 403) -and $attempt -lt $maxAttempts) { + Write-Warning "Search request denied (HTTP $statusCode). Waiting ${delaySeconds}s for RBAC propagation (attempt $attempt of $maxAttempts)." + Start-Sleep -Seconds $delaySeconds + continue + } + + throw + } + } +} + +$originalPublicAccess = Ensure-SearchPublicAccess +try { + # Use preview API version required for OneLake + $apiVersion = '2024-05-01-preview' # Create index with exact schema from working test Write-Host "Creating OneLake index: $indexName" @@ -214,10 +417,10 @@ $indexBody = @{ # First, check if index exists and delete it if it does $existingIndexUri = "https://$aiSearchName.search.windows.net/indexes/$indexName" + "?api-version=$apiVersion" try { - $existingIndex = Invoke-SecureRestMethod -Uri $existingIndexUri -Headers $headers -Method GET -ErrorAction SilentlyContinue + $existingIndex = Invoke-SearchRequest -Method 'GET' -Uri $existingIndexUri if ($existingIndex) { Write-Host "Deleting existing index to recreate with correct schema..." - Invoke-SecureRestMethod -Uri $existingIndexUri -Headers $headers -Method DELETE + Invoke-SearchRequest -Method 'DELETE' -Uri $existingIndexUri Write-Host "Existing index deleted." } } catch { @@ -228,7 +431,7 @@ try { # Create the index $createIndexUri = "https://$aiSearchName.search.windows.net/indexes" + "?api-version=$apiVersion" try { - $response = Invoke-SecureRestMethod -Uri $createIndexUri -Headers $headers -Body $indexBody -Method POST + $response = Invoke-SearchRequest -Method 'POST' -Uri $createIndexUri -Body $indexBody Write-Host "" Write-Host "OneLake index created successfully!" Write-Host "Index Name: $($response.name)" @@ -244,3 +447,6 @@ try { Write-Host "" Write-Host "✅ OneLake index setup complete!" +} finally { + Restore-SearchPublicAccess -OriginalAccess $originalPublicAccess +} diff --git a/scripts/automationScripts/OneLakeIndex/04_create_onelake_datasource.ps1 b/scripts/automationScripts/OneLakeIndex/04_create_onelake_datasource.ps1 index eec7160..5cff914 100644 --- a/scripts/automationScripts/OneLakeIndex/04_create_onelake_datasource.ps1 +++ b/scripts/automationScripts/OneLakeIndex/04_create_onelake_datasource.ps1 @@ -36,6 +36,11 @@ if ($fabricWorkspaceMode -and $fabricWorkspaceMode.ToString().Trim().ToLowerInva exit 0 } +$outputs = $null +if ($env:AZURE_OUTPUTS_JSON) { + try { $outputs = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop } catch { $outputs = $null } +} + # Import security module . "$PSScriptRoot/../SecurityModule.ps1" @@ -66,11 +71,14 @@ if ($dataSourceName -eq 'onelake-reports-datasource' -and $workspaceName) { } # Resolve parameters from environment +if (-not $aiSearchName -and $outputs -and $outputs.aiSearchName -and $outputs.aiSearchName.value) { $aiSearchName = $outputs.aiSearchName.value } if (-not $aiSearchName) { $aiSearchName = $env:aiSearchName } if (-not $aiSearchName) { $aiSearchName = $env:AZURE_AI_SEARCH_NAME } +if (-not $resourceGroup -and $outputs -and $outputs.aiSearchResourceGroup -and $outputs.aiSearchResourceGroup.value) { $resourceGroup = $outputs.aiSearchResourceGroup.value } if (-not $resourceGroup) { $resourceGroup = $env:aiSearchResourceGroup } if (-not $resourceGroup) { $resourceGroup = $env:AZURE_RESOURCE_GROUP_NAME } if (-not $resourceGroup) { $resourceGroup = $env:AZURE_RESOURCE_GROUP } +if (-not $subscription -and $outputs -and $outputs.aiSearchSubscriptionId -and $outputs.aiSearchSubscriptionId.value) { $subscription = $outputs.aiSearchSubscriptionId.value } if (-not $subscription) { $subscription = $env:aiSearchSubscriptionId } if (-not $subscription) { $subscription = $env:AZURE_SUBSCRIPTION_ID } @@ -114,25 +122,220 @@ Write-Host "Lakehouse ID: $lakehouseId" Write-Host "Query Path: $queryPath" Write-Host "" -# Acquire Entra ID access token for Azure AI Search data plane -try { - $accessToken = az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv -} catch { - $accessToken = $null +function Get-SearchPublicNetworkAccess { + try { + return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "publicNetworkAccess" -o tsv + } catch { + return $null + } } -if (-not $accessToken) { - Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" - exit 1 +function Get-SearchResourceId { + try { + return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "id" -o tsv + } catch { + return $null + } } -$headers = @{ - 'Authorization' = "Bearer $accessToken" - 'Content-Type' = 'application/json' +function Get-ArmAccessToken { + try { + return az account get-access-token --resource https://management.azure.com/ --subscription $subscription --query accessToken -o tsv + } catch { + return $null + } } -# Use preview API version required for OneLake -$apiVersion = '2024-05-01-preview' +function Invoke-AzCliWithTimeout { + param( + [string[]]$Args, + [int]$TimeoutSeconds = 120 + ) + + $escapedArgs = $Args | ForEach-Object { + if ($_ -match '\s|"') { '"' + ($_ -replace '"', '\"') + '"' } else { $_ } + } + + $azPath = (Get-Command az -ErrorAction SilentlyContinue).Source + if (-not $azPath) { + throw "Azure CLI (az) not found on PATH." + } + + $psi = New-Object System.Diagnostics.ProcessStartInfo + $psi.FileName = $azPath + $psi.Arguments = ($escapedArgs -join ' ') + $psi.RedirectStandardOutput = $true + $psi.RedirectStandardError = $true + $psi.UseShellExecute = $false + $psi.CreateNoWindow = $true + + $process = [System.Diagnostics.Process]::Start($psi) + if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { + try { $process.Kill() } catch { } + throw "Azure CLI command timed out after $TimeoutSeconds seconds: az $($psi.Arguments)" + } + + $stdout = $process.StandardOutput.ReadToEnd() + $stderr = $process.StandardError.ReadToEnd() + + if ($process.ExitCode -ne 0) { + throw "Azure CLI failed with exit code $($process.ExitCode): $stderr" + } + + return $stdout +} + +function Set-SearchPublicNetworkAccess { + param([string]$Mode) + + $timeoutSeconds = 120 + if ($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS, [ref]$timeoutSeconds) | Out-Null + } + + $maxRetries = 3 + if ($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES, [ref]$maxRetries) | Out-Null + } + + $waitSeconds = 120 + if ($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS, [ref]$waitSeconds) | Out-Null + } + + for ($attempt = 1; $attempt -le $maxRetries; $attempt++) { + try { + $resourceId = Get-SearchResourceId + if (-not $resourceId) { + throw "Unable to resolve AI Search resource ID." + } + $armToken = Get-ArmAccessToken + if (-not $armToken) { + throw "Unable to acquire ARM access token." + } + $body = @{ properties = @{ publicNetworkAccess = $Mode } } | ConvertTo-Json -Compress + $url = "https://management.azure.com${resourceId}?api-version=2023-11-01" + $headers = @{ Authorization = "Bearer $armToken"; 'Content-Type' = 'application/json' } + Invoke-RestMethod -Method Patch -Uri $url -Headers $headers -Body $body | Out-Null + $deadline = (Get-Date).AddSeconds($waitSeconds) + do { + $current = Get-SearchPublicNetworkAccess + if ($current -eq $Mode) { return } + Start-Sleep -Seconds 5 + } while ((Get-Date) -lt $deadline) + + throw "Timed out waiting for AI Search public network access to become '$Mode'." + } catch { + if ($attempt -lt $maxRetries) { + Write-Warning "Failed to set AI Search public network access (attempt $attempt of $maxRetries): $($_.Exception.Message)" + Start-Sleep -Seconds 10 + continue + } + throw + } + } +} + +function Ensure-SearchPublicAccess { + if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { + Write-Host "Skipping temporary public network access toggle for AI Search." + return $null + } + + $current = Get-SearchPublicNetworkAccess + if (-not $current) { return $null } + + if ($current -eq 'Disabled') { + Write-Warning "AI Search public network access is Disabled. Enabling temporarily for OneLake setup." + Set-SearchPublicNetworkAccess -Mode 'Enabled' + } + + return $current +} + +function Restore-SearchPublicAccess { + param([string]$OriginalAccess) + + if (-not $OriginalAccess) { return } + if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { return } + + $current = Get-SearchPublicNetworkAccess + if ($current -and $current -ne $OriginalAccess) { + Write-Host "Restoring AI Search public network access to '$OriginalAccess'." + try { + Set-SearchPublicNetworkAccess -Mode $OriginalAccess + } catch { + Write-Warning "Failed to restore AI Search public network access: $($_.Exception.Message)" + } + } +} + +function Get-SearchAccessToken { + try { + return az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv + } catch { + return $null + } +} + +function New-SearchHeaders { + param( + [string]$AccessToken + ) + + if ($AccessToken) { + return @{ + 'Authorization' = "Bearer $AccessToken" + 'Content-Type' = 'application/json' + } + } + + return $null +} + +function Invoke-SearchRequest { + param( + [string]$Method, + [string]$Uri, + [string]$Body + ) + + $maxAttempts = 6 + $delaySeconds = 30 + + for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { + $accessToken = Get-SearchAccessToken + $headers = New-SearchHeaders -AccessToken $accessToken + + if (-not $headers) { + Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" + exit 1 + } + + try { + if ($Body) { + return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method -Body $Body + } + return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method + } catch { + $statusCode = $null + try { $statusCode = $_.Exception.Response.StatusCode.value__ } catch { } + + if (($statusCode -eq 401 -or $statusCode -eq 403) -and $attempt -lt $maxAttempts) { + Write-Warning "Search request denied (HTTP $statusCode). Waiting ${delaySeconds}s for RBAC propagation (attempt $attempt of $maxAttempts)." + Start-Sleep -Seconds $delaySeconds + continue + } + + throw + } + } +} + +$originalPublicAccess = Ensure-SearchPublicAccess +try { + # Use preview API version required for OneLake + $apiVersion = '2024-05-01-preview' # Create OneLake data source with System-Assigned Managed Identity Write-Host "Creating OneLake data source: $dataSourceName" @@ -180,13 +383,13 @@ $dataSourceBody = @{ # First, check if datasource exists and delete it if it does $existingDataSourceUri = "https://$aiSearchName.search.windows.net/datasources/$dataSourceName" + "?api-version=$apiVersion" try { - $existingDataSource = Invoke-SecureRestMethod -Uri $existingDataSourceUri -Headers $headers -Method GET -ErrorAction SilentlyContinue + $existingDataSource = Invoke-SearchRequest -Method 'GET' -Uri $existingDataSourceUri if ($existingDataSource) { Write-Host "Found existing datasource. Checking for dependent indexers..." # Get all indexers to see if any reference this datasource $indexersUri = "https://$aiSearchName.search.windows.net/indexers?api-version=$apiVersion" - $indexers = Invoke-SecureRestMethod -Uri $indexersUri -Headers $headers -Method GET + $indexers = Invoke-SearchRequest -Method 'GET' -Uri $indexersUri $dependentIndexers = $indexers.value | Where-Object { $_.dataSourceName -eq $dataSourceName } @@ -195,7 +398,7 @@ try { foreach ($indexer in $dependentIndexers) { $deleteIndexerUri = "https://$aiSearchName.search.windows.net/indexers/$($indexer.name)?api-version=$apiVersion" try { - Invoke-SecureRestMethod -Uri $deleteIndexerUri -Headers $headers -Method DELETE + Invoke-SearchRequest -Method 'DELETE' -Uri $deleteIndexerUri Write-Host "Deleted indexer: $($indexer.name)" } catch { Write-Host "Warning: Could not delete indexer $($indexer.name): $($_.Exception.Message)" @@ -204,7 +407,7 @@ try { } Write-Host "Deleting existing datasource to recreate with current values..." - Invoke-SecureRestMethod -Uri $existingDataSourceUri -Headers $headers -Method DELETE + Invoke-SearchRequest -Method 'DELETE' -Uri $existingDataSourceUri Write-Host "Existing datasource deleted." } } catch { @@ -215,7 +418,7 @@ try { # Create the datasource $createDataSourceUri = "https://$aiSearchName.search.windows.net/datasources" + "?api-version=$apiVersion" try { - $response = Invoke-SecureRestMethod -Uri $createDataSourceUri -Headers $headers -Body $dataSourceBody -Method POST + $response = Invoke-SearchRequest -Method 'POST' -Uri $createDataSourceUri -Body $dataSourceBody Write-Host "" Write-Host "OneLake data source created successfully!" Write-Host "Datasource Name: $($response.name)" @@ -256,3 +459,6 @@ Write-Host "" Write-Host "⚠️ IMPORTANT: Ensure the AI Search System-Assigned Managed Identity has:" Write-Host " 1. OneLake data access role in the Fabric workspace" Write-Host " 2. Storage Blob Data Reader role in Azure" +} finally { + Restore-SearchPublicAccess -OriginalAccess $originalPublicAccess +} diff --git a/scripts/automationScripts/OneLakeIndex/05_create_onelake_indexer.ps1 b/scripts/automationScripts/OneLakeIndex/05_create_onelake_indexer.ps1 index 8331c39..d6c74be 100644 --- a/scripts/automationScripts/OneLakeIndex/05_create_onelake_indexer.ps1 +++ b/scripts/automationScripts/OneLakeIndex/05_create_onelake_indexer.ps1 @@ -35,6 +35,11 @@ if ($fabricWorkspaceMode -and $fabricWorkspaceMode.ToString().Trim().ToLowerInva exit 0 } +$outputs = $null +if ($env:AZURE_OUTPUTS_JSON) { + try { $outputs = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop } catch { $outputs = $null } +} + function Get-SafeName([string]$name) { if (-not $name) { return $null } $safe = $name.ToLower() -replace "[^a-z0-9-]", "-" -replace "-+", "-" @@ -76,11 +81,14 @@ if ($indexerName -eq 'onelake-reports-indexer') { } # Resolve parameters from environment + if (-not $aiSearchName -and $outputs -and $outputs.aiSearchName -and $outputs.aiSearchName.value) { $aiSearchName = $outputs.aiSearchName.value } if (-not $aiSearchName) { $aiSearchName = $env:aiSearchName } if (-not $aiSearchName) { $aiSearchName = $env:AZURE_AI_SEARCH_NAME } + if (-not $resourceGroup -and $outputs -and $outputs.aiSearchResourceGroup -and $outputs.aiSearchResourceGroup.value) { $resourceGroup = $outputs.aiSearchResourceGroup.value } if (-not $resourceGroup) { $resourceGroup = $env:aiSearchResourceGroup } if (-not $resourceGroup) { $resourceGroup = $env:AZURE_RESOURCE_GROUP_NAME } if (-not $resourceGroup) { $resourceGroup = $env:AZURE_RESOURCE_GROUP } + if (-not $subscription -and $outputs -and $outputs.aiSearchSubscriptionId -and $outputs.aiSearchSubscriptionId.value) { $subscription = $outputs.aiSearchSubscriptionId.value } if (-not $subscription) { $subscription = $env:aiSearchSubscriptionId } if (-not $subscription) { $subscription = $env:AZURE_SUBSCRIPTION_ID } @@ -100,25 +108,220 @@ if ($workspaceName) { Write-Host "Derived Fabric Workspace Name: $workspaceName" if ($folderPath) { Write-Host "Folder Path: $folderPath" } Write-Host "" -# Acquire Entra ID access token for Azure AI Search data plane -try { - $accessToken = az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv -} catch { - $accessToken = $null +function Get-SearchPublicNetworkAccess { + try { + return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "publicNetworkAccess" -o tsv + } catch { + return $null + } } -if (-not $accessToken) { - Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" - exit 1 +function Get-SearchResourceId { + try { + return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "id" -o tsv + } catch { + return $null + } +} + +function Get-ArmAccessToken { + try { + return az account get-access-token --resource https://management.azure.com/ --subscription $subscription --query accessToken -o tsv + } catch { + return $null + } +} + +function Invoke-AzCliWithTimeout { + param( + [string[]]$Args, + [int]$TimeoutSeconds = 120 + ) + + $escapedArgs = $Args | ForEach-Object { + if ($_ -match '\s|"') { '"' + ($_ -replace '"', '\"') + '"' } else { $_ } + } + + $azPath = (Get-Command az -ErrorAction SilentlyContinue).Source + if (-not $azPath) { + throw "Azure CLI (az) not found on PATH." + } + + $psi = New-Object System.Diagnostics.ProcessStartInfo + $psi.FileName = $azPath + $psi.Arguments = ($escapedArgs -join ' ') + $psi.RedirectStandardOutput = $true + $psi.RedirectStandardError = $true + $psi.UseShellExecute = $false + $psi.CreateNoWindow = $true + + $process = [System.Diagnostics.Process]::Start($psi) + if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { + try { $process.Kill() } catch { } + throw "Azure CLI command timed out after $TimeoutSeconds seconds: az $($psi.Arguments)" + } + + $stdout = $process.StandardOutput.ReadToEnd() + $stderr = $process.StandardError.ReadToEnd() + + if ($process.ExitCode -ne 0) { + throw "Azure CLI failed with exit code $($process.ExitCode): $stderr" + } + + return $stdout +} + +function Set-SearchPublicNetworkAccess { + param([string]$Mode) + + $timeoutSeconds = 120 + if ($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS, [ref]$timeoutSeconds) | Out-Null + } + + $maxRetries = 3 + if ($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES, [ref]$maxRetries) | Out-Null + } + + $waitSeconds = 120 + if ($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS, [ref]$waitSeconds) | Out-Null + } + + for ($attempt = 1; $attempt -le $maxRetries; $attempt++) { + try { + $resourceId = Get-SearchResourceId + if (-not $resourceId) { + throw "Unable to resolve AI Search resource ID." + } + $armToken = Get-ArmAccessToken + if (-not $armToken) { + throw "Unable to acquire ARM access token." + } + $body = @{ properties = @{ publicNetworkAccess = $Mode } } | ConvertTo-Json -Compress + $url = "https://management.azure.com${resourceId}?api-version=2023-11-01" + $headers = @{ Authorization = "Bearer $armToken"; 'Content-Type' = 'application/json' } + Invoke-RestMethod -Method Patch -Uri $url -Headers $headers -Body $body | Out-Null + $deadline = (Get-Date).AddSeconds($waitSeconds) + do { + $current = Get-SearchPublicNetworkAccess + if ($current -eq $Mode) { return } + Start-Sleep -Seconds 5 + } while ((Get-Date) -lt $deadline) + + throw "Timed out waiting for AI Search public network access to become '$Mode'." + } catch { + if ($attempt -lt $maxRetries) { + Write-Warning "Failed to set AI Search public network access (attempt $attempt of $maxRetries): $($_.Exception.Message)" + Start-Sleep -Seconds 10 + continue + } + throw + } + } +} + +function Ensure-SearchPublicAccess { + if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { + Write-Host "Skipping temporary public network access toggle for AI Search." + return $null + } + + $current = Get-SearchPublicNetworkAccess + if (-not $current) { return $null } + + if ($current -eq 'Disabled') { + Write-Warning "AI Search public network access is Disabled. Enabling temporarily for OneLake setup." + Set-SearchPublicNetworkAccess -Mode 'Enabled' + } + + return $current } -$headers = @{ - 'Authorization' = "Bearer $accessToken" - 'Content-Type' = 'application/json' +function Restore-SearchPublicAccess { + param([string]$OriginalAccess) + + if (-not $OriginalAccess) { return } + if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { return } + + $current = Get-SearchPublicNetworkAccess + if ($current -and $current -ne $OriginalAccess) { + Write-Host "Restoring AI Search public network access to '$OriginalAccess'." + try { + Set-SearchPublicNetworkAccess -Mode $OriginalAccess + } catch { + Write-Warning "Failed to restore AI Search public network access: $($_.Exception.Message)" + } + } +} + +function Get-SearchAccessToken { + try { + return az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv + } catch { + return $null + } +} + +function New-SearchHeaders { + param( + [string]$AccessToken + ) + + if ($AccessToken) { + return @{ + 'Authorization' = "Bearer $AccessToken" + 'Content-Type' = 'application/json' + } + } + + return $null +} + +function Invoke-SearchRequest { + param( + [string]$Method, + [string]$Uri, + [string]$Body + ) + + $maxAttempts = 6 + $delaySeconds = 30 + + for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { + $accessToken = Get-SearchAccessToken + $headers = New-SearchHeaders -AccessToken $accessToken + + if (-not $headers) { + Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" + exit 1 + } + + try { + if ($Body) { + return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method -Body $Body + } + return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method + } catch { + $statusCode = $null + try { $statusCode = $_.Exception.Response.StatusCode.value__ } catch { } + + if (($statusCode -eq 401 -or $statusCode -eq 403) -and $attempt -lt $maxAttempts) { + Write-Warning "Search request denied (HTTP $statusCode). Waiting ${delaySeconds}s for RBAC propagation (attempt $attempt of $maxAttempts)." + Start-Sleep -Seconds $delaySeconds + continue + } + + throw + } + } } -# Use preview API version required for OneLake -$apiVersion = '2024-05-01-preview' +$originalPublicAccess = Ensure-SearchPublicAccess +try { + # Use preview API version required for OneLake + $apiVersion = '2024-05-01-preview' # Create OneLake indexer Write-Host "Creating OneLake indexer: $indexerName" @@ -179,7 +382,7 @@ $indexerBody = @{ # Delete existing indexer if present try { $deleteUrl = "https://$aiSearchName.search.windows.net/indexers/$indexerName?api-version=$apiVersion" - Invoke-RestMethod -Uri $deleteUrl -Headers $headers -Method DELETE + Invoke-SearchRequest -Method 'DELETE' -Uri $deleteUrl Write-Host "Deleted existing indexer" } catch { Write-Host "No existing indexer to delete" @@ -189,15 +392,27 @@ try { $createUrl = "https://$aiSearchName.search.windows.net/indexers?api-version=$apiVersion" try { - $response = Invoke-RestMethod -Uri $createUrl -Headers $headers -Method POST -Body $indexerBody + $response = Invoke-SearchRequest -Method 'POST' -Uri $createUrl -Body $indexerBody Write-Host "✅ Successfully created OneLake indexer: $($response.name)" # Run the indexer immediately Write-Host "" Write-Host "Running indexer..." $runUrl = "https://$aiSearchName.search.windows.net/indexers/$indexerName/run?api-version=$apiVersion" - Invoke-RestMethod -Uri $runUrl -Headers $headers -Method POST - Write-Host "✅ Indexer execution started" + try { + Invoke-SearchRequest -Method 'POST' -Uri $runUrl + Write-Host "✅ Indexer execution started" + } catch { + $runStatusCode = $null + $runErrorBody = $null + try { $runStatusCode = $_.Exception.Response.StatusCode.value__ } catch { } + try { $runErrorBody = $_.ErrorDetails.Message } catch { } + if ($runStatusCode -eq 409 -and $runErrorBody -match 'invocation.*in progress') { + Write-Warning "Indexer is already running; continuing without starting a new run." + } else { + throw + } + } # Wait a moment and check status Write-Host "" @@ -205,7 +420,7 @@ try { Start-Sleep -Seconds 30 $statusUrl = "https://$aiSearchName.search.windows.net/indexers/$indexerName/status?api-version=$apiVersion" - $status = Invoke-RestMethod -Uri $statusUrl -Headers $headers -Method GET + $status = Invoke-SearchRequest -Method 'GET' -Uri $statusUrl Write-Host "" Write-Host "🎯 INDEXER EXECUTION RESULTS:" @@ -232,7 +447,7 @@ try { # Check the search index for documents $searchUrl = "https://$aiSearchName.search.windows.net/indexes/$indexName/docs?api-version=$apiVersion&search=*&`$count=true&`$top=3" try { - $searchResults = Invoke-RestMethod -Uri $searchUrl -Headers $headers -Method GET + $searchResults = Invoke-SearchRequest -Method 'GET' -Uri $searchUrl Write-Host "Total documents in search index: $($searchResults.'@odata.count')" if ($searchResults.value.Count -gt 0) { @@ -247,10 +462,10 @@ try { } } else { Write-Host "" - Write-Host "⚠️ No documents were processed. This may indicate:" - Write-Host " 1. Permission issues with AI Search accessing OneLake" - Write-Host " 2. No documents found in the specified path" - Write-Host " 3. Authentication problems with the managed identity" + Write-Host "ℹ️ No documents were processed. This is expected if the lakehouse is empty." + Write-Host " If you expected documents, check:" + Write-Host " 1. Documents exist in the configured path" + Write-Host " 2. AI Search has access to OneLake" } } catch { @@ -264,18 +479,24 @@ try { Write-Host "HTTP Reason: $($_.Exception.Response.ReasonPhrase)" } - # Try using curl to get a better error message - Write-Host "" - Write-Host "Attempting to get detailed error using curl..." - $curlResult = & curl -s -w "%{http_code}" -X POST $createUrl -H "api-key: $apiKey" -H "Content-Type: application/json" -d $indexerBody - Write-Host "Curl result: $curlResult" + # Try using curl with bearer token to get a better error message when possible + try { + $accessToken = Get-SearchAccessToken + } catch { $accessToken = $null } + if ($accessToken) { + Write-Host "" + Write-Host "Attempting to get detailed error using curl..." + $curlResult = & curl -s -D - -X POST "$createUrl" -H "Authorization: Bearer $accessToken" -H "Content-Type: application/json" -d $indexerBody + Write-Host "Curl result:" + Write-Host $curlResult + } # Check if prerequisite resources exist Write-Host "" Write-Host "Checking prerequisite resources..." try { $indexUrl = "https://$aiSearchName.search.windows.net/indexes/$indexName?api-version=$apiVersion" - $indexExists = Invoke-RestMethod -Uri $indexUrl -Headers $headers -Method GET -ErrorAction SilentlyContinue + $indexExists = Invoke-SearchRequest -Method 'GET' -Uri $indexUrl Write-Host "✅ Index '$indexName' exists" } catch { Write-Host "❌ Index '$indexName' does not exist or is inaccessible" @@ -283,7 +504,7 @@ try { try { $datasourceUrl = "https://$aiSearchName.search.windows.net/datasources/$dataSourceName?api-version=$apiVersion" - $datasourceExists = Invoke-RestMethod -Uri $datasourceUrl -Headers $headers -Method GET -ErrorAction SilentlyContinue + $datasourceExists = Invoke-SearchRequest -Method 'GET' -Uri $datasourceUrl Write-Host "✅ Datasource '$dataSourceName' exists" } catch { Write-Host "❌ Datasource '$dataSourceName' does not exist or is inaccessible" @@ -292,5 +513,8 @@ try { exit 1 } -Write-Host "" -Write-Host "OneLake indexer setup completed!" + Write-Host "" + Write-Host "OneLake indexer setup completed!" +} finally { + Restore-SearchPublicAccess -OriginalAccess $originalPublicAccess +} diff --git a/scripts/automationScripts/OneLakeIndex/06_setup_ai_foundry_search_rbac.ps1 b/scripts/automationScripts/OneLakeIndex/06_setup_ai_foundry_search_rbac.ps1 index e31614d..0c6e646 100644 --- a/scripts/automationScripts/OneLakeIndex/06_setup_ai_foundry_search_rbac.ps1 +++ b/scripts/automationScripts/OneLakeIndex/06_setup_ai_foundry_search_rbac.ps1 @@ -40,6 +40,8 @@ if ($fabricWorkspaceMode -and $fabricWorkspaceMode.ToString().Trim().ToLowerInva Set-StrictMode -Version Latest +$script:aiFoundryProjectName = $null + # Import security module $skipRoleAssignment = $false if ($env:SKIP_FOUNDATION_RBAC -and $env:SKIP_FOUNDATION_RBAC.ToLowerInvariant() -eq 'true') { @@ -87,6 +89,19 @@ Log "==================================================================" Log "Setting up AI Foundry to AI Search RBAC integration" Log "==================================================================" +$outputs = $null +if ($env:AZURE_OUTPUTS_JSON) { + try { $outputs = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop } catch { $outputs = $null } +} + +if (-not $AISearchName -and $outputs -and $outputs.aiSearchName -and $outputs.aiSearchName.value) { $AISearchName = $outputs.aiSearchName.value } +if (-not $AISearchResourceGroup -and $outputs -and $outputs.aiSearchResourceGroup -and $outputs.aiSearchResourceGroup.value) { $AISearchResourceGroup = $outputs.aiSearchResourceGroup.value } +if (-not $AISearchSubscriptionId -and $outputs -and $outputs.aiSearchSubscriptionId -and $outputs.aiSearchSubscriptionId.value) { $AISearchSubscriptionId = $outputs.aiSearchSubscriptionId.value } +if (-not $AIFoundryName -and $outputs -and $outputs.aiFoundryName -and $outputs.aiFoundryName.value) { $AIFoundryName = $outputs.aiFoundryName.value } +if (-not $AIFoundryResourceGroup -and $outputs -and $outputs.aiFoundryResourceGroup -and $outputs.aiFoundryResourceGroup.value) { $AIFoundryResourceGroup = $outputs.aiFoundryResourceGroup.value } +if (-not $AIFoundrySubscriptionId -and $outputs -and $outputs.aiFoundrySubscriptionId -and $outputs.aiFoundrySubscriptionId.value) { $AIFoundrySubscriptionId = $outputs.aiFoundrySubscriptionId.value } +if (-not $script:aiFoundryProjectName -and $outputs -and $outputs.aiFoundryProjectName -and $outputs.aiFoundryProjectName.value) { $script:aiFoundryProjectName = $outputs.aiFoundryProjectName.value } + # Get values from azd environment if not provided if (-not $AISearchName -or -not $AIFoundryName) { Log "Getting configuration from azd environment..." @@ -147,9 +162,65 @@ if (-not $AISearchName -or -not $AIFoundryName) { exit 1 } +function Test-AiFoundryProjectExists { + param([string]$ProjectName) + if (-not $ProjectName) { return $false } + + try { + $projectResourceId = "/subscriptions/$AIFoundrySubscriptionId/resourceGroups/$AIFoundryResourceGroup/providers/Microsoft.CognitiveServices/accounts/$AIFoundryName/projects/$ProjectName" + $null = az resource show --ids $projectResourceId --query id -o tsv 2>$null + return ($LASTEXITCODE -eq 0) + } catch { + return $false + } +} + +if ($script:aiFoundryProjectName) { + if ($script:aiFoundryProjectName -eq $AIFoundryName) { + Warn "AI Foundry project name matches account name; clearing and attempting discovery." + $script:aiFoundryProjectName = $null + } elseif (-not (Test-AiFoundryProjectExists -ProjectName $script:aiFoundryProjectName)) { + Warn "AI Foundry project '$script:aiFoundryProjectName' not found; clearing and attempting discovery." + $script:aiFoundryProjectName = $null + } +} + +if (-not $script:aiFoundryProjectName) { + try { + $projectCandidatesRaw = az resource list ` + --resource-group $AIFoundryResourceGroup ` + --subscription $AIFoundrySubscriptionId ` + --resource-type "Microsoft.CognitiveServices/accounts/projects" ` + --query "[?contains(id, '/accounts/$AIFoundryName/')].name" -o tsv 2>$null + + if ($projectCandidatesRaw) { + [string[]]$projectCandidates = ($projectCandidatesRaw -split "\r?\n") | Where-Object { $_ -and $_.Trim() } | ForEach-Object { $_.Trim() } + if ($projectCandidates.Length -eq 1) { + $script:aiFoundryProjectName = $projectCandidates[0] + Log "Discovered AI Foundry project: $script:aiFoundryProjectName" + } elseif ($projectCandidates.Length -gt 1) { + $script:aiFoundryProjectName = $projectCandidates[0] + Warn "Multiple AI Foundry projects detected; defaulting to '$script:aiFoundryProjectName'. Override via -AIFoundryName/-AIFoundryResourceGroup if needed." + Log "Candidates: $($projectCandidates -join ', ')" + } + } + } catch { + Warn "Unable to auto-discover AI Foundry project: $($_.Exception.Message)" + } +} + $additionalPrincipalIds = @() try { - if ($env_vars -and $env_vars.ContainsKey('aiSearchAdditionalAccessObjectIds')) { + if ($env:AZURE_OUTPUTS_JSON) { + try { + $out0 = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop + if ($out0.aiSearchAdditionalAccessObjectIds -and $out0.aiSearchAdditionalAccessObjectIds.value) { + $jsonValue = $out0.aiSearchAdditionalAccessObjectIds.value | ConvertTo-Json -Compress + $additionalPrincipalIds = ConvertTo-PrincipalIdArray -RawValue $jsonValue + } + } catch { } + } + if ($additionalPrincipalIds.Count -eq 0 -and $env_vars -and $env_vars.ContainsKey('aiSearchAdditionalAccessObjectIds')) { $additionalPrincipalIds = ConvertTo-PrincipalIdArray -RawValue $env_vars['aiSearchAdditionalAccessObjectIds'] } if ($additionalPrincipalIds.Count -eq 0) { diff --git a/scripts/automationScripts/OneLakeIndex/setup_ai_services_rbac.ps1 b/scripts/automationScripts/OneLakeIndex/setup_ai_services_rbac.ps1 index e5082ee..9cac831 100644 --- a/scripts/automationScripts/OneLakeIndex/setup_ai_services_rbac.ps1 +++ b/scripts/automationScripts/OneLakeIndex/setup_ai_services_rbac.ps1 @@ -28,6 +28,22 @@ function Log([string]$m) { Write-Host "[ai-services-rbac] $m" -ForegroundColor C function Warn([string]$m) { Write-Warning "[ai-services-rbac] $m" } function Success([string]$m) { Write-Host "[ai-services-rbac] ✅ $m" -ForegroundColor Green } +function Test-AiFoundryProjectExists { + param( + [string]$AccountScope, + [string]$ProjectName + ) + + if (-not $ProjectName) { return $false } + try { + $projectResourceId = "$AccountScope/projects/$ProjectName" + $null = az resource show --ids $projectResourceId --query id -o tsv 2>$null + return ($LASTEXITCODE -eq 0) + } catch { + return $false + } +} + function ConvertTo-PrincipalIdArray { param([string]$RawValue) $ids = @() @@ -220,6 +236,14 @@ try { $projectName = $env:aiFoundryProjectName if (-not $projectName) { $projectName = $env:AI_FOUNDRY_PROJECT_NAME } if (-not $projectName -and $aiFoundryAccount.defaultProject) { $projectName = $aiFoundryAccount.defaultProject } + if ($projectName -eq $AIFoundryName) { + Warn "AI Foundry project name matches account name; clearing and attempting discovery." + $projectName = $null + } elseif ($projectName -and -not (Test-AiFoundryProjectExists -AccountScope $accountScope -ProjectName $projectName)) { + Warn "AI Foundry project '$projectName' not found; clearing and attempting discovery." + $projectName = $null + } + if (-not $projectName) { try { $projectListArgs = @('--resource-group', $AIFoundryResourceGroup, '--resource-type', 'Microsoft.CognitiveServices/accounts/projects', '--query', "[?starts_with(name, '$AIFoundryName/')].name", '-o', 'tsv') diff --git a/scripts/automationScripts/SecurityModule.ps1 b/scripts/automationScripts/SecurityModule.ps1 index 2d28990..1b6b1d1 100644 --- a/scripts/automationScripts/SecurityModule.ps1 +++ b/scripts/automationScripts/SecurityModule.ps1 @@ -115,8 +115,26 @@ function Invoke-SecureRestMethod { } catch { # Sanitize error message to remove sensitive data - $sanitizedError = $_.Exception.Message -replace 'Bearer [A-Za-z0-9\-\._~\+\/]+=*', 'Bearer [REDACTED]' - Write-Error "Secure $Description failed: $sanitizedError" -ErrorAction Stop + $sanitizedError = $_.Exception.Message -replace 'Bearer [A-Za-z0-9\-\._~\+\/=]+=*', 'Bearer [REDACTED]' + $statusCode = $null + $responseBody = $null + $response = $null + try { $response = $_.Exception.Response } catch { $response = $null } + if ($response) { + try { $statusCode = $response.StatusCode } catch { $statusCode = $null } + try { + $reader = New-Object System.IO.StreamReader($response.GetResponseStream()) + $responseBody = $reader.ReadToEnd() + } catch { $responseBody = $null } + } + if ($responseBody) { + $responseBody = $responseBody -replace 'Bearer [A-Za-z0-9\-\._~\+\/=]+=*', 'Bearer [REDACTED]' + } + + Write-Error "Secure $Description failed: $sanitizedError" + if ($statusCode) { Write-Error "HTTP Status: $statusCode" } + if ($responseBody) { Write-Error "HTTP Body: $responseBody" } + throw } } diff --git a/scripts/preprovision-integrated.ps1 b/scripts/preprovision-integrated.ps1 index 9023792..1b85a7a 100644 --- a/scripts/preprovision-integrated.ps1 +++ b/scripts/preprovision-integrated.ps1 @@ -257,6 +257,54 @@ $deploymentName = "ai-landing-zone-$envNameForDeployment-$(Get-Date -Format 'yyy Write-Host " [+] Deployment name: $deploymentName" -ForegroundColor Green +function Format-AzDeploymentError { + param( + [string]$Raw + ) + + $code = $null + $message = $null + $rawText = $Raw + + if (-not [string]::IsNullOrWhiteSpace($Raw)) { + try { + $json = $Raw | ConvertFrom-Json -ErrorAction Stop + if ($null -ne $json.error) { + $code = $json.error.code + $message = $json.error.message + if ($json.error.details -and $json.error.details.Count -gt 0) { + $detail = $json.error.details[0] + if ($detail.code) { $code = $detail.code } + if ($detail.message) { $message = $detail.message } + } + } + } catch { + # Not JSON, fall back to regex matching below. + } + + if (-not $code -and $Raw -match 'DeploymentActive') { + $code = 'DeploymentActive' + } + if (-not $code -and $Raw -match 'AccountProvisioningStateInvalid') { + $code = 'AccountProvisioningStateInvalid' + } + if (-not $code -and $Raw -match "management\.azure\.com") { + $code = 'NetworkResolutionFailed' + } + + if ([string]::IsNullOrWhiteSpace($message)) { + $lines = $Raw -split "`r?`n" | Where-Object { $_ -and $_ -notmatch '^WARNING:' } + $message = ($lines | Select-Object -First 3) -join ' ' + } + } + + return [pscustomobject]@{ + Code = $code + Message = $message + Raw = $rawText + } +} + $compiledParent = Join-Path $env:TEMP ("parent.$deploymentName.parameters.json") & az bicep build-params --file $parentParamsFile --outfile $compiledParent | Out-Null @@ -310,14 +358,104 @@ foreach ($name in $allowedParamNames) { $filteredParams = Join-Path $env:TEMP ("ai-landing-zone.$deploymentName.parameters.json") $filtered | ConvertTo-Json -Depth 50 | Set-Content -Path $filteredParams -Encoding UTF8 -& az deployment group create --name $deploymentName --resource-group $ResourceGroup --template-file $submoduleMain --parameters ("@" + $filteredParams) -if ($LASTEXITCODE -ne 0) { +$deployOutput = & az deployment group create --name $deploymentName --resource-group $ResourceGroup --template-file $submoduleMain --parameters ("@" + $filteredParams) --only-show-errors 2>&1 +$deployExitCode = $LASTEXITCODE +if ($deployExitCode -ne 0) { Write-Host "[X] AI Landing Zone submodule deployment failed" -ForegroundColor Red + + $raw = ($deployOutput | Out-String).Trim() + $parsed = Format-AzDeploymentError -Raw $raw + + if (-not [string]::IsNullOrWhiteSpace($parsed.Code) -or -not [string]::IsNullOrWhiteSpace($parsed.Message)) { + $reasonParts = @() + if ($parsed.Code) { $reasonParts += $parsed.Code } + if ($parsed.Message) { $reasonParts += $parsed.Message } + Write-Host (" Failure: {0}" -f ($reasonParts -join " - ")) -ForegroundColor Yellow + } + + if ($parsed.Code -eq 'DeploymentActive') { + Write-Host " Another deployment is still running in this resource group. Wait for it to complete or cancel it, then re-run." -ForegroundColor Yellow + } elseif ($parsed.Code -eq 'AccountProvisioningStateInvalid') { + Write-Host " AI Foundry account is still provisioning. Retry after it reaches 'Succeeded'." -ForegroundColor Yellow + } elseif ($parsed.Code -eq 'NetworkResolutionFailed') { + Write-Host " Network/DNS could not resolve management.azure.com. Check connectivity and retry." -ForegroundColor Yellow + } + + if ($env:AZD_VERBOSE_ERRORS -and -not [string]::IsNullOrWhiteSpace($raw)) { + $preview = ($raw -split "`r?`n" | Select-Object -First 5) -join "`n" + Write-Host " Raw error (first lines):" -ForegroundColor DarkGray + Write-Host $preview -ForegroundColor DarkGray + } + exit 1 } Write-Host " [+] AI Landing Zone deployment complete" -ForegroundColor Green +Write-Host "" +Write-Host "[2] Publishing submodule outputs to azd env..." -ForegroundColor Cyan + +function Set-AzdEnvValue { + param( + [string]$Name, + [string]$Value + ) + + if ([string]::IsNullOrWhiteSpace($Value)) { return } + try { + & azd env set $Name $Value 2>$null | Out-Null + } catch { + # Ignore and continue. + } +} + +$aiSearchName = $null +try { $aiSearchName = [string]$parentJson.parameters.searchServiceName.value } catch { } +if ([string]::IsNullOrWhiteSpace($aiSearchName)) { + try { $aiSearchName = [string]$parentJson.parameters.aiFoundrySearchServiceName.value } catch { } +} +if ([string]::IsNullOrWhiteSpace($aiSearchName)) { + try { $aiSearchName = (az search service list --resource-group $ResourceGroup --query "[0].name" -o tsv 2>$null).Trim() } catch { } +} + +$aiFoundryName = $null +try { $aiFoundryName = [string]$parentJson.parameters.aiFoundryAccountName.value } catch { } +if ([string]::IsNullOrWhiteSpace($aiFoundryName)) { + try { + $aiFoundryName = (az cognitiveservices account list --resource-group $ResourceGroup --query "[?kind=='AIServices']|[0].name" -o tsv 2>$null).Trim() + } catch { } +} + +$aiFoundryProjectName = $null +try { $aiFoundryProjectName = [string]$parentJson.parameters.aiFoundryProjectName.value } catch { } +if ([string]::IsNullOrWhiteSpace($aiFoundryProjectName) -and -not [string]::IsNullOrWhiteSpace($aiFoundryName)) { + try { + $projectCandidatesRaw = az resource list --resource-group $ResourceGroup --resource-type "Microsoft.CognitiveServices/accounts/projects" --query "[?contains(id, '/accounts/$aiFoundryName/')].name" -o tsv 2>$null + if ($projectCandidatesRaw) { + [string[]]$projectCandidates = ($projectCandidatesRaw -split "\r?\n") | Where-Object { $_ -and $_.Trim() } | ForEach-Object { $_.Trim() } + if ($projectCandidates.Length -ge 1) { + $aiFoundryProjectName = $projectCandidates[0] + } + } + } catch { + # Ignore discovery failures and continue. + } +} + +$aiSearchResourceId = $null +if (-not [string]::IsNullOrWhiteSpace($aiSearchName)) { + try { $aiSearchResourceId = (az resource show --resource-group $ResourceGroup --name $aiSearchName --resource-type Microsoft.Search/searchServices --query id -o tsv 2>$null).Trim() } catch { } +} + +Set-AzdEnvValue -Name 'aiSearchName' -Value $aiSearchName +Set-AzdEnvValue -Name 'AZURE_AI_SEARCH_NAME' -Value $aiSearchName +Set-AzdEnvValue -Name 'aiSearchResourceId' -Value $aiSearchResourceId +Set-AzdEnvValue -Name 'aiSearchResourceGroup' -Value $ResourceGroup +Set-AzdEnvValue -Name 'aiSearchSubscriptionId' -Value $SubscriptionId +Set-AzdEnvValue -Name 'aiFoundryName' -Value $aiFoundryName +Set-AzdEnvValue -Name 'aiFoundryResourceGroup' -Value $ResourceGroup +Set-AzdEnvValue -Name 'aiFoundryProjectName' -Value $aiFoundryProjectName + Write-Host "" Write-Host "[OK] Preprovision complete!" -ForegroundColor Green From d167a97c04918a33661706f44b44976950904266 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Tue, 10 Mar 2026 12:56:51 -0400 Subject: [PATCH 04/22] Add postgresql provisioning and fabric automation for mirror --- README.md | 2 +- azure.yaml | 12 + docs/DeploymentGuide.md | 1 + docs/PARAMETER_GUIDE.md | 11 + infra/main.bicep | 193 ++++++++++- infra/main.bicepparam | 36 +- .../materialize_document_folders.ps1 | 48 ++- .../mirror/create_postgresql_mirror.ps1 | 228 +++++++++++++ .../prepare_postgresql_for_mirroring.ps1 | 308 ++++++++++++++++++ ...esql_mirroring_prep_with_public_access.ps1 | 75 +++++ 10 files changed, 886 insertions(+), 28 deletions(-) create mode 100644 scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 create mode 100644 scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 create mode 100644 scripts/automationScripts/FabricWorkspace/mirror/run_postgresql_mirroring_prep_with_public_access.ps1 diff --git a/README.md b/README.md index bd096ce..a1485c0 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This accelerator extends the [AI Landing Zone](https://github.com/Azure/ai-landi ### Solution Architecture -| ![Architecture](./img/Architecture/AI-Landing-Zone-without-platform.png) | +| ![Architecture](./img/Architecture/Deploy-AI-App-in-Prod-Architecture_final.png) | |---| ### Key Components diff --git a/azure.yaml b/azure.yaml index 2c5c43e..65fc3d0 100644 --- a/azure.yaml +++ b/azure.yaml @@ -71,6 +71,18 @@ hooks: interactive: false shell: pwsh continueOnError: false + + # Stage 7.4: Prepare PostgreSQL for Fabric mirroring (server params + role) + - run: ./scripts/automationScripts/FabricWorkspace/Mirror/run_postgresql_mirroring_prep_with_public_access.ps1 + interactive: false + shell: pwsh + continueOnError: false + + # Stage 7.5: Create PostgreSQL Mirrored Database (if PostgreSQL is provisioned) + - run: ./scripts/automationScripts/FabricWorkspace/Mirror/create_postgresql_mirror.ps1 + interactive: false + shell: pwsh + continueOnError: false # Stage 8: Setup Fabric Workspace Private Link (for VNet integration) - run: ./scripts/automationScripts/FabricWorkspace/SecureWorkspace/setup_fabric_private_link.ps1 diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index 6216b3a..8667b97 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -179,6 +179,7 @@ Edit `infra/main.bicepparam` or set environment variables: | Parameter | Description | Default | |-----------|-------------|---------| | `networkIsolation` | Enable network isolation | `false` | +| `postgreSqlNetworkIsolation` | PostgreSQL private networking toggle (defaults to `networkIsolation`) | `networkIsolation` | | `useExistingVNet` | Reuse an existing VNet | `false` | | `existingVnetResourceId` | Existing VNet resource ID (when `useExistingVNet=true`) | `` | | `vmUserName` | Jump box VM admin username | `` | diff --git a/docs/PARAMETER_GUIDE.md b/docs/PARAMETER_GUIDE.md index 667ccf0..92fc713 100644 --- a/docs/PARAMETER_GUIDE.md +++ b/docs/PARAMETER_GUIDE.md @@ -437,6 +437,17 @@ az cognitiveservices account list-usage \ ## Individual Service Configuration +### PostgreSQL Flexible Server (Repo Wrapper) + +Use these in `infra/main.bicepparam` when deploying via this repo. `postgreSqlNetworkIsolation` defaults to `networkIsolation`. + +```bicep-params +param deployPostgreSql = true +param postgreSqlNetworkIsolation = networkIsolation +``` + +When `postgreSqlNetworkIsolation` is `false`, PostgreSQL uses public access and does not create private endpoints or private DNS resources. + ### Storage Account ```json diff --git a/infra/main.bicep b/infra/main.bicep index 96cfd9b..6409187 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -205,12 +205,104 @@ param purviewAccountResourceId string = '' @description('Optional. Existing Purview collection name') param purviewCollectionName string = '' +// ======================================== +// PARAMETERS - POSTGRESQL FLEXIBLE SERVER +// ======================================== + +@description('Deploy PostgreSQL Flexible Server.') +param deployPostgreSql bool = false + +@description('PostgreSQL Flexible Server name.') +param postgreSqlServerName string = 'pg${resourceToken}' + +@description('Enable network isolation for PostgreSQL (private DNS + private endpoint).') +param postgreSqlNetworkIsolation bool = networkIsolation + +@description('Create and link the PostgreSQL private DNS zone to the VNet.') +param deployPostgreSqlPrivateDnsLink bool = true + +@description('Optional override for the PostgreSQL private DNS VNet link name.') +param postgreSqlPrivateDnsLinkNameOverride string = '' + +@description('PostgreSQL admin username.') +param postgreSqlAdminLogin string = 'pgadmin' + +@description('PostgreSQL admin password.') +@secure() +param postgreSqlAdminPassword string + +@description('Store PostgreSQL admin password in Key Vault.') +param enablePostgreSqlKeyVaultSecret bool = true + +@description('Key Vault secret name for PostgreSQL admin password.') +param postgreSqlAdminSecretName string = 'postgres-admin-password' + +@description('PostgreSQL role name for Fabric mirroring.') +param postgreSqlFabricUserName string = 'fabric_user' + +@description('Key Vault secret name for the Fabric mirroring PostgreSQL role password.') +param postgreSqlFabricUserSecretName string = 'postgres-fabric-user-password' + +@description('PostgreSQL SKU name (tier + family + cores).') +param postgreSqlSkuName string = 'Standard_D2s_v3' + +@description('PostgreSQL tier aligned with SKU.') +@allowed([ + 'Burstable' + 'GeneralPurpose' + 'MemoryOptimized' +]) +param postgreSqlTier string = 'GeneralPurpose' + +@description('PostgreSQL availability zone. -1 means no zone preference.') +@allowed([ + -1 + 1 + 2 + 3 +]) +param postgreSqlAvailabilityZone int = -1 + +@description('PostgreSQL high availability mode.') +@allowed([ + 'Disabled' + 'SameZone' + 'ZoneRedundant' +]) +param postgreSqlHighAvailability string = 'Disabled' + +@description('PostgreSQL high availability standby zone. -1 means no zone preference.') +@allowed([ + -1 + 1 + 2 + 3 +]) +param postgreSqlHighAvailabilityZone int = -1 + +@description('PostgreSQL version.') +@allowed([ + '11' + '12' + '13' + '14' + '15' + '16' + '17' + '18' +]) +param postgreSqlVersion string = '16' + +@description('PostgreSQL storage size in GB.') +param postgreSqlStorageSizeGB int = 32 + // ======================================== // FABRIC CAPACITY DEPLOYMENT // ======================================== var effectiveFabricCapacityMode = fabricCapacityMode var effectiveFabricWorkspaceMode = fabricWorkspaceMode +var effectiveLocation = !empty(location) ? location : resourceGroup().location var envSlugSanitized = replace(replace(replace(replace(replace(replace(replace(replace(toLower(environmentName), ' ', ''), '-', ''), '_', ''), '.', ''), '/', ''), '\\', ''), ':', ''), ',', '') @@ -218,11 +310,92 @@ var envSlugTrimmed = substring(envSlugSanitized, 0, min(40, length(envSlugSaniti var capacityNameBase = !empty(envSlugTrimmed) ? 'fabric${envSlugTrimmed}' : 'fabric${baseName}' var capacityName = substring(capacityNameBase, 0, min(50, length(capacityNameBase))) +var effectiveVnetResourceId = useExistingVNet && !empty(existingVnetResourceId) + ? existingVnetResourceId + : resourceId('Microsoft.Network/virtualNetworks', vnetName) + +var postgreSqlPrivateDnsZoneName = 'privatelink.postgres.database.azure.com' +var postgreSqlPrivateDnsLinkNameRaw = '${postgreSqlServerName}-vnetlink' +var postgreSqlPrivateEndpointNameRaw = '${postgreSqlServerName}-pe' +var postgreSqlPrivateDnsLinkName = substring(postgreSqlPrivateDnsLinkNameRaw, 0, min(80, length(postgreSqlPrivateDnsLinkNameRaw))) +var effectivePostgreSqlPrivateDnsLinkName = !empty(postgreSqlPrivateDnsLinkNameOverride) + ? postgreSqlPrivateDnsLinkNameOverride + : postgreSqlPrivateDnsLinkName +var postgreSqlPrivateEndpointName = substring(postgreSqlPrivateEndpointNameRaw, 0, min(80, length(postgreSqlPrivateEndpointNameRaw))) + +var effectiveKeyVaultResourceId = !empty(keyVaultResourceId) + ? keyVaultResourceId + : resourceId('Microsoft.KeyVault/vaults', keyVaultName) + +resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = { + name: last(split(effectiveKeyVaultResourceId, '/')) +} + +resource postgreSqlPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = if (deployPostgreSql && postgreSqlNetworkIsolation) { + name: postgreSqlPrivateDnsZoneName + location: 'global' + tags: deploymentTags +} + +resource postgreSqlPrivateDnsZoneVnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = if (deployPostgreSql && postgreSqlNetworkIsolation && deployPostgreSqlPrivateDnsLink) { + name: '${postgreSqlPrivateDnsZone.name}/${effectivePostgreSqlPrivateDnsLinkName}' + location: 'global' + properties: { + virtualNetwork: { + id: effectiveVnetResourceId + } + registrationEnabled: false + } +} + +var postgreSqlPrivateEndpoints = postgreSqlNetworkIsolation ? [ + { + name: postgreSqlPrivateEndpointName + subnetResourceId: '${effectiveVnetResourceId}/subnets/${peSubnetName}' + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: postgreSqlPrivateDnsZone.id + } + ] + } + } +] : [] + +module postgreSqlFlexibleServer 'br/public:avm/res/db-for-postgre-sql/flexible-server:0.15.2' = if (deployPostgreSql) { + name: 'postgresql-flexible' + params: { + availabilityZone: postgreSqlAvailabilityZone + highAvailability: postgreSqlHighAvailability + highAvailabilityZone: postgreSqlHighAvailabilityZone + name: postgreSqlServerName + skuName: postgreSqlSkuName + tier: postgreSqlTier + administratorLogin: postgreSqlAdminLogin + administratorLoginPassword: postgreSqlAdminPassword + managedIdentities: { + systemAssigned: true + } + publicNetworkAccess: postgreSqlNetworkIsolation ? 'Disabled' : 'Enabled' + version: postgreSqlVersion + storageSizeGB: postgreSqlStorageSizeGB + privateEndpoints: postgreSqlPrivateEndpoints + tags: deploymentTags + } +} + +resource postgreSqlAdminSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = if (deployPostgreSql && enablePostgreSqlKeyVaultSecret) { + name: '${keyVault.name}/${postgreSqlAdminSecretName}' + properties: { + value: postgreSqlAdminPassword + } +} + module fabricCapacity 'modules/fabric-capacity.bicep' = if (effectiveFabricCapacityMode == 'create') { name: 'fabric-capacity' params: { capacityName: capacityName - location: location + location: effectiveLocation sku: fabricCapacitySku adminMembers: fabricCapacityAdmins tags: deploymentTags @@ -233,14 +406,6 @@ module fabricCapacity 'modules/fabric-capacity.bicep' = if (effectiveFabricCapac // OUTPUTS - Pass through from AI Landing Zone // ======================================== -var effectiveVnetResourceId = useExistingVNet && !empty(existingVnetResourceId) - ? existingVnetResourceId - : resourceId('Microsoft.Network/virtualNetworks', vnetName) - -var effectiveKeyVaultResourceId = !empty(keyVaultResourceId) - ? keyVaultResourceId - : resourceId('Microsoft.KeyVault/vaults', keyVaultName) - var effectiveAiSearchResourceId = !empty(aiSearchResourceId) ? aiSearchResourceId : resourceId('Microsoft.Search/searchServices', searchServiceName) @@ -276,6 +441,16 @@ output fabricCapacityResourceIdOut string = effectiveFabricCapacityResourceId output fabricCapacityName string = effectiveFabricCapacityName output fabricCapacityId string = effectiveFabricCapacityResourceId +// PostgreSQL outputs +output postgreSqlServerNameOut string = deployPostgreSql ? postgreSqlFlexibleServer.outputs.name : '' +output postgreSqlServerResourceId string = deployPostgreSql ? postgreSqlFlexibleServer.outputs.resourceId : '' +output postgreSqlServerFqdn string = deployPostgreSql ? postgreSqlFlexibleServer.outputs.fqdn : '' +output postgreSqlSystemAssignedPrincipalId string = deployPostgreSql ? postgreSqlFlexibleServer.outputs.systemAssignedMIPrincipalId : '' +output postgreSqlAdminSecretName string = deployPostgreSql && enablePostgreSqlKeyVaultSecret ? postgreSqlAdminSecretName : '' +output postgreSqlAdminLoginOut string = deployPostgreSql ? postgreSqlAdminLogin : '' +output postgreSqlFabricUserNameOut string = deployPostgreSql ? postgreSqlFabricUserName : '' +output postgreSqlFabricUserSecretNameOut string = deployPostgreSql && enablePostgreSqlKeyVaultSecret ? postgreSqlFabricUserSecretName : '' + var effectiveFabricWorkspaceName = effectiveFabricWorkspaceMode == 'byo' ? (!empty(fabricWorkspaceName) ? fabricWorkspaceName : (!empty(environmentName) ? 'workspace-${environmentName}' : 'workspace-${baseName}')) : (!empty(environmentName) ? 'workspace-${environmentName}' : 'workspace-${baseName}') diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 12861a7..df755ed 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -34,6 +34,40 @@ param deploymentTags = {} param appConfigLabel = 'ai-lz' param networkIsolation = true +// Coordinate PostgreSQL networking with the overall isolation flag by default. +param postgreSqlNetworkIsolation = networkIsolation +// Skip this if a PostgreSQL private DNS zone is already linked to the VNet. +param deployPostgreSqlPrivateDnsLink = true +// Optional: use an existing VNet link name to avoid conflicts. +param postgreSqlPrivateDnsLinkNameOverride = '' + +// ======================================== +// POSTGRESQL FLEXIBLE SERVER (Optional) +// ======================================== + +var postgreSqlEnvNameLower = toLower(environmentName) +var postgreSqlEnvNameSanitized = replace(replace(replace(replace(replace(replace(replace(postgreSqlEnvNameLower, ' ', '-'), '_', '-'), '.', ''), '/', ''), '\\', ''), ':', ''), ',', '') +var postgreSqlEnvNameTrimmed = substring(postgreSqlEnvNameSanitized, 0, min(50, length(postgreSqlEnvNameSanitized))) +var postgreSqlServerNameBase = !empty(postgreSqlEnvNameTrimmed) + ? 'pg-${postgreSqlEnvNameTrimmed}' + : 'pg${uniqueString(readEnvironmentVariable('AZURE_SUBSCRIPTION_ID', ''), environmentName, location)}' + +param deployPostgreSql = true +param postgreSqlServerName = substring(postgreSqlServerNameBase, 0, min(63, length(postgreSqlServerNameBase))) +param postgreSqlAdminLogin = 'pgadmin' +param postgreSqlAdminPassword = readEnvironmentVariable('POSTGRES_ADMIN_PASSWORD', '$(secretOrRandomPassword)') +param enablePostgreSqlKeyVaultSecret = true +param postgreSqlAdminSecretName = 'postgres-admin-password' +param postgreSqlFabricUserName = 'fabric_user' +param postgreSqlFabricUserSecretName = 'postgres-fabric-user-password' +param postgreSqlSkuName = 'Standard_D2s_v3' +param postgreSqlTier = 'GeneralPurpose' +param postgreSqlAvailabilityZone = 1 +param postgreSqlHighAvailability = 'Disabled' +param postgreSqlHighAvailabilityZone = -1 +param postgreSqlVersion = '16' +param postgreSqlStorageSizeGB = 32 + // ======================================== // FEATURE TOGGLES // ======================================== @@ -44,7 +78,7 @@ param deployAiFoundrySubnet = true param deployAppConfig = true param deployKeyVault = true param deployVmKeyVault = readEnvironmentVariable('DEPLOY_VM_KEY_VAULT', 'true') == 'true' -param deployLogAnalytics = true +param deployLogAnalytics = false param deployAppInsights = true param deploySearchService = true param deployStorageAccount = true diff --git a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/materialize_document_folders.ps1 b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/materialize_document_folders.ps1 index e4bc979..f47d157 100644 --- a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/materialize_document_folders.ps1 +++ b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/materialize_document_folders.ps1 @@ -75,14 +75,14 @@ if (-not $WorkspaceId) { } if (-not $WorkspaceId) { - Write-Warning "[materialize] WorkspaceId not provided and could not be resolved from environment; skipping." + Write-Host "[materialize] WorkspaceId not provided and could not be resolved; skipping." exit 0 } # Get access token for OneLake (uses Storage scope) $storageToken = Get-SecureApiToken -Resource $SecureApiResources.Storage -Description "Storage" if (!$storageToken) { - Write-Warning "[materialize] Failed to get storage access token; skipping." + Write-Host "[materialize] Failed to get storage access token; skipping." exit 0 } @@ -94,23 +94,27 @@ Write-Host "[materialize] Getting lakehouse ID for '$LakehouseName'..." # Create secure headers $fabricHeaders = New-SecureHeaders -Token $fabricToken -AdditionalHeaders @{ 'Content-Type' = 'application/json' } -try { - $lakehousesResponse = Invoke-SecureRestMethod -Uri "https://api.fabric.microsoft.com/v1/workspaces/$WorkspaceId/lakehouses" -Headers $fabricHeaders -Method Get - $lakehouse = $lakehousesResponse.value | Where-Object { $_.displayName -eq $LakehouseName } - - if (!$lakehouse) { - Write-Warning "[materialize] Lakehouse '$LakehouseName' not found in workspace; skipping." - exit 0 - } - - $lakehouseId = $lakehouse.id - Write-Host "[materialize] Found lakehouse '$LakehouseName' with ID: $lakehouseId" - -} catch { - # Import security module - $SecurityModulePath = Join-Path $PSScriptRoot "../../SecurityModule.ps1" +$lakehouseId = $null +for ($attempt = 1; $attempt -le 6; $attempt++) { + try { + $lakehousesResponse = Invoke-SecureRestMethod -Uri "https://api.fabric.microsoft.com/v1/workspaces/$WorkspaceId/lakehouses" -Headers $fabricHeaders -Method Get + $lakehouse = $lakehousesResponse.value | Where-Object { $_.displayName -eq $LakehouseName } | Select-Object -First 1 + if ($lakehouse) { + $lakehouseId = $lakehouse.id + break + } + } catch { } + + Start-Sleep -Seconds (5 * $attempt) } +if (-not $lakehouseId) { + Write-Host "[materialize] Lakehouse '$LakehouseName' not found yet; skipping." + exit 0 +} + +Write-Host "[materialize] Found lakehouse '$LakehouseName' with ID: $lakehouseId" + # Create secure headers for storage access $storageHeaders = New-SecureHeaders -Token $storageToken @@ -122,6 +126,16 @@ $onelakeHeaders = $storageHeaders + @{ # Base URI for OneLake access $baseUri = "https://onelake.dfs.fabric.microsoft.com/$WorkspaceId/$lakehouseId" +# Skip if structure already exists +try { + $checkUri = "$baseUri/Files/documents" + "?resource=filesystem&recursive=true" + $checkResponse = Invoke-SecureRestMethod -Uri $checkUri -Headers $onelakeHeaders -Method GET + if ($checkResponse.paths) { + Write-Host "[materialize] Folder structure already present; skipping." + exit 0 + } +} catch { } + # Define folder structure to create $foldersToCreate = @( "Files/documents", diff --git a/scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 b/scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 new file mode 100644 index 0000000..4883b72 --- /dev/null +++ b/scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 @@ -0,0 +1,228 @@ +<# +.SYNOPSIS + Create a Fabric mirrored database for the provisioned PostgreSQL server. +#> + +[CmdletBinding()] +param( + [string]$MirrorName = $env:FABRIC_POSTGRES_MIRROR_NAME, + [string]$DatabaseName = $env:POSTGRES_DATABASE_NAME, + [string]$ConnectionId = $env:FABRIC_POSTGRES_CONNECTION_ID, + [string]$WorkspaceId = $env:FABRIC_WORKSPACE_ID +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Import security module +$SecurityModulePath = Join-Path $PSScriptRoot "../../SecurityModule.ps1" +. $SecurityModulePath + +function Log([string]$m){ Write-Host "[fabric-pg-mirror] $m" } +function Warn([string]$m){ Write-Warning "[fabric-pg-mirror] $m" } + +# Skip when Fabric workspace is disabled +$fabricWorkspaceMode = $env:fabricWorkspaceMode +if (-not $fabricWorkspaceMode) { $fabricWorkspaceMode = $env:fabricWorkspaceModeOut } +if (-not $fabricWorkspaceMode) { + try { + $azdMode = & azd env get-value fabricWorkspaceModeOut 2>$null + if ($azdMode) { $fabricWorkspaceMode = $azdMode.ToString().Trim() } + } catch {} +} +if (-not $fabricWorkspaceMode -and $env:AZURE_OUTPUTS_JSON) { + try { + $out0 = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop + if ($out0.fabricWorkspaceModeOut -and $out0.fabricWorkspaceModeOut.value) { $fabricWorkspaceMode = $out0.fabricWorkspaceModeOut.value } + elseif ($out0.fabricWorkspaceMode -and $out0.fabricWorkspaceMode.value) { $fabricWorkspaceMode = $out0.fabricWorkspaceMode.value } + } catch {} +} +if ($fabricWorkspaceMode -and $fabricWorkspaceMode.ToString().Trim().ToLowerInvariant() -eq 'none') { + Warn "Fabric workspace mode is 'none'; skipping PostgreSQL mirror." + exit 0 +} + +# Resolve PostgreSQL outputs +$postgreSqlServerResourceId = $null +$postgreSqlServerName = $null +$postgreSqlServerFqdn = $null +$postgreSqlSystemAssignedPrincipalId = $null + +if ($env:AZURE_OUTPUTS_JSON) { + try { + $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop + if ($out.postgreSqlServerResourceId -and $out.postgreSqlServerResourceId.value) { $postgreSqlServerResourceId = $out.postgreSqlServerResourceId.value } + if ($out.postgreSqlServerNameOut -and $out.postgreSqlServerNameOut.value) { $postgreSqlServerName = $out.postgreSqlServerNameOut.value } + if ($out.postgreSqlServerFqdn -and $out.postgreSqlServerFqdn.value) { $postgreSqlServerFqdn = $out.postgreSqlServerFqdn.value } + if ($out.postgreSqlSystemAssignedPrincipalId -and $out.postgreSqlSystemAssignedPrincipalId.value) { $postgreSqlSystemAssignedPrincipalId = $out.postgreSqlSystemAssignedPrincipalId.value } + } catch {} +} + +if (-not $postgreSqlServerResourceId) { + try { + $val = & azd env get-value postgreSqlServerResourceId 2>$null + if ($val) { $postgreSqlServerResourceId = $val.ToString().Trim() } + } catch {} +} +if (-not $postgreSqlServerName) { + try { + $val = & azd env get-value postgreSqlServerNameOut 2>$null + if ($val) { $postgreSqlServerName = $val.ToString().Trim() } + } catch {} +} +if (-not $postgreSqlServerFqdn) { + try { + $val = & azd env get-value postgreSqlServerFqdn 2>$null + if ($val) { $postgreSqlServerFqdn = $val.ToString().Trim() } + } catch {} +} +if (-not $postgreSqlSystemAssignedPrincipalId) { + try { + $val = & azd env get-value postgreSqlSystemAssignedPrincipalId 2>$null + if ($val) { $postgreSqlSystemAssignedPrincipalId = $val.ToString().Trim() } + } catch {} +} + +if (-not $postgreSqlServerResourceId -or [string]::IsNullOrWhiteSpace($postgreSqlServerResourceId)) { + Warn "PostgreSQL server outputs not found; skipping mirror." + exit 0 +} + +# Resolve workspace id if needed +if (-not $WorkspaceId) { + $workspaceEnvPath = Join-Path ([IO.Path]::GetTempPath()) 'fabric_workspace.env' + if (Test-Path $workspaceEnvPath) { + Get-Content $workspaceEnvPath | ForEach-Object { + if ($_ -match '^FABRIC_WORKSPACE_ID=(.+)$') { $WorkspaceId = $Matches[1].Trim() } + } + } +} +if (-not $WorkspaceId -and $env:AZURE_OUTPUTS_JSON) { + try { + $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop + if ($out.fabricWorkspaceIdOut -and $out.fabricWorkspaceIdOut.value) { $WorkspaceId = $out.fabricWorkspaceIdOut.value } + elseif ($out.fabricWorkspaceId -and $out.fabricWorkspaceId.value) { $WorkspaceId = $out.fabricWorkspaceId.value } + } catch {} +} +if (-not $WorkspaceId) { + try { + $val = & azd env get-value fabricWorkspaceIdOut 2>$null + if (-not $val) { $val = & azd env get-value fabricWorkspaceId 2>$null } + if ($val) { $WorkspaceId = $val.ToString().Trim() } + } catch {} +} + +if (-not $WorkspaceId) { Warn "WorkspaceId not resolved; skipping mirror."; exit 0 } + +if (-not $ConnectionId) { + try { + $val = & azd env get-value fabricPostgresConnectionId 2>$null + if ($val) { $ConnectionId = $val.ToString().Trim() } + } catch {} +} + +if (-not $ConnectionId) { + Warn "FABRIC_POSTGRES_CONNECTION_ID not set; create a Fabric connection and rerun." + exit 0 +} + +if (-not $DatabaseName) { $DatabaseName = 'postgres' } +if (-not $MirrorName) { + $envName = $env:AZURE_ENV_NAME + if ([string]::IsNullOrWhiteSpace($envName)) { $envName = 'env' } + $MirrorName = "pg-mirror-$envName" +} + +# Acquire Fabric token +try { $fabricToken = Get-SecureApiToken -Resource $SecureApiResources.Fabric -Description "Fabric" } catch { $fabricToken = $null } +if (-not $fabricToken) { Warn "Cannot acquire Fabric API token; ensure az login."; exit 0 } + +$fabricHeaders = New-SecureHeaders -Token $fabricToken +$apiRoot = 'https://api.fabric.microsoft.com/v1' + +if ($postgreSqlSystemAssignedPrincipalId) { + $roleAssignmentBody = @{ + principal = @{ + id = $postgreSqlSystemAssignedPrincipalId + type = 'ServicePrincipal' + } + role = 'Contributor' + } | ConvertTo-Json -Depth 4 + + try { + Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$WorkspaceId/roleAssignments" -Headers $fabricHeaders -Method Post -Body $roleAssignmentBody | Out-Null + Log "Granted Fabric workspace access to PostgreSQL managed identity: $postgreSqlSystemAssignedPrincipalId" + } catch { + $msg = $_.Exception.Message + if ($msg -like '*409*' -or $msg -like '*already*') { + Log "PostgreSQL managed identity already has Fabric workspace access." + } else { + Warn "Failed to grant workspace access to PostgreSQL managed identity: $msg" + } + } +} else { + Warn "PostgreSQL managed identity principalId not found; skipping Fabric RBAC assignment." +} + +# Skip if mirror already exists +try { + $existing = Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$WorkspaceId/mirroredDatabases" -Headers $fabricHeaders -Method Get -ErrorAction Stop + if ($existing.value) { + $match = $existing.value | Where-Object { $_.displayName -eq $MirrorName } + if ($match) { Log "Mirror already exists: $MirrorName ($($match.id))"; exit 0 } + } +} catch {} + +$mirroringJson = @{ + properties = @{ + source = @{ + type = 'AzurePostgreSql' + typeProperties = @{ + connection = $ConnectionId + database = $DatabaseName + } + } + target = @{ + type = 'MountedRelationalDatabase' + typeProperties = @{ + defaultSchema = 'public' + format = 'Delta' + } + } + } +} + +$mirroringJsonText = $mirroringJson | ConvertTo-Json -Depth 10 +$mirroringPayload = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($mirroringJsonText)) + +$body = @{ + displayName = $MirrorName + description = "Mirrored PostgreSQL database from $postgreSqlServerName" + definition = @{ + parts = @( + @{ + path = 'mirroring.json' + payload = $mirroringPayload + payloadType = 'InlineBase64' + } + ) + } +} + +Log "Creating mirrored database '$MirrorName' in workspace $WorkspaceId" +try { + $resp = Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$WorkspaceId/mirroredDatabases" -Headers $fabricHeaders -Method Post -Body $body -ErrorAction Stop + Log "Created mirror: $($resp.id)" +} catch { + $rawBody = $null + try { + $resp = $_.Exception.Response + if ($resp) { + $reader = New-Object System.IO.StreamReader($resp.GetResponseStream()) + $rawBody = $reader.ReadToEnd() + } + } catch { $rawBody = $null } + Warn "Failed to create mirror: $($_.Exception.Message)" + if ($rawBody) { Warn "Fabric API response body: $rawBody" } + throw +} diff --git a/scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 b/scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 new file mode 100644 index 0000000..5c86542 --- /dev/null +++ b/scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 @@ -0,0 +1,308 @@ +<# +.SYNOPSIS + Prepare PostgreSQL flexible server for Fabric mirroring. +#> + +[CmdletBinding()] +param( + [string]$DatabaseName = $env:POSTGRES_DATABASE_NAME, + [string]$FabricUserName = $env:POSTGRES_FABRIC_USER_NAME, + [string]$EntraRoleName = $env:POSTGRES_FABRIC_ENTRA_ROLE_NAME, + [string]$EntraObjectId = $env:POSTGRES_FABRIC_ENTRA_OBJECT_ID, + [string]$EntraObjectType = $env:POSTGRES_FABRIC_ENTRA_OBJECT_TYPE, + [string]$EntraRequireMfa = $env:POSTGRES_FABRIC_ENTRA_REQUIRE_MFA, + [string]$EnableFabricMirroring = $env:POSTGRES_ENABLE_FABRIC_MIRRORING, + [string]$SkipAzureCdc = $env:POSTGRES_SKIP_AZURE_CDC, + [int]$MirrorCount = 1 +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Import security module +$SecurityModulePath = Join-Path $PSScriptRoot "../../SecurityModule.ps1" +. $SecurityModulePath + +function Log([string]$m){ Write-Host "[pg-mirroring-prep] $m" } +function Warn([string]$m){ Write-Warning "[pg-mirroring-prep] $m" } +function IsTrue([string]$v){ return ($v -and $v.ToString().Trim().ToLowerInvariant() -in @('1','true','yes')) } + +# Resolve PostgreSQL outputs +$postgreSqlServerResourceId = $null +$postgreSqlServerName = $null +$postgreSqlAdminLogin = $null +$postgreSqlAdminSecretName = $null +$postgreSqlFabricUserSecretName = $null +$keyVaultResourceId = $null + +if ($env:AZURE_OUTPUTS_JSON) { + try { + $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop + if ($out.postgreSqlServerResourceId -and $out.postgreSqlServerResourceId.value) { $postgreSqlServerResourceId = $out.postgreSqlServerResourceId.value } + if ($out.postgreSqlServerNameOut -and $out.postgreSqlServerNameOut.value) { $postgreSqlServerName = $out.postgreSqlServerNameOut.value } + if ($out.postgreSqlAdminLoginOut -and $out.postgreSqlAdminLoginOut.value) { $postgreSqlAdminLogin = $out.postgreSqlAdminLoginOut.value } + if ($out.postgreSqlAdminSecretName -and $out.postgreSqlAdminSecretName.value) { $postgreSqlAdminSecretName = $out.postgreSqlAdminSecretName.value } + if ($out.postgreSqlFabricUserSecretNameOut -and $out.postgreSqlFabricUserSecretNameOut.value) { $postgreSqlFabricUserSecretName = $out.postgreSqlFabricUserSecretNameOut.value } + if ($out.keyVaultResourceId -and $out.keyVaultResourceId.value) { $keyVaultResourceId = $out.keyVaultResourceId.value } + if ($out.postgreSqlFabricUserNameOut -and $out.postgreSqlFabricUserNameOut.value -and (-not $FabricUserName)) { $FabricUserName = $out.postgreSqlFabricUserNameOut.value } + } catch {} +} + +function Get-AzdEnvValue([string]$key){ + try { + $val = & azd env get-value $key 2>$null + if ($val -and -not ($val -match '^\s*ERROR:')) { return $val.ToString().Trim() } + } catch {} + return $null +} + +if (-not $postgreSqlServerResourceId) { $postgreSqlServerResourceId = Get-AzdEnvValue 'postgreSqlServerResourceId' } +if (-not $postgreSqlServerName) { $postgreSqlServerName = Get-AzdEnvValue 'postgreSqlServerNameOut' } +if (-not $postgreSqlAdminLogin) { $postgreSqlAdminLogin = Get-AzdEnvValue 'postgreSqlAdminLoginOut' } +if (-not $postgreSqlAdminSecretName) { $postgreSqlAdminSecretName = Get-AzdEnvValue 'postgreSqlAdminSecretName' } +if (-not $postgreSqlFabricUserSecretName) { $postgreSqlFabricUserSecretName = Get-AzdEnvValue 'postgreSqlFabricUserSecretNameOut' } +if (-not $keyVaultResourceId) { $keyVaultResourceId = Get-AzdEnvValue 'keyVaultResourceId' } +if (-not $FabricUserName) { $FabricUserName = Get-AzdEnvValue 'postgreSqlFabricUserNameOut' } + +if (-not $postgreSqlServerResourceId) { + Warn "PostgreSQL server outputs not found; skipping mirroring prep." + exit 0 +} + +if (-not $DatabaseName) { $DatabaseName = 'postgres' } +if (-not $FabricUserName) { $FabricUserName = 'fabric_user' } +if ($EntraRoleName) { $FabricUserName = $EntraRoleName } +if ([string]::IsNullOrWhiteSpace($FabricUserName) -or ($FabricUserName -notmatch '^[a-zA-Z0-9_]+$')) { + if (-not $EntraRoleName) { + Warn "Invalid Fabric user name '$FabricUserName'. Use only letters, numbers, and underscore." + exit 1 + } +} + +$enableFabricMirroring = if ($EnableFabricMirroring) { IsTrue $EnableFabricMirroring } else { $true } +$skipAzureCdc = IsTrue $SkipAzureCdc + +# Parse resource ID +$parts = $postgreSqlServerResourceId.Split('/', [System.StringSplitOptions]::RemoveEmptyEntries) +if ($parts.Length -lt 8) { Warn "Invalid PostgreSQL resource ID."; exit 1 } +$subscriptionId = $parts[1] +$resourceGroup = $parts[3] +if (-not $postgreSqlServerName) { $postgreSqlServerName = $parts[7] } + +# Resolve Key Vault name +$keyVaultName = $null +if ($keyVaultResourceId) { + $kvParts = $keyVaultResourceId.Split('/', [System.StringSplitOptions]::RemoveEmptyEntries) + if ($kvParts.Length -ge 8) { $keyVaultName = $kvParts[7] } +} + +function Test-KeyVaultAccess([string]$vaultName) { + try { + $null = az keyvault secret list --vault-name $vaultName --maxresults 1 --query "[0].id" -o tsv 2>$null + return $true + } catch { + return $false + } +} + +function Invoke-AzCli([string[]]$Args) { + $azCmd = $null + try { $azCmd = (Get-Command az -ErrorAction Stop).Source } catch { $azCmd = $null } + if ($azCmd -and $azCmd.ToLowerInvariant().EndsWith('.cmd')) { + $cliRoot = Split-Path (Split-Path $azCmd -Parent) -Parent + $pythonExe = Join-Path $cliRoot 'python.exe' + if (Test-Path $pythonExe) { + & $pythonExe -m azure.cli @Args + return + } + } + & az @Args +} + +# Fetch admin password from Key Vault or environment +$adminPassword = $null +if ($keyVaultName -and $postgreSqlAdminSecretName) { + try { + $adminPassword = az keyvault secret show --vault-name $keyVaultName --name $postgreSqlAdminSecretName --query value -o tsv 2>$null + } catch {} +} +if (-not $adminPassword) { $adminPassword = $env:POSTGRES_ADMIN_PASSWORD } +if (-not $adminPassword) { + if (-not $keyVaultName) { + Warn "PostgreSQL admin password not found (Key Vault or env)." + exit 1 + } + if (-not (Test-KeyVaultAccess $keyVaultName)) { + Warn "Key Vault '$keyVaultName' is not reachable. Run from a VNet-connected host or enable trusted access, then retry." + exit 1 + } + + $bytes = New-Object byte[] 32 + [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) + $adminPassword = ([Convert]::ToBase64String($bytes).TrimEnd('=')) + 'a!' + + Log "Resetting PostgreSQL admin password and storing in Key Vault..." + az postgres flexible-server update -g $resourceGroup -n $postgreSqlServerName --admin-password "$adminPassword" --subscription $subscriptionId 1>$null + az keyvault secret set --vault-name $keyVaultName --name $postgreSqlAdminSecretName --value $adminPassword 1>$null +} + +# Ensure Fabric role password in Key Vault +if (-not $postgreSqlFabricUserSecretName) { $postgreSqlFabricUserSecretName = 'postgres-fabric-user-password' } +$fabricUserPassword = $null +if ($keyVaultName) { + try { + $fabricUserPassword = az keyvault secret show --vault-name $keyVaultName --name $postgreSqlFabricUserSecretName --query value -o tsv 2>$null + } catch {} +} +if (-not $fabricUserPassword) { + $bytes = New-Object byte[] 32 + [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) + $fabricUserPassword = ([Convert]::ToBase64String($bytes).TrimEnd('=')) + 'a!' + if ($keyVaultName) { + try { + az keyvault secret set --vault-name $keyVaultName --name $postgreSqlFabricUserSecretName --value $fabricUserPassword 1>$null + Log "Stored Fabric user password in Key Vault: $postgreSqlFabricUserSecretName" + } catch { + Warn "Failed to store Fabric user password in Key Vault." + } + } +} + +# Set server parameters for mirroring +$changed = $false +$needsRestart = $false + +function Get-ParamValue([string]$paramName) { + try { + $val = az postgres flexible-server parameter show -g $resourceGroup -s $postgreSqlServerName -n $paramName --query value -o tsv --subscription $subscriptionId 2>$null + return $val + } catch { return $null } +} + +function Get-ParamAllowedValues([string]$paramName) { + try { + $val = az postgres flexible-server parameter show -g $resourceGroup -s $postgreSqlServerName -n $paramName --query "allowedValues" -o tsv --subscription $subscriptionId 2>$null + if ($val) { return ($val -split ',') | ForEach-Object { $_.Trim() } | Where-Object { $_ } } + } catch { } + return @() +} + +function Set-ParamValue([string]$paramName, [string]$value, [bool]$requiresRestart) { + $current = Get-ParamValue $paramName + if ($current -ne $value) { + Log "Setting $paramName to '$value' (was '$current')" + az postgres flexible-server parameter set -g $resourceGroup -s $postgreSqlServerName -n $paramName --value $value --subscription $subscriptionId 1>$null + $script:changed = $true + if ($requiresRestart) { $script:needsRestart = $true } + } +} + +Set-ParamValue -paramName 'wal_level' -value 'logical' -requiresRestart $true + +if ($enableFabricMirroring) { + # Match portal enablement: configure Fabric mirroring flags for the server. + Set-ParamValue -paramName 'azure.fabric_mirror_enabled' -value 'on' -requiresRestart $true + Set-ParamValue -paramName 'azure.mirror_databases' -value $DatabaseName -requiresRestart $true +} + +if ($skipAzureCdc) { + Log "Skipping azure_cdc configuration per POSTGRES_SKIP_AZURE_CDC." +} else { + # Ensure azure_cdc is allowlisted + $extensions = Get-ParamValue 'azure.extensions' + $extensionsAllowed = Get-ParamAllowedValues 'azure.extensions' + if (-not $extensions) { $extensions = '' } + $extList = $extensions -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ } + if ($extensionsAllowed -and -not ($extensionsAllowed -contains 'azure_cdc')) { + Warn "azure_cdc is not available in 'azure.extensions' for this server. Skipping allowlist update." + } else { + if (-not ($extList -contains 'azure_cdc')) { + $extList += 'azure_cdc' + } + Set-ParamValue -paramName 'azure.extensions' -value ($extList -join ',') -requiresRestart $true + } + + # Ensure azure_cdc is preloaded + $preload = Get-ParamValue 'shared_preload_libraries' + $preloadAllowed = Get-ParamAllowedValues 'shared_preload_libraries' + if (-not $preload) { $preload = '' } + $preloadList = $preload -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ } + if ($preloadAllowed -and -not ($preloadAllowed -contains 'azure_cdc')) { + Warn "azure_cdc is not available in 'shared_preload_libraries' for this server. Skipping preload update." + } else { + if (-not ($preloadList -contains 'azure_cdc')) { + $preloadList += 'azure_cdc' + } + Set-ParamValue -paramName 'shared_preload_libraries' -value ($preloadList -join ',') -requiresRestart $true + } +} + +# Increase max_worker_processes by 3 per mirrored database +$maxWorkers = Get-ParamValue 'max_worker_processes' +if ($maxWorkers -and $maxWorkers -as [int]) { + $currentWorkers = [int]$maxWorkers + $targetWorkers = $currentWorkers + (3 * $MirrorCount) + Set-ParamValue -paramName 'max_worker_processes' -value $targetWorkers.ToString() -requiresRestart $true +} + +if ($changed -and $needsRestart) { + Log "Restarting PostgreSQL server to apply mirroring settings..." + az postgres flexible-server restart -g $resourceGroup -n $postgreSqlServerName --subscription $subscriptionId 1>$null +} + +# Configure role and grants +$useEntra = (-not [string]::IsNullOrWhiteSpace($EntraRoleName)) -or (-not [string]::IsNullOrWhiteSpace($EntraObjectId)) +$mfaFlag = if ($EntraRequireMfa -and $EntraRequireMfa.ToLowerInvariant() -eq 'true') { 'true' } else { 'false' } + +if ($useEntra) { + if (-not $EntraRoleName) { + Warn "Entra role name is required when using Entra mapping. Set POSTGRES_FABRIC_ENTRA_ROLE_NAME." + exit 1 + } + if (-not $EntraObjectType) { $EntraObjectType = 'user' } + + $createPrincipalSql = if ($EntraObjectId) { + "select * from pg_catalog.pgaadauth_create_principal_with_oid('$EntraRoleName', '$EntraObjectId', '$EntraObjectType', false, $mfaFlag);" + } else { + "select * from pg_catalog.pgaadauth_create_principal('$EntraRoleName', false, $mfaFlag);" + } + $grantParts = @( + if (-not $skipAzureCdc) { ('GRANT azure_cdc_admin TO "{0}";' -f $EntraRoleName) }, + ('GRANT CREATE ON DATABASE "{0}" TO "{1}";' -f $DatabaseName, $EntraRoleName), + ('GRANT USAGE ON SCHEMA public TO "{0}";' -f $EntraRoleName), + ('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "{0}";' -f $EntraRoleName) + ) | Where-Object { $_ } + $grantSql = if ($grantParts) { [string]::Join(' ', $grantParts) } else { '' } +} else { + $escapedPassword = $fabricUserPassword.Replace("'", "''") + $createRoleSql = ('CREATE ROLE "{0}" CREATEDB CREATEROLE LOGIN REPLICATION PASSWORD ''{1}'';' -f $FabricUserName, $escapedPassword) + $grantParts = @( + if (-not $skipAzureCdc) { ('GRANT azure_cdc_admin TO "{0}";' -f $FabricUserName) }, + ('GRANT CREATE ON DATABASE "{0}" TO "{1}";' -f $DatabaseName, $FabricUserName), + ('GRANT USAGE ON SCHEMA public TO "{0}";' -f $FabricUserName), + ('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "{0}";' -f $FabricUserName) + ) | Where-Object { $_ } + $grantSql = if ($grantParts) { [string]::Join(' ', $grantParts) } else { '' } +} + +Log "Creating/validating Fabric mirroring role in database '$DatabaseName'..." +try { + if ($useEntra) { + Invoke-AzCli @('postgres','flexible-server','execute','-n', $postgreSqlServerName,'-g', $resourceGroup,'-u', $postgreSqlAdminLogin,'-p', $adminPassword,'-d', $DatabaseName,'-q', $createPrincipalSql,'--subscription', $subscriptionId) 1>$null + } else { + try { + Invoke-AzCli @('postgres','flexible-server','execute','-n', $postgreSqlServerName,'-g', $resourceGroup,'-u', $postgreSqlAdminLogin,'-p', $adminPassword,'-d', $DatabaseName,'-q', $createRoleSql,'--subscription', $subscriptionId) 1>$null + } catch { + $msg = $_.Exception.Message + if ($msg -notmatch 'already exists') { throw } + Log "Fabric role already exists; continuing." + } + } + if ($grantSql) { + Invoke-AzCli @('postgres','flexible-server','execute','-n', $postgreSqlServerName,'-g', $resourceGroup,'-u', $postgreSqlAdminLogin,'-p', $adminPassword,'-d', $DatabaseName,'-q', $grantSql,'--subscription', $subscriptionId) 1>$null + } + Log "Fabric mirroring role configured." +} catch { + Warn "Failed to apply SQL grants. Ensure your machine can reach the server or use a VNet gateway." + throw +} diff --git a/scripts/automationScripts/FabricWorkspace/mirror/run_postgresql_mirroring_prep_with_public_access.ps1 b/scripts/automationScripts/FabricWorkspace/mirror/run_postgresql_mirroring_prep_with_public_access.ps1 new file mode 100644 index 0000000..623ca09 --- /dev/null +++ b/scripts/automationScripts/FabricWorkspace/mirror/run_postgresql_mirroring_prep_with_public_access.ps1 @@ -0,0 +1,75 @@ +<# +.SYNOPSIS + Temporarily enables public access for Key Vault and PostgreSQL, runs mirroring prep, + then restores original network settings. +#> + +[CmdletBinding()] +param() + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Log([string]$m){ Write-Host "[pg-mirroring-prep-wrapper] $m" } +function Warn([string]$m){ Write-Warning "[pg-mirroring-prep-wrapper] $m" } + +$rg = $env:AZURE_RESOURCE_GROUP +if (-not $rg) { $rg = 'rg-dev030826' } + +function Get-AzdEnvValue([string]$key){ + try { + $val = & azd env get-value $key 2>$null + if ($val -and -not ($val -match '^\s*ERROR:')) { return $val.ToString().Trim() } + } catch {} + return $null +} + +$kvResourceId = $null +if ($env:AZURE_OUTPUTS_JSON) { + try { + $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop + if ($out.keyVaultResourceId -and $out.keyVaultResourceId.value) { $kvResourceId = $out.keyVaultResourceId.value } + } catch {} +} +if (-not $kvResourceId) { $kvResourceId = Get-AzdEnvValue 'keyVaultResourceId' } + +if (-not $kvResourceId) { throw 'Key Vault resource ID not found.' } +$kvParts = $kvResourceId.Split('/', [System.StringSplitOptions]::RemoveEmptyEntries) +$kvName = $kvParts[7] + +$pgName = $null +if ($env:AZURE_OUTPUTS_JSON) { + try { + $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop + if ($out.postgreSqlServerNameOut -and $out.postgreSqlServerNameOut.value) { $pgName = $out.postgreSqlServerNameOut.value } + } catch {} +} +if (-not $pgName) { $pgName = Get-AzdEnvValue 'postgreSqlServerNameOut' } +if (-not $pgName) { $pgName = 'pg-dev030826' } + +$kvPublic = az keyvault show -g $rg -n $kvName --query "properties.publicNetworkAccess" -o tsv +$kvBypass = az keyvault show -g $rg -n $kvName --query "properties.networkAcls.bypass" -o tsv +$kvEnabledForDeployment = az keyvault show -g $rg -n $kvName --query "properties.enabledForDeployment" -o tsv + +$pgPublic = az postgres flexible-server show -g $rg -n $pgName --query "network.publicNetworkAccess" -o tsv + +Log "Key Vault: $kvName (public: $kvPublic, bypass: $kvBypass, enabledForDeployment: $kvEnabledForDeployment)" +Log "PostgreSQL: $pgName (public: $pgPublic)" + +try { + Log 'Temporarily enabling public access for Key Vault and PostgreSQL.' + az keyvault update -g $rg -n $kvName --public-network-access Enabled --bypass AzureServices 1>$null + az postgres flexible-server update -g $rg -n $pgName --public-access Enabled 1>$null + + $prepScript = Join-Path $PSScriptRoot 'prepare_postgresql_for_mirroring.ps1' + pwsh $prepScript +} finally { + Log 'Restoring original network settings.' + $restoreBypass = $kvBypass + if (-not $restoreBypass) { + $restoreBypass = ($kvEnabledForDeployment -eq 'true') ? 'AzureServices' : 'None' + } + + az postgres flexible-server update -g $rg -n $pgName --public-access $pgPublic 1>$null + az keyvault update -g $rg -n $kvName --public-network-access $kvPublic --bypass $restoreBypass 1>$null +} From 81d391bb93289b060b18f1a23da5e65277b4de73 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Wed, 11 Mar 2026 10:40:37 -0400 Subject: [PATCH 05/22] doc updates and notation update postgresql mirror scripts --- .gitignore | 1 + README.md | 1 + docs/postgresql_mirroring.md | 57 +++++++++++++++++++ infra/main.bicep | 10 +++- infra/main.bicepparam | 4 +- .../mirror/create_postgresql_mirror.ps1 | 14 +++++ 6 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 docs/postgresql_mirroring.md diff --git a/.gitignore b/.gitignore index 66f21bb..faf38e2 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ __pycache__ # Local-only Bicep parameter overrides infra/*.local.bicepparam infra/*.local.bicepparam.json +copy.main.bicepparam diff --git a/README.md b/README.md index a1485c0..4ad3e2b 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,7 @@ Supporting documentation |----------|-------------| | [Deployment Guide](./docs/DeploymentGuide.md) | Complete deployment instructions | | [Post Deployment Steps](./docs/post_deployment_steps.md) | Verify your deployment | +| [PostgreSQL Mirroring](./docs/postgresql_mirroring.md) | Create the Fabric connection and mirror PostgreSQL | | [Parameter Guide](./docs/PARAMETER_GUIDE.md) | Configure deployment parameters | | [Quota Check Guide](./docs/quota_check.md) | Check Azure OpenAI quota availability | diff --git a/docs/postgresql_mirroring.md b/docs/postgresql_mirroring.md new file mode 100644 index 0000000..b38f50e --- /dev/null +++ b/docs/postgresql_mirroring.md @@ -0,0 +1,57 @@ +# PostgreSQL Mirroring to Fabric + +This guide explains how to complete PostgreSQL mirroring in Microsoft Fabric after deployment. + +## Why a Fabric Connection Is Required + +The Fabric mirroring API requires a Fabric "connection" object that stores the PostgreSQL endpoint and credentials. The mirror call only accepts a `connectionId` and database name, so a valid Fabric connection must exist before mirroring can be created. + +## Prerequisites + +- Deployment finished, and PostgreSQL Flexible Server exists. +- Post-provision prep ran (it creates the `fabric_user` role and sets required PostgreSQL flags). +- You can sign in to Fabric (app.fabric.microsoft.com) with access to the workspace. + +## Step 1: Confirm PostgreSQL Details + +Get the PostgreSQL server FQDN and database name: + +- FQDN: from `azd env get-value postgreSqlServerFqdn` +- Database name: `postgres` (default) or your custom DB + +## Step 2: Create the Fabric Connection (UI) + +1. Open the Fabric workspace. +2. Go to **Settings** -> **Manage connections and gateways**. +3. Select **New connection** -> **PostgreSQL**. +4. Enter: + - Server: PostgreSQL FQDN + - Database: your database name + - User: `fabric_user` + - Password: value from Key Vault secret `postgres-fabric-user-password` +5. Save and copy the **Connection ID**. + +## Step 3: Set the Connection ID in azd + +```powershell +azd env set-value fabricPostgresConnectionId "" +azd env set-value POSTGRES_DATABASE_NAME "postgres" +``` + +## Step 4: Create the Mirror + +Run the mirror script: + +```powershell +./scripts/automationScripts/FabricWorkspace/Mirror/create_postgresql_mirror.ps1 +``` + +## Verify + +- In Fabric, a mirrored database named `pg-mirror-` should appear. +- Re-running the script is safe; it will skip if the mirror already exists. + +## Notes + +- The deployment now skips the mirror step until a valid Fabric connection exists, so `azd up` will no longer fail on this step. +- If you rotate passwords, update the Fabric connection in the workspace. diff --git a/infra/main.bicep b/infra/main.bicep index 6409187..ec404f9 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -295,6 +295,8 @@ param postgreSqlVersion string = '16' @description('PostgreSQL storage size in GB.') param postgreSqlStorageSizeGB int = 32 +@description('Generated value used when postgreSqlAdminPassword is left as the placeholder token.') +param generatedPostgreSqlAdminPassword string = newGuid() // ======================================== // FABRIC CAPACITY DEPLOYMENT @@ -327,6 +329,10 @@ var effectiveKeyVaultResourceId = !empty(keyVaultResourceId) ? keyVaultResourceId : resourceId('Microsoft.KeyVault/vaults', keyVaultName) +var effectivePostgreSqlAdminPassword = postgreSqlAdminPassword == '$(secretOrRandomPassword)' + ? '${uniqueString(subscription().id, resourceGroup().id, postgreSqlServerName)}!${replace(generatedPostgreSqlAdminPassword, '-', '')}' + : postgreSqlAdminPassword + resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = { name: last(split(effectiveKeyVaultResourceId, '/')) } @@ -372,7 +378,7 @@ module postgreSqlFlexibleServer 'br/public:avm/res/db-for-postgre-sql/flexible-s skuName: postgreSqlSkuName tier: postgreSqlTier administratorLogin: postgreSqlAdminLogin - administratorLoginPassword: postgreSqlAdminPassword + administratorLoginPassword: effectivePostgreSqlAdminPassword managedIdentities: { systemAssigned: true } @@ -387,7 +393,7 @@ module postgreSqlFlexibleServer 'br/public:avm/res/db-for-postgre-sql/flexible-s resource postgreSqlAdminSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = if (deployPostgreSql && enablePostgreSqlKeyVaultSecret) { name: '${keyVault.name}/${postgreSqlAdminSecretName}' properties: { - value: postgreSqlAdminPassword + value: effectivePostgreSqlAdminPassword } } diff --git a/infra/main.bicepparam b/infra/main.bicepparam index df755ed..9e9249a 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -74,7 +74,7 @@ param postgreSqlStorageSizeGB = 32 param deployGroundingWithBing = false param deployAiFoundry = true -param deployAiFoundrySubnet = true +param deployAiFoundrySubnet = false param deployAppConfig = true param deployKeyVault = true param deployVmKeyVault = readEnvironmentVariable('DEPLOY_VM_KEY_VAULT', 'true') == 'true' @@ -93,7 +93,7 @@ param sideBySideDeploy = readEnvironmentVariable('SIDE_BY_SIDE', 'true') == 'tru param deploySoftware = false param deployApim = false param deployAfProject = true -param deployAAfAgentSvc = true +param deployAAfAgentSvc = false param enableAgenticRetrieval = readEnvironmentVariable('ENABLE_AGENTIC_RETRIEVAL', 'false') == 'true' // ======================================== diff --git a/scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 b/scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 index 4883b72..5830064 100644 --- a/scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 +++ b/scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 @@ -140,6 +140,20 @@ if (-not $fabricToken) { Warn "Cannot acquire Fabric API token; ensure az login. $fabricHeaders = New-SecureHeaders -Token $fabricToken $apiRoot = 'https://api.fabric.microsoft.com/v1' +# Guard: skip until the Fabric PostgreSQL connection exists +if ($ConnectionId) { + try { + $connections = Invoke-SecureRestMethod -Uri "$apiRoot/connections" -Headers $fabricHeaders -Method Get -Description "Fabric connections" + $match = $connections.value | Where-Object { $_.id -eq $ConnectionId } + if (-not $match) { + Warn "FABRIC_POSTGRES_CONNECTION_ID not found in Fabric. Create the connection and rerun." + exit 0 + } + } catch { + Warn "Unable to validate Fabric connection ID; continuing with mirror attempt." + } +} + if ($postgreSqlSystemAssignedPrincipalId) { $roleAssignmentBody = @{ principal = @{ From e7df29b4170e7fec5be935995af1d24f77d3eb8b Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Wed, 11 Mar 2026 10:42:04 -0400 Subject: [PATCH 06/22] remove values --- infra/main.bicepparam | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 9e9249a..a08e57c 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -8,7 +8,7 @@ param environmentName = readEnvironmentVariable('AZURE_ENV_NAME', '') param location = readEnvironmentVariable('AZURE_LOCATION', '') param cosmosLocation = readEnvironmentVariable('AZURE_COSMOS_LOCATION', '') // Entra object ID of the identity to grant RBAC (user, group, service principal, or UAI). Set this if Graph lookup is blocked. -param principalId = '0d60355b-dcae-4331-b55f-283d80aabde5' +param principalId = '' param principalType = 'User' // ======================================== @@ -24,7 +24,7 @@ param useExistingVNet = false param existingVnetResourceId = readEnvironmentVariable('EXISTING_VNET_RESOURCE_ID', '') // Optional additional Entra object IDs to grant Search roles. -param aiSearchAdditionalAccessObjectIds = ['0d60355b-dcae-4331-b55f-283d80aabde5'] +param aiSearchAdditionalAccessObjectIds = [''] // ======================================== // OPTIONAL INPUTS (Configuration) @@ -299,14 +299,14 @@ param fabricWorkspaceName = '' // optional (helpful for naming/UX) param fabricCapacitySku = 'F8' // Fabric capacity admin members (email addresses or object IDs). -param fabricCapacityAdmins = ['admin@MngEnv282784.onmicrosoft.com'] +param fabricCapacityAdmins = [''] // ======================================== // PURVIEW PARAMETERS (Optional) // ======================================== // Existing Purview account resource ID (in different subscription if needed). -param purviewAccountResourceId = '/subscriptions/48ab3756-f962-40a8-b0cf-b33ddae744bb/resourceGroups/Governance/providers/Microsoft.Purview/accounts/swantekPurview' +param purviewAccountResourceId = '' // Purview collection name (leave empty to auto-generate from environment name). param purviewCollectionName = '' From 2daac4a1d83d184dbbbf4b9958199bd980a00e93 Mon Sep 17 00:00:00 2001 From: sethsteenken Date: Wed, 11 Mar 2026 13:22:00 -0400 Subject: [PATCH 07/22] Fabric - minor param cleanup, use Fabric API over Power BI API when creating workspace. --- infra/main.bicep | 4 +- infra/main.bicepparam | 11 +-- .../create_fabric_workspace.ps1 | 78 ++++++++++++++----- 3 files changed, 67 insertions(+), 26 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 6409187..afb8e30 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -397,7 +397,9 @@ module fabricCapacity 'modules/fabric-capacity.bicep' = if (effectiveFabricCapac capacityName: capacityName location: effectiveLocation sku: fabricCapacitySku - adminMembers: fabricCapacityAdmins + adminMembers: union(deployer().?userPrincipalName == null + ? [deployer().objectId] + : [deployer().userPrincipalName], fabricCapacityAdmins) tags: deploymentTags } } diff --git a/infra/main.bicepparam b/infra/main.bicepparam index df755ed..4366a6a 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -7,9 +7,6 @@ using './main.bicep' param environmentName = readEnvironmentVariable('AZURE_ENV_NAME', '') param location = readEnvironmentVariable('AZURE_LOCATION', '') param cosmosLocation = readEnvironmentVariable('AZURE_COSMOS_LOCATION', '') -// Entra object ID of the identity to grant RBAC (user, group, service principal, or UAI). Set this if Graph lookup is blocked. -param principalId = '0d60355b-dcae-4331-b55f-283d80aabde5' -param principalType = 'User' // ======================================== // OPTIONAL INPUTS (Existing Resources) @@ -24,7 +21,7 @@ param useExistingVNet = false param existingVnetResourceId = readEnvironmentVariable('EXISTING_VNET_RESOURCE_ID', '') // Optional additional Entra object IDs to grant Search roles. -param aiSearchAdditionalAccessObjectIds = ['0d60355b-dcae-4331-b55f-283d80aabde5'] +param aiSearchAdditionalAccessObjectIds = ['b87eb6d7-7812-43b9-815a-223830f60b44'] // ======================================== // OPTIONAL INPUTS (Configuration) @@ -73,7 +70,7 @@ param postgreSqlStorageSizeGB = 32 // ======================================== param deployGroundingWithBing = false -param deployAiFoundry = true +param deployAiFoundry = false param deployAiFoundrySubnet = true param deployAppConfig = true param deployKeyVault = true @@ -296,10 +293,10 @@ param fabricWorkspaceId = '' // required when fabricWorkspacePreset='byo' param fabricWorkspaceName = '' // optional (helpful for naming/UX) // Fabric capacity SKU. -param fabricCapacitySku = 'F8' +param fabricCapacitySku = 'F2' // Fabric capacity admin members (email addresses or object IDs). -param fabricCapacityAdmins = ['admin@MngEnv282784.onmicrosoft.com'] +param fabricCapacityAdmins = [] // ======================================== // PURVIEW PARAMETERS (Optional) diff --git a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/create_fabric_workspace.ps1 b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/create_fabric_workspace.ps1 index bc867e6..e8630bc 100644 --- a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/create_fabric_workspace.ps1 +++ b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/create_fabric_workspace.ps1 @@ -159,16 +159,16 @@ if ((-not $fabricWorkspaceMode) -or ($fabricWorkspaceMode.ToString().Trim().ToLo # Acquire tokens securely try { - Log "Acquiring Power BI API token..." - $accessToken = Get-SecureApiToken -Resource $SecureApiResources.PowerBI -Description "Power BI" + Log "Acquiring Fabric API token..." + $accessToken = Get-SecureApiToken -Resource $SecureApiResources.Fabric -Description "Fabric" } catch { Fail "Authentication failed: $($_.Exception.Message)" } -$apiRoot = 'https://api.powerbi.com/v1.0/myorg' +$apiRoot = 'https://api.fabric.microsoft.com/v1' # Create secure headers -$powerBIHeaders = New-SecureHeaders -Token $accessToken +$apiHeaders = New-SecureHeaders -Token $accessToken # Resolve capacity GUID if capacity ARM id given $capacityGuid = $null @@ -178,7 +178,7 @@ if ($CapacityId) { Log "Deriving Fabric capacity GUID for name: $capName" try { - $caps = Invoke-SecureRestMethod -Uri "$apiRoot/admin/capacities" -Headers $powerBIHeaders -Method Get + $caps = Invoke-SecureRestMethod -Uri "$apiRoot/capacities" -Headers $apiHeaders -Method Get if ($caps.value) { Log "Searching through $($caps.value.Count) capacities for: '$capName'" @@ -228,7 +228,7 @@ if ($CapacityId) { # Check if workspace exists $workspaceId = $null try { - $groups = Invoke-SecureRestMethod -Uri "$apiRoot/groups?%24top=5000" -Headers $powerBIHeaders -Method Get -ErrorAction Stop + $groups = Invoke-SecureRestMethod -Uri "$apiRoot/groups?%24top=5000" -Headers $apiHeaders -Method Get -ErrorAction Stop $g = $groups.value | Where-Object { $_.name -eq $WorkspaceName } if ($g) { $workspaceId = $g.id } } catch { } @@ -240,7 +240,7 @@ if ($workspaceId) { $currentCapacity = $null $policyBlocked = $false try { - $workspace = Invoke-SecureRestMethod -Uri "$apiRoot/groups/$workspaceId" -Headers $powerBIHeaders -Method Get -ErrorAction Stop + $workspace = Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$workspaceId" -Headers $apiHeaders -Method Get -ErrorAction Stop if ($workspace.capacityId) { $currentCapacity = $workspace.capacityId } } catch { $errMsg = $_.Exception.Message @@ -259,12 +259,12 @@ if ($workspaceId) { } else { Log "Assigning workspace to capacity GUID $capacityGuid" try { - $assignResp = Invoke-SecureWebRequest -Uri "$apiRoot/groups/$workspaceId/AssignToCapacity" -Method Post -Headers ($powerBIHeaders) -Body (@{ capacityId = $capacityGuid } | ConvertTo-Json) -ErrorAction Stop + $assignResp = Invoke-SecureWebRequest -Uri "$apiRoot/workspaces/$workspaceId/assignToCapacity" -Method Post -Headers ($apiHeaders) -Body (@{ capacityId = $capacityGuid } | ConvertTo-Json) -ErrorAction Stop Log "Capacity assignment response: $($assignResp.StatusCode)" # Verify assignment worked Start-Sleep -Seconds 3 - $workspace = Invoke-SecureRestMethod -Uri "$apiRoot/groups/$workspaceId" -Headers $powerBIHeaders -Method Get -ErrorAction Stop + $workspace = Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$workspaceId" -Headers $apiHeaders -Method Get -ErrorAction Stop if ($workspace.capacityId) { Log "Workspace successfully assigned to capacity: $($workspace.capacityId)" } else { @@ -283,15 +283,38 @@ if ($workspaceId) { # assign admins if ($AdminUPNs) { $admins = $AdminUPNs -split ',' | ForEach-Object { $_.Trim() } - try { $currentUsers = Invoke-SecureRestMethod -Uri "$apiRoot/groups/$workspaceId/users" -Headers $powerBIHeaders -Method Get -ErrorAction Stop } catch { $currentUsers = $null } + try { $currentRoleAssignments = Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$workspaceId/roleAssignments" -Headers $apiHeaders -Method Get -ErrorAction Stop } catch { $currentRoleAssignments = $null } foreach ($admin in $admins) { if ([string]::IsNullOrWhiteSpace($admin)) { continue } $hasAdmin = $false - if ($currentUsers -and $currentUsers.value) { $hasAdmin = ($currentUsers.value | Where-Object { $_.identifier -eq $admin -and $_.groupUserAccessRight -eq 'Admin' }) } + if ($currentRoleAssignments -and $currentRoleAssignments.value) { + $hasAdmin = ($currentRoleAssignments.value | Where-Object { + (($_.principal.id -eq $admin) -or ($_.principal.userDetails.userPincipalName -eq $admin)) -and $_.role -eq 'Admin' + }) + } if (-not $hasAdmin) { Log "Adding admin: $admin" try { - Invoke-SecureWebRequest -Uri "$apiRoot/groups/$workspaceId/users" -Method Post -Headers ($powerBIHeaders) -Body (@{ identifier = $admin; groupUserAccessRight = 'Admin'; principalType = 'User' } | ConvertTo-Json) -ErrorAction Stop + $principalId = $admin + if ($admin -like '*@*') { + try { + $userJson = az ad user show --id $admin --output json 2>$null + if ($LASTEXITCODE -eq 0 -and $userJson) { + $userObj = $userJson | ConvertFrom-Json -ErrorAction Stop + if ($userObj.id) { + $principalId = $userObj.id + } else { + Warn "No Entra user id returned for '$admin'." + } + } else { + Warn "Unable to resolve Entra user for '$admin' via az ad user show." + } + } catch { + Warn "Failed to resolve principal id for '$admin': $($_)" + } + } + + Invoke-SecureWebRequest -Uri "$apiRoot/workspaces/$workspaceId/roleAssignments" -Method Post -Headers ($apiHeaders) -Body (@{ principal = @{ id = $pincipalId; type = 'User' }; role = 'Admin' } | ConvertTo-Json) -ErrorAction Stop } catch { Warn "Failed to add $($admin): $($_)" } } else { Log "Admin already present: $admin" } } @@ -309,9 +332,9 @@ if ($workspaceId) { # Create workspace Log "Creating Fabric workspace '$WorkspaceName'..." -$createPayload = @{ name = $WorkspaceName; type = 'Workspace' } | ConvertTo-Json -Depth 4 +$createPayload = @{ displayName = $WorkspaceName } | ConvertTo-Json -Depth 4 try { - $resp = Invoke-SecureWebRequest -Uri "$apiRoot/groups?workspaceV2=true" -Method Post -Headers $powerBIHeaders -Body $createPayload -ErrorAction Stop + $resp = Invoke-SecureWebRequest -Uri "$apiRoot/workspaces" -Method Post -Headers $apiHeaders -Body $createPayload -ErrorAction Stop $body = $resp.Content | ConvertFrom-Json -ErrorAction SilentlyContinue $workspaceId = $body.id Log "Created workspace id: $workspaceId" @@ -321,12 +344,12 @@ try { if ($capacityGuid) { try { Log "Assigning workspace to capacity GUID: $capacityGuid" - $assignResp = Invoke-SecureWebRequest -Uri "$apiRoot/groups/$workspaceId/AssignToCapacity" -Method Post -Headers ($powerBIHeaders) -Body (@{ capacityId = $capacityGuid } | ConvertTo-Json) -ErrorAction Stop + $assignResp = Invoke-SecureWebRequest -Uri "$apiRoot/workspaces/$workspaceId/assignToCapacity" -Method Post -Headers ($apiHeaders) -Body (@{ capacityId = $capacityGuid } | ConvertTo-Json) -ErrorAction Stop Log "Capacity assignment response: $($assignResp.StatusCode)" # Verify assignment worked Start-Sleep -Seconds 3 - $workspace = Invoke-SecureRestMethod -Uri "$apiRoot/groups/$workspaceId" -Headers $powerBIHeaders -Method Get -ErrorAction Stop + $workspace = Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$workspaceId" -Headers $apiHeaders -Method Get -ErrorAction Stop if ($workspace.capacityId) { Log "Workspace successfully assigned to capacity: $($workspace.capacityId)" } else { @@ -340,9 +363,28 @@ if ($AdminUPNs) { $admins = $AdminUPNs -split ',' | ForEach-Object { $_.Trim() } foreach ($admin in $admins) { if ([string]::IsNullOrWhiteSpace($admin)) { continue } + Log "Adding admin: $admin" try { - Invoke-SecureWebRequest -Uri "$apiRoot/groups/$workspaceId/users" -Method Post -Headers ($powerBIHeaders) -Body (@{ identifier = $admin; groupUserAccessRight = 'Admin'; principalType = 'User' } | ConvertTo-Json) -ErrorAction Stop - Log "Added admin: $admin" + $principalId = $admin + if ($admin -like '*@*') { + try { + $userJson = az ad user show --id $admin --output json 2>$null + if ($LASTEXITCODE -eq 0 -and $userJson) { + $userObj = $userJson | ConvertFrom-Json -ErrorAction Stop + if ($userObj.id) { + $principalId = $userObj.id + } else { + Warn "No Entra user id returned for '$admin'." + } + } else { + Warn "Unable to resolve Entra user for '$admin' via az ad user show." + } + } catch { + Warn "Failed to resolve principal id for '$admin': $($_)" + } + } + + Invoke-SecureWebRequest -Uri "$apiRoot/workspaces/$workspaceId/roleAssignments" -Method Post -Headers ($apiHeaders) -Body (@{ principal = @{ id = $pincipalId; type = 'User' }; role = 'Admin' } | ConvertTo-Json) -ErrorAction Stop } catch { Warn "Failed to add $($admin): $($_)" } } } From cf8b27db8b73385090723333ca28c360d76f3629 Mon Sep 17 00:00:00 2001 From: sethsteenken Date: Wed, 11 Mar 2026 15:36:21 -0400 Subject: [PATCH 08/22] Infra - minor param fix --- infra/main.bicep | 6 ++++-- infra/main.bicepparam | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 763e0d8..4b06447 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -344,7 +344,8 @@ resource postgreSqlPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' } resource postgreSqlPrivateDnsZoneVnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = if (deployPostgreSql && postgreSqlNetworkIsolation && deployPostgreSqlPrivateDnsLink) { - name: '${postgreSqlPrivateDnsZone.name}/${effectivePostgreSqlPrivateDnsLinkName}' + name: effectivePostgreSqlPrivateDnsLinkName + parent: postgreSqlPrivateDnsZone location: 'global' properties: { virtualNetwork: { @@ -391,7 +392,8 @@ module postgreSqlFlexibleServer 'br/public:avm/res/db-for-postgre-sql/flexible-s } resource postgreSqlAdminSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = if (deployPostgreSql && enablePostgreSqlKeyVaultSecret) { - name: '${keyVault.name}/${postgreSqlAdminSecretName}' + name: postgreSqlAdminSecretName + parent: keyVault properties: { value: effectivePostgreSqlAdminPassword } diff --git a/infra/main.bicepparam b/infra/main.bicepparam index c8bac9a..f7c2155 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -299,7 +299,7 @@ param fabricWorkspaceName = '' // optional (helpful for naming/UX) param fabricCapacitySku = 'F2' // Fabric capacity admin members (email addresses or object IDs). -param fabricCapacityAdmins = [''] +param fabricCapacityAdmins = [] // ======================================== // PURVIEW PARAMETERS (Optional) From c2fd16a7ff016e7f0f47447eeaabe2803600ff4c Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Thu, 12 Mar 2026 09:51:11 -0400 Subject: [PATCH 09/22] Script modifications, script order of operations and documentation adds --- docs/post_deployment_steps.md | 2 + docs/postgresql_mirroring.md | 105 ++++++++- .../prepare_postgresql_for_mirroring.ps1 | 216 ++++++++++++------ 3 files changed, 244 insertions(+), 79 deletions(-) diff --git a/docs/post_deployment_steps.md b/docs/post_deployment_steps.md index 9239d6e..07e4e40 100644 --- a/docs/post_deployment_steps.md +++ b/docs/post_deployment_steps.md @@ -46,6 +46,8 @@ az fabric capacity resume --capacity-name --resource-group **PostgreSQL Mirroring:** If you enabled PostgreSQL mirroring, follow the detailed steps in [PostgreSQL mirroring](./postgresql_mirroring.md) to finalize the connection and mirror creation. + --- ## 3. Verify AI Foundry Project diff --git a/docs/postgresql_mirroring.md b/docs/postgresql_mirroring.md index b38f50e..8bd5f23 100644 --- a/docs/postgresql_mirroring.md +++ b/docs/postgresql_mirroring.md @@ -2,6 +2,19 @@ This guide explains how to complete PostgreSQL mirroring in Microsoft Fabric after deployment. +## Automation status + +What is automated today: + +- PostgreSQL server prep (roles, grants, seed table, parameters). +- Mirror creation **after** a Fabric connection exists (scripted). + +What is still manual and why: + +- Fabric connection creation is **portal-only** today. The public Fabric API does not currently expose a supported endpoint to create PostgreSQL connections, so the connection must be created in the UI to obtain a `connectionId`. + +Once Fabric exposes a supported API for connection creation, this step can be fully automated. + ## Why a Fabric Connection Is Required The Fabric mirroring API requires a Fabric "connection" object that stores the PostgreSQL endpoint and credentials. The mirror call only accepts a `connectionId` and database name, so a valid Fabric connection must exist before mirroring can be created. @@ -9,8 +22,9 @@ The Fabric mirroring API requires a Fabric "connection" object that stores the P ## Prerequisites - Deployment finished, and PostgreSQL Flexible Server exists. -- Post-provision prep ran (it creates the `fabric_user` role and sets required PostgreSQL flags). - You can sign in to Fabric (app.fabric.microsoft.com) with access to the workspace. +- PostgreSQL authentication mode is **PostgreSQL and Microsoft Entra authentication** (password auth enabled). +- You have access to the Key Vault that stores the PostgreSQL secrets. ## Step 1: Confirm PostgreSQL Details @@ -18,29 +32,88 @@ Get the PostgreSQL server FQDN and database name: - FQDN: from `azd env get-value postgreSqlServerFqdn` - Database name: `postgres` (default) or your custom DB +- Admin login: `pgadmin` +- Fabric login: `fabric_user` (used by Fabric) +- Fabric password: Key Vault secret `postgres-fabric-user-password` + +## Step 2: Prepare the Database (Automated by Default) + +The mirroring prep script configures the server and creates a seed table so Fabric always finds at least one table to replicate. + +### Automated (recommended) + +Run: + +```powershell +pwsh ./scripts/automationScripts/FabricWorkspace/Mirror/prepare_postgresql_for_mirroring.ps1 +``` + +If you are running from a non-VNet host and the Key Vault blocks public access, set: + +```powershell +$env:POSTGRES_TEMP_ENABLE_KV_PUBLIC_ACCESS = 'true' +``` + +What it does now: + +- Creates or validates the `fabric_user` role. +- Ensures PostgreSQL auth modes are enabled (password + Entra). +- Grants `azure_cdc_admin` and database permissions. +- Creates a seed table: `public.fabric_mirror_seed` (owned by the mirroring user when created as `fabric_user`). +- Uses `psql` fallback when `rdbms-connect` cannot install. -## Step 2: Create the Fabric Connection (UI) +### Manual (only if automation fails) + +Connect as `pgadmin@` in the `postgres` database and run: + +```sql +CREATE ROLE "fabric_user" CREATEDB CREATEROLE LOGIN REPLICATION PASSWORD ''; +GRANT azure_cdc_admin TO "fabric_user"; +GRANT CREATE ON DATABASE "postgres" TO "fabric_user"; +GRANT USAGE ON SCHEMA public TO "fabric_user"; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "fabric_user"; + +CREATE TABLE IF NOT EXISTS public.fabric_mirror_seed ( + id bigserial PRIMARY KEY, + created_at timestamptz NOT NULL DEFAULT now() +); +INSERT INTO public.fabric_mirror_seed (created_at) +SELECT now() +WHERE NOT EXISTS (SELECT 1 FROM public.fabric_mirror_seed); + +ALTER TABLE public.fabric_mirror_seed OWNER TO "fabric_user"; +``` + +Update the Key Vault secret after you set the password (automation already does this unless it failed): + +```powershell +az keyvault secret set --vault-name --name postgres-fabric-user-password --value "" +``` + +> Ownership note: Fabric requires the mirror user to own tables. If you create tables as `pgadmin`, change ownership to `fabric_user`. + +## Step 3: Create the Fabric Connection (UI) 1. Open the Fabric workspace. 2. Go to **Settings** -> **Manage connections and gateways**. 3. Select **New connection** -> **PostgreSQL**. 4. Enter: - - Server: PostgreSQL FQDN - - Database: your database name - - User: `fabric_user` + - Server: PostgreSQL FQDN (example: `pg-.postgres.database.azure.com`) + - Database: `postgres` (or your custom DB) + - User: `fabric_user@` (example: `fabric_user@pg-dev031126a`) - Password: value from Key Vault secret `postgres-fabric-user-password` 5. Save and copy the **Connection ID**. -## Step 3: Set the Connection ID in azd +## Step 4: Set the Connection ID in azd ```powershell azd env set-value fabricPostgresConnectionId "" azd env set-value POSTGRES_DATABASE_NAME "postgres" ``` -## Step 4: Create the Mirror +## Step 5: Create the Mirror -Run the mirror script: +Run the mirror script (this is the automation step after the connection exists): ```powershell ./scripts/automationScripts/FabricWorkspace/Mirror/create_postgresql_mirror.ps1 @@ -55,3 +128,19 @@ Run the mirror script: - The deployment now skips the mirror step until a valid Fabric connection exists, so `azd up` will no longer fail on this step. - If you rotate passwords, update the Fabric connection in the workspace. + +## Troubleshooting + +### Invalid credentials + +- Ensure PostgreSQL auth is **PostgreSQL and Microsoft Entra authentication** (password auth enabled). +- Use `fabric_user@` in the Fabric connection. +- Verify the Key Vault secret matches the role password. Automation sets it unless it failed. + +### Must be owner of table + +If Fabric reports `must be owner of table `: + +```sql +ALTER TABLE public.fabric_mirror_seed OWNER TO "fabric_user"; +``` diff --git a/scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 b/scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 index 5c86542..f4360ea 100644 --- a/scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 +++ b/scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 @@ -12,7 +12,9 @@ param( [string]$EntraObjectType = $env:POSTGRES_FABRIC_ENTRA_OBJECT_TYPE, [string]$EntraRequireMfa = $env:POSTGRES_FABRIC_ENTRA_REQUIRE_MFA, [string]$EnableFabricMirroring = $env:POSTGRES_ENABLE_FABRIC_MIRRORING, - [string]$SkipAzureCdc = $env:POSTGRES_SKIP_AZURE_CDC, + [string]$TempEnableKeyVaultPublicAccess = $env:POSTGRES_TEMP_ENABLE_KV_PUBLIC_ACCESS, + [string]$CreateMirrorSeedTable = $env:POSTGRES_CREATE_MIRRORING_SEED_TABLE, + [string]$MirrorSeedTableName = $env:POSTGRES_MIRRORING_SEED_TABLE_NAME, [int]$MirrorCount = 1 ) @@ -27,6 +29,18 @@ function Log([string]$m){ Write-Host "[pg-mirroring-prep] $m" } function Warn([string]$m){ Write-Warning "[pg-mirroring-prep] $m" } function IsTrue([string]$v){ return ($v -and $v.ToString().Trim().ToLowerInvariant() -in @('1','true','yes')) } +function Ensure-AzExtension([string]$name) { + try { + $null = az extension show --name $name 2>$null + return $true + } catch { + Warn "Azure CLI extension '$name' is required but not installed." + Warn "Install: az extension add --name $name" + Warn "If install fails: & \"C:\Program Files\Microsoft SDKs\Azure\CLI2\python.exe\" -m pip install --upgrade pip setuptools wheel" + return $false + } +} + # Resolve PostgreSQL outputs $postgreSqlServerResourceId = $null $postgreSqlServerName = $null @@ -35,6 +49,26 @@ $postgreSqlAdminSecretName = $null $postgreSqlFabricUserSecretName = $null $keyVaultResourceId = $null +function Ensure-Psql([bool]$allowInstall) { + if (Get-Command psql -ErrorAction SilentlyContinue) { + return $true + } + + if (-not $allowInstall) { + Warn "psql not found. Install PostgreSQL client tools or set POSTGRES_ALLOW_PSQL_INSTALL=true." + return $false + } + + Warn "psql not found. Attempting to install PostgreSQL client tools via winget..." + try { + & winget install --id PostgreSQL.PostgreSQL -e --source winget --accept-package-agreements --accept-source-agreements 1>$null + } catch { + Warn "winget failed to install PostgreSQL client tools." + return $false + } + + return [bool](Get-Command psql -ErrorAction SilentlyContinue) +} if ($env:AZURE_OUTPUTS_JSON) { try { $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop @@ -80,7 +114,9 @@ if ([string]::IsNullOrWhiteSpace($FabricUserName) -or ($FabricUserName -notmatch } $enableFabricMirroring = if ($EnableFabricMirroring) { IsTrue $EnableFabricMirroring } else { $true } -$skipAzureCdc = IsTrue $SkipAzureCdc +if (-not $MirrorSeedTableName) { $MirrorSeedTableName = 'fabric_mirror_seed' } +if (-not $CreateMirrorSeedTable) { $CreateMirrorSeedTable = 'true' } +$createMirrorSeedTable = IsTrue $CreateMirrorSeedTable # Parse resource ID $parts = $postgreSqlServerResourceId.Split('/', [System.StringSplitOptions]::RemoveEmptyEntries) @@ -89,6 +125,14 @@ $subscriptionId = $parts[1] $resourceGroup = $parts[3] if (-not $postgreSqlServerName) { $postgreSqlServerName = $parts[7] } +Log "Ensuring PostgreSQL auth modes (password + Entra) are enabled..." +try { + az postgres flexible-server update -g $resourceGroup -n $postgreSqlServerName --subscription $subscriptionId --microsoft-entra-auth Enabled --password-auth Enabled 1>$null +} catch { + Warn "Failed to enable PostgreSQL password/Entra auth modes. Configure the server Authentication settings in the portal and retry." + throw +} + # Resolve Key Vault name $keyVaultName = $null if ($keyVaultResourceId) { @@ -105,6 +149,18 @@ function Test-KeyVaultAccess([string]$vaultName) { } } +$tempEnableKvPublicAccess = IsTrue $TempEnableKeyVaultPublicAccess + +function Set-KeyVaultPublicAccess([string]$vaultName, [string]$state) { + if (-not $vaultName) { return } + try { + az keyvault update -n $vaultName --public-network-access $state 1>$null + } catch { + Warn "Failed to set Key Vault public network access to '$state' for $vaultName." + throw + } +} + function Invoke-AzCli([string[]]$Args) { $azCmd = $null try { $azCmd = (Get-Command az -ErrorAction Stop).Source } catch { $azCmd = $null } @@ -119,53 +175,89 @@ function Invoke-AzCli([string[]]$Args) { & az @Args } -# Fetch admin password from Key Vault or environment -$adminPassword = $null -if ($keyVaultName -and $postgreSqlAdminSecretName) { - try { - $adminPassword = az keyvault secret show --vault-name $keyVaultName --name $postgreSqlAdminSecretName --query value -o tsv 2>$null - } catch {} +function Invoke-PostgresSql([string]$sqlText) { + if ($script:sqlExecutionMode -eq 'az') { + Invoke-AzCli @('postgres','flexible-server','execute','-n', $postgreSqlServerName,'-g', $resourceGroup,'-u', $postgreSqlAdminLogin,'-p', $adminPassword,'-d', $DatabaseName,'-q', $sqlText,'--subscription', $subscriptionId) 1>$null + return + } + + $fqdn = "$postgreSqlServerName.postgres.database.azure.com" + $pgUser = "$postgreSqlAdminLogin@$postgreSqlServerName" + $env:PGPASSWORD = $adminPassword + $pgConn = "host=$fqdn port=5432 dbname=$DatabaseName sslmode=require" + & psql -d $pgConn -U $pgUser -v ON_ERROR_STOP=1 -c $sqlText 1>$null } -if (-not $adminPassword) { $adminPassword = $env:POSTGRES_ADMIN_PASSWORD } -if (-not $adminPassword) { - if (-not $keyVaultName) { - Warn "PostgreSQL admin password not found (Key Vault or env)." + +$hasRdbmsConnect = Ensure-AzExtension 'rdbms-connect' +if (-not $hasRdbmsConnect) { + $allowPsqlInstall = IsTrue ($env:POSTGRES_ALLOW_PSQL_INSTALL) + if (-not $env:POSTGRES_ALLOW_PSQL_INSTALL) { $allowPsqlInstall = $true } + if (-not (Ensure-Psql $allowPsqlInstall)) { exit 1 } - if (-not (Test-KeyVaultAccess $keyVaultName)) { - Warn "Key Vault '$keyVaultName' is not reachable. Run from a VNet-connected host or enable trusted access, then retry." - exit 1 +} + +$script:sqlExecutionMode = if ($hasRdbmsConnect) { 'az' } else { 'psql' } + +# Fetch admin password from Key Vault or environment +$adminPassword = $null +try { + if ($tempEnableKvPublicAccess -and $keyVaultName) { + Log "Temporarily enabling Key Vault public access for secret operations..." + Set-KeyVaultPublicAccess -vaultName $keyVaultName -state 'Enabled' } - $bytes = New-Object byte[] 32 - [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) - $adminPassword = ([Convert]::ToBase64String($bytes).TrimEnd('=')) + 'a!' + if ($keyVaultName -and $postgreSqlAdminSecretName) { + try { + $adminPassword = az keyvault secret show --vault-name $keyVaultName --name $postgreSqlAdminSecretName --query value -o tsv 2>$null + } catch {} + } + if (-not $adminPassword) { $adminPassword = $env:POSTGRES_ADMIN_PASSWORD } + if (-not $adminPassword) { + if (-not $keyVaultName) { + Warn "PostgreSQL admin password not found (Key Vault or env)." + exit 1 + } + if (-not (Test-KeyVaultAccess $keyVaultName)) { + Warn "Key Vault '$keyVaultName' is not reachable. Run from a VNet-connected host or enable trusted access, then retry." + exit 1 + } - Log "Resetting PostgreSQL admin password and storing in Key Vault..." - az postgres flexible-server update -g $resourceGroup -n $postgreSqlServerName --admin-password "$adminPassword" --subscription $subscriptionId 1>$null - az keyvault secret set --vault-name $keyVaultName --name $postgreSqlAdminSecretName --value $adminPassword 1>$null -} + $bytes = New-Object byte[] 32 + [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) + $adminPassword = ([Convert]::ToBase64String($bytes).TrimEnd('=')) + 'a!' -# Ensure Fabric role password in Key Vault -if (-not $postgreSqlFabricUserSecretName) { $postgreSqlFabricUserSecretName = 'postgres-fabric-user-password' } -$fabricUserPassword = $null -if ($keyVaultName) { - try { - $fabricUserPassword = az keyvault secret show --vault-name $keyVaultName --name $postgreSqlFabricUserSecretName --query value -o tsv 2>$null - } catch {} -} -if (-not $fabricUserPassword) { - $bytes = New-Object byte[] 32 - [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) - $fabricUserPassword = ([Convert]::ToBase64String($bytes).TrimEnd('=')) + 'a!' + Log "Resetting PostgreSQL admin password and storing in Key Vault..." + az postgres flexible-server update -g $resourceGroup -n $postgreSqlServerName --admin-password "$adminPassword" --subscription $subscriptionId 1>$null + az keyvault secret set --vault-name $keyVaultName --name $postgreSqlAdminSecretName --value $adminPassword 1>$null + } + + # Ensure Fabric role password in Key Vault + if (-not $postgreSqlFabricUserSecretName) { $postgreSqlFabricUserSecretName = 'postgres-fabric-user-password' } + $fabricUserPassword = $null if ($keyVaultName) { try { - az keyvault secret set --vault-name $keyVaultName --name $postgreSqlFabricUserSecretName --value $fabricUserPassword 1>$null - Log "Stored Fabric user password in Key Vault: $postgreSqlFabricUserSecretName" - } catch { - Warn "Failed to store Fabric user password in Key Vault." + $fabricUserPassword = az keyvault secret show --vault-name $keyVaultName --name $postgreSqlFabricUserSecretName --query value -o tsv 2>$null + } catch {} + } + if (-not $fabricUserPassword) { + $bytes = New-Object byte[] 32 + [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) + $fabricUserPassword = ([Convert]::ToBase64String($bytes).TrimEnd('=')) + 'a!' + if ($keyVaultName) { + try { + az keyvault secret set --vault-name $keyVaultName --name $postgreSqlFabricUserSecretName --value $fabricUserPassword 1>$null + Log "Stored Fabric user password in Key Vault: $postgreSqlFabricUserSecretName" + } catch { + Warn "Failed to store Fabric user password in Key Vault." + } } } +} finally { + if ($tempEnableKvPublicAccess -and $keyVaultName) { + Log "Restoring Key Vault public access to Disabled..." + Set-KeyVaultPublicAccess -vaultName $keyVaultName -state 'Disabled' + } } # Set server parameters for mirroring @@ -205,37 +297,6 @@ if ($enableFabricMirroring) { Set-ParamValue -paramName 'azure.mirror_databases' -value $DatabaseName -requiresRestart $true } -if ($skipAzureCdc) { - Log "Skipping azure_cdc configuration per POSTGRES_SKIP_AZURE_CDC." -} else { - # Ensure azure_cdc is allowlisted - $extensions = Get-ParamValue 'azure.extensions' - $extensionsAllowed = Get-ParamAllowedValues 'azure.extensions' - if (-not $extensions) { $extensions = '' } - $extList = $extensions -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ } - if ($extensionsAllowed -and -not ($extensionsAllowed -contains 'azure_cdc')) { - Warn "azure_cdc is not available in 'azure.extensions' for this server. Skipping allowlist update." - } else { - if (-not ($extList -contains 'azure_cdc')) { - $extList += 'azure_cdc' - } - Set-ParamValue -paramName 'azure.extensions' -value ($extList -join ',') -requiresRestart $true - } - - # Ensure azure_cdc is preloaded - $preload = Get-ParamValue 'shared_preload_libraries' - $preloadAllowed = Get-ParamAllowedValues 'shared_preload_libraries' - if (-not $preload) { $preload = '' } - $preloadList = $preload -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ } - if ($preloadAllowed -and -not ($preloadAllowed -contains 'azure_cdc')) { - Warn "azure_cdc is not available in 'shared_preload_libraries' for this server. Skipping preload update." - } else { - if (-not ($preloadList -contains 'azure_cdc')) { - $preloadList += 'azure_cdc' - } - Set-ParamValue -paramName 'shared_preload_libraries' -value ($preloadList -join ',') -requiresRestart $true - } -} # Increase max_worker_processes by 3 per mirrored database $maxWorkers = Get-ParamValue 'max_worker_processes' @@ -267,7 +328,7 @@ if ($useEntra) { "select * from pg_catalog.pgaadauth_create_principal('$EntraRoleName', false, $mfaFlag);" } $grantParts = @( - if (-not $skipAzureCdc) { ('GRANT azure_cdc_admin TO "{0}";' -f $EntraRoleName) }, + ('GRANT azure_cdc_admin TO "{0}";' -f $EntraRoleName), ('GRANT CREATE ON DATABASE "{0}" TO "{1}";' -f $DatabaseName, $EntraRoleName), ('GRANT USAGE ON SCHEMA public TO "{0}";' -f $EntraRoleName), ('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "{0}";' -f $EntraRoleName) @@ -277,7 +338,7 @@ if ($useEntra) { $escapedPassword = $fabricUserPassword.Replace("'", "''") $createRoleSql = ('CREATE ROLE "{0}" CREATEDB CREATEROLE LOGIN REPLICATION PASSWORD ''{1}'';' -f $FabricUserName, $escapedPassword) $grantParts = @( - if (-not $skipAzureCdc) { ('GRANT azure_cdc_admin TO "{0}";' -f $FabricUserName) }, + ('GRANT azure_cdc_admin TO "{0}";' -f $FabricUserName), ('GRANT CREATE ON DATABASE "{0}" TO "{1}";' -f $DatabaseName, $FabricUserName), ('GRANT USAGE ON SCHEMA public TO "{0}";' -f $FabricUserName), ('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "{0}";' -f $FabricUserName) @@ -301,6 +362,19 @@ try { if ($grantSql) { Invoke-AzCli @('postgres','flexible-server','execute','-n', $postgreSqlServerName,'-g', $resourceGroup,'-u', $postgreSqlAdminLogin,'-p', $adminPassword,'-d', $DatabaseName,'-q', $grantSql,'--subscription', $subscriptionId) 1>$null } + if ($enableFabricMirroring -and $createMirrorSeedTable) { + $seedTableSql = @" +CREATE TABLE IF NOT EXISTS public.\"$MirrorSeedTableName\" ( + id bigserial PRIMARY KEY, + created_at timestamptz NOT NULL DEFAULT now() +); +INSERT INTO public.\"$MirrorSeedTableName\" (created_at) +SELECT now() +WHERE NOT EXISTS (SELECT 1 FROM public.\"$MirrorSeedTableName\"); +"@ + Invoke-PostgresSql $seedTableSql + Log "Ensured mirror seed table exists: public.$MirrorSeedTableName" + } Log "Fabric mirroring role configured." } catch { Warn "Failed to apply SQL grants. Ensure your machine can reach the server or use a VNet gateway." From 531dc53662a1477749bf57fd6bdb0cb0aec0711b Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Thu, 12 Mar 2026 10:59:16 -0400 Subject: [PATCH 10/22] Documentation and postprovision steps updated --- docs/post_deployment_steps.md | 11 ++++++++++- docs/postgresql_mirroring.md | 6 +++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/post_deployment_steps.md b/docs/post_deployment_steps.md index 07e4e40..8b347d9 100644 --- a/docs/post_deployment_steps.md +++ b/docs/post_deployment_steps.md @@ -10,6 +10,7 @@ After running `azd up` or `azd provision` followed by `azd hooks run postprovisi |-----------|---------------|----------------| | Fabric Capacity | Azure Portal → Microsoft Fabric capacities | **Active** (not Paused) | | Fabric Workspace | [app.fabric.microsoft.com](https://app.fabric.microsoft.com) | Workspace visible with 3 lakehouses | +| PostgreSQL Mirroring (if enabled) | Fabric → Workspace → Connections/Mirror | Connection saved and mirror running | | AI Foundry Project | [ai.azure.com](https://ai.azure.com) | Project accessible, models deployed | | AI Search Index | Azure Portal → AI Search → Indexes | `onelake-index` exists with documents | | Purview Scan | Purview Portal → Data Map → Sources | Fabric data source registered | @@ -46,7 +47,15 @@ az fabric capacity resume --capacity-name --resource-group **PostgreSQL Mirroring:** If you enabled PostgreSQL mirroring, follow the detailed steps in [PostgreSQL mirroring](./postgresql_mirroring.md) to finalize the connection and mirror creation. +### PostgreSQL Mirroring (if enabled) + +Use these short steps to create the Fabric connection and enable mirroring. For full details and troubleshooting, see [PostgreSQL mirroring](./postgresql_mirroring.md). + +1. In Fabric, open the workspace, then select **Connections** → **New** → **PostgreSQL**. +2. Use the PostgreSQL server name, database name, and the `fabric_user` credentials stored in Key Vault. +3. Test the connection and **Save**. +4. In the workspace, select **New** → **Data pipeline** → **Mirror database**. +5. Pick the PostgreSQL connection, select the target database, and **Start mirroring**. --- diff --git a/docs/postgresql_mirroring.md b/docs/postgresql_mirroring.md index 8bd5f23..fd8a7ce 100644 --- a/docs/postgresql_mirroring.md +++ b/docs/postgresql_mirroring.md @@ -64,7 +64,7 @@ What it does now: ### Manual (only if automation fails) -Connect as `pgadmin@` in the `postgres` database and run: +Connect as `pgadmin` in the `postgres` database and run: ```sql CREATE ROLE "fabric_user" CREATEDB CREATEROLE LOGIN REPLICATION PASSWORD ''; @@ -100,7 +100,7 @@ az keyvault secret set --vault-name --name postgres-fabric-user- 4. Enter: - Server: PostgreSQL FQDN (example: `pg-.postgres.database.azure.com`) - Database: `postgres` (or your custom DB) - - User: `fabric_user@` (example: `fabric_user@pg-dev031126a`) + - User: `fabric_user` (example: `fabric_user`) - Password: value from Key Vault secret `postgres-fabric-user-password` 5. Save and copy the **Connection ID**. @@ -134,7 +134,7 @@ Run the mirror script (this is the automation step after the connection exists): ### Invalid credentials - Ensure PostgreSQL auth is **PostgreSQL and Microsoft Entra authentication** (password auth enabled). -- Use `fabric_user@` in the Fabric connection. +- Use `fabric_user` in the Fabric connection. - Verify the Key Vault secret matches the role password. Automation sets it unless it failed. ### Must be owner of table From 4ee31df7cdf79217e578f019e8750738ac2add60 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Thu, 12 Mar 2026 18:47:30 -0400 Subject: [PATCH 11/22] Adjust post deployment step md --- docs/post_deployment_steps.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/post_deployment_steps.md b/docs/post_deployment_steps.md index 8b347d9..6c328f6 100644 --- a/docs/post_deployment_steps.md +++ b/docs/post_deployment_steps.md @@ -51,6 +51,8 @@ az fabric capacity resume --capacity-name --resource-group Date: Wed, 18 Mar 2026 19:02:22 -0400 Subject: [PATCH 12/22] update docs and post deployment steps --- README.md | 20 ++++- docs/PARAMETER_GUIDE.md | 12 +++ docs/post_deployment_steps.md | 18 +++-- docs/postgresql_mirroring.md | 76 +++++++++++++----- ...poly-AI-App-in-Prod-Architecture-final.png | Bin 0 -> 99929 bytes 5 files changed, 98 insertions(+), 28 deletions(-) create mode 100644 img/Architecture/Depoly-AI-App-in-Prod-Architecture-final.png diff --git a/README.md b/README.md index 4ad3e2b..fa03ecd 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This accelerator extends the [AI Landing Zone](https://github.com/Azure/ai-landi ### Solution Architecture -| ![Architecture](./img/Architecture/Deploy-AI-App-in-Prod-Architecture_final.png) | +| ![Architecture](./img/Architecture/Depoly-AI-App-in-Prod-Architecture-final.png) | |---| ### Key Components @@ -31,6 +31,7 @@ This accelerator extends the [AI Landing Zone](https://github.com/Azure/ai-landi |-----------|---------| | **Azure AI Foundry** | Unified platform for AI development, testing, and deployment with playground, prompt flow, and publishing | | **Microsoft Fabric** | Data foundation with lakehouses (bronze/silver/gold) for document storage and OneLake indexing | +| **Azure Database for PostgreSQL** | Optional operational data source that can be prepared for Microsoft Fabric mirroring after deployment | | **Azure AI Search** | Retrieval backbone enabling RAG (Retrieval-Augmented Generation) chat experiences | | **Microsoft Purview** | Governance layer for cataloging, scans, and Data Security Posture Management | | **Private Networking** | All traffic secured via private endpoints—no public internet exposure | @@ -61,6 +62,9 @@ This accelerator extends the [AI Landing Zone](https://github.com/Azure/ai-landi - **Integrated data-to-AI pipeline**
Connect Fabric lakehouses → OneLake indexer → AI Search → Foundry playground for grounded chat experiences. + - **PostgreSQL-to-Fabric mirroring path**
+ Provision Azure Database for PostgreSQL, prepare it for Fabric mirroring, create the Fabric connection, and mirror operational data into OneLake for downstream analytics and AI scenarios. + - **Governance built-in**
Microsoft Purview integration for cataloging, scoped scans, and Data Security Posture Management (DSPM). @@ -169,6 +173,7 @@ After deployment, you'll have a complete, enterprise-ready platform that unifies |-------|-----------------|----------------| | **AI Platform** | Azure AI Foundry with OpenAI models, playground, and prompt flow | Build, test, and publish AI chat applications without managing infrastructure | | **Data Foundation** | Microsoft Fabric with bronze/silver/gold lakehouses and OneLake indexing | Store documents at scale and automatically feed them into your AI workflows | +| **Operational Data Mirroring** | Azure Database for PostgreSQL prepared for Fabric mirroring | Bring PostgreSQL operational data into Fabric with a documented connection and mirror setup path | | **Search & Retrieval** | Azure AI Search with vector and semantic search | Enable RAG (Retrieval-Augmented Generation) for grounded, accurate AI responses | | **Governance** | Microsoft Purview with cataloging, scans, and DSPM | Track data lineage, enforce policies, and maintain compliance visibility | | **Security** | Private endpoints, managed identities, RBAC, network isolation | Zero public internet exposure—all traffic stays on the Microsoft backbone | @@ -186,6 +191,9 @@ After deployment, you'll have a complete, enterprise-ready platform that unifies - **Fabric-powered retrieval workflows**
Land documents in a Fabric lakehouse, index them with OneLake + Azure AI Search, and wire the index into the Foundry playground for grounded chat experiences. + - **Fabric mirroring for PostgreSQL** +
Prepare Azure Database for PostgreSQL for Fabric mirroring, create the Fabric connection, and mirror source data into Fabric using the documented post-deployment flow. + - **Governed data and agent operations**
Integrate Microsoft Purview for cataloging, scoped scans, and Data Security Posture Management (DSPM) so compliance teams can monitor the same assets the app consumes. @@ -208,6 +216,16 @@ After deployment, you'll have a complete, enterprise-ready platform that unifies 5. **Publish application** → Deploy the chat experience to end users 6. **Monitor governance** → Review data lineage and security posture in Purview +### PostgreSQL Mirroring Setup + +If you deploy Azure Database for PostgreSQL, the repo also supports a documented Fabric mirroring path after deployment: + +1. Prepare the PostgreSQL server and mirroring user with the provided automation. +2. Create the Fabric PostgreSQL connection using the `fabric_user` credentials stored in Key Vault. +3. Start the mirror in Fabric so PostgreSQL data lands in OneLake. + +See the detailed steps in [docs/postgresql_mirroring.md](./docs/postgresql_mirroring.md) and the shorter checklist in [docs/post_deployment_steps.md](./docs/post_deployment_steps.md). +
diff --git a/docs/PARAMETER_GUIDE.md b/docs/PARAMETER_GUIDE.md index 92fc713..65bf916 100644 --- a/docs/PARAMETER_GUIDE.md +++ b/docs/PARAMETER_GUIDE.md @@ -444,10 +444,22 @@ Use these in `infra/main.bicepparam` when deploying via this repo. `postgreSqlNe ```bicep-params param deployPostgreSql = true param postgreSqlNetworkIsolation = networkIsolation +param postgreSqlMirrorConnectionMode = 'fabricUser' +param postgreSqlAuthConfig = { + activeDirectoryAuth: 'Enabled' + passwordAuth: 'Enabled' +} ``` When `postgreSqlNetworkIsolation` is `false`, PostgreSQL uses public access and does not create private endpoints or private DNS resources. +`postgreSqlAuthConfig` should remain set to both authentication modes enabled if you plan to configure Fabric mirroring after deployment. This ensures the server is created with password authentication available for the `fabric_user` connection instead of relying on a later hook to change the auth mode. + +`postgreSqlMirrorConnectionMode` controls which credential the manual Fabric PostgreSQL connection should use after deployment: + +- `fabricUser` uses the dedicated least-privilege mirroring user and `postgres-fabric-user-password`. This is the production-oriented default. +- `admin` uses the PostgreSQL admin login and `postgres-admin-password`. This is intended for demo automation scenarios where you want to avoid creating a separate mirroring user. + ### Storage Account ```json diff --git a/docs/post_deployment_steps.md b/docs/post_deployment_steps.md index 6c328f6..86e9641 100644 --- a/docs/post_deployment_steps.md +++ b/docs/post_deployment_steps.md @@ -46,18 +46,22 @@ az fabric capacity resume --capacity-name --resource-group "` and rerun the script. --- diff --git a/docs/postgresql_mirroring.md b/docs/postgresql_mirroring.md index fd8a7ce..761a2f1 100644 --- a/docs/postgresql_mirroring.md +++ b/docs/postgresql_mirroring.md @@ -7,13 +7,8 @@ This guide explains how to complete PostgreSQL mirroring in Microsoft Fabric aft What is automated today: - PostgreSQL server prep (roles, grants, seed table, parameters). -- Mirror creation **after** a Fabric connection exists (scripted). - -What is still manual and why: - -- Fabric connection creation is **portal-only** today. The public Fabric API does not currently expose a supported endpoint to create PostgreSQL connections, so the connection must be created in the UI to obtain a `connectionId`. - -Once Fabric exposes a supported API for connection creation, this step can be fully automated. +- Fabric connection creation or reuse for PostgreSQL mirroring. +- Mirror creation after the Fabric connection is resolved. ## Why a Fabric Connection Is Required @@ -25,6 +20,7 @@ The Fabric mirroring API requires a Fabric "connection" object that stores the P - You can sign in to Fabric (app.fabric.microsoft.com) with access to the workspace. - PostgreSQL authentication mode is **PostgreSQL and Microsoft Entra authentication** (password auth enabled). - You have access to the Key Vault that stores the PostgreSQL secrets. +- Decide which connection mode you are using: `fabricUser` (default) or `admin` via `postgreSqlMirrorConnectionMode`. ## Step 1: Confirm PostgreSQL Details @@ -32,9 +28,9 @@ Get the PostgreSQL server FQDN and database name: - FQDN: from `azd env get-value postgreSqlServerFqdn` - Database name: `postgres` (default) or your custom DB -- Admin login: `pgadmin` -- Fabric login: `fabric_user` (used by Fabric) -- Fabric password: Key Vault secret `postgres-fabric-user-password` +- Connection mode: from `azd env get-value postgreSqlMirrorConnectionModeOut` +- Fabric login: from `azd env get-value postgreSqlMirrorConnectionUserNameOut` +- Fabric password secret name: from `azd env get-value postgreSqlMirrorConnectionSecretNameOut` ## Step 2: Prepare the Database (Automated by Default) @@ -56,10 +52,10 @@ $env:POSTGRES_TEMP_ENABLE_KV_PUBLIC_ACCESS = 'true' What it does now: -- Creates or validates the `fabric_user` role. +- Creates or validates the `fabric_user` role when mode is `fabricUser`. - Ensures PostgreSQL auth modes are enabled (password + Entra). - Grants `azure_cdc_admin` and database permissions. -- Creates a seed table: `public.fabric_mirror_seed` (owned by the mirroring user when created as `fabric_user`). +- Creates a seed table: `public.fabric_mirror_seed` (owned by the mirroring identity, either `fabric_user` or `pgadmin`). - Uses `psql` fallback when `rdbms-connect` cannot install. ### Manual (only if automation fails) @@ -92,7 +88,41 @@ az keyvault secret set --vault-name --name postgres-fabric-user- > Ownership note: Fabric requires the mirror user to own tables. If you create tables as `pgadmin`, change ownership to `fabric_user`. -## Step 3: Create the Fabric Connection (UI) +## Step 3: Create or Reuse the Fabric Connection (Automated by Default) + +Run: + +```powershell +pwsh ./scripts/automationScripts/FabricWorkspace/Mirror/create_postgresql_mirror.ps1 +``` + +What the script does now: + +- Reuses `fabricPostgresConnectionId` when it is already stored in `azd`. +- Otherwise resolves the connection login from `postgreSqlMirrorConnectionUserNameOut`. +- Resolves the connection password secret name from `postgreSqlMirrorConnectionSecretNameOut`. +- Reads the chosen secret from Key Vault, creates or reuses the Fabric PostgreSQL connection, and stores the resulting `fabricPostgresConnectionId` back into `azd`. +- Creates the mirrored database after the connection is available. + +If your PostgreSQL server is reachable only through a Fabric VNet data gateway, set the gateway ID before rerunning the script: + +```powershell +azd env set-value fabricPostgresGatewayId "" +``` + +Without `fabricPostgresGatewayId`, the script creates a standard cloud connection. + +### Manual fallback + +If you need to create the Fabric connection manually, do not hardcode `fabric_user`, `pgadmin`, or the secret name. Read the values from the deployment outputs first: + +```powershell +azd env get-value postgreSqlMirrorConnectionModeOut +azd env get-value postgreSqlMirrorConnectionUserNameOut +azd env get-value postgreSqlMirrorConnectionSecretNameOut +``` + +Then in Fabric: 1. Open the Fabric workspace. 2. Go to **Settings** -> **Manage connections and gateways**. @@ -100,11 +130,11 @@ az keyvault secret set --vault-name --name postgres-fabric-user- 4. Enter: - Server: PostgreSQL FQDN (example: `pg-.postgres.database.azure.com`) - Database: `postgres` (or your custom DB) - - User: `fabric_user` (example: `fabric_user`) - - Password: value from Key Vault secret `postgres-fabric-user-password` + - User: the value from `postgreSqlMirrorConnectionUserNameOut` + - Password: the Key Vault secret value stored under `postgreSqlMirrorConnectionSecretNameOut` 5. Save and copy the **Connection ID**. -## Step 4: Set the Connection ID in azd +## Step 4: Persist the Connection ID in azd (only if you created it manually) ```powershell azd env set-value fabricPostgresConnectionId "" @@ -113,7 +143,7 @@ azd env set-value POSTGRES_DATABASE_NAME "postgres" ## Step 5: Create the Mirror -Run the mirror script (this is the automation step after the connection exists): +If the previous script already created the connection automatically, re-running it is safe and idempotent. If you created the connection manually, run it once now: ```powershell ./scripts/automationScripts/FabricWorkspace/Mirror/create_postgresql_mirror.ps1 @@ -126,7 +156,8 @@ Run the mirror script (this is the automation step after the connection exists): ## Notes -- The deployment now skips the mirror step until a valid Fabric connection exists, so `azd up` will no longer fail on this step. +- The deployment now attempts to create or reuse the Fabric PostgreSQL connection automatically before creating the mirror. +- If automatic connection creation cannot reach Key Vault or the source database, the script exits without failing the entire deployment and leaves a manual fallback path. - If you rotate passwords, update the Fabric connection in the workspace. ## Troubleshooting @@ -134,8 +165,13 @@ Run the mirror script (this is the automation step after the connection exists): ### Invalid credentials - Ensure PostgreSQL auth is **PostgreSQL and Microsoft Entra authentication** (password auth enabled). -- Use `fabric_user` in the Fabric connection. -- Verify the Key Vault secret matches the role password. Automation sets it unless it failed. +- Use the login from `postgreSqlMirrorConnectionUserNameOut` in the Fabric connection. +- Verify the Key Vault secret named by `postgreSqlMirrorConnectionSecretNameOut` matches the chosen connection credential. + +### Private networking or gateway-required sources + +- If the PostgreSQL server is private-only, set `fabricPostgresGatewayId` in `azd` before rerunning the script so the connection is created under the Fabric VNet gateway. +- If the gateway ID is not set, the automation uses a shareable cloud connection. ### Must be owner of table diff --git a/img/Architecture/Depoly-AI-App-in-Prod-Architecture-final.png b/img/Architecture/Depoly-AI-App-in-Prod-Architecture-final.png new file mode 100644 index 0000000000000000000000000000000000000000..59f8a337a331d987ac775f80036f87507df7e87b GIT binary patch literal 99929 zcmd43WmHvN^e>E}(v6^WiXb7~4N?+<(s1aI?(PQZ?gkMl>F)0C?(VL;(C2^O7~}o$ ze!FWN@tl43UNQ4G=bGyT$ViDG!Q;R~K|vvji3-U=LA@w|f_feg`y5<3*N}bz{(EL6 zDHUx3GZYJrIFyuVXVgsB>u9;jF*d*%Juv2fIcvtv z#qB-*Z+Y%4(^@xNBV3JYr%^gZ%PwsI+pp2=#vcheTo&Dq)MosmiQg;qy9=P##xIST0xli3tk9yP_`TWUwU~e04>)o>7@4nAT!e>WkUQ z!x^{t8EOriQLxxgL(>f2#&avoN#2JcKu^C8ec3K0>Qkf1JKKZxuo8R9U8@d3adWw%42q za;Eu5X_S_V6OTN~ZZ==IP=7wyV5HEdb>|3~B|s6}(+!H$nzxP1iC1fKc4wq&wR-&t zJ(+AcX=N+r()ytA0CSdH`G zb)QssDR1w@k&BdLF`06=Gx|8kmBPBwFU@7?9U6ivu)R4t`CC0X%Kq#k#$*LW&^%#r zf8oAJw^x}n2n!{L9j}Zmm7_m3Q@u!<$L6NK{cvMr4OWHJyNLY(^U7;Ebm)Ysl!39d zwA9qh7Zqs4=%djz!^3LR;dv1ZZdCR?A;zO_G1v6j>GrzrdQ3kDY6$wV&j>T~vcQ8x zkis!NwbNleV4$5b;$NyXxu6v&my0V-*gPzzaA%We!=_Je4q91RA>e#mYI=APm;uk6 zuoV!A3Gbu33Z8pfGa-;6_AfmXFAit>S0}W+CQ9h}dR$EuNy}}(LR#9J)!znDP za>Jfi-JEKYqvL8pF2$a>{jW7!t>zP zXHU*W5|KfP=e@SR-hhhXg=%SSjYqFV#*bdbrKP&|Bc73zgp|Hd8 zwikm!5*Zn8;(Pwi?hbe{(5l*Bn7OTnR_}Wm7XB0Reqv&4+>-? zYE8#GOf|jgZj(GP!(3ucLqv6Mm^6Tmrp|nr^D&8gc61b)&_rHu;))^_{q8?cQl-E$ zM#scTu^H@edW?VJ)_DI4`^00e?&WE2N83^GH9?li8P2-XV)y#Vc_NFlt=8J{ZRK8s zKD!+IVnK;R5ibwwYwKTgbry0f;iNIQYn3JoZwM(CUt+c%(z5-U&{N(-X$1!DQa|+= zADmi*=k6DYtGHq>ttrk2>C700y z^dGL>jZ|T$!a+6O9APt>HnhF0?nvzV+Bh>qv7zkIKfl^Dzt=+EH~1blToOFKTylE} zES(YW?T ze&KOft74034DI%YQE#9Ov4o3@+nl4MJfxh4;IkFE&TYL6;Rckf z6fhzTz>Z^Nbg0nMgm99iRiDGgYs_MaU^I6K3&RDWws51{wY8fpR2dGdAmny-RBX;W z+-Wj}Qe4aAwu`(_;f%u~CnuOk!>+XLPXIh{Htk`I;pTlQK!{L^cu^Lu>`D?gkzpa> zN)=ta)m(O}-80zRNk>9LO4D1=(>f}6l=u4e>lJJx`64X5VqM06^C$2wX!F2kq&*?E z`WzZuUU$E){f6h8mV?#eD_6e)ho)v;WyHdn2`<#0{%)nSi5A_%`q-mYS1#qg`h$w# z3loaPgnwQqr?bO!rkHrS^S(P*N?&&YFSLxAfWWS9yL`{McYI*70Y_^CKeD)4_o{)iC%q2Yl10G?uv$oZn?J>fEBwT#zN5exS_Hv z{P9#MXu{=!{qgfmVDyK|4ijG66QRDKQSW_ayU9^!)M|Ei+{vQOQ`R~Ls||3+Wk)9= zr^EA01QV1861!+y*UuLhD2{JuLuzIN9<_=TtN&c56L2~R9LbRUqsC>k{7TcAO6Ul{ zMg`ZNhs6lYIIdA{4VN0H0WkJdZfj#be=J$;%r*US z>{bJzka5af>p?jzg}Mz11A{lF-ML8D`m^XL zgoXr<8q1Zk4omSKM`rfy^4;kSe~WN?nRt9iC)_TFl+k2vG9d=xik_}s z1-)dd0)$R;n8ulE(DD?R$ZJEWS?#rgq8kryZ>O5Bf21o*mFy$D5_IMoi08VkY+RmR zdy&O3ZPpn|>J9Ar(VI9^Q`7NpXyUK(%JN?oCu@opA@p*1#9`CU_kld1sxz*nk* z5qVvGU6*p*AobiGR#9Qk}nF{yOQU*am! z7Q`;2zQK3Sj*oE`PCxr5kaL8U;+5=c>PALn^MsV;m18W3xlLzT0)#w6!xrIqCDc#z zU~ez>oo?1BxlnXEd=Tr^6|xJcsHpF@n>^;`RGaTSJo~TnE`X&v?wpySEVFf)d8DUY z3tSvb5r$P0ZIt=?HEVLaSG@O6=^t1BPzqEJURdBf?+%m;CNw^7+1#WesS9F+|nH)uImz1z^Yq7s3;H^%A;`K;e$8(IKrf@4w3RPb! zw3<*F;G)&I#-cF|I<}fPzWe$sEDV*a9G`a;uLJVp2XEbk6b1zsHjV4#$#6Lfat?d1 z1C9eQ@a56U&Qo_%%83mqM*9NC!78(L?bkQToIVqU(ng01&NxjI4Tv#>-b*T-hw1Io z;8oflxl&$5sP4qW{7=$mCeH5#An$HPb*TIzwb(w}AVu}=-&1hqLNjroeZc7K(3VwK=9ylGIQtU2o zFYh`re(Oqo$Loy1)9-+=QB)qM^uS|p@A!MMjGl=a6M}~#9SU3&n4ZAYfvIokn+Dg> zPrV51%iC!5v=N7|riqnndG;UUayr_6D^`!3^e*MfN+xhQ9EA}(Csr2?uHtsBpDcxG zsD|*q?EO{2+dlz1zXLO+ZY;a{NYWu(s91dxlJk!?bFqbkFi$b}Q``EimWuK}(d z5%PcU-4|Z2@IBoC+Lqe?@t0vkR=suS-wHA!w*JBE)ie&};QTjtzDW|%=cI6+m;r5C z;l;vsLxL-OF}jq2Ifgjr-lhMAeAc*HC5j}kBX#U{jHdTj`9fCTGYWqxEB+rMaRe!n z#E6oSiNSs_mc0ytHcZIrW9t3W@|4PWYFnygV&Y_4B|_A{i@E37kh9(=TYi8EgLC#t zCB-w7SyEEc;o5;DU~0adzkV_buAzkHvUG3QTnp&4BT8JH=&n2ZdH$Qv!tc3&IWg?qDu2A~t71Eon(LoVK z7;woN;{|`$>x-6=!inS*ls#L1jYugwZdMB8HPI(hE%Cf~@xnV8&w-mYH!n}@zeg(G zwp-`270-6}4kEo}XhTRbdj{oxyS~2unn6c2g+ux;>D^(VEYixOqGN;FR+^glo|0JZ zPH1o5yhjh}?dkP-4F(6<22>bjiO9m?`F=@7_@??H`R|j3N>=o%8O6`t)jQ_<3&bYh zQ$InWrh8AeA!QsK+~nkBpX{_wF-a*05Xu6uDD98K%grLMc&}kPR~7EOW34OvQs6!1 z@s?51fRv;Z8q4?gjo`$iF90`+DcT`~FaqSb5)-tb9G4hLYW%_-$=ue~)WG|1#%k;1 z(|okGGb1xIi!slr0bT-V@Z#)DvBYv6;3Y3X0dE?Dz9Y2O&}1$&ZbK4*pFe*V%VeTe z7_>KgdynQynFP;1fXF^TB&6&OHwcL9K&k{F^w1PG!}gx&Q>?zrqqFyPUWIl|SnTW^ zECP5>(q)b34_)A+`6iB{xU=<(+`lA7=O=GENXhe9NMBXeR)G&#DKG1KJP2%^i3jjj zT0q_iTw47F(kgIYRsk+1_I@W63~4Z)KAW0)3P9HLx+ytVR?uDNLQ$1V9nW!3vA947 ze|sMtQ$968*C+sc4{RqSo?Y8m=xQu3ZvBJ& zk6_qNzZ&Ohh9ojokdCU54O%3pJbu4md)gs6DCv+>&REX%(m4SJ13e}2a3{(zf}w>y^}c(w-(bIl+#ct?N$T0Z_L#sq7JxnA^cAXG^@-dqoLwkUJS z&GDJT1GNNEoO;(#Se3~vSeAE~dhV^k`A&hw?7F4!T!qP9Z7vzbU}7WDM!O*pg{7+_ zNN>zH`R}0XKU(YJb2;U#|M30(3kxEs%fZ%B?faZy5N(5`g>-0eyI3_ybj^tGgM`xg z(ODd)8G*poSa+m|EBo)sU4R+vjD_q$whg2ri05K`x-3>-A>tV*F=#CDp`f66hlxKe z+TPyY`-xMu-1pA*Lf*RC-ZC9N2=Lh?r{DF!%!LnWJJ>mT7t$yHlpZe}Q4LfqCjkV8 z07kuIU1;*VX(=-V(b$Z*$y${K+f`kh#TO6;vl&_j=?B`qXM34s_#=faz-_lY)rG~m z?dTaajIXcH&Ut0#%3DVL5Fjv4AE>B6mQnfVa2+ZJ2DjnLl>ZSJ)O1zyujS<9}a2Or?F!l$!K1NULR3Vo3qI->a#3Ek5TZHdGo}N&M>o z-EBSDP~DL~vXDGQ#5t3@(#c%g2L~ajGdPv^Q{5Q$@tkHUez|@*X?#_01*#&~Twsxq zBzRL(EN`q06n_#q#91sqM_vP{<^fC(Sb|3&#{8v#T#ojRS!5Ke&`n^9R1gSn(phUh zn>-$uP8TpQ7Dw?QrAxwqPCT{}e_}T5`!As-BAry_&>KrnCxs!e(qyK2eYWb-_|Km% zJ(2UiHqOEVC?!yR@JeJg5rRc}i_XQuB|sZ%c7KwexQ@$8?A_8zdgge4qT35{p%Lhz zu9Z%MT|*zehd|~NTtP0=E{-G?^h6Vwo>scDsOld4OgU!2z{n`_1CP>RdjqWf#`H1; z*qxq0cob>rdUZO5_b>VvTn=4Kbk`eWXgK4-!V;2BfZA6%gQS^dwe#&n!Icck-Xb9m zRv=?M+trx-_fBD9VsB-x(TktIeznjQ>gwmFN@G8*sl;V>0to*V&B5(IL4iVfvJa_V zUSJAV{X65pAa`iTFA*dX7N{4DZcqGHueZ2&Ojh^9Fb#^O9yVYCH$0dQnxddQu~<5R zi~V|RJ9u}pGPiLH7#UAN0bmjvLHK7OFesfpA}=3;gw2C@1%gjNR?cEKUe}_b;voQ3 zDL{4;g+6fEI6XI4WUgW#^vhH1=GIo#6`rnnb@dv)9VA|Xc|vfZHXdHFJAStu9`1t^ zoGR`k7etd{=)}c)?gL~Oq;~K`MPUuC{vbVQfJAFCm~FLy>!R5|0A>H8)1rkzCEMt` zk-xBt)yi47e(qP|KlJ&<;3`Dc8Cwp&Nb$KhZ8!lN`!nfHeb)&iSe&O}i2 z(xy@acD0uv&%4|o;qhcpp`hkow|PKW^P2S~x@kNm|!$UEct?YYGb22eACv(~z+??-9NlArrUrf0_gZ8GW z!Re)sA9terZg+&1mbzNiS-+qldIOs{^JCXhs@wb?&-eglLACpJ@>P|8HKL^YO>bAvp@RYiV}-kr0_BF6(#BO{W|=10`c&QfII^& z*%KtcKZ$qq!gNS-9mrPbrqebL=SJ({)XBL5E8pqV**dmIF>DS$anSH8@8^&e$-iNd zI!@vk`%)#TZ^)#eMk~#cH=W1$t<=u-9xkFd_I>BsW9R!+Z2YVPyD^(^SsYz-e@0@~ z+Hid-(<_?P3|hi*x1*Slu`+3gg2-i*~Y1V8u^1-tMwoNEj!w#g=8G}o55vpu0ri|2ja|t7=UyXlmds?)w zXG8K=3k^qqM0seVtsE$_Va30-;@|g)%9!5_yo408%_y*Ubr<$V2(!MAdMEJh1r_Pg zWjqpv>6^#2D2H*$FY0QkGV8kf`h@J3S;fUNRM9*SSF6(8=TSvPW`cr(!otF~wzdGz zr}DUDjTDBe(M5McsOLLBiWbKiM#{T!Ppv8~zf$Y(OAj@Y;cho%?(lTHfFp#PKIc7% ziQe3z!!Oh_2+=j8edR^DU3o^nOCfdL*$Q*1$`{O_Anh~iknxD)hw4H~EP+6I9_Q&gOd%&yA5M8zqVSiyfKQ`oUa@qzd!Tz#tp>1|# zVyqHst+zT%ySSgf*)p^fRZ@80?qI&c!l$>iq5h)Y%OWFWVxrxB{@j-VTTFFSN-cbR z5=F+wqT|o+-_(?p+dJd=h^^kb=PQ=F0!K*VrI5b##@*GS2 z-r_Cd!GYWQGRU{^V%q{5r+Lc>5iI<@Z3jR!FMrKdu#WEj%fswe{JSg zgYie?y;p-tXUe>~ECfAcG(qN0nsF<&88wxKQATGiqE%y}bp5~YYSQ#%i=Eda5By3I z7a_l}%eHkO_v)1=Vwd`U?-BL1Ed5u|%*4zIOH@B!*r1+UVRA2=DwvyL0`LdkaWVp9rRQF9dzgW~{DH5WB1Sx)N zJkqDq=B%Qk!L+p?mQ>}Fb;^Y81k&F{MZC6qQ_Ws5A3l5lX*o`lnKFNWSWZsPUKXn8 zopx-ncHH;P% za)jsS+RaR{F*_Ic;TtQW{;C8eBTZUZucEI?BH7@pFjf3t9$YYlVUKWM5&xGtB{lbkTqh!ibNxbem<@A$iL z>^!x4!N>WZ*$d7sy5jyVpELFSW`c1ISbHEFIwB2hst+Tu*J~P_aAI7z8TPDm>t7pE zu((q7CGk3!g$Nt3*u1%L>&R+JOggJyVh3SRAiElc-M;C4Wjy&p2{zY@c58ZsW30*C zhUr)47t;2jcRj{A{jW-usB+$@$<|y15k6p=PE($ooB&C~!^3;`Iqsv)&R4t37{*94 zG32)lYlM$iolDJLc`6E}2|EeB;$OOqHz=taxyX znj%K$eE0$G9{VfeGnWn#1qtcLMubO(;Tw;G%mYz4+xhq6*-XE}BffDIhHUH@6o&A> z^362#%}`=&m+_7xg%ek(Kb$I`>7a$Z?bj>){4SBlLH0t_oSxBB+NcWb(9sTMe7GFh$69uzO71nv_ugUcOlEd*LK?$3Ksn1>Jp=hd#y zLeM3V#&r(Hs1f~A?`zAu$Op=APCjYV^pYfKmPra*29)YT_co(&Dh0$FVP-l8=4F22 zZjD!LBlgroZ6^x+gxOCVvihc{DIyw44HZV53_S!We5AoR)1_-VJ&D|9p z!P{7T+OJdg*$9cn)%2>Jq*kJu2<@%xn#``4sD^d`*BOFFUdz@EmG#q{+7>G zed~W){y50ApHaj7txDg$4H-r@^ZtOh?~jrZ-AlnioxIdUVtRUNy4WzLAC_BIn#;Y$ z*0!L!C+g!j7pl?jH7BwvOl$c?Ty zc9Otlwvbg=PZD9Z*wVUIB#hcWY1?t~5VMc3S#TO_hRf>5G*)DI=>=e4`lI=wwp!fK zI;No!WAd1PN}GKX8Gu1eBE^24Znsa4!sIRH_t9!A3^^Hlt*biGf~emdvvnBpHgTr- zQd}GRyigc6y+&NSCsa6EShC;B-Gth*6YCM_Njdo@x8`cihj<*u_454BWCG!7Zrpqp zaJ)#_$@ay^jtx*E>G*n_PbR1)aBZS^FNcJWJ%gCxpVWJ$+BlU`!KiO|69%F(qbDujEwV z9X&NFYOE&~$PY^#h?yuxKl@tSjO9|lp0fIP_%QDEdhUuNWvAf>90XJO%GV2(kIo$0s(c+)?r-mt z>ym}Y_@h{Dzw>?;IYg11V?PgtD+1$Xo3M|WP@XGE3huCFW%Zqe~# z=-G=O4+eg^eb>m9vREdv5+Q32eI`f}u1Nhi3BH0RnFaQ;NJ?Va<_RcX5yLrjzKKij z=pu#=5=EmdiDZiYwuyJ0nYYk4L-}4npoFZQC;@XcD-K>zM)$LKBlMm(DNBVMP2^-7&HY`Qgu;~X+l z7!hOUdiLfDL!1norT=Zo8IymujCY(yP8v|LU! zyGK4Z%8Z*!pM&<^ld+U~$CDd`N1;nlhQFBp#B-zxLpeXCQ+>Aj9y{-+hp;|US6?~> z_iL)^HYJh-$IY(F5+y(8F1MzpS8jIeumg?`9Pb@@lBW5$5(WCJup+>bg%a}ht0R!+3ps6 z+KYt+*=jHExcjWkWo&JI-|V>T}R8KS|awuKOK1EsTl?&p?CL* z8gH~QwKMoZvTSw$k9N8P!14(rwSpiMW0cY7Ith^Szc@g_#) zP9nQUl4<+V^fL|_a?9{RBaa4=mf|0O`7;NO2nT3wJlZo}{Fn|vqS(jgB z!E(>JCFPRvRb@)ii+(~Z9N88g=;{tGvC1NYXEfCRgsyBnNK7_(A4Dn?_0<$>)@FRF zxZj0^#bAER-H>Wh37YHiagEo$LXRu0SyyEeLqpn)oi8(~ zH~jZOoPNFF*4eC83Gc0tJ?oOU*{mgQW40Z2@8Bf2Jke;1KI!5mto&%x5@NpnJTJ6P zB1DQIz+8!iF93_iT>W9dX2i?$K`>bQbna!_P0wQaYIkDUIa%2U$C{Q~uG;hkRo zd#hZY(+hWark7r?79K^W~meBqUllGbS&;d{d<+-ufs#?2j`5)k$)XhvLVE7$!u*-l=v&ZTw~&)8}Xh*`;_N z(g!BQ3H!;etALNA*d+WTAjqoQM3KCxvv&2399`cn5>8p4$Su&H2ch1G!@!TNI z=5tM@ulny1ED;n$=iR!MO|#D2&hN{nR4Ewl*2(rIP{{~ zM%75R96`O+?gs@n@t{-OAC3vS)fKT1(8(R0uk5d^b>1C0N=do*Kop;{h}JN&hWd*I z^C0avapG=~Rx4oLFeUS~m4~sZAw}40Z)nKYI%hUQ88V{dYV<<_;wR<86Sgel$^SN8 z=2uk1fh7cuk{|X9p0YyF&6hJWYz8XPhq$8oP7JQiP|Ne3IqlLlA{)ChyTSZxwMm_i ziAdwYogfS5Oz7{G!mavg*7%I)QRxa}MT`?%}#6~2k)6r^l z1a@~&eTFDpgh}i7363kA+GZEjhIP@12IbI*N+VTAQ_?>d@O;zF#gxRqz5p9Sa344( zmjzDQGtr6B#KHy~;tyg6MH3U=>zmEwp=6sg_Nu+9Mj$S6WBj~5UETVe*n7>AogbB% znVF3UGpOCtp*KBQxq!#flLOF~j*X-!_01E^EYD<1w? z@V%z_kLtHnc05W!SV|opvd-jT_L%Wq_7K6Jkt=+^i{AK%hI4zI7+5d(|59Z76vEa1 zbEwVGl&OKfuYaImP|v1KbS15{>QgX*ozvEmq8ZAv`tKr$J1Hdze9?G9e_z3~=mW(% zXgm@KEUaVIs)z^JRsj(C@}uf$47&MF{_!iBi zBlil+LbSLGUpQP4lEig{zpR&B7|I>5pnbtG$^A8qV9dOAesp}iHncXkv^FubHgTkN zdZDx3bfWbSwl7Bj$9+1dlhZATOy6U|`)c;|^!)nut52^~l{Qw?)a{Frkx?iiZ%TN$ zCPTcZmluq~rNfBaahBi8Hc#@IU7)f{cS&E-=cqgfo*|+5UpQt;#+nzFI6`N1+< zET37@ntjbfT$Rj*_I^=7jVVv;@tM>MF-)ED5vC7aFC4EaGqpTlp;U^y!$0Nj-4A+S zt@1s8oUTQRExs=d8*7(^h%r-_0A_Ls4MOzwIdMtc9sia6j6lDuzhX-dzl} z7o3lCa6z~P{E5ON^PNvfi$YQ2B+x5dp0l(Xv&2S4VPa!XPEOX=)ZAQMogFSV=HzUE z6#8TlWODc1!^6Yt71GJ&q37&nUC-&P)F5UI3n_ji8d>zJXibxGADQVZ1--~?6LQAp zdVKspoC)!Qw+TaZlK%V#ADh*96jUHa42;H88tn=pkpVfSq6a<%J;E7Mfj?EOchYXA z8dN8=k}`PiqcOB#5b<>jM82co^KkJni2r9b;dOhtb8Ar7pbOTUPd#9X^Zetu6t{E8 zLN;0M&BYYt2mQr71-tkxm>pS4=jt{ER%_Oj3!3k44069oJBSJ=CtznN9n3@M4hN}` zPc7zobnOYV@m;6H7V^@!?ulSq79H%}Es#;fH59j%whNb|N5)>6li-}Rv(#CY_EH5u-ehRj<*k0Ofn-QamdAyk%*y!e4 z96cGrsK`bC|gC)+MW>fOe;;MPK{ElRZlmSFI43t?DB{O{-B}sQWp0|m_ z@EI#AIt?B=2VF+=J0oNW2h!>9sVl>6?fawZEGbZC645SnR260o|~<%a8lt6+AC${ znlF(qc}J|8h7veg3$F_?cEaX$6W&lT(ESN^7!1anQEy(`U@>)V+1`l4jhD{w#pqf< z;EoCw8fZb{n18g@BPXN^n+|}eyv9K_p6}N9Zxw%#7Kz+k+ht|>xuZF@01Yi zfYj2jU1j=rga@eRE!6+_?poUFHw6VqgR|0K0fmAxhHmpvGn=om;pgY~jChHFr5ID@ zK?GZ_OhdhI;OV7WtikWsxb688VPs^)J9{70V!|O5;HxVuh~`El|km;rgAGk2W-Uy>I*bN!oebW z1qGnEJYCog+!F;VlOARk*PyhY9_&|uyJP7fw8l?3+x-A~g-=D(A+$=nGlX+zrp-c? zkI!AXpyHz5SVl(1sPdr>=Epk0(5N(TaBkVa;2;VjqVaLg>9;hi26KjE=ZAxvq zJ#3`=EeI50zmSlhLR4H_Tw2=B!lLAE1RfEW<#)T>7COZFSXfqeqN58nb91XOne8Bah5Xfaf5vBVw-trg`SJd8 zb1*eEA>roX_HZznla=Ogd;fmx?)ucw(D2KbFW~Rhk_9q`A5!DP%`WH{^x}K@_RX6v z3sqy|y?wiSx`u`Zu*n8)#0h@YxC$gkOIw@w@y^QG8{^&Qrgb)hW<$l0RarSX3%miy zC!W4UW+NL5KY?<2VU zSzJ6YICx#UO&l!`s-YAJZ9J3VNZ{)a3(lwgSy@^2PM69XwIA=0#H6LA#l@BX=!o(0 zL50}<%FiGFejZ1k%!F~nd02;mkMFWGmNyA;!zX6aA4md^y(1@gdAQz8b-kfnG;R-k zP3U~Y4;0|XTgHa#jYLk{VKgl*tqS95&(*TcR5t=_AQ{q0Z1j|rhUYuuRTL(!t`7;! zMiCJaW8ce_3kr*hIz^bZ+WhxG&g{ybf+818E-Q-+8ymZh6#{|Wp-}@Vg;>pLw0Og{ zcx}l92M0S{9!N?{cPo{#oNUGRh*6sgWFyfyO5$~~Pl z*VorE{9RkanS&|ZP8WOACbJdUSy@TkPPS)T!|mx zdfXmw7v1iTJ6AhH5gIn-&w-(F2JEr1v9W(mNOiDv1%%KBxBU} z_iU;?Dp1crdhL!S-Zg~hEY8{m^7*sz{RptIQw=VhuK0w6iynDY)GRD9gT4+8DOk|&e$Ji0hY~#;Bx{^a&dOv7)%`` zOxMLtvTsX(>5hp-yF@^Og)N@T8nsliE7xd#?koz-O@N)li$hIQq^I{MmA&NjYh5-RY4TV8^juzdk>ogphiJT{Uxwsw%X`xzHSP@px|I*12qk3tV0G&c6<4mG-@^0yAuUQlZ8l6K57bR zsQzF|a!QKnKEW>_?@(0=fUIFCymw)bf;_<2Fxl=YdJ+MzVZoBd49aXB92~AZn#RVy zCK25nwW3@<#NZn|jem{_`2YH~&msbE5*7hLNNPiCIaM?~F~At+Ol*L84K7BMJ#}?; zO-)U=xHOT{QUeLh2#dZe#V@eoKPgU^8)4IKreBpO9 z7=dIxplG0?<#%D>0-*ib_9&29-utsr6rL-0v!iAh!oGM$nApV<-Ojfx#&L;>v%q6t zqnnu>0zWS-B9h8(wGon2iHOf(z1|aD0!~A0VqsvM0yn?x_r_vTqCw~8Z2O7^5C>E` z(9}XJihccx`G}_-)F}^?ldinn>F1MgUQ$Q%BnzM!W#0A?X4(6nFy&?1q)M^IGgo;T z#b-0m^cgHL@$vCZNx-K>SHaW7HG+nD-H&#Ky8=?gHgY9B>5cBkk=kJ)3U9 z5O*E8W=rXg9H(!4FN)W-pSEP&p#au@dEx;Qupa*#>=J=5o|-%DD}c<>V0PBTb~quS zy!=lW6+uoVO)Voc^ST5EkmuS-ruU>aFf_EW(SvUmS~}BCn$+Rd%a_aHEXNwTmt`~n zf*n_+S%ETxckkZ8OQ@WTD?s3aIFx~nxNW?vD=RD8Sd8Bwrva7N_S9GpFK8s5MuD0E zMQ%g1z-fw$iZ&IUo}4s1J~%ztoYM0GWT%{T(e!H|mABDsvEgDcnGdzCP_6c;&k3KW zn7G)ttxc%+dSVufkdTgg#{iV3-i2mZk&%(<=)Wo&eenB&>@3fv4_tfD>S&rN)|=!vz9RivX_MSJZEigSS^sL5s)WZh%b}02}~L z)oT#D5gURIAX4w~TmFbAG-*)*2xFI}H%P73;vDhU4( z2$`Im9D9gd-q#im4g4!FSl~tFMgd1jKm7oBSh>!gUcg|oPz^w4;Fj@OjNdelC$U>y z9Ly(1MU|N^3pAJmZwbBy(5N$%Py}{;a}xv)%-q}nuBXMM?>*c6c}&X*MhiX|&g22@;prpD$2h|deZ>6f2cM6 z!1HtbJI{|KUN@c)!h}IRpyJf;o0)JVKYvDa<+eMR1EAA+W?%qtp>B^NT-^{hl3;Jt!UwynWPYd>rJSn-42tM zmX;${j3VHJ_UEb@IXE1FAp<<%gB1oqpp1x@H+2zrVx_iHvOs>7bXl9FT)EzQdjz;> z8kLHxo12xe=%O0!)tJwTEHw1=#~`pt<#ww0_ahEVQm8PV7dYIGX8`_qn)$T0v;fG< ztl9EHwVl_1VydmPQy;L{Q$PZQd~AeW7IS~&q6Ru3j?R@MK(1KboY6YXfEro`T~1I% z2fA$;0cl-WSO8F$O@qztfol=bj=r4wnS43G4Zx)Uz;=$MV)qc*_T&+oLH zjnF?84v$d}(Ur#>=n0V1n9v~Vv2$!GwAk! z2Oz4dTrM|fAh5Z+^fzm`ex*N>0x)00dhKCD5aE%8nAkGHEakg*8@Z{jE!TXSU_&IG zSkQNGv5}A>^+^p}fFA^CAs{5A8j8tjTKB_NbeM@yA{$bg!&W3WAiV6#JKlK2JczYVfs-v@0L_~zu4Y{H- z?LDXff&2R_CgzPqga$NkV`*QZyj?-gym$uH1fBn@aG+;^;JC~D?{CKp;q&nnV_R68 z|IE+FhyhNw$`0s#2r}}&<5qKY;P{Dfp7~GDyrgGPX{2AjDSUYSukIKB)l;qD?t1%> z^{GP#nC4vM-JP><&aI;Z+=nN<2BRaFWu&>M_3iDQoLD{$=YBy+M>p6vfDDgE^!w?a zCTMrIp};8d^|WWugOIABFN~|tp!kY1YkuKApY#0O$o;=f|MLF-|2~aM*n==&9aky^rgTmip`cFk z3Q5r@80!D>Eoq@0>Jg^60j7@wkB{L@ug70BzSbhfc`%;fSD}K0mOrHwK0)5hr;4F6tTesZzOcx^w}t%) z8cI`5O$|8K5||SMk%Y6iI=Z(y|kKUTlL#O%a~Q&W>BIs<3tmqe^~8DJp(%Rl|a z0Wmbh2M(@DeVdUEr0A~3PKCSBnP5O{2xcpkU{Qu2LH;?lnYE=yYdN80ZEdJ9FHcBe zUlZb@6MCGEb6n z7~dvF=G{9_RyC$8!^E@7OKB<_yhgdb)yb**mR!~#|F>QMe9v9%*Ag2C`?H=cE&Pvn zkREoO1D3GqWwuwB)&^n<6Z`FCKBT1oEQ#OUseS}~w)}odK-Gm_fZ6E#@^1w=JlZ&E z+gMB_dbbo^9Jr9KPwB*v8ZH!H^x=Zo2jr|#bKw4ibW7fHZ)jL^R(#M$u=r%kLZNvK zk|ZIoypXK@g7eWO>+e7jiY(fT?`)hrJlBW&s_H7V#XA~qI)su@>=Rl4CiTT2@TMv% zDlj+qNMI}?>eS0A%r8cbzhY>%c@k`BZyDgZp5|bUjf=A<+}mFNa(6+eJu}inW4zPR z6ODcfR#*GID9Q8C9+6Kjvq_S`DmxdAK=O3h%q^Q*_Nxi=I5L4m(k(fpAaG&{b`> z(C#{fGd1Zio<8gPUcL3veQ4=`tYc#`|S>%KB>WmnKH@J zgn3ws!O}3_t*{_%isPeOKn`V)5^2^;Je|#h4%!v_O{4a3aLM}z29Q|&QBb}~LJr_9 ztc6IKJAb(h$cm=POO4{mxv@<4%)|Kzf>xYy-r#zHck99_6*4xQmXn_>)pgWs&fI6r zY+%6x8zGlp#vX*mj2F)BD^$jkEQ={t!5~8Z$p`m$6zk@@Q*;|-!7?*&V^8+a;e+q= z{**VqJDhgk(KWQbie`Y0Ao?AAhG0TPRUHZ*G4}`KpFa#Ot*rbf7yV|fE32w7;@Opr z**T<9nF%_B^wE0A5%+fdG*F2N5bz%>Q-)Yn6+@N27^^G zZ;qaY)E9Gqn$a}Oh2DxOXJFU5DEd_uX$cvOlI-qsr^>6(68#)9y?5&LZZP##mkvRr zX~ctbVDRQd5AMothMAoYHm0PfkrJ@*;+mc1n*xWPj>_fU}@R`{i@&>@`)7{WxJ-@K=1Ag?eSc764GRFB4x6 z%-23PJeZ2!0p}P%g_!_`Sp4f^q3H=fRD5Pimz$tffVBb+%69BJ(XdeM{|v-tgGp4y zO*oL(k2Zz+=Qtz2MjHCad8Q1 z(-SUkb!_kPOxV+9xLi36(p%yLq>^O2DG=_5Q+UNMcLX5g_l_~#^)o6lm(A7z*Wt)C z(ZVzI@aU@cazm9_{O`xDpeUiztF?E@Q|s*YZEDH3Tdt62!7uG9?fd3!&*sluTS0SR z8xdK)Kw=Z+mnbhy^RKFb#yHH zYUmS#d*nSyyzGvT{CuB}Pi*0o8MBS|LN3;|eZEnqy~&O~_|??55{25V>-B-`MVank zy?XwrA3t`0nteY~>(nR91V$~>MB&ceN(fwoEdj8{YShet!}}R@iyOjq;IRh3fSA4` z!<&EFqaDXEwjBecnho#;0-1Y}S}V5^wrA?e`V;njkeiDOFfNl2pz(Wj+WGyRW3#fe z9RvS8{*jjr|FvfV#3W7E1mOe$BROYYAWE77*O`uzp1b4Kp|i00NtAxGZX^oNdiyH? z#~?(H{zf1AGcFMico-S+y-ea6wE~vk=rs-za3F9OtQAvxTD9G;wPd-(Z0GX&GRgxs zQtLV7BjCp8{JY%O74W_y>6{N7Qo-AxxH$n_2kjH$=|W$C-)&$N z`q*ISrbiUYKYMd~UHq(Yng3!oVm({U&+DKa?yXyC?dr{~_ZL^fjo|4V%X8@X%J9=L zL0NMMlCpeyJ6A^;yX_cee(SRh2Y+o%5r3nRL`jTf4n=y9oxS7X53jS4l1W`|SMtk0 znMT2_X?Ks>@ww&Q=Ej&;{Vg{9Lb~hXeqf2S%AP7yHtrwbZ>nAYT`iKD7~wV2=hKn3 zXU*E;N~I>3zGy6ZwKIP}3{?B}a0*Pq+Q12K(E%IRY6-0LjIzn`E*rfO_4NP(2yECQ zA{|%VJ-{HFZDk4Zr}~jxg9p`yoBhCp34Ra8Q9zMu2H3!VHu2N;?Z+cdpZFce#>h`) zOZas<@6|d%4JzS3?`M0KA5$8G}{*H8WVz*~UE@4W18Yqj81QX4H`qJIw-XxI_!cejOi zMKArauq_LVF1S-8-|CM|BrNdq+QYbXOA=4Vn1vZb%aHn|r07udKmU4bo}pW2Yo3pD z^b%^HKA(xCRrk8@^i1L@%X77ffbsC1r-kjQNb;FTD}(xJQEhcwqtOu?`-I-}zLM^g z-e-n}L=KubSg7ilT)}T=Hx;XG(1CC$ysi&FH(6Xf(q5Y_F@Tw?Lh9OO#Hh#hPEcvTeusz zCF?K(m+NyO__&0)R9;VH5-o4;*2hRpY09zK*jVC3blzlF4K>k<|*TAqM{AXPq(gn;;mCqJNGNN5$vJ+D8^oWQ5bbH0-RU^e0KE&rcC zccd@?j^Ui|Ek#dwN3%HS1n~o#18T*%1ZYoS_y^w~fF;@!r0RmT$o+p9E9K}Ikq+V0 zeSl8@c$zRI9AMGUS1Ldiej8`GaASYNHhjNmj|zY4D$Ji#dsQ_89OC@?D}Vl$D>gq} z8MLBeu&|T!x{Y-LbN|>gXC%_2N+CE3+hZiM&pGU15(Uo#AkKbUPVIt{iLI>-kOKxhJYa)1)2`1bnHct3fBoPD={a$UeR=D-t!E*dZvb#( zeRa3N{Itmfp=ILX9`NmM)&{?Q1IU4e<89!#A4%n~1(O?Suc}W^^^e|uFamLwfQx!# z85s^QkmSM&(1pO;{{k9LTvVJoo%`FpjyFiL{?&X-Pk)Hj-z1MFG=32kNGt@TPqgE* z+4w<-NrLdIe?<$ue_K)$aK0qNJnMx|$}O2=t4`Y-b1d$ji1Wh889I1fL&hkRXZ4%Q zw>&(9GdF;(aJLa@Yb_@C;2CfD$y`!)f(Wg4-#lFWYFj`qIbuJa*!u3K{HV1cV7XEb zncmuzkDH}wOLVfH$iLN?&EHjq8fJ#RlRQVX7&UYQ{bO9jY}`i<(qt} z{<`d=&h)PMfdZMXns91A)Wy^uFSraD!n<9l;qukkxyF@Q0 zWTpzZIE*;)PG1On9#ubzhjjVLzEzqDj=NH#@gEZ#otB$g9#|TWKyW(6wi(CzMI#4L zs+^8z#+K5R!Ud?J(&_+`U_(cnKySk@l|O2(@>JKMDG*nvm<0hU04U=4Gv{;n@7rV! zo3T%p0MaHfd6<&izpR%vdQx=QCP2dZ{w<<&q&5K7ff*4HLhG!%73ApDdT(#aZ$08% z@_(XHNFesRW@Z|I@z*|zcklEgC+p*We_Zikaa1PRLtI;`iaqVXF}8qb-?6l^VicGe ziP5}~0E`jBg=$)C(sRF@Ja#&6`+P&u`576uxmF1>QG<9cJ^2jzdB!+X+_x$F3=FOt z%6ayF4E30Qx`AH%-RyL!s+uZ-jF!DGwG#4%Ba6j`%{4rmJ#CL{_Ff-(6_!yIb^N@x zlpNy+xgnPK^h`tVS;e>UmRk(&2L1+*!U|6_ve{^)J&zt(&40r5sH(L)^hqIlp30_i zmNOGzF(lwB^qbVnEb?N(Ic78U%4xlbJ8E~{YL!RdldY_eF3uHoOR73jY|r~LSoCIJ ze)A`il{!V2>i$eo&IO~|AZTVG``qi}4T{#+C9d^~bu+)`BKtH>xn0i6^s9(LnUpbj7Aqf786U zeDJjfr}cx$FH#at3y;piO%io})Sq^=Xva=2Cx4&=wQ%k9Hc9@F%s0QW+dDYYm^^yc zb;gNn%w=vCGAu)--$c*R&Mr@o5^Z4~HFqvE9}!}K?so6h_M89uBdE7`1FU4Dl>r{n z-w~e7Z((uq)Yq;(L<=xOb2Gl{ZLT*LFfoBZb`wBXFd_-1xZN;&0zFvdgkHQ!nTzz| zw*zAaSOS2V3&3ye&*K@5bW^CULjVc3XtI_mHWVQ92s;t!ZSZ)FgLKQDPXkGK0grNv zg_VI7I8NzVFeD;dS^#|$_Sh1TzS@b%vd^v~adBMrpFLYne>vkEhex7(V>kdU?bS3p zfJf?G0W=>~I{%}v?BGbC7Xm;V*AS->gNk$lh`)KA&R|;Ar(SA7?De>E)Vqt_U#WwW zrv-e4z5Ys$9gN$N&j`?4$*Zc<&pNaOCzh}i7P80T(M1@X80PM@Cgs<$-M!wVhPLWL z#mpKy7-{NnomX5?Z0D9TvAJUAa8Ha;`IIQ~c-*=P*GgxES40BcO@0*JaQZ>VKI|=z zPy$U>A+prB+WLwh)(JG~0DUw$dssfRzZ5Y-!sWW;^lzY(ldR->KP1sc82HN=yso;8 zZRzVzoTp~_aw2`$$J^W-6dNjvDMG#V^PW4Z0;NySb;1HzJjO{J2}ehU<=&4aa>~7q zbCIjNyv9$R)qGLdE>F%N3{N(ZpQlZ9bS44~#TuACj!|rCtXs#>d5V>8Jb#j2cB5h- zzU3I_^(TK+;Uw~8_4Dl&LLfTE8>+QlRJfFf;de5Y$QM)3p6)$bgH{a$Jc2{Q7xMrIYemCN4QWlitza5}sXo=EsV$M1_IcDU6Df2Mi`#M?wp-w-# zJg%V_#9ynYAMB$`8Y))N{=Hr(Qhu@u@@3B3)MpOW~1} z0QoWHqqT6(!b!RD=jjo&!xJ(D{1r^|JCbb?5!;AV78l76N9K2bJ5bg!deo@dxB$>- z$NTXf%-LF@yxT4OfXVX(a7#c02M%ZDhyG2dZ#qgRj@f>K9+*21R*&{WVZOF)3;Mix|vRMtzuvj?e_>+c@RqS zpjptM&q|3pRP@SXaNzgS_#PMv8#AMQ)E!9~Wa~`*JQJ&QeRvz-M;~s#y~odGQPNrv z_~tBSGWK2}@Xo9Cu70!fi+ya?!~5p47f|dw3$rYo-#9EhL__IXjYaygFHC zXn2@2&f!wlO)9?eNZh}mMarkQ2lfqiZPkmL+eQ$#Pvr)koLtHMIex-xWoq2%RK-U2jCWfF+?tF(FGK7urv$@MI-=J2Y4hV00jkXL86&fFR(;R zdO5U4C38a@7f&shmg33(xZV_wasDuY1E4T~%wk7MMz-;(6d4HtVHOk&otvBiJ;oJ8 zI`OJ6L9qA7x3XBHxv}eo!%0k_M~Lmjde((oGr+x+VR@MV3=IgxwRVgQ;v=6CK$2h( z#9Dxdvn5Bs$IE+lFvrKo&VGBb3t%kk`v72QqFrBZdTCiwdzLj>1Q5C>7r1~g(*oM? z_3y3(rqh!jU^NTYBJP)$Nj$bofF*o=FxL!D7a9QpaH86;VQWKn#Ep!1leky9)iN=k z&JCy_<)mAe`fpW7EAge;vaNS!;)4QDiM?|~%u8W?Smdm+nF{9pmSG$uu5MjD_NHD5 z$F0Exm}h^3KDOtv9Yw|)a$xTdPcCnIi_l5^UeEfuQ^FmHMcVGTcvzI&MIe zQf94Hv64{YOIV^2!Nrz13*Y7IvcPsie*$OP*Cumjg{mQg5tRFq(U?7Z>M?0-`<&nh z6X84IFe2u8vMN62J}K$8QHB0=drU{WR(!BG0ewD2Vs3F|a(YW0MTK=rEM*Wk}no!fI@6ST*flFtE3r z_b#+uhJEWFOZZDbru_2x}ikn&$xL=GHh-{n*IXRlreJgwe)Ez(j-MUzU2 zqGzi=1eGn@%BY1%7Copze|2`b3kSS+v`-gq|B8CE4!L)_j+)40o+l&TH8TU7**9qt z##f|@xZXB)emVk-_gY@3mB6YP$vr;%mQQAMC}C83beK#&D~>$=S2*>a7uLH6@4_r? z{_#I}is}57I5<3ftut-587g!!v?kH~y3uqcdw*$4;>YHNX1>Fc^B&)qL|q@NV-0?b^`@C{ zhHT-`^#pMDsW79L#j_?B(H4b0x7p{n_|#&A28NZlx}~&Su&|hib;>hTVuZdF$$8aU zwTrM7_3_Hd&|afdW7@b2O_a+o1P`MvNfZWs-H*~xqmwDf1a*KcUN8l>0^KJrgflT# z@okO`d1OSY3rBNrHFrhnuOFGX5qiW|j~CBPv-` z8eJ(GGsf>HsVL=-*l|(k9itq=o{yMR7!=>cY6@yUD$=9Hz!;j(h3KI`t|U1zk|XY4 zy`y!y>(lDvV~gc!J`cNcP$(&RtZth$RO=|j8gtz-jOnU1L_4qu|bj|S*nH~i@mdIQiAnqnS?2YIWc@sTM(jS(Y;Sa;eq(|# z^E--YDc?i>EP~b>yv?&~Q{Hl%&wX-Wq{-da<2uxU7_Y=$Hu*gnVXD8RY98N1y9O4lO*GTB> zFIVD0@AsC^Y*wu5h5m}_tGoQP`_yi{Ai^9^#yQ{;y3XmGwMt?*&Qf|ErDmO{3ElOP zRe}YcEW&^*ByGh;plvZN%RHh%CkTh{bDX=CZ(5sFcBOUh4m!OmX}p*c6`3kJp)fj+ zin}lUT?{sc-%rZ0?g&{XgxJ5P^3>=UDJl$=F_NHsDTHnsVwrDC%lLMvHP-#Ka}z1> z`7U|mv>+reYZF6+$|$uj;5OkB97LNMXtH zlg=xGsh6+;g1u7KQ(7NzkxjEQTgQi@Vg*4!AfRJnGhzRQn2LCR0ESy>pr@Z2B?=v; z(0p$tgDCLVd&E;g+nmj@k$HL%Ub1BO`k>wGrn!f6s`EUlI{`#)=C`m=7cS@p)!X41 z>&H8HpEo81jFvc04-OXBb=4nw)e|Kd0PJgLk@U@hA^DGd#neDl#CW@L%mYO{D+mKM zY3Lc1VEQsH!a17U57D74v}>(tSbCW}Nk|oT${`c4QxVgMy$aJGbqzO(DOnHY>+KDh zLbtUkT@s`5pefft{>G=<$Uxt$UR^SBcpuWBLf+b&m-Fi0($A#e4}QqUTfKZ<@QwM9 z!UqpF>P?V$GyI4O=iaa#wH{ zHVyw#TNa;88)4O=g2$;qb@qk1k}wd}Q2pvh%NF6gtWqO}y_BY}@qirHS)Ze>Uz>`)d}XsePN>vV(HF*dF_Z!|Lo;A*;hxksOuG7#(LaS;4dC zfi2~^zR(|`1W`;`aU-Y}=T!h6_b;^i^;IKBRl>kAer-d2JsoC$${>B~6Jw^PR&Z9^ zt~9k~gwqvTxN|JI-&eWt9F|My9PX!qmdMc2aS=ZQx|8^>24yauc=x?tt zpGu7;OEOo*VjTEEzbq+i{^pO%J#LKnms;W4kyk8wI zBKZN0D-c!YV{syL(qNZd?iehzRqj{JROLLgATJ3oGTdxyvqsYysT@(Fzloi~1JgTI-gIky3f+<4caI>(G#Ul zI4f1u(CVT`6h2diDrx*y14gJE#?ZuYN?v+qfKuAMz*oB4hsJiGuu>IGVn1ueFx(HU*q=sK^SDIMLnMSwlJ|>bReN|-AtDeW9 zKPlfQ;1QW@WO=^doeYU9=)8obik@+&?Q3}h6RK!}!DA4_`bX|-&Vse!PdAqSzagHC zI9P0%gVI%1+GnzM67pC1R+evj2UgWsW+WzxZO`fT8-AJ}bvjZ6)(a8g|9A=IMj2pY zS@;&(Rxak7+P@eSV!78$bR+`0ZA&7 z^*7tlL!TKLU%CFi48n@hhbOD>!@Z~%K9IyU7q`u}swqy(y=2wV>tNlpfF2^n<1mjE zwlwQvwEQGiAW1Dk8ElNML``L)^rKmpyiR#0baB%!*>`4O(Iz9NNc(HT!I7^EwWtA& zIA+IM*T;UkoX&KUi3gF(*WJnfpBbU~c$ilbnHFZ3Rr?*Zb!%n^D`!q!&r zK0hs0pI=i$xEU+*%!!LY5CTDnUwr-ayBXH7Lay_X(2R?RRALl_x(s5gJ=*u?MQVg4v+|RHlXr*aaI6!2 z{JKLCeV1XP9#(ScK20pl)}hP2yTTD9(r8F@R$MA%Qu^wS)yM%ZB4$p%i8bAK4g z##SIiB2pN9Rk-s`cw86}Ws5_tg=Dht2jbU7|%z0E<_~uGnuA5O0iVif? zj+895k!8Qdf9DQk#zo!yd7RDhk0dVQzU;u#LS{!G$+rW991#hps3rmK5Y=Ta55w<8 zw5^S8Gh;9t5zKx1&Ts|=aif@Zn};u>sVT{S1d*Z&Blmnn7KvGScoDKhTf zN}LTZY?eQrc*z|jmB%s0&6hd;yq%6Do)1ea>O}r4=1JRu9Hch;lA;6LlS}N&1HT?p zMz#?XC8CJJPvZYfFJt%os%=F>ZbC_i_5&S_j5^4>)ZA!eh++ek+o8jo65wrF2bh9> zvh>RtpJrPH=F`&ixnAHrG=ASbFQQYK`6ZW}?(9{aE(>u%*5SSmxM0akPh}QU9}Ria zK;SA!BcE>l`~n*4Xdaw&kAyga%&|;f7wI7!B(-NTB{<(%)2waa`K%sp($|PkN-#2| zC4a_F_V;OVlxQLKIPKxV%OpXbO;U_f3YAmN%yed@CysHrZf!gmK2v~RM&S0ae5FgJTZN-J->=?OFsLt8DcotoUXSpmttBwjO}M%Cttwp3j`-%%YsG4*q0DE;;T6 z$=ekyT*xcMne^d|9;N3^#&IkT^N7LG#YfoJT95Z3mhRViX@6JuyN7ZhG~_y}G4b>z zuCQ@%(CQCnBw-XHoEC6sw=cCX0SWv8RB_ zsAEbD^D=ojRZ(%bz5J&&0&rr%?VVYBeYoqDm%PdNG~KdQ@kj$?d&B4fpED!P&CWOTKnniC=w-kCWrz%3NF5Q9h* zj8OE=Gn!P0sPV%=7mBNvbMeFxZPVC=TEh>6AHw2yP110UQYN30M|^vjWetQ{R`d!r zDf$b^zShCoCP-!vmzKuAe$dX2bQvg~Fv5oV8zab%3iUTBEfvKNRM)J{Qhw57<>KAT zypwsl_gIdjDU>F#8To#3fEpOt)1+0q>k&uAQP?j71c$n1W*SpXt$K>(lQ9nKbzVvq z6KL>cK7u!13%Bg>X` zi5SZ?Oxm+zBA;XV2x)Jhf#~RS*X`+Cmbg}8f}_~`g!%b-fctOp^k#N1@>1tv%zNax zerd-1g+V4rGzeU!4LX!(HluVjb|4Y^ive22|L2o-#RsXg`Z6TorA(YsQNiXVv=NW3 z2j>zpU+@QEZ5y)c7Dn}Uu`xbOEXvbagYhAQ#)q@q-2gYYv2o6tU2FJ#sF_Tyb{CML zO@JMJC@a@sR5Hv!HS*Kf-qzo#S9mk>t%~3@p{m<-WoLrtc^q^D7s7cnbP3?_!@Gf7 z3L+Hd;i;+%s9)!Nvplqh^I+vey0^FYm3uKOlHby7x+^u!jq=HI8KdpQmi^8D-8c1l z);l3@oWT}5oATvygegk`iC%N`pW>}$9qhYNR9I-9guU>@)=EGLFU)}L?&2~tq2q32 zQwoeld3iMa!cXNu>yy*pFYVg~Jd*mWE!;ZJtvB)D(-9F7FYc0C{n>}}Z>@l%5NxQx zQTLuWtvuDuiO@VtuPn_w*A}Jor62+8D@!%^id?~PwK{7}FS}=yc5xz_ty!tkD?>H+ zQy~9wiSx1|s52SA12P|of_&x02V|hNr(ie?4ijGo{zMHOP%#hg)v`O5et0JAsbCnW zWa#*S+yP{qIeB>m$grysjqn4NKtcZg{rekIjrf|d>l@)0I5-Kw-xJWlCoP{@$HawyMP@G`sK@$XE+$38im}7RNhvodptedhJ+xldJ5%1 zD#sw#^OmTX|B+}1@6F@a6`&9R{|KrZyrK|>eSC~dWP@$&7Bn-~#rafc463kcJnrJ^ zb6841n_^G}*|_A-A|~(;?oZpEth537|1Mw@CqQm$%cgb>d3oCzLcYKl0ffwU5;`zF z?uHdVBU9%B=I47mycZwWwEFw{fZ1|H1ch%R508X+r$Cq`p72dAP*?0Sw4S`laBCq2F6y}b|?`9p== z$%%;*g*8X6HWqJOAv@L%)rnE$fgc-03J7cQI%&uUcsL|kw9H+%Z(V0^5WE$8U?NahBB~49D@_8(YvhB^j!=Rnj05nH(n79F*Amnr3yWYzg6tI1s ztzKd-x6qx}q>)|FQC6QD9ZuzAIqJ(p6<$yC=%2Hw8;dqfe`;x0@(V#kvO>NntVv(f zM=?WkKALsk$%6*9@$0<>*I=4xocEpz^%~pKHwJoxGK>nTSLh@kaPrS3BJn~|u)GSA z5`cbk{@~z{M)dmO&Gt>{BY(67r#aduXEY(0E_*_$Um@IgS>NX9(lJYBv~+c4g2iBZ z7SU*(Txlm+-t(jADYSW4m@4ZSDSy&YmM$SHnEI(=CSAZ{!iJB30UN=*&i+8t-dei= zz2gDCDM0T-!+zn%`f5+Pl?W$Zb9R`|-QGIu3G@&?>`<53b8L8`QQ9}gnnsn^W~;j zaUs_g0gNl*%*l)?0yLmIF(@1JMz5$}nKH{wFeFYrIJH_Kyis5fo(_>K`}~e4W$91l z;OfO4mman*J-cKVeBFvj1dsFhE4M#gJvXc?hrpOVc$Yh&*}WW3mQIe6(N4%wV}3mmT$=7ikuYG%V}Mu$BomdLEbg{SNOh{z;(A&k>=swAt?qek6Mx98qgYTr8Z zZp6bq*CuRx*j`yafB%D%D+>zn$4{m-N40amLGb{pTLK5<^e`yLcar<6(S0|j>fgp6 ze(oHCHq?kPV%)%6x~;9O?q7yywBL)HmM#hEbr4Rq$xccO{=B`Q`^d2 z8c>iR0;7$le#*}+(9&aiB448rbMm^Z^sV>Ubx!`K&^wQY>&c6@GnI;()oR%@dUz?2 z7l?XyPtS6z$t#-^=M&-|x?Huuq6<+MY>6~{CFOq%>Oqp@j16iUkWalDYwtQhJw+Gn<#7H`M$4J?>34qzga^G0Ty+P$YI&h6NcsFBvMP^ zv^|ze>iK4U% z4qo-?MM*dnur*|8RaMmy5D?RW9(d}R@n5*;l*c}2O(|K@iB@Tq=HQymak zao1dfo76mjbmxv$j|?w;Qr)g~pT@aREq8KA8`o^wqoC&lx@fKQY(2>QVNM|S(b6%k zyqc!ndC3rcGgisgOIbtnGd3E7GfQIkl5}oGj%q+%?Y1^!ZKg5)u@iNO$!G~7sUaR% z?!-4Iq8o!ds68_(y`#|Y`4v#%*2WdzW(RKjrz8oO_oy6S+&(`4+^*LBSax37HlqSak&?1b z7xRh7$|9S>9cZ3M;(o~O-=s-4$T~oX@O5IRz(Y;_fiU;=TF;qZ5I97RZ~oRme*Z&3?ic!E1qLmjIPff zwR-?$ozYUM`pjtS64jCO&^Y4NTT;wI9${p?;#vH!&)pnv<+V95Nc}40RyIwPC!fhGjAa7!PIn#@xOtn8DL;#)Vr)OX;s#;1MxuF zaB5)SUWE`CJ&n13S@5j>koV64p?~fYi~L{-%$rbUOU^XrkAs4?yG|*x$t$-*^S)2S zyA69JPh>69@$1%>z0w0mL6Fo=hdsm42eL<#E>&>>g@VZBjq8I(wO_xZ%T(OCWpoHT zr)^nRS48T|Jdk4mIGxL4aBSy8?{d@|h*c=qTT z$*2dxGdNGAHzJB-eJ~Bu9&4O@E?}0tK_5!J4B3oZeO^8*Tzg!Z23Cv81sag6DzrDW z$+NvN#ur05qR~Oye;l7J99OgS{cTG%ezqs@OjcJp6=cm|+W|-HKo=dtm1GLvnf6<| z=q&2ZX@$E-c}IvkmK;m^AUq<;9wFP{yE6l|5YzveI~UQyzi^HRM*VzP{dJXdd=|OI zs`Hjjbq_aW6>)#i0U;PSA`7>Lg3c64N)znJhIKh-ZwP!JnS4J)tRB(l8(im4zO@!L z*NfaK)jH)LGJ&u)t0!zTi%!lvDZT!s!o-q?uDd7pz0^8|9TqXi<^PeAyW~&aJ3)d8 z<+XaSSB~=)^81CyA^E2dCw=UUyR3ML)2BdK?g)ZQo5$liHdwdY!pKt--plz@eCTGQ zY3G|?sXXqcz^3r9#rdZjuIvmY$kl1At2hN8nuwU|NT_iOWJ0rbndJu$jqw|~s{4nG zlgf(KLt`wILXDF;Cxtu?#W93dc)n?QQ=qVP!MIATZ(YvcH7YE4EWi8m_%z1)?Jmpo z^Y6R#cq3wSl9)$J>rhS&D8LH!-xq>?-Z;6qEx>pBRKoL_U}fPVWDRKl@ZByi$uje#`z zu=QIHO7QCa6F9$B0X+n*1s;!l;7fU=iap^yxDo9i=$9uJ2ryFNRW>|#Jv?Q&+8$2R zsJT)ec>K-nOx$Al^n%0ecfA0Dz`?zfL`qTf4Y=0eL5Kf<+P5&h71WBZ=wUxN^R5Ko z)JbFIZ=clVHS50k)h_IZN^-fX$PQvw-~f2pdN^A>!GZH`OAYDl9O$k=kW!}kBy<{1 z-a-fk<%Y$Nl@&!+@H59EKya{6rpB`_+JdCJFkh>$+8(7z?9G=f^V$JlS{WC1r+5>t z2%<3seD**5*57%vTXllcfmy_xRS%pBCpwiwZC@?0r?G%8xUf__d z`wBex7-%H=eXYQuUV0&B3)?t`I|99DdM< zSD7J}je>Xtl@z>4Bd!M&JNwQ z$}D-tr06uR<{PJ}pTscK-U7Fecx9z6_M&?LRA z=@%e96?F*p{sxva=>pE!b*o6<9v>J;zFnXoZ#=)(&%vVu=IJkgS#=cih2ViF&hqo; zlJZ8mo9;!~3UA%9vh7>zZ;LcGEz9u@o@%z#<7aw{7t_}qF&5KcZ+Oror%-#h8mZ6h zgAz?;F|B?9)#;UUf*Y62U87{+h1!f%AK7D^O@tB(O=66&HHB+ln*Avgf3Ar}yn%Xy z-vU5ZB1F1z&jh1}q7IP-xbJTlI-`8zz0Th+;PCN!mrLWJ_wdQk)uzOCWHZU@QGw;O z$+~1l&W5JKKQfmCvgp=N!PO1-CcfTK=AdY-Nk7B5Hqc@(L5U<(Gs;L{(9+Eq?(qlq z5wKWdG~F!Lxf}&SV2S4aE?7%(JZu+%mF`S~neDm$AM}VC7=KP!@Tknh1-u+XfpR?x zU4hJniGKV3*l9f?$i)t4z+U>j>E;Tm&Fb3q%~23oT^1hV2BUC-_PPL+i7HPofc6z8 zvS?M+Q`cQ~lcd~ZvZsEpji>+YeseGNP+J`8#|3#J;I4J-uH*9}@B3 z3O>wan+$x+N}mA(Qi{;#JM9PE{#|8{lvdC$S3OpIFOFY9U8pRYr{MaIo=Du0?7d_Y z&KJy*;&K0^tWIk^!Tq$9x<1p!&zYDDe^rBW+SOML6>6+`(mnl|HZcGVrmeEL&$yil zOW+YILL60iq2zri^1vAvebuZrQsghk>rh*S5rQ^7FAOn(x#iVBFW9_htdtKOw&%;d z{GR=;lP;UnjF-2jNykZFE^Vn)6|3>cSj4KRLxjoiG^IK^Q@yCQ-;shuT_b?g zg5+^&oHpqtSj?LxVyvq@g9OEc?n4s?7}X!J)=}aZ@!0Nyfyb_r*FS7<{{ihxEugjV z?K2c_1Z``-Ej$zyJXz8&8R^EJbMNU=J(eWdhqpcUYAh_5ZjVIanmyN>MjBJHtH$_7 z%q;vzV=eEaTS}wJ9ygt-$0^y_T3RJ3_due>-k!-G$-BvgI@A5l-^~4^BDGk1Myes)zPA3-1iO7Wc zi_Qt6A@?ei0dLI9n8R+@43Lcz*kbedYe1 zJ#9SHb$1iYdkivQ!h8|P8t?ipIMJbK!J7m&-tcn2)7bq>2+JmjrA%&hDKP_HM!)Nb#AT+hC$NPZM#e%t%2Um$~2 zfW(XftgYzs6l&~o7eh_ZD{CP6twS?bJOX@tzZ@M2N_1evj*E?5DbMa=#W&S(S?_*r zYikRL0t-4g_I7qItNu8EvP(orm~y<~5W4>k0K5Q!>qLgJc>_=c7Cvt8;o+oT->gSo zAI@_C;0geXLNJG~oMzfl?lB6_$~>Pvyy7stWvM%Va>WDxui;>?{!ikq^VwL`U=<&0 zhQEiZ@bd?*3*ql?u7QHO?zv3)^d-#(RT?5CcK)8z`u+IeN&nGme9ZPTbKRe5f-H%x? zN!~-86+oEy<{SCg@RF|ji=(d~aBCsh2GQ2AtH=I}^*_G%UO!ok?!aJ9W(3Ooi$nvJ zxw&q(3eE;%^mDEphT)uwWz%{$EWW>N1RR--PgY(C2|2S`Vsw14tVtrV6jT2Uz@$7p zP2aym5e!N=DmS06eIb-6znJw&(k^+wWl$C(ZlnYfDJbsOj`o7=Brw8+<|1>qjJEUK zhb-SA>{O`oZ>(afm{tLA@ED5C#dwazi_zdf{y*gH?~m&0DjNVVxPExJyGv*N;(Pci z=No%uA<}pXv^B|>_Joy>vZPa0>w+%)e?!1L2HxyXw}wRuEPLNA2iGgl*dUkZ`# zU<1LAB#=V`8D};;+OH#Ym@6014Uobx{s9fZG06P~J^}9l*#|MB##pymI5AU?7CfOp zp3OW97L5>U%FZsNHV1i87qhIKV^gjn9H5H8fZ@c)-Vi(wMKImm9SpWbj)1%gNK`?d z1Gjk?@DS)hVO}KQ{P&Fh7gEKFc%ihA4L}O(hO&i z)c8MV_tEt2`5xAP)nG_R2K>Q+vnqfA^XkLkc}g z6`qy)!u7w^uKNgCgioLiUpzW2rM`k;2};na@_jyofZh}*-(!#KA5qU9?@DGND^o^Ee|VDMS64pu)n+S&u4 z%8eb`ACi9IP%a-Z#U?peCnhIVCnpp!4+TD`XR06)Tz+lL?e<9lm+asOv6W3f%THiZ zs0~r9?7qN>6mscGc4zHLFOX_}Mc~uqUWl)D35?yjUgBY4<749P9BsjJ}2Acdx6 zEk*s6{J(!ab-%q&lX=(t5kvu5ao9Mzya`hyMI-r&HzhCTsU8ZR(lj*}nmh}MK)Uq> z)CIsfY05~1V`N!b_P3^Cu{#(8_AMZc6ahjJJOLN;vs-*=#YgpNk=?Iyx6>^ZxvSs!*RG@GP7n#SVpj>FbyChq_=Az(L;0A-A z;C{QMAF42*Ap%N&6$1bu)oQ=W==i?~`^%`RzAt_hK7fR@fYObWNJvU|Nq46p-5?<) z-QC?K-3`(uos!ZZNO#bY_T3R1VILxQSOtTqnVi(65S$ ziZ^Os5rGig%?&Pth62#E1x+I$)#zM_M+c%l(4>+BqQT%xULFXAVl`B;ybxNq{RPA{ z{hi-8f4i-zDkh$>aJI6ta&mIAvSL^jadw7EZ35j~@MUale9Gwpq?W-QW80u|Lq#7h z);D|E*ymcgcu+pIHvj(|x3|F^G3YliuzwU=7!(E+(Rsa$6&FH1KwJTizS`)T?|$@yOP9>^j{UA4rJe?g zC;kO|P$kkBAV&%GS)t;jhb?|UpX$rM)pno;r(7sgQdtRD5gBaeu~jkFzdo@g1oNM1$jr+ra zni$ZB%&x9}HE;#4{69%K@AEOvR>k5qPv}1L`0d+g7Z+gGIWse{4wf&&(JQ@_kB)$| z&1yO}vws124sVv(wCU;T8D`A<3*G~7Z_Qfw`l6x%z+qPZp@UC^MPUVVoDr92tSmAv z|ACEt=IgrcSkE*i``Z-#Sv89|`HaL7TXej~|3`VDk+TY2R~cK_Y%qEq&|Q;~626$2 zB5N;y_l^S8Ya-lPmONuW@C#BNHwyPDQNw`Qi`XpsCV~3;dXYd>A*2nN5peHm1LWX2 zfC;b}7X%i-4Z9Gg1#oHG9&vRBqJ%%&wUvP+U8DhxY$}lRG+1-T%Yr`w^3l9rV~xGfOgBh3wm7RHm6iX1zd2CN2|^=;YPEvb1O*13 z2|XpV7=yMdJh6#T5cD7i3;UhqTd!N7A;~wI2B`w2hzO)C0_@t`k@wx?+zH}tb81A z=7EkwFa~s`6Uj(+-3|dpDc4#sMz&j8CfGkN0z+nat{UF}1%>}O)`*qV#C!)E`#ERV z17$b&f%+&Nyj&SgE@P=s;RI#q=eqev_7Qcef;eQayXr<+NR%d64LS?pRH3Tn8WMhi)xG63ttZ3`};t zlQ90|Xdj+>o7JWerryW*r`4z1i345w*x%A!F&=z3Co*MEx9#+brDmkWG7yanQjKuwU%Ype*9 zhZKDQ7z$9!rp`Q+)mY9mwsBi6RDtLk7!a@#Hrluhgd98H0U1Otu9FPoQ#n2FM`s!u znvMZmW8*yOf*Rhyjh-Ow3Rp{M7fA`IF`pd)W+3k{T9)!q{>Ab`rD?_8%_acQgGg`-?9tN=(i;tY<%-6~hJdxZ2oN_| zy>U&DuD5^W_MZTH3OLApb4^G8I<7P2<|}RkgvNIc4{4G{#vh-a7kSQ`s4MED1f(>u zU6B4DyuyUHQpEiFrh3+=p|O0ysGYGmC;Za(DrOXgSlX!tJYxMSuy#T3Ja11}GA z@b%Wq60JiN)WzF`hpb2v-DZo~5aR*k@pKbJ1oCGWr_U-{(g&O0zwZsw+WwQG`AFnq z5S?8jWnG^F0~40SD0Yp$1nIIZe2%PQR$`TO+DMe@@X8>@YucEWKX-7H&*bvmQhq4- z1YWw*WUg#6l5OMUO&l6|0mPH-6!}NTzZz8Yw(C8WWhKTE5aVz2xnWr_E1sW3O}aHV z?~!AA?_=55=5hQ*Ldl!P-|OckB_#CN`P(m>hAt*?dYcgB{SE&tH7l#rewzB){&+G* z@j=>4du?{^)tQ51xwVzTcCkp%(kVgE%zHE+)3eVmh- zRLiCCCgl~>)DjaC0=5b)f~`zV3xk3NVkjjbKJy=L!Ui_Y&!q{|4TyvHJ(6X#x5l*F zoQ8P1yJfU@{uF$I2m-rJ7kl9LGN`|?zrDY?+v+e|I?2e$@73``F;CL(ec^k@@h4B2 zLTjhbIH#HXJnzEvhxFS`BcigHcxdgOzQcpTI4asZ$@+-xCdQ~6j*T2}$Y~{de3 zHPy!j{(QSsRYsedhZ!k?3`UR)lMS#1Z*FrFjS)Ul#7ubajQWq9N{*kryZzfGm=paF znSBW#_!sLC~T@>=QIX>PaJsS;|$(6F4^ZKEc($x9Q z^YJF(n^P7TnVY-Y619aN+U2SYBRv8M80oNXQ45$P)MWv{oK&b^&j1och%{ z`4F9q2kEC841U2VbVyTXyniVDJ9_cfrXOofzQy5SZrNqxeYfB2-`=V_X>2SIK?3`r z`VS2uE*GFx8eL`$6mEe;E1Bx?Tks1Y(k^k)2uu)~@z#OlW+%|bb#dXa@XlhZ5&8c8 z`>Olldn(@rt1FQG>FDZ0%>#hEukV;ClU5xqFK>D=5GQ~5ZQDuCq#2fXL0r!C8UUow zgKBM7THdKwL|agpEN~Z(I1EPd%r=N zlYdoJ1Mu)b>&~nKz0^m%2jl#=Hg?o6SC%L+Pn(-tvHgTUZ>XP52IIpo!E}M893b31 zC!40QMX!j@nw)aq&wG(d#?IbnTiAJ^DmrloD*_li7ZNN{yV+PYJp}Bb1Dg>p_L{F- zxX)esQ&r0_{{9}X+$t*<|2Ff&$ZK6geICq;az$$_b80GkZ!ir%Sf^;F;D22F2u3Kr zL|LWm)RmVaHqlkvKlK$ce!#KNY!Jj=W5(lqVWANT6By~zke?&Jg^#}G)J{AgS$Ipc z_25J1erQER`h~v}(CQ;#w|HJ{4-*z5vy{|JX|j@o3M#?#<5jPF>x*i6f|8fy?zLPB@2z1;o@?By#}EnrIGcUr?zVgl9s*!6_EzRER|Fn zjeIn{2MyPa@N!?@r=))UmUz2c2klw5pk&g)Iun=OL~?_r4ysZ{n=YjnG%ghF3` zKLP}7Ca(~MVBiru1YBvkL*>5w{aqW>hJi3GlKmq}Qk`rbIW;Q~2S+LhQ!I_scD2oo zb&9;V!#_48JlyJ(xPA0j29F}F6i!}nZ>)voMNl!QPzlBu(_W#DeWkDf%CazmUS6x4 z-(e}Z-jhE$94e~Ix%~3=yq{K50^b;a*MhDtQ@LW~iX@1+;}c_3F}-h>7i(AJ+EN;1 zDnqct+}glpX?peLq`dH%3qva_vlM7W-)U3z>hs(Gj!*l)cTxc3npeIcrXkp_$f4z#PXk{R`7u{F|z05<7B`<0L36eq~(F_%vpWJ)LwPf|t&F!5XciD7FdIx7`P;2C&qzI0qn7M3T zSn$6Y*@F>Im;1I{0DgRVnl#?m-vYu(OggQ}C*g-}7YDAAM@9%(vPTc`{2g9S+>aC2 z0&T8rN57+D_sBT2%eiqW5d6CLC@@I*MI&3q!rsI@jA!)q^lSq4Z{U`(1uGNv-Pyp_ z_q?}m93(*$KYOql2RuCF@Amf;w~)bff+h?dd>(k-L9XNy{%Nfqe^Y8%B%&BK8&Jng z+yQlfU?6~*oqcF*ECLB53JIg~$CmBQh4R+g4Fzv{w}GgCkc5P|yL@JySC2Rw{0Pvx z?X5PZi6pfgar*;yWsozu@#=}X-h^xpOe~kG=9Ga{1bm9TJc@{jeFzyQ()$jk=!tkJ<0kJ)$p1JqJu+=mtSjwsmKzYh`)Xq>Ukb3Z#|JP&hsqy{wvB?K( z#ftTWGBx?Yz79uly1ASD|G4T;P+#CZOVI?XcCkZ)q6YT0ud46MY1WKfca#w^at#VV7`FW)376N#JnWLfK^oBf_V_$ zU1tiSf4Mq4m4fbB2RqQIKl)oXUFSHL{Dc6Ob3j5uLJs*aY;XgR=e75d-z;8c@qYarNQ z1hz>~`o@Z@&<$?Ak+WlN&OXnkO8@*0d`7cZ=s`WEwe9WgAUYZTsHiZlT{PQnkB-LV zNF&C=`aU-{GBot^e;QV3-ozkz_zeAUjI}t~JvK^vqTI#GRelm2k~A9E5eN7OmK_X+)7W zv{Y0n%P|&AW)6zM&CM_iQ@`4EKA54)&zrbz$jh4`m5hBQVdmLoV`OR?sFdH^+e`Kd zDC6adg#nd$6Km^sU{V0KIRW^U~*E!=f7uv)aXVB-UJO)`>_3*f*AYCR(S z+@Rzf@CKcjJO9dO>BS}CLDx#_*E~{mU5w(!5M;>u(Gj@u8tdO2_3F4S=`C)j1}$Jr zl1Z|Kgc=N>k1`K?@9>a^n>((Y`;)Qp=1?3BSdG}&*e{LE%=WZx+qXA1x;N~Aq9=&o zzze3?WaJHmmYzN|I9O6ra^0a>hw&>=wkP#|U@5Hv))#Mro(NUhr|x`o3+p3WzW!SU z4kxc)y@G*gH&_XM2ub$s2jx$qUtiZeuQ#IvEOPVC&d=N1+9nQMEHD;4LALkwcpnl4 zEz&et;SZ+|Z94#ip6`jWz#<6I$HxbEQ6e-nbmG8`mJfO~0M2N4XW8`rK|vlK9{(1q z^T6J42~#g)U|=9D{5>E5z6D|mtYNaUDzyv&!o1j{wx$L=t2)PBxq9`af&!DME!;jQ z7%<>5-ElNKoG>cGL*g=)TMo^l6y`QIZ$d=J#>RlKIpY9v5SYE5Ly!?{V+)HG)3H=w zenjpJ0|~+)6@L9v%*YbM+#qpId>{6_U(hO;Fmu?Z7^%2;6a)IXSPduEbYs!Cz4W zOA!A-Q6gSfW?)O?*22KR0JY%ye2^~{r}YvP6;x1Hziz5Ipzk~tjkxHnuTj1XeV!YZQ~OkLMo6lvGCMbi`~sF( zs}~-+z8~X&!vNV@7|7~tK>Z*#x!%Oaro*TYc`I6x=c%EpN-)on4Gt}03A7aXcEsY3K~C&(&_kW6yng572*HlfZhFf6yxtyV>`hm~W*Iyd2s zy);7gBRch@bXYlZTCizcCqWvZm3;nye~SqgoL_g{CvkBEmR))>GN0tX5}5e-DXFQ$ z8+Kp=^1eUf-0Dy+7d!z6qUVzyjACI|S1yMg;FJgE$(q-Fc|F1YiOEBoMe_BXGk8^#}>Yg0$T%X>?S^ zi55M`j~-;{ap;mZI-U8KvjO|sC){W4ePo&vdf7f2Fgj+iDD*!*1civ7)T!O|WU))I zS`;8>LueBW^5s)@zgY4wwMj*B$0I-#=z^p_wsrepj1D3a8N{p4Ds9EC#Z#-tqe2u0 zUn}JlYm!mZGyfPfySsxhhf(MVn&q7(A9)zy5P49EYQDEMjJGW|E`%>P>Bi&W`*{~Z+a~4*bQlU6zi1&<~881ANCNGvQzD>(~7+VN@8`g^! zPNd9N`n~Y`2%@h?oVtrKq@^|G)ggKkCCm$=E=YQXPj&PpJ zaGsw;+WmahSj82*hrU(!Mijk*2o4EGeQz5pw|}6xoXJR4R2uTF-jQmnGqPN5k1LIMA9h>F% z?GLXyQ*;QYC4$9r!S+}48Z2l8Zs>8T-|5aLi&T<%4DUE8grE01y+R`I@Gu2$Vifga z6b*b|j)5OgFF3}SGyd7|PKCeWom;QmrGOMCz(E$K27?G(gWrat?IQOd4Oib^KhAs6 zp@oz`eNA0oeG&$%g?vhpiv^NLr)zBF5pZDJ)4E)@~2{-d+1?g9zh%&;>J zf2aJ!Dvl6UoL0-V_R#zJ-X{F9L|Rl%m=F zbYQ1Uqmdso#wxv^xNm7a#|)C4X2v|;mz4fJe_*IlHKyoyZ~sLk*I<$49`k3)Z|A%8 z;<72Bkmx8dK_@%?k~|t6#z}Cf_&_u?R6IqaFxg5z zNtrGxUxCW4&AJK6{1|Mk@%DY)l z#~scupUa4Bq(Z)p{Q9L5^(~l@1pl#1B=kM&gT(=Ljc?5mZWFMM%%*eYh|$L~u_Ozk5}Swi`JMz`Kyq&-m#ZBPdjG z0#f1hn}I1__-q#A0^;pEMsFTU`{d7qDo+}WqFC-X^QwREE*vWAUtx&jz8Y$M{YU5h z{^pmygti^DT>`AL)MizO7?mMgrDeK=%%NA_ZY9-Cg-jWC2so~5FZ7DwAQ}qf3ojn_ zy2=u~2=q5@jyBah-nl>S{&DN%CBAj;9x$&TD0{6g8vV{!=cNvA_nZaSk|2M!i^jc& zn=ba-{k&I|AEk@oZNIlcWW;^8t*TDoSZNgEf9l)H*Ozs+IB<#OG+xNyXR<6b0-rg6 z@%TUHKShoLJwA~oB}9kblK(W)c|oRDF_F!azRyRTeJT%sUr|DPdNg7XnWhxz?8fhW zLCp-zkA(>f$ywcwq6vhLxMMSD%F0_@?8{Sg)qZ62?(OsbxJqxcbagivR>nJcZ@|3t z=b&e2voXM^M5%(zcA0*$qPI}oUcVG4e14?R@ zIxPKH$&v|gC>Wh`&!b1Jy&xy+%~frR99~>2fjL1nNYuC-iF2WaRWdrAlH`2>nf#uq z-?)f<4J|l4cMerj86SVHw9PZB#*5-Cztv_pZO`)7cI@UGM5nTc1;P<{R$1x@wkBQb=cGnt|8 zFJ=_p!f!zwkR|EJ3Sb@K!*8kUB_Aelcazk^4KxqpFy^Je6Pxd{&%r}2d~c^iTlfw& zmX`$$c|jjhrk1NOqu?*bb(~yvYTq65>=-jRjU%&V+)efdzss|!saPc z-B_fR!qU@#FE_`vh4j8`)MYzrA()UqXN0cxV;=&}Y|OFUNOi?07MNYX=f0{CFC>&` za&k-iJcY<&?f}2+L4AAdu54T@T&f`X8o4mU0Wgg)iYD}k#kJXGy$3AaQAG+=a+>-q zD_&onwxG4dhFhphU(%xFeKZb-Kn}*+F@48VbMo;sIeL^?c(HpNS$wdHrHgNvXj46j zBB&$sy<>tM;f=vLK)lSr8{~hIXT75HBC$wU$?x|5h*P%XR=zF`7?$_4Erbn_E*N|* z9V0`YPai>@JS8!ewL8n#qImkbwl*NfMdFL+ix`@nTmFWO6vNS@P?NYIh@gzi(FdlH zriZ%_$~}9GQhC$(x4Q`#;k+!Iaae>0hlTK?9ITCGNtebgZj~=zdbuxZ=O0eBOpXhu360PL( z#2wD_5=Toq#%!}7^!!ucZ*8ZMw+kiOuSK%Mp1Shm4{b*?K7mtUawfFJ0OQ3vG{>03 zj+Xl|HBy~9!-RP&+nB|1`8l}n0ZSnG2HO0+nTY(=`SS%l%td8q6K zlH0?M-Y;vVQ@6FOO;MaE@MkH#9PJ~P7kBY!(G*ghw5gODJfLyZx4>( z)5NmP^-THM3Z)DsNt}{fV={a_5X))vL}4$-r+}$58d;-e3fFX(Ak#;`ID8Yx$=tg+ zM5Ph$HdS`NK0z14Ks_BsJrS8*MfwEE$ru!F*rS56rtt65`%L(<6e+^FX4Uuy@0!`A zL#6d-=o6HdAmVq(A;PEXn&*XUmK{wx9z}JwB$nJU;ib}`?4_vB2y6HU!=+Vl94m0I zhuJSFEdyqcuzQfXDm8*$Yq|u1jegG8Gk_7?92*;!F>!GB#RqV@3q)9Y3=(aHHkG*9 zkRd*TFCkquY_1CM`+a|?Y|iJ{HuyaG%09}B;Lj;lr+eJq<_ToZb8?`Voay>DIO@m@ zA`-ly+B%yKJNV;Z>GGD3GhflDMvn_Zsus%mi>SkDSfR-9wrnl@QKE}!ZPj3{tVw2- zbh?xL!51I~`v}f2_LYzB;jl?U&fZZe1;o}RaMJfP)0KW#$x%RMS5|weKPN?pB}O&J z!ERYuADnL5RAS3KwrUp>bgEvNra`D&jFYWMtCG!YtH9ByvL=u~YpazSp)@#W!N=Sz zPF1AP)vK^gsy4E1(Ef_{y@*8uM1;dfm)6h#Pl*nvt4!kJwNVT$3wfs1(Hb5e9vdfT z97s$HC0>Q~0aiGo5%;*kAoivaqynL;56^E6 z<-Iof%^%G5gFn-FJm1k3X}rn&a@K)4L@*T8bzzL;y}npF^&IcWzAUGnWTB6^6CYY2 zZE{#HonOZegXmZ-T=D|aEX?9nobSW5J2#|S#%CG-7+W%1C(a!oJ{&5iOz-<6uACns z?LE_z8Z0g3hq~Vi^(?(`h*HT50zCE8`SZZk@Ggim#p+90kyr6w^ zb4N&UWv>;m8i)YS0k+K9g2>OEqr>{$^GluK3swVg8EBkVclWG0|okIyx%( z)Tb(RI6RMB=YE}O@gf9kl@-4FuNadj*AVTQwQ+zFdF)W1gP48B!$elBXX9Iep$HOm zmiOKoSzpVoeopYaJ}ga`V>RdIO35)+X`vvyR!~%_Q&%!o~R@m+7#o~=-|6Cew zY0FKw>N8;G8$Lfnwj9AVo`-n}EzPU-m%|7MU`loO6;`bw$o=m24(jqwK3QaL&w6AC z#P4kh8F;7SApTlPyOj zzs=zqDScV5uQhkUe0ckezRU2nA>GQ_%GQR>zYU|1ROALuHUZ}yvTRQTkX-LT|Ezf! zLm?cLqS%=>QkARxOrO+lpUeNp-B{R{?85uk)18A40Tlk+2Yc7Y-s!NA0*A|Gd@k`q^# z9t~}{A8_}`A|jNe`rbA5YpkDxiLZySp_%Rlro3w#xyawaIbV1%&cT@^PZAlZe#kg=IaJ%_E@b?$a%G~z4eGDD0Zk4RU_~F-PtgH&D zcnZh7Nfl4+9YPO37oK*wMLk%voEhD>Tpa8$qFP(px z?OtQ_<5}XGG8fhQHC|_nlWN?F9YlEt81aMJV`!FXOFO1@NgU4WNRI9Dj|?Wr@JrGM zCQlNjZ?XV9BN4mbr9nLizil^%FQLG3s8}QM?$Gl+hwh*Gm)c(K@yH*LC5xqBCKz$2 zFr%ct)@C{tNH^k0{1~k~b?F$7Tp(FIId$@g45Uwts!8~~WEi^)m{$tdPmL4d!o&&J zyESpF`zf8Vg_JRP3-PJ7rbvxagsBc~)(UmoA9mW@K!nTF^@<8RcY7agW$l+H6@qr@ zJPm7_mSV|g?ajHj)2;?E7fDHo5KYzXZ3@}xVhsWU*+yb?2*5U7U1@plC!ZA4F;pLz zdzPhG9sS|&J?TnoI2NfZ)!2d*5p2k@@+QGVz|N^u#SMp&aO!oas?JjtL1=|aHUEP3h=q11!9+5GN^+|a}OUt~08>&kN2`(6} z$Gw4P&$T37I=mFEnUGs4#e=PujCZ5dJg4yim7N$3r}Me2e)0b4LYcQomqcHWR{-e4 zSVw0N9&BA@IJ{munwlTGjbWR!JuxNKag*A~aX;85QkEr|BuCkAq`L$z zOTgRf`dAm7&-FYnX92P*uDo5QR>rL$n{HRr(aXfqQG%&l{5PH}I;Yl2sd?030*MfV zvS~u-MX|w`Qd>irw>#wO!yDePHzG)E5DvIiQ=gPMIDP5E0!v1f`1yC89&sNNOhjQ6 z^c;LlVfGIFV7rV-11_KRO~$fxeoRfrk-8s>5tJWKLsQO)WrmI{&-+Ush=UNJ85hB1 zBsxx5-{M!t<(1_?3_@|n9s}l#T}@3*8@$LZg zTRw918Myn)p)n#Bp{vm102LTrk93eIqM)GAs~#Ge_)sVah9Y@c!+IjWU?uZe{c;|S zv08k)A^v#yi~3zra)dRPO*A2bbaW)jIH&g6C-Kd`AhdVXY1U`8$3o^S;KSyHR+IEe z%1?VJ^h^f3IFUhS=4Q8-$4<{KdX+H!_Nm8*hie=icn3VRv@Ra-#4QO#9oak&3|k=3 zJ`gHau}UfE3OOfL)zti?s6yNq7%HBVZRBosEynqLF3(GN|NF=INQ2kYx5MN$)5lE> zI{oEu7Lya>rb`#uNWD(@#ADbZ2IhtvA6Hp9nf@sX!8D_S_RnbE_wUPXZF*R~kJ!97 zdtv~M0_v^%*mqJN=%aGPgVylx3^t2VMgnCsE}8zNj2I=MdRE!b3`K9mNf5+Y8s7nt zWbC}qf`9<|K!LI)J`I9FuW@W1S^FE8{$o=3v=-9V9_+Es>`leN{EXY0v>iquaSc)s;L46R+#1C!SFkJhx4Eo;9X(}b5ooJzR1op7 zYxU>A{F-Q3$IhzH$hOiGw6(bPN6+Vm8#y30Gcz(AxSS4R5fDhP!uai@sMw-0CVgU@ z)0~=~ri)J+QUy(ru?;(|nLSS~BDupLpI_bE4vSVCcfS`GHF?JW_nzF)9S%N8n>OK~ zN`RSXs9f$EoM{;8i*&2L^)h;3-g&MrNOqdDz%+(4;alGKNg}BrJ7vEXWb! zJwGrG)KLRKad9z{ZX!UvoBfcl$oEUOr|F;%Ff$>M&>9Uo2R_hQMfI~0Z2dCsD4C|r*I2p7SPA38O z9DFwOo4MG`G1I(5{d%M$$(s&TV*z!d~8*}zpD zSgc#Eb?BC;h>MAt*W_u5hvRi`o=&WpUA)1>6s*V>13+2-C6RFIm$;ZLZJTynpf9M| zWJexrZFUj0#lmg>S!h$;2~#C5{~fgd1@N>+H+Z0nqe5X}$*w=0V2j(I|2aUQ$a5NC@WJ(%&+hz#5Pc+_^Og$TR zz!NOw*!_rS^|uk2iNN5ySRS2}e{|bHK7%_zFNTcfH^9SVVPj8EO#zG>_#rGb6ucn( zxTCV;4*94FpxPk8At6EnvXYWWx0dd{0Gwvmtn;T8IvP=7tF&O>Q3p=!dl6|@-j=8@ zOGIN6?8Bg`cd{>Z=L)Xa4Iz(!|0FHF>iz6_aSfVS(w{y7cn%RZwg?v|cuxQ_-`Lv= zeuDS_D5n{ChT!+$-tGv|c95hNX?pwnYXH`$`hGa!9q7kqWM=YvTv-(;X=-TTfz~iU zU``jw16(K80hE>pUbIIBEWgbjLS2Mt5;8O8rd~ss%T0E;e|q@r(};?T=d$pbqeFeh zW0gu-BD%Y~Md<>B7i&yVAV7PpT&Edpagx-;#6-P9M~DdtG;8Y~!UlSKtrlwngmZM7 z9pI&Kj+8YtBx_Dwd2_G9)74Z{dxMYPY_<5|&->BzkB&sd!~i-U_+;ngBq~-)U>>W02i*S!7kq#e zUC`ch&rCKMNgT`M)1Ec5we<#l_=~SHGNaH=D6o3HsOo9{%`Q35}rr z?c28iou=vhmxPFDbn_HE;BKR8t=&4ZnM&Y5KhC> z@IJ4kWEcuPCp^$pKN%X5zxr2Xp`P*30OlkFkHZR236_=wOB3qYu7`>F>gN*7?Q=*a zH9?^XXi>`|`)wi?&EBbmS2!>^Sj0Qaml&#w_AjM+#$IC=;&zZWHf(` zGaRf}krxGKG3YQaw0m&gHG$`T_C#;iL75Y&5Kn9<(Vw4Bb@ak1Mlg?)&05O&ZzQk3 zSzQoS`hi~QLDC5#RuF-MG{QP{<4Hs}w z=Ra+y^!0a#v}-T6xj8B+Vcp%#s_W-8*X+F#62&im`{9EFz>t~^)YY;5Q9nODB_|5k zA_GumI=YZCQ!t5h^|M^R_}iTGyA>KjH&DO%7~41iBMUyUt9ea!vjPv`LloMxzVk7p zc>aNb9nVkqrM}uk=!mkMsjOe*goTCO-CMx4I=d7D7mM1nOgJ0)f1-zk(U@Z8R2Mk)&5MXk(Dg*h7k?`EZ+qL>I;u51;$$~aig3#?x{S84~Q zN8)q}@j)R5Q*zRIS6pNm+x(M5irU`HI@Y1!m{5o~jKO?`?jsd4K_2l$S=KS{o0%zS zDoK_go%x3Ej6fh$X99sBQoITs?E9r=#X@y2BT-ZV-5~-lY}Vve@WdrAOY*Vc=NDq{ z1f#=vL;$AazFC!pTN=#YP_%vuyDDF5$++@leqqKq0n3k*xX_*;oCad)z`#5Pge?ng zAvGkfZ~{LSA;kcJq*RmBu8s7+Z5?Fd`o7ND?o&$25Hju$7K)OR^Jl#?D(ZAw4*zvl z{u`ZcgFAvBaI?y)s;Z-#Fp$pJtfaJ0R^VS-rPf{V>Enk!L(4Pxm;hQ+{$5~0IXk}) z2f2W!S@0u~9BHPjyE`{Gyr`w|;Wl?2LQs#Q?l3e)r#j)Jjm@12$0W~zz3KMm4qDRs zuh*);2Zmo-re)53npu+;T(~QH_R8HkhR-PDA`|w#>b!diR)#SMIC~6JQ&UKE!Qopi^EFUyrQ zGB)<~^aNXutZNq7W6mVV)d{sW(aUKiwJ_yp;YDxe(lat98O;_2<0ax3+9j$Q8|8sv zYkl;R3`3}7I;;g2*oeMXQUj>l^_`W_-$t> z`9XsvtHQ@`H+GVegZKYBp&^p3aIM3WXTw4S&hrAWtzyPZiwX+JZD+ymGdKVX0sA+F zzP^6E;0HdwcCfXUv;P5+s$xkMETC_mo^&|KN|_*N1F#A^biIlqjDQUvKrxJ%W~gRs z;d}r{Bs;29xdOP~op0=A1a$11><6A;x#6<+i8`L#g3qq}2fM zLeQuI@K522^2oK#Kh!drHbxfWjC-i&>P(#WaMzCzBD%E9kSIyT&9>EJ;0}izU<9CN1_oxI z%?wN}Os!3fDja)E z7X&9L9VHz_O&FYE1s)ztOG^M~q@<)Yrlf=(5l_gm!upKt9UUD3mG=D1L4iI975@+O zl&^SROxYD1fd!`Z_f6s1o;nk+S0s#PSm|6Y3>3T%0JHm@y1R5j0Zq(6-q6TYZEXUI zzSPR^$m#AKO}8SpOHNI>K4^D&e)6m->C6?8h4+z&B8icGOhCjSxi|=$zY9c|7P2cd zTH4*Oog!>}iXSqGYirj>L9+YmSWEb!cj_l>m>?>Gq1ETl=7xrxpMzV|ep|x8fY+FO zSd7s2<|mHt-q1j72q{Y14QLAE`T29=YI-v7Z?;_T?=AkDPxSl8cfQnxJN3@a&>EK* zkl>YR#a;b@ot&IHd%4uErmcOy-VImxQ%FD}C^huw>hW@O9yTb*S$zYNvvD-?gYbM3 zy1KfYUno$uP*KrnLGx=6wMZa(($HryF}_4rP@%yQ)2Wc5Cj8*U)ojqk z@_ElLMP}7~*8f;pO)W4Hne1t=F+M5jP+A$rXM7hI7Zx>G*mF|+C+kp#5nEe3LKK+_`d}P}3rpj>Z11Pyj zfl}Mf_I}DWvu*T?;Hi#>vrjeUow?jA$4Pk-bSiiP8{2nYfJF1}`J5CW2xG3U{Hxg! z5RSIth`6n20g-I4DyPE6=5Kb_Tv}OLq4A%k7I(_PmkCR{1=a>;#g#zr_zAQW`2c6z zfdy1}?3$N(u8%)P*w>ma#f~g(@5kltn4S(Q$_2J0CU$joDXev^ckA=HRf2*UpX-TW zjzTsxe=963-+{zSSqNOjjG;{KpEvuzaC~_u)B&TXFpyF#O zje6*~PD%CQb*fTHJO?M7k4}!l;I;}ugm?k0Po}TXH6^_Jn*t>| zbDP3FVP#eAG6mW#hmQ%;Vp{E_4Gl@D(s`3IGGpKy&vZw(LzU&lhmyM7c+OwW49(eS zcSCo+4$Tk!^?r{S4XdLJR{0v;ZB z)t8OeuX!I18Apap{+4YHnLMHrThK$|-w3!x$9?>1IKw4x>!2wh5|qk~G&$+K)hyrS z5b&)n2oc)Exp?vQ-%6gMd1M~Z5WH>yI3;ifGZ|Zy>)7O?#kcL2^Y!&5h!-1_E$NhR8f|RTZ`9ayS&-SznS( zz6l+nDK(Xam6`EOz044)`R{AG>pRDp$xgD)zi*EkQ!6RVWQvq&TbA0PmjVZu_h1nU z<~X@j-*A_lg~7T_@QIy>^!a`{6L0<{>Tk%rG%z}fN*mwbFYeF{5mpZvo)vhyaq6LY zzCVIBFZ;2FzroK+W-wG^^4aps;BeDha~o>F=YU@Og60D4Vr5`oqcq_l)uNSLWN^+U z=3PS8XU+kYDP)3Tc(j>mv^pk~YRq1Pr5>BX=z}GK8fQpO5BJ2vg6r6lb6TU7>WX-4 zIJx=}7NC}bhNg?S=nwr?bZfv?+Es5Dybb){Bgyk5ARuJZpTM&(5Opt?kyl({u!x^y zA1tHC!7aOdTK~#|@dkkvud2GC!q$vT8xgam6b>KNn3QyFUeDX};9|ct4XxHOs;^;P z{fyIkA=!a1WxVnQWHpWkywUx)Lp6tXmzW_o>Gu);y0XlkFUbb47g13;7xd9Q>Fo(%*lBrIPAIgWPsE= zSX6lYrCxt{lEcWrz<9|@fj|4`qB|Ei_uG%RR6%(be`oG|O%hZCsdrX1$&vjg#qPeX z1S^p={xxS=Tq+9o7miU_=bc+woXyag8}B+4zrf<2nGp(RtRReucX*6wb{v}HFngg9 zqm&V|Dwsn^hjy88hf_fE+Dzqbg773~6>GFEdagFU3$u=9r;DzhO$A)o<@6+_|0&?{ z045TQfrJKLPcQU12sl*n#QEl?myG9t=DIx4Tnpz1n2fXN3lIaBE`~q0fa(UA&@;TV z`j)Gy*MZNYDTkakWVlYT>3`F`g945gJ8eM%-Yi25hF#$6)sLmS{}I<1HCtu|#v3i) z%K(a8UAeQ%c(Teb_ht7 zRrR|7m-;)kZ&{gkn7j3VCs|M7e^zT8W%6 zdx>* z3>HR^7Kgh^L-KiRd#8Sz(b1sV#Xq1NSvTXJ?CTfN7tKwqI>D_AbRn~E0LLCZm9w$F z*fI<-Ho^ZEH+V@&*|S7ReETbm;`#8cokpJf!I-TXk|$hr%$^~l{^)!Cf6?{UVNrL{ zzvuuWNJ=B!APCY(gVNI7-5@C4BNBo%2-4l%ozmT1BHi6}Hom`e?|Ghc?|(ecFwC&O zd#|0|NUOWQU|p(QnqnTV>J&hVX^#xIKD6q- ze+r!u{U+ykNVGBhPtn~+@JQ1x(jN_ovK!iBaja7ON>H#7T<9~%t*i2b8KH}&F!_>E z{$3<&i!fH6x&EMDhW-%b6zk7Eq z`aL}ZF#L#SY6Q8s)FwRv3)B@5nt`^%8k83_nMs``!F`-bG{?Ugp2MO&Cne@5HWq@~ zz9QJ{WoAR^?djuGngL#~af5JxJi~ZQ-IsAwxwmrI(tz(5D^G_K&&%~NMqT+tJ$>SJ z`vf`}Dqr(6*~CC0vvbt{GADTQAhSrkGIZmV8#vF*y!!a-hil_jPcEPo-n(C>Pp*Mw z7%)jUTf;W{Hra>zF2+RL!si!8bgh8 z>uvDhrs!~4aZg?LT3f-If{Lt}zX4DZ!PSb>%xSD8#B_sf&IF$pzVB#YY#)RvDr}9K#Ac3?kyJs zvmptbKr`d-DFgZ={{HIbF4wrAzyW4UB$SDBYH(3!vd#_s-oscHb$?9q+Gq6)bv8KI zJJTDuRuJ~JO6+5PC0Knj^5LQ186lqlt4w#;8x)#L6fXl1SASpPrBQD*1cE4Y)PUThio=8Qa{qmD?CY5qU$DEpF9ic+*MC9UwV3UTG@HZIBJoKH#EUQ!cGqg?#%`JW*7Qy zye>Xfk?#A4;B)uAJ{6Pt_ZyK;=beQn$R7*T8fj|IM|)xq_!r-viv3Whvc4~KME`@f zT|-Rqs~_giP>})|u{i^A#j=0jG)&Zih&9(ecKBY{+YzyFaqhBM|FK7o%sD zy`wgDv?W6!|PnqA6F}j$LHU03*&HM0P4!x_OeGXX<)Kg%q0Jd~eScoU>yLTMa zlh6(6(PcGkn3hOV=(gg>Lfzprgp5BQjDf^ScxmrY-(R(pmc`TGa-Ex}azwoThP?2w z!bV0>VJRhHDYk&XJT(~r#~LSnhTzjCe;+dinl?QHJq}i{do|n9P(+Xhfs6(yoY;Sb z>Gh&7oY|4kfUd9|zpKjD4$$%d)eO+EP*PG{eu0)fzI}b|YNVqB!g)*F0|+WjG_r3t zj$_{FA%#nJ+T}6kt|cvzLlWj(WaAWX~XLVJ1K` z~a!2LQ^x7(QSvy4Dhv2&UZyjrg9aEpdtfg!fLDUOCFB9(+<(A z>7GNr4r*1)CJ7{2s|Z^O%*ukyg@ZdYgPH|Unf&}8+GUL)?fN%__f$+j?=KqGV1u9k zsU3NW*7>^<)Po`C=bfVPoK7Fa%M*J#cSbt-SmHbU=wYgVN**el3BQQS8-vd0O}j8O zU$6Fbdcd(I{NbW`a1QyX^#xf3n_5b*&UC-qO0`G;o7j>6_3MiPjmcVh#jkW_ypZs8 zb_I+d-Xd|P7yGk=C2QcMm1x|u@YwBI0!0+dcHY3l_qiK^wkJPGw_HEB zng)?P%~tIb$v4D@;1-8o+ibxx>wHhYA6pJLcksx#Z*{)?07SUI?kaHv;mn3ur!~8=1HVu&D_uUq#awAP7n;K7IfIB$ zCoRUL`aL;0IYY@kuFLV~8O`;lS4EO^R@SPE_ zv!1_lrC&*jp03S7oF3HC-dT+|Z@?y(_{1{;`Qf==B<8iL{9sveF}N)Ny&Yl$10dyK z7)tV~Kr_gw3b_yK6f7mkHNP!rupP)&fha^=^*|xeZx}!ke8g269L;+wE@B-}KS@Zr z$|Uxwe;UB_W;Pfnw*Nf`IfaeVfhn9XD^7BQYgrKw@IjFUH$HTM?9^ zes5tF>czm-4IG9Cr-y~}Oe#bA)Sc|!%GRNiJBzaMtul6g;?Pkre?|oNN=zgNXDno2 zNNP^WlI_>FGt3m~>Bd{F&TDa4JDApKuH7X*wTO#K`;RUPbl3ox^(aeICq%4FFxXdb z7KwaD8jsVLlE02gtwN#3{&5XQ*=HBk5;r)kK}U5g75xU~M_C;TN;N5zE+#iG`v=hB z0WunP{IfM7^3vGMt_ORwpp*xRzX;3mNh~2}JOhnb>Z7DgK#Rx0o(9O%K>4S-NDFKZ zW@%}Bnqa5=h)_TvRe1S25A=(#uCDX_W!&UWWShPC$sbOxXJ(u#!~dyhNPAJIux}o9 z=5=<_02zpnlKfxx-i;ZRG~VRP(@8gbfnHQCh&Iw%ra=0@NK1yyaE|rV?sQ}sL=jL3 zDd44DKw7=;Z^386Et49@GNXAu`s_4=>Q~%R{v92Q@M}TAk}5;%5MW6yPq*+2WAp}Z z<&+^L#-TMhvh_8s#?G+1hoP4TL4RtH;ltk5_oaXxkq|vE+`M)%3JZM zP$(747VZ>cv$L{dzL-GlpOT1As9=4rnH(H+UjAKwEKS4%DQuKxC#NXXR zy@6HkQuQL@WT021U#1RT1ELbp*c3BM%Wr4on7I=6>2(20*|OyOyXzbgK*wvW;7I}^ zp7+vapbXSe_GM*2(Fx=gAc^$zmzUS)&)}xBQ-35)a|(Vzl$`u^Z*ReSHFr1HPymCo zcMjvX=iJyD#nsCQb^bpkAJo*;*vrCW;b!aeVuimI524?kc7-f9G~b^r>!Fegcr13O zQA8j@fkA70LS^~s|e*^l-|PhGZb7%w;Rj#&gZH^bDW0yzB$VoP8f3ik8sJE61v zE{ErJLe8mU<|Qs}TIw26$Ty@s?Ax*+{F@+R;B(A(nPuL@VJw?*c%NM@0 zYIgU}qV`;SAAX^Rmx8MkffZ7qhxsYQGNRWOUaZvntYUYdy@-|sj?OzBL76{w2WIf| zwaUmp z!Kx?;(xD!zQgiPF+}CBB82`P#$Q~HFN<512e025?T3EU7=1x(&$i@^EK~||Sdu%>% zA*dJ9l(Gbw7|HTuEm2Bbgex6S{++993cFZMD^F=l>Pl3NC;J^LcuyE9@5On|a&@T4 z^vl8Nh{GD5qlYBk>Ee#Jy3J!Zhx%v9>ql*Hz%V0;`@0O&7G}-(_yv@>Y~_?V%Iqq< z|K=Mp;WV63M>E{owE2?Ksf4|sRNG-`6yG-U7;j^63?4eJP1V1wY53vF_fx>EcPwkau+m!rez)}jcDvUd zw99Q4YV#~djqim}p>UaS*U{0SyvI7C@|^qP&ZC%n@tns`yQfRf0B_}v(dsQW%kbfq$6v2$n$ugM0|4;w0re6jZN-G!i0wTr!VNthcGZ0Q>Nh^6n$$xfpGfp*4(cN!L^%lY z^;OC&X9ausH-d*#5NzWUcD~P6bLENPB#n5#;24t`&Xr2-rADeFGG@(^evbKC0$Vxn z8%6JGFY74|M~KiU96wy@Hk?e#d$#9D=hNq*$k`Wt{dy>3cx_L6;$|)6dcEh-t6?M^ zKNVpbU4)@Hya>`!&PEJ$QvM@94)(woxgIH^O)rY-{m=KPC4$JHOP!3C7xL_4^OR z@5ol)!~CJpBT$m`oTK|CfsOe}!k8?Y0S8GQ_A5yUMGW1K{`Jo2K27qTukXieNDZq6 z%x7+!KlC8{)_=|dP{Mf1DUDiOw#CI8P*9&-RQ#?KYhV8JMOgUy6reI%LEFJ!VS+z_ zmgeDiUG4HviSFaJ+I+S3a@}t93#_>@?@zs9V1G5oynKs^q@%q!WxHrPXnZhJt4oJH zvK{>Gt`P$b4e}M6W-PHS2scPHz4o+BB5bk6o_6E2vBj4%mZXORK|F*ej>ns5--;Yr z<5ZN9lx9|g_Z%tEtJ}i^QxllhH%>2Cwi>*s$!`R2)$W&{W5c>#{qau7>t!j! zTSAWKKTP`5$)K8F-O)K&MD{4Ey6$@TETd&~J2&}lWCKOoNPMsM(Zk<3`x#3|vN@CO zzObRY%V&_(n2d4m(`6(0YuAqLMm+X}nSQI2pL_E)BY_!|9`;A!8K+zA2g3c{EcV)Y#{)=NfED2bSQs0+<=8gT7vp zKsF;l@2q?OET^fOvc&hy$XG%7Kn+RkpZ}b56!zkS&!+o#6(r>JF+9Xwlle{R1Om3< z^-r~@S79&OzdgP7THl$Hanoz@B|Sj(NeS36Jg0&_D+qI#0Grwl znG~z*3VHMIP?so`4q!5upyB19(dDAhJZjw>9P3E7K_Q_qY&@cA9PFLNQ0;G_1w7o30E$~r8h6u#Gca-1@fv*B-1^*ao%3Jb*ol32ZN1v*wF1J0>hU$9z;fBnBPU;9Kkw1L>}1I;hd{(gIymqX z-k#vV6H!sT1YLX{*uTe`8qLmpT*5<0Oc!Q+7W%DA}RF4l^%Y8*w)M1vKy@Xn>R&0G*2D-yO83Y)@i+zD0|x5rA7C(ZzL3q9Mlwi zM0vtMy2C`orFKV?X+J6^{tfQ=z#C$lmDRDohEzYY2acNJ;-!nze*eW`@WEqjPKdsq zt>^fT-h70l+Yre-IcbBRt4Le=mvPm-L;) z)`H_-BJWrFs#2trSJ>>)+}qMS)mGnXu*C(b6;_(E!kHqdo?c6EzzC_B_+^}$j}^99 zG(YrII6n?0Sqea>63HeNCu15}4PcXoAdm@7MUHR75BKK`STyf=!blF+LrmXSx8D@a z(+nTIh$TjZ_=S<^wnWMgZno5{>1|;{{)nA8W-e?wbgjJ-7{9*<5C7ZuYp~W9Uyhaa zTDrqX(p&iXnjZRki#JYx6`_bWv0Uz7gL5dM3)_0<7o^;B(^|t@6gLoUC8)sh1Oz8D z^G`d0tA~YzV^3tf3Vn6K`qL=3Q=K7dWy_X7K5aZ)(ihP~RIe^3ZDjWy4j1GrP49b{ zui4^+0_m%CO5l;a(%;HQYm7^N4$<=}^IYtFsEG6;7;s^_m zj?Y+`berum;TS+%`|yj2!kFAJKTHn#}y0Xq_L;-kw!6516w}Zba$PneZst+)!Iz7fgIhCs=bd`oW0D6Ks=8-Y-ZOXm66m_6D}HZoZqCSH`$sZ>BL?HIPHJkEJ9>zV zrsBz#fk4>{c$u|)o!>LDpv9LlxrPD6W_4|Bf~1Jaqa=H293Aj$?btY6ae2+#H+e@m zf48kng(%x)#gNJ2#TWPs$ey1+owb=BUWki}+akCLdQwit%BJu&iOMQl(VSYagc+Ba zvZ|oJbM+77hCG?%u^~?^#JypI$NA`1>2#ine6$o664OxGmpWIyB_!&3&z0QtQ1yE=@$+Ks zmPj&8q@0^8ndB9p>SNs{=Zf+GArVYkF99Ox%EJCxB?!MZ*)-tckvlJ>@?u z7~gIYA$}i1n{EaYcz%KIv)WMN%X|sC#<+OCsZWU@I9SoJ9e5O;;;TViCdd0@Wy83M z0^L@}Y&W~)5@%w%(Tke#?cbFbEh9f+LD}*ZkJ6+=dT!GK=L-IX)aQ)#@o%vq`By5? zCFzplJd#ybRr~84*WzSwnFrFv`M864ArQ!uB#XsF%|0#r?MBdW3NBs&Q}$ChDyjZN ze&Sj^6dri;4Sd;ES~skM){Hkv6ltG*ecBs>VnBo&=c(A@((~}*VhEDh3&hl?*i5r@ zlK+{4mBlO?6IU_&R0yAs8n>7*e2{EQ$(^MnA_{PPt92Kk@C0Yy5-JvTO0}P z*pjagUqr|Uu$KEX=}=)K8XCTR8N`)dn3m~~AwpZ=^~>__)UETu-ux?puEC-lRhqrc zKOSj5+9kmP;%%CBBc@|13 z|G8NnU{Y}5c=d8YilO(KG}Gcr8ySH%-cOZ78N)!a8*+A*5_&ggn4;>i!{wqa|C#^w zi~FK$q>}&=J#iAB(HEs?L<2l8J$c0#l?nD$N+g}y#R2lgGenu*Rr9RJ>dpnQ%>ezq z4TS{oQr_kYxMp}=kux%$`{0m1JhUK#23P+>Rby`H(Ku?#-r{3Yl)ZaJQ^N&e>iV-X zP@eES+Z6(=(w%u0d`yBHIGCSg7+Udh;e(&P0y+?z3;qmGsme3|PFrU-H(cnTWK|H!Xg>~4Z|?*jaEaT%b4&hq2T() zn?L%WeBuA#joom;*)D0~ycN@&TjFor(btTWs9ohe=&B~+U0+<)A!>h zp8iXP`q+Xr6Ui1nJ^{fif;g>N^?5WF4mMi6nnAL|8YX8Qyc-!RZ2C1jBc3FwjEMfq zZ>eP%5d7@yHP|9DYfDA?Xa_7Tq^#3kRQGSq{P)fGPQhrSRkVt4pV(i_vDNr9r*Mwa z2sVq2uNX-J-l*K=cf*H`EE6yhr(N5YvQba~N#G<|Jsg5QG$b&t1znisqwSnMvJm!p zpgoC2Bge(UL5Wuht|7U~^eNZ^`NeNY0ciN8mKINvjd7;J!nV-+H@L{Yzd=OI8BW%i zp9`@g8DB9cyd+Go(3xKM`Y0ihTU2CHF$+ZL)>w-^-<(-M)N4PJ*9kEs@;b{>L=2<| zE(=wB{z{T#E7xM%LY(oz;UaK?gMA1KuFDOZl3#{0^9jfP934&z!SZHeH)V$TQ?_hM zD5sr9&1|)ixZzk8@)x{_Bcs?@prk%7|I7(V23F0opx|JkSvaEK^ya!Ql-SC|Bx@zr zl*Rr|Q?P(Y437XD& zYxBK%9b#n@$02HEu&+N5C9sNp%l=g3XMR)6^EkO$@qK~yj~7Zv^pmUa-Q`Y8qh_4^ z2vtlKq7jLJb?m0^3po-&YvQHgn?d`R;4u^}SrzOaodfVkCAu5MIv?FT=N2zDyUD8e z!uI_+{7PLh7ZYgFfhOA146xDRb-OPRls>Ob{iwq>m8JMBbO`H)aVM=IJI+cwP>#&azF@C9YS;bE98g%~JA!X) zYGTmF;K3sE6LLPUkIP1Ukvvyywd+=3t6C`iM-6E`zw6O>(*8vAjwtr}bn~rtZD|P& zLVBI6=R5wAr+v{7VA5A1hl0)?)?{p{`K-&#-uN()u0fTKY1~7Cw7Tk1F_Mba)%{afHntuGsc1Uz8NjFH7cqYnwbCS$qB+N!ihr zQz^0NyIYQeQBMCgevUt2UWlr;)%g_bf;B%U$F>CkB;3*d1LsQkV z)NMjK=lx#05hS}Wy{I3HTy3rA>!YxBD8Ah@P1*^?{|07Q0Bqn_jV`F39F_E;7$!U^Iyx>wvbwq-Oc*#C zmw=#rb#l|C*7!;tQD~?xHsa)yg$IvlK>W*x&2Ad@bi8#A`;P}XNH{pB>sYg{udD=WKHETnIsXHD&-*MtB7 zlAgdjJyXf4NlQxmJY(nJ>a&zzL10OQWBf69D?NT-`4H;-`Vpc)+Y{Kl!7-n~eIC|u zGvviwO8Leb;2&UH0k1++kBYbD{vszsT%zlucc2FwksV3)!yDat2XXb8(9+U47=Enf zFv$9W+Qn_jmrlBF{c6mI{~TqgplB1x5n-{at`WfV*D+DF<3bTsI|T+&z`Nns8y(OZB;DAgBwYkr z<)gzPOZ?OwddXmKlRtD)JLoTao$2bEiF7=Dj~Ws{O|{XrVVXlMGG;i0*106uk{ zhCt~5=IR>wTnLN5E43B;Qi1gwg(L7MiAGiV&VE>}cM9^+=B6h2c zNGY<4rz#Kkk>djhhdZ~)v>y92{=@9UJT zmXuWl%9MW$dbW3S6&5K=?Y~ zDCqfq*8?B=?h}}DH(5Gv0f-smGg!m$rcd3WQADzR=WF1?2FA0H@K}c;B?gu1Y;iYq zmWfyb19%krz1-(i@aGo(55>s)MLG(8hr26WFxFMR&V^-&dr0|$T)cGQvN>jEW@%wy zCJq|!Fkj`#fXexuyaRU9P(Wy~$dV}J^FORuh@&I_osk0=7pHCIg3kj<)1vmb3_uP5 z00nA_SNyL8(0G55`Rngn9nD6EZQux@sMQ=S6b(>Squarr?oJt(1Yr7Q5@HBoGYKvF z!oW|)l2Z2c^pwkHZcSi_9|Q^DSMnbQM^<=}jeBY`8c5uWi(1aO{Otj_TNVEGDO$(> z8%x8_Mh3iV1WzJ>f_t!scCgcS+op=uf!Q)m!bF;_KJ%9`>~fq87TZJDm+l3TPt@ak zl0rB~|K#(5nVFez8ECul9B`KaCMw!Gi;%Bv`Y%Pnr@=ssX#p)ZApK->9hj1UYr+=1 zAm;mifh#hAi~7AJFhnyk*6|~~cC$mQij_IhQqsE8z~3pG7zNF(x_fTLO%W4F6)rc_ z#CG@h4k_%za9!dpc9yKA1ciu_cXu?4@K6XV4G$>q}@m%mqw4x8eTqMmD%6by1V5fH+hz8oK)g25oI=)c(5d*|$seWmbiy zcX)pJZ+U@KfnJyNxmJf_=j1I%(L8C?U1O=XFMarBn*%x^e|_jH9Pz_Oj^7L%W&WI% z!^hU++DWh;AQm2mj0AjSz%E~N+Yw^10N@&w=iJ?|Q}iUUXHl#A`v*lS>z*8LHam#; z3BUkoV;@CE%*D1YuJ^T6nY6HVCcLb%32ddg2=AG4n4<*Hn6eTeN_#3c1C|FqO z2q$)@@*F%#0)p?ZS}zfRX~j{}AN>;rw@{L8ewDM=rIVOT246+{5&RnjMAQv1axv)A zS%Lpaq{+uXk&gUVff&sX559e1q8uS5d>Zo<_szvH0rCe1a7>Z-Gs1(dQCS4T<f1qo~iPuRe#2QoYE2i`Y@?VXx|OGfCN z?{gs#e|||3VP$6ged%a`CQ>Y>%@|Uc_l|AOYTt}ri%Ycvh~oMWb~gJpct2YBPDLE& z-OH#7awC-OH?IxjwBtV4Y)q{_S5$b+sjTO|P&L^rl1d7fq-t)G=d(K>1nz~8YHRHm zHQ)!*^Wzj=uSxUFX|J$mS$!mXS$t=%^8U_*B&^2#`0tZ-6dEzb z{yYJ%5GR$Wnn^7sYg^pt48($x%Vl~I{`{$ZFhhp{uTK)Sz~w*}%_7@ZW{nBpanm?b7CAySi;(4`7 zsl30!w({-1PLS$ndYHG~@-2B6rR^Ra=MVS*+`&0qH3Mvoqg{7(qzf{@rQS?xx#u*$-QL3Z zxjWolGsrf;0OvPo;7CEAp?$nr%2C=aB?fW0TXD!L=8?||J`tX-9Vul?;=!|VtDSS4yC zKXn?s0=}Kx!5?Y`1oy;x{;Jh-T<(P*0P-9BDU5b`C;2~scK7!7;?L!iP1(73SjwR! z+9s$Cy?ER@#{04M11{b#4po{wIGjm<%lAQd{<-p@fg4h0TaT)vi@V(WK(o;^ZVIl+ zb^s=C{Ro1oKnp7)wPR&a5CHoAXLbVamjE`S#;^j&kYL`gDV0B`_~bbPOgjL$3D~(v z9$NifWdfCwEP(y|LxOsO_fK~OzEArMkl8n50QjLN7(x<8r^rM8P;NQ()S-pDy~w~N znQ1|XkGs9P9@{k;kvP|&7S&IR#U_s4l{qR=>vp-)u3W`0r6k$}hlA-(E_s)>MkNP+ z>V>gYm;g!riNEXsPH=zm&6+wnh8bNHgY(@D)lHb$n%jN!az%bG4QlPr@#v3&a)BUy!2bZ$L^w@gv#C zp3Lb7Ur{}5HXB=glm^k>cj$({NVV zaxyS5m2g)1*vVKDi{BJ`m;PZ%vr3)$EFAmpsQY^&A0viNr9 z?Z3P&U~?5EaKg7V2(|xA1EO25a+h4JoHTfq6u^|FdP(Ddi>~eJr^~)calYI3*&GzG zo!K2d7ZWU+zpuZ6lmhl~fKA7}|48X&K=E$G|9@i)GOhx;qhoiY-`gBe$4>i_)o!z( z&g&w)kB#|b|A~b;hWwhE_{Y|OAoeu`Zx8zU+=~(7apQl_b;!GOpOo5=!e(HJ&y+J9JFhI)zf(o_cwUr8xwawG;8vOuJp+(-b51I0i-wq zU+t=#e=zu#Z1nFJ+vU%<`AY@m`HN0FtQRX^H1*P`eIksL&bVZXzhmGqFJn&Exo=(3 z!x~K4oMKW5nCQco*7~yqsA%=D$Bk}IEpA>FskapJ|HTh{IdLu(hW5qDY zGaN*p!G5iYdNuou*{G0vcH&T&sr-UvjPz^9{r6#C(ub?96*iAeryv*?lQYCjJ{C}@ z0vjk$a$5*|vEt zr*2OEnsVL@WdVwFaG8oBOZ#Y?U1&LBguPceCB)>MOvV@E(3dK%_UR$JN4P_;+zB(n zEt(FrcfJ%al~_PI;N!Gicgu!`v->wVVOqhSd%)z0nQ4C1cZjOp4H7geMYG0IOpXO; z!Ue!K+m{x$kO3r^ZB%eydLnMs@dGTiEYov9rc5Gc4zeaz2fc^%SDrc&)7QX>4lrIL zi#`ED60GMaXjk-_K9ApB$}qlF^8hq=hjTPfuo;+$fw@8~ zI5(h=WWLQexZT9Z!aoE+wSbAEAm)h5#%~U*c@}KFUm_wdnr@v7{wG&}E8b{G3=3K3 zUjaD{nEmYEU1-^TQ3H2#SF0t$CsmL(6vJeJ*Axj8qq51dQUZ@ldeFs2*Hc&5y3iZA zEXD11@h}@R(~XVwNiK&(IMX{ij=*e*8I4MHlG)iId=m=p&)eI(7|xxFodZX_f_e-} zMdQnKB~f==RSUC2SG{R@a%eKd!>pjnejgOQOQaGqwzg1TU42sj>ySFI*OBMDbn%_q z+J|xKyo{;4yn4nvbT8g<006NT24KWlm>F;pszp`Bzn@rU;bZVN(sa`*+;N1_;k7J_5uuNcBKL zLlVvGI5G>kk)0l&qIx-dCp0t^7tS>T7(M6+e<$_$8iPlfgqCk))!+Ei`OEQ>|58om z1a@UO0X;t{VM1KoF_0kW$#A*qHs^A#0ba7l7s(DA(|Un#PmWKMTJ+nR`P~ski*P~v z15~v4clUvB9uE!<08i;=I1xaa<3JOm+bERy-POIf&vJJ%_btHxiL1x%oP^D70_Y>K zD+JOTPDVzQkhS&oMeuW*55k(;)0AMCY&|e51jk>RJD*PjY zZc%&d91{s~?D-1cXU|i7>^UPpls2`qQNGy3x!~zXv}zBVrA!xa;BIKU4~90xw%lyEpy-(R$fW4?0*ZQorM?484ZjZjeF4ZG zbaTOF?|D;a!zAEy+&P@ljmOM}evj)k#3<0A+(O~GDG?%j1Jw=Pcy$mQ@Y&wk)|!g8 zWl8`Du6K5JYBS-XCIHcZT5YDL#n)Us)(WkS~E6-fOHfNi0E3sB~|;Uqrnsx)9?{LTwyhl&T@XteR!S62b(1*cs2Y=!f$s*jD;jkWlf1Aml7>$mz^0 zL4ExeU0c93Y{s?O<@d$_xNT@iuL?Q^+ddk$JCK6&p$~-OPqN9(^Z@{RSsJ=_MBa5t zknWm{j|!*emH^=Fg$jj+BLXMW1j#*Gp@KU&V2lkkglM|q+eS^Zhqm_JIkXT1) z0Ki=sWhOO2c`sev&<+(Vm29U5*Il_2a)_m+CH9Yta$YpZx16(G=eA?# zd~PoKrQ{Cn8N2N|c%@Gtvghq=RJ)ce*_6d;V_Bx|9-1XVz#iSTk(o)nF6sH6Ld0;l zhwZ!qJ;}D|H)WF3yGr3BX<4Q&1rG-8?q&)7MHXN4y9?Ew2!jlhk}WuLC4C05Jk+Ri12>ZKi}mq4 z;K`WU>$NAu;av#YLBJ*y&?vjWby)m33#*CtIGJxWdX1V+V+#a!K*{4}EdLZ}8y1xWQX;@LeUOWfxxj-sN zYXYc4;4`KJF&;{z)nlwi-=+%R)GX1U%%6zUWyKYDaP<{1tqG?ajS?|bqj+2vXbES7nCMrsZUJ`WIM{ZC8&w{EGmG?TgL++_bJ84T$Lx9dUI(;n4e z9`Gtf^O<7&zh>60siFe>MWZ!2%QefP zukdpP1{x%vN_2{72_%w*I38C!pxLVdM5+C5dRo(*oR+X?o$T2oADLSU@9K8drwyKD zHIh~US(hek7wCFa5^^cCB{kplF}y+ejvKA1NE_aCV&T8@8+}!S26}Mzz8|i+Y`3I# z@8Y)s1%(Ntl0M-CX%k3OP40I1)jtQ{S{2qH(7jmyYjn}@;7?iY7 zxW!>>Kcw26iYS6Wj@CvdkYC-&h2lgaD>JjC^9%AdJ&Ncot#j=2=@GdXoG{vEe}~J^ zr4szSo!8RP+2u+(yIR!3(~TUq9&rf+f&9;HV2WfNP?gU%vThxK!W{aw4&p}ApFbcs zE@I$1fl@Ait%A@prw&oX{E4OQ&44cr?OIw|nwY=__$>TU+W(-KLBL2YFE4G>oKieA zaci5~;LfFiyuKzhQBzu4`tJTNF)1;YlN0zy$8tJa-U9d~=7DOI>SIH{ReitJd!Bqx zi~wU}sFt`cjoLwz`fu%I0f+UFxD{s@GV3-vyR4QM$fhu@^X?=lI<7u-eE0oqa>jN^ z$G=_{#`E7LToQkho-`{;VV&D^oAG_1gw1$#;WV%}1>HMHZFnzw*gA3T8FtpGtI_m* zr{Eg&`XINg{Wj?v0lq$Q?(=NMimD~T{l8;#GiUZ{XMK*Z`e~_M!KbBQi5^-9C zEBkAUVlq&*Zarfs__P*)fz{MV6HF>-ER-t@beg!-rt_4tT|!Bhd88{j^7{w#@Dt|g zEoD@E#NM`5iFI%IWPMB< zMwgSjtudSU&ScP5SgJqe{4r_R<}=jeb6*N?lay6QKJi%1-IcieN13jreDL;AGYj_g zE(cs7jgHJzBj3(pb}RYBL>B*SZ|@GYhUu`+H9D(+3Wrdf^(`&EkT z-!qixk1!kJ&2(mh@F0V3R4>h7s$w3cc<|B1$w?x!LF?6X1X@Sf+36OXs9^EPWHaok zs9;FA8a?0VKt8Hlrh{#t9N5UR6#LgXrfWM1ptII+UhCx>y~6i40eUkRVipHNO1EO4 zcuB+gMKZ!Y?U&r0oeRoB)ahYj?&NdU>95ko)|mYY>a5w_ZHseSd<=}AR5dCQd|pun zoP}Vw^91nj@UKBa?Uxcc4rT1TtB>MtHyFGy23GW_NehiTYJ#V4j%eMw4J+2N@nD0P!(eZ469Vd>T*bI zog$}lP|MAIj20$~mMCYni728+sj4_VJtyAZlV-vX$d#eA5T3Rw$W1Xa%oOJYQ^urEN9n39^oloDO`W1k+9`d2ua7~Yj4^-zgeNOi!m!!xjWQDJil=?-&mhqh13JfT& zYH0H`u-&S^;{;iVu=32m8h&Lf%6h?_VI5)ttR}*`__*1lD=lIRtDtUU4>)m9@~D=Z z9N4NLT>x)++ULN~mJRWhSVYE`(yI6L3b`*jw^eRD$OL-iT80V#;6Sw4P#Ufs@m!>s=;h`9*E zs@iVgGpnSyArwabofo;-u{Onm?3Wv`w7l-SXj9uH;b#-@sW;4rG#fEf@r8dGqI&z1 zPuQV1j%MtNz+`1+qNCCvKHmH+yOuHeDA|1J&k!c-r=9{dl$w>`Y2C5~X4|zK+ZilT zwl2-929021*%X$ph2_C-l|``}4u^(jG9LbO9epBD(biJQq3q<)>@<7-L*2DK*1|zi zM1PG}#jzaLkrmq_wBwH6lunQ18byYC_FZUZ_}fa*{@i-Inhj{2O8$Jo#nY$pL})Tu z-8!y;N9Z*`B3ylefpb1K;c;$7p3a553oCBD`dN$i9kIbD7sDbgt&i8_#aP6H z4{yyoz=m5}&{V{LNIS5O|E3RcA-)#&PuI|hekMgU{;kcyPe7Xm7JyTfN7?0qmHXB$ zm9c!b)n+vCuNt%>Ik7ve%nV+FyB+QKZ2I8=4+4&T&Ioo@SrLHpSDf9Ou z6B#hyza&OtY1OYEUL~;Db)I>4J?UBb13sIr$K=E;YfWX9ITofJ(dq;4crNsLt>F#G zODZm2Xh>5^bU6CC5K#>568{^9H68*sFtz6SkU@s^50iPr7-lu+u|6uRAUC=RckDD& zmXw~HE|};=%9*N2_P8<3Q-vD-`sebUhT7|`P7XesUx9j?e2;H5Jo9^P9}1D*o!|X6 zvYVKKWZ1riJi$&KUKPSUW^YuN>tjhE3+ut056Um2j9I%hqs5c@RATyG3TwVdryaS1 z7!%xQ=ql-Cg{0=}6r$eZ(D zrM&No?ALGEc0w1FD*8^Lr0n~{x$e8#u5TsV=1u-yGASbgr*ot8GD&G3m2CN9>D-+r zk}dn23)rpjQJa!*8E_}{xh5=xgMp-SYPp(`i!MXXex~L;%Oj$)8&UdJ4*O#hCJfb2 zID8ku*^Iv8+t0W|D+>5JQ9ssK$;JV%{@l6btjIexk%t(X=57FSM+uHh1+T{Oqt&G6 zb{ca3?-y-|j(lv;Jg-$H_-l%}mT7w8nHqH*`GqFTD$D9Ix6f^H$NwGQHV(M<-gC`0vu3Sxty!~96xlMgrw|A)0?pIc zc-~F8G?K6H3K>2Lg<r@JZJyJ|c}dG?=Zy&i%jU=n^2fGZn;hl~(C z!F_-X95J$o9AU%M4*NL-KyGiM`e%Z~pQb;ATQiC#e)T6$>+cHR6z(3k^NZ^^_c+0ExxhU3ZLM_qZQI(0S??P);knA6&zoOqT+^_S z6B)khVNHX5vW^L|<}+s>&6UN^=ysTTtAx0knEN@8LG5<>r0^>E1k>Lpx*9R&di6Mw zdEvWcw&tJb2PfH^0TZe}7YF0nftj)AwM-=MiYGv!H$at)kO2F!u?0?l0s|l)5qL3mpodU z-_YboU96l}iQPAtI|Vi+_&Vsu?G&N${G^+4YAn&tn_=zJug;A(d%5*DTs*kWb>-`o zQ**$bub2zd%6S>v`z!@TNfC}zj%h_xPhoC+q#EJ>L6n=_M-Q|(a`kO?krROIJ4>=RS5^F-l)$19zH+#>&df z&a_`wJGt8i=$be=ITxtqz*+nKJ@um6spL_lDIfe$E2c6B-5yt?viIvUHZ#uy{=P=>`EQ-}mu{cnAP0c}3E0rXbm*MX!k$(}!mx<99Ma>bp{g51H z6Rh`*pMP4o0oBR-M4Ua$=)EMsCKsy7!02)Qj+}*sg|4tA9d3|>;sE4fy@!8t+?wf2 znCQ|r2hWNasUWb76+pUEfixi+&D*Sp^z`{>HQL2%&59zWAM|4`^mXn({3{Dx&S*}S zHL@X9pQHuWbOYM-@?tX06ux0xzGIn2RvE~(H-EM8uccb@USpxJeTHh9rHDQy>^QLF zX)X7eKWXy|6;T&SbN3X8{yb3nExk)xzcyW;R++byJdfY;E6~vW(?EZE%83y5(*Ue@zc^_|ZvNu1SR@ z_2rCtCiJW96`ez7e(gK)F|nIcxOW$(oB&&>|x8)dTF zxu^P+MfwCKuST^$eh$2xAq*un`wbNo19ambVz_@VITmp`WTh#ve!b+sXWAg=og0XK z34f&gu47}-s_ixZU5h0o?1_~aHbI-rOCG2XB~HKt@hdX}zKQ9(FOAKJ_Th%hCE{`*Eq6r#^yk)x0b34fjx{z3Mon72Wk1Pa69G61P>{`?`SKNBaQikmO2iPIp|l&iB{o7qk;sMK zAu`DPsaf!4X=f6rpBU@|nV^eW{$klnLplfqRHOKZLCpN%aA9Z`(T1~}UeJiPv48pF6N9`qddqnlhU4bf@hc|aUD>FBk( zJ6T@C(3;No48dy47ki)8c9V1H+UTTQfh5ZzSdFs8;qTM{Qno22gn*0yuRI0W&we7g znF)h*J$HEv6@$)u{OJRhcP3wx@e}Br^O}7vfv4rSKun~p4xvx)_iKOf*A{Nz z2~+qoDwF?lNkHLmC+9p`PtmV>KJp_*w5kZcdW+0*q0)0_5~S5`$@DKI!)%ok&#O6AHqpuL5@QVx>g%U0_<+lG){O8>FQs2f+u! z_iYN@u5v`8{%3ETc&{W8U%a>ruS7J34qd~)WIzze6p&@XmS~TDo;g=nJ|@~`>i3)z zJn{=JLPj%LZaxCcWMW^lULZ<**4`w~y79f0kZU{TsjUCtP}Gg1w6eF8BR?G@>%{)e z<%#d~P2pNs#(!SFjuG$^cIVlB)xvHo7fXY(m0M1{*r2K zMqs^SzMp2=HznF)YZLiJ!o-Azru1jo0duHdM%ZBC>}~e~C}P|w7G95BSfZ}335zyv zym#TPCCP0uvS9;_64GLsn)ynNE;2PM9Q~-#k_~?bqOcY{pKx^AloBkUde2HOZA$4{ z6HmNdK}C3{LVB~{@IO9#nw$@Ez-y&nmP|9dKH_w5ZIz$yy!mN_V*akJZVktsYw)87 zemY`p*GQJq0T$mJl7sDxF5Gx%pD4WdQX|Aw5mAvwtb6~g#XqLHx;VK??H%Ti4ngFi zqN47)x|w`kQ`4?0Jx3QJGmdCe%gnwC#eHzLEr$(oO^gG&;&Gh?i^<8!2vUBkFO6OH zu|CD{Zl}-Qip80#(9F+KF#89EwHrhoYx|4PB9<$;R99WSib*ieU{t88&s0i3K~{KA ze2f6Ur0e||*=2VrcS}N`$d3d6P*AS$btkI<=qRhVJyShW6axa`>FU416bOXxy>D^s zVGEZI>?WtIeO2^@TaamYSt|1UoO5mEAm%xX+tI>J(!C8_)$pB#?cCVZ&!0aV>YBOi zw@0GL#dmkAZRz0g3GrtlUgbBSRuau64^9Ql%DkMB+FNR#pPh{#G^(848yOkd-NmaE z1spF?OniJK5E|^~iG%0o=V+*?l;>}V0|!0Jy>lG}_65TLof$_xJ_G_b&Ay762^z~( zR5`HHLsL>p=6=*PT>Oq_IXgQ8!$d|#X2E_lRjdIBg2+@Aee7qnpJ}ld;3=B_U2GI* zug>yX`oM(>nvM5dk<@LIE{}$LI!pL|NMSeD!-!AwI5F%j#ax6^DNBSiS_SLmD zdU|?rMK*ggTkcIVd!j(liTK=|9Ua-@2C8dn(2$W8ia%*+m>U@( zKnhh$!vVdO7+PTE*EO+8Gj*er%|SpQl}Fht*Zao?OsW&u>yzWVGMulm^lJ# z7*^V9HD42Ea{<~!uf~0AxJv%@+g#_|95`LW&*ypjVJC3K<^ur%0pL%&)6VOdlyrTv zs``59MZN#%aOWSJksiL=uxoxxo+c)Bk&b8Ku8C7V$(=@)($&*}dak;MwZcmQPxxF{ z(h;v+E&qP#z4NIZ=q$BM@ruixvJO!GQI1^#^vNVtEBdTmFyJcQ7 zF=X5P{jit7>c+<8%*^(mKjl`7T-bz?p}C_g^J8PoG&D$%--CmIePn!m98MQ(^i$O5 zc>oG8NE@gN$C(EBlX!S|04LVfqSNyJLVaaR%YEfAA2>L9czpcE1u0i1G4|)r&sti8 z8@A?VW@4evQ3G0M^1Yb&_}?`(=v0d2dB4T#m4AMPwgqJG=BS4hdCh6rqt@G(4tQJ$iHPJ3=#UK7ACkV_`W6!n$+!C{|}6WODu}Ee$qS@F7p1 zJp*fSZ>~B7Xe@CpY!}umgQ(XXgd(J7<^gEop!Rwx$O64(POtgJrn_T`Q}5 zATof{u3!?Mh{Fmv8sK$zxfY?kpCvr_lL@}QrlO)ksql=1ha}=M(Z)bFksb9;(66$9 z63}XlW{6s_b8~agPfSeAJ-!HcG;EcNZEnsj#l?FZJ&`_r8fP<~vY4*{JPF^GL_p$- zP*O{7?iOH*i)GXy!Nc3y-}eBYhRjr(PbaXMFFUOBNMS(yjK2TyWR^DtN;_%Bcl=R6K_B( zmg;rS`SvaHTV4^EFxeJ$+zDh}013X!;yaB5oDU-g4 zg996vlBwz2db5L1fTtJmrj3e|R-Xt1?w5^`bYYAw*T9++@JVZQ+<$YB z_6sPpJ)o{$$;@p*<@f~YFAfhcw=*LmE5iqkuukrBWD?K&s8fM`bpyq&!N3AjXnB=@ z`|fiYAYoH}u_F($IoXt9PrO?KF#3GfxPZ+q;_%SsljX;X64ThrX7|97AL{7HJVQS^pSK44{5?HAh)L+gsc~5ho7vIUhdKob`&g-=d1 z?4)BdMoS;%N-@z^)iBV@=i8$I*{&R0gX9`+h%m+%y*YpNtkb=WJ%)?wGLQjszG}k# zem`Z@ns578Vf?R#!vfcAfCMgx>5M2Ttt5}p)ea1CjWp{x-3dgT+t&0s#h*3a7ZICf z)Vc(B32`_`P`i<4z^)6H0X--%EPzAtR8fu1ha_ zQV|xAM%)MX@0Cb57eUXy$e>Z;1~FSxnBSkmqDDV*)W;@ex%e{izGz8C#QDF0z20MG ze?o^+kZ#{B18W=lYrTGeKXMyMeR(|WYL$$_d95U>J2dXMoN7&%YqX3jPR$dlvjOldF_J)U z(kTzKl98o*yae_U!EiVY<3kq0TkL)y<^X2_ipDTm+>LGWztN|srvQKfPNy`tr?V1q z7&&p<)PL9?(~kMb?mbqeY`F}+)U>`;Apn%IOTd{A$R+&1-311C^kZ|r^#4%Z*_*HOd zs0BZN3j6f#zA_MA6-C8wBH@*lCtytj1A;8!>!~k9YgfPE{{jz)p$$L@ejd8LLaUPD z!NH&$0T#8a$n35HLpxx)dWMJy*!gH<`T<)a_!W@r4aKvF=PTvp=2m`w2U6Dn%O7fJ z$@Ufs0ejM8g&Lohn>y7#6+i$`#)843+s1`)@~v~@98mbq4dL=Ji#yEowp2I}Pwr0$ zh26uEyf6A`@n_YH!M1X>DgoBRocOvZ3fVmC7h!&LId%T2CRQ}}&w80Mb-wzruAeuQ zfB+E`5U2S@nvK0>TW@mo2AdrqRas{=z$*v1$e_?quC82vl*hHyzOQ2>-~?2m26mWA zp}~1aL<|Ef8i|NA>!5_Y)Z{Y0v_x_z^YlCBkAQ&W=K=#J3fSIv}dyFFtxUCZ-Nou9?x`i z9-D;B>0?*XzX?!+r+E1?QW#l0oD!KV68iMVUh~N<)3IH=W}*R-;@@ud5e7OuMJ4~E5Tn+V0Hj7z!AC!948-GK#N%YUX) z;@3~~_2qvixsQnXDS@+t5ATv2_~xjOrt-wSxM0&YGNjc}=yTuEDTk#my#8{L&QnpV z%FDM&1p}g5aFX(*&lV8MsTbGO)qS9(q-0`>(z)K++RDw%H8wOJeHO@!+lBQq2 zP?BD0Ru~3Nu-a|@{`Kn@C^X)}%1=)prr3Y+H5L#WojjeHgw-7BHUAZBjic9yjgRlU z5p+#{v2{rHrF-UhW5AyZpMfeffBmvn9~+_x&X@LNHM6G4fw7Uamo5@pqZDh@-$D@Y z7y#^DZf=NkkpqoO1jEp5XK16lL|ne^vBN{<>XtR5>39ET7E^Bo&W3%wtyjxjA?~kuIDbcz^renw%uwok*3!jpkaw3^(n1 z&Exh5xz(Hc!QbB>K$_bfmTtd>Z-6nD=W2(+*Y*+SfN<#;_qW!eY!yjUSJ!LcJY3!U zs#&Fh2skAqBrvnE{KtaG=tR(tnt_2q+vIPfN@vW|pU!IWeN zQ+Mdzh7%2A<)ac+Ct z#FRqQRpMiim9{p?L&T=c?FjplT@W!ocIGd)xns08%6D1~tel)zE5PETlYjwRsm^7QO=-I`64Gh_q{Kr<4)(v>oxY`bEZRyY?mDXQ5H9`-HFBI_SHq=;&B; zH1Hjp z!6TypOn_8WIW;xxw6y)%)4gYDjrKG=JV~YKzlVl4hZn2+MS%jF&$%0jW8C5lJrxGz zq0uiTf*x7)Qi*=ctqH11-?uwxv6RfqklN4p+oCg?=3I6FM80PF(F+{D5Hn+OS%bj}nDBjk0h ziX)aAA5GGG5YV`>H#B$=q!4wcC3B-SDnc8&-Y39peCdXEgZRW7i{SbWo*viETv$tb z;({&Gm5oM~9NzB^o>%1S*CUY1%*9B(RByLsS-UtjF;Qi|GuG791fnc}(-$mzv+6l8 zj&kwLUqHWsNm+j}7ssfR0#IgL93Cd7Ef^wIZI_LWIGtE_!3|?BlK>$VKua!@c&VBS zn=Vad{_;G#s=S;DP)2e9evaz1KY^s1!}@@l`1U;u%L(ubz>@Q}GzS6$cn3j79$@tZ zemfwlvbV3QiXwj?17B-sNVrf2Ylvl-r=vAMyT#q*>sKbm#dmys+`10}+tkwqSmW71 zv#Tm8Rn^wIIf&*fv9qzoL`MSyDA<(Ulei|z9`HkXQ3;}jBf=iQC5njz;qC43uP{cM zPPr;cDI3lT5lY6lF#_rH`rk3`%OAaFGJFh-!|B$0HlVxt`Ip&A{s}k;A%UUjMjmTR4fZ?Wcn5ebyTXcda;UV; zWlT1(EH6=!$*QZXS4+vjXI+PqIB&O1$8(M-Sce01n+htK`I5 z9cqWOcsVH`&N+Zq9IRH!6ZVjhkgL!h;01vscHXOX=&vhlP*!5Ub~y!bsV5|u5iv3{ zZjFmX5OX_DYdKwAT@mxS|KxCpGv-nk5NLx(=K+&`G3<2@Mc1ODps-k|iz+SKUYINN z_Vxyz(@`oqDc`#ZDsKh{yO`(jMqbJIxr-~`6?ctENl7o%R}t8=iVs~&Y#fq|O=o%>%f|N9L_uRryqsU%kbX`cXF6Mxc)C1?2M zi%bfS%U}W(&fg5GLCY*|%e_PNA#<9l~InNcnk2T!iUr_@r2sG{PJ6UOBJ z!NC|YTW7zArY{4y_Z(mqjXRdnaB#$>wjs7$+HGwNWHDs`T5D}drQ0tZGILj2gqvSN zvvYEezw)$r1=t@4MwUC+=jK7QX%#>T-Z)@fbpg9?NXzt})` zX#e&MpqJa#{F<8BVI4_n>1Y-7<7hr+=8Bx00FI-DVaH8nF)@VFH#Z#zke)KThA?Rc zbTk4oF}MX)5ETeOCAu6T&|#%XflGmNNrRtNqej3|ZR-O}voloueSLkQOf3i=iwzF$ zPythkYoOck4RCO9fcxD$I4C0_@h4EIfP7IxUw`%b3F`=dpO8{sgHS|+U48yJjq`8E%zJ0 ztel)VzOVD#U(o@^xK$=ACRs0q0{Ha(JJQZ!n-Bco)1t+QQQZ&rx~=%nY;=J90{GSB zJ|M-|Ou_EkBS5lEWANt91YsEdesD6;D>xK9(=8{@UONaMk zZ14gdearMc83jLo>xozMfP|!E#T|*rFCEP2v55(#otP#NX!A4zQ790jY~2R{$Svi1 z{%Q!MD8E8=Ny`)w?*Yi1X%2^*x&-op%oIP;_{kWO#MiH1LEzQUa2+_u3w7F+k!TXl z3IL7}6E~ZVQ+a-tmtV1kLb82EhU`?bPsOg~w&Fo5GRpG!TS`j*Wfe&zZqP5MA>{IJ zAZl{L_~i29^_URfy)ftmBc&Np997h@k4`tfwG1%3a&i%X$)b1+{g#UVRuZ4bN3dNV z2?8m396V8fw^OcWFtmbWO(R26m+aO{?@(e6uQMbhB$^~Wfk+NxxaRr2Bp}$Sg2CTJ zqe*m$y4-gJ2GC@Kr3bV6{M&Rq{rK26rNhwJm{F%CDZ)d7|KkoS>E>#s7lFa8sboDC zfBeJr3nxrsV#`EExDUS^#*Ks0OK7lzxABcr6JNLCGq;V{99$6Y`g1+s^+#ixc(Grb z0`osd9^Oc@%TCRlRr@@7_rbF7lZ}G!XU3{P{?eKvmBRF5HlE*0icG$?Y|H{(Iv(*! z(d%E9kFg-zq{*_XpEZ-5*EGx1bpqZYmXmhF6C*k3L)!L%3Veh7G;TB4L4U$5Ogn;8 z91{NJJ^lo3HNFFRFFf%x2mLnqBOH1C36_ro&0q#)vK0wY5SX4rd?ib#3nzQw(+}jS z5g-s<{Q9rSKOy`NfMf6R-GcJ`zU5_|(1w=){e9yCX-JTc-XSg~z$7T`7eYURgp5`% zuSzWjrz@|OSNP)Vaw#+t@d*g*y3t?QtH6hR@P6sJQV(rFzud-g}=(@wG#_{()M~Zg_Fg0{Ug(^ z_CGM%rcB*ZH5va=?!e2&pp?MvUPwzyDr5!%fu9;1v;8BqIAh)&jQaN(kkj8kIW_VX zgGNhcIKMsvpWn+pvTaywK#0<0AjQLRXeg4lfOx}kYpJj^&`L7Ih5YAR>s$L1Fni#y zfZ7!zzZP8d^XN)GQwKB}N&EGg@Mx*ivP0S+NeuLC1Ys`?G>;9^fjjf~*hCd}8dR!9 zSmg0G`+fzxyLQ#p`tXp`f%>^Q@Eh{9EC$iS%VPmVfLB%kBt;|Ty&eP=nvI8pEg^r= zO1Nt66Vcptic%6#);qvX&(goQw@g;jkb1SR8-V)P$5%>)YD;psGXq!SZTCCXf^9(1o(Y0l9Pi}98z6syn-gZJ? z8THy?+>in9bIw{quJk59m;GJl!^1JY*j*#C@QprWD{C5P+M z3JvY~TnIh=3|=1hmYI2?hI)p6DbA`w`7}GXc&mbBW_P$UZH!-Jc~sPp-OGh9{}Be$ zM`38V6(M}>M&*wwv~KHS^Pc_q86@HnwA&LCQ)McIo)c%=Yc5=Nh=;F?HC2KI92oJ6 z0FR^Pq>J*p*m%)__!<=wpzW0U5&m?MLnj1ieB}ZH7UK{F2*fFn$L7+s$u*wg6*jZ} z*aShY*b^Ysz#PF+Hl&-_E{^rkcmHOs+_L33r<*f`ua z`K(==Tk7kt*7H9#Ui_`@$Km*$Fn{6ze=ReXTO!;F=PkT*cAc}i2|mAHzJ`^*jQxD0 zjl<>%#6~p_Z(u@*cMvk{`oEI+-B>g2TR46s7!pG1N+df$J&B1{RMaJ38ap%a@LPB9|Bnv1nMpjZLikOBMo3HAE_HpiV=r?W;jS4 z7kBQwJi*zqV*{8BNO%BfJ|Qs?6``?46H14D{!aJt2Ha}DH;+G2>Q~X3Ra3onX~BO6 zv2g1%Pu3F;r*(x#JbUe(G7T}N?UET{+;p$q{U(*hJEy7i} zP0q_Lmwg;F^$t(T)_>Jk;teHNtJ9AgQF~krPAeY$%+kO;xB2&p*K)XavPI(X=Mi;An!g{Fo(n!cw)^6# znrN!m)JA&u0Jq-XCSRSulQ6CZ?!9zG&1~Ua*pLQ2uIG2OcDhb@Za)m>ul=W3JuQFu zGldVIf0YmH#R(oMQ@C}S?b99c0==ru$@-4Eh#y_}5^~5Y9P2xS4wiw%a`{?-n%({@ z%%`;%8-LvkyO=L>l;SlRYT)V`OV8b{*b}Xiyh4%-bKnAg#(guxJ|BuC{69?$FUj$4 zBWfOjY5c(XUlH=zE5d>YS2_Srg0!&!O@n;{>-46+^nAj89`-op5NdBA3TVVL*35)m zG|mMFPH6VY8+DKGtaLx35<9!f_B1>&w~!TSR&DKk)7qf_D1;FoAiNar+S?bI-@u!! z(qe1r@HbsF5bs7|U@(EzeS|&laHZ@{E^a8FK%CJ4()tn2vphX47y8Y(V^`quCN>B_ zzC;V@IpW`wAT0|ggRBs6k>vEo_Pitwn4+4CqoXri;qY>go^T?g!Tsf_mw1VUiG;9u zu(2D8klFs49UkH>u9!b*7Bk23`n{pZ=aKL7636Cgj1i2mmR;d@)E;}Yl8y#dLc+X#2Ofx|H zs%+YyWMfWNB7h>v+h^AS-Q6d6yZB^ltJJ3l^I~ORm+vSrh+(C9X4m$OG|>je@wD7u#lD(5P=1ta`hWAbh*M z6;-wv^&^UH^8e1G!nHc}=4F(xCH>$D6xt&ihBM!GRzK3l4XN|{YS6R9YOQ^f9nur= z3VR+5pX;O2zgD8o$x$qf>@ylp*nz?5YJ4=>hI}CJx82>{jfRu*qx*)GlVASxalbR4 zaQ%ntHZUCF#J9USE+^nKo$NMvpGlEYKzSAHgL&hdp6tv_UyRj#kI9~cuu7vp!q)Ot z)z$lN-zEL9A)k7Fjz=!$5!u?j+=2GLRGrk=AE%tX^{gPM4ewLm?@YG0b$?HWHI9#W z`DV49mYYRUQMmx3>ya;2EDa5J z=E1+Z-#qlaSWHc@J;~u%#-0xtS&O(oKN9z%6KhAC77;O-Fk^D{7h)hKB6usN2k8zY zrh|@#$b{*`&$R2;Bo*fWSHC?1@A>)M3kfQ~$OvMd*qJMi&i5Rw<7LGvw%hXaS99SF zTzCF28a6|W>V=vc)0BA*%p&-&=O>~QNt+Hl=XKRgP0jiT^WSpd?bB2E?aemdO!TGW z+g+N#0*Q(DhfCB>DpMcy#}lm?WaRLMU;FXD-$|W8>7WJKL#NxL`lrDlnWC)hJ=1e| z7TR}3_2t^3z&zN)10BN3!KO}+%QV-wF%?_(lDw0qE;XGHnZi#=m7ss12mry!X(Ga1 z)WJb2e@$g=BVOCfiuShCYb(3ak+PA3{{D{TSFvwWJu5mKWu{x5K-uU|a*~1JVB_i~ zAz>^lBPZ*-Q<1))fNO*!6d@!sgUpN+t;&E0ta;tKFl2O9CpL<_$bpVVxtHn7LgJxy z3$_aX(=!vgevnatzu&3LYVVT&)XIov%O4lFW@?gvkO(y_?E2bOSSln)v0|lWB!TcG zr8(~WZ!^WxN-!YH27iVfJ!wVH_m-01o9Eb@a4VdgO|##ho|8%eB2zH3hzE_S=(NW{ z$%QH<>*NlP{|9Lbd-!6z-&Kb#CdYy~5Bl)~i;@zI`LfeN1^DS#Y3$|YCB>lB=7X~z zI!R8>LK7?R-J0b*5}Bymi|iw5Pk#4;Eg&hzS*a+!O1DovjdD%0VgZv%N|l zKCmCNU*qo=X^lo~mS^32c~%PDq|9BcImB~X_?nR=k}$8Pql$+|`T&I8(Mf%*$q>Ya z`KFx42RV)ken_1BSX*5yb$#V`$Rup~z0~Gl$uAz+Y1s|kO9>oHUw*|!u^>{mJ8S&d zD1B2HhVAZpAGp==`T1shPpT_3>${2b^=zkQ8Kj_)h|~|Jn5|nGznZ~UZ`Fj2lLMbJ z;UmfZ6YKcLI&%Z-oQwbCITP-SD|>NaVfa*RGYd0jcJ_phU#toWyWOsKi_bdzPXbf#>}EA|AK*a)|t=6JB@BcimMeeJT@!MFJG1mvQ$)5#KMTE zi30+(W6D38>K!+&`y!$phfX^#y|c&VbFG?=u~Y)x2WFlJOkr>`h{b$D>FaMhXlZso z_7g0fCJ6g&QiUI;0u6F;d293=#jGTbLpv>Oi{6QWkbG&+tgr7xHaglJ)TP6smf7Wb zDvm_J=87#B-x)x6sic^{gu-uk>xZ1KmtOjCB?shnZGZn{j`V(H(NuL&H3D>+C;k-? zj`Ek2Jlf%;KlPMkSaM>ou2JjRWT`Q_B-D~OJatOI($w5+x8$5@Vyy>E4&Ph@o1T%7 z$>(iYspa~M^IyM&+&;+E)doSSFoljCJ*TuLHIUjtEI~VJ@GX@IxgJc11*CWv#l1>(+C@~rS`71S(&v~pV$csT?K~a5%evx0< z4T>Q{@c*K`r#4t|4DskBO}~G}D@r0YDrQB;hgwX~lqOoC)xV`Q6cb8wt}Mx-ZyKDR zZwx%9E==)Y*PHsyQE8{G*;tb~F0TQ)ZD&4xeRy zyEAfvvoqA;Vy*c_({BXGEW#Z^99A=t7ix?eGyJE$1Y{y}J! zTkPVzyw1bwT=gm{*VR>qXzks$xSP{~%Rb=n99ipKKiy#|przXy?5Q?5m@qp7yZ57+ zV$UvbLM$P&Z5GU_+<|94kd?37BNi6CG;{L}_f{=(?(SYQD1+%W2Qyzgit1ZDn>8=6 zJ?>}atPb>%aqHqi1Or3p)wS1MX$06eNsnbB&SvhOiQ%Ph0 zQ1&N`!>%ET>jgOPn#O?-1bXhAnEQv7;A95hILscgb;00a>*Rl-I(*>2NI*+3DrUWj zcwP+9L3fPrqZ*@Qo{*Goz7CN9tOOANL&7E zbTCai9uDzLSaO`;8~wh%Am>k%V)zB7oIl*m>1HBhi56^ajok7l@D3p>w(l7y6u@&v zy?1Kalx#o!tE-(Ci2uzc9kTN0J!6&Ezadr&znPIR!FobCh#ZR{=l44RtHHNZ2F%I< zI0*hPbpLGx#eXtvz#Jd$aYO%bwOwu?mZ`u45PLwxti_Z{^uJF3hyuX)m-d;A7yE?; zC7XdDp7)pj9c%pIv&Sw$P^i~^AZSYIliCgLv+Nn;FuE%B`N!Kq+Ax3*KttKn%0|Gn zx3&W|fI2bA=!=UV!`J~qlEo=Zb~XQamyaI0uLfgPM$_%U>MF>D0g7xh$EbW3`ZVxq zPghS*SJ$A7bZAf*P9itKQb}Um58K6ww?c;#L0G9<`Dnd_KV@nzu73XjcvAZg2;mvuIy_A4S%UV#h%~MxhHJz z1Mfu&)IQm6w;nd)#`}27Qmq~`^gU8ABk!O76GWc^IytVEBO88yshHHg4ao&0PJLCL zm}QHKVr;#pB{u}m9c(!?;c)cz>&Xu&wd~LLAY`}RDPsKbiQ8Y^tsSnd3E}DV3)g@W zSZzup+GD%|At6t|>Cszc#qTuHQHqXx*e|YSVP*j!Jt`{x>inTuS;2F1N^WS%;oj4; zoQVni^#PIC`Lf*wH*it5&SILLKFb#`j?B^gzF->N6D8Lq( zp3S#ANQdoB)Wec;FN$z&k16sC-fRuCWcsA=3=OG7ZBk(pihYqioJ?hLN`}HJU>3>X zCPc*4V(A>AYN*8f?LKh&!W;-2W=|nJgO33}yKf;+F21m%Z_9~-EC36h`-}?)c0RDt z`kQ^#PiuNgHaJ*qi2)Xs`CA`HBQR>jO?JjXcED7}8G-!jeVoNT_tnkv_7Y<+>C{*l zY&(blQ?oXYUEK%EY0lOmEkt-@<^;)b;DW$G7Ko1p#Dl_vCQV#tSu>2kWM_X}M8QFe zFEBe^;_q|Q=v{uWy1x_d(RM!lXkMwnV_MhM)dK`{PAn-MMj=LiW!8EY;Z^bXHuUc- zog05#(_PJ^3zZ{s^Olm<^h8_B{SObd&fiAo$}exOrKTocr?7-3UHbP#YpY}jnL)48sZLW)?34RMZ0~!Q zF3Il>Q`be1T^>@`!=_EoN#jRNTfW=O;gMu3rN6d)rcjWtE`!7Zf&yo8gvfTWQ6{>xqpuWn z)I58H*7fR+d7o9w;8n?*8=a%@QPxXg!^ATNdMe{uQXXD6L^WX3TnydbxwjE9OS(T**AWLRZ0$=qk2y;X%B zWoGRkU%V8{Q#J#?lVGf&1f`Mey+lP=4zX<;56!?KpR#nlzDp*i;xM2 zC$>1Y(>o+L)8F%MnYcQow3^m~l`vI6C{I$t)1BMnmWj)tL74%n_HJFDW%h{lIyGW6 zo_~8=m-E3~t9DMks)x&sq5d90$WWS!nwcP(CuUGKtIKV+Ve9h{OgfObspNx37vI&# z^M&zPY7z^b)`F6i@op*l*h3q~Y2x4Wi9C1iZI>_ymH6vz1z4a)AIwYN>pJr+#9h&C z$P}SI3Ar+%SS3VA4~<0c?2OusF+(Y23(QU(9N0f&ZR29<80v}3p{IaSmt!pxyg2Ur zX)Z3-eU;b58WUqUFdw!$Ya=?UeLjk)lnWg&RNC3dJU&;wx`fgtN`#Zhc)HZ?`5?}+ zO@IC(V#EgRcBNhNN2FU4mQ9XkWAD2^$MrDB3JQIr&F9zuJr13SJG|*k_57S_Z04g} za++vkRJVKSI=*r*5_yGPOm~wdF_EVZuLoD3yI=S#31krWX}04_ust}2Ijz5qctwfF zh*__gGZi_Wd-x0H$5h!eI5UO2U8-?NuIBvo?3_{(m8*XcWOn^CZQ;y^Tuei{f8@5m z%@?^H%j_3#PN{4d42ywBqYb+b8J4bQD@oqql2yjZwiVj&Q!F@sE`RfshV4H3-sY{E zLqHz3#gV@Grgf0IW%-9`+W|etJ0-3ImCdytC0f^O*2Ts+nrI~|YFkSBcUieo`I0go zw;zw`_Zmv*w%js;rYlc%)Jl_eYt-!xx_75qan3??Xe{EW2WYNRNuf8fQE9KKpiwg) z&eu}aG7Ta>Itk?~c&RNJWZP!WuLgM_I)O5pO%JPHTD|_Zrr8g&{hnnJ92=z-L z;D=>KJjC)HW5mBQzx?}p{t}bZ5&vMmaGU#gwQ&NKtm^&o9}J=1!MBE;NMjSaHZ`lC zfk47PM*bJTq9a=mk<$n{)^M=g$2D-%uSmi;4$1bRC5`FVXt5^EFRH+oK5jsmZLJ+| z_FuK+jg^+ge*OlE1+-g@BBw#Pa$mu@34P{j?TO}di*Xiol8dtj#f6Tw z4^JtSq3aAIlC^}>V>0&avlsMj#CnrbXE-}GH zpXuEA23&=|^_f*9$n4`#b1hvN#5m)n6r8f&t=cu*E!)Fzua9um9gB($hj(IM&@)q- z){iJwY}sFxpL;bkE;sml9$Tv)a`I{glL@}b4DByw<}6nS^>VU==u&iHhbs2h^&uV+l~58?iTH89fs2Glau$+Kp-t1 zgV~a>B(jAX`lDifp;nq`|_ z$ZZ_Ry;J#B^xN!9bi&5-3~P>P4+nLpzR&lQrXG!gCA!S-*=5RGF=H)P_>+>-l!9?Jvo_J`7iCdGWeX`0`f& z;JIM-*k%%)F{zg>8PVD`DX`kEn;?VI35oZSx+~o;44mf2tVf|Z-ksIGp^p9$iNSRt z=DN(BU@xroC%kfhaW8LlNsy2MvGsB)G|ujg^-VmL{kk%v!myHSaC236#fFw`ODv|C zk+gAClzwh#NG#)JeyiffCnv$mornr!lFAf5vLv9nR$L!@o(Xh>a)Ns3KzBB{1 zZP$g=llYiD#)*gfuO0JV>6UR`y&BR~j>pmNot>~A>F-s|_0(WJU{R0OneSZL?Jbig zFixn`L|t8){UV>_Yax_0bl56>STf_(=~ykL#G+Qv(K-6D2#uHC3)v4_zO6mJOjjun z!w~v7sFuN!UjXx!&xmD7oFOXx|GGQtx2W1~j}IyWf>J8=NQbn5gtVY^2}qYPzyng! z4F&?zB_Syd3c?6TgObt+0+LD$k`hV}XASzi=Y7w)uJZ?+eena=%-*y2zH{AceLw4t zSrTy0PB-Y`+N557q;1=8sA6SaNs_&B5MU*+H=Nz9W!+`ze8VCq&j6FpWhL}g3|3%H z_7HT*EpUo5$*0IJ$OvMjaMZ+(y7A+)j|PuyO2bU2z@ zd{4(4O6IsSb~$h0#ex6_+YR1C%;t01@v65=VmvP*MCqm@TlR7)g6a<Gif%C`N;3qDLQJfn!Z#;9u-9YEkJhfj4Qc7Ok? z%po3Lqj_r(W~%v~BtsTooG(XjH1|5H>R8RW{Cugn;g((7fNU!_!_DpjHrCTOU<*uM z()DJ6NsP-F?`6aF`Y76~NM0#r$%Cqz>Q2LhG|XY>xv^XUrRv=5$3}LnK|Y1fus9EezO7Jn@jO?wR!TL zagW(%rOZNOn!3U!yb1kQ?vPQc@CbY8g`drxs9AIvQ!XvO--mMJeN(#Aslxgme`7HE z#RAIE5uPxP{UWk+(siT$v*7Y2VyW+(S{#+f!FS_h)G;5B=UQdecS|}oxf+6DXLF8= z*okvYrP2{asmh`5JW9w!7~LT&aQ_qYx3dnvYz-sBOC zJ6%i<5{ap6r4-4YZP8qNeRBsetm^@78#jpcdH!szYw!2S)idhsJ zZT8#lqZtj?AF7NddWZ9`h#O1@M7%iE7dr~VJCMvA&nJIrG5h*mV@(y`F);eo{)sMt z!ETZ!ThssI?t((W(XSY`|s(! z^3X`q)#$)G9=av>CR2L3&Wm|#L_4cVEQLg-?N)4v0%II$#nUP8TFVc4(HT4hn}ixa z)?F8R8KNYW$gKE+QVqJ1DrX`n!~DU$);(gn{7!(Z4$UTiQbjW)WZ7LyT_5Q6!LRDjf?604RcJrw1{6>1p zf!L0D<}JlB?kauLBl1pPB}wPEyVeg{@XJ>u_gBK~Fg;we4;b4`XkdiC+#=<9O%MCT z6kg}+cO1O?_^B^)2|YYw%j_7g8!66&KQ~AXan5!L#>_R#FP59e zyy>TV^p$k3a2Wo|AMhzyAZ{t;@{3acl=-c7!1k=uk%1|q*C44Y83pfIHWT^!x)yo_ ziOe5q;P227;548ET>Ww)5CKOTH&z}5G91o3_+(&)A`q#>;3$CIy9!6Xw-juFxH&K0 zw!&as!Om`-lch#nzIZq|N%?Bk z&13ZkHr3VDntd8dz!J0y)PQjMMM46LT$*8{RGz5RSVO}L(P`P^(Y?TXdjG68r9n3< zWH(BG1#s{SBb>OnxZ8GN&ev6&^ z;uPx>l5ts#0|yUJZ+AC9;uWix{YPEA*(21p)|qmqTc=Tn4tsvETkdAx8UNrxBZ9A} zmt9d5l;&8WL^=WZQr_vLCAlqs-dK);H{ddO8-j-_FW1!6+!g5taX07@F6!5NyWPHx zD`gFwim%3d_e`r#d8&s`KWq*kAnf6@&R7o8Jc4C3(P{gK9; zEQQxPoNk#q#m4pCg2%aVi^-pL2gw4~k*oLY>{g`PY$gqf*Y(-o_svQDc<0I6ThtX* z*wS(f!=`VQ#*ABaDJKFP@c!Wz3{y4vijfH0vCjo10^1VY#Ek$-%Lk zUYe(f`S>-uj^kbib1JQ^1hUx$=e7So0itYdmWy>xVRYdAKYY*03d?A z4G8Qeu?*td3kwUIrtMcmoXoId^?M!?>;Au1$E|(zOiZTo^($y|gLhtC{3^9Y>NtK= z06j9_9)EozF)r@Yp1lmdexrZAlVoB{O!MQzY!%SsdmMdh4ddkG42okJq>*A{gB=Rx zsA(rE?o&wJ+%Eu-JrBhC3NaAk zvoW88#`6Bdhg^L1C%ZS+8;>QA`Nep7)obisU9Vek-MBFpRKi~kEZKJ|par1Su!xGL zn3|KxoFgRMLxsUhqPH!&U7$6=#!N9WF~0f~7Akro@#*O%8X9lBwyeg#wuFR)^t=#O z4u)p%LjUdG1HeS&+!BUCL{RXqI6=wcR|(GBIdOY>I`{5n5&A{-?^!4rxObu0zuKEO zVqrciTVhDQJiQAo39y+%G%n%gko+(CTkV8I68}uV4~;DS=-V4>DphSD4+7k~sd3e$ z%E8#xbsInuVf!z)*i}_k=S^RNhTZq)((vJ5W>r#DB>jx1)K%vBs69@C_PaPSx2UKAWi#Z8P*GHj zsi~<=_YeSp14@aHkMA%UWZgSX5f_Xbtb_2Sw5U4mM)-Y}-%+`R; z8QbG|5+{CjSJwVD;`W$PSlDCZTPvhAQHwo)VT`cvD05j9wKba?-0-ErLVl2&u=VP; zT8JlzOHzto>a2UbqzM@m6ws$;+*mlX#ApLe^S3+AP z&(+IjGw~e5;mN3fL_>88tgH(V$QfI*EyJ-(g$N5Yh-%&SiNF$+7J5n+8#?%p$i#nY zREg@;R8;I%j)*0rE!v-vsy~lQ^P4`s({5Dnb-JckEzO76*gC)0ICu(0UQ;q%vmy$T zc??#KdAgKb7GK{LofaQGU1}6s+9@VxXnb^#z{$?wG2xat3b7Sc;`-o{Wqo3}dYKBY z_FltY?H?_5e7I%at)CqJ_zichQtm|%!TI#9MZdXoE=b1iok2zA;TKwpqiNqR?jBWD zUh&4y=0a6fRiI!4{lW&bI*8o^wmV`bGXgd~D)SyM{sKw z_dIi@t<+TH&J!jM80%hhL`5&O)paB6Gl@Pc$A;oSgCb^Zuz{?M`f2~0rswOxaf&?xnXt;y zn}sLBMrf9c*q;F9A>O+vNK%Rrp-#BN>zMzd6c;h`Hx_|=Tr2qlL`$VR>%5%Y+?<^E z?QKY3KxWPQGiT1Wfl1Wb-j^cb^DQUT+(q||b zUi;B1(u*XJPa6d6C@e1stE?YZ&(=$2l(IW=b1~NL^!GK=-*7A!ur^v4XF;HaA%6?{ zvm_m`rtrq(Sef8HA=oI;54uBR$?5!meEpD2Wq!Kpji}qBqjm4Wp}FiInrEICn(mFy zg|o?V8OuO~L?~|m?EX%b;_YfrP6TC0uz*0N|L#|~-uyHXG8U07iWFu}S!^ z){%o9-kRA9;b$Icv2(JF1u(9zYR|u>;uHNbHo~|taZwsS`C2-U8XNT`0rzOa3Rt%3 zkFjr5BKjR+7YHNQ_A93A(|{!e77nL?O!~x^3C1&hkPQ3N{got4m3_^(s+P zb0#mx1k)d{86N)&Rr3Pgj~_pp&(5}7ZMtS-!%|(XVx6gUo`Aa9-%IL-VQb}a*Z%i> zJG8Z(uH?Zw`c+mkcTF7ooKALRs?`07AHK>KlYvvX2>Q^7D46`ov#*(Te{{Wa_2?9W z78_f`jyd<02`fVo=m)zr?$r?=FEU?CLW(O@uzkU#b06&JjD3}57DEPLKAS#o_g?AsM^cMsTBY(E2Dv&;(t(RF7R7kj4nyi2~P0+sOz2%0fv z*u=irF4P1%7?De12^vaub)$p1Ts)DN7KE30mLHwgUnu>)qQ9-XQ$Ay6UKZ^bRkoZz zFkKQ|c=1YGr1Du~iee<#Nnmx2UIHkaqHj?NU^y$RJj#m4+qLJ;odeT#6x(-ay#D(> zZ6TdV*$tTezl3 z84N4!3JVHI9zi75cL-*{2x3ZD>%egbyMn1Oum@`yhswnx7oVvw!0Vr_aApBMQ{wTjUN! zxstb$r;|rU68LWh-2u=;JsF?j=1t_yX9Gi7vo=E!!h6=@G!$evJ}yW$>F#+7UK2Z? zj5PZ%Z`sAgjYPS4d9A~L8)!lD9Gsj~7O-FfI2WuAFqjv1>b!q0(XHVkCc*&QazzB` z{a$5e;(QLpapIs*(>=DBt#Yf7OUEnnt=sY_=Kb;tQ`{(RVmhv8>J^H9NWoiP6Y1o5 zT;efLwqOl}J(po+_;DSwGj?goR;J?-E+H7wdrfJpoxVkEj&N77)x?uxVg}~l_Q5Jo z@>La3l;ej`>~WeY@$t;J)BX2tWE7A{W`>-~N_SX296p7|H=YuJP+nN;;KT&iKmKOm zXV#a|fYupZPUu{+eXM(&SCZ8WUJNRho}O*T7h|nq5CR?Q5UOiQN%^q7s?(-$pL6Vx z$G%ztY3$bq56UGdI4nd)N?HcyC}sK3d8G zu$9P1WKLNO=IF}H;aK5mq-xJlm%7(%UDGf3%>c(TLW+=986-a|v`HuzNG~-jYhM>` z4YsC|(mAO)wvZ|YW{$LipncySwfZ%J%*Czj*+kNzs>!TYa!Q(yDnp_3CX=tjbEj}?U)%PdM z>u zIA^glqqJEjWe^Tl-v2`d%)q|`-ORsz`}R?}3x>`g$%49=76JZaX+~XxW$Hyk$Cxng zTdYjZH)1*!j&QpOyUts(6*Nq8fxaXH;@C%CtUt!!Z52!=dL9A8ePdkJUGtPc=Nz92`=?U@mkX=WK-LwyV<( zQ-MZeY<^nYGL4T=k-o{SsJM8%(rx+G7^)S>)8D^`wzcK53Zq;N1O4CB19VzIPcJDA z;_0~C$N(E{e$|9sTB2Q=$is^7^^W^@eoQv%Oui^Q?5c&eJk(pF-RO3xe=|x}&}Q5nLK(e~nUwHBQHn=by=q+ANK7tzzU@Sd?`k z>wwO4J91YmfkV>& za|DV_ZN*QtpxJEHSWkqL{kC1;)Z%X)l>_f|@4rcNunMzw^>k_~QqTzT%dahtn5w-H zWud2+wu3(P#_<0A<*(W8dA%u|Leh>vu{M!VVrh)}aGjftHvh=cd9M{Tl%2@bw{Oc@ zWSb(~lh=v=tU;ZUaHH^H`F}#c_&sPS;qknc1pVb7U)TktdRkIuG^tKoul8MNBM8K= z!dFG+PtHc*U!rNc@Gr;C2{6#E8}4ptboeATHWqTkl)+nP3a_rNwks>OwMI~Ys}oU! zjWDOYpyqaeMa=wUcYja5wS|uN27?fMx4lq1%VLo|i>0NoZE6jR^4Q<@lCQ0o^|fe= z6wrSSRO8={juco^t!D7^=gQ+IoVa0X(o5Wx4=1&4f5KX* zbGy{vJ5%*~oDdeOFM3t6x{@5Ot~;PC(x; za)32AO%30F+xB$e%dGa?cp1tOpMp8835z%nP(`AlArX+Dz~_OP&Qv<cNZ{L3vS} zk?Sh*$tcm0rt@}mM%76Caw!01|KrEX3|_P3E6dA;-{rV#jxALYoZ{gc$G!mTzEmpF z_fvJ=|E9!y1%;?&a}>0NF6s@z*5AxuNPc}y^UaWG5~!i3No*-AeSfksd%uYDXN{zG z7*tze);)ge#=Rm9_WXFwvh%;s%KR~qefsdoFomWCss};oFrOc(rBq*^=1_ha#E<~) z>xH^3Knjy_Crs)noPqd&UP*oy)}u=BDc=Ib$l~Oi((SRG$o<6le{<<^Eq4q_b}|p*)jF-MWcm4siGH!_O?vn} z6U3x%0%oF@78iGymP^H4Ynqy7`0fn&`(wQ8)v`3mz^o5~FoAmnZ1#07SElsSmTa|5 zi8e>AM0{5QM}l^ewnz?tk<3(S@7AL^-mrF5e%Y*D?0-aj}q{8!fX9 zG8!46y>v-1n%U`3B|2UhgzQbS5qzIVhrs(sLqeIO&C$5GKoTY2-f}iL9Giv0Rm4i= z7NGb3BCL#zqN0_xA7*A|z@d&tudZzNca4j&d-=oH#`5xLS(skG*aJZ6=m48Pzk$SF zFl_JM18(DWWnJwDxJX!7(3bOKt$6D7J#QI>&JD*}@&EPM@f`C5AEtpW3CAa(5_Xu# ze0BD(-@cX)>5p9(9(ZL|MU6-|;KH6Dh^SV|<-gl2n-KlhYDswB6OgFoqYpH)rotJH zQ;3PD==q1b&mp-wi~s~BjEE+CPb1o^+$!KsK`fFR-9`kC|9B_f@0+`T<>sU_eqYo+ z|1g{{?)$;1PuL6H`IEZ={oCm+8fL#Ns-ih9vAXfK3I zoCuQ<{a5l;P8)L&$KSA~00{sbu8%dXyQ@<-_}t`KJ#ysx9Y4jZjdL5JuTU@7QZ}NMQS&OBInuR2?*bfm1HN`@w z!Nbz!<1!CsRMWvuPU7-h^P_!5B7RHat{eGcT zyj<;EO;Im01^ldVKE4G}Y{enXbtwG*Tu6I0S1IAXim~e|wz4V=ROL`(c9*fEB^*zm zK^+vv_tvrYKkq_6Ol>t6Ojfy!x{aMw;c#L;>F;ySY?F*Hl^tU7oWuFT#>~s~s4R4> zT79uC1;#&DZ8=MAIVdB`b3qfAil4u`dm=-Juj-v6#)?l)I$3xAeqztdAX`2;(s8}$ tPyPwCzWL{p8U2$K6!7W)uhZs_aRf8m>X1!RQP?+DysdVtM8@pVe*uU3f>Zzi literal 0 HcmV?d00001 From 60bcbd0ca61721e5012c8cd6af7ad34cbb554cac Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Fri, 20 Mar 2026 08:41:16 -0400 Subject: [PATCH 13/22] docs: rename AI Foundry references to Microsoft Foundry --- docs/ACCESSING_PRIVATE_RESOURCES.md | 2 +- docs/DeploymentGuide.md | 4 ++-- docs/PARAMETER_GUIDE.md | 8 ++++---- docs/Required_roles_scopes_resources.md | 14 +++++++------- docs/TRANSPARENCY_FAQ.md | 2 +- docs/automation-outputs-mapping.md | 4 ++-- docs/deploy_app_from_foundry.md | 18 +++++++++--------- docs/faq.md | 12 ++++++------ 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/ACCESSING_PRIVATE_RESOURCES.md b/docs/ACCESSING_PRIVATE_RESOURCES.md index 0f7e4f0..f1b50c4 100644 --- a/docs/ACCESSING_PRIVATE_RESOURCES.md +++ b/docs/ACCESSING_PRIVATE_RESOURCES.md @@ -32,7 +32,7 @@ Once connected to the Jump VM, you can: - **Azure AI Search**: Manage indexes via Azure Portal - **Storage Account**: Browse blobs via Azure Portal or Storage Explorer - **Container Registry**: Push/pull images using Docker CLI -- **AI Foundry**: Manage projects and deployments +- **Microsoft Foundry**: Manage projects and deployments ### 3. Install Tools on Jump VM (Optional) diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index 8667b97..e68d01a 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -236,7 +236,7 @@ Running postprovision hooks ✓ Lakehouse creation (bronze, silver, gold) ✓ Purview registration ✓ OneLake indexing setup - ✓ AI Foundry RBAC configuration + ✓ Microsoft Foundry RBAC configuration ``` ### Step 5: Verify Deployment @@ -268,7 +268,7 @@ Then follow the [Post Deployment Steps](./post_deployment_steps.md) to validate: ### Connect Foundry to Search Index 1. Navigate to [ai.azure.com](https://ai.azure.com) -2. Open your AI Foundry project +2. Open your Microsoft Foundry project 3. Go to **Playgrounds** → **Chat** 4. Click **Add your data** → Select your Search index 5. Test with a sample query diff --git a/docs/PARAMETER_GUIDE.md b/docs/PARAMETER_GUIDE.md index 65bf916..65d3641 100644 --- a/docs/PARAMETER_GUIDE.md +++ b/docs/PARAMETER_GUIDE.md @@ -18,7 +18,7 @@ This guide focuses on configuration concepts for the **AI Landing Zone**. 1. [Basic Parameters](#basic-parameters) 2. [Deployment Toggles](#deployment-toggles) 3. [Network Configuration](#network-configuration) -4. [AI Foundry Configuration](#ai-foundry-configuration) +4. [Microsoft Foundry Configuration](#microsoft-foundry-configuration) 5. [Individual Service Configuration](#individual-service-configuration) 6. [Common Customization Examples](#common-customization-examples) @@ -293,11 +293,11 @@ param logAnalyticsWorkspaceResourceId = '/subscriptions//resourceGroups/< --- -## AI Foundry Configuration +## Microsoft Foundry Configuration ### aiFoundryDefinition -Controls AI Foundry hub/project and model deployments. +Controls Microsoft Foundry account/project and model deployments. ```json "aiFoundryDefinition": { @@ -314,7 +314,7 @@ Controls AI Foundry hub/project and model deployments. ### includeAssociatedResources **Type**: `boolean` **Default**: `true` -**Description**: Create dedicated AI Search, Cosmos DB, Key Vault, and Storage for AI Foundry. +**Description**: Create dedicated AI Search, Cosmos DB, Key Vault, and Storage for Microsoft Foundry. Set to `false` if you want to use shared resources. diff --git a/docs/Required_roles_scopes_resources.md b/docs/Required_roles_scopes_resources.md index caa4f31..873896c 100644 --- a/docs/Required_roles_scopes_resources.md +++ b/docs/Required_roles_scopes_resources.md @@ -1,4 +1,4 @@ -# Required Roles and Scopes for AI Foundry isolated network template deployment +# Required Roles and Scopes for Microsoft Foundry isolated network template deployment To deploy this code, assign roles with minimal privileges to create and manage necessary Azure resources. Ensure roles are assigned at the appropriate subscription or resource group levels. ## Role Assignments: @@ -13,14 +13,14 @@ Be sure these resource providers are registered in your Azure subscription. To r | **Resource Type** | **Azure Resource Provider** | **Type** | **Description** | |-------------------|----------------------------|----------|-----------------| -| Application Insights | Microsoft.Insights | /components | An Azure Application Insights instance associated with the Azure AI Foundry Hub | +| Application Insights | Microsoft.Insights | /components | An Azure Application Insights instance associated with the Microsoft Foundry account | |Azure Log Analytics|Microsoft.OperationalInsights|/workspaces|An Azure Log Analytics workspace used to collect diagnostics| -|Azure Key Vault|Microsoft.KeyVault|/vaults|An Azure Key Vault instance associated with the Azure AI Foundry Hub| -|Azure Storage Account|Microsoft.Storage|/storageAccounts|An Azure Storage instance associated with the Azure AI Foundry Hub| -|Azure Container Registry|Microsoft.ContainerRegistry|/registries|An Azure Container Registry instance associated with the Azure AI Foundry Account| +|Azure Key Vault|Microsoft.KeyVault|/vaults|An Azure Key Vault instance associated with the Microsoft Foundry account| +|Azure Storage Account|Microsoft.Storage|/storageAccounts|An Azure Storage instance associated with the Microsoft Foundry account| +|Azure Container Registry|Microsoft.ContainerRegistry|/registries|An Azure Container Registry instance associated with the Microsoft Foundry account| |Azure AI Services|Microsoft.CognitiveServices|/accounts|An Azure AI Services as the model-as-a-service endpoint provider including GPT-4o and ADA Text Embeddings model deployments| -|Azure Virtual Network|Microsoft.Network|/virtualNetworks|A bring-your-own (BYO) virtual network hosting a virtual machine to connect to Azure AI Foundry which will be behind a private endpoint when in network isolation mode. | +|Azure Virtual Network|Microsoft.Network|/virtualNetworks|A bring-your-own (BYO) virtual network hosting a virtual machine to connect to Microsoft Foundry which will be behind a private endpoint when in network isolation mode. | |Bastion Host|Microsoft.Network||A Bastion Host defined in the BYO virtual network that provides RDP connectivity to the jumpbox virtual machine| |Azure NAT Gateway|Microsoft.Network|/natGateways|An Azure NAT Gateway that provides outbound connectivity to the jumpbox virtual machine| -|Azure Private Endpoints|Microsoft.Network|/privateEndpoints|Azure Private Endpoints defined in the BYO virtual network for Azure Container Registry, Azure Key Vault, Azure Storage Account, and Azure AI Foundry Hub/Project| +|Azure Private Endpoints|Microsoft.Network|/privateEndpoints|Azure Private Endpoints defined in the BYO virtual network for Azure Container Registry, Azure Key Vault, Azure Storage Account, and Microsoft Foundry account/project| |Azure Private DNS Zones|Microsoft.Network|/privateDnsZones|Azure Private DNS Zones are used for the DNS resolution of the Azure Private Endpoints| diff --git a/docs/TRANSPARENCY_FAQ.md b/docs/TRANSPARENCY_FAQ.md index 72d796f..5944ac0 100644 --- a/docs/TRANSPARENCY_FAQ.md +++ b/docs/TRANSPARENCY_FAQ.md @@ -4,7 +4,7 @@ ### What is the Deploy Your AI Application In Production solution accelerator? -This solution accelerator automates the deployment of a complete, production-ready AI application environment in Azure. It provisions Azure AI Foundry, Microsoft Fabric, Azure AI Search, and Microsoft Purview—all pre-wired with private networking, managed identities, and governance controls. +This solution accelerator automates the deployment of a complete, production-ready AI application environment in Azure. It provisions Microsoft Foundry, Microsoft Fabric, Azure AI Search, and Microsoft Purview—all pre-wired with private networking, managed identities, and governance controls. ### What is the intended use of this solution accelerator? diff --git a/docs/automation-outputs-mapping.md b/docs/automation-outputs-mapping.md index 6bbdbb3..e81607d 100644 --- a/docs/automation-outputs-mapping.md +++ b/docs/automation-outputs-mapping.md @@ -39,11 +39,11 @@ The postprovision automation scripts consume deployment outputs via the `AZURE_O | `aiSearchSubscriptionId` | `aiSearchSubscriptionId` | OneLake indexing scripts | Subscription for AI Search | | `aiSearchAdditionalAccessObjectIds` | `aiSearchAdditionalAccessObjectIds` | RBAC scripts | Optional Entra principals granted Search roles | -### AI Foundry +### Microsoft Foundry | Bicep Output | Script Variable | Used By | Purpose | |-------------|-----------------|---------|---------| -| `aiFoundryProjectName` | `aiFoundryName` | `06_setup_ai_foundry_search_rbac.ps1` | AI Foundry project name | +| `aiFoundryProjectName` | `aiFoundryName` | `06_setup_ai_foundry_search_rbac.ps1` | Microsoft Foundry project name | | `aiFoundryServicesName` | `aiServicesName` | RBAC scripts | Cognitive Services account name | ### Purview Integration diff --git a/docs/deploy_app_from_foundry.md b/docs/deploy_app_from_foundry.md index 5058b77..b7f99e6 100644 --- a/docs/deploy_app_from_foundry.md +++ b/docs/deploy_app_from_foundry.md @@ -1,33 +1,33 @@ -# Deploy an Application from Azure AI Foundry +# Deploy an Application from Microsoft Foundry -This guide explains how to deploy a chat application directly from the Azure AI Foundry playground to Azure App Service. +This guide explains how to deploy a chat application directly from the Microsoft Foundry playground to Azure App Service. ## Overview -Azure AI Foundry provides a built-in capability to publish playground experiences as web applications. This accelerator deploys the required infrastructure (App Service, managed identity, networking) so you can publish directly from the Foundry playground. +Microsoft Foundry provides a built-in capability to publish playground experiences as web applications. This accelerator deploys the required infrastructure (App Service, managed identity, networking) so you can publish directly from the Foundry playground. ## Prerequisites - Completed deployment of this accelerator (`azd up`) -- Access to the AI Foundry project via the Jump VM +- Access to the Microsoft Foundry project via the Jump VM - An AI Search index with your data (created via OneLake indexer or manually) ## Steps to Deploy an App from Foundry Playground -### 1. Access AI Foundry via Jump VM +### 1. Access Microsoft Foundry via Jump VM -Since all resources are deployed with private endpoints, you must access AI Foundry through the Jump VM: +Since all resources are deployed with private endpoints, you must access Microsoft Foundry through the Jump VM: 1. Go to the [Azure Portal](https://portal.azure.com) 2. Navigate to your resource group 3. Select the **Jump VM** (Windows Virtual Machine) 4. Click **Connect** → **Bastion** 5. Enter the VM credentials (set during deployment) -6. Once connected, open a browser and navigate to [AI Foundry](https://ai.azure.com) +6. Once connected, open a browser and navigate to [Microsoft Foundry](https://ai.azure.com) ### 2. Configure Your Playground -1. In AI Foundry, select your **Project** +1. In Microsoft Foundry, select your **Project** 2. Navigate to **Playgrounds** → **Chat playground** 3. Configure your deployment: - Select your **GPT model deployment** (e.g., gpt-4o) @@ -61,7 +61,7 @@ After deployment completes: ## Additional Resources - [Deploy a web app for chat on your data](https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/deploy-web-app) -- [Azure AI Foundry documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/) +- [Microsoft Foundry documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/) - [Customize the web app](https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/deploy-web-app#customize-the-web-app) ## Troubleshooting diff --git a/docs/faq.md b/docs/faq.md index ef69848..365c3a9 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,13 +1,13 @@ # Frequently Asked Questions -## How do Azure AI Foundry account and project identities interact with Azure AI Search RBAC? +## How do Microsoft Foundry account and project identities interact with Azure AI Search RBAC? -Fabric/Azure AI Foundry creates **separate managed identities** for the Foundry account and for each project. Azure RBAC permissions do **not** cascade from the account to its projects, so a role assignment that targets the account identity does not automatically grant the same access to the project identity. +Microsoft Foundry creates **separate managed identities** for the Foundry account and for each project. Azure RBAC permissions do **not** cascade from the account to its projects, so a role assignment that targets the account identity does not automatically grant the same access to the project identity. The post-provision script `scripts/automationScripts/OneLakeIndex/06_setup_ai_foundry_search_rbac.ps1` therefore resolves **both** identities: -- `aiFoundryIdentity` → the AI Foundry **account** managed identity -- `projectPrincipalId` → the AI Foundry **project** managed identity +- `aiFoundryIdentity` → the Microsoft Foundry **account** managed identity +- `projectPrincipalId` → the Microsoft Foundry **project** managed identity It then assigns the required Azure AI Search roles to every principal it finds. If the script cannot resolve the project identity, it logs a warning and only the account identity receives the roles. In that case, re-run the script once the project identity exists or assign the roles manually. @@ -46,9 +46,9 @@ az role assignment create \ Because the knowledge source uses the **project** identity when it ingests data, those roles must be granted to the project principal even if the account identity already has them. -## How do I integrate an existing Azure AI Foundry project into the AI Landing Zone? +## How do I integrate an existing Microsoft Foundry project into the AI Landing Zone? -Integrating the new Azure AI Foundry project model (Cognitive Services account plus project announced at Ignite) into an AI Landing Zone is a matter of extending the landing zone controls so the project runs entirely inside the isolated estate. Work through these considerations: +Integrating the Microsoft Foundry project model (Cognitive Services account plus project) into an AI Landing Zone is a matter of extending the landing zone controls so the project runs entirely inside the isolated estate. Work through these considerations: 1. **Locate the project**: Record the account and project resource IDs, region, and tenant. Confirm the region aligns with the landing zone virtual network and private DNS footprint so private endpoints can be created without cross-region limitations. 2. **Carve out network space**: Add a dedicated subnet (or set of subnets) in the landing zone virtual network for the Foundry managed network. Apply the landing zone NSG, UDR, and firewall baselines. If the project already uses managed network isolation, update it to target the new subnet; otherwise plan for a fresh isolated project and migrate assets with export/import tooling. From 90fa399853ebe4bee5f8f53f08f513ff50ae4917 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Fri, 20 Mar 2026 13:20:14 -0400 Subject: [PATCH 14/22] harden Fabric mirroring automation and align docs with Microsoft Foundry --- CHANGELOG.md | 27 +- README.md | 225 +++-- azure.yaml | 16 +- docs/post_deployment_steps.md | 66 +- docs/postgresql_mirroring.md | 159 ++- infra/main.bicep | 18 + .../create_purview_collection.ps1 | 94 +- .../assign_workspace_to_domain.ps1 | 147 ++- .../CreateWorkspace/create_fabric_domain.ps1 | 36 +- .../create_fabric_workspace.ps1 | 246 ++++- .../ensure_active_capacity.ps1 | 53 +- .../register_fabric_datasource.ps1 | 117 ++- .../mirror/create_postgresql_mirror.ps1 | 904 +++++++++++++++--- .../prepare_postgresql_for_mirroring.ps1 | 703 ++++++++++++-- .../run_postgresql_mirroring_followup.ps1 | 59 ++ ...esql_mirroring_prep_with_public_access.ps1 | 75 -- .../test_postgresql_mirroring_prereqs.ps1 | 200 ++++ .../OneLakeIndex/01_setup_rbac.ps1 | 32 +- .../02_create_onelake_skillsets.ps1 | 210 +--- .../OneLakeIndex/03_create_onelake_index.ps1 | 212 +--- .../04_create_onelake_datasource.ps1 | 212 +--- .../05_create_onelake_indexer.ps1 | 212 +--- .../06_setup_ai_foundry_search_rbac.ps1 | 5 + .../OneLakeIndex/SearchHelpers.ps1 | 209 ++++ scripts/automationScripts/SecurityModule.ps1 | 67 +- scripts/preprovision-integrated.ps1 | 36 +- 26 files changed, 3004 insertions(+), 1336 deletions(-) create mode 100644 scripts/automationScripts/FabricWorkspace/mirror/run_postgresql_mirroring_followup.ps1 delete mode 100644 scripts/automationScripts/FabricWorkspace/mirror/run_postgresql_mirroring_prep_with_public_access.ps1 create mode 100644 scripts/automationScripts/FabricWorkspace/mirror/test_postgresql_mirroring_prereqs.ps1 create mode 100644 scripts/automationScripts/OneLakeIndex/SearchHelpers.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 89b5219..f3d01f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,25 @@ All notable changes to this project will be documented in this file. -## [Unreleased] - 2026-03-06 +## [2026-03-20] +### Added +- Read-only PostgreSQL mirroring preflight script for validating runner prerequisites before mirror setup +- PostgreSQL mirroring follow-up wrapper to run preflight, preparation, and mirror creation as a deliberate post-deployment flow +- Shared AI Search helper module for OneLake indexing scripts to centralize public network access toggles and tokenized REST calls + +### Changed +- PostgreSQL mirroring guidance now treats mirroring as a follow-up step after `azd up`, with clearer public-access versus private-network paths +- Postprovision now restores only PostgreSQL mirroring readiness preparation instead of attempting full mirror creation during the main deployment run +- Fabric connection and workspace automation now resolve more values from deployment outputs, azd environment values, and deployed resources when transient hook context is incomplete +- PostgreSQL mirroring scripts now support explicit connection-mode outputs, stronger credential handling, clearer network-path failures, and gateway-aware Fabric connection creation +- Purview collection and Fabric datasource registration scripts now derive default names and deployment context more reliably from outputs and environment values +- Post-deployment and mirroring documentation consolidated the mirror workflow into a single primary runbook and clarified when mirroring should be deferred + +### Removed +- Temporary PostgreSQL mirroring prep wrapper that toggled public access as a separate script +- Fabric connection probe debug script and the redundant PostgreSQL mirroring opt-in guide + +## [2026-03-18] ### Added - Parameter to override Log Analytics workspace resource ID and output mapping for automation scripts - Optional `SKIP_PURVIEW_INTEGRATION` guard for Purview automation scripts (used by hooks when Purview is disabled) @@ -12,9 +30,10 @@ All notable changes to this project will be documented in this file. - Preprovision error output simplified with concise failure reason and optional verbose diagnostics - Main parameter file reordered into required/optional/defaulted sections with clearer comments - OneLake indexing scripts prefer outputs, include AAD-only auth, and handle transient 409 run conflicts +- Post-deployment steps now include Fabric mirroring checklist items and Key Vault networking guidance for retrieving the `fabric_user` password -### Fixed -- Power BI headers initialization in Log Analytics linkage script to resolve workspace ID lookups +### Removed +- Log Analytics linkage script `scripts/automationScripts/FabricPurviewAutomation/connect_log_analytics.ps1` ## [1.3] - 2025-12-09 ### Added @@ -22,7 +41,7 @@ All notable changes to this project will be documented in this file. - Microsoft Purview integration for governance and data cataloging - OneLake indexing pipeline connecting Fabric lakehouses to AI Search - Comprehensive post-provision automation (22 hooks for Fabric/Purview/Search setup) -- New documentation: `deploy_app_from_foundry.md` for publishing apps from AI Foundry +- New documentation: `deploy_app_from_foundry.md` for publishing apps from Microsoft Foundry - New documentation: `TRANSPARENCY_FAQ.md` for responsible AI transparency - New documentation: `NewUserGuide.md` for first-time users - Header icons matching GSA standard format diff --git a/README.md b/README.md index fa03ecd..5066bdb 100644 --- a/README.md +++ b/README.md @@ -1,87 +1,68 @@ # Deploy Your AI Application In Production -Stand up a complete, production-ready AI application environment in Azure with a single command. This solution accelerator provisions Azure AI Foundry, Microsoft Fabric, Azure AI Search, and connects to your tenant level Microsoft Purview (when resourceId is provided) —all pre-wired with private networking, managed identities, and governance controls—so you can move from proof-of-concept to production in hours instead of weeks. +Stand up a complete, production-ready AI application environment in Azure with a single command. This solution accelerator provisions Microsoft Foundry, Microsoft Fabric, Azure AI Search, and connects to your tenant level Microsoft Purview (when resourceId is provided) —all pre-wired with private networking, managed identities, and governance controls—so you can move from proof-of-concept to production in hours instead of weeks.
-[**SOLUTION OVERVIEW**](#solution-overview) \| [**QUICK DEPLOY**](#quick-deploy) \| [**BUSINESS SCENARIO**](#business-scenario) \| [**SUPPORTING DOCUMENTATION**](#supporting-documentation) +[**START HERE**](#start-here) \| [**SOLUTION OVERVIEW**](#solution-overview) \| [**BUSINESS SCENARIO**](#business-scenario) \| [**SUPPORTING DOCUMENTATION**](#supporting-documentation)
- - - -

-Solution Overview + + + +

+Start Here

-This accelerator extends the [AI Landing Zone](https://github.com/Azure/ai-landing-zone) reference architecture to deliver an enterprise-scale, production-ready foundation for deploying secure AI applications and agents in Azure. It packages Microsoft's Well-Architected Framework principles around networking, identity, and operations from day zero. - -### Solution Architecture - -| ![Architecture](./img/Architecture/Depoly-AI-App-in-Prod-Architecture-final.png) | -|---| - -### Key Components +### Use This Accelerator When -| Component | Purpose | -|-----------|---------| -| **Azure AI Foundry** | Unified platform for AI development, testing, and deployment with playground, prompt flow, and publishing | -| **Microsoft Fabric** | Data foundation with lakehouses (bronze/silver/gold) for document storage and OneLake indexing | -| **Azure Database for PostgreSQL** | Optional operational data source that can be prepared for Microsoft Fabric mirroring after deployment | -| **Azure AI Search** | Retrieval backbone enabling RAG (Retrieval-Augmented Generation) chat experiences | -| **Microsoft Purview** | Governance layer for cataloging, scans, and Data Security Posture Management | -| **Private Networking** | All traffic secured via private endpoints—no public internet exposure | +This accelerator is a good fit if you want an end to end AI and Data platform built from the AI Landing Zone, in one deployment: -
- -### Additional Resources - -- [AI Landing Zone Documentation](https://github.com/Azure/bicep-ptn-aiml-landing-zone) -- [Azure AI Foundry Documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/) -- [Microsoft Fabric Documentation](https://learn.microsoft.com/en-us/fabric/) - -
+1. Microsoft Foundry +2. Azure AI Search and OneLake index +3. Microsoft Fabric workspace and lakehouses +4. Optional Microsoft Purview integration +5. Private networking and production-style Azure controls - - - -### Key features -
- Click to learn more about the key features this solution enables +If you only want a small Foundry demo or a basic RAG sample, this repo is heavier than you need. - - **Single-command deployment**
- Run `azd up` to provision 30+ Azure resources in ~45 minutes with pre-wired security controls. - - - **Production-grade security from day zero**
- Private endpoints, managed identities, and RBAC enabled by default—no public internet exposure. +### Required Deployment Steps - - **Integrated data-to-AI pipeline**
- Connect Fabric lakehouses → OneLake indexer → AI Search → Foundry playground for grounded chat experiences. +1. Start from an environment with `azd`, `az`, and `pwsh` available +2. Authenticate with Azure and select the target subscription and region +3. Initialize git submodules if you are not using Codespaces or Dev Containers +4. Review `infra/main.bicepparam` and decide whether Fabric and Purview are enabled for the first run +5. Check Azure OpenAI quota in the target region +6. Run `azd up` +7. Validate the deployment in [docs/post_deployment_steps.md](./docs/post_deployment_steps.md) - - **PostgreSQL-to-Fabric mirroring path**
- Provision Azure Database for PostgreSQL, prepare it for Fabric mirroring, create the Fabric connection, and mirror operational data into OneLake for downstream analytics and AI scenarios. +For the first attempt, the lowest-risk path is to keep Fabric and Purview disabled unless you already have their prerequisites in place. - - **Governance built-in**
- Microsoft Purview integration for cataloging, scoped scans, and Data Security Posture Management (DSPM). +### Dependency Map - - **Extensible AVM-driven platform**
- Toggle additional Azure services through AI Landing Zone parameters for broader intelligent app scenarios. +| Area | Required to enable it | If missing | +|------|------------------------|------------| +| Base deployment | Azure subscription permissions, `az`, `azd`, `pwsh`, Azure sign-in, initialized submodules, Azure OpenAI quota | `azd up` fails before or during provisioning | +| Fabric automation | Fabric Administrator permissions or an existing Fabric setup, plus valid Fabric parameter values | Postprovision Fabric steps fail | +| Fabric capacity creation | At least one valid `fabricCapacityAdmins` entry when `fabricCapacityPreset='create'` | Capacity creation fails | +| Purview integration | Existing Purview account resource ID in the target tenant and subscription | Purview steps fail | +| PostgreSQL mirroring | PostgreSQL enabled in the deployment with `postgreSqlNetworkIsolation = false`, then follow the post-deploy mirror steps | Database deploys, but mirroring is not completed | +| Private networking | `networkIsolation = true` and enough deployment time for private endpoint provisioning | Deployment takes longer and is harder to troubleshoot if other prerequisites are not already stable | -
+### Choose Your Starting Path -

- - - -

-Quick deploy -

+| Goal | Recommended path | +|------|------------------| +| Fastest realistic validation | Local `azd up` workflow | +| Clean environment with fewer local setup issues | GitHub Codespaces | +| Deep customization before deploy | Read [docs/PARAMETER_GUIDE.md](./docs/PARAMETER_GUIDE.md) first | +| Lowest-risk first run | Disable Fabric and Purview, then re-enable later | -### How to install or deploy +### How to Install or Deploy Follow the deployment guide to deploy this solution to your own Azure subscription. @@ -96,7 +77,7 @@ Follow the deployment guide to deploy this solution to your own Azure subscripti
-> **Important: This repository uses git submodules** +> **Important: This repository uses git submodules** >
Clone with submodules included: > ```bash > git clone --recurse-submodules https://github.com/microsoft/Deploy-Your-AI-Application-In-Production.git @@ -107,15 +88,21 @@ Follow the deployment guide to deploy this solution to your own Azure subscripti > ``` > **GitHub Codespaces and Dev Containers handle this automatically.** -> **Windows shell note** ->
Preprovision runs with PowerShell (`pwsh`) by default. Run `azd` from PowerShell 7+ (or any terminal that can invoke `pwsh`). - -
+> **Shell requirement** +>
The repo uses `azd` as the main deployment interface. The preprovision and postprovision hooks run with PowerShell (`pwsh`), so your environment must be able to invoke `pwsh`. -> **Important: Check Azure OpenAI Quota Availability** +> **Important: Check Azure OpenAI quota availability** >
To ensure sufficient quota is available in your subscription, please follow the [quota check instructions guide](./docs/quota_check.md) before deploying. -
+### First Deployment Checklist + +1. Run `azd auth login` and confirm the target subscription with `az account show` +2. Create a new environment and set `AZURE_SUBSCRIPTION_ID` and `AZURE_LOCATION` +3. Review `infra/main.bicepparam`, especially `principalId`, `aiSearchAdditionalAccessObjectIds`, `fabricCapacityPreset`, `fabricWorkspacePreset`, `fabricCapacityAdmins`, `purviewAccountResourceId`, `networkIsolation`, and `postgreSqlNetworkIsolation` +4. Run `azd up` +5. Follow [docs/post_deployment_steps.md](./docs/post_deployment_steps.md) to verify the deployment + +> **Note:** Mirroring automation in the current branch is set for PostgreSQL deployments where `postgreSqlNetworkIsolation = false`. If you want PostgreSQL fully isolated, keep the private networking path and plan on the Fabric VNet gateway route for end-to-end mirroring. ### Prerequisites & Costs @@ -144,13 +131,13 @@ Follow the deployment guide to deploy this solution to your own Azure subscripti | Service | SKU | Estimated Monthly Cost | |---------|-----|------------------------| - | Azure AI Foundry | Standard | [Pricing](https://azure.microsoft.com/pricing/details/machine-learning/) | + | Microsoft Foundry | Standard | [Pricing](https://azure.microsoft.com/pricing/details/machine-learning/) | | Azure OpenAI | Pay-per-token | [Pricing](https://azure.microsoft.com/pricing/details/cognitive-services/openai-service/) | | Azure AI Search | Standard | [Pricing](https://azure.microsoft.com/pricing/details/search/) | | Microsoft Fabric | F8 Capacity (if enabled) | [Pricing](https://azure.microsoft.com/pricing/details/microsoft-fabric/) | | Virtual Network + Bastion | Standard | [Pricing](https://azure.microsoft.com/pricing/details/azure-bastion/) | - > **Cost Optimization:** Fabric capacity can be paused when not in use. Use `az fabric capacity suspend` to stop billing. + > **Cost Optimization:** Fabric capacity can be paused when not in use. Use `az fabric capacity suspend` to stop billing. Use the [Azure Pricing Calculator](https://azure.microsoft.com/pricing/calculator/) for detailed estimates. @@ -159,54 +146,87 @@ Follow the deployment guide to deploy this solution to your own Azure subscripti
- + -

-Business Scenario +

+Solution Overview

-### What You Get +This accelerator extends the [AI Landing Zone](https://github.com/Azure/ai-landing-zone) reference architecture to deliver an enterprise-scale, production-ready foundation for deploying secure AI applications and agents in Azure. It packages Microsoft's Well-Architected Framework principles around networking, identity, and operations from day zero. -After deployment, you'll have a complete, enterprise-ready platform that unifies AI development, data management, and governance: +### Solution Architecture -| Layer | What's Deployed | Why It Matters | -|-------|-----------------|----------------| -| **AI Platform** | Azure AI Foundry with OpenAI models, playground, and prompt flow | Build, test, and publish AI chat applications without managing infrastructure | -| **Data Foundation** | Microsoft Fabric with bronze/silver/gold lakehouses and OneLake indexing | Store documents at scale and automatically feed them into your AI workflows | -| **Operational Data Mirroring** | Azure Database for PostgreSQL prepared for Fabric mirroring | Bring PostgreSQL operational data into Fabric with a documented connection and mirror setup path | -| **Search & Retrieval** | Azure AI Search with vector and semantic search | Enable RAG (Retrieval-Augmented Generation) for grounded, accurate AI responses | -| **Governance** | Microsoft Purview with cataloging, scans, and DSPM | Track data lineage, enforce policies, and maintain compliance visibility | -| **Security** | Private endpoints, managed identities, RBAC, network isolation | Zero public internet exposure—all traffic stays on the Microsoft backbone | +| ![Architecture](./img/Architecture/Depoly-AI-App-in-Prod-Architecture-final.png) | +|---| + +### Key Components + +| Component | Purpose | +|-----------|---------| +| **Microsoft Foundry** | Unified platform for AI development, testing, and deployment with playground, prompt flow, and publishing | +| **Microsoft Fabric** | Data foundation with lakehouses (bronze/silver/gold) for document storage and OneLake indexing | +| **Azure Database for PostgreSQL** | Optional operational data source that can be prepared for Microsoft Fabric mirroring, including automated Fabric connection creation or reuse after deployment | +| **Azure AI Search** | Retrieval backbone enabling RAG (Retrieval-Augmented Generation) chat experiences | +| **Microsoft Purview** | Governance layer for cataloging, scans, and Data Security Posture Management | +| **Private Networking** | All traffic secured via private endpoints—no public internet exposure |
+### Additional Resources + +- [AI Landing Zone Documentation](https://github.com/Azure/bicep-ptn-aiml-landing-zone) +- [Microsoft Foundry documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/) +- [Microsoft Fabric Documentation](https://learn.microsoft.com/en-us/fabric/) + ### Key Features
- Click to learn more about key features + Click to learn more about the key features this solution enables - - **Production-grade AI Foundry deployments** -
Stand up Azure AI Foundry projects in a locked-down virtual network with private endpoints, managed identities, and telemetry aligned to the Well-Architected Framework. + - **Single-command deployment**
+ Run `azd up` to provision 30+ Azure resources in ~45 minutes with pre-wired security controls. - - **Fabric-powered retrieval workflows** -
Land documents in a Fabric lakehouse, index them with OneLake + Azure AI Search, and wire the index into the Foundry playground for grounded chat experiences. + - **Production-grade security from day zero**
+ Private endpoints, managed identities, and RBAC enabled by default—no public internet exposure. - - **Fabric mirroring for PostgreSQL** -
Prepare Azure Database for PostgreSQL for Fabric mirroring, create the Fabric connection, and mirror source data into Fabric using the documented post-deployment flow. + - **Integrated data-to-AI pipeline**
+ Connect Fabric lakehouses → OneLake indexer → AI Search → Foundry playground for grounded chat experiences. - - **Governed data and agent operations** -
Integrate Microsoft Purview for cataloging, scoped scans, and Data Security Posture Management (DSPM) so compliance teams can monitor the same assets the app consumes. + - **PostgreSQL-to-Fabric mirroring path**
+ Provision Azure Database for PostgreSQL, prepare it for Fabric mirroring, automatically create or reuse the Fabric connection, and mirror operational data into OneLake for downstream analytics and AI scenarios. - - **Extensible AVM-driven platform** -
Toggle additional Azure services (API Management, Cosmos DB, SQL, and more) through AI Landing Zone parameters to tailor the environment for broader intelligent app scenarios. + - **Governance built-in**
+ Microsoft Purview integration for cataloging, scoped scans, and Data Security Posture Management (DSPM). - - **Launch-ready demos and pilots** -
Publish experiences from Azure AI Foundry directly to a browser-based application, giving stakeholders an end-to-end view from infrastructure to user-facing app. + - **Extensible AVM-driven platform**
+ Toggle additional Azure services through AI Landing Zone parameters for broader intelligent app scenarios.

+ + + +

+Business Scenario +

+ +### What You Get + +After deployment, you'll have a complete, enterprise-ready platform that unifies AI development, data management, and governance: + +| Layer | What's Deployed | Why It Matters | +|-------|-----------------|----------------| +| **AI Platform** | Microsoft Foundry with OpenAI models, playground, and prompt flow | Build, test, and publish AI chat applications without managing infrastructure | +| **Data Foundation** | Microsoft Fabric with bronze/silver/gold lakehouses and OneLake indexing | Store documents at scale and automatically feed them into your AI workflows | +| **Operational Data Mirroring** | Azure Database for PostgreSQL prepared for Fabric mirroring | Bring PostgreSQL operational data into Fabric with an automated connection-and-mirror flow plus documented fallback steps | +| **Search & Retrieval** | Azure AI Search with vector and semantic search | Enable RAG (Retrieval-Augmented Generation) for grounded, accurate AI responses | +| **Governance** | Microsoft Purview with cataloging, scans, and DSPM | Track data lineage, enforce policies, and maintain compliance visibility | +| **Security** | Private endpoints, managed identities, RBAC, network isolation | Zero public internet exposure—all traffic stays on the Microsoft backbone | + +
+ ### Sample Workflow 1. **Deploy infrastructure** → Run `azd up` to provision all resources (~45 minutes) @@ -216,15 +236,14 @@ After deployment, you'll have a complete, enterprise-ready platform that unifies 5. **Publish application** → Deploy the chat experience to end users 6. **Monitor governance** → Review data lineage and security posture in Purview -### PostgreSQL Mirroring Setup +### PostgreSQL Post-Provision Steps -If you deploy Azure Database for PostgreSQL, the repo also supports a documented Fabric mirroring path after deployment: +If you deploy Azure Database for PostgreSQL, use these docs after deployment: -1. Prepare the PostgreSQL server and mirroring user with the provided automation. -2. Create the Fabric PostgreSQL connection using the `fabric_user` credentials stored in Key Vault. -3. Start the mirror in Fabric so PostgreSQL data lands in OneLake. +1. [docs/postgresql_mirroring.md](./docs/postgresql_mirroring.md) +2. [docs/post_deployment_steps.md](./docs/post_deployment_steps.md) -See the detailed steps in [docs/postgresql_mirroring.md](./docs/postgresql_mirroring.md) and the shorter checklist in [docs/post_deployment_steps.md](./docs/post_deployment_steps.md). +If the post-provision mirroring automation cannot complete, start with the **Minimal Manual Fallback** section in [docs/postgresql_mirroring.md](./docs/postgresql_mirroring.md). It calls out the shortest path for both public-access and private-network deployments.
@@ -241,7 +260,7 @@ Supporting documentation |----------|-------------| | [Deployment Guide](./docs/DeploymentGuide.md) | Complete deployment instructions | | [Post Deployment Steps](./docs/post_deployment_steps.md) | Verify your deployment | -| [PostgreSQL Mirroring](./docs/postgresql_mirroring.md) | Create the Fabric connection and mirror PostgreSQL | +| [PostgreSQL Mirroring](./docs/postgresql_mirroring.md) | Automate or troubleshoot the Fabric connection and PostgreSQL mirror flow | | [Parameter Guide](./docs/PARAMETER_GUIDE.md) | Configure deployment parameters | | [Quota Check Guide](./docs/quota_check.md) | Check Azure OpenAI quota availability | @@ -264,7 +283,7 @@ Supporting documentation **Recommendations:** - Enable [GitHub secret scanning](https://docs.github.com/code-security/secret-scanning/about-secret-scanning) on your repository - Consider enabling [Microsoft Defender for Cloud](https://learn.microsoft.com/azure/defender-for-cloud/) - - Review the [AI Foundry security documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/) + - Review the [Microsoft Foundry security documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/) > ⚠️ **Important:** This template is built to showcase Azure services. Implement additional security measures before production use. diff --git a/azure.yaml b/azure.yaml index 65fc3d0..b76fdd8 100644 --- a/azure.yaml +++ b/azure.yaml @@ -55,13 +55,13 @@ hooks: continueOnError: false # Stage 5: Purview Collection Creation - - run: "$env:SKIP_PURVIEW_INTEGRATION='true'; ./scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1" + - run: ./scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1 interactive: false shell: pwsh continueOnError: false # Stage 6: Register Fabric as Purview Data Source - - run: "$env:SKIP_PURVIEW_INTEGRATION='true'; ./scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1" + - run: ./scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1 interactive: false shell: pwsh continueOnError: false @@ -72,14 +72,8 @@ hooks: shell: pwsh continueOnError: false - # Stage 7.4: Prepare PostgreSQL for Fabric mirroring (server params + role) - - run: ./scripts/automationScripts/FabricWorkspace/Mirror/run_postgresql_mirroring_prep_with_public_access.ps1 - interactive: false - shell: pwsh - continueOnError: false - - # Stage 7.5: Create PostgreSQL Mirrored Database (if PostgreSQL is provisioned) - - run: ./scripts/automationScripts/FabricWorkspace/Mirror/create_postgresql_mirror.ps1 + # Stage 7.5: Prepare PostgreSQL server for Fabric mirroring readiness + - run: ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 interactive: false shell: pwsh continueOnError: false @@ -139,7 +133,7 @@ hooks: continueOnError: false # Stage 17: Trigger Purview Scan (if Purview enabled) - - run: "$env:SKIP_PURVIEW_INTEGRATION='true'; ./scripts/automationScripts/FabricPurviewAutomation/trigger_purview_scan_for_fabric_workspace.ps1" + - run: ./scripts/automationScripts/FabricPurviewAutomation/trigger_purview_scan_for_fabric_workspace.ps1 interactive: false shell: pwsh continueOnError: false diff --git a/docs/post_deployment_steps.md b/docs/post_deployment_steps.md index 86e9641..b1cd628 100644 --- a/docs/post_deployment_steps.md +++ b/docs/post_deployment_steps.md @@ -10,8 +10,7 @@ After running `azd up` or `azd provision` followed by `azd hooks run postprovisi |-----------|---------------|----------------| | Fabric Capacity | Azure Portal → Microsoft Fabric capacities | **Active** (not Paused) | | Fabric Workspace | [app.fabric.microsoft.com](https://app.fabric.microsoft.com) | Workspace visible with 3 lakehouses | -| PostgreSQL Mirroring (if enabled) | Fabric → Workspace → Connections/Mirror | Connection saved and mirror running | -| AI Foundry Project | [ai.azure.com](https://ai.azure.com) | Project accessible, models deployed | +| Microsoft Foundry project | [ai.azure.com](https://ai.azure.com) | Project accessible, models deployed | | AI Search Index | Azure Portal → AI Search → Indexes | `onelake-index` exists with documents | | Purview Scan | Purview Portal → Data Map → Sources | Fabric data source registered | @@ -48,27 +47,38 @@ az fabric capacity resume --capacity-name --resource-group "` and rerun the script. +2. If you have not run the separate mirroring follow-up, stop here for this test cycle. + - The deployment can still be considered successful for Fabric workspace, PostgreSQL server, and Purview automation. + - PostgreSQL mirroring remains a documented follow-up item, not a same-run success criterion. +3. If you want mirroring now, follow the current runbook in [PostgreSQL mirroring](./postgresql_mirroring.md). +4. After the follow-up completes, verify `azd env get-value fabricPostgresConnectionId` returns a Fabric connection ID. +5. In Fabric, confirm the PostgreSQL connection exists under **Connections** and that the mirrored database is running. --- -## 3. Verify AI Foundry Project +## 3. Verify Microsoft Foundry Project 1. Navigate to [ai.azure.com](https://ai.azure.com) -2. Sign in and select your AI Foundry project +2. Sign in and select your Microsoft Foundry project 3. Verify: - **Models** — Check that GPT-4o and text-embedding-ada-002 (or configured models) are deployed - **Connections** — AI Search connection should be listed @@ -76,7 +86,7 @@ Use these short steps to verify the automatic Fabric connection and mirroring fl ### Testing AI Search Connection in Playground -1. In AI Foundry, go to **Playgrounds** → **Chat** +1. In Microsoft Foundry, go to **Playgrounds** → **Chat** 2. Click **Add your data** 3. Select your AI Search index (`onelake-index`) 4. Ask a question about your indexed documents @@ -113,6 +123,16 @@ If no documents appear, check: 3. Verify the Fabric data source is registered (e.g., `Fabric-Workspace-`) 4. Check **Scans** to see if the initial scan completed +If `purviewCollectionName` is left empty in [infra/main.bicepparam](../infra/main.bicepparam), the automation now uses `collection-`. + +If you need to rerun the Purview steps after provisioning: + +```powershell +pwsh ./scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1 +pwsh ./scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1 +pwsh ./scripts/automationScripts/FabricPurviewAutomation/trigger_purview_scan_for_fabric_workspace.ps1 +``` + ### Data Lineage 1. In Purview, go to **Data Catalog** → **Browse** @@ -125,15 +145,15 @@ If no documents appear, check: When `networkIsolation` is set to `true`: -### Check AI Foundry Network Settings +### Check Microsoft Foundry Network Settings -1. Go to **Azure Portal** → **Azure AI Foundry** → your account +1. Go to **Azure Portal** → **Microsoft Foundry** → your account 2. Click **Settings** → **Networking** 3. Verify: - **Public network access**: Disabled (if fully isolated) - **Private endpoints**: Active connections listed - ![Image showing the Azure Portal for AI Foundry and the settings blade](../img/provisioning/checkNetworkIsolation1.png) + ![Image showing the Azure Portal for Microsoft Foundry and the settings blade](../img/provisioning/checkNetworkIsolation1.png) 4. Open the **Workspace managed outbound access** tab to see private endpoints @@ -141,7 +161,7 @@ When `networkIsolation` is set to `true`: ### Test Isolation -When accessing AI Foundry from outside the virtual network, you should see an access denied message: +When accessing Microsoft Foundry from outside the virtual network, you should see an access denied message: ![Image showing access denied from public network](../img/provisioning/checkNetworkIsolation4.png) @@ -170,7 +190,7 @@ For network-isolated deployments, use Azure Bastion to access resources: ![Image showing bastion login](../img/provisioning/checkNetworkIsolation8.png) 5. Once connected, open **Edge browser** and navigate to: - - [ai.azure.com](https://ai.azure.com) — AI Foundry + - [ai.azure.com](https://ai.azure.com) — Microsoft Foundry - [app.fabric.microsoft.com](https://app.fabric.microsoft.com) — Fabric 6. Complete MFA if prompted @@ -195,9 +215,9 @@ az resource show --ids /subscriptions//resourceGroups//providers/Micros az fabric capacity resume --capacity-name --resource-group ``` -### AI Search Connection Fails in AI Foundry Playground +### AI Search Connection Fails in Microsoft Foundry Playground -Verify RBAC roles are assigned to the AI Foundry identities: +Verify RBAC roles are assigned to the Microsoft Foundry identities: ```bash # Get the AI Search resource ID @@ -208,7 +228,7 @@ az role assignment list --scope $SEARCH_ID --output table ``` Required roles on the AI Search service: -- **Search Service Contributor** — For the AI Foundry account and project managed identities +- **Search Service Contributor** — For the Microsoft Foundry account and project managed identities - **Search Index Data Contributor** — For read/write access to index data - **Search Index Data Reader** — For read access to index data @@ -266,7 +286,7 @@ pwsh ./scripts/automationScripts/.ps1 Once verification is complete: 1. **Upload documents** to the bronze lakehouse for indexing -2. **Test the AI Foundry playground** with your indexed content +2. **Test the Microsoft Foundry playground** with your indexed content 3. **Configure additional models** if needed -4. **[Deploy your app](./deploy_app_from_foundry.md)** from the AI Foundry playground +4. **[Deploy your app](./deploy_app_from_foundry.md)** from the Microsoft Foundry playground 5. **Review governance** in Microsoft Purview diff --git a/docs/postgresql_mirroring.md b/docs/postgresql_mirroring.md index 761a2f1..b69f71f 100644 --- a/docs/postgresql_mirroring.md +++ b/docs/postgresql_mirroring.md @@ -2,13 +2,141 @@ This guide explains how to complete PostgreSQL mirroring in Microsoft Fabric after deployment. +Mirroring automation in the current branch is set for PostgreSQL deployments where `postgreSqlNetworkIsolation = false`. + +If you want full PostgreSQL isolation, the database deployment can still succeed, but end-to-end Fabric mirroring moves to the Fabric VNet gateway path. + +If you are not changing the network approach right now, there are only two valid post-deployment outcomes: + +1. Use a public-network path that lets Fabric reach PostgreSQL, then complete the mirror. +2. Keep PostgreSQL private and treat mirroring as deferred. + +Do not expect a private-endpoint PostgreSQL deployment to produce a working Fabric mirror during the main deployment workflow alone. + +## Minimal Manual Fallback + +Use the shortest follow-up path below after deployment. + +Choose one path up front: + +- If you do not want to expose PostgreSQL publicly for Fabric, stop after the rest of post-provision validation and leave mirroring for a later run. +- If you want the mirror now, use the public-access path below. + +### Public Access Enabled + +Use this path when the PostgreSQL server has `publicNetworkAccess=Enabled`. In this repo, that corresponds to `postgreSqlNetworkIsolation = false`. + +1. In Azure Portal, open the PostgreSQL Flexible Server. +2. Open **Fabric Mirroring** on the server and let the portal prepare the server-side prerequisites. + - Microsoft documentation explicitly calls out this page as the path that automates the server-side mirroring prerequisites. + - This overlaps with what `prepare_postgresql_for_mirroring.ps1` is trying to automate. + - It does **not** create the Fabric connection object or the mirrored database item in the Fabric workspace. +3. In **Networking**, make sure Fabric can reach the server. + - Shortest path: add the `0.0.0.0` firewall rule to allow Azure services. + - If you only need to read the password secret yourself, temporarily add only your client IP to Key Vault, retrieve the secret, then remove the IP again. +4. In Fabric, create a new **Mirrored Azure Database for PostgreSQL** item. +5. Use these deployment values instead of hardcoding names: + +```powershell +azd env get-value postgreSqlServerFqdn +azd env get-value postgreSqlMirrorConnectionModeOut +azd env get-value postgreSqlMirrorConnectionUserNameOut +azd env get-value postgreSqlMirrorConnectionSecretNameOut +``` + +6. Read the password from Key Vault: + +```powershell +az keyvault secret show --vault-name --name --query value -o tsv +``` + +7. If the admin login fails in Fabric, switch to the dedicated Fabric PostgreSQL role instead of continuing to retry `pgadmin`. +8. After the connection is created, persist the connection ID for future reruns: + +```powershell +azd env set-value fabricPostgresConnectionId "" +``` + +### Private Network or Private Endpoint + +Use this path when the PostgreSQL server is private-only or Fabric cannot reach it over public networking. + +1. Treat mirroring as deferred for this provisioning cycle. +2. Use the PostgreSQL server's **Fabric Mirroring** page in Azure Portal only if you want to confirm the source-server prerequisite experience. +3. Continue validating the rest of the deployment: Fabric workspace, lakehouses, PostgreSQL server, AI Search, and Purview. +4. For end-to-end mirroring with PostgreSQL kept private, use the Fabric VNet gateway route. + +### What to Do First + +If you just need the mirror working with the fewest manual steps: + +1. Prefer **Public Access Enabled** plus **Allow Azure services** when your deployment intentionally permits public connectivity. +2. Prefer the PostgreSQL server's **Fabric Mirroring** page in Azure Portal over running local SQL. +3. Use the dedicated Fabric role if the admin login is rejected by Fabric. + +If you are intentionally staying private for now, the correct action is to skip mirror creation for this provisioning test and continue validating the rest of the deployment. + +## Recommended Repo Flow + +In this repo, mirroring should be treated as a deliberate follow-up step after the main deployment completes. + +That means: + +1. `azd up` deploys the infrastructure and core postprovision automation. +2. PostgreSQL mirroring is not a required same-run success criterion. +3. If you want mirroring, run it afterward from a runner that can actually reach PostgreSQL, Key Vault, and Fabric. + +The cleanest sequence is: + +1. Run `azd up`. +2. Validate the deployment with [post_deployment_steps.md](./post_deployment_steps.md). +3. Connect to the deployed VM or another runner with PostgreSQL network reachability. +4. Run the mirroring follow-up flow. +5. Verify the Fabric connection and mirrored database. + +Running from the deployed VM is usually the least fragile option because it avoids local DNS, firewall, VPN, and endpoint-security issues. + +### Follow-Up Wrapper + +If you want the repo-managed sequence, run: + +```powershell +pwsh -NoProfile -File .\scripts\automationScripts\FabricWorkspace\Mirror\run_postgresql_mirroring_followup.ps1 +``` + +That wrapper runs, in order: + +1. `test_postgresql_mirroring_prereqs.ps1` +2. `prepare_postgresql_for_mirroring.ps1` +3. `create_postgresql_mirror.ps1` + +### Preflight First + +Before attempting mirroring from a VM or any other runner, use the read-only preflight: + +```powershell +pwsh -NoProfile -File .\scripts\automationScripts\FabricWorkspace\Mirror\test_postgresql_mirroring_prereqs.ps1 +``` + +It checks the things that usually break the flow: + +1. `az` and `azd` availability +2. Azure sign-in +3. required `azd` environment values +4. DNS resolution for PostgreSQL +5. TCP connectivity to PostgreSQL on `5432` +6. Fabric token acquisition + +If preflight fails, fix the runner first instead of continuing into SQL prep or Fabric connection creation. + ## Automation status What is automated today: -- PostgreSQL server prep (roles, grants, seed table, parameters). -- Fabric connection creation or reuse for PostgreSQL mirroring. -- Mirror creation after the Fabric connection is resolved. +- PostgreSQL server deployment during `azd up`. +- PostgreSQL mirroring prep during `azd up` postprovision (server parameters, auth mode, mirroring role/grants, and seed table). +- Manual or follow-up Fabric connection creation for PostgreSQL mirroring. +- Manual or follow-up mirror creation after the Fabric connection is resolved. ## Why a Fabric Connection Is Required @@ -32,11 +160,19 @@ Get the PostgreSQL server FQDN and database name: - Fabric login: from `azd env get-value postgreSqlMirrorConnectionUserNameOut` - Fabric password secret name: from `azd env get-value postgreSqlMirrorConnectionSecretNameOut` -## Step 2: Prepare the Database (Automated by Default) +## Step 2: Prepare the Database (Run Automatically During Postprovision) The mirroring prep script configures the server and creates a seed table so Fabric always finds at least one table to replicate. -### Automated (recommended) +During `azd up`, postprovision now runs: + +```powershell +pwsh ./scripts/automationScripts/FabricWorkspace/Mirror/prepare_postgresql_for_mirroring.ps1 +``` + +Re-run it manually only if you need to repair or reapply the PostgreSQL mirroring readiness settings. + +### Manual rerun Run: @@ -50,8 +186,9 @@ If you are running from a non-VNet host and the Key Vault blocks public access, $env:POSTGRES_TEMP_ENABLE_KV_PUBLIC_ACCESS = 'true' ``` -What it does now: +What it does: +- Invokes Azure-side Fabric mirroring enablement for the selected database when available. - Creates or validates the `fabric_user` role when mode is `fabricUser`. - Ensures PostgreSQL auth modes are enabled (password + Entra). - Grants `azure_cdc_admin` and database permissions. @@ -60,6 +197,10 @@ What it does now: ### Manual (only if automation fails) +If your deployment allows public access, the shortest supported fallback is usually the server's **Fabric Mirroring** page in Azure Portal instead of running these SQL statements manually. + +Use that portal page only for the server-side prerequisite work. You still need either the automation or a manual Fabric connection and mirrored database creation step afterward. + Connect as `pgadmin` in the `postgres` database and run: ```sql @@ -114,6 +255,8 @@ Without `fabricPostgresGatewayId`, the script creates a standard cloud connectio ### Manual fallback +If your deployment has public access enabled, try the **Minimal Manual Fallback** section first. It is shorter than manually creating the Fabric connection from scratch. + If you need to create the Fabric connection manually, do not hardcode `fabric_user`, `pgadmin`, or the secret name. Read the values from the deployment outputs first: ```powershell @@ -157,7 +300,8 @@ If the previous script already created the connection automatically, re-running ## Notes - The deployment now attempts to create or reuse the Fabric PostgreSQL connection automatically before creating the mirror. -- If automatic connection creation cannot reach Key Vault or the source database, the script exits without failing the entire deployment and leaves a manual fallback path. +- If automatic connection creation cannot reach Key Vault or the source database, the script leaves a manual fallback path. +- Without public reachability or `fabricPostgresGatewayId`, a private PostgreSQL server is not expected to mirror successfully. - If you rotate passwords, update the Fabric connection in the workspace. ## Troubleshooting @@ -172,6 +316,7 @@ If the previous script already created the connection automatically, re-running - If the PostgreSQL server is private-only, set `fabricPostgresGatewayId` in `azd` before rerunning the script so the connection is created under the Fabric VNet gateway. - If the gateway ID is not set, the automation uses a shareable cloud connection. +- If automation still cannot complete SQL prep from your machine, use the PostgreSQL server's **Fabric Mirroring** page first, then fall back to a Bastion or other VNet-connected host only if needed. ### Must be owner of table diff --git a/infra/main.bicep b/infra/main.bicep index 4b06447..f4543c8 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -243,6 +243,19 @@ param postgreSqlFabricUserName string = 'fabric_user' @description('Key Vault secret name for the Fabric mirroring PostgreSQL role password.') param postgreSqlFabricUserSecretName string = 'postgres-fabric-user-password' +@description('Credential mode used for the Fabric PostgreSQL connection. Use fabricUser for the production-oriented least-privilege path or admin for a simplified demo automation path.') +@allowed([ + 'fabricUser' + 'admin' +]) +param postgreSqlMirrorConnectionMode string = 'fabricUser' + +@description('Authentication configuration for PostgreSQL Flexible Server. Defaults to both Microsoft Entra and password authentication enabled so Fabric mirroring can be configured immediately after deployment.') +param postgreSqlAuthConfig resourceInput<'Microsoft.DBforPostgreSQL/flexibleServers@2025-06-01-preview'>.properties.authConfig = { + activeDirectoryAuth: 'Enabled' + passwordAuth: 'Enabled' +} + @description('PostgreSQL SKU name (tier + family + cores).') param postgreSqlSkuName string = 'Standard_D2s_v3' @@ -296,6 +309,7 @@ param postgreSqlVersion string = '16' @description('PostgreSQL storage size in GB.') param postgreSqlStorageSizeGB int = 32 @description('Generated value used when postgreSqlAdminPassword is left as the placeholder token.') +@secure() param generatedPostgreSqlAdminPassword string = newGuid() // ======================================== @@ -380,6 +394,7 @@ module postgreSqlFlexibleServer 'br/public:avm/res/db-for-postgre-sql/flexible-s tier: postgreSqlTier administratorLogin: postgreSqlAdminLogin administratorLoginPassword: effectivePostgreSqlAdminPassword + authConfig: postgreSqlAuthConfig managedIdentities: { systemAssigned: true } @@ -460,6 +475,9 @@ output postgreSqlAdminSecretName string = deployPostgreSql && enablePostgreSqlKe output postgreSqlAdminLoginOut string = deployPostgreSql ? postgreSqlAdminLogin : '' output postgreSqlFabricUserNameOut string = deployPostgreSql ? postgreSqlFabricUserName : '' output postgreSqlFabricUserSecretNameOut string = deployPostgreSql && enablePostgreSqlKeyVaultSecret ? postgreSqlFabricUserSecretName : '' +output postgreSqlMirrorConnectionModeOut string = deployPostgreSql ? postgreSqlMirrorConnectionMode : '' +output postgreSqlMirrorConnectionUserNameOut string = deployPostgreSql ? (postgreSqlMirrorConnectionMode == 'admin' ? postgreSqlAdminLogin : postgreSqlFabricUserName) : '' +output postgreSqlMirrorConnectionSecretNameOut string = deployPostgreSql && enablePostgreSqlKeyVaultSecret ? (postgreSqlMirrorConnectionMode == 'admin' ? postgreSqlAdminSecretName : postgreSqlFabricUserSecretName) : '' var effectiveFabricWorkspaceName = effectiveFabricWorkspaceMode == 'byo' ? (!empty(fabricWorkspaceName) ? fabricWorkspaceName : (!empty(environmentName) ? 'workspace-${environmentName}' : 'workspace-${baseName}')) diff --git a/scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1 b/scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1 index fc42a30..2657df3 100644 --- a/scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1 +++ b/scripts/automationScripts/FabricPurviewAutomation/create_purview_collection.ps1 @@ -53,6 +53,56 @@ function Get-AzdEnvValue([string]$key){ return $value.Trim() } +function Get-LatestDeploymentOutputs([string]$resourceGroup, [string]$subscriptionId, [string]$environmentName) { + if ([string]::IsNullOrWhiteSpace($resourceGroup)) { return $null } + + try { + $listArgs = @('deployment', 'group', 'list', '--resource-group', $resourceGroup, '-o', 'json') + if ($subscriptionId) { $listArgs += @('--subscription', $subscriptionId) } + $deploymentsJson = & az @listArgs 2>$null + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($deploymentsJson)) { return $null } + + $deployments = @($deploymentsJson | ConvertFrom-Json -ErrorAction Stop) + if (-not $deployments) { return $null } + + $preferred = $null + if (-not [string]::IsNullOrWhiteSpace($environmentName)) { + $preferred = $deployments | + Where-Object { $_.name -like "$environmentName-*" } | + Sort-Object { $_.properties.timestamp } -Descending | + Select-Object -First 1 + } + if (-not $preferred) { + $preferred = $deployments | + Where-Object { $_.name -notlike 'PolicyDeployment_*' } | + Sort-Object { $_.properties.timestamp } -Descending | + Select-Object -First 1 + } + if (-not $preferred) { return $null } + + $showArgs = @('deployment', 'group', 'show', '--resource-group', $resourceGroup, '--name', $preferred.name, '--query', 'properties.outputs', '-o', 'json') + if ($subscriptionId) { $showArgs += @('--subscription', $subscriptionId) } + $outputsJson = & az @showArgs 2>$null + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($outputsJson)) { return $null } + + return $outputsJson | ConvertFrom-Json -ErrorAction Stop + } catch { + return $null + } +} + +function Get-OutputValue([object]$outputsObject, [string]$propertyName) { + if (-not $outputsObject) { return $null } + + $property = $outputsObject.PSObject.Properties[$propertyName] + if (-not $property -or -not $property.Value) { return $null } + + $valueProperty = $property.Value.PSObject.Properties['value'] + if ($valueProperty) { return $valueProperty.Value } + + return $null +} + function Resolve-PurviewFromResourceId([string]$resourceId) { if ([string]::IsNullOrWhiteSpace($resourceId)) { return $null } $parts = $resourceId.Split('/', [System.StringSplitOptions]::RemoveEmptyEntries) @@ -64,18 +114,52 @@ function Resolve-PurviewFromResourceId([string]$resourceId) { } } +function Get-DefaultPurviewCollectionName() { + $environmentName = $env:AZURE_ENV_NAME + if (-not $environmentName) { $environmentName = Get-AzdEnvValue -key 'AZURE_ENV_NAME' } + if ([string]::IsNullOrWhiteSpace($environmentName)) { return $null } + + return "collection-$($environmentName.Trim())" +} + # Use azd env if available +$outputs = $null +if ($env:AZURE_OUTPUTS_JSON) { + try { $outputs = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop } catch { $outputs = $null } +} +if (-not $outputs) { + $deploymentResourceGroup = $env:AZURE_RESOURCE_GROUP + if (-not $deploymentResourceGroup) { $deploymentResourceGroup = Get-AzdEnvValue -key 'AZURE_RESOURCE_GROUP' } + $deploymentSubscriptionId = $env:AZURE_SUBSCRIPTION_ID + if (-not $deploymentSubscriptionId) { $deploymentSubscriptionId = Get-AzdEnvValue -key 'AZURE_SUBSCRIPTION_ID' } + $deploymentEnvironmentName = $env:AZURE_ENV_NAME + if (-not $deploymentEnvironmentName) { $deploymentEnvironmentName = Get-AzdEnvValue -key 'AZURE_ENV_NAME' } + $outputs = Get-LatestDeploymentOutputs -resourceGroup $deploymentResourceGroup -subscriptionId $deploymentSubscriptionId -environmentName $deploymentEnvironmentName +} + $purviewAccountName = $null $purviewSubscriptionId = $null $purviewResourceGroup = $null $collectionName = $null -$purviewAccountName = Get-AzdEnvValue -key 'purviewAccountName' -$purviewSubscriptionId = Get-AzdEnvValue -key 'purviewSubscriptionId' -$purviewResourceGroup = Get-AzdEnvValue -key 'purviewResourceGroup' +$purviewAccountResourceId = $null + +if ($outputs) { + $purviewAccountName = Get-OutputValue -outputsObject $outputs -propertyName 'purviewAccountName' + $purviewSubscriptionId = Get-OutputValue -outputsObject $outputs -propertyName 'purviewSubscriptionId' + $purviewResourceGroup = Get-OutputValue -outputsObject $outputs -propertyName 'purviewResourceGroup' + $collectionName = Get-OutputValue -outputsObject $outputs -propertyName 'purviewCollectionName' + if (-not $collectionName) { $collectionName = Get-OutputValue -outputsObject $outputs -propertyName 'desiredFabricDomainName' } + $purviewAccountResourceId = Get-OutputValue -outputsObject $outputs -propertyName 'purviewAccountResourceId' +} + +if (-not $purviewAccountName) { $purviewAccountName = Get-AzdEnvValue -key 'purviewAccountName' } +if (-not $purviewSubscriptionId) { $purviewSubscriptionId = Get-AzdEnvValue -key 'purviewSubscriptionId' } +if (-not $purviewResourceGroup) { $purviewResourceGroup = Get-AzdEnvValue -key 'purviewResourceGroup' } # First try purviewCollectionName, then fall back to desiredFabricDomainName for backwards compatibility -$collectionName = Get-AzdEnvValue -key 'purviewCollectionName' +if (-not $collectionName) { $collectionName = Get-AzdEnvValue -key 'purviewCollectionName' } if (-not $collectionName) { $collectionName = Get-AzdEnvValue -key 'desiredFabricDomainName' } -$purviewAccountResourceId = Get-AzdEnvValue -key 'purviewAccountResourceId' +if (-not $collectionName) { $collectionName = Get-DefaultPurviewCollectionName } +if (-not $purviewAccountResourceId) { $purviewAccountResourceId = Get-AzdEnvValue -key 'purviewAccountResourceId' } if (-not $purviewAccountResourceId) { $purviewAccountResourceId = $env:PURVIEW_ACCOUNT_RESOURCE_ID } diff --git a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/assign_workspace_to_domain.ps1 b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/assign_workspace_to_domain.ps1 index e90d2fb..e608162 100644 --- a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/assign_workspace_to_domain.ps1 +++ b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/assign_workspace_to_domain.ps1 @@ -19,14 +19,106 @@ function Log([string]$m){ Write-Host "[assign-domain] $m" } function Warn([string]$m){ Write-Warning "[assign-domain] $m" } function Fail([string]$m){ Write-Error "[assign-domain] $m"; Clear-SensitiveVariables -VariableNames @('accessToken', 'fabricToken'); exit 1 } +function Get-NormalizedString { + param( + [Parameter(ValueFromPipeline = $true)] + $Value + ) + + if ($null -eq $Value) { return $null } + + if ($Value -is [string]) { + $trimmed = $Value.Trim() + if ([string]::IsNullOrWhiteSpace($trimmed)) { return $null } + if ($trimmed -in @('System.Object[]', 'System.Object')) { return $null } + return $trimmed + } + + if ($Value -is [System.Collections.IEnumerable] -and -not ($Value -is [string])) { + foreach ($item in $Value) { + $candidate = Get-NormalizedString -Value $item + if ($candidate) { return $candidate } + } + return $null + } + + if ($Value.PSObject) { + foreach ($propertyName in @('value', 'id', 'resourceId', 'name', 'displayName')) { + if ($Value.PSObject.Properties[$propertyName]) { + $candidate = Get-NormalizedString -Value $Value.$propertyName + if ($candidate) { return $candidate } + } + } + } + + $stringValue = $Value.ToString().Trim() + if ([string]::IsNullOrWhiteSpace($stringValue)) { return $null } + if ($stringValue -in @('System.Object[]', 'System.Object')) { return $null } + return $stringValue +} + +function Get-CapacityLookupName { + param( + [string]$ResolvedCapacityId, + [string]$ResolvedCapacityName + ) + + if ($ResolvedCapacityId) { + if ($ResolvedCapacityId -match '^[0-9a-fA-F-]{36}$') { return $ResolvedCapacityId } + if ($ResolvedCapacityId -like '*/providers/Microsoft.Fabric/capacities/*') { + return ($ResolvedCapacityId -split '/')[ -1 ] + } + return $ResolvedCapacityId + } + + return $ResolvedCapacityName +} + +function Get-AzdEnvValue { + param( + [Parameter(Mandatory = $true)] + [string]$Key + ) + + try { + $value = & azd env get-value $Key 2>$null + if ($LASTEXITCODE -ne 0) { return $null } + return Get-NormalizedString -Value $value + } catch { + return $null + } +} + +function Get-EnvironmentName { + if ($env:AZURE_ENV_NAME) { return $env:AZURE_ENV_NAME.Trim() } + return Get-AzdEnvValue -Key 'AZURE_ENV_NAME' +} + +function Resolve-DeployedFabricCapacity { + param( + [string]$SubscriptionId, + [string]$ResourceGroup + ) + + if (-not $ResourceGroup) { return $null } + + try { + $args = @('resource', 'list', '--resource-group', $ResourceGroup, '--resource-type', 'Microsoft.Fabric/capacities', '--query', '[0].{id:id,name:name}', '-o', 'json') + if ($SubscriptionId) { $args += @('--subscription', $SubscriptionId) } + $json = & az @args 2>$null + if ($LASTEXITCODE -ne 0 -or -not $json) { return $null } + return $json | ConvertFrom-Json -ErrorAction Stop + } catch { + return $null + } +} + # Skip when Fabric workspace automation is disabled or BYO $fabricWorkspaceMode = $env:fabricWorkspaceMode if (-not $fabricWorkspaceMode) { $fabricWorkspaceMode = $env:fabricWorkspaceModeOut } if (-not $fabricWorkspaceMode) { - try { - $azdMode = & azd env get-value fabricWorkspaceModeOut 2>$null - if ($azdMode) { $fabricWorkspaceMode = $azdMode.ToString().Trim() } - } catch {} + $azdMode = Get-AzdEnvValue -Key 'fabricWorkspaceModeOut' + if ($azdMode) { $fabricWorkspaceMode = $azdMode } } if (-not $fabricWorkspaceMode -and $env:AZURE_OUTPUTS_JSON) { try { @@ -68,13 +160,18 @@ $FABRIC_CAPACITY_NAME = $env:FABRIC_CAPACITY_NAME if ($env:AZURE_OUTPUTS_JSON) { try { $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop - if (-not $FABRIC_CAPACITY_ID -and $out.fabricCapacityId -and $out.fabricCapacityId.value) { $FABRIC_CAPACITY_ID = $out.fabricCapacityId.value } + if (-not $FABRIC_CAPACITY_ID -and $out.fabricCapacityId -and $out.fabricCapacityId.value) { $FABRIC_CAPACITY_ID = Get-NormalizedString -Value $out.fabricCapacityId.value } if (-not $FABRIC_WORKSPACE_NAME -and $out.desiredFabricWorkspaceName -and $out.desiredFabricWorkspaceName.value) { $FABRIC_WORKSPACE_NAME = $out.desiredFabricWorkspaceName.value } if (-not $FABRIC_DOMAIN_NAME -and $out.desiredFabricDomainName -and $out.desiredFabricDomainName.value) { $FABRIC_DOMAIN_NAME = $out.desiredFabricDomainName.value } - if (-not $FABRIC_CAPACITY_NAME -and $out.fabricCapacityName -and $out.fabricCapacityName.value) { $FABRIC_CAPACITY_NAME = $out.fabricCapacityName.value } + if (-not $FABRIC_CAPACITY_NAME -and $out.fabricCapacityName -and $out.fabricCapacityName.value) { $FABRIC_CAPACITY_NAME = Get-NormalizedString -Value $out.fabricCapacityName.value } } catch { } } +if (-not $FABRIC_WORKSPACE_NAME) { $FABRIC_WORKSPACE_NAME = Get-AzdEnvValue -Key 'desiredFabricWorkspaceName' } +if (-not $FABRIC_DOMAIN_NAME) { $FABRIC_DOMAIN_NAME = Get-AzdEnvValue -Key 'desiredFabricDomainName' } +if (-not $FABRIC_CAPACITY_ID) { $FABRIC_CAPACITY_ID = Get-AzdEnvValue -Key 'fabricCapacityId' } +if (-not $FABRIC_CAPACITY_NAME) { $FABRIC_CAPACITY_NAME = Get-AzdEnvValue -Key 'fabricCapacityName' } + # Try .azure env file if ((-not $FABRIC_WORKSPACE_NAME) -or (-not $FABRIC_DOMAIN_NAME) -or (-not $FABRIC_CAPACITY_ID)) { $envDir = $env:AZURE_ENV_NAME @@ -83,15 +180,34 @@ if ((-not $FABRIC_WORKSPACE_NAME) -or (-not $FABRIC_DOMAIN_NAME) -or (-not $FABR $envPath = Join-Path -Path '.azure' -ChildPath "$envDir/.env" if (Test-Path $envPath) { Get-Content $envPath | ForEach-Object { - if ($_ -match '^fabricCapacityId=(?:"|")?(.+?)(?:"|")?$') { if (-not $FABRIC_CAPACITY_ID) { $FABRIC_CAPACITY_ID = $Matches[1] } } + if ($_ -match '^fabricCapacityId=(?:"|")?(.+?)(?:"|")?$') { if (-not $FABRIC_CAPACITY_ID) { $FABRIC_CAPACITY_ID = Get-NormalizedString -Value $Matches[1] } } if ($_ -match '^desiredFabricWorkspaceName=(?:"|")?(.+?)(?:"|")?$') { if (-not $FABRIC_WORKSPACE_NAME) { $FABRIC_WORKSPACE_NAME = $Matches[1] } } if ($_ -match '^desiredFabricDomainName=(?:"|")?(.+?)(?:"|")?$') { if (-not $FABRIC_DOMAIN_NAME) { $FABRIC_DOMAIN_NAME = $Matches[1] } } - if ($_ -match '^fabricCapacityName=(?:"|")?(.+?)(?:"|")?$') { if (-not $FABRIC_CAPACITY_NAME) { $FABRIC_CAPACITY_NAME = $Matches[1] } } + if ($_ -match '^fabricCapacityName=(?:"|")?(.+?)(?:"|")?$') { if (-not $FABRIC_CAPACITY_NAME) { $FABRIC_CAPACITY_NAME = Get-NormalizedString -Value $Matches[1] } } } } } } +$FABRIC_CAPACITY_ID = Get-NormalizedString -Value $FABRIC_CAPACITY_ID +$FABRIC_CAPACITY_NAME = Get-NormalizedString -Value $FABRIC_CAPACITY_NAME + +$environmentName = Get-EnvironmentName +if (-not $FABRIC_WORKSPACE_NAME -and $environmentName) { $FABRIC_WORKSPACE_NAME = "workspace-$environmentName" } +if (-not $FABRIC_DOMAIN_NAME -and $environmentName) { $FABRIC_DOMAIN_NAME = "domain-$environmentName" } + +if (-not $FABRIC_CAPACITY_ID -or -not $FABRIC_CAPACITY_NAME) { + $subscriptionId = $env:AZURE_SUBSCRIPTION_ID + if (-not $subscriptionId) { $subscriptionId = Get-AzdEnvValue -Key 'AZURE_SUBSCRIPTION_ID' } + $resourceGroup = $env:AZURE_RESOURCE_GROUP + if (-not $resourceGroup) { $resourceGroup = Get-AzdEnvValue -Key 'AZURE_RESOURCE_GROUP' } + $resolvedCapacity = Resolve-DeployedFabricCapacity -SubscriptionId $subscriptionId -ResourceGroup $resourceGroup + if ($resolvedCapacity) { + if (-not $FABRIC_CAPACITY_ID -and $resolvedCapacity.id) { $FABRIC_CAPACITY_ID = Get-NormalizedString -Value $resolvedCapacity.id } + if (-not $FABRIC_CAPACITY_NAME -and $resolvedCapacity.name) { $FABRIC_CAPACITY_NAME = Get-NormalizedString -Value $resolvedCapacity.name } + } +} + if (-not $FABRIC_WORKSPACE_NAME) { Fail 'FABRIC_WORKSPACE_NAME unresolved (no outputs/env/bicep).' } if (-not $FABRIC_DOMAIN_NAME) { Fail 'FABRIC_DOMAIN_NAME unresolved (no outputs/env/bicep).' } if (-not $FABRIC_CAPACITY_ID -and -not $FABRIC_CAPACITY_NAME) { Fail 'FABRIC_CAPACITY_ID or FABRIC_CAPACITY_NAME unresolved (no outputs/env/bicep).' } @@ -130,7 +246,7 @@ if (-not $domainId) { Fail "Domain '$FABRIC_DOMAIN_NAME' not found. Create it fi # 2. Resolve capacity GUID - Direct approach with immediate success when APIs work $capacityGuid = $null -$capName = if ($FABRIC_CAPACITY_ID) { ($FABRIC_CAPACITY_ID -split '/')[-1] } else { $FABRIC_CAPACITY_NAME } +$capName = Get-CapacityLookupName -ResolvedCapacityId $FABRIC_CAPACITY_ID -ResolvedCapacityName $FABRIC_CAPACITY_NAME Log "Deriving Fabric capacity GUID for name: $capName" # Try Fabric API first - this should work immediately for deployed capacities @@ -138,12 +254,21 @@ try { Log "Calling Fabric API: $apiFabricRoot/capacities" $caps = Invoke-SecureRestMethod -Uri "$apiFabricRoot/capacities" -Headers $fabricHeaders -Method Get -ErrorAction Stop if ($caps.value) { - $match = $caps.value | Where-Object { $_.displayName -eq $capName } | Select-Object -First 1 + $match = $caps.value | Where-Object { + $displayName = if ($_.PSObject.Properties['displayName']) { $_.displayName } else { '' } + $name = if ($_.PSObject.Properties['name']) { $_.name } else { '' } + $id = if ($_.PSObject.Properties['id']) { $_.id } else { '' } + $displayName -eq $capName -or $name -eq $capName -or $id -eq $capName + } | Select-Object -First 1 if ($match) { $capacityGuid = $match.id Log "SUCCESS: Found capacity via Fabric API: $capacityGuid" } else { - $available = ($caps.value | ForEach-Object { $_.displayName }) -join ', ' + $available = ($caps.value | ForEach-Object { + if ($_.PSObject.Properties['displayName']) { $_.displayName } + elseif ($_.PSObject.Properties['name']) { $_.name } + elseif ($_.PSObject.Properties['id']) { $_.id } + }) -join ', ' Log "Capacity '$capName' not found. Available: $available" } } diff --git a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/create_fabric_domain.ps1 b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/create_fabric_domain.ps1 index c120196..c97e7ff 100644 --- a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/create_fabric_domain.ps1 +++ b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/create_fabric_domain.ps1 @@ -17,14 +17,33 @@ function Log([string]$m){ Write-Host "[fabric-domain] $m" } function Warn([string]$m){ Write-Warning "[fabric-domain] $m" } function Fail([string]$m){ Write-Error "[fabric-domain] $m"; Clear-SensitiveVariables -VariableNames @('accessToken', 'fabricToken'); exit 1 } +function Get-AzdEnvValue { + param( + [Parameter(Mandatory = $true)] + [string]$Key + ) + + try { + $value = & azd env get-value $Key 2>$null + if ($LASTEXITCODE -ne 0) { return $null } + if (-not $value) { return $null } + return $value.ToString().Trim() + } catch { + return $null + } +} + +function Get-EnvironmentName { + if ($env:AZURE_ENV_NAME) { return $env:AZURE_ENV_NAME.Trim() } + return Get-AzdEnvValue -Key 'AZURE_ENV_NAME' +} + # Skip when Fabric workspace automation is disabled or BYO $fabricWorkspaceMode = $env:fabricWorkspaceMode if (-not $fabricWorkspaceMode) { $fabricWorkspaceMode = $env:fabricWorkspaceModeOut } if (-not $fabricWorkspaceMode) { - try { - $azdMode = & azd env get-value fabricWorkspaceModeOut 2>$null - if ($azdMode) { $fabricWorkspaceMode = $azdMode.ToString().Trim() } - } catch {} + $azdMode = Get-AzdEnvValue -Key 'fabricWorkspaceModeOut' + if ($azdMode) { $fabricWorkspaceMode = $azdMode } } if (-not $fabricWorkspaceMode -and $env:AZURE_OUTPUTS_JSON) { try { @@ -44,12 +63,19 @@ $domainName = $env:desiredFabricDomainName $workspaceName = $env:desiredFabricWorkspaceName if (-not $domainName -and $env:AZURE_OUTPUTS_JSON) { try { $domainName = ($env:AZURE_OUTPUTS_JSON | ConvertFrom-Json).desiredFabricDomainName.value } catch {} } if (-not $workspaceName -and $env:AZURE_OUTPUTS_JSON) { try { $workspaceName = ($env:AZURE_OUTPUTS_JSON | ConvertFrom-Json).desiredFabricWorkspaceName.value } catch {} } +if (-not $domainName) { $domainName = Get-AzdEnvValue -Key 'desiredFabricDomainName' } +if (-not $workspaceName) { $workspaceName = Get-AzdEnvValue -Key 'desiredFabricWorkspaceName' } + +$environmentName = Get-EnvironmentName +if (-not $domainName -and $environmentName) { $domainName = "domain-$environmentName" } +if (-not $workspaceName -and $environmentName) { $workspaceName = "workspace-$environmentName" } # Fallback: try reading from parameter file if (-not $domainName -and (Test-Path 'infra/main.bicepparam')) { try { $bicepparam = Get-Content 'infra/main.bicepparam' -Raw - $m = [regex]::Match($bicepparam, "param\s+domainName\s*=\s*'(?[^']+)'") + $m = [regex]::Match($bicepparam, "param\s+desiredFabricDomainName\s*=\s*'(?[^']+)'") + if (-not $m.Success) { $m = [regex]::Match($bicepparam, "param\s+domainName\s*=\s*'(?[^']+)'") } if ($m.Success) { $domainName = $m.Groups['val'].Value } } catch {} } diff --git a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/create_fabric_workspace.ps1 b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/create_fabric_workspace.ps1 index e8630bc..338d573 100644 --- a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/create_fabric_workspace.ps1 +++ b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/create_fabric_workspace.ps1 @@ -21,14 +21,106 @@ function Log([string]$m){ Write-Host "[fabric-workspace] $m" } function Warn([string]$m){ Write-Warning "[fabric-workspace] $m" } function Fail([string]$m){ Write-Error "[fabric-workspace] $m"; Clear-SensitiveVariables -VariableNames @('accessToken'); exit 1 } +function Get-NormalizedString { + param( + [Parameter(ValueFromPipeline = $true)] + $Value + ) + + if ($null -eq $Value) { return $null } + + if ($Value -is [string]) { + $trimmed = $Value.Trim() + if ([string]::IsNullOrWhiteSpace($trimmed)) { return $null } + if ($trimmed -in @('System.Object[]', 'System.Object')) { return $null } + return $trimmed + } + + if ($Value -is [System.Collections.IEnumerable] -and -not ($Value -is [string])) { + foreach ($item in $Value) { + $candidate = Get-NormalizedString -Value $item + if ($candidate) { return $candidate } + } + return $null + } + + if ($Value.PSObject) { + foreach ($propertyName in @('value', 'id', 'resourceId', 'name', 'displayName')) { + if ($Value.PSObject.Properties[$propertyName]) { + $candidate = Get-NormalizedString -Value $Value.$propertyName + if ($candidate) { return $candidate } + } + } + } + + $stringValue = $Value.ToString().Trim() + if ([string]::IsNullOrWhiteSpace($stringValue)) { return $null } + if ($stringValue -in @('System.Object[]', 'System.Object')) { return $null } + return $stringValue +} + +function Get-CapacityLookupName { + param( + [string]$ResolvedCapacityId, + [string]$ResolvedCapacityName + ) + + if ($ResolvedCapacityId) { + if ($ResolvedCapacityId -match '^[0-9a-fA-F-]{36}$') { return $ResolvedCapacityId } + if ($ResolvedCapacityId -like '*/providers/Microsoft.Fabric/capacities/*') { + return ($ResolvedCapacityId -split '/')[ -1 ] + } + return $ResolvedCapacityId + } + + return $ResolvedCapacityName +} + +function Get-AzdEnvValue { + param( + [Parameter(Mandatory = $true)] + [string]$Key + ) + + try { + $value = & azd env get-value $Key 2>$null + if ($LASTEXITCODE -ne 0) { return $null } + return Get-NormalizedString -Value $value + } catch { + return $null + } +} + +function Get-EnvironmentName { + if ($env:AZURE_ENV_NAME) { return $env:AZURE_ENV_NAME.Trim() } + return Get-AzdEnvValue -Key 'AZURE_ENV_NAME' +} + +function Resolve-DeployedFabricCapacity { + param( + [string]$SubscriptionId, + [string]$ResourceGroup + ) + + if (-not $ResourceGroup) { return $null } + + try { + $args = @('resource', 'list', '--resource-group', $ResourceGroup, '--resource-type', 'Microsoft.Fabric/capacities', '--query', '[0].{id:id,name:name}', '-o', 'json') + if ($SubscriptionId) { $args += @('--subscription', $SubscriptionId) } + $json = & az @args 2>$null + if ($LASTEXITCODE -ne 0 -or -not $json) { return $null } + return $json | ConvertFrom-Json -ErrorAction Stop + } catch { + return $null + } +} + # Skip or BYO handling based on deployment outputs $fabricWorkspaceMode = $env:fabricWorkspaceMode if (-not $fabricWorkspaceMode) { $fabricWorkspaceMode = $env:fabricWorkspaceModeOut } if (-not $fabricWorkspaceMode) { - try { - $azdMode = & azd env get-value fabricWorkspaceModeOut 2>$null - if ($azdMode) { $fabricWorkspaceMode = $azdMode.ToString().Trim() } - } catch {} + $azdMode = Get-AzdEnvValue -Key 'fabricWorkspaceModeOut' + if ($azdMode) { $fabricWorkspaceMode = $azdMode } } if (-not $fabricWorkspaceMode -and $env:AZURE_OUTPUTS_JSON) { try { @@ -81,21 +173,28 @@ if (-not $WorkspaceName -and $env:desiredFabricWorkspaceName) { $WorkspaceName = if (-not $WorkspaceName -and $env:fabricWorkspaceNameOut) { $WorkspaceName = $env:fabricWorkspaceNameOut } if (-not $CapacityId -and $env:fabricCapacityId) { $CapacityId = $env:fabricCapacityId } if (-not $CapacityId -and $env:fabricCapacityResourceIdOut) { $CapacityId = $env:fabricCapacityResourceIdOut } +$CapacityName = $null +if ($env:FABRIC_CAPACITY_NAME) { $CapacityName = $env:FABRIC_CAPACITY_NAME } +if (-not $CapacityName -and $env:fabricCapacityName) { $CapacityName = $env:fabricCapacityName } # Fallback: try azd env get-value (common in azd hook execution where AZURE_OUTPUTS_JSON is not present) if (-not $WorkspaceName) { - try { - $azdWorkspaceName = & azd env get-value desiredFabricWorkspaceName 2>$null - if (-not $azdWorkspaceName) { $azdWorkspaceName = & azd env get-value fabricWorkspaceNameOut 2>$null } - if ($azdWorkspaceName) { $WorkspaceName = $azdWorkspaceName.ToString().Trim() } - } catch {} + $azdWorkspaceName = Get-AzdEnvValue -Key 'desiredFabricWorkspaceName' + if (-not $azdWorkspaceName) { $azdWorkspaceName = Get-AzdEnvValue -Key 'fabricWorkspaceNameOut' } + if ($azdWorkspaceName) { $WorkspaceName = $azdWorkspaceName } } if (-not $CapacityId) { - try { - $azdCapacityId = & azd env get-value fabricCapacityResourceIdOut 2>$null - if (-not $azdCapacityId) { $azdCapacityId = & azd env get-value fabricCapacityId 2>$null } - if ($azdCapacityId) { $CapacityId = $azdCapacityId.ToString().Trim() } - } catch {} + $azdCapacityId = Get-AzdEnvValue -Key 'fabricCapacityResourceIdOut' + if (-not $azdCapacityId) { $azdCapacityId = Get-AzdEnvValue -Key 'fabricCapacityId' } + $CapacityId = Get-NormalizedString -Value $azdCapacityId +} +if (-not $CapacityName) { + $CapacityName = Get-AzdEnvValue -Key 'fabricCapacityName' +} + +if (-not $WorkspaceName) { + $environmentName = Get-EnvironmentName + if ($environmentName) { $WorkspaceName = "workspace-$environmentName" } } # Resolve from AZURE_OUTPUTS_JSON if present @@ -103,7 +202,10 @@ if (-not $WorkspaceName -and $env:AZURE_OUTPUTS_JSON) { try { $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json; $WorkspaceName = $out.desiredFabricWorkspaceName.value } catch {} } if (-not $CapacityId -and $env:AZURE_OUTPUTS_JSON) { - try { $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json; $CapacityId = $out.fabricCapacityId.value } catch {} + try { $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json; $CapacityId = Get-NormalizedString -Value $out.fabricCapacityId.value } catch {} +} +if (-not $CapacityName -and $env:AZURE_OUTPUTS_JSON) { + try { $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json; $CapacityName = Get-NormalizedString -Value $out.fabricCapacityName.value } catch {} } # Fallbacks: try .azure//.env and infra/main.bicep before failing @@ -119,7 +221,8 @@ if (-not $WorkspaceName) { if (Test-Path $envFile) { Get-Content $envFile | ForEach-Object { if ($_ -match '^FABRIC_WORKSPACE_NAME=(.+)$') { $WorkspaceName = $Matches[1].Trim("'", '"') } - if ($_ -match '^fabricCapacityId=(.+)$') { $CapacityId = $Matches[1].Trim("'", '"') } + if ($_ -match '^fabricCapacityId=(.+)$') { $CapacityId = Get-NormalizedString -Value $Matches[1].Trim("'", '"') } + if ($_ -match '^fabricCapacityName=(.+)$') { $CapacityName = Get-NormalizedString -Value $Matches[1].Trim("'", '"') } } } } @@ -149,10 +252,26 @@ if (-not $WorkspaceName -and (Test-Path 'infra/main-orchestrator.bicep')) { if (-not $WorkspaceName) { Fail 'FABRIC_WORKSPACE_NAME unresolved (no outputs/env/bicep).' } +$WorkspaceName = Get-NormalizedString -Value $WorkspaceName +$CapacityId = Get-NormalizedString -Value $CapacityId +$CapacityName = Get-NormalizedString -Value $CapacityName + +if (-not $CapacityId -or -not $CapacityName) { + $subscriptionId = $env:AZURE_SUBSCRIPTION_ID + if (-not $subscriptionId) { $subscriptionId = Get-AzdEnvValue -Key 'AZURE_SUBSCRIPTION_ID' } + $resourceGroup = $env:AZURE_RESOURCE_GROUP + if (-not $resourceGroup) { $resourceGroup = Get-AzdEnvValue -Key 'AZURE_RESOURCE_GROUP' } + $resolvedCapacity = Resolve-DeployedFabricCapacity -SubscriptionId $subscriptionId -ResourceGroup $resourceGroup + if ($resolvedCapacity) { + if (-not $CapacityId -and $resolvedCapacity.id) { $CapacityId = Get-NormalizedString -Value $resolvedCapacity.id } + if (-not $CapacityName -and $resolvedCapacity.name) { $CapacityName = Get-NormalizedString -Value $resolvedCapacity.name } + } +} + # If we are in create mode, fail fast when Fabric capacity wasn't provided. # This avoids creating an orphaned workspace and then failing later when we try to assign a capacity. if ((-not $fabricWorkspaceMode) -or ($fabricWorkspaceMode.ToString().Trim().ToLowerInvariant() -eq 'create')) { - if (-not $CapacityId) { + if (-not $CapacityId -and -not $CapacityName) { Fail "FABRIC_CAPACITY_ID unresolved. Either set Fabric to 'none' (fabricWorkspaceModeOut=none) or provide/provision a capacity (fabricCapacityModeOut=create/byo and fabricCapacityResourceIdOut)." } } @@ -170,11 +289,57 @@ $apiRoot = 'https://api.fabric.microsoft.com/v1' # Create secure headers $apiHeaders = New-SecureHeaders -Token $accessToken +function Resolve-WorkspaceIdByName { + param( + [Parameter(Mandatory = $true)] + [string]$Name + ) + + $foundId = $null + $nameLower = $Name.ToLower() + + # Prefer workspaces list (when available) + try { + $workspaces = Invoke-SecureRestMethod -Uri "$apiRoot/workspaces?%24top=5000" -Headers $apiHeaders -Method Get -ErrorAction Stop + if ($workspaces.value) { + $match = $workspaces.value | Where-Object { + $displayName = if ($_.PSObject.Properties['displayName']) { $_.displayName } else { $null } + $wsName = if ($_.PSObject.Properties['name']) { $_.name } else { $null } + ($displayName -and $displayName.ToLower() -eq $nameLower) -or + ($wsName -and $wsName.ToLower() -eq $nameLower) + } + if ($match) { $foundId = $match.id } + } + } catch { + Warn "Workspace list (/workspaces) failed: $($_.Exception.Message)" + } + + if (-not $foundId) { + try { + $groups = Invoke-SecureRestMethod -Uri "$apiRoot/groups?%24top=5000" -Headers $apiHeaders -Method Get -ErrorAction Stop + $g = $groups.value | Where-Object { + $groupName = if ($_.PSObject.Properties['name']) { $_.name } else { $null } + $groupDisplayName = if ($_.PSObject.Properties['displayName']) { $_.displayName } else { $null } + ($groupName -and $groupName.ToLower() -eq $nameLower) -or + ($groupDisplayName -and $groupDisplayName.ToLower() -eq $nameLower) + } + if ($g) { $foundId = $g.id } + } catch { + Warn "Workspace list (/groups) failed: $($_.Exception.Message)" + } + } + + return $foundId +} + # Resolve capacity GUID if capacity ARM id given $capacityGuid = $null Log "CapacityId parameter: '$CapacityId'" -if ($CapacityId) { - $capName = ($CapacityId -split '/')[ -1 ] +if ($CapacityName) { + Log "CapacityName parameter: '$CapacityName'" +} +$capName = Get-CapacityLookupName -ResolvedCapacityId $CapacityId -ResolvedCapacityName $CapacityName +if ($capName) { Log "Deriving Fabric capacity GUID for name: $capName" try { @@ -186,19 +351,20 @@ if ($CapacityId) { foreach ($cap in $caps.value) { $capDisplayName = if ($cap.PSObject.Properties['displayName']) { $cap.displayName } else { '' } $capName2 = if ($cap.PSObject.Properties['name']) { $cap.name } else { '' } + $capId = if ($cap.PSObject.Properties['id']) { $cap.id } else { '' } - Log " Checking capacity: displayName='$capDisplayName' name='$capName2' id='$($cap.id)'" + Log " Checking capacity: displayName='$capDisplayName' name='$capName2' id='$capId'" # Direct string comparison - if ($capDisplayName -eq $capName -or $capName2 -eq $capName) { - $capacityGuid = $cap.id + if ($capDisplayName -eq $capName -or $capName2 -eq $capName -or $capId -eq $capName) { + $capacityGuid = $capId Log "EXACT MATCH FOUND: Using capacity '$capDisplayName' with GUID: $capacityGuid" break } # Case-insensitive fallback - if ($capDisplayName.ToLower() -eq $capName.ToLower() -or $capName2.ToLower() -eq $capName.ToLower()) { - $capacityGuid = $cap.id + if (([string]$capDisplayName).ToLowerInvariant() -eq $capName.ToLowerInvariant() -or ([string]$capName2).ToLowerInvariant() -eq $capName.ToLowerInvariant() -or ([string]$capId).ToLowerInvariant() -eq $capName.ToLowerInvariant()) { + $capacityGuid = $capId Log "CASE-INSENSITIVE MATCH FOUND: Using capacity '$capDisplayName' with GUID: $capacityGuid" break } @@ -207,7 +373,10 @@ if ($CapacityId) { if (-not $capacityGuid) { Log "NO MATCH FOUND. Available capacities:" foreach ($cap in $caps.value) { - Log " - displayName='$($cap.displayName)' name='$($cap.name)' id='$($cap.id)'" + $availableDisplayName = if ($cap.PSObject.Properties['displayName']) { $cap.displayName } else { '' } + $availableName = if ($cap.PSObject.Properties['name']) { $cap.name } else { '' } + $availableId = if ($cap.PSObject.Properties['id']) { $cap.id } else { '' } + Log " - displayName='$availableDisplayName' name='$availableName' id='$availableId'" } Fail "Could not find capacity named '$capName'" } @@ -227,11 +396,7 @@ if ($CapacityId) { # Check if workspace exists $workspaceId = $null -try { - $groups = Invoke-SecureRestMethod -Uri "$apiRoot/groups?%24top=5000" -Headers $apiHeaders -Method Get -ErrorAction Stop - $g = $groups.value | Where-Object { $_.name -eq $WorkspaceName } - if ($g) { $workspaceId = $g.id } -} catch { } +$workspaceId = Resolve-WorkspaceIdByName -Name $WorkspaceName if ($workspaceId) { Log "Workspace '$WorkspaceName' already exists (id=$workspaceId). Ensuring capacity assignment & admins." @@ -289,7 +454,7 @@ if ($workspaceId) { $hasAdmin = $false if ($currentRoleAssignments -and $currentRoleAssignments.value) { $hasAdmin = ($currentRoleAssignments.value | Where-Object { - (($_.principal.id -eq $admin) -or ($_.principal.userDetails.userPincipalName -eq $admin)) -and $_.role -eq 'Admin' + (($_.principal.id -eq $admin) -or ($_.principal.userDetails.userPrincipalName -eq $admin)) -and $_.role -eq 'Admin' }) } if (-not $hasAdmin) { @@ -314,7 +479,7 @@ if ($workspaceId) { } } - Invoke-SecureWebRequest -Uri "$apiRoot/workspaces/$workspaceId/roleAssignments" -Method Post -Headers ($apiHeaders) -Body (@{ principal = @{ id = $pincipalId; type = 'User' }; role = 'Admin' } | ConvertTo-Json) -ErrorAction Stop + Invoke-SecureWebRequest -Uri "$apiRoot/workspaces/$workspaceId/roleAssignments" -Method Post -Headers ($apiHeaders) -Body (@{ principal = @{ id = $principalId; type = 'User' }; role = 'Admin' } | ConvertTo-Json) -ErrorAction Stop } catch { Warn "Failed to add $($admin): $($_)" } } else { Log "Admin already present: $admin" } } @@ -338,7 +503,20 @@ try { $body = $resp.Content | ConvertFrom-Json -ErrorAction SilentlyContinue $workspaceId = $body.id Log "Created workspace id: $workspaceId" -} catch { Fail "Workspace creation failed: $_" } +} catch { + $errMsg = $_.Exception.Message + if ($errMsg -match '409' -or $errMsg -match 'Conflict') { + Warn "Workspace create returned 409 (Conflict). Attempting to resolve existing workspace by name." + $workspaceId = Resolve-WorkspaceIdByName -Name $WorkspaceName + if ($workspaceId) { Log "Using existing workspace id: $workspaceId" } + + if (-not $workspaceId) { + Fail "Workspace creation failed with 409, but existing workspace could not be resolved. $_" + } + } else { + Fail "Workspace creation failed: $_" + } +} # Assign to capacity if ($capacityGuid) { @@ -384,7 +562,7 @@ if ($AdminUPNs) { } } - Invoke-SecureWebRequest -Uri "$apiRoot/workspaces/$workspaceId/roleAssignments" -Method Post -Headers ($apiHeaders) -Body (@{ principal = @{ id = $pincipalId; type = 'User' }; role = 'Admin' } | ConvertTo-Json) -ErrorAction Stop + Invoke-SecureWebRequest -Uri "$apiRoot/workspaces/$workspaceId/roleAssignments" -Method Post -Headers ($apiHeaders) -Body (@{ principal = @{ id = $principalId; type = 'User' }; role = 'Admin' } | ConvertTo-Json) -ErrorAction Stop } catch { Warn "Failed to add $($admin): $($_)" } } } diff --git a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/ensure_active_capacity.ps1 b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/ensure_active_capacity.ps1 index 42b27e0..0fac27b 100644 --- a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/ensure_active_capacity.ps1 +++ b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/ensure_active_capacity.ps1 @@ -22,6 +22,41 @@ function Log([string]$m){ Write-Host "[fabric-capacity] $m" } function Warn([string]$m){ Write-Warning "[fabric-capacity] $m" } function Fail([string]$m){ Write-Error "[script] $m"; Clear-SensitiveVariables -VariableNames @("accessToken", "fabricToken", "purviewToken", "powerBIToken", "storageToken"); exit 1 } +function Get-AzdEnvValue { + param( + [Parameter(Mandatory = $true)] + [string]$Key + ) + + try { + $value = & azd env get-value $Key 2>$null + if ($LASTEXITCODE -ne 0) { return $null } + if (-not $value) { return $null } + return $value.ToString().Trim() + } catch { + return $null + } +} + +function Resolve-DeployedFabricCapacity { + param( + [string]$SubscriptionId, + [string]$ResourceGroup + ) + + if (-not $ResourceGroup) { return $null } + + try { + $args = @('resource', 'list', '--resource-group', $ResourceGroup, '--resource-type', 'Microsoft.Fabric/capacities', '--query', '[0].{id:id,name:name}', '-o', 'json') + if ($SubscriptionId) { $args += @('--subscription', $SubscriptionId) } + $json = & az @args 2>$null + if ($LASTEXITCODE -ne 0 -or -not $json) { return $null } + return $json | ConvertFrom-Json -ErrorAction Stop + } catch { + return $null + } +} + # Helper: parse AZURE_OUTPUTS_JSON if provided function Get-OutputValue($jsonString, $path) { if (-not $jsonString) { return $null } @@ -49,10 +84,8 @@ $fabricCapacityMode = $null if ($env:fabricCapacityMode) { $fabricCapacityMode = $env:fabricCapacityMode } if (-not $fabricCapacityMode) { $fabricCapacityMode = $env:fabricCapacityModeOut } if (-not $fabricCapacityMode) { - try { - $azdMode = & azd env get-value fabricCapacityModeOut 2>$null - if ($azdMode) { $fabricCapacityMode = $azdMode.ToString().Trim() } - } catch { } + $azdMode = Get-AzdEnvValue -Key 'fabricCapacityModeOut' + if ($azdMode) { $fabricCapacityMode = $azdMode } } if (-not $fabricCapacityMode -and $azureOutputsJson) { $val = Get-OutputValue -jsonString $azureOutputsJson -path 'fabricCapacityModeOut.value' @@ -122,6 +155,18 @@ if (-not $FABRIC_CAPACITY_ID -and $FABRIC_CAPACITY_NAME) { } } +if (-not $FABRIC_CAPACITY_ID -or -not $FABRIC_CAPACITY_NAME) { + $subscriptionId = $env:AZURE_SUBSCRIPTION_ID + if (-not $subscriptionId) { $subscriptionId = Get-AzdEnvValue -Key 'AZURE_SUBSCRIPTION_ID' } + $resourceGroup = $env:AZURE_RESOURCE_GROUP + if (-not $resourceGroup) { $resourceGroup = Get-AzdEnvValue -Key 'AZURE_RESOURCE_GROUP' } + $resolvedCapacity = Resolve-DeployedFabricCapacity -SubscriptionId $subscriptionId -ResourceGroup $resourceGroup + if ($resolvedCapacity) { + if (-not $FABRIC_CAPACITY_ID -and $resolvedCapacity.id) { $FABRIC_CAPACITY_ID = $resolvedCapacity.id } + if (-not $FABRIC_CAPACITY_NAME -and $resolvedCapacity.name) { $FABRIC_CAPACITY_NAME = $resolvedCapacity.name } + } +} + if (-not $FABRIC_CAPACITY_ID) { Warn "FABRIC_CAPACITY_ID unresolved; skipping capacity activation checks." Clear-SensitiveVariables -VariableNames @("accessToken", "fabricToken", "purviewToken", "powerBIToken", "storageToken") diff --git a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1 b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1 index 9156436..c54a7bb 100644 --- a/scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1 +++ b/scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric_datasource.ps1 @@ -57,7 +57,17 @@ function Test-FabricCapacityActive { try { $resJson = & az resource show --ids $capacityId -o json 2>$null | ConvertFrom-Json -ErrorAction Stop - $state = $resJson.properties.state + $state = $null + if ($resJson.PSObject.Properties['properties'] -and $resJson.properties -and $resJson.properties.PSObject.Properties['state']) { + $state = $resJson.properties.state + } + if (-not $state -and $resJson.PSObject.Properties['state']) { + $state = $resJson.state + } + if (-not $state -and $resJson.PSObject.Properties['provisioningState']) { + $state = $resJson.provisioningState + } + if (-not $state) { return $true } if ($state -eq 'Active') { return $true } Log "Fabric capacity state: $state" return $false @@ -76,12 +86,65 @@ if (-not (Test-FabricCapacityActive)) { function Get-AzdEnvValue([string]$key){ $value = $null - try { $value = & azd env get-value $key 2>$null } catch { $value = $null } + try { + $value = & azd env get-value $key 2>$null + if ($LASTEXITCODE -ne 0) { $value = $null } + } catch { $value = $null } if ([string]::IsNullOrWhiteSpace($value)) { return $null } if ($value -match '^\s*ERROR:') { return $null } return $value.Trim() } +function Get-LatestDeploymentOutputs([string]$resourceGroup, [string]$subscriptionId, [string]$environmentName) { + if ([string]::IsNullOrWhiteSpace($resourceGroup)) { return $null } + + try { + $listArgs = @('deployment', 'group', 'list', '--resource-group', $resourceGroup, '-o', 'json') + if ($subscriptionId) { $listArgs += @('--subscription', $subscriptionId) } + $deploymentsJson = & az @listArgs 2>$null + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($deploymentsJson)) { return $null } + + $deployments = @($deploymentsJson | ConvertFrom-Json -ErrorAction Stop) + if (-not $deployments) { return $null } + + $preferred = $null + if (-not [string]::IsNullOrWhiteSpace($environmentName)) { + $preferred = $deployments | + Where-Object { $_.name -like "$environmentName-*" } | + Sort-Object { $_.properties.timestamp } -Descending | + Select-Object -First 1 + } + if (-not $preferred) { + $preferred = $deployments | + Where-Object { $_.name -notlike 'PolicyDeployment_*' } | + Sort-Object { $_.properties.timestamp } -Descending | + Select-Object -First 1 + } + if (-not $preferred) { return $null } + + $showArgs = @('deployment', 'group', 'show', '--resource-group', $resourceGroup, '--name', $preferred.name, '--query', 'properties.outputs', '-o', 'json') + if ($subscriptionId) { $showArgs += @('--subscription', $subscriptionId) } + $outputsJson = & az @showArgs 2>$null + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($outputsJson)) { return $null } + + return $outputsJson | ConvertFrom-Json -ErrorAction Stop + } catch { + return $null + } +} + +function Get-OutputValue([object]$outputsObject, [string]$propertyName) { + if (-not $outputsObject) { return $null } + + $property = $outputsObject.PSObject.Properties[$propertyName] + if (-not $property -or -not $property.Value) { return $null } + + $valueProperty = $property.Value.PSObject.Properties['value'] + if ($valueProperty) { return $valueProperty.Value } + + return $null +} + function Resolve-PurviewFromResourceId([string]$resourceId) { if ([string]::IsNullOrWhiteSpace($resourceId)) { return $null } $parts = $resourceId.Split('/', [System.StringSplitOptions]::RemoveEmptyEntries) @@ -93,14 +156,52 @@ function Resolve-PurviewFromResourceId([string]$resourceId) { } } -# Resolve Purview account and collection name from azd (if present) -$purviewAccountName = Get-AzdEnvValue -key 'purviewAccountName' +function Get-DefaultPurviewCollectionName() { + $environmentName = $env:AZURE_ENV_NAME + if (-not $environmentName) { $environmentName = Get-AzdEnvValue -key 'AZURE_ENV_NAME' } + if ([string]::IsNullOrWhiteSpace($environmentName)) { return $null } + + return "collection-$($environmentName.Trim())" +} + +# Resolve Purview account and collection name from outputs/env/azd +$outputs = $null +if ($env:AZURE_OUTPUTS_JSON) { + try { $outputs = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop } catch { $outputs = $null } +} +if (-not $outputs) { + $deploymentResourceGroup = $env:AZURE_RESOURCE_GROUP + if (-not $deploymentResourceGroup) { $deploymentResourceGroup = Get-AzdEnvValue -key 'AZURE_RESOURCE_GROUP' } + $deploymentSubscriptionId = $env:AZURE_SUBSCRIPTION_ID + if (-not $deploymentSubscriptionId) { $deploymentSubscriptionId = Get-AzdEnvValue -key 'AZURE_SUBSCRIPTION_ID' } + $deploymentEnvironmentName = $env:AZURE_ENV_NAME + if (-not $deploymentEnvironmentName) { $deploymentEnvironmentName = Get-AzdEnvValue -key 'AZURE_ENV_NAME' } + $outputs = Get-LatestDeploymentOutputs -resourceGroup $deploymentResourceGroup -subscriptionId $deploymentSubscriptionId -environmentName $deploymentEnvironmentName +} + +$purviewAccountName = $null +$collectionName = $null +$purviewAccountResourceId = $null +$purviewSubscriptionId = $null +$purviewResourceGroup = $null + +if ($outputs) { + $purviewAccountName = Get-OutputValue -outputsObject $outputs -propertyName 'purviewAccountName' + $collectionName = Get-OutputValue -outputsObject $outputs -propertyName 'purviewCollectionName' + if (-not $collectionName) { $collectionName = Get-OutputValue -outputsObject $outputs -propertyName 'desiredFabricDomainName' } + $purviewAccountResourceId = Get-OutputValue -outputsObject $outputs -propertyName 'purviewAccountResourceId' + $purviewSubscriptionId = Get-OutputValue -outputsObject $outputs -propertyName 'purviewSubscriptionId' + $purviewResourceGroup = Get-OutputValue -outputsObject $outputs -propertyName 'purviewResourceGroup' +} + +if (-not $purviewAccountName) { $purviewAccountName = Get-AzdEnvValue -key 'purviewAccountName' } # First try purviewCollectionName, then fall back to desiredFabricDomainName for backwards compatibility -$collectionName = Get-AzdEnvValue -key 'purviewCollectionName' +if (-not $collectionName) { $collectionName = Get-AzdEnvValue -key 'purviewCollectionName' } if (-not $collectionName) { $collectionName = Get-AzdEnvValue -key 'desiredFabricDomainName' } -$purviewAccountResourceId = Get-AzdEnvValue -key 'purviewAccountResourceId' -$purviewSubscriptionId = Get-AzdEnvValue -key 'purviewSubscriptionId' -$purviewResourceGroup = Get-AzdEnvValue -key 'purviewResourceGroup' +if (-not $collectionName) { $collectionName = Get-DefaultPurviewCollectionName } +if (-not $purviewAccountResourceId) { $purviewAccountResourceId = Get-AzdEnvValue -key 'purviewAccountResourceId' } +if (-not $purviewSubscriptionId) { $purviewSubscriptionId = Get-AzdEnvValue -key 'purviewSubscriptionId' } +if (-not $purviewResourceGroup) { $purviewResourceGroup = Get-AzdEnvValue -key 'purviewResourceGroup' } if (-not $purviewAccountResourceId) { $purviewAccountResourceId = $env:PURVIEW_ACCOUNT_RESOURCE_ID } diff --git a/scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 b/scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 index 5830064..f455afe 100644 --- a/scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 +++ b/scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 @@ -8,7 +8,14 @@ param( [string]$MirrorName = $env:FABRIC_POSTGRES_MIRROR_NAME, [string]$DatabaseName = $env:POSTGRES_DATABASE_NAME, [string]$ConnectionId = $env:FABRIC_POSTGRES_CONNECTION_ID, - [string]$WorkspaceId = $env:FABRIC_WORKSPACE_ID + [string]$WorkspaceId = $env:FABRIC_WORKSPACE_ID, + [string]$ConnectionDisplayName = $env:FABRIC_POSTGRES_CONNECTION_NAME, + [string]$GatewayId = $env:FABRIC_POSTGRES_GATEWAY_ID, + [string]$MirrorConnectionMode = $env:POSTGRES_MIRROR_CONNECTION_MODE, + [string]$MirrorConnectionUserName = $env:POSTGRES_MIRROR_CONNECTION_USER_NAME, + [string]$MirrorConnectionSecretName = $env:POSTGRES_MIRROR_CONNECTION_SECRET_NAME, + [string]$MirrorConnectionPassword = $env:POSTGRES_MIRROR_CONNECTION_PASSWORD, + [string]$TempEnableKeyVaultPublicAccess = $env:POSTGRES_TEMP_ENABLE_KV_PUBLIC_ACCESS ) Set-StrictMode -Version Latest @@ -20,6 +27,318 @@ $SecurityModulePath = Join-Path $PSScriptRoot "../../SecurityModule.ps1" function Log([string]$m){ Write-Host "[fabric-pg-mirror] $m" } function Warn([string]$m){ Write-Warning "[fabric-pg-mirror] $m" } +function Fail([string]$m){ Write-Error "[fabric-pg-mirror] $m"; exit 1 } +function IsTrue([string]$v){ return ($v -and $v.ToString().Trim().ToLowerInvariant() -in @('1','true','yes')) } + +function Get-AzdEnvValue([string]$key) { + try { + $val = & azd env get-value $key 2>$null + if ($val -and -not ($val -match '^\s*ERROR:')) { return $val.ToString().Trim() } + } catch {} + + return $null +} + +function Get-LatestDeploymentOutputs([string]$resourceGroup, [string]$subscriptionId, [string]$environmentName) { + if ([string]::IsNullOrWhiteSpace($resourceGroup)) { return $null } + + try { + $listArgs = @('deployment', 'group', 'list', '--resource-group', $resourceGroup, '-o', 'json') + if ($subscriptionId) { $listArgs += @('--subscription', $subscriptionId) } + $deploymentsJson = & az @listArgs 2>$null + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($deploymentsJson)) { return $null } + + $deployments = @($deploymentsJson | ConvertFrom-Json -ErrorAction Stop) + if (-not $deployments) { return $null } + + $preferred = $null + if (-not [string]::IsNullOrWhiteSpace($environmentName)) { + $preferred = $deployments | + Where-Object { $_.name -like "$environmentName-*" } | + Sort-Object { $_.properties.timestamp } -Descending | + Select-Object -First 1 + } + if (-not $preferred) { + $preferred = $deployments | + Where-Object { $_.name -notlike 'PolicyDeployment_*' } | + Sort-Object { $_.properties.timestamp } -Descending | + Select-Object -First 1 + } + if (-not $preferred) { return $null } + + $showArgs = @('deployment', 'group', 'show', '--resource-group', $resourceGroup, '--name', $preferred.name, '--query', 'properties.outputs', '-o', 'json') + if ($subscriptionId) { $showArgs += @('--subscription', $subscriptionId) } + $outputsJson = & az @showArgs 2>$null + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($outputsJson)) { return $null } + + return $outputsJson | ConvertFrom-Json -ErrorAction Stop + } catch { + return $null + } +} + +function Set-AzdEnvValue([string]$key, [string]$value) { + if ([string]::IsNullOrWhiteSpace($value)) { return } + + try { + & azd env set-value $key $value 1>$null + } catch { + Warn "Failed to persist '$key' to azd env: $($_.Exception.Message)" + } +} + +function Get-ResourceNameFromId([string]$resourceId) { + if ([string]::IsNullOrWhiteSpace($resourceId)) { return $null } + + $segments = $resourceId.Split('/', [System.StringSplitOptions]::RemoveEmptyEntries) + if ($segments.Length -lt 2) { return $null } + + return $segments[$segments.Length - 1] +} + +function Get-ResourceGroupFromId([string]$resourceId) { + if ([string]::IsNullOrWhiteSpace($resourceId)) { return $null } + + $segments = $resourceId.Split('/', [System.StringSplitOptions]::RemoveEmptyEntries) + $rgIndex = [Array]::IndexOf($segments, 'resourceGroups') + if ($rgIndex -lt 0 -or $rgIndex + 1 -ge $segments.Length) { return $null } + + return $segments[$rgIndex + 1] +} + +function Invoke-AzCliCapture([string[]]$Args) { + $output = & az @Args + if ($LASTEXITCODE -ne 0) { + throw "Azure CLI command failed with exit code ${LASTEXITCODE}: az $($Args -join ' ')" + } + + return $output +} + +function Test-KeyVaultAccess([string]$vaultName) { + try { + $null = Invoke-AzCliCapture @('keyvault','secret','list','--vault-name', $vaultName,'--maxresults','1','--query','[0].id','-o','tsv') + return $true + } catch { + return $false + } +} + +function Set-KeyVaultPublicAccess([string]$vaultName, [string]$state) { + if ([string]::IsNullOrWhiteSpace($vaultName)) { return } + + & az keyvault update -n $vaultName --public-network-access $state 1>$null + if ($LASTEXITCODE -ne 0) { + throw "Failed to set Key Vault public network access to '$state' for '$vaultName'." + } +} + +function Get-PostgreSqlPublicAccess([string]$resourceGroup, [string]$serverName, [string]$subscriptionId) { + if ([string]::IsNullOrWhiteSpace($resourceGroup) -or [string]::IsNullOrWhiteSpace($serverName)) { return $null } + + try { + $args = @('postgres', 'flexible-server', 'show', '--resource-group', $resourceGroup, '--name', $serverName, '--query', 'network.publicNetworkAccess', '-o', 'tsv') + if ($subscriptionId) { $args += @('--subscription', $subscriptionId) } + + $value = & az @args 2>$null + if ($LASTEXITCODE -ne 0 -or -not $value) { return $null } + + return $value.ToString().Trim() + } catch { + return $null + } +} + +function Set-PostgreSqlPublicAccess([string]$resourceGroup, [string]$serverName, [string]$state, [string]$subscriptionId) { + if ([string]::IsNullOrWhiteSpace($resourceGroup) -or [string]::IsNullOrWhiteSpace($serverName) -or [string]::IsNullOrWhiteSpace($state)) { return } + + $args = @('postgres', 'flexible-server', 'update', '--resource-group', $resourceGroup, '--name', $serverName, '--public-access', $state) + if ($subscriptionId) { $args += @('--subscription', $subscriptionId) } + + & az @args 1>$null + if ($LASTEXITCODE -ne 0) { + throw "Failed to set PostgreSQL public access to '$state' for '$serverName'." + } +} + +function Add-PostgreSqlFirewallRule([string]$resourceGroup, [string]$serverName, [string]$ruleName, [string]$startIpAddress, [string]$endIpAddress, [string]$subscriptionId) { + if ([string]::IsNullOrWhiteSpace($resourceGroup) -or [string]::IsNullOrWhiteSpace($serverName) -or [string]::IsNullOrWhiteSpace($ruleName)) { return } + + $args = @('postgres', 'flexible-server', 'firewall-rule', 'create', '--resource-group', $resourceGroup, '--name', $serverName, '--rule-name', $ruleName, '--start-ip-address', $startIpAddress, '--end-ip-address', $endIpAddress) + if ($subscriptionId) { $args += @('--subscription', $subscriptionId) } + + & az @args 1>$null + if ($LASTEXITCODE -ne 0) { + throw "Failed to create PostgreSQL firewall rule '$ruleName' for '$serverName'." + } +} + +function Remove-PostgreSqlFirewallRule([string]$resourceGroup, [string]$serverName, [string]$ruleName, [string]$subscriptionId) { + if ([string]::IsNullOrWhiteSpace($resourceGroup) -or [string]::IsNullOrWhiteSpace($serverName) -or [string]::IsNullOrWhiteSpace($ruleName)) { return } + + $args = @('postgres', 'flexible-server', 'firewall-rule', 'delete', '--resource-group', $resourceGroup, '--name', $serverName, '--rule-name', $ruleName, '--yes') + if ($subscriptionId) { $args += @('--subscription', $subscriptionId) } + + & az @args 1>$null 2>$null +} + +function Invoke-FabricPagedGet([string]$InitialUri, [hashtable]$Headers, [string]$Description) { + $results = @() + $nextUri = $InitialUri + + while ($nextUri) { + $page = Invoke-SecureRestMethod -Uri $nextUri -Headers $Headers -Method Get -Description $Description + + if ($page -is [System.Array]) { + $results += @($page) + $nextUri = $null + continue + } + + $valueProperty = $page.PSObject.Properties['value'] + if ($valueProperty -and $valueProperty.Value) { + $results += @($page.value) + } elseif ($page) { + $results += @($page) + } + + $continuationProperty = $page.PSObject.Properties['continuationUri'] + if ($continuationProperty -and $continuationProperty.Value) { + $nextUri = $continuationProperty.Value + } else { + $nextUri = $null + } + } + + return $results +} + +function Get-ConnectionParameterValue([object]$parameterDefinition, [string]$ServerFqdn, [string]$TargetDatabase, [string]$UserName) { + $parameterName = $parameterDefinition.name.ToString().Trim().ToLowerInvariant() + $allowedValues = @($parameterDefinition.allowedValues) + + switch ($parameterName) { + 'server' { return $ServerFqdn } + 'host' { return $ServerFqdn } + 'database' { return $TargetDatabase } + 'databasename' { return $TargetDatabase } + 'port' { return 5432 } + 'username' { return $UserName } + 'user' { return $UserName } + default { + if ($allowedValues.Count -eq 1) { + return $allowedValues[0] + } + } + } + + return $null +} + +function New-ConnectionDetailsParameter([object]$parameterDefinition, $value) { + $parameter = @{ + dataType = $parameterDefinition.dataType + name = $parameterDefinition.name + } + + switch ($parameterDefinition.dataType) { + 'Number' { $parameter.value = [int]$value } + 'Boolean' { $parameter.value = [bool]$value } + default { $parameter.value = [string]$value } + } + + return $parameter +} + +function Select-PostgreSqlConnectionMetadata([object[]]$SupportedTypes) { + $candidates = @($SupportedTypes | Where-Object { + $creationMethods = @($_.creationMethods) + $_.type -match 'postgres' -or (@($creationMethods | Where-Object { $_.name -match 'postgres' })).Count -gt 0 + }) + + if (-not $candidates) { + throw 'Fabric did not report a supported PostgreSQL connection type.' + } + + $orderedCandidates = $candidates | Sort-Object @( + @{ Expression = { + if ($_.type -match '^Azure.*PostgreSQL$') { 0 } + elseif ($_.type -match '^PostgreSQL$') { 1 } + else { 2 } + } + }, + @{ Expression = { $_.type } } + ) + + foreach ($candidate in $orderedCandidates) { + $selectedMethod = @($candidate.creationMethods | Sort-Object @( + @{ Expression = { if ($_.name -match 'postgres') { 0 } else { 1 } } }, + @{ Expression = { $_.name } } + )) | Select-Object -First 1 + + if ($selectedMethod) { + return @{ + Type = $candidate.type + CreationMethod = $selectedMethod + Metadata = $candidate + } + } + } + + throw 'Fabric reported PostgreSQL connection metadata, but no creation method was available.' +} + +function New-FabricPostgreSqlConnectionBody( + [string]$DisplayName, + [string]$ConnectivityType, + [string]$ConnectionType, + [string]$CreationMethod, + [object[]]$Parameters, + [string]$PrivacyLevel, + [string]$ConnectionEncryption, + [string]$UserName, + [string]$Password, + [string]$GatewayId +) { + $body = @{ + connectivityType = $ConnectivityType + displayName = $DisplayName + connectionDetails = @{ + type = $ConnectionType + creationMethod = $CreationMethod + parameters = $Parameters + } + privacyLevel = $PrivacyLevel + credentialDetails = @{ + singleSignOnType = 'None' + connectionEncryption = $ConnectionEncryption + skipTestConnection = $false + credentials = @{ + credentialType = 'Basic' + username = [string]$UserName + password = [string]$Password + } + } + } + + if ($GatewayId) { + $body.gatewayId = $GatewayId + } + + return $body +} + +function Test-IsFabricIncorrectCredentialFailure([string]$ResponseBody) { + if ([string]::IsNullOrWhiteSpace($ResponseBody)) { return $false } + + return ($ResponseBody -match 'IncorrectCredentials' -or $ResponseBody -match 'AccessUnauthorized') +} + +function Test-IsFabricConnectivityTimeoutFailure([string]$ResponseBody) { + if ([string]::IsNullOrWhiteSpace($ResponseBody)) { return $false } + + return ($ResponseBody -match 'did not properly respond' -or $ResponseBody -match 'failed to respond' -or $ResponseBody -match 'No such host is known' -or $ResponseBody -match 'Gateway_MashupDataAccessError') +} # Skip when Fabric workspace is disabled $fabricWorkspaceMode = $env:fabricWorkspaceMode @@ -46,7 +365,12 @@ if ($fabricWorkspaceMode -and $fabricWorkspaceMode.ToString().Trim().ToLowerInva $postgreSqlServerResourceId = $null $postgreSqlServerName = $null $postgreSqlServerFqdn = $null +$serverDetails = $null $postgreSqlSystemAssignedPrincipalId = $null +$postgreSqlAdminLogin = $null +$postgreSqlFabricUserName = $null +$postgreSqlFabricUserSecretName = $null +$keyVaultResourceId = $null if ($env:AZURE_OUTPUTS_JSON) { try { @@ -55,32 +379,148 @@ if ($env:AZURE_OUTPUTS_JSON) { if ($out.postgreSqlServerNameOut -and $out.postgreSqlServerNameOut.value) { $postgreSqlServerName = $out.postgreSqlServerNameOut.value } if ($out.postgreSqlServerFqdn -and $out.postgreSqlServerFqdn.value) { $postgreSqlServerFqdn = $out.postgreSqlServerFqdn.value } if ($out.postgreSqlSystemAssignedPrincipalId -and $out.postgreSqlSystemAssignedPrincipalId.value) { $postgreSqlSystemAssignedPrincipalId = $out.postgreSqlSystemAssignedPrincipalId.value } + if ($out.postgreSqlAdminLoginOut -and $out.postgreSqlAdminLoginOut.value) { $postgreSqlAdminLogin = $out.postgreSqlAdminLoginOut.value } + if ($out.postgreSqlFabricUserNameOut -and $out.postgreSqlFabricUserNameOut.value) { $postgreSqlFabricUserName = $out.postgreSqlFabricUserNameOut.value } + if ($out.postgreSqlFabricUserSecretNameOut -and $out.postgreSqlFabricUserSecretNameOut.value) { $postgreSqlFabricUserSecretName = $out.postgreSqlFabricUserSecretNameOut.value } + if ($out.keyVaultResourceId -and $out.keyVaultResourceId.value) { $keyVaultResourceId = $out.keyVaultResourceId.value } + if ($out.postgreSqlMirrorConnectionModeOut -and $out.postgreSqlMirrorConnectionModeOut.value -and (-not $MirrorConnectionMode)) { $MirrorConnectionMode = $out.postgreSqlMirrorConnectionModeOut.value } + if ($out.postgreSqlMirrorConnectionUserNameOut -and $out.postgreSqlMirrorConnectionUserNameOut.value -and (-not $MirrorConnectionUserName)) { $MirrorConnectionUserName = $out.postgreSqlMirrorConnectionUserNameOut.value } + if ($out.postgreSqlMirrorConnectionSecretNameOut -and $out.postgreSqlMirrorConnectionSecretNameOut.value -and (-not $MirrorConnectionSecretName)) { $MirrorConnectionSecretName = $out.postgreSqlMirrorConnectionSecretNameOut.value } + if ($out.postgreSqlFabricUserNameOut -and $out.postgreSqlFabricUserNameOut.value -and (-not $MirrorConnectionUserName)) { $MirrorConnectionUserName = $out.postgreSqlFabricUserNameOut.value } + if ($out.postgreSqlFabricUserSecretNameOut -and $out.postgreSqlFabricUserSecretNameOut.value -and (-not $MirrorConnectionSecretName)) { $MirrorConnectionSecretName = $out.postgreSqlFabricUserSecretNameOut.value } + if ($out.postgreSqlAdminSecretName -and $out.postgreSqlAdminSecretName.value -and (-not $MirrorConnectionSecretName)) { $MirrorConnectionSecretName = $out.postgreSqlAdminSecretName.value } } catch {} } -if (-not $postgreSqlServerResourceId) { - try { - $val = & azd env get-value postgreSqlServerResourceId 2>$null - if ($val) { $postgreSqlServerResourceId = $val.ToString().Trim() } - } catch {} +if (-not $postgreSqlServerResourceId) { $postgreSqlServerResourceId = Get-AzdEnvValue 'postgreSqlServerResourceId' } +if (-not $postgreSqlServerName) { $postgreSqlServerName = Get-AzdEnvValue 'postgreSqlServerNameOut' } +if (-not $postgreSqlServerFqdn) { $postgreSqlServerFqdn = Get-AzdEnvValue 'postgreSqlServerFqdn' } +if (-not $postgreSqlSystemAssignedPrincipalId) { $postgreSqlSystemAssignedPrincipalId = Get-AzdEnvValue 'postgreSqlSystemAssignedPrincipalId' } +if (-not $postgreSqlAdminLogin) { $postgreSqlAdminLogin = Get-AzdEnvValue 'postgreSqlAdminLoginOut' } +if (-not $postgreSqlFabricUserName) { $postgreSqlFabricUserName = Get-AzdEnvValue 'postgreSqlFabricUserNameOut' } +if (-not $postgreSqlFabricUserSecretName) { $postgreSqlFabricUserSecretName = Get-AzdEnvValue 'postgreSqlFabricUserSecretNameOut' } +if (-not $keyVaultResourceId) { $keyVaultResourceId = Get-AzdEnvValue 'keyVaultResourceId' } +if (-not $MirrorConnectionMode) { $MirrorConnectionMode = Get-AzdEnvValue 'postgreSqlMirrorConnectionModeOut' } +if (-not $MirrorConnectionUserName) { $MirrorConnectionUserName = Get-AzdEnvValue 'postgreSqlMirrorConnectionUserNameOut' } +if (-not $MirrorConnectionSecretName) { $MirrorConnectionSecretName = Get-AzdEnvValue 'postgreSqlMirrorConnectionSecretNameOut' } +if (-not $MirrorConnectionUserName) { $MirrorConnectionUserName = Get-AzdEnvValue 'postgreSqlFabricUserNameOut' } +if (-not $MirrorConnectionSecretName) { $MirrorConnectionSecretName = Get-AzdEnvValue 'postgreSqlFabricUserSecretNameOut' } +if (-not $MirrorConnectionSecretName) { $MirrorConnectionSecretName = Get-AzdEnvValue 'postgreSqlAdminSecretName' } +if (-not $GatewayId) { $GatewayId = Get-AzdEnvValue 'fabricPostgresGatewayId' } + +$subscriptionIdFromEnv = $env:AZURE_SUBSCRIPTION_ID +if (-not $subscriptionIdFromEnv) { $subscriptionIdFromEnv = Get-AzdEnvValue 'AZURE_SUBSCRIPTION_ID' } +$resourceGroupFromEnv = $env:AZURE_RESOURCE_GROUP +if (-not $resourceGroupFromEnv) { $resourceGroupFromEnv = Get-AzdEnvValue 'AZURE_RESOURCE_GROUP' } + +$deploymentOutputs = $null +if (-not $env:AZURE_OUTPUTS_JSON) { + $deploymentEnvironmentName = $env:AZURE_ENV_NAME + if (-not $deploymentEnvironmentName) { $deploymentEnvironmentName = Get-AzdEnvValue 'AZURE_ENV_NAME' } + $deploymentOutputs = Get-LatestDeploymentOutputs -resourceGroup $resourceGroupFromEnv -subscriptionId $subscriptionIdFromEnv -environmentName $deploymentEnvironmentName } -if (-not $postgreSqlServerName) { - try { - $val = & azd env get-value postgreSqlServerNameOut 2>$null - if ($val) { $postgreSqlServerName = $val.ToString().Trim() } - } catch {} +if ($deploymentOutputs) { + if (-not $postgreSqlServerResourceId -and $deploymentOutputs.postgreSqlServerResourceId -and $deploymentOutputs.postgreSqlServerResourceId.value) { $postgreSqlServerResourceId = $deploymentOutputs.postgreSqlServerResourceId.value } + if (-not $postgreSqlServerName -and $deploymentOutputs.postgreSqlServerNameOut -and $deploymentOutputs.postgreSqlServerNameOut.value) { $postgreSqlServerName = $deploymentOutputs.postgreSqlServerNameOut.value } + if (-not $postgreSqlServerFqdn -and $deploymentOutputs.postgreSqlServerFqdn -and $deploymentOutputs.postgreSqlServerFqdn.value) { $postgreSqlServerFqdn = $deploymentOutputs.postgreSqlServerFqdn.value } + if (-not $postgreSqlSystemAssignedPrincipalId -and $deploymentOutputs.postgreSqlSystemAssignedPrincipalId -and $deploymentOutputs.postgreSqlSystemAssignedPrincipalId.value) { $postgreSqlSystemAssignedPrincipalId = $deploymentOutputs.postgreSqlSystemAssignedPrincipalId.value } + if (-not $postgreSqlAdminLogin -and $deploymentOutputs.postgreSqlAdminLoginOut -and $deploymentOutputs.postgreSqlAdminLoginOut.value) { $postgreSqlAdminLogin = $deploymentOutputs.postgreSqlAdminLoginOut.value } + if (-not $postgreSqlFabricUserName -and $deploymentOutputs.postgreSqlFabricUserNameOut -and $deploymentOutputs.postgreSqlFabricUserNameOut.value) { $postgreSqlFabricUserName = $deploymentOutputs.postgreSqlFabricUserNameOut.value } + if (-not $postgreSqlFabricUserSecretName -and $deploymentOutputs.postgreSqlFabricUserSecretNameOut -and $deploymentOutputs.postgreSqlFabricUserSecretNameOut.value) { $postgreSqlFabricUserSecretName = $deploymentOutputs.postgreSqlFabricUserSecretNameOut.value } + if (-not $keyVaultResourceId -and $deploymentOutputs.keyVaultResourceId -and $deploymentOutputs.keyVaultResourceId.value) { $keyVaultResourceId = $deploymentOutputs.keyVaultResourceId.value } + if (-not $MirrorConnectionMode -and $deploymentOutputs.postgreSqlMirrorConnectionModeOut -and $deploymentOutputs.postgreSqlMirrorConnectionModeOut.value) { $MirrorConnectionMode = $deploymentOutputs.postgreSqlMirrorConnectionModeOut.value } + if (-not $MirrorConnectionUserName -and $deploymentOutputs.postgreSqlMirrorConnectionUserNameOut -and $deploymentOutputs.postgreSqlMirrorConnectionUserNameOut.value) { $MirrorConnectionUserName = $deploymentOutputs.postgreSqlMirrorConnectionUserNameOut.value } + if (-not $MirrorConnectionSecretName -and $deploymentOutputs.postgreSqlMirrorConnectionSecretNameOut -and $deploymentOutputs.postgreSqlMirrorConnectionSecretNameOut.value) { $MirrorConnectionSecretName = $deploymentOutputs.postgreSqlMirrorConnectionSecretNameOut.value } } -if (-not $postgreSqlServerFqdn) { + +function Resolve-PrimaryResource { + param( + [string]$ResourceType, + [string]$ResourceGroup, + [string]$SubscriptionId + ) + + if ([string]::IsNullOrWhiteSpace($ResourceGroup)) { return $null } + try { - $val = & azd env get-value postgreSqlServerFqdn 2>$null - if ($val) { $postgreSqlServerFqdn = $val.ToString().Trim() } - } catch {} + $args = @('resource', 'list', '--resource-group', $ResourceGroup, '--query', "[?type=='$ResourceType'].{id:id,name:name}", '-o', 'json') + if ($SubscriptionId) { $args += @('--subscription', $SubscriptionId) } + $json = & az @args 2>$null + if ($LASTEXITCODE -ne 0 -or -not $json) { return $null } + + $resources = @($json | ConvertFrom-Json -ErrorAction Stop) + if (-not $resources) { return $null } + + if ($ResourceType -eq 'Microsoft.KeyVault/vaults') { + $preferred = $resources | Where-Object { $_.name -notlike 'kv-ai-*' } | Select-Object -First 1 + if ($preferred) { return $preferred } + } + + return $resources | Select-Object -First 1 + } catch { + return $null + } } -if (-not $postgreSqlSystemAssignedPrincipalId) { + +if (-not $postgreSqlServerResourceId) { + $pgResource = Resolve-PrimaryResource -ResourceType 'Microsoft.DBforPostgreSQL/flexibleServers' -ResourceGroup $resourceGroupFromEnv -SubscriptionId $subscriptionIdFromEnv + if ($pgResource) { + $postgreSqlServerResourceId = $pgResource.id + if (-not $postgreSqlServerName) { $postgreSqlServerName = $pgResource.name } + } +} + +if (-not $keyVaultResourceId) { + $kvResource = Resolve-PrimaryResource -ResourceType 'Microsoft.KeyVault/vaults' -ResourceGroup $resourceGroupFromEnv -SubscriptionId $subscriptionIdFromEnv + if ($kvResource) { $keyVaultResourceId = $kvResource.id } +} + +function Resolve-PostgreSqlServerDetails { + param( + [string]$ServerName, + [string]$ResourceGroup, + [string]$SubscriptionId + ) + + if ([string]::IsNullOrWhiteSpace($ServerName) -or [string]::IsNullOrWhiteSpace($ResourceGroup)) { + return $null + } + try { - $val = & azd env get-value postgreSqlSystemAssignedPrincipalId 2>$null - if ($val) { $postgreSqlSystemAssignedPrincipalId = $val.ToString().Trim() } - } catch {} + $args = @('postgres', 'flexible-server', 'show', '--resource-group', $ResourceGroup, '--name', $ServerName, '-o', 'json') + if ($SubscriptionId) { $args += @('--subscription', $SubscriptionId) } + + $json = & az @args 2>$null + if ($LASTEXITCODE -ne 0 -or -not $json) { return $null } + + return $json | ConvertFrom-Json -ErrorAction Stop + } catch { + return $null + } +} + +if (-not $postgreSqlServerName -and $postgreSqlServerResourceId) { + $postgreSqlServerName = Get-ResourceNameFromId $postgreSqlServerResourceId +} + +if (-not $postgreSqlServerFqdn -and $postgreSqlServerName) { + $serverDetails = Resolve-PostgreSqlServerDetails -ServerName $postgreSqlServerName -ResourceGroup $resourceGroupFromEnv -SubscriptionId $subscriptionIdFromEnv + if ($serverDetails) { + if (-not $postgreSqlServerFqdn -and $serverDetails.fullyQualifiedDomainName) { + $postgreSqlServerFqdn = $serverDetails.fullyQualifiedDomainName + Set-AzdEnvValue -key 'postgreSqlServerFqdn' -value $postgreSqlServerFqdn + } + + if (-not $postgreSqlSystemAssignedPrincipalId -and $serverDetails.identity -and $serverDetails.identity.principalId) { + $postgreSqlSystemAssignedPrincipalId = $serverDetails.identity.principalId + Set-AzdEnvValue -key 'postgreSqlSystemAssignedPrincipalId' -value $postgreSqlSystemAssignedPrincipalId + } + + if (-not $postgreSqlAdminLogin -and $serverDetails.administratorLogin) { + $postgreSqlAdminLogin = $serverDetails.administratorLogin + Set-AzdEnvValue -key 'postgreSqlAdminLoginOut' -value $postgreSqlAdminLogin + } + } } if (-not $postgreSqlServerResourceId -or [string]::IsNullOrWhiteSpace($postgreSqlServerResourceId)) { @@ -88,6 +528,11 @@ if (-not $postgreSqlServerResourceId -or [string]::IsNullOrWhiteSpace($postgreSq exit 0 } +if (-not $postgreSqlServerFqdn) { + Warn "PostgreSQL server FQDN not resolved; skipping mirror." + exit 0 +} + # Resolve workspace id if needed if (-not $WorkspaceId) { $workspaceEnvPath = Join-Path ([IO.Path]::GetTempPath()) 'fabric_workspace.env' @@ -115,128 +560,365 @@ if (-not $WorkspaceId) { if (-not $WorkspaceId) { Warn "WorkspaceId not resolved; skipping mirror."; exit 0 } if (-not $ConnectionId) { - try { - $val = & azd env get-value fabricPostgresConnectionId 2>$null - if ($val) { $ConnectionId = $val.ToString().Trim() } - } catch {} -} - -if (-not $ConnectionId) { - Warn "FABRIC_POSTGRES_CONNECTION_ID not set; create a Fabric connection and rerun." - exit 0 + $ConnectionId = Get-AzdEnvValue 'fabricPostgresConnectionId' } if (-not $DatabaseName) { $DatabaseName = 'postgres' } +if (-not $MirrorConnectionMode) { $MirrorConnectionMode = 'fabricUser' } +if (-not $MirrorConnectionUserName -and $MirrorConnectionMode -eq 'admin') { $MirrorConnectionUserName = $postgreSqlAdminLogin } +if (-not $MirrorConnectionUserName -and $MirrorConnectionMode -eq 'fabricUser') { $MirrorConnectionUserName = 'fabric_user' } +if (-not $MirrorConnectionSecretName) { + $MirrorConnectionSecretName = if ($MirrorConnectionMode -eq 'admin') { 'postgres-admin-password' } else { 'postgres-fabric-user-password' } +} +if (-not $postgreSqlFabricUserName) { $postgreSqlFabricUserName = 'fabric_user' } +if (-not $postgreSqlFabricUserSecretName) { $postgreSqlFabricUserSecretName = 'postgres-fabric-user-password' } if (-not $MirrorName) { $envName = $env:AZURE_ENV_NAME if ([string]::IsNullOrWhiteSpace($envName)) { $envName = 'env' } $MirrorName = "pg-mirror-$envName" } +if (-not $ConnectionDisplayName) { + $displayUserLabel = $MirrorConnectionUserName + if ([string]::IsNullOrWhiteSpace($displayUserLabel)) { + $displayUserLabel = if ($MirrorConnectionMode -eq 'admin') { 'admin' } else { 'connection' } + } + $ConnectionDisplayName = "$postgreSqlServerFqdn;$DatabaseName $displayUserLabel" +} + +$keyVaultName = Get-ResourceNameFromId $keyVaultResourceId +$tempEnableKvPublicAccess = IsTrue $TempEnableKeyVaultPublicAccess +$postgreSqlResourceGroup = $resourceGroupFromEnv +if (-not $postgreSqlResourceGroup) { $postgreSqlResourceGroup = Get-ResourceGroupFromId $postgreSqlServerResourceId } +$restorePostgreSqlPublicAccess = $null +$temporarilyEnabledPostgreSqlPublicAccess = $false +$temporaryFirewallRuleName = 'AllowAzureServicesFabricMirrorTemp' +$temporarilyAddedAzureServicesFirewallRule = $false + +try { + if (-not $GatewayId -and $postgreSqlResourceGroup -and $postgreSqlServerName) { + $restorePostgreSqlPublicAccess = Get-PostgreSqlPublicAccess -resourceGroup $postgreSqlResourceGroup -serverName $postgreSqlServerName -subscriptionId $subscriptionIdFromEnv + if ($restorePostgreSqlPublicAccess -and $restorePostgreSqlPublicAccess.ToLowerInvariant() -ne 'enabled') { + Log "Temporarily enabling PostgreSQL public access so Fabric can create the connection..." + Set-PostgreSqlPublicAccess -resourceGroup $postgreSqlResourceGroup -serverName $postgreSqlServerName -state 'Enabled' -subscriptionId $subscriptionIdFromEnv + $temporarilyEnabledPostgreSqlPublicAccess = $true + } -# Acquire Fabric token -try { $fabricToken = Get-SecureApiToken -Resource $SecureApiResources.Fabric -Description "Fabric" } catch { $fabricToken = $null } -if (-not $fabricToken) { Warn "Cannot acquire Fabric API token; ensure az login."; exit 0 } + Log "Temporarily allowing Azure services to reach PostgreSQL for Fabric connection validation..." + Add-PostgreSqlFirewallRule -resourceGroup $postgreSqlResourceGroup -serverName $postgreSqlServerName -ruleName $temporaryFirewallRuleName -startIpAddress '0.0.0.0' -endIpAddress '0.0.0.0' -subscriptionId $subscriptionIdFromEnv + $temporarilyAddedAzureServicesFirewallRule = $true + } + + # Acquire Fabric token + try { $fabricToken = Get-SecureApiToken -Resource $SecureApiResources.Fabric -Description "Fabric" } catch { $fabricToken = $null } + if (-not $fabricToken) { Warn "Cannot acquire Fabric API token; ensure az login."; exit 0 } -$fabricHeaders = New-SecureHeaders -Token $fabricToken -$apiRoot = 'https://api.fabric.microsoft.com/v1' + $fabricHeaders = New-SecureHeaders -Token $fabricToken + $apiRoot = 'https://api.fabric.microsoft.com/v1' -# Guard: skip until the Fabric PostgreSQL connection exists -if ($ConnectionId) { + $connections = $null try { - $connections = Invoke-SecureRestMethod -Uri "$apiRoot/connections" -Headers $fabricHeaders -Method Get -Description "Fabric connections" - $match = $connections.value | Where-Object { $_.id -eq $ConnectionId } - if (-not $match) { - Warn "FABRIC_POSTGRES_CONNECTION_ID not found in Fabric. Create the connection and rerun." - exit 0 - } + $connections = Invoke-FabricPagedGet -InitialUri "$apiRoot/connections" -Headers $fabricHeaders -Description 'Fabric connections' } catch { - Warn "Unable to validate Fabric connection ID; continuing with mirror attempt." + Warn "Unable to list existing Fabric connections. Automatic connection reuse will be limited." } -} -if ($postgreSqlSystemAssignedPrincipalId) { - $roleAssignmentBody = @{ - principal = @{ - id = $postgreSqlSystemAssignedPrincipalId - type = 'ServicePrincipal' + if ($ConnectionId) { + try { + $match = @($connections | Where-Object { $_.id -eq $ConnectionId }) | Select-Object -First 1 + if (-not $match) { + Warn "Stored Fabric PostgreSQL connection ID '$ConnectionId' was not found. Attempting to resolve or recreate the connection." + $ConnectionId = $null + } else { + Log "Using existing Fabric connection ID: $ConnectionId" + } + } catch { + Warn "Unable to validate Fabric connection ID '$ConnectionId'; attempting to continue." } - role = 'Contributor' - } | ConvertTo-Json -Depth 4 + } - try { - Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$WorkspaceId/roleAssignments" -Headers $fabricHeaders -Method Post -Body $roleAssignmentBody | Out-Null - Log "Granted Fabric workspace access to PostgreSQL managed identity: $postgreSqlSystemAssignedPrincipalId" - } catch { - $msg = $_.Exception.Message - if ($msg -like '*409*' -or $msg -like '*already*') { - Log "PostgreSQL managed identity already has Fabric workspace access." - } else { - Warn "Failed to grant workspace access to PostgreSQL managed identity: $msg" + if (-not $ConnectionId -and $connections) { + $expectedPath = "$postgreSqlServerFqdn;$DatabaseName" + $existingConnection = @($connections | Where-Object { + $_.displayName -eq $ConnectionDisplayName -or $_.connectionDetails.path -eq $expectedPath + }) | Select-Object -First 1 + + if ($existingConnection) { + $ConnectionId = $existingConnection.id + Log "Reusing existing Fabric PostgreSQL connection '$($existingConnection.displayName)' ($ConnectionId)." + Set-AzdEnvValue -key 'fabricPostgresConnectionId' -value $ConnectionId } } -} else { - Warn "PostgreSQL managed identity principalId not found; skipping Fabric RBAC assignment." -} -# Skip if mirror already exists -try { - $existing = Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$WorkspaceId/mirroredDatabases" -Headers $fabricHeaders -Method Get -ErrorAction Stop - if ($existing.value) { - $match = $existing.value | Where-Object { $_.displayName -eq $MirrorName } - if ($match) { Log "Mirror already exists: $MirrorName ($($match.id))"; exit 0 } + if (-not $ConnectionId) { + if ([string]::IsNullOrWhiteSpace($MirrorConnectionUserName)) { + Warn "Mirror connection username was not resolved. Check postgreSqlMirrorConnectionUserNameOut and retry." + exit 0 + } + + if ([string]::IsNullOrWhiteSpace($MirrorConnectionSecretName) -and [string]::IsNullOrWhiteSpace($MirrorConnectionPassword)) { + Warn "Mirror connection secret name was not resolved. Check postgreSqlMirrorConnectionSecretNameOut and retry." + exit 0 + } + + try { + if (-not $MirrorConnectionPassword) { + if ($tempEnableKvPublicAccess -and $keyVaultName) { + Log "Temporarily enabling Key Vault public access for Fabric connection secret retrieval..." + Set-KeyVaultPublicAccess -vaultName $keyVaultName -state 'Enabled' + } + + if ($keyVaultName -and $MirrorConnectionSecretName) { + if (-not (Test-KeyVaultAccess $keyVaultName)) { + Warn "Key Vault '$keyVaultName' is not reachable. Automatic Fabric connection creation requires access to the mirror credential secret." + exit 0 + } + + $MirrorConnectionPassword = Invoke-AzCliCapture @('keyvault','secret','show','--vault-name', $keyVaultName,'--name', $MirrorConnectionSecretName,'--query','value','-o','tsv') + } + } + + if (-not $MirrorConnectionPassword) { + Warn "Mirror connection password was not resolved from Key Vault or environment. Automatic Fabric connection creation skipped." + exit 0 + } + + $supportedTypesUri = if ($GatewayId) { + "$apiRoot/connections/supportedConnectionTypes?gatewayId=$([System.Uri]::EscapeDataString($GatewayId))&showAllCreationMethods=true" + } else { + "$apiRoot/connections/supportedConnectionTypes?showAllCreationMethods=true" + } + + $supportedTypes = Invoke-FabricPagedGet -InitialUri $supportedTypesUri -Headers $fabricHeaders -Description 'Supported Fabric connection types' + $selectedMetadata = Select-PostgreSqlConnectionMetadata -SupportedTypes $supportedTypes + + if ($selectedMetadata.Metadata.supportedCredentialTypes -notcontains 'Basic') { + Warn "Fabric does not report Basic auth support for connection type '$($selectedMetadata.Type)'. Automatic connection creation skipped." + exit 0 + } + + $parameterList = @() + foreach ($parameterDefinition in @($selectedMetadata.CreationMethod.parameters)) { + $parameterValue = Get-ConnectionParameterValue -parameterDefinition $parameterDefinition -ServerFqdn $postgreSqlServerFqdn -TargetDatabase $DatabaseName -UserName $MirrorConnectionUserName + if ($null -eq $parameterValue) { + if ($parameterDefinition.required) { + throw "Unsupported required PostgreSQL Fabric connection parameter '$($parameterDefinition.name)' for creation method '$($selectedMetadata.CreationMethod.name)'." + } + + continue + } + + $parameterList += New-ConnectionDetailsParameter -parameterDefinition $parameterDefinition -value $parameterValue + } + + $connectionEncryption = @('Encrypted', 'Any', 'NotEncrypted') | Where-Object { + @($selectedMetadata.Metadata.supportedConnectionEncryptionTypes) -contains $_ + } | Select-Object -First 1 + if (-not $connectionEncryption) { $connectionEncryption = 'Encrypted' } + + $primaryAttempt = @{ + UserName = $MirrorConnectionUserName + SecretName = $MirrorConnectionSecretName + Password = $MirrorConnectionPassword + DisplayName = $ConnectionDisplayName + } + $connectionAttempts = @($primaryAttempt) + $canFallbackToFabricUser = ( + $MirrorConnectionMode -eq 'admin' -and + -not [string]::IsNullOrWhiteSpace($postgreSqlFabricUserName) -and + -not [string]::IsNullOrWhiteSpace($postgreSqlFabricUserSecretName) -and + $postgreSqlFabricUserName -ne $MirrorConnectionUserName + ) + + if ($canFallbackToFabricUser) { + $connectionAttempts += @{ + UserName = $postgreSqlFabricUserName + SecretName = $postgreSqlFabricUserSecretName + Password = $null + DisplayName = "$postgreSqlServerFqdn;$DatabaseName $postgreSqlFabricUserName" + } + } + + $lastConnectionFailure = $null + foreach ($connectionAttempt in $connectionAttempts) { + if (-not $connectionAttempt.Password -and $keyVaultName -and $connectionAttempt.SecretName) { + $connectionAttempt.Password = Invoke-AzCliCapture @('keyvault','secret','show','--vault-name', $keyVaultName,'--name', $connectionAttempt.SecretName,'--query','value','-o','tsv') + } + + if (-not $connectionAttempt.Password) { + throw "Mirror connection password was not resolved for user '$($connectionAttempt.UserName)'." + } + + $createConnectionBody = New-FabricPostgreSqlConnectionBody -DisplayName $connectionAttempt.DisplayName -ConnectivityType $(if ($GatewayId) { 'VirtualNetworkGateway' } else { 'ShareableCloud' }) -ConnectionType $selectedMetadata.Type -CreationMethod $selectedMetadata.CreationMethod.name -Parameters $parameterList -PrivacyLevel 'None' -ConnectionEncryption $connectionEncryption -UserName $connectionAttempt.UserName -Password $connectionAttempt.Password -GatewayId $GatewayId + + try { + Log "Creating Fabric PostgreSQL connection '$($connectionAttempt.DisplayName)' for $postgreSqlServerFqdn/$DatabaseName" + $connectionResponse = Invoke-SecureRestMethod -Uri "$apiRoot/connections" -Headers $fabricHeaders -Method Post -Body $createConnectionBody -Description 'Create Fabric PostgreSQL connection' + $ConnectionId = $connectionResponse.id + $ConnectionDisplayName = $connectionAttempt.DisplayName + Set-AzdEnvValue -key 'fabricPostgresConnectionId' -value $ConnectionId + Log "Created Fabric PostgreSQL connection: $ConnectionId" + $lastConnectionFailure = $null + break + } catch { + $responseBody = $null + try { + if ($_.Exception.Response) { + $responseBody = Read-SecureResponseBody -Response $_.Exception.Response + if ($responseBody) { $responseBody = Sanitize-SecureResponseBody -ResponseBody $responseBody } + } + } catch {} + + $lastConnectionFailure = @{ + Message = $_.Exception.Message + ResponseBody = $responseBody + } + + if ($connectionAttempt.UserName -eq $MirrorConnectionUserName -and $canFallbackToFabricUser -and (Test-IsFabricIncorrectCredentialFailure -ResponseBody $responseBody)) { + Warn "Fabric rejected the admin credential for PostgreSQL mirroring. Retrying with dedicated Fabric user '$postgreSqlFabricUserName'." + continue + } + + throw + } + } + + if (-not $ConnectionId -and $lastConnectionFailure) { + throw $lastConnectionFailure.Message + } + } catch { + $responseBody = $null + try { + if ($_.Exception.Response) { + $responseBody = Read-SecureResponseBody -Response $_.Exception.Response + if ($responseBody) { $responseBody = Sanitize-SecureResponseBody -ResponseBody $responseBody } + } + } catch {} + + Warn "Automatic Fabric PostgreSQL connection creation failed: $($_.Exception.Message)" + if ($responseBody) { Warn "Fabric API response body: $responseBody" } + if (-not $GatewayId -and $serverDetails -and @($serverDetails.privateEndpointConnections).Count -gt 0 -and (Test-IsFabricConnectivityTimeoutFailure -ResponseBody $responseBody)) { + Fail "Fabric cannot reach PostgreSQL server '$postgreSqlServerFqdn' over the default shared-cloud connection path. This server has a private endpoint, and no Fabric VNet data gateway is configured. Create or identify a Fabric VNet data gateway with network reachability to the PostgreSQL private endpoint, set azd env value 'fabricPostgresGatewayId' to that gateway ID, and rerun mirror creation. See docs/postgresql_mirroring.md for the gateway-backed path." + } + + Fail "Fabric PostgreSQL connection creation failed. See the warnings above for the service response. If your PostgreSQL source is only reachable through private networking, set 'fabricPostgresGatewayId' before rerunning this script." + } finally { + if ($tempEnableKvPublicAccess -and $keyVaultName) { + Log "Restoring Key Vault public access to Disabled after Fabric connection secret retrieval..." + Set-KeyVaultPublicAccess -vaultName $keyVaultName -state 'Disabled' + } + } + } + + if (-not $ConnectionId) { + Warn "No Fabric PostgreSQL connection ID is available; skipping mirrored database creation." + exit 0 + } + + try { + $validatedConnection = @($connections | Where-Object { $_.id -eq $ConnectionId }) | Select-Object -First 1 + if (-not $validatedConnection) { + $validatedConnection = Invoke-SecureRestMethod -Uri "$apiRoot/connections/$ConnectionId" -Headers $fabricHeaders -Method Get -Description 'Fabric connection details' + } + if ($validatedConnection -and $validatedConnection.id) { + Log "Validated Fabric PostgreSQL connection '$($validatedConnection.displayName)' ($ConnectionId)." + } + } catch { + Warn "Unable to validate Fabric connection ID '$ConnectionId'; continuing with mirror attempt." } -} catch {} -$mirroringJson = @{ - properties = @{ - source = @{ - type = 'AzurePostgreSql' - typeProperties = @{ - connection = $ConnectionId - database = $DatabaseName + if ($postgreSqlSystemAssignedPrincipalId) { + $roleAssignmentBody = @{ + principal = @{ + id = $postgreSqlSystemAssignedPrincipalId + type = 'ServicePrincipal' + } + role = 'Contributor' + } | ConvertTo-Json -Depth 4 + + try { + Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$WorkspaceId/roleAssignments" -Headers $fabricHeaders -Method Post -Body $roleAssignmentBody | Out-Null + Log "Granted Fabric workspace access to PostgreSQL managed identity: $postgreSqlSystemAssignedPrincipalId" + } catch { + $msg = $_.Exception.Message + if ($msg -like '*409*' -or $msg -like '*already*') { + Log "PostgreSQL managed identity already has Fabric workspace access." + } else { + Warn "Failed to grant workspace access to PostgreSQL managed identity: $msg" } } - target = @{ - type = 'MountedRelationalDatabase' - typeProperties = @{ - defaultSchema = 'public' - format = 'Delta' + } else { + Warn "PostgreSQL managed identity principalId not found; skipping Fabric RBAC assignment." + } + + # Skip if mirror already exists + try { + $existing = Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$WorkspaceId/mirroredDatabases" -Headers $fabricHeaders -Method Get -ErrorAction Stop + if ($existing.value) { + $match = $existing.value | Where-Object { $_.displayName -eq $MirrorName } + if ($match) { Log "Mirror already exists: $MirrorName ($($match.id))"; exit 0 } + } + } catch {} + + $mirroringJson = @{ + properties = @{ + source = @{ + type = 'AzurePostgreSql' + typeProperties = @{ + connection = $ConnectionId + database = $DatabaseName + } + } + target = @{ + type = 'MountedRelationalDatabase' + typeProperties = @{ + defaultSchema = 'public' + format = 'Delta' + } } } } -} -$mirroringJsonText = $mirroringJson | ConvertTo-Json -Depth 10 -$mirroringPayload = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($mirroringJsonText)) + $mirroringJsonText = $mirroringJson | ConvertTo-Json -Depth 10 + $mirroringPayload = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($mirroringJsonText)) + + $body = @{ + displayName = $MirrorName + description = "Mirrored PostgreSQL database from $postgreSqlServerName" + definition = @{ + parts = @( + @{ + path = 'mirroring.json' + payload = $mirroringPayload + payloadType = 'InlineBase64' + } + ) + } + } -$body = @{ - displayName = $MirrorName - description = "Mirrored PostgreSQL database from $postgreSqlServerName" - definition = @{ - parts = @( - @{ - path = 'mirroring.json' - payload = $mirroringPayload - payloadType = 'InlineBase64' + Log "Creating mirrored database '$MirrorName' in workspace $WorkspaceId" + try { + $resp = Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$WorkspaceId/mirroredDatabases" -Headers $fabricHeaders -Method Post -Body $body -ErrorAction Stop + Log "Created mirror: $($resp.id)" + } catch { + $rawBody = $null + try { + if ($_.Exception.Response) { + $rawBody = Read-SecureResponseBody -Response $_.Exception.Response + if ($rawBody) { $rawBody = Sanitize-SecureResponseBody -ResponseBody $rawBody } } - ) + } catch { $rawBody = $null } + Warn "Failed to create mirror: $($_.Exception.Message)" + if ($rawBody) { Warn "Fabric API response body: $rawBody" } + throw + } +} finally { + if ($temporarilyAddedAzureServicesFirewallRule -and $postgreSqlResourceGroup -and $postgreSqlServerName) { + Log "Removing temporary PostgreSQL firewall rule '$temporaryFirewallRuleName'..." + Remove-PostgreSqlFirewallRule -resourceGroup $postgreSqlResourceGroup -serverName $postgreSqlServerName -ruleName $temporaryFirewallRuleName -subscriptionId $subscriptionIdFromEnv } -} -Log "Creating mirrored database '$MirrorName' in workspace $WorkspaceId" -try { - $resp = Invoke-SecureRestMethod -Uri "$apiRoot/workspaces/$WorkspaceId/mirroredDatabases" -Headers $fabricHeaders -Method Post -Body $body -ErrorAction Stop - Log "Created mirror: $($resp.id)" -} catch { - $rawBody = $null - try { - $resp = $_.Exception.Response - if ($resp) { - $reader = New-Object System.IO.StreamReader($resp.GetResponseStream()) - $rawBody = $reader.ReadToEnd() - } - } catch { $rawBody = $null } - Warn "Failed to create mirror: $($_.Exception.Message)" - if ($rawBody) { Warn "Fabric API response body: $rawBody" } - throw + if ($temporarilyEnabledPostgreSqlPublicAccess -and $postgreSqlResourceGroup -and $postgreSqlServerName -and $restorePostgreSqlPublicAccess) { + Log "Restoring PostgreSQL public access to '$restorePostgreSqlPublicAccess' after Fabric mirror setup..." + Set-PostgreSqlPublicAccess -resourceGroup $postgreSqlResourceGroup -serverName $postgreSqlServerName -state $restorePostgreSqlPublicAccess -subscriptionId $subscriptionIdFromEnv + } } diff --git a/scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 b/scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 index f4360ea..8ab2563 100644 --- a/scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 +++ b/scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 @@ -7,6 +7,7 @@ param( [string]$DatabaseName = $env:POSTGRES_DATABASE_NAME, [string]$FabricUserName = $env:POSTGRES_FABRIC_USER_NAME, + [string]$MirrorConnectionMode = $env:POSTGRES_MIRROR_CONNECTION_MODE, [string]$EntraRoleName = $env:POSTGRES_FABRIC_ENTRA_ROLE_NAME, [string]$EntraObjectId = $env:POSTGRES_FABRIC_ENTRA_OBJECT_ID, [string]$EntraObjectType = $env:POSTGRES_FABRIC_ENTRA_OBJECT_TYPE, @@ -22,23 +23,26 @@ Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' # Import security module + $SecurityModulePath = Join-Path $PSScriptRoot "../../SecurityModule.ps1" . $SecurityModulePath function Log([string]$m){ Write-Host "[pg-mirroring-prep] $m" } function Warn([string]$m){ Write-Warning "[pg-mirroring-prep] $m" } +function Fail([string]$m){ Write-Error "[pg-mirroring-prep] $m"; exit 1 } function IsTrue([string]$v){ return ($v -and $v.ToString().Trim().ToLowerInvariant() -in @('1','true','yes')) } +function Convert-SqlToAzQueryText([string]$sqlText){ return (($sqlText -replace "[`r`n]+", ' ').Trim()) } function Ensure-AzExtension([string]$name) { - try { - $null = az extension show --name $name 2>$null + $null = & az extension show --name $name 2>$null + if ($LASTEXITCODE -eq 0) { return $true - } catch { - Warn "Azure CLI extension '$name' is required but not installed." - Warn "Install: az extension add --name $name" - Warn "If install fails: & \"C:\Program Files\Microsoft SDKs\Azure\CLI2\python.exe\" -m pip install --upgrade pip setuptools wheel" - return $false } + + Warn "Azure CLI extension '$name' is required but not installed." + Warn "Install: az extension add --name $name" + Warn "If install fails: & \"C:\Program Files\Microsoft SDKs\Azure\CLI2\python.exe\" -m pip install --upgrade pip setuptools wheel" + return $false } # Resolve PostgreSQL outputs @@ -48,26 +52,194 @@ $postgreSqlAdminLogin = $null $postgreSqlAdminSecretName = $null $postgreSqlFabricUserSecretName = $null $keyVaultResourceId = $null +$script:PsqlPath = $null +$script:NpgsqlReady = $false +$script:NpgsqlPackageVersion = '8.0.3' +$script:LastPostgresCredentialError = $null + +function Invoke-AzCli([string[]]$AzArguments) { + $script:LASTEXITCODE = 0 + $azCmd = 'az' + try { + $resolved = Get-Command az -ErrorAction Stop + if ($resolved -and $resolved.Source) { $azCmd = $resolved.Source } + } catch {} + + $output = & $azCmd @AzArguments 2>&1 + $outputText = (($output | ForEach-Object { $_.ToString() }) -join [Environment]::NewLine) + if ($LASTEXITCODE -ne 0) { + if ([string]::IsNullOrWhiteSpace($outputText)) { + throw "Azure CLI command failed with exit code ${LASTEXITCODE}: az $($AzArguments -join ' ')" + } + + throw "Azure CLI command failed with exit code ${LASTEXITCODE}: az $($AzArguments -join ' ')`n$outputText" + } + + if ($outputText -match 'Welcome to the cool new Azure CLI!|^Group\s+az\b|^Commands:\s*$') { + throw "Azure CLI returned help text instead of executing the command: az $($AzArguments -join ' ')" + } +} + +function Invoke-AzCliCapture([string[]]$AzArguments) { + $script:LASTEXITCODE = 0 + $azCmd = 'az' + try { + $resolved = Get-Command az -ErrorAction Stop + if ($resolved -and $resolved.Source) { $azCmd = $resolved.Source } + } catch {} + + $output = & $azCmd @AzArguments 2>&1 + $outputText = (($output | ForEach-Object { $_.ToString() }) -join [Environment]::NewLine) + if ($LASTEXITCODE -ne 0) { + if ([string]::IsNullOrWhiteSpace($outputText)) { + throw "Azure CLI command failed with exit code ${LASTEXITCODE}: az $($AzArguments -join ' ')" + } + + throw "Azure CLI command failed with exit code ${LASTEXITCODE}: az $($AzArguments -join ' ')`n$outputText" + } + + if ($outputText -match 'Welcome to the cool new Azure CLI!|^Group\s+az\b|^Commands:\s*$') { + throw "Azure CLI returned help text instead of executing the command: az $($AzArguments -join ' ')" + } + + return $output +} + +function Invoke-AzCliWithServerBusyRetry([string[]]$AzArguments, [int]$MaxRetries = 8, [int]$DelaySeconds = 15) { + for ($attempt = 1; $attempt -le $MaxRetries; $attempt++) { + try { + return Invoke-AzCliCapture $AzArguments + } catch { + $message = $_.Exception.Message + if ($attempt -lt $MaxRetries -and $message -match 'ServerIsBusy|server .* is busy processing another operation') { + Warn "Azure reported the PostgreSQL server is busy. Retrying in $DelaySeconds seconds (attempt $attempt of $MaxRetries)..." + Start-Sleep -Seconds $DelaySeconds + continue + } + + throw + } + } +} + +function Resolve-PsqlPath() { + if ($script:PsqlPath -and (Test-Path $script:PsqlPath)) { + return $script:PsqlPath + } + + $cmd = Get-Command psql -ErrorAction SilentlyContinue + if ($cmd) { + $script:PsqlPath = $cmd.Source + return $script:PsqlPath + } + + $candidatePaths = @() + foreach ($root in @($env:ProgramFiles, ${env:ProgramFiles(x86)})) { + if (-not $root) { continue } + $postgresRoot = Join-Path $root 'PostgreSQL' + if (-not (Test-Path $postgresRoot)) { continue } + + $candidatePaths += Get-ChildItem -Path $postgresRoot -Directory -ErrorAction SilentlyContinue | + Sort-Object Name -Descending | + ForEach-Object { Join-Path $_.FullName 'bin\psql.exe' } + } + + $resolved = $candidatePaths | Where-Object { Test-Path $_ } | Select-Object -First 1 + if ($resolved) { + $script:PsqlPath = $resolved + } + + return $script:PsqlPath +} function Ensure-Psql([bool]$allowInstall) { - if (Get-Command psql -ErrorAction SilentlyContinue) { + if (Resolve-PsqlPath) { return $true } if (-not $allowInstall) { - Warn "psql not found. Install PostgreSQL client tools or set POSTGRES_ALLOW_PSQL_INSTALL=true." + Warn "psql not found. Set POSTGRES_ALLOW_PSQL_INSTALL=true to install it automatically, or allow the portable Npgsql fallback to run." return $false } Warn "psql not found. Attempting to install PostgreSQL client tools via winget..." try { - & winget install --id PostgreSQL.PostgreSQL -e --source winget --accept-package-agreements --accept-source-agreements 1>$null + & winget install --id PostgreSQL.PostgreSQL.16 -e --source winget --accept-package-agreements --accept-source-agreements 1>$null } catch { Warn "winget failed to install PostgreSQL client tools." return $false } - return [bool](Get-Command psql -ErrorAction SilentlyContinue) + return [bool](Resolve-PsqlPath) +} + +function Ensure-Npgsql() { + if ($script:NpgsqlReady) { + return $true + } + + if ($PSVersionTable.PSVersion.Major -lt 7) { + Warn "Portable Npgsql fallback requires pwsh 7 or later." + return $false + } + + try { + $cacheRoot = Join-Path $env:LOCALAPPDATA "DeployYourAIApplicationInProduction\tools\npgsql\$($script:NpgsqlPackageVersion)" + $nugetExe = Join-Path $cacheRoot 'nuget.exe' + $restoreMarker = Join-Path $cacheRoot '.restored' + + if (-not (Test-Path $restoreMarker)) { + New-Item -ItemType Directory -Path $cacheRoot -Force | Out-Null + + if (-not (Test-Path $nugetExe)) { + Invoke-WebRequest -Uri 'https://dist.nuget.org/win-x86-commandline/latest/nuget.exe' -OutFile $nugetExe + } + + & $nugetExe install Npgsql -Version $script:NpgsqlPackageVersion -OutputDirectory $cacheRoot -Framework net8.0 -DirectDownload -NonInteractive 1>$null + if ($LASTEXITCODE -ne 0) { + throw "nuget.exe failed to restore Npgsql dependencies." + } + + New-Item -ItemType File -Path $restoreMarker -Force | Out-Null + } + + $runtimeDlls = Get-ChildItem -Path $cacheRoot -Recurse -Filter *.dll | Where-Object { + $_.FullName -match '[\\/]lib[\\/]net8\.0[\\/]' + } + + if (-not $runtimeDlls) { + throw 'Portable Npgsql restore did not produce any net8.0 assemblies.' + } + + foreach ($dll in ($runtimeDlls | Where-Object Name -ne 'Npgsql.dll' | Sort-Object FullName)) { + try { + Add-Type -Path $dll.FullName -ErrorAction Stop + } catch { + if ($_.Exception.Message -notmatch 'already loaded|Duplicate type name') { + throw + } + } + } + + $npgsqlDll = $runtimeDlls | Where-Object Name -eq 'Npgsql.dll' | Select-Object -First 1 + if (-not $npgsqlDll) { + throw 'Portable Npgsql restore did not produce Npgsql.dll.' + } + + try { + Add-Type -Path $npgsqlDll.FullName -ErrorAction Stop + } catch { + if ($_.Exception.Message -notmatch 'already loaded|Duplicate type name') { + throw + } + } + + $script:NpgsqlReady = $true + return $true + } catch { + Warn "Portable Npgsql fallback failed: $($_.Exception.Message)" + return $false + } } if ($env:AZURE_OUTPUTS_JSON) { try { @@ -79,17 +251,47 @@ if ($env:AZURE_OUTPUTS_JSON) { if ($out.postgreSqlFabricUserSecretNameOut -and $out.postgreSqlFabricUserSecretNameOut.value) { $postgreSqlFabricUserSecretName = $out.postgreSqlFabricUserSecretNameOut.value } if ($out.keyVaultResourceId -and $out.keyVaultResourceId.value) { $keyVaultResourceId = $out.keyVaultResourceId.value } if ($out.postgreSqlFabricUserNameOut -and $out.postgreSqlFabricUserNameOut.value -and (-not $FabricUserName)) { $FabricUserName = $out.postgreSqlFabricUserNameOut.value } + if ($out.postgreSqlMirrorConnectionModeOut -and $out.postgreSqlMirrorConnectionModeOut.value -and (-not $MirrorConnectionMode)) { $MirrorConnectionMode = $out.postgreSqlMirrorConnectionModeOut.value } } catch {} } function Get-AzdEnvValue([string]$key){ try { $val = & azd env get-value $key 2>$null - if ($val -and -not ($val -match '^\s*ERROR:')) { return $val.ToString().Trim() } + if ($LASTEXITCODE -eq 0 -and $val -and -not ($val -match '^\s*ERROR:')) { return $val.ToString().Trim() } } catch {} return $null } +function Resolve-PrimaryResource { + param( + [string]$ResourceType, + [string]$ResourceGroup, + [string]$SubscriptionId + ) + + if ([string]::IsNullOrWhiteSpace($ResourceGroup)) { return $null } + + try { + $args = @('resource', 'list', '--resource-group', $ResourceGroup, '--query', "[?type=='$ResourceType'].{id:id,name:name}", '-o', 'json') + if ($SubscriptionId) { $args += @('--subscription', $SubscriptionId) } + $json = & az @args 2>$null + if ($LASTEXITCODE -ne 0 -or -not $json) { return $null } + + $resources = @($json | ConvertFrom-Json -ErrorAction Stop) + if (-not $resources) { return $null } + + if ($ResourceType -eq 'Microsoft.KeyVault/vaults') { + $preferred = $resources | Where-Object { $_.name -notlike 'kv-ai-*' } | Select-Object -First 1 + if ($preferred) { return $preferred } + } + + return $resources | Select-Object -First 1 + } catch { + return $null + } +} + if (-not $postgreSqlServerResourceId) { $postgreSqlServerResourceId = Get-AzdEnvValue 'postgreSqlServerResourceId' } if (-not $postgreSqlServerName) { $postgreSqlServerName = Get-AzdEnvValue 'postgreSqlServerNameOut' } if (-not $postgreSqlAdminLogin) { $postgreSqlAdminLogin = Get-AzdEnvValue 'postgreSqlAdminLoginOut' } @@ -97,6 +299,25 @@ if (-not $postgreSqlAdminSecretName) { $postgreSqlAdminSecretName = Get-AzdEnvVa if (-not $postgreSqlFabricUserSecretName) { $postgreSqlFabricUserSecretName = Get-AzdEnvValue 'postgreSqlFabricUserSecretNameOut' } if (-not $keyVaultResourceId) { $keyVaultResourceId = Get-AzdEnvValue 'keyVaultResourceId' } if (-not $FabricUserName) { $FabricUserName = Get-AzdEnvValue 'postgreSqlFabricUserNameOut' } +if (-not $MirrorConnectionMode) { $MirrorConnectionMode = Get-AzdEnvValue 'postgreSqlMirrorConnectionModeOut' } + +$subscriptionId = $env:AZURE_SUBSCRIPTION_ID +if (-not $subscriptionId) { $subscriptionId = Get-AzdEnvValue 'AZURE_SUBSCRIPTION_ID' } +$resourceGroupFromEnv = $env:AZURE_RESOURCE_GROUP +if (-not $resourceGroupFromEnv) { $resourceGroupFromEnv = Get-AzdEnvValue 'AZURE_RESOURCE_GROUP' } + +if (-not $postgreSqlServerResourceId) { + $pgResource = Resolve-PrimaryResource -ResourceType 'Microsoft.DBforPostgreSQL/flexibleServers' -ResourceGroup $resourceGroupFromEnv -SubscriptionId $subscriptionId + if ($pgResource) { + $postgreSqlServerResourceId = $pgResource.id + if (-not $postgreSqlServerName) { $postgreSqlServerName = $pgResource.name } + } +} + +if (-not $keyVaultResourceId) { + $kvResource = Resolve-PrimaryResource -ResourceType 'Microsoft.KeyVault/vaults' -ResourceGroup $resourceGroupFromEnv -SubscriptionId $subscriptionId + if ($kvResource) { $keyVaultResourceId = $kvResource.id } +} if (-not $postgreSqlServerResourceId) { Warn "PostgreSQL server outputs not found; skipping mirroring prep." @@ -104,10 +325,20 @@ if (-not $postgreSqlServerResourceId) { } if (-not $DatabaseName) { $DatabaseName = 'postgres' } +if ([string]::IsNullOrWhiteSpace($postgreSqlAdminLogin)) { $postgreSqlAdminLogin = 'pgadmin' } +if ([string]::IsNullOrWhiteSpace($postgreSqlAdminSecretName)) { $postgreSqlAdminSecretName = 'postgres-admin-password' } if (-not $FabricUserName) { $FabricUserName = 'fabric_user' } if ($EntraRoleName) { $FabricUserName = $EntraRoleName } +if (-not $MirrorConnectionMode) { $MirrorConnectionMode = 'fabricUser' } +$MirrorConnectionMode = $MirrorConnectionMode.Trim() +if ($MirrorConnectionMode -notin @('fabricUser', 'admin')) { + Warn "Unsupported PostgreSQL mirror connection mode '$MirrorConnectionMode'. Use 'fabricUser' or 'admin'." + exit 1 +} +$useAdminForMirrorConnection = $MirrorConnectionMode -eq 'admin' +$useEntra = (-not [string]::IsNullOrWhiteSpace($EntraRoleName)) -or (-not [string]::IsNullOrWhiteSpace($EntraObjectId)) if ([string]::IsNullOrWhiteSpace($FabricUserName) -or ($FabricUserName -notmatch '^[a-zA-Z0-9_]+$')) { - if (-not $EntraRoleName) { + if (-not $EntraRoleName -and -not $useAdminForMirrorConnection) { Warn "Invalid Fabric user name '$FabricUserName'. Use only letters, numbers, and underscore." exit 1 } @@ -125,9 +356,23 @@ $subscriptionId = $parts[1] $resourceGroup = $parts[3] if (-not $postgreSqlServerName) { $postgreSqlServerName = $parts[7] } +$serverState = $null +try { + $serverStateJson = Invoke-AzCliCapture @('postgres','flexible-server','show','-g', $resourceGroup,'-n', $postgreSqlServerName,'--subscription', $subscriptionId,'-o','json') + if ($serverStateJson) { + $serverState = $serverStateJson | ConvertFrom-Json -ErrorAction Stop + } +} catch { + Warn "Unable to read PostgreSQL server state before mirroring prep." +} + +if ($serverState -and [string]::IsNullOrWhiteSpace($serverState.administratorLogin)) { + Fail "PostgreSQL server '$postgreSqlServerName' was created without an administrator login. Password authentication cannot be enabled in-place on this server. Redeploy the server with postgreSqlAuthConfig.passwordAuth='Enabled' and a non-empty postgreSqlAdminLogin so Fabric mirroring can be configured automatically." +} + Log "Ensuring PostgreSQL auth modes (password + Entra) are enabled..." try { - az postgres flexible-server update -g $resourceGroup -n $postgreSqlServerName --subscription $subscriptionId --microsoft-entra-auth Enabled --password-auth Enabled 1>$null + Invoke-AzCli @('postgres','flexible-server','update','-g', $resourceGroup,'-n', $postgreSqlServerName,'--subscription', $subscriptionId,'--microsoft-entra-auth','Enabled','--password-auth','Enabled') } catch { Warn "Failed to enable PostgreSQL password/Entra auth modes. Configure the server Authentication settings in the portal and retry." throw @@ -142,7 +387,7 @@ if ($keyVaultResourceId) { function Test-KeyVaultAccess([string]$vaultName) { try { - $null = az keyvault secret list --vault-name $vaultName --maxresults 1 --query "[0].id" -o tsv 2>$null + $null = Invoke-AzCliCapture @('keyvault','secret','list','--vault-name', $vaultName,'--maxresults','1','--query','[0].id','-o','tsv') return $true } catch { return $false @@ -154,50 +399,190 @@ $tempEnableKvPublicAccess = IsTrue $TempEnableKeyVaultPublicAccess function Set-KeyVaultPublicAccess([string]$vaultName, [string]$state) { if (-not $vaultName) { return } try { - az keyvault update -n $vaultName --public-network-access $state 1>$null + Invoke-AzCli @('keyvault','update','-n', $vaultName,'--public-network-access', $state) } catch { Warn "Failed to set Key Vault public network access to '$state' for $vaultName." throw } } -function Invoke-AzCli([string[]]$Args) { - $azCmd = $null - try { $azCmd = (Get-Command az -ErrorAction Stop).Source } catch { $azCmd = $null } - if ($azCmd -and $azCmd.ToLowerInvariant().EndsWith('.cmd')) { - $cliRoot = Split-Path (Split-Path $azCmd -Parent) -Parent - $pythonExe = Join-Path $cliRoot 'python.exe' - if (Test-Path $pythonExe) { - & $pythonExe -m azure.cli @Args - return - } +function Get-PublicClientIp() { + $candidates = @( + 'https://api.ipify.org', + 'https://ifconfig.me/ip', + 'https://icanhazip.com' + ) + + foreach ($candidate in $candidates) { + try { + $value = Invoke-RestMethod -Uri $candidate -TimeoutSec 10 + $ip = $value.ToString().Trim() + if ($ip -match '^\d{1,3}(\.\d{1,3}){3}$') { + return $ip + } + } catch {} + } + + return $null +} + +function Add-PostgreSqlFirewallRule([string]$resourceGroupName, [string]$serverName, [string]$ruleName, [string]$ipAddress, [string]$subscription) { + if ([string]::IsNullOrWhiteSpace($resourceGroupName) -or [string]::IsNullOrWhiteSpace($serverName) -or [string]::IsNullOrWhiteSpace($ruleName) -or [string]::IsNullOrWhiteSpace($ipAddress)) { + return + } + + Invoke-AzCli @('postgres','flexible-server','firewall-rule','create','--resource-group', $resourceGroupName,'--name', $serverName,'--rule-name', $ruleName,'--start-ip-address', $ipAddress,'--end-ip-address', $ipAddress,'--subscription', $subscription) +} + +function Remove-PostgreSqlFirewallRule([string]$resourceGroupName, [string]$serverName, [string]$ruleName, [string]$subscription) { + if ([string]::IsNullOrWhiteSpace($resourceGroupName) -or [string]::IsNullOrWhiteSpace($serverName) -or [string]::IsNullOrWhiteSpace($ruleName)) { + return } - & az @Args + + & az postgres flexible-server firewall-rule delete --resource-group $resourceGroupName --name $serverName --rule-name $ruleName --subscription $subscription --yes 1>$null 2>$null } function Invoke-PostgresSql([string]$sqlText) { if ($script:sqlExecutionMode -eq 'az') { - Invoke-AzCli @('postgres','flexible-server','execute','-n', $postgreSqlServerName,'-g', $resourceGroup,'-u', $postgreSqlAdminLogin,'-p', $adminPassword,'-d', $DatabaseName,'-q', $sqlText,'--subscription', $subscriptionId) 1>$null + $queryText = Convert-SqlToAzQueryText $sqlText + Invoke-AzCli @('postgres','flexible-server','execute','--name', $postgreSqlServerName,'--admin-user', $postgreSqlAdminLogin,'--admin-password', $adminPassword,'--database-name', $DatabaseName,'--querytext', $queryText,'--subscription', $subscriptionId) 1>$null return } + if ($script:sqlExecutionMode -eq 'npgsql') { + $fqdn = "$postgreSqlServerName.postgres.database.azure.com" + $connString = "Host=$fqdn;Port=5432;Database=$DatabaseName;Username=$postgreSqlAdminLogin;Password=$adminPassword;SSL Mode=Require;Trust Server Certificate=true" + $conn = [Npgsql.NpgsqlConnection]::new($connString) + try { + $conn.Open() + $cmd = $conn.CreateCommand() + $cmd.CommandText = $sqlText + [void]$cmd.ExecuteNonQuery() + return + } finally { + $conn.Dispose() + } + } + $fqdn = "$postgreSqlServerName.postgres.database.azure.com" - $pgUser = "$postgreSqlAdminLogin@$postgreSqlServerName" + $pgUser = $postgreSqlAdminLogin $env:PGPASSWORD = $adminPassword $pgConn = "host=$fqdn port=5432 dbname=$DatabaseName sslmode=require" - & psql -d $pgConn -U $pgUser -v ON_ERROR_STOP=1 -c $sqlText 1>$null + & $script:PsqlPath -d $pgConn -U $pgUser -v ON_ERROR_STOP=1 -c $sqlText 1>$null + if ($LASTEXITCODE -ne 0) { + throw "psql command failed with exit code $LASTEXITCODE for admin user '$postgreSqlAdminLogin'." + } } -$hasRdbmsConnect = Ensure-AzExtension 'rdbms-connect' -if (-not $hasRdbmsConnect) { - $allowPsqlInstall = IsTrue ($env:POSTGRES_ALLOW_PSQL_INSTALL) - if (-not $env:POSTGRES_ALLOW_PSQL_INSTALL) { $allowPsqlInstall = $true } - if (-not (Ensure-Psql $allowPsqlInstall)) { - exit 1 +function Invoke-PostgresSqlAsUser([string]$userName, [string]$userPassword, [string]$sqlText) { + if ([string]::IsNullOrWhiteSpace($userName) -or [string]::IsNullOrWhiteSpace($userPassword)) { + throw "User credentials are required to execute SQL as user." + } + + if ($script:sqlExecutionMode -eq 'az') { + $queryText = Convert-SqlToAzQueryText $sqlText + Invoke-AzCli @('postgres','flexible-server','execute','--name', $postgreSqlServerName,'--admin-user', $userName,'--admin-password', $userPassword,'--database-name', $DatabaseName,'--querytext', $queryText,'--subscription', $subscriptionId) 1>$null + return + } + + if ($script:sqlExecutionMode -eq 'npgsql') { + $fqdn = "$postgreSqlServerName.postgres.database.azure.com" + $connString = "Host=$fqdn;Port=5432;Database=$DatabaseName;Username=$userName;Password=$userPassword;SSL Mode=Require;Trust Server Certificate=true" + $conn = [Npgsql.NpgsqlConnection]::new($connString) + try { + $conn.Open() + $cmd = $conn.CreateCommand() + $cmd.CommandText = $sqlText + [void]$cmd.ExecuteNonQuery() + return + } finally { + $conn.Dispose() + } + } + + $fqdn = "$postgreSqlServerName.postgres.database.azure.com" + $pgUser = $userName + $env:PGPASSWORD = $userPassword + $pgConn = "host=$fqdn port=5432 dbname=$DatabaseName sslmode=require" + & $script:PsqlPath -d $pgConn -U $pgUser -v ON_ERROR_STOP=1 -c $sqlText 1>$null + if ($LASTEXITCODE -ne 0) { + throw "psql command failed with exit code $LASTEXITCODE for user '$userName'." } } -$script:sqlExecutionMode = if ($hasRdbmsConnect) { 'az' } else { 'psql' } +function Test-PostgresCredential([string]$userName, [string]$userPassword) { + $script:LastPostgresCredentialError = $null + if ([string]::IsNullOrWhiteSpace($userName) -or [string]::IsNullOrWhiteSpace($userPassword)) { + $script:LastPostgresCredentialError = 'Missing PostgreSQL username or password.' + return $false + } + + $fqdn = "$postgreSqlServerName.postgres.database.azure.com" + + if ($script:sqlExecutionMode -eq 'az') { + try { + Invoke-AzCli @('postgres','flexible-server','execute','--name', $postgreSqlServerName,'--admin-user', $userName,'--admin-password', $userPassword,'--database-name', $DatabaseName,'--querytext', 'select 1;','--subscription', $subscriptionId) 1>$null + return $true + } catch { + $script:LastPostgresCredentialError = $_.Exception.Message + return $false + } + } + + if ($script:sqlExecutionMode -eq 'npgsql') { + $connString = "Host=$fqdn;Port=5432;Database=$DatabaseName;Username=$userName;Password=$userPassword;SSL Mode=Require;Trust Server Certificate=true" + $conn = [Npgsql.NpgsqlConnection]::new($connString) + try { + $conn.Open() + return $true + } catch { + $script:LastPostgresCredentialError = $_.Exception.Message + return $false + } finally { + $conn.Dispose() + } + } + + if ($script:sqlExecutionMode -eq 'psql') { + $env:PGPASSWORD = $userPassword + $pgConn = "host=$fqdn port=5432 dbname=$DatabaseName sslmode=require" + & $script:PsqlPath -d $pgConn -U $userName -v ON_ERROR_STOP=1 -c 'select 1;' 1>$null 2>$null + if ($LASTEXITCODE -eq 0) { + return $true + } + + $script:LastPostgresCredentialError = "psql exited with code $LASTEXITCODE while testing PostgreSQL connectivity." + return $false + } + + $script:LastPostgresCredentialError = 'No supported PostgreSQL credential validation backend is available.' + return $false +} + +$allowPsqlInstall = IsTrue ($env:POSTGRES_ALLOW_PSQL_INSTALL) +if (Ensure-AzExtension 'rdbms-connect') { + $script:sqlExecutionMode = 'az' +} elseif (Ensure-Npgsql) { + $script:sqlExecutionMode = 'npgsql' +} elseif (Ensure-Psql $allowPsqlInstall) { + $script:sqlExecutionMode = 'psql' +} else { + Fail 'No supported PostgreSQL SQL execution backend is available. Install the Azure CLI rdbms-connect extension, make psql available, or allow the portable Npgsql fallback to restore dependencies.' +} + +$temporaryClientFirewallRuleName = 'AllowCurrentClientFabricMirrorPrep' +$temporaryClientFirewallIp = $null +$temporaryClientFirewallRuleAdded = $false +if ($serverState -and $serverState.network -and $serverState.network.publicNetworkAccess -eq 'Enabled') { + $temporaryClientFirewallIp = Get-PublicClientIp + if ($temporaryClientFirewallIp) { + Log "Temporarily allowing current client IP $temporaryClientFirewallIp to reach PostgreSQL for mirroring preparation..." + Add-PostgreSqlFirewallRule -resourceGroupName $resourceGroup -serverName $postgreSqlServerName -ruleName $temporaryClientFirewallRuleName -ipAddress $temporaryClientFirewallIp -subscription $subscriptionId + $temporaryClientFirewallRuleAdded = $true + } else { + Warn 'Unable to determine the current public client IP. Mirroring preparation may fail if the server firewall does not already allow this host.' + } +} # Fetch admin password from Key Vault or environment $adminPassword = $null @@ -209,50 +594,57 @@ try { if ($keyVaultName -and $postgreSqlAdminSecretName) { try { - $adminPassword = az keyvault secret show --vault-name $keyVaultName --name $postgreSqlAdminSecretName --query value -o tsv 2>$null + $adminPassword = Invoke-AzCliCapture @('keyvault','secret','show','--vault-name', $keyVaultName,'--name', $postgreSqlAdminSecretName,'--query','value','-o','tsv') } catch {} } if (-not $adminPassword) { $adminPassword = $env:POSTGRES_ADMIN_PASSWORD } if (-not $adminPassword) { - if (-not $keyVaultName) { - Warn "PostgreSQL admin password not found (Key Vault or env)." - exit 1 - } - if (-not (Test-KeyVaultAccess $keyVaultName)) { + if ($keyVaultName -and (-not (Test-KeyVaultAccess $keyVaultName))) { Warn "Key Vault '$keyVaultName' is not reachable. Run from a VNet-connected host or enable trusted access, then retry." exit 1 } - $bytes = New-Object byte[] 32 - [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) - $adminPassword = ([Convert]::ToBase64String($bytes).TrimEnd('=')) + 'a!' + Fail "PostgreSQL admin password was not found in Key Vault secret '$postgreSqlAdminSecretName' or POSTGRES_ADMIN_PASSWORD. Provisioning is expected to create this credential; mirroring prep will not generate or rotate it." + } + + if (-not (Test-PostgresCredential -userName $postgreSqlAdminLogin -userPassword $adminPassword)) { + if ($script:LastPostgresCredentialError -match 'Connection timed out|timed out|timeout|failed to respond|No such host is known|Unable to connect|connection attempt failed') { + Fail "Unable to validate the PostgreSQL admin credential because this machine cannot reach '$postgreSqlServerName.postgres.database.azure.com' on port 5432 using the current DNS/network path. Run mirroring prep from a VNet-connected host or a client that can reach the server directly, then rerun the script. The stored secret '$postgreSqlAdminSecretName' was not rotated." + } - Log "Resetting PostgreSQL admin password and storing in Key Vault..." - az postgres flexible-server update -g $resourceGroup -n $postgreSqlServerName --admin-password "$adminPassword" --subscription $subscriptionId 1>$null - az keyvault secret set --vault-name $keyVaultName --name $postgreSqlAdminSecretName --value $adminPassword 1>$null + Fail "The stored PostgreSQL admin credential for '$postgreSqlAdminLogin' is out of sync with the server. Mirroring prep will not rotate it automatically because provisioning already owns that credential. Sync the existing secret '$postgreSqlAdminSecretName' with the live server password, then rerun mirroring prep." } - # Ensure Fabric role password in Key Vault - if (-not $postgreSqlFabricUserSecretName) { $postgreSqlFabricUserSecretName = 'postgres-fabric-user-password' } $fabricUserPassword = $null - if ($keyVaultName) { - try { - $fabricUserPassword = az keyvault secret show --vault-name $keyVaultName --name $postgreSqlFabricUserSecretName --query value -o tsv 2>$null - } catch {} - } - if (-not $fabricUserPassword) { - $bytes = New-Object byte[] 32 - [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) - $fabricUserPassword = ([Convert]::ToBase64String($bytes).TrimEnd('=')) + 'a!' + if (-not $useEntra) { + # Always provision the dedicated Fabric user so create_postgresql_mirror.ps1 can + # fall back to MD5-based auth when the admin account is unsuitable for Fabric. + if (-not $postgreSqlFabricUserSecretName) { $postgreSqlFabricUserSecretName = 'postgres-fabric-user-password' } if ($keyVaultName) { try { - az keyvault secret set --vault-name $keyVaultName --name $postgreSqlFabricUserSecretName --value $fabricUserPassword 1>$null - Log "Stored Fabric user password in Key Vault: $postgreSqlFabricUserSecretName" - } catch { - Warn "Failed to store Fabric user password in Key Vault." + $fabricUserPassword = Invoke-AzCliCapture @('keyvault','secret','show','--vault-name', $keyVaultName,'--name', $postgreSqlFabricUserSecretName,'--query','value','-o','tsv') + if ($fabricUserPassword) { + Log "Using Fabric user password from Key Vault secret: $postgreSqlFabricUserSecretName" + } + } catch {} + } + if (-not $fabricUserPassword) { + $bytes = New-Object byte[] 32 + [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) + $fabricUserPassword = ([Convert]::ToBase64String($bytes).TrimEnd('=')) + 'a!' + if ($keyVaultName) { + try { + Invoke-AzCli @('keyvault','secret','set','--vault-name', $keyVaultName,'--name', $postgreSqlFabricUserSecretName,'--value', $fabricUserPassword) + Log "Stored Fabric user password in Key Vault: $postgreSqlFabricUserSecretName" + } catch { + Fail "Failed to store Fabric user password in Key Vault. Refusing to continue because the server password must not change without Key Vault remaining in sync." + } } } } + if ($useAdminForMirrorConnection) { + Log "PostgreSQL mirror connection mode is 'admin'. Demo mode will prefer the admin credential, but a dedicated MD5-auth Fabric user will also be maintained as a fallback." + } } finally { if ($tempEnableKvPublicAccess -and $keyVaultName) { Log "Restoring Key Vault public access to Disabled..." @@ -266,24 +658,32 @@ $needsRestart = $false function Get-ParamValue([string]$paramName) { try { - $val = az postgres flexible-server parameter show -g $resourceGroup -s $postgreSqlServerName -n $paramName --query value -o tsv --subscription $subscriptionId 2>$null + $val = Invoke-AzCliCapture @('postgres','flexible-server','parameter','show','-g', $resourceGroup,'-s', $postgreSqlServerName,'-n', $paramName,'--query','value','-o','tsv','--subscription', $subscriptionId) return $val } catch { return $null } } function Get-ParamAllowedValues([string]$paramName) { try { - $val = az postgres flexible-server parameter show -g $resourceGroup -s $postgreSqlServerName -n $paramName --query "allowedValues" -o tsv --subscription $subscriptionId 2>$null + $val = Invoke-AzCliCapture @('postgres','flexible-server','parameter','show','-g', $resourceGroup,'-s', $postgreSqlServerName,'-n', $paramName,'--query','allowedValues','-o','tsv','--subscription', $subscriptionId) if ($val) { return ($val -split ',') | ForEach-Object { $_.Trim() } | Where-Object { $_ } } } catch { } return @() } +function Get-ParamDefaultValue([string]$paramName) { + try { + $val = Invoke-AzCliCapture @('postgres','flexible-server','parameter','show','-g', $resourceGroup,'-s', $postgreSqlServerName,'-n', $paramName,'--query','defaultValue','-o','tsv','--subscription', $subscriptionId) + if ($val) { return $val.ToString().Trim() } + } catch { } + return $null +} + function Set-ParamValue([string]$paramName, [string]$value, [bool]$requiresRestart) { $current = Get-ParamValue $paramName if ($current -ne $value) { Log "Setting $paramName to '$value' (was '$current')" - az postgres flexible-server parameter set -g $resourceGroup -s $postgreSqlServerName -n $paramName --value $value --subscription $subscriptionId 1>$null + Invoke-AzCli @('postgres','flexible-server','parameter','set','-g', $resourceGroup,'-s', $postgreSqlServerName,'-n', $paramName,'--value', $value,'--subscription', $subscriptionId) $script:changed = $true if ($requiresRestart) { $script:needsRestart = $true } } @@ -302,17 +702,30 @@ if ($enableFabricMirroring) { $maxWorkers = Get-ParamValue 'max_worker_processes' if ($maxWorkers -and $maxWorkers -as [int]) { $currentWorkers = [int]$maxWorkers - $targetWorkers = $currentWorkers + (3 * $MirrorCount) + $defaultWorkersValue = Get-ParamDefaultValue 'max_worker_processes' + $minimumWorkers = $currentWorkers + if ($defaultWorkersValue -and ($defaultWorkersValue -as [int])) { + $minimumWorkers = [int]$defaultWorkersValue + (3 * $MirrorCount) + } + $targetWorkers = [Math]::Max($currentWorkers, $minimumWorkers) Set-ParamValue -paramName 'max_worker_processes' -value $targetWorkers.ToString() -requiresRestart $true } if ($changed -and $needsRestart) { Log "Restarting PostgreSQL server to apply mirroring settings..." - az postgres flexible-server restart -g $resourceGroup -n $postgreSqlServerName --subscription $subscriptionId 1>$null + Invoke-AzCli @('postgres','flexible-server','restart','-g', $resourceGroup,'-n', $postgreSqlServerName,'--subscription', $subscriptionId) +} + +if ($enableFabricMirroring) { + try { + Log "Invoking Azure-side Fabric mirroring enablement for database '$DatabaseName'..." + [void](Invoke-AzCliWithServerBusyRetry @('postgres','flexible-server','fabric-mirroring','start','-g', $resourceGroup,'-s', $postgreSqlServerName,'--database-names', $DatabaseName,'--subscription', $subscriptionId,'-y')) + } catch { + Warn "Azure-side Fabric mirroring enablement did not complete automatically. Continuing with local role and grant preparation." + } } # Configure role and grants -$useEntra = (-not [string]::IsNullOrWhiteSpace($EntraRoleName)) -or (-not [string]::IsNullOrWhiteSpace($EntraObjectId)) $mfaFlag = if ($EntraRequireMfa -and $EntraRequireMfa.ToLowerInvariant() -eq 'true') { 'true' } else { 'false' } if ($useEntra) { @@ -331,16 +744,29 @@ if ($useEntra) { ('GRANT azure_cdc_admin TO "{0}";' -f $EntraRoleName), ('GRANT CREATE ON DATABASE "{0}" TO "{1}";' -f $DatabaseName, $EntraRoleName), ('GRANT USAGE ON SCHEMA public TO "{0}";' -f $EntraRoleName), + ('GRANT CREATE ON SCHEMA public TO "{0}";' -f $EntraRoleName), ('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "{0}";' -f $EntraRoleName) ) | Where-Object { $_ } $grantSql = if ($grantParts) { [string]::Join(' ', $grantParts) } else { '' } } else { - $escapedPassword = $fabricUserPassword.Replace("'", "''") - $createRoleSql = ('CREATE ROLE "{0}" CREATEDB CREATEROLE LOGIN REPLICATION PASSWORD ''{1}'';' -f $FabricUserName, $escapedPassword) + $ensureRoleSql = @' +DO $do$ +BEGIN + PERFORM set_config($q$password_encryption$q$, $q$md5$q$, true); + IF EXISTS ( + SELECT 1 FROM pg_roles WHERE rolname = $q${0}$q$ + ) THEN + ALTER ROLE "{0}" WITH CREATEDB CREATEROLE LOGIN REPLICATION PASSWORD $pwd${1}$pwd$; + ELSE + CREATE ROLE "{0}" CREATEDB CREATEROLE LOGIN REPLICATION PASSWORD $pwd${1}$pwd$; + END IF; +END $do$; +'@ -f $FabricUserName, $fabricUserPassword $grantParts = @( ('GRANT azure_cdc_admin TO "{0}";' -f $FabricUserName), ('GRANT CREATE ON DATABASE "{0}" TO "{1}";' -f $DatabaseName, $FabricUserName), ('GRANT USAGE ON SCHEMA public TO "{0}";' -f $FabricUserName), + ('GRANT CREATE ON SCHEMA public TO "{0}";' -f $FabricUserName), ('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "{0}";' -f $FabricUserName) ) | Where-Object { $_ } $grantSql = if ($grantParts) { [string]::Join(' ', $grantParts) } else { '' } @@ -349,19 +775,75 @@ if ($useEntra) { Log "Creating/validating Fabric mirroring role in database '$DatabaseName'..." try { if ($useEntra) { - Invoke-AzCli @('postgres','flexible-server','execute','-n', $postgreSqlServerName,'-g', $resourceGroup,'-u', $postgreSqlAdminLogin,'-p', $adminPassword,'-d', $DatabaseName,'-q', $createPrincipalSql,'--subscription', $subscriptionId) 1>$null + Invoke-PostgresSql $createPrincipalSql } else { - try { - Invoke-AzCli @('postgres','flexible-server','execute','-n', $postgreSqlServerName,'-g', $resourceGroup,'-u', $postgreSqlAdminLogin,'-p', $adminPassword,'-d', $DatabaseName,'-q', $createRoleSql,'--subscription', $subscriptionId) 1>$null - } catch { - $msg = $_.Exception.Message - if ($msg -notmatch 'already exists') { throw } - Log "Fabric role already exists; continuing." - } + Invoke-PostgresSql $ensureRoleSql } if ($grantSql) { - Invoke-AzCli @('postgres','flexible-server','execute','-n', $postgreSqlServerName,'-g', $resourceGroup,'-u', $postgreSqlAdminLogin,'-p', $adminPassword,'-d', $DatabaseName,'-q', $grantSql,'--subscription', $subscriptionId) 1>$null + Invoke-PostgresSql $grantSql + } + if ($useAdminForMirrorConnection) { + Log "Using PostgreSQL admin login '$postgreSqlAdminLogin' as the preferred Fabric mirror connection identity." } + $verifyRoleSql = if ($useAdminForMirrorConnection) { +@' +DO $do$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_roles WHERE rolname = $q${0}$q$ AND rolcanlogin + ) THEN + RAISE EXCEPTION $msg$PostgreSQL admin role missing or cannot login: {0}$msg$; + END IF; +END $do$; +'@ -f $postgreSqlAdminLogin + } elseif ($useEntra) { +@' +DO $do$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_roles WHERE rolname = $q${0}$q$ AND rolcanlogin + ) THEN + RAISE EXCEPTION $msg$Fabric mirroring role missing or cannot login: {0}$msg$; + END IF; + IF NOT pg_has_role($q${0}$q$, $q$azure_cdc_admin$q$, $q$member$q$) THEN + RAISE EXCEPTION $msg$Fabric mirroring role missing azure_cdc_admin membership: {0}$msg$; + END IF; + IF NOT has_database_privilege($q${0}$q$, $q${1}$q$, $q$CREATE$q$) THEN + RAISE EXCEPTION $msg$Fabric mirroring role missing CREATE privilege on database {1}: {0}$msg$; + END IF; + IF NOT has_schema_privilege($q${0}$q$, $q$public$q$, $q$CREATE$q$) THEN + RAISE EXCEPTION $msg$Fabric mirroring role missing CREATE privilege on schema public: {0}$msg$; + END IF; +END $do$; +'@ -f $EntraRoleName, $DatabaseName + } else { +@' +DO $do$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_roles WHERE rolname = $q${0}$q$ AND rolcanlogin + ) THEN + RAISE EXCEPTION $msg$Fabric mirroring role missing or cannot login: {0}$msg$; + END IF; + IF NOT pg_has_role($q${0}$q$, $q$azure_cdc_admin$q$, $q$member$q$) THEN + RAISE EXCEPTION $msg$Fabric mirroring role missing azure_cdc_admin membership: {0}$msg$; + END IF; + IF NOT has_database_privilege($q${0}$q$, $q${1}$q$, $q$CREATE$q$) THEN + RAISE EXCEPTION $msg$Fabric mirroring role missing CREATE privilege on database {1}: {0}$msg$; + END IF; + IF NOT has_schema_privilege($q${0}$q$, $q$public$q$, $q$CREATE$q$) THEN + RAISE EXCEPTION $msg$Fabric mirroring role missing CREATE privilege on schema public: {0}$msg$; + END IF; + IF EXISTS (SELECT 1 FROM pg_proc WHERE proname = $q$azure_roles_authtype$q$) AND NOT EXISTS ( + SELECT 1 FROM azure_roles_authtype() + WHERE rolename = $q${0}$q$ AND authtype = $q$MD5$q$ + ) THEN + RAISE EXCEPTION $msg$Fabric mirroring role is not using MD5 password auth: {0}$msg$; + END IF; +END $do$; +'@ -f $FabricUserName, $DatabaseName + } + Invoke-PostgresSql $verifyRoleSql if ($enableFabricMirroring -and $createMirrorSeedTable) { $seedTableSql = @" CREATE TABLE IF NOT EXISTS public.\"$MirrorSeedTableName\" ( @@ -372,11 +854,66 @@ INSERT INTO public.\"$MirrorSeedTableName\" (created_at) SELECT now() WHERE NOT EXISTS (SELECT 1 FROM public.\"$MirrorSeedTableName\"); "@ - Invoke-PostgresSql $seedTableSql + $ownerName = if ($useAdminForMirrorConnection) { $postgreSqlAdminLogin } elseif ($useEntra) { $EntraRoleName } else { $FabricUserName } + $createdAsFabricUser = $false + if (-not $useAdminForMirrorConnection -and -not $useEntra -and $FabricUserName -and $fabricUserPassword) { + try { + Invoke-PostgresSqlAsUser -userName $FabricUserName -userPassword $fabricUserPassword -sqlText $seedTableSql + $createdAsFabricUser = $true + } catch { + Warn "Failed to create seed table as '$FabricUserName'; falling back to admin." + } + } + + if (-not $createdAsFabricUser) { + Invoke-PostgresSql $seedTableSql + } + if ($ownerName -and -not ($createdAsFabricUser -and $ownerName -eq $FabricUserName)) { + $ownerSql = ('ALTER TABLE public."{0}" OWNER TO "{1}";' -f $MirrorSeedTableName, $ownerName) + Invoke-PostgresSql $ownerSql + } + $verifyTableSql = @' +DO $do$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.tables + WHERE table_schema = $q$public$q$ AND table_name = $q${0}$q$ + ) THEN + RAISE EXCEPTION $msg$Mirror seed table not found: public.{0}$msg$; + END IF; +END $do$; +'@ -f $MirrorSeedTableName + Invoke-PostgresSql $verifyTableSql + if ($ownerName) { + $verifyOwnerSql = @' +DO $do$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_tables + WHERE schemaname = $q$public$q$ + AND tablename = $q${0}$q$ + AND tableowner = $q${1}$q$ + ) THEN + RAISE EXCEPTION $msg$Mirror seed table owner mismatch: public.{0} owner is not {1}$msg$; + END IF; +END $do$; +'@ -f $MirrorSeedTableName, $ownerName + Invoke-PostgresSql $verifyOwnerSql + } Log "Ensured mirror seed table exists: public.$MirrorSeedTableName" } - Log "Fabric mirroring role configured." + if ($useAdminForMirrorConnection) { + Log "PostgreSQL admin demo mode configured for Fabric mirroring." + } else { + Log "Fabric mirroring role configured." + } } catch { Warn "Failed to apply SQL grants. Ensure your machine can reach the server or use a VNet gateway." + Warn "For the shortest manual fallback, see docs/postgresql_mirroring.md and start with the 'Minimal Manual Fallback' section." throw +} finally { + if ($temporaryClientFirewallRuleAdded) { + Log "Removing temporary PostgreSQL firewall rule '$temporaryClientFirewallRuleName' for client IP $temporaryClientFirewallIp..." + Remove-PostgreSqlFirewallRule -resourceGroupName $resourceGroup -serverName $postgreSqlServerName -ruleName $temporaryClientFirewallRuleName -subscription $subscriptionId + } } diff --git a/scripts/automationScripts/FabricWorkspace/mirror/run_postgresql_mirroring_followup.ps1 b/scripts/automationScripts/FabricWorkspace/mirror/run_postgresql_mirroring_followup.ps1 new file mode 100644 index 0000000..f6b9930 --- /dev/null +++ b/scripts/automationScripts/FabricWorkspace/mirror/run_postgresql_mirroring_followup.ps1 @@ -0,0 +1,59 @@ +<# +.SYNOPSIS + Runs the full PostgreSQL-to-Fabric mirroring follow-up flow from a chosen runner such as the deployed VM. + +.DESCRIPTION + Executes: + 1. Read-only preflight + 2. PostgreSQL mirroring preparation + 3. Fabric connection creation and mirror creation +#> + +[CmdletBinding()] +param( + [switch]$SkipPreflight, + [switch]$SkipPrep, + [switch]$SkipMirrorCreation +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Log([string]$m){ Write-Host "[pg-mirroring-followup] $m" } +function Fail([string]$m){ Write-Error "[pg-mirroring-followup] $m"; exit 1 } + +function Invoke-Step([string]$label, [string]$scriptPath) { + Log "Starting: $label" + & pwsh -NoProfile -File $scriptPath + if ($LASTEXITCODE -ne 0) { + Fail "$label failed with exit code $LASTEXITCODE." + } + Log "Completed: $label" +} + +$preflightScript = Join-Path $PSScriptRoot 'test_postgresql_mirroring_prereqs.ps1' +$prepScript = Join-Path $PSScriptRoot 'prepare_postgresql_for_mirroring.ps1' +$mirrorScript = Join-Path $PSScriptRoot 'create_postgresql_mirror.ps1' + +Log 'PostgreSQL mirroring follow-up started.' + +if (-not $SkipPreflight) { + Invoke-Step -label 'Mirroring preflight' -scriptPath $preflightScript +} else { + Log 'Skipping preflight by request.' +} + +if (-not $SkipPrep) { + Invoke-Step -label 'PostgreSQL mirroring preparation' -scriptPath $prepScript +} else { + Log 'Skipping mirroring preparation by request.' +} + +if (-not $SkipMirrorCreation) { + Invoke-Step -label 'Fabric connection and mirror creation' -scriptPath $mirrorScript +} else { + Log 'Skipping mirror creation by request.' +} + +Log 'PostgreSQL mirroring follow-up completed successfully.' +exit 0 \ No newline at end of file diff --git a/scripts/automationScripts/FabricWorkspace/mirror/run_postgresql_mirroring_prep_with_public_access.ps1 b/scripts/automationScripts/FabricWorkspace/mirror/run_postgresql_mirroring_prep_with_public_access.ps1 deleted file mode 100644 index 623ca09..0000000 --- a/scripts/automationScripts/FabricWorkspace/mirror/run_postgresql_mirroring_prep_with_public_access.ps1 +++ /dev/null @@ -1,75 +0,0 @@ -<# -.SYNOPSIS - Temporarily enables public access for Key Vault and PostgreSQL, runs mirroring prep, - then restores original network settings. -#> - -[CmdletBinding()] -param() - -Set-StrictMode -Version Latest -$ErrorActionPreference = 'Stop' - -function Log([string]$m){ Write-Host "[pg-mirroring-prep-wrapper] $m" } -function Warn([string]$m){ Write-Warning "[pg-mirroring-prep-wrapper] $m" } - -$rg = $env:AZURE_RESOURCE_GROUP -if (-not $rg) { $rg = 'rg-dev030826' } - -function Get-AzdEnvValue([string]$key){ - try { - $val = & azd env get-value $key 2>$null - if ($val -and -not ($val -match '^\s*ERROR:')) { return $val.ToString().Trim() } - } catch {} - return $null -} - -$kvResourceId = $null -if ($env:AZURE_OUTPUTS_JSON) { - try { - $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop - if ($out.keyVaultResourceId -and $out.keyVaultResourceId.value) { $kvResourceId = $out.keyVaultResourceId.value } - } catch {} -} -if (-not $kvResourceId) { $kvResourceId = Get-AzdEnvValue 'keyVaultResourceId' } - -if (-not $kvResourceId) { throw 'Key Vault resource ID not found.' } -$kvParts = $kvResourceId.Split('/', [System.StringSplitOptions]::RemoveEmptyEntries) -$kvName = $kvParts[7] - -$pgName = $null -if ($env:AZURE_OUTPUTS_JSON) { - try { - $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json -ErrorAction Stop - if ($out.postgreSqlServerNameOut -and $out.postgreSqlServerNameOut.value) { $pgName = $out.postgreSqlServerNameOut.value } - } catch {} -} -if (-not $pgName) { $pgName = Get-AzdEnvValue 'postgreSqlServerNameOut' } -if (-not $pgName) { $pgName = 'pg-dev030826' } - -$kvPublic = az keyvault show -g $rg -n $kvName --query "properties.publicNetworkAccess" -o tsv -$kvBypass = az keyvault show -g $rg -n $kvName --query "properties.networkAcls.bypass" -o tsv -$kvEnabledForDeployment = az keyvault show -g $rg -n $kvName --query "properties.enabledForDeployment" -o tsv - -$pgPublic = az postgres flexible-server show -g $rg -n $pgName --query "network.publicNetworkAccess" -o tsv - -Log "Key Vault: $kvName (public: $kvPublic, bypass: $kvBypass, enabledForDeployment: $kvEnabledForDeployment)" -Log "PostgreSQL: $pgName (public: $pgPublic)" - -try { - Log 'Temporarily enabling public access for Key Vault and PostgreSQL.' - az keyvault update -g $rg -n $kvName --public-network-access Enabled --bypass AzureServices 1>$null - az postgres flexible-server update -g $rg -n $pgName --public-access Enabled 1>$null - - $prepScript = Join-Path $PSScriptRoot 'prepare_postgresql_for_mirroring.ps1' - pwsh $prepScript -} finally { - Log 'Restoring original network settings.' - $restoreBypass = $kvBypass - if (-not $restoreBypass) { - $restoreBypass = ($kvEnabledForDeployment -eq 'true') ? 'AzureServices' : 'None' - } - - az postgres flexible-server update -g $rg -n $pgName --public-access $pgPublic 1>$null - az keyvault update -g $rg -n $kvName --public-network-access $kvPublic --bypass $restoreBypass 1>$null -} diff --git a/scripts/automationScripts/FabricWorkspace/mirror/test_postgresql_mirroring_prereqs.ps1 b/scripts/automationScripts/FabricWorkspace/mirror/test_postgresql_mirroring_prereqs.ps1 new file mode 100644 index 0000000..443f533 --- /dev/null +++ b/scripts/automationScripts/FabricWorkspace/mirror/test_postgresql_mirroring_prereqs.ps1 @@ -0,0 +1,200 @@ +<# +.SYNOPSIS + Read-only preflight for PostgreSQL mirroring from the current runner. + +.DESCRIPTION + Checks whether the current execution environment is likely to succeed when running + PostgreSQL mirroring preparation and Fabric mirror creation. +#> + +[CmdletBinding()] +param( + [int]$TcpTimeoutMs = 5000 +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Info([string]$m){ Write-Host "[pg-mirror-preflight] $m" } +function Pass([string]$m){ Write-Host "[PASS] $m" -ForegroundColor Green } +function Warn([string]$m){ Write-Warning "[pg-mirror-preflight] $m" } +function Fail([string]$m){ Write-Host "[FAIL] $m" -ForegroundColor Red } + +$script:CriticalFailures = 0 +$script:Warnings = 0 + +function Add-CriticalFailure([string]$message) { + $script:CriticalFailures++ + Fail $message +} + +function Add-Warning([string]$message) { + $script:Warnings++ + Warn $message +} + +function Test-CommandAvailable([string]$name) { + return [bool](Get-Command $name -ErrorAction SilentlyContinue) +} + +function Get-AzdEnvValue([string]$key) { + try { + $value = & azd env get-value $key 2>$null + if ($LASTEXITCODE -eq 0 -and $value -and -not ($value -match '^\s*ERROR:')) { + return $value.ToString().Trim() + } + } catch {} + + return $null +} + +function Test-TcpPort([string]$hostName, [int]$port, [int]$timeoutMs) { + $client = New-Object System.Net.Sockets.TcpClient + try { + $async = $client.BeginConnect($hostName, $port, $null, $null) + if (-not $async.AsyncWaitHandle.WaitOne($timeoutMs, $false)) { + return $false + } + + $client.EndConnect($async) + return $true + } catch { + return $false + } finally { + $client.Dispose() + } +} + +function Test-ResolveDns([string]$hostName) { + try { + [void][System.Net.Dns]::GetHostAddresses($hostName) + return $true + } catch { + return $false + } +} + +function Invoke-AzCliText([string[]]$args) { + $output = & az @args 2>&1 + $text = (($output | ForEach-Object { $_.ToString() }) -join [Environment]::NewLine) + return @{ + ExitCode = $LASTEXITCODE + Text = $text + } +} + +Info 'Checking local prerequisites...' + +if (Test-CommandAvailable 'az') { + Pass 'Azure CLI is available.' +} else { + Add-CriticalFailure 'Azure CLI is not available.' +} + +if (Test-CommandAvailable 'azd') { + Pass 'Azure Developer CLI is available.' +} else { + Add-CriticalFailure 'Azure Developer CLI is not available.' +} + +if ($script:CriticalFailures -gt 0) { + Info "Preflight failed with $script:CriticalFailures critical issue(s)." + exit 1 +} + +$accountCheck = Invoke-AzCliText @('account','show','--query','id','-o','tsv') +if ($accountCheck.ExitCode -eq 0 -and -not [string]::IsNullOrWhiteSpace($accountCheck.Text)) { + Pass "Azure CLI is authenticated. Subscription: $($accountCheck.Text.Trim())" +} else { + Add-CriticalFailure 'Azure CLI is not authenticated for this runner.' +} + +$environmentName = Get-AzdEnvValue 'AZURE_ENV_NAME' +if ($environmentName) { + Pass "azd environment is selected: $environmentName" +} else { + Add-CriticalFailure 'No azd environment is currently selected.' +} + +$requiredValues = @( + 'postgreSqlServerFqdn', + 'postgreSqlMirrorConnectionModeOut', + 'postgreSqlMirrorConnectionUserNameOut', + 'postgreSqlMirrorConnectionSecretNameOut', + 'keyVaultResourceId', + 'fabricWorkspaceIdOut' +) + +$resolved = @{} +foreach ($key in $requiredValues) { + $resolved[$key] = Get-AzdEnvValue $key + if ($resolved[$key]) { + Pass "Resolved azd value: $key" + } else { + Add-CriticalFailure "Required azd value is missing: $key" + } +} + +if ($script:CriticalFailures -gt 0) { + Info "Preflight failed with $script:CriticalFailures critical issue(s)." + exit 1 +} + +$postgresFqdn = $resolved['postgreSqlServerFqdn'] +$secretName = $resolved['postgreSqlMirrorConnectionSecretNameOut'] +$keyVaultResourceId = $resolved['keyVaultResourceId'] + +$resourceIdSegments = $keyVaultResourceId.Split('/', [System.StringSplitOptions]::RemoveEmptyEntries) +$keyVaultName = if ($resourceIdSegments.Length -ge 8) { $resourceIdSegments[$resourceIdSegments.Length - 1] } else { $null } + +Info 'Checking runner connectivity...' + +if (Test-ResolveDns $postgresFqdn) { + Pass "DNS resolves for PostgreSQL host: $postgresFqdn" +} else { + Add-CriticalFailure "DNS resolution failed for PostgreSQL host: $postgresFqdn" +} + +if (Test-TcpPort -hostName $postgresFqdn -port 5432 -timeoutMs $TcpTimeoutMs) { + Pass "Runner can open TCP 5432 to $postgresFqdn" +} else { + Add-CriticalFailure "Runner cannot open TCP 5432 to $postgresFqdn" +} + +Info 'Checking secret and Fabric prerequisites...' + +if ($keyVaultName) { + $kvShow = Invoke-AzCliText @('keyvault','show','--name', $keyVaultName, '--query', 'name', '-o', 'tsv') + if ($kvShow.ExitCode -eq 0) { + Pass "Key Vault metadata is reachable: $keyVaultName" + } else { + Add-CriticalFailure "Key Vault metadata is not reachable for $keyVaultName" + } + + $secretCheck = Invoke-AzCliText @('keyvault','secret','show','--vault-name', $keyVaultName, '--name', $secretName, '--query', 'id', '-o', 'tsv') + if ($secretCheck.ExitCode -eq 0 -and -not [string]::IsNullOrWhiteSpace($secretCheck.Text)) { + Pass "Mirroring secret is readable from Key Vault: $secretName" + } else { + Add-Warning "The mirroring secret is not readable from Key Vault right now. The wrapper may still succeed if it temporarily opens Key Vault public access, but this runner is not currently able to read the secret directly." + } +} else { + Add-CriticalFailure 'Unable to resolve Key Vault name from keyVaultResourceId.' +} + +$fabricTokenCheck = Invoke-AzCliText @('account','get-access-token','--resource','https://api.fabric.microsoft.com','--query','accessToken','-o','tsv') +if ($fabricTokenCheck.ExitCode -eq 0 -and -not [string]::IsNullOrWhiteSpace($fabricTokenCheck.Text)) { + Pass 'Fabric API token acquisition succeeded.' +} else { + Add-CriticalFailure 'Fabric API token acquisition failed for this runner.' +} + +Info 'Preflight summary:' +Info "Critical failures: $script:CriticalFailures" +Info "Warnings: $script:Warnings" + +if ($script:CriticalFailures -gt 0) { + exit 1 +} + +Pass 'This runner passed the critical mirroring preflight checks.' +exit 0 \ No newline at end of file diff --git a/scripts/automationScripts/OneLakeIndex/01_setup_rbac.ps1 b/scripts/automationScripts/OneLakeIndex/01_setup_rbac.ps1 index 535d01f..1507992 100644 --- a/scripts/automationScripts/OneLakeIndex/01_setup_rbac.ps1 +++ b/scripts/automationScripts/OneLakeIndex/01_setup_rbac.ps1 @@ -8,14 +8,27 @@ param( Set-StrictMode -Version Latest +function Get-AzdEnvValue { + param( + [Parameter(Mandatory = $true)] + [string]$Key + ) + + try { + $value = & azd env get-value $Key 2>$null + if ($LASTEXITCODE -ne 0 -or -not $value) { return $null } + return $value.ToString().Trim() + } catch { + return $null + } +} + # Skip when Fabric is disabled for this environment $fabricWorkspaceMode = $env:fabricWorkspaceMode if (-not $fabricWorkspaceMode) { $fabricWorkspaceMode = $env:fabricWorkspaceModeOut } if (-not $fabricWorkspaceMode) { - try { - $azdMode = & azd env get-value fabricWorkspaceModeOut 2>$null - if ($azdMode) { $fabricWorkspaceMode = $azdMode.ToString().Trim() } - } catch { } + $azdMode = Get-AzdEnvValue -Key 'fabricWorkspaceModeOut' + if ($azdMode) { $fabricWorkspaceMode = $azdMode } } if (-not $fabricWorkspaceMode -and $env:AZURE_OUTPUTS_JSON) { try { @@ -85,6 +98,17 @@ try { if (-not $aiFoundryName) { $aiFoundryName = $env_vars['aiFoundryName'] } if (-not $fabricWorkspaceName -and $outputs -and $outputs.desiredFabricWorkspaceName -and $outputs.desiredFabricWorkspaceName.value) { $fabricWorkspaceName = $outputs.desiredFabricWorkspaceName.value } if (-not $fabricWorkspaceName) { $fabricWorkspaceName = $env_vars['desiredFabricWorkspaceName'] } + if (-not $fabricWorkspaceName) { $fabricWorkspaceName = $env_vars['FABRIC_WORKSPACE_NAME'] } + if (-not $fabricWorkspaceName) { $fabricWorkspaceName = $env:FABRIC_WORKSPACE_NAME } + if (-not $fabricWorkspaceName) { $fabricWorkspaceName = Get-AzdEnvValue -Key 'FABRIC_WORKSPACE_NAME' } + if (-not $fabricWorkspaceName) { $fabricWorkspaceName = Get-AzdEnvValue -Key 'fabricWorkspaceNameOut' } + if (-not $fabricWorkspaceName) { $fabricWorkspaceName = Get-AzdEnvValue -Key 'desiredFabricWorkspaceName' } + if (-not $fabricWorkspaceName -and (Test-Path (Join-Path ([IO.Path]::GetTempPath()) 'fabric_workspace.env'))) { + Get-Content (Join-Path ([IO.Path]::GetTempPath()) 'fabric_workspace.env') | ForEach-Object { + if ($_ -match '^FABRIC_WORKSPACE_NAME=(.+)$' -and -not $fabricWorkspaceName) { $fabricWorkspaceName = $Matches[1].Trim() } + } + } + if (-not $fabricWorkspaceName -and $env:AZURE_ENV_NAME) { $fabricWorkspaceName = "workspace-$($env:AZURE_ENV_NAME.Trim())" } if (-not $aiSearchResourceId -and $outputs -and $outputs.aiSearchResourceId -and $outputs.aiSearchResourceId.value) { $aiSearchResourceId = $outputs.aiSearchResourceId.value } if (-not $aiSearchResourceId) { $aiSearchResourceId = $env_vars['aiSearchResourceId'] } diff --git a/scripts/automationScripts/OneLakeIndex/02_create_onelake_skillsets.ps1 b/scripts/automationScripts/OneLakeIndex/02_create_onelake_skillsets.ps1 index a6bf208..3b683e1 100644 --- a/scripts/automationScripts/OneLakeIndex/02_create_onelake_skillsets.ps1 +++ b/scripts/automationScripts/OneLakeIndex/02_create_onelake_skillsets.ps1 @@ -53,215 +53,7 @@ if (-not $aiSearchName -or -not $resourceGroup -or -not $subscription) { exit 1 } -function Get-SearchPublicNetworkAccess { - try { - return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "publicNetworkAccess" -o tsv - } catch { - return $null - } -} - -function Get-SearchResourceId { - try { - return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "id" -o tsv - } catch { - return $null - } -} - -function Get-ArmAccessToken { - try { - return az account get-access-token --resource https://management.azure.com/ --subscription $subscription --query accessToken -o tsv - } catch { - return $null - } -} - -function Invoke-AzCliWithTimeout { - param( - [string[]]$Args, - [int]$TimeoutSeconds = 120 - ) - - $escapedArgs = $Args | ForEach-Object { - if ($_ -match '\s|"') { '"' + ($_ -replace '"', '\"') + '"' } else { $_ } - } - - $azPath = (Get-Command az -ErrorAction SilentlyContinue).Source - if (-not $azPath) { - throw "Azure CLI (az) not found on PATH." - } - - $psi = New-Object System.Diagnostics.ProcessStartInfo - $psi.FileName = $azPath - $psi.Arguments = ($escapedArgs -join ' ') - $psi.RedirectStandardOutput = $true - $psi.RedirectStandardError = $true - $psi.UseShellExecute = $false - $psi.CreateNoWindow = $true - - $process = [System.Diagnostics.Process]::Start($psi) - if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { - try { $process.Kill() } catch { } - throw "Azure CLI command timed out after $TimeoutSeconds seconds: az $($psi.Arguments)" - } - - $stdout = $process.StandardOutput.ReadToEnd() - $stderr = $process.StandardError.ReadToEnd() - - if ($process.ExitCode -ne 0) { - throw "Azure CLI failed with exit code $($process.ExitCode): $stderr" - } - - return $stdout -} - -function Set-SearchPublicNetworkAccess { - param([string]$Mode) - - $timeoutSeconds = 120 - if ($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS) { - [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS, [ref]$timeoutSeconds) | Out-Null - } - - $maxRetries = 3 - if ($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES) { - [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES, [ref]$maxRetries) | Out-Null - } - - $waitSeconds = 120 - if ($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS) { - [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS, [ref]$waitSeconds) | Out-Null - } - - for ($attempt = 1; $attempt -le $maxRetries; $attempt++) { - try { - $resourceId = Get-SearchResourceId - if (-not $resourceId) { - throw "Unable to resolve AI Search resource ID." - } - $armToken = Get-ArmAccessToken - if (-not $armToken) { - throw "Unable to acquire ARM access token." - } - $body = @{ properties = @{ publicNetworkAccess = $Mode } } | ConvertTo-Json -Compress - $url = "https://management.azure.com${resourceId}?api-version=2023-11-01" - $headers = @{ Authorization = "Bearer $armToken"; 'Content-Type' = 'application/json' } - Invoke-RestMethod -Method Patch -Uri $url -Headers $headers -Body $body | Out-Null - $deadline = (Get-Date).AddSeconds($waitSeconds) - do { - $current = Get-SearchPublicNetworkAccess - if ($current -eq $Mode) { return } - Start-Sleep -Seconds 5 - } while ((Get-Date) -lt $deadline) - - throw "Timed out waiting for AI Search public network access to become '$Mode'." - } catch { - if ($attempt -lt $maxRetries) { - Write-Warning "Failed to set AI Search public network access (attempt $attempt of $maxRetries): $($_.Exception.Message)" - Start-Sleep -Seconds 10 - continue - } - throw - } - } -} - -function Ensure-SearchPublicAccess { - if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { - Write-Host "Skipping temporary public network access toggle for AI Search." - return $null - } - - $current = Get-SearchPublicNetworkAccess - if (-not $current) { return $null } - - if ($current -eq 'Disabled') { - Write-Warning "AI Search public network access is Disabled. Enabling temporarily for OneLake setup." - Set-SearchPublicNetworkAccess -Mode 'Enabled' - } - - return $current -} - -function Restore-SearchPublicAccess { - param([string]$OriginalAccess) - - if (-not $OriginalAccess) { return } - if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { return } - - $current = Get-SearchPublicNetworkAccess - if ($current -and $current -ne $OriginalAccess) { - Write-Host "Restoring AI Search public network access to '$OriginalAccess'." - try { - Set-SearchPublicNetworkAccess -Mode $OriginalAccess - } catch { - Write-Warning "Failed to restore AI Search public network access: $($_.Exception.Message)" - } - } -} - -function Get-SearchAccessToken { - try { - return az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv - } catch { - return $null - } -} - -function New-SearchHeaders { - param( - [string]$AccessToken - ) - - if ($AccessToken) { - return @{ - 'Authorization' = "Bearer $AccessToken" - 'Content-Type' = 'application/json' - } - } - - return $null -} - -function Invoke-SearchRequest { - param( - [string]$Method, - [string]$Uri, - [string]$Body - ) - - $maxAttempts = 6 - $delaySeconds = 30 - - for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { - $accessToken = Get-SearchAccessToken - $headers = New-SearchHeaders -AccessToken $accessToken - - if (-not $headers) { - Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" - exit 1 - } - - try { - if ($Body) { - return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method -Body $Body - } - return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method - } catch { - $statusCode = $null - try { $statusCode = $_.Exception.Response.StatusCode.value__ } catch { } - - if (($statusCode -eq 401 -or $statusCode -eq 403) -and $attempt -lt $maxAttempts) { - Write-Warning "Search request denied (HTTP $statusCode). Waiting ${delaySeconds}s for RBAC propagation (attempt $attempt of $maxAttempts)." - Start-Sleep -Seconds $delaySeconds - continue - } - - throw - } - } -} +. "$PSScriptRoot/SearchHelpers.ps1" $originalPublicAccess = Ensure-SearchPublicAccess try { diff --git a/scripts/automationScripts/OneLakeIndex/03_create_onelake_index.ps1 b/scripts/automationScripts/OneLakeIndex/03_create_onelake_index.ps1 index ed4cf43..3a7ee43 100644 --- a/scripts/automationScripts/OneLakeIndex/03_create_onelake_index.ps1 +++ b/scripts/automationScripts/OneLakeIndex/03_create_onelake_index.ps1 @@ -90,221 +90,13 @@ if (-not $aiSearchName -or -not $resourceGroup -or -not $subscription) { exit 1 } +. "$PSScriptRoot/SearchHelpers.ps1" + Write-Host "Index Name: $indexName" if ($workspaceName) { Write-Host "Derived Fabric Workspace Name: $workspaceName" } if ($domainName) { Write-Host "Derived Fabric Domain Name: $domainName" } Write-Host "" -function Get-SearchPublicNetworkAccess { - try { - return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "publicNetworkAccess" -o tsv - } catch { - return $null - } -} - -function Get-SearchResourceId { - try { - return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "id" -o tsv - } catch { - return $null - } -} - -function Get-ArmAccessToken { - try { - return az account get-access-token --resource https://management.azure.com/ --subscription $subscription --query accessToken -o tsv - } catch { - return $null - } -} - -function Invoke-AzCliWithTimeout { - param( - [string[]]$Args, - [int]$TimeoutSeconds = 120 - ) - - $escapedArgs = $Args | ForEach-Object { - if ($_ -match '\s|"') { '"' + ($_ -replace '"', '\"') + '"' } else { $_ } - } - - $azPath = (Get-Command az -ErrorAction SilentlyContinue).Source - if (-not $azPath) { - throw "Azure CLI (az) not found on PATH." - } - - $psi = New-Object System.Diagnostics.ProcessStartInfo - $psi.FileName = $azPath - $psi.Arguments = ($escapedArgs -join ' ') - $psi.RedirectStandardOutput = $true - $psi.RedirectStandardError = $true - $psi.UseShellExecute = $false - $psi.CreateNoWindow = $true - - $process = [System.Diagnostics.Process]::Start($psi) - if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { - try { $process.Kill() } catch { } - throw "Azure CLI command timed out after $TimeoutSeconds seconds: az $($psi.Arguments)" - } - - $stdout = $process.StandardOutput.ReadToEnd() - $stderr = $process.StandardError.ReadToEnd() - - if ($process.ExitCode -ne 0) { - throw "Azure CLI failed with exit code $($process.ExitCode): $stderr" - } - - return $stdout -} - -function Set-SearchPublicNetworkAccess { - param([string]$Mode) - - $timeoutSeconds = 120 - if ($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS) { - [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS, [ref]$timeoutSeconds) | Out-Null - } - - $maxRetries = 3 - if ($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES) { - [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES, [ref]$maxRetries) | Out-Null - } - - $waitSeconds = 120 - if ($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS) { - [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS, [ref]$waitSeconds) | Out-Null - } - - for ($attempt = 1; $attempt -le $maxRetries; $attempt++) { - try { - $resourceId = Get-SearchResourceId - if (-not $resourceId) { - throw "Unable to resolve AI Search resource ID." - } - $armToken = Get-ArmAccessToken - if (-not $armToken) { - throw "Unable to acquire ARM access token." - } - $body = @{ properties = @{ publicNetworkAccess = $Mode } } | ConvertTo-Json -Compress - $url = "https://management.azure.com${resourceId}?api-version=2023-11-01" - $headers = @{ Authorization = "Bearer $armToken"; 'Content-Type' = 'application/json' } - Invoke-RestMethod -Method Patch -Uri $url -Headers $headers -Body $body | Out-Null - $deadline = (Get-Date).AddSeconds($waitSeconds) - do { - $current = Get-SearchPublicNetworkAccess - if ($current -eq $Mode) { return } - Start-Sleep -Seconds 5 - } while ((Get-Date) -lt $deadline) - - throw "Timed out waiting for AI Search public network access to become '$Mode'." - } catch { - if ($attempt -lt $maxRetries) { - Write-Warning "Failed to set AI Search public network access (attempt $attempt of $maxRetries): $($_.Exception.Message)" - Start-Sleep -Seconds 10 - continue - } - throw - } - } -} - -function Ensure-SearchPublicAccess { - if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { - Write-Host "Skipping temporary public network access toggle for AI Search." - return $null - } - - $current = Get-SearchPublicNetworkAccess - if (-not $current) { return $null } - - if ($current -eq 'Disabled') { - Write-Warning "AI Search public network access is Disabled. Enabling temporarily for OneLake setup." - Set-SearchPublicNetworkAccess -Mode 'Enabled' - } - - return $current -} - -function Restore-SearchPublicAccess { - param([string]$OriginalAccess) - - if (-not $OriginalAccess) { return } - if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { return } - - $current = Get-SearchPublicNetworkAccess - if ($current -and $current -ne $OriginalAccess) { - Write-Host "Restoring AI Search public network access to '$OriginalAccess'." - try { - Set-SearchPublicNetworkAccess -Mode $OriginalAccess - } catch { - Write-Warning "Failed to restore AI Search public network access: $($_.Exception.Message)" - } - } -} - -function Get-SearchAccessToken { - try { - return az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv - } catch { - return $null - } -} - -function New-SearchHeaders { - param( - [string]$AccessToken - ) - - if ($AccessToken) { - return @{ - 'Authorization' = "Bearer $AccessToken" - 'Content-Type' = 'application/json' - } - } - - return $null -} - -function Invoke-SearchRequest { - param( - [string]$Method, - [string]$Uri, - [string]$Body - ) - - $maxAttempts = 6 - $delaySeconds = 30 - - for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { - $accessToken = Get-SearchAccessToken - $headers = New-SearchHeaders -AccessToken $accessToken - - if (-not $headers) { - Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" - exit 1 - } - - try { - if ($Body) { - return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method -Body $Body - } - return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method - } catch { - $statusCode = $null - try { $statusCode = $_.Exception.Response.StatusCode.value__ } catch { } - - if (($statusCode -eq 401 -or $statusCode -eq 403) -and $attempt -lt $maxAttempts) { - Write-Warning "Search request denied (HTTP $statusCode). Waiting ${delaySeconds}s for RBAC propagation (attempt $attempt of $maxAttempts)." - Start-Sleep -Seconds $delaySeconds - continue - } - - throw - } - } -} - $originalPublicAccess = Ensure-SearchPublicAccess try { # Use preview API version required for OneLake diff --git a/scripts/automationScripts/OneLakeIndex/04_create_onelake_datasource.ps1 b/scripts/automationScripts/OneLakeIndex/04_create_onelake_datasource.ps1 index 5cff914..b3cfdd3 100644 --- a/scripts/automationScripts/OneLakeIndex/04_create_onelake_datasource.ps1 +++ b/scripts/automationScripts/OneLakeIndex/04_create_onelake_datasource.ps1 @@ -117,221 +117,13 @@ if (-not $workspaceId -or -not $lakehouseId) { exit 1 } +. "$PSScriptRoot/SearchHelpers.ps1" + Write-Host "Workspace ID: $workspaceId" Write-Host "Lakehouse ID: $lakehouseId" Write-Host "Query Path: $queryPath" Write-Host "" -function Get-SearchPublicNetworkAccess { - try { - return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "publicNetworkAccess" -o tsv - } catch { - return $null - } -} - -function Get-SearchResourceId { - try { - return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "id" -o tsv - } catch { - return $null - } -} - -function Get-ArmAccessToken { - try { - return az account get-access-token --resource https://management.azure.com/ --subscription $subscription --query accessToken -o tsv - } catch { - return $null - } -} - -function Invoke-AzCliWithTimeout { - param( - [string[]]$Args, - [int]$TimeoutSeconds = 120 - ) - - $escapedArgs = $Args | ForEach-Object { - if ($_ -match '\s|"') { '"' + ($_ -replace '"', '\"') + '"' } else { $_ } - } - - $azPath = (Get-Command az -ErrorAction SilentlyContinue).Source - if (-not $azPath) { - throw "Azure CLI (az) not found on PATH." - } - - $psi = New-Object System.Diagnostics.ProcessStartInfo - $psi.FileName = $azPath - $psi.Arguments = ($escapedArgs -join ' ') - $psi.RedirectStandardOutput = $true - $psi.RedirectStandardError = $true - $psi.UseShellExecute = $false - $psi.CreateNoWindow = $true - - $process = [System.Diagnostics.Process]::Start($psi) - if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { - try { $process.Kill() } catch { } - throw "Azure CLI command timed out after $TimeoutSeconds seconds: az $($psi.Arguments)" - } - - $stdout = $process.StandardOutput.ReadToEnd() - $stderr = $process.StandardError.ReadToEnd() - - if ($process.ExitCode -ne 0) { - throw "Azure CLI failed with exit code $($process.ExitCode): $stderr" - } - - return $stdout -} - -function Set-SearchPublicNetworkAccess { - param([string]$Mode) - - $timeoutSeconds = 120 - if ($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS) { - [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS, [ref]$timeoutSeconds) | Out-Null - } - - $maxRetries = 3 - if ($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES) { - [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES, [ref]$maxRetries) | Out-Null - } - - $waitSeconds = 120 - if ($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS) { - [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS, [ref]$waitSeconds) | Out-Null - } - - for ($attempt = 1; $attempt -le $maxRetries; $attempt++) { - try { - $resourceId = Get-SearchResourceId - if (-not $resourceId) { - throw "Unable to resolve AI Search resource ID." - } - $armToken = Get-ArmAccessToken - if (-not $armToken) { - throw "Unable to acquire ARM access token." - } - $body = @{ properties = @{ publicNetworkAccess = $Mode } } | ConvertTo-Json -Compress - $url = "https://management.azure.com${resourceId}?api-version=2023-11-01" - $headers = @{ Authorization = "Bearer $armToken"; 'Content-Type' = 'application/json' } - Invoke-RestMethod -Method Patch -Uri $url -Headers $headers -Body $body | Out-Null - $deadline = (Get-Date).AddSeconds($waitSeconds) - do { - $current = Get-SearchPublicNetworkAccess - if ($current -eq $Mode) { return } - Start-Sleep -Seconds 5 - } while ((Get-Date) -lt $deadline) - - throw "Timed out waiting for AI Search public network access to become '$Mode'." - } catch { - if ($attempt -lt $maxRetries) { - Write-Warning "Failed to set AI Search public network access (attempt $attempt of $maxRetries): $($_.Exception.Message)" - Start-Sleep -Seconds 10 - continue - } - throw - } - } -} - -function Ensure-SearchPublicAccess { - if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { - Write-Host "Skipping temporary public network access toggle for AI Search." - return $null - } - - $current = Get-SearchPublicNetworkAccess - if (-not $current) { return $null } - - if ($current -eq 'Disabled') { - Write-Warning "AI Search public network access is Disabled. Enabling temporarily for OneLake setup." - Set-SearchPublicNetworkAccess -Mode 'Enabled' - } - - return $current -} - -function Restore-SearchPublicAccess { - param([string]$OriginalAccess) - - if (-not $OriginalAccess) { return } - if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { return } - - $current = Get-SearchPublicNetworkAccess - if ($current -and $current -ne $OriginalAccess) { - Write-Host "Restoring AI Search public network access to '$OriginalAccess'." - try { - Set-SearchPublicNetworkAccess -Mode $OriginalAccess - } catch { - Write-Warning "Failed to restore AI Search public network access: $($_.Exception.Message)" - } - } -} - -function Get-SearchAccessToken { - try { - return az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv - } catch { - return $null - } -} - -function New-SearchHeaders { - param( - [string]$AccessToken - ) - - if ($AccessToken) { - return @{ - 'Authorization' = "Bearer $AccessToken" - 'Content-Type' = 'application/json' - } - } - - return $null -} - -function Invoke-SearchRequest { - param( - [string]$Method, - [string]$Uri, - [string]$Body - ) - - $maxAttempts = 6 - $delaySeconds = 30 - - for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { - $accessToken = Get-SearchAccessToken - $headers = New-SearchHeaders -AccessToken $accessToken - - if (-not $headers) { - Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" - exit 1 - } - - try { - if ($Body) { - return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method -Body $Body - } - return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method - } catch { - $statusCode = $null - try { $statusCode = $_.Exception.Response.StatusCode.value__ } catch { } - - if (($statusCode -eq 401 -or $statusCode -eq 403) -and $attempt -lt $maxAttempts) { - Write-Warning "Search request denied (HTTP $statusCode). Waiting ${delaySeconds}s for RBAC propagation (attempt $attempt of $maxAttempts)." - Start-Sleep -Seconds $delaySeconds - continue - } - - throw - } - } -} - $originalPublicAccess = Ensure-SearchPublicAccess try { # Use preview API version required for OneLake diff --git a/scripts/automationScripts/OneLakeIndex/05_create_onelake_indexer.ps1 b/scripts/automationScripts/OneLakeIndex/05_create_onelake_indexer.ps1 index d6c74be..8cc7881 100644 --- a/scripts/automationScripts/OneLakeIndex/05_create_onelake_indexer.ps1 +++ b/scripts/automationScripts/OneLakeIndex/05_create_onelake_indexer.ps1 @@ -100,6 +100,8 @@ if (-not $aiSearchName -or -not $resourceGroup -or -not $subscription) { exit 1 } +. "$PSScriptRoot/SearchHelpers.ps1" + Write-Host "Index Name: $indexName" Write-Host "Data Source: $dataSourceName" Write-Host "Skillset: $skillsetName" @@ -108,216 +110,6 @@ if ($workspaceName) { Write-Host "Derived Fabric Workspace Name: $workspaceName" if ($folderPath) { Write-Host "Folder Path: $folderPath" } Write-Host "" -function Get-SearchPublicNetworkAccess { - try { - return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "publicNetworkAccess" -o tsv - } catch { - return $null - } -} - -function Get-SearchResourceId { - try { - return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "id" -o tsv - } catch { - return $null - } -} - -function Get-ArmAccessToken { - try { - return az account get-access-token --resource https://management.azure.com/ --subscription $subscription --query accessToken -o tsv - } catch { - return $null - } -} - -function Invoke-AzCliWithTimeout { - param( - [string[]]$Args, - [int]$TimeoutSeconds = 120 - ) - - $escapedArgs = $Args | ForEach-Object { - if ($_ -match '\s|"') { '"' + ($_ -replace '"', '\"') + '"' } else { $_ } - } - - $azPath = (Get-Command az -ErrorAction SilentlyContinue).Source - if (-not $azPath) { - throw "Azure CLI (az) not found on PATH." - } - - $psi = New-Object System.Diagnostics.ProcessStartInfo - $psi.FileName = $azPath - $psi.Arguments = ($escapedArgs -join ' ') - $psi.RedirectStandardOutput = $true - $psi.RedirectStandardError = $true - $psi.UseShellExecute = $false - $psi.CreateNoWindow = $true - - $process = [System.Diagnostics.Process]::Start($psi) - if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { - try { $process.Kill() } catch { } - throw "Azure CLI command timed out after $TimeoutSeconds seconds: az $($psi.Arguments)" - } - - $stdout = $process.StandardOutput.ReadToEnd() - $stderr = $process.StandardError.ReadToEnd() - - if ($process.ExitCode -ne 0) { - throw "Azure CLI failed with exit code $($process.ExitCode): $stderr" - } - - return $stdout -} - -function Set-SearchPublicNetworkAccess { - param([string]$Mode) - - $timeoutSeconds = 120 - if ($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS) { - [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS, [ref]$timeoutSeconds) | Out-Null - } - - $maxRetries = 3 - if ($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES) { - [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES, [ref]$maxRetries) | Out-Null - } - - $waitSeconds = 120 - if ($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS) { - [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS, [ref]$waitSeconds) | Out-Null - } - - for ($attempt = 1; $attempt -le $maxRetries; $attempt++) { - try { - $resourceId = Get-SearchResourceId - if (-not $resourceId) { - throw "Unable to resolve AI Search resource ID." - } - $armToken = Get-ArmAccessToken - if (-not $armToken) { - throw "Unable to acquire ARM access token." - } - $body = @{ properties = @{ publicNetworkAccess = $Mode } } | ConvertTo-Json -Compress - $url = "https://management.azure.com${resourceId}?api-version=2023-11-01" - $headers = @{ Authorization = "Bearer $armToken"; 'Content-Type' = 'application/json' } - Invoke-RestMethod -Method Patch -Uri $url -Headers $headers -Body $body | Out-Null - $deadline = (Get-Date).AddSeconds($waitSeconds) - do { - $current = Get-SearchPublicNetworkAccess - if ($current -eq $Mode) { return } - Start-Sleep -Seconds 5 - } while ((Get-Date) -lt $deadline) - - throw "Timed out waiting for AI Search public network access to become '$Mode'." - } catch { - if ($attempt -lt $maxRetries) { - Write-Warning "Failed to set AI Search public network access (attempt $attempt of $maxRetries): $($_.Exception.Message)" - Start-Sleep -Seconds 10 - continue - } - throw - } - } -} - -function Ensure-SearchPublicAccess { - if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { - Write-Host "Skipping temporary public network access toggle for AI Search." - return $null - } - - $current = Get-SearchPublicNetworkAccess - if (-not $current) { return $null } - - if ($current -eq 'Disabled') { - Write-Warning "AI Search public network access is Disabled. Enabling temporarily for OneLake setup." - Set-SearchPublicNetworkAccess -Mode 'Enabled' - } - - return $current -} - -function Restore-SearchPublicAccess { - param([string]$OriginalAccess) - - if (-not $OriginalAccess) { return } - if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { return } - - $current = Get-SearchPublicNetworkAccess - if ($current -and $current -ne $OriginalAccess) { - Write-Host "Restoring AI Search public network access to '$OriginalAccess'." - try { - Set-SearchPublicNetworkAccess -Mode $OriginalAccess - } catch { - Write-Warning "Failed to restore AI Search public network access: $($_.Exception.Message)" - } - } -} - -function Get-SearchAccessToken { - try { - return az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv - } catch { - return $null - } -} - -function New-SearchHeaders { - param( - [string]$AccessToken - ) - - if ($AccessToken) { - return @{ - 'Authorization' = "Bearer $AccessToken" - 'Content-Type' = 'application/json' - } - } - - return $null -} - -function Invoke-SearchRequest { - param( - [string]$Method, - [string]$Uri, - [string]$Body - ) - - $maxAttempts = 6 - $delaySeconds = 30 - - for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { - $accessToken = Get-SearchAccessToken - $headers = New-SearchHeaders -AccessToken $accessToken - - if (-not $headers) { - Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" - exit 1 - } - - try { - if ($Body) { - return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method -Body $Body - } - return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method - } catch { - $statusCode = $null - try { $statusCode = $_.Exception.Response.StatusCode.value__ } catch { } - - if (($statusCode -eq 401 -or $statusCode -eq 403) -and $attempt -lt $maxAttempts) { - Write-Warning "Search request denied (HTTP $statusCode). Waiting ${delaySeconds}s for RBAC propagation (attempt $attempt of $maxAttempts)." - Start-Sleep -Seconds $delaySeconds - continue - } - - throw - } - } -} - $originalPublicAccess = Ensure-SearchPublicAccess try { # Use preview API version required for OneLake diff --git a/scripts/automationScripts/OneLakeIndex/06_setup_ai_foundry_search_rbac.ps1 b/scripts/automationScripts/OneLakeIndex/06_setup_ai_foundry_search_rbac.ps1 index 0c6e646..53eabfa 100644 --- a/scripts/automationScripts/OneLakeIndex/06_setup_ai_foundry_search_rbac.ps1 +++ b/scripts/automationScripts/OneLakeIndex/06_setup_ai_foundry_search_rbac.ps1 @@ -166,6 +166,11 @@ function Test-AiFoundryProjectExists { param([string]$ProjectName) if (-not $ProjectName) { return $false } + # Accept either "project" or "account/project" formats. + if ($ProjectName -match '/') { + $ProjectName = ($ProjectName -split '/', 2)[1] + } + try { $projectResourceId = "/subscriptions/$AIFoundrySubscriptionId/resourceGroups/$AIFoundryResourceGroup/providers/Microsoft.CognitiveServices/accounts/$AIFoundryName/projects/$ProjectName" $null = az resource show --ids $projectResourceId --query id -o tsv 2>$null diff --git a/scripts/automationScripts/OneLakeIndex/SearchHelpers.ps1 b/scripts/automationScripts/OneLakeIndex/SearchHelpers.ps1 new file mode 100644 index 0000000..7acbf4d --- /dev/null +++ b/scripts/automationScripts/OneLakeIndex/SearchHelpers.ps1 @@ -0,0 +1,209 @@ +function Get-SearchPublicNetworkAccess { + try { + return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "publicNetworkAccess" -o tsv + } catch { + return $null + } +} + +function Get-SearchResourceId { + try { + return az search service show --name $aiSearchName --resource-group $resourceGroup --subscription $subscription --query "id" -o tsv + } catch { + return $null + } +} + +function Get-ArmAccessToken { + try { + return az account get-access-token --resource https://management.azure.com/ --subscription $subscription --query accessToken -o tsv + } catch { + return $null + } +} + +function Invoke-AzCliWithTimeout { + param( + [string[]]$Args, + [int]$TimeoutSeconds = 120 + ) + + $escapedArgs = $Args | ForEach-Object { + if ($_ -match '\s|"') { '"' + ($_ -replace '"', '\"') + '"' } else { $_ } + } + + $azPath = (Get-Command az -ErrorAction SilentlyContinue).Source + if (-not $azPath) { + throw "Azure CLI (az) not found on PATH." + } + + $psi = New-Object System.Diagnostics.ProcessStartInfo + $psi.FileName = $azPath + $psi.Arguments = ($escapedArgs -join ' ') + $psi.RedirectStandardOutput = $true + $psi.RedirectStandardError = $true + $psi.UseShellExecute = $false + $psi.CreateNoWindow = $true + + $process = [System.Diagnostics.Process]::Start($psi) + if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { + try { $process.Kill() } catch { } + throw "Azure CLI command timed out after $TimeoutSeconds seconds: az $($psi.Arguments)" + } + + $stdout = $process.StandardOutput.ReadToEnd() + $stderr = $process.StandardError.ReadToEnd() + + if ($process.ExitCode -ne 0) { + throw "Azure CLI failed with exit code $($process.ExitCode): $stderr" + } + + return $stdout +} + +function Set-SearchPublicNetworkAccess { + param([string]$Mode) + + $timeoutSeconds = 120 + if ($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_TIMEOUT_SECONDS, [ref]$timeoutSeconds) | Out-Null + } + + $maxRetries = 3 + if ($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_MAX_RETRIES, [ref]$maxRetries) | Out-Null + } + + $waitSeconds = 120 + if ($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS) { + [int]::TryParse($env:AI_SEARCH_PUBLIC_ACCESS_POLL_SECONDS, [ref]$waitSeconds) | Out-Null + } + + for ($attempt = 1; $attempt -le $maxRetries; $attempt++) { + try { + $resourceId = Get-SearchResourceId + if (-not $resourceId) { + throw "Unable to resolve AI Search resource ID." + } + $armToken = Get-ArmAccessToken + if (-not $armToken) { + throw "Unable to acquire ARM access token." + } + $body = @{ properties = @{ publicNetworkAccess = $Mode } } | ConvertTo-Json -Compress + $url = "https://management.azure.com${resourceId}?api-version=2023-11-01" + $headers = @{ Authorization = "Bearer $armToken"; 'Content-Type' = 'application/json' } + Invoke-RestMethod -Method Patch -Uri $url -Headers $headers -Body $body | Out-Null + $deadline = (Get-Date).AddSeconds($waitSeconds) + do { + $current = Get-SearchPublicNetworkAccess + if ($current -eq $Mode) { return } + Start-Sleep -Seconds 5 + } while ((Get-Date) -lt $deadline) + + throw "Timed out waiting for AI Search public network access to become '$Mode'." + } catch { + if ($attempt -lt $maxRetries) { + Write-Warning "Failed to set AI Search public network access (attempt $attempt of $maxRetries): $($_.Exception.Message)" + Start-Sleep -Seconds 10 + continue + } + throw + } + } +} + +function Ensure-SearchPublicAccess { + if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { + Write-Host "Skipping temporary public network access toggle for AI Search." + return $null + } + + $current = Get-SearchPublicNetworkAccess + if (-not $current) { return $null } + + if ($current -eq 'Disabled') { + Write-Warning "AI Search public network access is Disabled. Enabling temporarily for OneLake setup." + Set-SearchPublicNetworkAccess -Mode 'Enabled' + } + + return $current +} + +function Restore-SearchPublicAccess { + param([string]$OriginalAccess) + + if (-not $OriginalAccess) { return } + if ($env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE -and $env:AI_SEARCH_SKIP_PUBLIC_ACCESS_TOGGLE.ToLowerInvariant() -eq 'true') { return } + + $current = Get-SearchPublicNetworkAccess + if ($current -and $current -ne $OriginalAccess) { + Write-Host "Restoring AI Search public network access to '$OriginalAccess'." + try { + Set-SearchPublicNetworkAccess -Mode $OriginalAccess + } catch { + Write-Warning "Failed to restore AI Search public network access: $($_.Exception.Message)" + } + } +} + +function Get-SearchAccessToken { + try { + return az account get-access-token --resource https://search.azure.com --subscription $subscription --query accessToken -o tsv + } catch { + return $null + } +} + +function New-SearchHeaders { + param( + [string]$AccessToken + ) + + if ($AccessToken) { + return @{ + 'Authorization' = "Bearer $AccessToken" + 'Content-Type' = 'application/json' + } + } + + return $null +} + +function Invoke-SearchRequest { + param( + [string]$Method, + [string]$Uri, + [string]$Body + ) + + $maxAttempts = 6 + $delaySeconds = 30 + + for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { + $accessToken = Get-SearchAccessToken + $headers = New-SearchHeaders -AccessToken $accessToken + + if (-not $headers) { + Write-Error "Failed to acquire Azure AI Search access token via Microsoft Entra ID" + exit 1 + } + + try { + if ($Body) { + return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method -Body $Body + } + return Invoke-RestMethod -Uri $Uri -Headers $headers -Method $Method + } catch { + $statusCode = $null + try { $statusCode = $_.Exception.Response.StatusCode.value__ } catch { } + + if (($statusCode -eq 401 -or $statusCode -eq 403) -and $attempt -lt $maxAttempts) { + Write-Warning "Search request denied (HTTP $statusCode). Waiting ${delaySeconds}s for RBAC propagation (attempt $attempt of $maxAttempts)." + Start-Sleep -Seconds $delaySeconds + continue + } + + throw + } + } +} \ No newline at end of file diff --git a/scripts/automationScripts/SecurityModule.ps1 b/scripts/automationScripts/SecurityModule.ps1 index 1b6b1d1..e1edfe5 100644 --- a/scripts/automationScripts/SecurityModule.ps1 +++ b/scripts/automationScripts/SecurityModule.ps1 @@ -69,6 +69,66 @@ function New-SecureHeaders { } } +function Read-SecureResponseBody { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + $Response + ) + + $responseStream = $null + $reader = $null + + try { + $responseStream = $Response.GetResponseStream() + if (-not $responseStream) { return $null } + + $reader = New-Object System.IO.StreamReader($responseStream) + return $reader.ReadToEnd() + } + catch { + return $null + } + finally { + if ($reader -ne $null) { + $reader.Dispose() + } + elseif ($responseStream -ne $null) { + $responseStream.Dispose() + } + } +} + +function Sanitize-SecureResponseBody { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [AllowNull()] + [string]$ResponseBody, + + [Parameter(Mandatory = $false)] + [int]$MaxLength = 1024 + ) + + if ([string]::IsNullOrEmpty($ResponseBody)) { + return $null + } + + $sanitizedBody = $ResponseBody + $sanitizedBody = $sanitizedBody -replace 'Bearer [A-Za-z0-9\-\._~\+\/=]+=*', 'Bearer [REDACTED]' + $sanitizedBody = $sanitizedBody -replace '"access_token"\s*:\s*".*?"', '"access_token":"[REDACTED]"' + $sanitizedBody = $sanitizedBody -replace '"refresh_token"\s*:\s*".*?"', '"refresh_token":"[REDACTED]"' + $sanitizedBody = $sanitizedBody -replace '"client_secret"\s*:\s*".*?"', '"client_secret":"[REDACTED]"' + $sanitizedBody = $sanitizedBody -replace '"password"\s*:\s*".*?"', '"password":"[REDACTED]"' + $sanitizedBody = $sanitizedBody -replace '([?&](sig|signature|token|code)=)[^&\s"]+', '$1[REDACTED]' + + if ($sanitizedBody.Length -gt $MaxLength) { + $sanitizedBody = $sanitizedBody.Substring(0, $MaxLength) + '...[truncated]' + } + + return $sanitizedBody +} + # Secure REST method with error sanitization function Invoke-SecureRestMethod { [CmdletBinding()] @@ -122,13 +182,10 @@ function Invoke-SecureRestMethod { try { $response = $_.Exception.Response } catch { $response = $null } if ($response) { try { $statusCode = $response.StatusCode } catch { $statusCode = $null } - try { - $reader = New-Object System.IO.StreamReader($response.GetResponseStream()) - $responseBody = $reader.ReadToEnd() - } catch { $responseBody = $null } + $responseBody = Read-SecureResponseBody -Response $response } if ($responseBody) { - $responseBody = $responseBody -replace 'Bearer [A-Za-z0-9\-\._~\+\/=]+=*', 'Bearer [REDACTED]' + $responseBody = Sanitize-SecureResponseBody -ResponseBody $responseBody } Write-Error "Secure $Description failed: $sanitizedError" diff --git a/scripts/preprovision-integrated.ps1 b/scripts/preprovision-integrated.ps1 index 1b85a7a..d90c7b7 100644 --- a/scripts/preprovision-integrated.ps1 +++ b/scripts/preprovision-integrated.ps1 @@ -254,6 +254,8 @@ if (-not [string]::IsNullOrWhiteSpace($SubscriptionId)) { $envNameForDeployment = $env:AZURE_ENV_NAME if ([string]::IsNullOrWhiteSpace($envNameForDeployment)) { $envNameForDeployment = 'default' } $deploymentName = "ai-landing-zone-$envNameForDeployment-$(Get-Date -Format 'yyyyMMddHHmmss')" +$deploymentRetryCount = 6 +$deploymentRetryDelaySeconds = 30 Write-Host " [+] Deployment name: $deploymentName" -ForegroundColor Green @@ -358,14 +360,40 @@ foreach ($name in $allowedParamNames) { $filteredParams = Join-Path $env:TEMP ("ai-landing-zone.$deploymentName.parameters.json") $filtered | ConvertTo-Json -Depth 50 | Set-Content -Path $filteredParams -Encoding UTF8 -$deployOutput = & az deployment group create --name $deploymentName --resource-group $ResourceGroup --template-file $submoduleMain --parameters ("@" + $filteredParams) --only-show-errors 2>&1 -$deployExitCode = $LASTEXITCODE -if ($deployExitCode -ne 0) { - Write-Host "[X] AI Landing Zone submodule deployment failed" -ForegroundColor Red +$deployOutput = $null +$deployExitCode = 1 +$parsed = $null +$raw = $null + +for ($attempt = 1; $attempt -le $deploymentRetryCount; $attempt++) { + $deployOutput = & az deployment group create --name $deploymentName --resource-group $ResourceGroup --template-file $submoduleMain --parameters ("@" + $filteredParams) --only-show-errors 2>&1 + $deployExitCode = $LASTEXITCODE + + if ($deployExitCode -eq 0) { + break + } $raw = ($deployOutput | Out-String).Trim() $parsed = Format-AzDeploymentError -Raw $raw + if ($parsed.Code -ne 'AccountProvisioningStateInvalid' -or $attempt -eq $deploymentRetryCount) { + break + } + + Write-Host " [!] AI Foundry account is still provisioning (attempt $attempt/$deploymentRetryCount). Waiting ${deploymentRetryDelaySeconds}s before retry..." -ForegroundColor Yellow + Start-Sleep -Seconds $deploymentRetryDelaySeconds +} + +if ($deployExitCode -ne 0) { + Write-Host "[X] AI Landing Zone submodule deployment failed" -ForegroundColor Red + + if (-not $raw) { + $raw = ($deployOutput | Out-String).Trim() + } + if (-not $parsed) { + $parsed = Format-AzDeploymentError -Raw $raw + } + if (-not [string]::IsNullOrWhiteSpace($parsed.Code) -or -not [string]::IsNullOrWhiteSpace($parsed.Message)) { $reasonParts = @() if ($parsed.Code) { $reasonParts += $parsed.Code } From e45220e2c3224de16eb13cde1b4d984c35c86448 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Fri, 20 Mar 2026 13:20:32 -0400 Subject: [PATCH 15/22] update change log --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3d01f7..2a08de2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,16 @@ All notable changes to this project will be documented in this file. - Shared AI Search helper module for OneLake indexing scripts to centralize public network access toggles and tokenized REST calls ### Changed +- Repository documentation now uses Microsoft Foundry naming more consistently, including the README, deployment verification guide, and related runbooks - PostgreSQL mirroring guidance now treats mirroring as a follow-up step after `azd up`, with clearer public-access versus private-network paths - Postprovision now restores only PostgreSQL mirroring readiness preparation instead of attempting full mirror creation during the main deployment run +- PostgreSQL infrastructure outputs now expose the intended Fabric connection identity and default authentication settings needed for mirroring setup - Fabric connection and workspace automation now resolve more values from deployment outputs, azd environment values, and deployed resources when transient hook context is incomplete - PostgreSQL mirroring scripts now support explicit connection-mode outputs, stronger credential handling, clearer network-path failures, and gateway-aware Fabric connection creation - Purview collection and Fabric datasource registration scripts now derive default names and deployment context more reliably from outputs and environment values +- Fabric workspace and capacity automation now tolerate more incomplete hook context, recover more reliably from existing resources, and improve capacity/workspace lookup behavior +- Preprovision retries the landing-zone deployment when Foundry account provisioning is still settling instead of failing immediately on transient provisioning-state errors +- Secure REST helpers now sanitize captured response bodies before surfacing API errors in automation logs - Post-deployment and mirroring documentation consolidated the mirror workflow into a single primary runbook and clarified when mirroring should be deferred ### Removed From ba7050ff1da93c3a3401445c3df8fd3900f1d69b Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Fri, 20 Mar 2026 13:46:25 -0400 Subject: [PATCH 16/22] update parameters to add postgres with mixed auth to connect fabric mirror --- infra/main.bicepparam | 75 ++++++------------------------------------- 1 file changed, 9 insertions(+), 66 deletions(-) diff --git a/infra/main.bicepparam b/infra/main.bicepparam index f7c2155..635b4a4 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -60,6 +60,11 @@ param enablePostgreSqlKeyVaultSecret = true param postgreSqlAdminSecretName = 'postgres-admin-password' param postgreSqlFabricUserName = 'fabric_user' param postgreSqlFabricUserSecretName = 'postgres-fabric-user-password' +param postgreSqlMirrorConnectionMode = 'fabricUser' +param postgreSqlAuthConfig = { + activeDirectoryAuth: 'Enabled' + passwordAuth: 'Enabled' +} param postgreSqlSkuName = 'Standard_D2s_v3' param postgreSqlTier = 'GeneralPurpose' param postgreSqlAvailabilityZone = 1 @@ -77,12 +82,12 @@ param deployAiFoundry = true param deployAiFoundrySubnet = false param deployAppConfig = true param deployKeyVault = true -param deployVmKeyVault = readEnvironmentVariable('DEPLOY_VM_KEY_VAULT', 'true') == 'true' +param deployVmKeyVault = readEnvironmentVariable('DEPLOY_VM_KEY_VAULT', 'true') == 'false' param deployLogAnalytics = false param deployAppInsights = true param deploySearchService = true param deployStorageAccount = true -param deployCosmosDb = true +param deployCosmosDb = false param deployContainerApps = true param deployContainerRegistry = true param deployContainerEnv = true @@ -153,14 +158,6 @@ param storageAccountContainersList = [ name: 'documents-images' canonical_name: 'DOCUMENTS_IMAGES_STORAGE_CONTAINER' } - { - name: 'documents' - canonical_name: 'DOCUMENTS_STORAGE_CONTAINER' - } - { - name: 'nl2sql' - canonical_name: 'NL2SQL_STORAGE_CONTAINER' - } ] param databaseContainersList = [ @@ -202,60 +199,6 @@ param containerAppsList = [ 'KeyVaultSecretsUser' ] } - { - name: null - external: true - service_name: 'frontend' - profile_name: 'main' - min_replicas: 1 - max_replicas: 1 - canonical_name: 'FRONTEND_APP' - roles: [ - 'AppConfigurationDataReader' - 'AcrPull' - 'StorageBlobDataReader' - 'StorageBlobDelegator' - 'KeyVaultSecretsUser' - ] - } - { - name: null - external: false - service_name: 'dataingest' - profile_name: 'main' - min_replicas: 1 - max_replicas: 1 - canonical_name: 'DATAINGEST_APP' - roles: [ - 'AppConfigurationDataReader' - 'CognitiveServicesUser' - 'CognitiveServicesOpenAIUser' - 'AcrPull' - 'CosmosDBBuiltInDataContributor' - 'SearchIndexDataContributor' - 'StorageBlobDataContributor' - 'KeyVaultSecretsUser' - ] - } - { - name: null - external: false - service_name: 'mcp' - profile_name: 'main' - min_replicas: 1 - max_replicas: 1 - canonical_name: 'MCP_APP' - roles: [ - 'AppConfigurationDataReader' - 'CognitiveServicesUser' - 'CognitiveServicesOpenAIUser' - 'AcrPull' - 'CosmosDBBuiltInDataContributor' - 'SearchIndexDataContributor' - 'StorageBlobDataContributor' - 'KeyVaultSecretsUser' - ] - } ] param vmAdminPassword = readEnvironmentVariable('VM_ADMIN_PASSWORD', '$(secretOrRandomPassword)') @@ -296,10 +239,10 @@ param fabricWorkspaceId = '' // required when fabricWorkspacePreset='byo' param fabricWorkspaceName = '' // optional (helpful for naming/UX) // Fabric capacity SKU. -param fabricCapacitySku = 'F2' +param fabricCapacitySku = 'F8' // Fabric capacity admin members (email addresses or object IDs). -param fabricCapacityAdmins = [] +param fabricCapacityAdmins = [''] // ======================================== // PURVIEW PARAMETERS (Optional) From cd1948f8f0fb150920c54bd60fc8af46b9fbf19b Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Tue, 24 Mar 2026 16:43:15 -0400 Subject: [PATCH 17/22] Update post deployment steps and other supporting documentation --- README.md | 14 ++++++++- azure.yaml | 2 +- docs/DeploymentGuide.md | 12 ++++++++ docs/PARAMETER_GUIDE.md | 8 +++++ docs/post_deployment_steps.md | 9 ++++++ docs/postgresql_mirroring.md | 29 +++++++++++++++++- ...ploy-AI-App-in-Prod-Architecture_final.png | Bin 116548 -> 0 bytes ...poly-AI-App-in-Prod-Architecture-final.png | Bin 99929 -> 116029 bytes infra/main.bicep | 19 ++++++++++++ infra/main.bicepparam | 7 +++-- 10 files changed, 95 insertions(+), 5 deletions(-) delete mode 100644 img/Architecture/Deploy-AI-App-in-Prod-Architecture_final.png diff --git a/README.md b/README.md index 5066bdb..8948405 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,18 @@ If you only want a small Foundry demo or a basic RAG sample, this repo is heavie For the first attempt, the lowest-risk path is to keep Fabric and Purview disabled unless you already have their prerequisites in place. +> **Important:** The checked-in values in `infra/main.bicepparam` are an opinionated end-to-end provisioning path for this accelerator, not a neutral baseline for every scenario. They are useful for demonstrating the full stack and the automation flow, but they might enable services, networking, mirroring behavior, or governance hooks that you do not want in your target deployment. +> +> Before running `azd up`, review the active settings across: +> - repo wrapper parameters in `infra/main.bicepparam` +> - AI Landing Zone feature flags and topology implied by the preprovision deployment +> - postprovision automation expectations in `azure.yaml` +> - supporting server-specific settings such as PostgreSQL networking, mirroring mode, and Fabric/Purview inputs +> +> Treat the current defaults as the repo's "golden path" for a broad end-to-end demo and validation flow. Adjust them deliberately if you want a smaller, cheaper, or less integrated deployment. + +> **Security note (PostgreSQL mirroring):** The mirroring prep script must run from a VNet-connected host when Key Vault and PostgreSQL are private. If you need a non-VNet demo, temporarily open access to both Key Vault and PostgreSQL, run the script, then lock them down. See [docs/post_deployment_steps.md](./docs/post_deployment_steps.md) for the manual steps, including the temporary Key Vault override. + ### Dependency Map | Area | Required to enable it | If missing | @@ -98,7 +110,7 @@ Follow the deployment guide to deploy this solution to your own Azure subscripti 1. Run `azd auth login` and confirm the target subscription with `az account show` 2. Create a new environment and set `AZURE_SUBSCRIPTION_ID` and `AZURE_LOCATION` -3. Review `infra/main.bicepparam`, especially `principalId`, `aiSearchAdditionalAccessObjectIds`, `fabricCapacityPreset`, `fabricWorkspacePreset`, `fabricCapacityAdmins`, `purviewAccountResourceId`, `networkIsolation`, and `postgreSqlNetworkIsolation` +3. Review `infra/main.bicepparam`, especially `principalId`, `aiSearchAdditionalAccessObjectIds`, `fabricCapacityPreset`, `fabricWorkspacePreset`, `fabricCapacityAdmins`, `purviewAccountResourceId`, `networkIsolation`, `postgreSqlNetworkIsolation`, and `postgreSqlAllowAzureServices` 4. Run `azd up` 5. Follow [docs/post_deployment_steps.md](./docs/post_deployment_steps.md) to verify the deployment diff --git a/azure.yaml b/azure.yaml index b76fdd8..19dc2be 100644 --- a/azure.yaml +++ b/azure.yaml @@ -76,7 +76,7 @@ hooks: - run: ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 interactive: false shell: pwsh - continueOnError: false + continueOnError: true # Stage 8: Setup Fabric Workspace Private Link (for VNet integration) - run: ./scripts/automationScripts/FabricWorkspace/SecureWorkspace/setup_fabric_private_link.ps1 diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index e68d01a..5304018 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -144,6 +144,18 @@ azd env set AZURE_LOCATION eastus2 ### Step 3: Configure Parameters +> **Important:** The values currently checked into `infra/main.bicepparam` represent an opinionated end-to-end path for provisioning this accelerator, including AI Landing Zone infrastructure, Fabric-related automation, PostgreSQL options, and postprovision hooks. They are not guaranteed to be the right settings for every deployment. +> +> Before you run `azd up`, verify the feature flags and automation inputs you are inheriting from: +> - `infra/main.bicepparam` +> - the AI Landing Zone submodule deployment that runs in preprovision +> - `azure.yaml` postprovision hooks and their prerequisites +> - service-specific settings such as Fabric, Purview, network isolation, PostgreSQL mirroring mode, and Azure-services firewall access +> +> If your goal is not the full end-to-end accelerator flow, change the flags first instead of treating the current defaults as universally safe. + +> **Security note (PostgreSQL mirroring):** The mirroring prep script requires VNet access when Key Vault and PostgreSQL are private. If you need to demo mirroring end-to-end from a non-VNet machine, temporarily open access to both Key Vault and PostgreSQL before running the script and lock them down afterward. See [docs/postgresql_mirroring.md](./postgresql_mirroring.md). +
Required Parameters diff --git a/docs/PARAMETER_GUIDE.md b/docs/PARAMETER_GUIDE.md index 65d3641..f37f37c 100644 --- a/docs/PARAMETER_GUIDE.md +++ b/docs/PARAMETER_GUIDE.md @@ -444,6 +444,7 @@ Use these in `infra/main.bicepparam` when deploying via this repo. `postgreSqlNe ```bicep-params param deployPostgreSql = true param postgreSqlNetworkIsolation = networkIsolation +param postgreSqlAllowAzureServices = false param postgreSqlMirrorConnectionMode = 'fabricUser' param postgreSqlAuthConfig = { activeDirectoryAuth: 'Enabled' @@ -453,6 +454,13 @@ param postgreSqlAuthConfig = { When `postgreSqlNetworkIsolation` is `false`, PostgreSQL uses public access and does not create private endpoints or private DNS resources. +`postgreSqlAllowAzureServices` controls whether deployment also creates the PostgreSQL firewall rule that allows Azure services to connect (`0.0.0.0` to `0.0.0.0`). This is the declarative equivalent of the Azure portal **Allow public access from any Azure service within Azure to this server** setting. + +Recommended combinations: + +- Public/manual Fabric path: `postgreSqlNetworkIsolation = false` and `postgreSqlAllowAzureServices = true` +- Private/gateway path: `postgreSqlNetworkIsolation = true` and `postgreSqlAllowAzureServices = false` + `postgreSqlAuthConfig` should remain set to both authentication modes enabled if you plan to configure Fabric mirroring after deployment. This ensures the server is created with password authentication available for the `fabric_user` connection instead of relying on a later hook to change the auth mode. `postgreSqlMirrorConnectionMode` controls which credential the manual Fabric PostgreSQL connection should use after deployment: diff --git a/docs/post_deployment_steps.md b/docs/post_deployment_steps.md index b1cd628..1aa743e 100644 --- a/docs/post_deployment_steps.md +++ b/docs/post_deployment_steps.md @@ -53,6 +53,15 @@ Use these short steps to verify the PostgreSQL mirroring follow-up flow. For ful Mirroring in the current branch is a separate follow-up activity. Fabric connection creation and mirrored database creation are not part of `azd up`. +> **Security step (required for manual mirroring):** The mirroring prep script must run from a VNet-connected host when Key Vault and PostgreSQL are private. If you want to demo mirroring end-to-end from a non-VNet machine, temporarily open access to both Key Vault and PostgreSQL before running the script, then lock them down afterward. + +If you must run the mirroring prep from a non-VNet host, set the temporary Key Vault override before you run the script: + +```powershell +$env:POSTGRES_TEMP_ENABLE_KV_PUBLIC_ACCESS = "true" +pwsh ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 +``` + For post-deployment verification, the important distinction is simple: - If you did not intentionally run the mirroring follow-up, treat mirroring as deferred and do not use it as a deployment success criterion. diff --git a/docs/postgresql_mirroring.md b/docs/postgresql_mirroring.md index b69f71f..3c5fe5e 100644 --- a/docs/postgresql_mirroring.md +++ b/docs/postgresql_mirroring.md @@ -2,8 +2,16 @@ This guide explains how to complete PostgreSQL mirroring in Microsoft Fabric after deployment. +> **Security-critical note:** The mirroring prep script must run from a VNet-connected host when Key Vault and PostgreSQL are private. If you want to demo the full end-to-end mirroring flow from a non-VNet machine, you must temporarily open access to both Key Vault and PostgreSQL before running the script, then re-lock them afterward. Treat this as a deliberate security step, not a default configuration. + Mirroring automation in the current branch is set for PostgreSQL deployments where `postgreSqlNetworkIsolation = false`. +For the public/manual path, this repo now supports a declarative firewall toggle through `postgreSqlAllowAzureServices`. + +- `postgreSqlNetworkIsolation = false` makes PostgreSQL publicly reachable. +- `postgreSqlAllowAzureServices = true` creates the PostgreSQL `AllowAzureServices` firewall rule (`0.0.0.0` to `0.0.0.0`), which is the deployment equivalent of the Azure portal **Allow public access from any Azure service within Azure to this server** setting. +- That combination is the recommended configuration when you want `azd up` to leave PostgreSQL ready for a manual Fabric connection without using a VNet gateway. + If you want full PostgreSQL isolation, the database deployment can still succeed, but end-to-end Fabric mirroring moves to the Fabric VNet gateway path. If you are not changing the network approach right now, there are only two valid post-deployment outcomes: @@ -26,13 +34,21 @@ Choose one path up front: Use this path when the PostgreSQL server has `publicNetworkAccess=Enabled`. In this repo, that corresponds to `postgreSqlNetworkIsolation = false`. +Recommended deployment settings for this path: + +```bicep-params +param postgreSqlNetworkIsolation = false +param postgreSqlAllowAzureServices = true +``` + 1. In Azure Portal, open the PostgreSQL Flexible Server. 2. Open **Fabric Mirroring** on the server and let the portal prepare the server-side prerequisites. - Microsoft documentation explicitly calls out this page as the path that automates the server-side mirroring prerequisites. - This overlaps with what `prepare_postgresql_for_mirroring.ps1` is trying to automate. - It does **not** create the Fabric connection object or the mirrored database item in the Fabric workspace. 3. In **Networking**, make sure Fabric can reach the server. - - Shortest path: add the `0.0.0.0` firewall rule to allow Azure services. + - If `postgreSqlAllowAzureServices = true`, deployment should already have created the Azure-services firewall rule. + - If it is not enabled in deployment, add the `0.0.0.0` firewall rule manually. - If you only need to read the password secret yourself, temporarily add only your client IP to Key Vault, retrieve the secret, then remove the IP again. 4. In Fabric, create a new **Mirrored Azure Database for PostgreSQL** item. 5. Use these deployment values instead of hardcoding names: @@ -134,6 +150,7 @@ If preflight fails, fix the runner first instead of continuing into SQL prep or What is automated today: - PostgreSQL server deployment during `azd up`. +- Optional PostgreSQL Azure-services firewall rule creation during `azd up` when `postgreSqlAllowAzureServices = true` and PostgreSQL public access is enabled. - PostgreSQL mirroring prep during `azd up` postprovision (server parameters, auth mode, mirroring role/grants, and seed table). - Manual or follow-up Fabric connection creation for PostgreSQL mirroring. - Manual or follow-up mirror creation after the Fabric connection is resolved. @@ -149,6 +166,7 @@ The Fabric mirroring API requires a Fabric "connection" object that stores the P - PostgreSQL authentication mode is **PostgreSQL and Microsoft Entra authentication** (password auth enabled). - You have access to the Key Vault that stores the PostgreSQL secrets. - Decide which connection mode you are using: `fabricUser` (default) or `admin` via `postgreSqlMirrorConnectionMode`. +- If you are using the public/manual path, prefer `postgreSqlAllowAzureServices = true` so Fabric can reach PostgreSQL without a VNet gateway. ## Step 1: Confirm PostgreSQL Details @@ -172,6 +190,15 @@ pwsh ./scripts/automationScripts/FabricWorkspace/Mirror/prepare_postgresql_for_m Re-run it manually only if you need to repair or reapply the PostgreSQL mirroring readiness settings. +> **Security step (manual demo path):** If you are not running from a VNet-connected host, temporarily enable Key Vault access and PostgreSQL firewall access for your client before running the script. Restore the locked-down settings immediately after. + +If you need the script to temporarily enable Key Vault public access while it runs, set: + +```powershell +$env:POSTGRES_TEMP_ENABLE_KV_PUBLIC_ACCESS = "true" +pwsh ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 +``` + ### Manual rerun Run: diff --git a/img/Architecture/Deploy-AI-App-in-Prod-Architecture_final.png b/img/Architecture/Deploy-AI-App-in-Prod-Architecture_final.png deleted file mode 100644 index 1b84712fb5831564382855186cbdfcdf858456ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116548 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%0DxN9p68Xk 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

ShE}J?_OEFSCE%9F0W$Hn0}PAN6W@z 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 diff --git a/docs/PARAMETER_GUIDE.md b/docs/parameter_guide.md similarity index 99% rename from docs/PARAMETER_GUIDE.md rename to docs/parameter_guide.md index f37f37c..d1be870 100644 --- a/docs/PARAMETER_GUIDE.md +++ b/docs/parameter_guide.md @@ -727,4 +727,4 @@ az deployment group what-if \ - **Quota errors**: Check regional quotas with `az vm list-usage` - **Network errors**: Verify CIDR ranges don't overlap -📖 **Deployment Guide**: [docs/DeploymentGuide.md](./DeploymentGuide.md) +📖 **Deployment Guide**: [docs/deploymentguide.md](./deploymentguide.md) diff --git a/docs/postgresql_mirroring.md b/docs/postgresql_mirroring.md index 3c5fe5e..3553757 100644 --- a/docs/postgresql_mirroring.md +++ b/docs/postgresql_mirroring.md @@ -21,14 +21,9 @@ If you are not changing the network approach right now, there are only two valid Do not expect a private-endpoint PostgreSQL deployment to produce a working Fabric mirror during the main deployment workflow alone. -## Minimal Manual Fallback +## Common Manual Flow (Non-VM Runner) -Use the shortest follow-up path below after deployment. - -Choose one path up front: - -- If you do not want to expose PostgreSQL publicly for Fabric, stop after the rest of post-provision validation and leave mirroring for a later run. -- If you want the mirror now, use the public-access path below. +This is the most common flow when you run `azd up` from a non-VNet machine. The postprovision mirroring prep often fails because Key Vault and PostgreSQL are private, so you finish the mirror manually. ### Public Access Enabled @@ -41,42 +36,39 @@ param postgreSqlNetworkIsolation = false param postgreSqlAllowAzureServices = true ``` -1. In Azure Portal, open the PostgreSQL Flexible Server. -2. Open **Fabric Mirroring** on the server and let the portal prepare the server-side prerequisites. - - Microsoft documentation explicitly calls out this page as the path that automates the server-side mirroring prerequisites. - - This overlaps with what `prepare_postgresql_for_mirroring.ps1` is trying to automate. - - It does **not** create the Fabric connection object or the mirrored database item in the Fabric workspace. -3. In **Networking**, make sure Fabric can reach the server. - - If `postgreSqlAllowAzureServices = true`, deployment should already have created the Azure-services firewall rule. - - If it is not enabled in deployment, add the `0.0.0.0` firewall rule manually. - - If you only need to read the password secret yourself, temporarily add only your client IP to Key Vault, retrieve the secret, then remove the IP again. -4. In Fabric, create a new **Mirrored Azure Database for PostgreSQL** item. -5. Use these deployment values instead of hardcoding names: +1. Run `azd up` and let postprovision finish (mirroring prep may fail on a non-VNet host). +2. In Azure Portal, open the Key Vault and temporarily enable public networking. +3. Re-run the prep script from your machine: ```powershell -azd env get-value postgreSqlServerFqdn -azd env get-value postgreSqlMirrorConnectionModeOut -azd env get-value postgreSqlMirrorConnectionUserNameOut -azd env get-value postgreSqlMirrorConnectionSecretNameOut +pwsh ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 ``` -6. Read the password from Key Vault: +4. Copy the `fabric_user` password from Key Vault: ```powershell +azd env get-value postgreSqlMirrorConnectionSecretNameOut az keyvault secret show --vault-name --name --query value -o tsv ``` -7. If the admin login fails in Fabric, switch to the dedicated Fabric PostgreSQL role instead of continuing to retry `pgadmin`. -8. After the connection is created, persist the connection ID for future reruns: +5. In Fabric, create a new **Mirrored Azure Database for PostgreSQL** item: -```powershell -azd env set-value fabricPostgresConnectionId "" -``` +- Server: PostgreSQL FQDN from `azd env get-value postgreSqlServerFqdn` +- Database: `postgres` (or your custom DB) +- Username: `fabric_user` +- Password: the Key Vault secret value + +6. Select **Connect** and verify the mirrored database appears. +7. Re-lock the Key Vault by disabling public networking after the connection succeeds. + +If the database or login fails, confirm `postgreSqlAllowAzureServices = true` (or add the `0.0.0.0` firewall rule) and re-run the prep script. ### Private Network or Private Endpoint Use this path when the PostgreSQL server is private-only or Fabric cannot reach it over public networking. +You must supply a Fabric VNet gateway ID for the connection flow in this mode. The repo may add a gateway option in a future update, but today you need to bring your own gateway and set `fabricPostgresGatewayId` before creating the connection. + 1. Treat mirroring as deferred for this provisioning cycle. 2. Use the PostgreSQL server's **Fabric Mirroring** page in Azure Portal only if you want to confirm the source-server prerequisite experience. 3. Continue validating the rest of the deployment: Fabric workspace, lakehouses, PostgreSQL server, AI Search, and Purview. @@ -84,13 +76,9 @@ Use this path when the PostgreSQL server is private-only or Fabric cannot reach ### What to Do First -If you just need the mirror working with the fewest manual steps: - -1. Prefer **Public Access Enabled** plus **Allow Azure services** when your deployment intentionally permits public connectivity. -2. Prefer the PostgreSQL server's **Fabric Mirroring** page in Azure Portal over running local SQL. -3. Use the dedicated Fabric role if the admin login is rejected by Fabric. +If you just need the mirror working with the fewest steps, follow the **Public Access Enabled** flow above. -If you are intentionally staying private for now, the correct action is to skip mirror creation for this provisioning test and continue validating the rest of the deployment. +If you are intentionally staying private for now, skip mirror creation for this provisioning test and continue validating the rest of the deployment. ## Recommended Repo Flow @@ -107,7 +95,7 @@ The cleanest sequence is: 1. Run `azd up`. 2. Validate the deployment with [post_deployment_steps.md](./post_deployment_steps.md). 3. Connect to the deployed VM or another runner with PostgreSQL network reachability. -4. Run the mirroring follow-up flow. +4. Run the mirroring follow-up flow, or use the manual steps above if you are not on the VNet. 5. Verify the Fabric connection and mirrored database. Running from the deployed VM is usually the least fragile option because it avoids local DNS, firewall, VPN, and endpoint-security issues. @@ -117,7 +105,7 @@ Running from the deployed VM is usually the least fragile option because it avoi If you want the repo-managed sequence, run: ```powershell -pwsh -NoProfile -File .\scripts\automationScripts\FabricWorkspace\Mirror\run_postgresql_mirroring_followup.ps1 +pwsh -NoProfile -File .\scripts\automationScripts\FabricWorkspace\mirror\run_postgresql_mirroring_followup.ps1 ``` That wrapper runs, in order: @@ -131,7 +119,7 @@ That wrapper runs, in order: Before attempting mirroring from a VM or any other runner, use the read-only preflight: ```powershell -pwsh -NoProfile -File .\scripts\automationScripts\FabricWorkspace\Mirror\test_postgresql_mirroring_prereqs.ps1 +pwsh -NoProfile -File .\scripts\automationScripts\FabricWorkspace\mirror\test_postgresql_mirroring_prereqs.ps1 ``` It checks the things that usually break the flow: @@ -164,7 +152,7 @@ The Fabric mirroring API requires a Fabric "connection" object that stores the P - Deployment finished, and PostgreSQL Flexible Server exists. - You can sign in to Fabric (app.fabric.microsoft.com) with access to the workspace. - PostgreSQL authentication mode is **PostgreSQL and Microsoft Entra authentication** (password auth enabled). -- You have access to the Key Vault that stores the PostgreSQL secrets. +- You have access to the Key Vault that stores the PostgreSQL secrets. (will require either connection via jumpbox vm, or temporarily enabling public access to keyvault to get the fabricUser secret) - Decide which connection mode you are using: `fabricUser` (default) or `admin` via `postgreSqlMirrorConnectionMode`. - If you are using the public/manual path, prefer `postgreSqlAllowAzureServices = true` so Fabric can reach PostgreSQL without a VNet gateway. @@ -182,10 +170,10 @@ Get the PostgreSQL server FQDN and database name: The mirroring prep script configures the server and creates a seed table so Fabric always finds at least one table to replicate. -During `azd up`, postprovision now runs: +During `azd up`, postprovision runs: ```powershell -pwsh ./scripts/automationScripts/FabricWorkspace/Mirror/prepare_postgresql_for_mirroring.ps1 +pwsh ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 ``` Re-run it manually only if you need to repair or reapply the PostgreSQL mirroring readiness settings. @@ -204,7 +192,7 @@ pwsh ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_m Run: ```powershell -pwsh ./scripts/automationScripts/FabricWorkspace/Mirror/prepare_postgresql_for_mirroring.ps1 +pwsh ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 ``` If you are running from a non-VNet host and the Key Vault blocks public access, set: @@ -261,7 +249,7 @@ az keyvault secret set --vault-name --name postgres-fabric-user- Run: ```powershell -pwsh ./scripts/automationScripts/FabricWorkspace/Mirror/create_postgresql_mirror.ps1 +pwsh ./scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 ``` What the script does now: @@ -316,7 +304,7 @@ azd env set-value POSTGRES_DATABASE_NAME "postgres" If the previous script already created the connection automatically, re-running it is safe and idempotent. If you created the connection manually, run it once now: ```powershell -./scripts/automationScripts/FabricWorkspace/Mirror/create_postgresql_mirror.ps1 +./scripts/automationScripts/FabricWorkspace/mirror/create_postgresql_mirror.ps1 ``` ## Verify diff --git a/docs/re-use-log-analytics.md b/docs/re-use-log-analytics.md deleted file mode 100644 index 342ea34..0000000 --- a/docs/re-use-log-analytics.md +++ /dev/null @@ -1,31 +0,0 @@ -[← Back to *DEPLOYMENT* guide](DeploymentGuide.md) - -# Reusing an Existing Log Analytics Workspace -To configure your environment to use an existing Log Analytics Workspace, follow these steps: ---- -### 1. Go to Azure Portal -Go to https://portal.azure.com - -### 2. Search for Log Analytics -In the search bar at the top, type "Log Analytics workspaces" and click on it and click on the workspace you want to use. - -![alt text](./images/re_use_log/logAnalyticsList.png) - -### 3. Copy Resource ID -In the Overview pane, Click on JSON View - -![alt text](./images/re_use_log/logAnalytics.png) - -Copy Resource ID that is your Workspace ID - -![alt text](./images/re_use_log/logAnalyticsJson.png) - -### 4. Set the Workspace ID in Your Environment -Run the following command in your terminal -```bash -azd env set AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID '' -``` -Replace `` with the value obtained from Step 3. - -### 5. Continue Deployment -Proceed with the next steps in the [Deployment Guide](DeploymentGuide.md). diff --git a/docs/required_roles_scopes_resources.md b/docs/required_roles_scopes_resources.md new file mode 100644 index 0000000..237043a --- /dev/null +++ b/docs/required_roles_scopes_resources.md @@ -0,0 +1,43 @@ +# Required roles and scopes for deployment + +This repository now deploys the AI Landing Zone submodule plus optional Fabric capacity, private endpoints, and PostgreSQL. The required access is driven by the role assignments defined in the submodule and the feature flags you enable (for example `deployContainerApps`, `deployAiFoundry`, `deployFabricCapacity`, `deployPostgreSql`). + +## Minimum permissions + +You need permissions to create resources and to write role assignments. The deployment assigns roles to the executor, container app identities, the AI Foundry project, Search, and (optionally) the jumpbox VM. That requires `Microsoft.Authorization/roleAssignments/write` at the resource scope. + +- **Recommended (simplest):** `Owner` at the subscription scope. +- **Least privilege (typical):** `Contributor` + `User Access Administrator` at the subscription scope. +- **Existing resource group:** `Contributor` + `User Access Administrator` at the resource group scope. + +If you cannot grant role assignments at the scope where resources are created, the deployment will fail when it attempts to assign RBAC roles. + +## Role assignments created by the template + +The full list of default role assignments is maintained in the AI Landing Zone submodule. These assignments change if you modify `containerAppsList` or disable services. + +- Default role assignment matrix: [submodules/ai-landing-zone/README.md](submodules/ai-landing-zone/README.md) +- Default container app roles (driven by `containerAppsList`): [infra/main.bicepparam](infra/main.bicepparam) + +## Required resource providers + +Register these providers in the subscription before deployment. Optional providers are only needed if the related feature flag is enabled. + +| Provider | When used | +|---|---| +| Microsoft.Authorization | Role assignments (always) | +| Microsoft.CognitiveServices | AI Foundry account, projects, and connections (deployAiFoundry) | +| Microsoft.Search | Azure AI Search (deploySearchService) | +| Microsoft.Storage | Storage accounts and blob containers (deployStorageAccount) | +| Microsoft.KeyVault | Key Vault and secrets (deployKeyVault, deployVmKeyVault, PostgreSQL secrets) | +| Microsoft.ContainerRegistry | ACR (deployContainerRegistry) | +| Microsoft.App | Container Apps and environments (deployContainerApps, deployContainerEnv) | +| Microsoft.DocumentDB | Cosmos DB account and data-plane roles (deployCosmosDb) | +| Microsoft.ManagedIdentity | User-assigned identities (useUAI) | +| Microsoft.Insights | Application Insights and private link scopes (deployAppInsights) | +| Microsoft.OperationalInsights | Log Analytics (deployLogAnalytics) | +| Microsoft.Network | VNets, subnets, private endpoints, private DNS, Bastion, NAT (networkIsolation or deployVM) | +| Microsoft.Compute | Jumpbox VM and extensions (deployVM) | +| Microsoft.Bing | Bing grounding (deployGroundingWithBing) | +| Microsoft.Fabric | Fabric capacity and private link services (deployFabricCapacity, Fabric private endpoint) | +| Microsoft.DBforPostgreSQL | PostgreSQL Flexible Server (deployPostgreSql) | diff --git a/docs/TRANSPARENCY_FAQ.md b/docs/transparency_faq.md similarity index 99% rename from docs/TRANSPARENCY_FAQ.md rename to docs/transparency_faq.md index 5944ac0..c8765e6 100644 --- a/docs/TRANSPARENCY_FAQ.md +++ b/docs/transparency_faq.md @@ -1,4 +1,4 @@ -# Responsible AI Transparency FAQ +# responsible ai transparency faq ## Deploy Your AI Application In Production: Responsible AI FAQ From 1acf2140d2c434622bf496468cd7e8d0c4af70d5 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Wed, 25 Mar 2026 14:23:30 -0400 Subject: [PATCH 19/22] update post deployment steps and checks to align to new infrastructure --- docs/post_deployment_steps.md | 51 +++++++++++------------------------ 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/docs/post_deployment_steps.md b/docs/post_deployment_steps.md index 1aa743e..627e2b0 100644 --- a/docs/post_deployment_steps.md +++ b/docs/post_deployment_steps.md @@ -49,38 +49,9 @@ az fabric capacity resume --capacity-name --resource-group **Security step (required for manual mirroring):** The mirroring prep script must run from a VNet-connected host when Key Vault and PostgreSQL are private. If you want to demo mirroring end-to-end from a non-VNet machine, temporarily open access to both Key Vault and PostgreSQL before running the script, then lock them down afterward. - -If you must run the mirroring prep from a non-VNet host, set the temporary Key Vault override before you run the script: - -```powershell -$env:POSTGRES_TEMP_ENABLE_KV_PUBLIC_ACCESS = "true" -pwsh ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 -``` - -For post-deployment verification, the important distinction is simple: - -- If you did not intentionally run the mirroring follow-up, treat mirroring as deferred and do not use it as a deployment success criterion. -- If you did run the mirroring follow-up, verify the Fabric connection and mirrored database from the workspace. - -If you need to complete mirroring after deployment, use the dedicated steps in [PostgreSQL mirroring](./postgresql_mirroring.md). - -The PostgreSQL server's **Fabric Mirroring** page only covers the source-server prerequisite preparation. It does not replace the Fabric workspace connection and mirrored database creation steps. - -1. Check the resolved mirroring identity instead of hardcoding it: - - `azd env get-value postgreSqlMirrorConnectionModeOut` - - `azd env get-value postgreSqlMirrorConnectionUserNameOut` - - `azd env get-value postgreSqlMirrorConnectionSecretNameOut` -2. If you have not run the separate mirroring follow-up, stop here for this test cycle. - - The deployment can still be considered successful for Fabric workspace, PostgreSQL server, and Purview automation. - - PostgreSQL mirroring remains a documented follow-up item, not a same-run success criterion. -3. If you want mirroring now, follow the current runbook in [PostgreSQL mirroring](./postgresql_mirroring.md). -4. After the follow-up completes, verify `azd env get-value fabricPostgresConnectionId` returns a Fabric connection ID. -5. In Fabric, confirm the PostgreSQL connection exists under **Connections** and that the mirrored database is running. +For the full steps (including the Fabric portal **New item** mirror), follow [PostgreSQL mirroring](./postgresql_mirroring.md). --- @@ -113,8 +84,20 @@ If the connection fails, verify RBAC roles are assigned (see Troubleshooting sec - **Status**: Success - **Last run**: Recent timestamp +> **Note:** Uploading new files to the bronze lakehouse does not auto-trigger the indexer. Re-run it manually after uploads: + +```bash +az search indexer run --name onelake-indexer --service-name --resource-group +``` + ### Test the Index +Re-index after uploads if you do not see new documents: + +```bash +az search indexer run --name onelake-indexer --service-name --resource-group +``` + 1. In the Search service, go to **Search explorer** 2. Run a simple query: `*` 3. Verify documents are returned @@ -142,11 +125,9 @@ pwsh ./scripts/automationScripts/FabricWorkspace/CreateWorkspace/register_fabric pwsh ./scripts/automationScripts/FabricPurviewAutomation/trigger_purview_scan_for_fabric_workspace.ps1 ``` -### Data Lineage +### Data Lineage (Optional) -1. In Purview, go to **Data Catalog** → **Browse** -2. Search for your lakehouse assets -3. Verify lineage shows data flow from bronze → silver → gold +Lineage appears only after you run data movement or transformation jobs (for example, copying data from bronze to silver). If you have not moved data yet, skip lineage verification. --- From a13ef5d469fb071b4123925e8284daf00ab6f8a0 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Thu, 26 Mar 2026 12:34:43 -0400 Subject: [PATCH 20/22] Updates and clarification of post provisioning script and environment validation. Update PostgreSQL mirroring steps --- docs/deploy_app_from_foundry.md | 4 ++ docs/post_deployment_steps.md | 94 +++++++++++++++++++++++---------- docs/postgresql_mirroring.md | 39 +++++++++----- 3 files changed, 97 insertions(+), 40 deletions(-) diff --git a/docs/deploy_app_from_foundry.md b/docs/deploy_app_from_foundry.md index b7f99e6..bd21729 100644 --- a/docs/deploy_app_from_foundry.md +++ b/docs/deploy_app_from_foundry.md @@ -6,6 +6,8 @@ This guide explains how to deploy a chat application directly from the Microsoft Microsoft Foundry provides a built-in capability to publish playground experiences as web applications. This accelerator deploys the required infrastructure (App Service, managed identity, networking) so you can publish directly from the Foundry playground. +> **UI note:** The new Foundry experience (the "New Foundry" toggle) does not currently show **Deploy to a web app**. Use the classic Foundry UI to publish for now. We will update this guide once the new UI supports web app deployment. + ## Prerequisites - Completed deployment of this accelerator (`azd up`) @@ -42,6 +44,8 @@ Since all resources are deployed with private endpoints, you must access Microso ### 4. Deploy to Web App +> **UI note:** If the "New Foundry" toggle is enabled, the **Deploy to a web app** option may be hidden. Switch to the classic Foundry UI to publish the web app. + 1. Click **Deploy** → **Deploy to a web app** 2. Configure deployment options: - **Create new** or **Update existing** web app diff --git a/docs/post_deployment_steps.md b/docs/post_deployment_steps.md index 627e2b0..59df07d 100644 --- a/docs/post_deployment_steps.md +++ b/docs/post_deployment_steps.md @@ -1,6 +1,6 @@ # Post Deployment Steps -After running `azd up` or `azd provision` followed by `azd hooks run postprovision`, use these steps to verify that all components were deployed correctly and are functioning as expected. +After running `azd up` or `azd provision` which then trigger the `azd hooks run postprovision`, use these steps to verify that all components were deployed correctly and are functioning as expected. --- @@ -10,8 +10,9 @@ After running `azd up` or `azd provision` followed by `azd hooks run postprovisi |-----------|---------------|----------------| | Fabric Capacity | Azure Portal → Microsoft Fabric capacities | **Active** (not Paused) | | Fabric Workspace | [app.fabric.microsoft.com](https://app.fabric.microsoft.com) | Workspace visible with 3 lakehouses | +| PostgreSQL Flexible Server | Azure Portal → Azure Database for PostgreSQL flexible servers | **Ready** | | Microsoft Foundry project | [ai.azure.com](https://ai.azure.com) | Project accessible, models deployed | -| AI Search Index | Azure Portal → AI Search → Indexes | `onelake-index` exists with documents | +| AI Search Index | Azure Portal → AI Search → Indexes | `onelake-index` exists | | Purview Scan | Purview Portal → Data Map → Sources | Fabric data source registered | --- @@ -45,33 +46,30 @@ az fabric capacity resume --capacity-name --resource-group .postgres.database.azure.com port=5432 dbname= user= sslmode=require" +``` --- @@ -108,12 +106,46 @@ If no documents appear, check: --- -## 5. Verify Purview Integration (if enabled) +## 5. Verify Microsoft Foundry Project + +1. Navigate to [ai.azure.com](https://ai.azure.com) +2. Sign in and select your Microsoft Foundry project +3. Verify: + - **Models** — Check that GPT-4o and text-embedding-ada-002 (or configured models) are deployed + - **Connections** — AI Search connection should be listed + - **Playground** — Test the chat playground with a sample query + +### Testing AI Search Connection in Playground + +Before testing, upload at least one sample PDF into the bronze lakehouse (Files/documents) and re-run the indexer. + +Re-run the indexer in the Azure portal: + +1. Navigate to **Azure Portal** → **AI Search** → your search service +2. Go to **Indexers** and select `onelake-indexer` +3. Click **Run** + +Or run it from the CLI: + +```bash +az search indexer run --name onelake-indexer --service-name --resource-group +``` + +1. In Microsoft Foundry, go to **Playgrounds** → **Chat** +2. Click **Add your data** +3. Select your AI Search index (`onelake-index`) +4. Ask a question about your indexed documents + +If the connection fails, verify RBAC roles are assigned (see Troubleshooting section). + +--- + +## 6. Verify Purview Integration (if enabled) 1. Navigate to the **Microsoft Purview governance portal** 2. Go to **Data Map** → **Sources** -3. Verify the Fabric data source is registered (e.g., `Fabric-Workspace-`) -4. Check **Scans** to see if the initial scan completed +3. Verify the Fabric data source is registered at the container level and the collection is `collection-` +4. Check **Scans** to confirm the workspace-scoped scan completed If `purviewCollectionName` is left empty in [infra/main.bicepparam](../infra/main.bicepparam), the automation now uses `collection-`. @@ -131,9 +163,9 @@ Lineage appears only after you run data movement or transformation jobs (for exa --- -## 6. Verify Network Isolation (if enabled) +## 7. Verify Network Isolation in Azure Portal (if enabled) -When `networkIsolation` is set to `true`: +When `networkIsolation` is set to `true` in [infra/main.bicepparam](../infra/main.bicepparam) during provisioning: ### Check Microsoft Foundry Network Settings @@ -159,7 +191,7 @@ This is **expected behavior** — the resources are only accessible from within --- -## 7. Connecting via Bastion (Network Isolated Deployments) +## 8. Connecting via Bastion (Network Isolated Deployments) For network-isolated deployments, use Azure Bastion to access resources: @@ -176,6 +208,9 @@ For network-isolated deployments, use Azure Bastion to access resources: ![Image showing bastion blade](../img/provisioning/checkNetworkIsolation7.png) 4. Enter the VM admin credentials (set during deployment) and click **Connect** + - Admin username: `vmUserName` in [infra/main.bicep](../infra/main.bicep) + - Admin password: `vmAdminPassword` in [infra/main.bicepparam](../infra/main.bicepparam) (defaults to the `VM_ADMIN_PASSWORD` environment variable) + - If you do not have them, reset the password in **Azure Portal** → **Virtual machine** → **Reset password**. ![Image showing bastion login](../img/provisioning/checkNetworkIsolation8.png) @@ -232,6 +267,7 @@ pwsh ./scripts/automationScripts/OneLakeIndex/06_setup_ai_foundry_search_rbac.ps 1. Verify documents exist in the bronze lakehouse: - Go to Fabric → bronze lakehouse → Files → documents + - If needed, follow [Testing AI Search Connection in Playground](#testing-ai-search-connection-in-playground) to upload a sample PDF 2. Check indexer status: - Azure Portal → AI Search → Indexers → `onelake-indexer` @@ -275,8 +311,10 @@ pwsh ./scripts/automationScripts/.ps1 Once verification is complete: -1. **Upload documents** to the bronze lakehouse for indexing -2. **Test the Microsoft Foundry playground** with your indexed content -3. **Configure additional models** if needed -4. **[Deploy your app](./deploy_app_from_foundry.md)** from the Microsoft Foundry playground -5. **Review governance** in Microsoft Purview +1. **Upload documents** to the bronze lakehouse for indexing (if you haven't already in previous steps) +2. **Test PostgreSQL connectivity** (if you plan to use the database) +3. **Complete PostgreSQL mirroring in Fabric** (if needed) — follow [PostgreSQL mirroring](./postgresql_mirroring.md) +4. **Test the Microsoft Foundry playground** with your indexed content +5. **Configure additional models** if needed +6. **[Deploy your app](./deploy_app_from_foundry.md)** from the Microsoft Foundry playground +7. **Review governance** in Microsoft Purview diff --git a/docs/postgresql_mirroring.md b/docs/postgresql_mirroring.md index 3553757..9102a6b 100644 --- a/docs/postgresql_mirroring.md +++ b/docs/postgresql_mirroring.md @@ -4,6 +4,13 @@ This guide explains how to complete PostgreSQL mirroring in Microsoft Fabric aft > **Security-critical note:** The mirroring prep script must run from a VNet-connected host when Key Vault and PostgreSQL are private. If you want to demo the full end-to-end mirroring flow from a non-VNet machine, you must temporarily open access to both Key Vault and PostgreSQL before running the script, then re-lock them afterward. Treat this as a deliberate security step, not a default configuration. +> **Resource naming note:** The AI Landing Zone submodule deploys Foundry and agent resources with an `ai-` prefix, including a separate Key Vault, Storage Account, and Cosmos DB. PostgreSQL mirroring uses the main deployment Key Vault from `keyVaultResourceId` (this is where the `postgreSql*` secrets live), not the `ai-` prefixed Key Vault. When a step says "Key Vault" in this doc, use the Key Vault from `keyVaultResourceId`. +> +> **How to find `keyVaultResourceId`:** +> - Run `azd env get-value keyVaultResourceId` from the repo root. +> - Or run `azd env get-values` and look for `keyVaultResourceId`. +> - Or in Azure Portal, open the Key Vault used for deployment and copy its **Resource ID** from the **Overview** blade. + Mirroring automation in the current branch is set for PostgreSQL deployments where `postgreSqlNetworkIsolation = false`. For the public/manual path, this repo now supports a declarative firewall toggle through `postgreSqlAllowAzureServices`. @@ -36,15 +43,18 @@ param postgreSqlNetworkIsolation = false param postgreSqlAllowAzureServices = true ``` -1. Run `azd up` and let postprovision finish (mirroring prep may fail on a non-VNet host). -2. In Azure Portal, open the Key Vault and temporarily enable public networking. -3. Re-run the prep script from your machine: +1. Run `azd up` and let postprovision finish (mirroring prep may warn on a non-VNet host). +2. Re-run the prep script from your machine (it configures PostgreSQL auth, creates the mirror user/role grants, and ensures a seed table exists for Fabric). The script will temporarily enable Key Vault public access for its own secret operations, then disable it again: ```powershell pwsh ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 ``` -4. Copy the `fabric_user` password from Key Vault: +3. In Azure Portal, open the Key Vault from `keyVaultResourceId` and temporarily enable public networking so you can copy the password: + - Azure Portal -> search for **Key Vaults** -> select the Key Vault that matches the name in the resource ID + - Go to **Networking** -> set **Public network access** to **Enabled** + - Select **Apply** to save +4. Copy the `fabric_user` password from that Key Vault (you will paste it into the Fabric connection wizard): ```powershell azd env get-value postgreSqlMirrorConnectionSecretNameOut @@ -52,14 +62,19 @@ az keyvault secret show --vault-name --name --quer ``` 5. In Fabric, create a new **Mirrored Azure Database for PostgreSQL** item: - -- Server: PostgreSQL FQDN from `azd env get-value postgreSqlServerFqdn` -- Database: `postgres` (or your custom DB) -- Username: `fabric_user` -- Password: the Key Vault secret value - -6. Select **Connect** and verify the mirrored database appears. -7. Re-lock the Key Vault by disabling public networking after the connection succeeds. + - Go to [app.fabric.microsoft.com](https://app.fabric.microsoft.com) and open your workspace (for example, `workspace-`) + - Select **New item** -> **Mirror data** -> **Azure Database for PostgreSQL** + - Enter: + - Server: PostgreSQL FQDN from `azd env get-value postgreSqlServerFqdn` + - Database: `postgres` (or your custom DB) + - Username: `fabric_user` + - Password: the Key Vault secret value + - For full portal screenshots and walkthrough, see [Tutorial: Configure Microsoft Fabric mirrored databases from Azure Database for PostgreSQL](https://learn.microsoft.com/fabric/mirroring/azure-database-postgresql-tutorial). + +6. Choose **Select data**, pick the `public.fabric_mirror_seed` table, preview the row, then select **Connect**. +7. On the next screen, name the mirror (or accept the default) and select **Create mirrored database**. +8. Verify the mirrored database appears. +9. Re-lock the Key Vault by disabling public networking after the connection succeeds. If the database or login fails, confirm `postgreSqlAllowAzureServices = true` (or add the `0.0.0.0` firewall rule) and re-run the prep script. From a02a782e9033ac6d58a36f8e44bbf59cf1b80f40 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Thu, 26 Mar 2026 15:21:23 -0400 Subject: [PATCH 21/22] Update mirror docs and corrected powershell try, catch, fail loops --- docs/postgresql_mirroring.md | 55 +++---------------- docs/required_roles_scopes_resources.md | 4 +- infra/main.bicepparam | 6 +- .../02_create_onelake_skillsets.ps1 | 2 +- .../OneLakeIndex/03_create_onelake_index.ps1 | 2 +- .../04_create_onelake_datasource.ps1 | 2 +- .../05_create_onelake_indexer.ps1 | 2 +- 7 files changed, 18 insertions(+), 55 deletions(-) diff --git a/docs/postgresql_mirroring.md b/docs/postgresql_mirroring.md index 9102a6b..6787306 100644 --- a/docs/postgresql_mirroring.md +++ b/docs/postgresql_mirroring.md @@ -44,24 +44,18 @@ param postgreSqlAllowAzureServices = true ``` 1. Run `azd up` and let postprovision finish (mirroring prep may warn on a non-VNet host). -2. Re-run the prep script from your machine (it configures PostgreSQL auth, creates the mirror user/role grants, and ensures a seed table exists for Fabric). The script will temporarily enable Key Vault public access for its own secret operations, then disable it again: - -```powershell -pwsh ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 -``` - -3. In Azure Portal, open the Key Vault from `keyVaultResourceId` and temporarily enable public networking so you can copy the password: +2. In Azure Portal, open the Key Vault from `keyVaultResourceId` and temporarily enable public networking so you can copy the password: - Azure Portal -> search for **Key Vaults** -> select the Key Vault that matches the name in the resource ID - Go to **Networking** -> set **Public network access** to **Enabled** - Select **Apply** to save -4. Copy the `fabric_user` password from that Key Vault (you will paste it into the Fabric connection wizard): +3. Copy the `fabric_user` password from that Key Vault (you will paste it into the Fabric connection wizard): ```powershell azd env get-value postgreSqlMirrorConnectionSecretNameOut az keyvault secret show --vault-name --name --query value -o tsv ``` -5. In Fabric, create a new **Mirrored Azure Database for PostgreSQL** item: +4. In Fabric, create a new **Mirrored Azure Database for PostgreSQL** item: - Go to [app.fabric.microsoft.com](https://app.fabric.microsoft.com) and open your workspace (for example, `workspace-`) - Select **New item** -> **Mirror data** -> **Azure Database for PostgreSQL** - Enter: @@ -71,12 +65,12 @@ az keyvault secret show --vault-name --name --quer - Password: the Key Vault secret value - For full portal screenshots and walkthrough, see [Tutorial: Configure Microsoft Fabric mirrored databases from Azure Database for PostgreSQL](https://learn.microsoft.com/fabric/mirroring/azure-database-postgresql-tutorial). -6. Choose **Select data**, pick the `public.fabric_mirror_seed` table, preview the row, then select **Connect**. -7. On the next screen, name the mirror (or accept the default) and select **Create mirrored database**. -8. Verify the mirrored database appears. -9. Re-lock the Key Vault by disabling public networking after the connection succeeds. +5. Choose **Select data**, pick the `public.fabric_mirror_seed` table, preview the row, then select **Connect**. +6. On the next screen, name the mirror (or accept the default) and select **Create mirrored database**. +7. Verify the mirrored database appears. +8. Re-lock the Key Vault by disabling public networking after the connection succeeds. -If the database or login fails, confirm `postgreSqlAllowAzureServices = true` (or add the `0.0.0.0` firewall rule) and re-run the prep script. +If the database or login fails, confirm `postgreSqlAllowAzureServices = true` (or add the `0.0.0.0` firewall rule). ### Private Network or Private Endpoint @@ -183,38 +177,7 @@ Get the PostgreSQL server FQDN and database name: ## Step 2: Prepare the Database (Run Automatically During Postprovision) -The mirroring prep script configures the server and creates a seed table so Fabric always finds at least one table to replicate. - -During `azd up`, postprovision runs: - -```powershell -pwsh ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 -``` - -Re-run it manually only if you need to repair or reapply the PostgreSQL mirroring readiness settings. - -> **Security step (manual demo path):** If you are not running from a VNet-connected host, temporarily enable Key Vault access and PostgreSQL firewall access for your client before running the script. Restore the locked-down settings immediately after. - -If you need the script to temporarily enable Key Vault public access while it runs, set: - -```powershell -$env:POSTGRES_TEMP_ENABLE_KV_PUBLIC_ACCESS = "true" -pwsh ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 -``` - -### Manual rerun - -Run: - -```powershell -pwsh ./scripts/automationScripts/FabricWorkspace/mirror/prepare_postgresql_for_mirroring.ps1 -``` - -If you are running from a non-VNet host and the Key Vault blocks public access, set: - -```powershell -$env:POSTGRES_TEMP_ENABLE_KV_PUBLIC_ACCESS = 'true' -``` +The mirroring prep script configures the server and creates a seed table so Fabric always finds at least one table to replicate. It runs during `azd up` postprovision. What it does: diff --git a/docs/required_roles_scopes_resources.md b/docs/required_roles_scopes_resources.md index 237043a..3480959 100644 --- a/docs/required_roles_scopes_resources.md +++ b/docs/required_roles_scopes_resources.md @@ -16,8 +16,8 @@ If you cannot grant role assignments at the scope where resources are created, t The full list of default role assignments is maintained in the AI Landing Zone submodule. These assignments change if you modify `containerAppsList` or disable services. -- Default role assignment matrix: [submodules/ai-landing-zone/README.md](submodules/ai-landing-zone/README.md) -- Default container app roles (driven by `containerAppsList`): [infra/main.bicepparam](infra/main.bicepparam) +- Default role assignment matrix: [submodules/ai-landing-zone/README.md](../submodules/ai-landing-zone/README.md) +- Default container app roles (driven by `containerAppsList`): [infra/main.bicepparam](../infra/main.bicepparam) ## Required resource providers diff --git a/infra/main.bicepparam b/infra/main.bicepparam index a264cdf..d1eb078 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -24,7 +24,7 @@ param useExistingVNet = false param existingVnetResourceId = readEnvironmentVariable('EXISTING_VNET_RESOURCE_ID', '') // Optional additional Entra object IDs to grant Search roles. -param aiSearchAdditionalAccessObjectIds = [''] +param aiSearchAdditionalAccessObjectIds = [] // ======================================== // OPTIONAL INPUTS (Configuration) @@ -205,7 +205,7 @@ param containerAppsList = [ ] param vmAdminPassword = readEnvironmentVariable('VM_ADMIN_PASSWORD', '$(secretOrRandomPassword)') -param vmSize = 'Standard_D8s_v5' +param vmSize = 'Standard_D2s_v3' // ======================================== // FABRIC CAPACITY PARAMETERS @@ -245,7 +245,7 @@ param fabricWorkspaceName = '' // optional (helpful for naming/UX) param fabricCapacitySku = 'F8' // Fabric capacity admin members (email addresses or object IDs). -param fabricCapacityAdmins = [''] +param fabricCapacityAdmins = [] // ======================================== // PURVIEW PARAMETERS (Optional) diff --git a/scripts/automationScripts/OneLakeIndex/02_create_onelake_skillsets.ps1 b/scripts/automationScripts/OneLakeIndex/02_create_onelake_skillsets.ps1 index 3b683e1..3d5e383 100644 --- a/scripts/automationScripts/OneLakeIndex/02_create_onelake_skillsets.ps1 +++ b/scripts/automationScripts/OneLakeIndex/02_create_onelake_skillsets.ps1 @@ -110,7 +110,7 @@ try { Write-Host "✅ Successfully created skillset: $($response.name)" } catch { Write-Error "Failed to create skillset: $($_.Exception.Message)" - exit 1 + throw } Write-Host "" diff --git a/scripts/automationScripts/OneLakeIndex/03_create_onelake_index.ps1 b/scripts/automationScripts/OneLakeIndex/03_create_onelake_index.ps1 index 3a7ee43..3dc7134 100644 --- a/scripts/automationScripts/OneLakeIndex/03_create_onelake_index.ps1 +++ b/scripts/automationScripts/OneLakeIndex/03_create_onelake_index.ps1 @@ -234,7 +234,7 @@ try { $errorContent = $_.Exception.Response.Content.ReadAsStringAsync().Result Write-Host "Error details: $errorContent" } - exit 1 + throw } Write-Host "" diff --git a/scripts/automationScripts/OneLakeIndex/04_create_onelake_datasource.ps1 b/scripts/automationScripts/OneLakeIndex/04_create_onelake_datasource.ps1 index b3cfdd3..d6126eb 100644 --- a/scripts/automationScripts/OneLakeIndex/04_create_onelake_datasource.ps1 +++ b/scripts/automationScripts/OneLakeIndex/04_create_onelake_datasource.ps1 @@ -109,7 +109,7 @@ Write-Host "=================================================================" if (-not $aiSearchName -or -not $resourceGroup -or -not $subscription) { Write-Error "AI Search configuration not found (name='$aiSearchName', rg='$resourceGroup', subscription='$subscription'). Cannot create OneLake data source." - exit 1 + throw } if (-not $workspaceId -or -not $lakehouseId) { diff --git a/scripts/automationScripts/OneLakeIndex/05_create_onelake_indexer.ps1 b/scripts/automationScripts/OneLakeIndex/05_create_onelake_indexer.ps1 index 8cc7881..0125f09 100644 --- a/scripts/automationScripts/OneLakeIndex/05_create_onelake_indexer.ps1 +++ b/scripts/automationScripts/OneLakeIndex/05_create_onelake_indexer.ps1 @@ -97,7 +97,7 @@ Write-Host "==============================================================" if (-not $aiSearchName -or -not $resourceGroup -or -not $subscription) { Write-Error "AI Search configuration not found (name='$aiSearchName', rg='$resourceGroup', subscription='$subscription'). Cannot create OneLake indexer." - exit 1 + throw } . "$PSScriptRoot/SearchHelpers.ps1" From 25dcdf89e8fcb637a861423a8df3568d2f0bfa42 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Thu, 26 Mar 2026 17:20:28 -0400 Subject: [PATCH 22/22] update to docs to correct failing links --- README.md | 2 +- docs/deploymentguide.md | 4 ++-- docs/required_roles_scopes_resources.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0247025..de4b868 100644 --- a/README.md +++ b/README.md @@ -283,7 +283,7 @@ Supporting documentation | [Required Roles & Scopes](./docs/required_roles_scopes_resources.md) | IAM requirements for deployment | | [Parameter Guide](./docs/parameter_guide.md) | All deployment parameters, toggles & model configs | | [Deploy App from Foundry](./docs/deploy_app_from_foundry.md) | Publish playground to App Service | -| [Accessing Private Resources](./docs/accessing_private_resources.md) | Connect via Jump VM | +| [Accessing Private Resources](./docs/ACCESSING_PRIVATE_RESOURCES.md) | Connect via Jump VM | ### Security Guidelines diff --git a/docs/deploymentguide.md b/docs/deploymentguide.md index 37a9402..8da0889 100644 --- a/docs/deploymentguide.md +++ b/docs/deploymentguide.md @@ -216,7 +216,7 @@ To check and adjust quota settings, follow the [Quota Check Guide](./quota_check

Reusing Existing Resources **Log Analytics Workspace:** -See [Re-use Log Analytics](./re-use-log-analytics.md) for instructions. +See [Parameter Guide](./parameter_guide.md) for Log Analytics reuse guidance.

Gg}?3!P-XxY=tcg`L+Iq%pRjRA6} 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 Ch~NwW literal 99929 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 diff --git a/infra/main.bicep b/infra/main.bicep index f4543c8..ee3c4bc 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -218,6 +218,9 @@ param postgreSqlServerName string = 'pg${resourceToken}' @description('Enable network isolation for PostgreSQL (private DNS + private endpoint).') param postgreSqlNetworkIsolation bool = networkIsolation +@description('Allow connections from Azure services to the PostgreSQL server when public access is enabled. This creates the 0.0.0.0 firewall rule equivalent to the portal Allow Azure services setting.') +param postgreSqlAllowAzureServices bool = false + @description('Create and link the PostgreSQL private DNS zone to the VNet.') param deployPostgreSqlPrivateDnsLink bool = true @@ -406,6 +409,22 @@ module postgreSqlFlexibleServer 'br/public:avm/res/db-for-postgre-sql/flexible-s } } +resource postgreSqlFlexibleServerResource 'Microsoft.DBforPostgreSQL/flexibleServers@2025-06-01-preview' existing = if (deployPostgreSql) { + name: postgreSqlServerName +} + +resource postgreSqlAllowAzureServicesFirewallRule 'Microsoft.DBforPostgreSQL/flexibleServers/firewallRules@2025-01-01-preview' = if (deployPostgreSql && !postgreSqlNetworkIsolation && postgreSqlAllowAzureServices) { + parent: postgreSqlFlexibleServerResource + name: 'AllowAzureServices' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + dependsOn: [ + postgreSqlFlexibleServer + ] +} + resource postgreSqlAdminSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = if (deployPostgreSql && enablePostgreSqlKeyVaultSecret) { name: postgreSqlAdminSecretName parent: keyVault diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 635b4a4..a264cdf 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -32,12 +32,15 @@ param aiSearchAdditionalAccessObjectIds = [''] param deploymentTags = {} param appConfigLabel = 'ai-lz' +// Create the provisioning of the AI landing zone with network isolation (vnet,private end points, etc) param networkIsolation = true // Coordinate PostgreSQL networking with the overall isolation flag by default. -param postgreSqlNetworkIsolation = networkIsolation +param postgreSqlNetworkIsolation = false +// Allow Fabric and other Azure services to reach PostgreSQL when public access is enabled. +param postgreSqlAllowAzureServices = true // Skip this if a PostgreSQL private DNS zone is already linked to the VNet. -param deployPostgreSqlPrivateDnsLink = true +param deployPostgreSqlPrivateDnsLink = false // Optional: use an existing VNet link name to avoid conflicts. param postgreSqlPrivateDnsLinkNameOverride = '' From 0291ac67485ae7bebb7e2284d368314819837792 Mon Sep 17 00:00:00 2001 From: Mike Swantek Date: Wed, 25 Mar 2026 12:10:21 -0400 Subject: [PATCH 18/22] updates to documentation, aligning all folders and files to lower case naming --- CHANGELOG.md | 14 +- README.md | 16 +- docs/Required_roles_scopes_resources.md | 26 --- docs/automation-outputs-mapping.md | 182 +++++++----------- ...{DeploymentGuide.md => deploymentguide.md} | 6 +- docs/faq.md | 35 +--- docs/images/re_use_log/logAnalytics.png | Bin 145324 -> 0 bytes docs/images/re_use_log/logAnalyticsJson.png | Bin 201052 -> 0 bytes docs/images/re_use_log/logAnalyticsList.png | Bin 90986 -> 0 bytes ...{PARAMETER_GUIDE.md => parameter_guide.md} | 2 +- docs/postgresql_mirroring.md | 74 +++---- docs/re-use-log-analytics.md | 31 --- docs/required_roles_scopes_resources.md | 43 +++++ ...RANSPARENCY_FAQ.md => transparency_faq.md} | 2 +- 14 files changed, 171 insertions(+), 260 deletions(-) delete mode 100644 docs/Required_roles_scopes_resources.md rename docs/{DeploymentGuide.md => deploymentguide.md} (98%) delete mode 100644 docs/images/re_use_log/logAnalytics.png delete mode 100644 docs/images/re_use_log/logAnalyticsJson.png delete mode 100644 docs/images/re_use_log/logAnalyticsList.png rename docs/{PARAMETER_GUIDE.md => parameter_guide.md} (99%) delete mode 100644 docs/re-use-log-analytics.md create mode 100644 docs/required_roles_scopes_resources.md rename docs/{TRANSPARENCY_FAQ.md => transparency_faq.md} (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a08de2..cc9b574 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,28 +47,28 @@ All notable changes to this project will be documented in this file. - OneLake indexing pipeline connecting Fabric lakehouses to AI Search - Comprehensive post-provision automation (22 hooks for Fabric/Purview/Search setup) - New documentation: `deploy_app_from_foundry.md` for publishing apps from Microsoft Foundry -- New documentation: `TRANSPARENCY_FAQ.md` for responsible AI transparency +- New documentation: `transparency_faq.md` for responsible AI transparency - New documentation: `NewUserGuide.md` for first-time users - Header icons matching GSA standard format - Fabric private networking documentation ### Changed - README.md restructured to match Microsoft GSA (Global Solution Accelerator) format -- DeploymentGuide.md consolidated with all deployment options in one place +- deploymentguide.md consolidated with all deployment options in one place - Updated Azure Fabric CLI commands (`az fabric capacity` replaces deprecated `az powerbi embedded-capacity`) - Post-provision scripts now validate Fabric capacity state before execution - Navigation links use pipe separators matching other GSA repos ### Removed - `github_actions_steps.md` (stub placeholder) -- `github_code_spaces_steps.md` (consolidated into DeploymentGuide.md) -- `local_environment_steps.md` (consolidated into DeploymentGuide.md) -- `Dev_ContainerSteps.md` (consolidated into DeploymentGuide.md) +- `github_code_spaces_steps.md` (consolidated into deploymentguide.md) +- `local_environment_steps.md` (consolidated into deploymentguide.md) +- `Dev_ContainerSteps.md` (consolidated into deploymentguide.md) - `transfer_project_connections.md` (feature deprecated) - `sample_app_setup.md` (replaced with `deploy_app_from_foundry.md`) - `Verify_Services_On_Network.md` (referenced non-existent script) -- `add_additional_services.md` (outdated, redundant with PARAMETER_GUIDE.md) -- `modify_deployed_models.md` (outdated, redundant with PARAMETER_GUIDE.md) +- `add_additional_services.md` (outdated, redundant with parameter_guide.md) +- `modify_deployed_models.md` (outdated, redundant with parameter_guide.md) ## [1.2] - 2025-05-13 ### Added diff --git a/README.md b/README.md index 8948405..0247025 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ For the first attempt, the lowest-risk path is to keep Fabric and Purview disabl |------|------------------| | Fastest realistic validation | Local `azd up` workflow | | Clean environment with fewer local setup issues | GitHub Codespaces | -| Deep customization before deploy | Read [docs/PARAMETER_GUIDE.md](./docs/PARAMETER_GUIDE.md) first | +| Deep customization before deploy | Read [docs/parameter_guide.md](./docs/parameter_guide.md) first | | Lowest-risk first run | Disable Fabric and Purview, then re-enable later | ### How to Install or Deploy @@ -80,7 +80,7 @@ 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). -[**📘 Click here to launch the Deployment Guide**](./docs/DeploymentGuide.md) +[**📘 Click here to launch the Deployment Guide**](./docs/deploymentguide.md)
@@ -270,20 +270,20 @@ Supporting documentation | Document | Description | |----------|-------------| -| [Deployment Guide](./docs/DeploymentGuide.md) | Complete deployment instructions | +| [Deployment Guide](./docs/deploymentguide.md) | Complete deployment instructions | | [Post Deployment Steps](./docs/post_deployment_steps.md) | Verify your deployment | | [PostgreSQL Mirroring](./docs/postgresql_mirroring.md) | Automate or troubleshoot the Fabric connection and PostgreSQL mirror flow | -| [Parameter Guide](./docs/PARAMETER_GUIDE.md) | Configure deployment parameters | +| [Parameter Guide](./docs/parameter_guide.md) | Configure deployment parameters | | [Quota Check Guide](./docs/quota_check.md) | Check Azure OpenAI quota availability | ### Customization & Operations | Document | Description | |----------|-------------| -| [Required Roles & Scopes](./docs/Required_roles_scopes_resources.md) | IAM requirements for deployment | -| [Parameter Guide](./docs/PARAMETER_GUIDE.md) | All deployment parameters, toggles & model configs | +| [Required Roles & Scopes](./docs/required_roles_scopes_resources.md) | IAM requirements for deployment | +| [Parameter Guide](./docs/parameter_guide.md) | All deployment parameters, toggles & model configs | | [Deploy App from Foundry](./docs/deploy_app_from_foundry.md) | Publish playground to App Service | -| [Accessing Private Resources](./docs/ACCESSING_PRIVATE_RESOURCES.md) | Connect via Jump VM | +| [Accessing Private Resources](./docs/accessing_private_resources.md) | Connect via Jump VM | ### Security Guidelines @@ -314,7 +314,7 @@ Have questions, found a bug, or want to request a feature? [Submit a new issue]( ## Responsible AI Transparency FAQ -Please refer to [Transparency FAQ](./docs/TRANSPARENCY_FAQ.md) for responsible AI transparency details of this solution accelerator. +Please refer to [Transparency FAQ](./docs/transparency_faq.md) for responsible AI transparency details of this solution accelerator.
diff --git a/docs/Required_roles_scopes_resources.md b/docs/Required_roles_scopes_resources.md deleted file mode 100644 index 873896c..0000000 --- a/docs/Required_roles_scopes_resources.md +++ /dev/null @@ -1,26 +0,0 @@ -# Required Roles and Scopes for Microsoft Foundry isolated network template deployment -To deploy this code, assign roles with minimal privileges to create and manage necessary Azure resources. Ensure roles are assigned at the appropriate subscription or resource group levels. - -## Role Assignments: -- Contributor role should be assigned at the subscription level to ensure the user has permission to create resource groups and manage resources. -- User Access Administrator Role: Since this deployment sets various role scopes, the User Access Administrator is required. - -A user with the Contributor role at the subscription level should have sufficient permissions to deploy the provided Bicep template. The Contributor role allows the user to create and manage all types of Azure resources, including resource groups, virtual networks, storage accounts, virtual machines, and other resources defined in the Bicep template. - -# Pre-deployment steps: - -Be sure these resource providers are registered in your Azure subscription. To register a resource provider in Azure, you need to have the Contributor role at the subscription level or higher. If you only have contributor permissions at the resource or resource group scope, you may need to reach out to the service owner to perform this task at the subscription level. - -| **Resource Type** | **Azure Resource Provider** | **Type** | **Description** | -|-------------------|----------------------------|----------|-----------------| -| Application Insights | Microsoft.Insights | /components | An Azure Application Insights instance associated with the Microsoft Foundry account | -|Azure Log Analytics|Microsoft.OperationalInsights|/workspaces|An Azure Log Analytics workspace used to collect diagnostics| -|Azure Key Vault|Microsoft.KeyVault|/vaults|An Azure Key Vault instance associated with the Microsoft Foundry account| -|Azure Storage Account|Microsoft.Storage|/storageAccounts|An Azure Storage instance associated with the Microsoft Foundry account| -|Azure Container Registry|Microsoft.ContainerRegistry|/registries|An Azure Container Registry instance associated with the Microsoft Foundry account| -|Azure AI Services|Microsoft.CognitiveServices|/accounts|An Azure AI Services as the model-as-a-service endpoint provider including GPT-4o and ADA Text Embeddings model deployments| -|Azure Virtual Network|Microsoft.Network|/virtualNetworks|A bring-your-own (BYO) virtual network hosting a virtual machine to connect to Microsoft Foundry which will be behind a private endpoint when in network isolation mode. | -|Bastion Host|Microsoft.Network||A Bastion Host defined in the BYO virtual network that provides RDP connectivity to the jumpbox virtual machine| -|Azure NAT Gateway|Microsoft.Network|/natGateways|An Azure NAT Gateway that provides outbound connectivity to the jumpbox virtual machine| -|Azure Private Endpoints|Microsoft.Network|/privateEndpoints|Azure Private Endpoints defined in the BYO virtual network for Azure Container Registry, Azure Key Vault, Azure Storage Account, and Microsoft Foundry account/project| -|Azure Private DNS Zones|Microsoft.Network|/privateDnsZones|Azure Private DNS Zones are used for the DNS resolution of the Azure Private Endpoints| diff --git a/docs/automation-outputs-mapping.md b/docs/automation-outputs-mapping.md index e81607d..bf5763f 100644 --- a/docs/automation-outputs-mapping.md +++ b/docs/automation-outputs-mapping.md @@ -1,147 +1,111 @@ # Automation Scripts - Azure Outputs Mapping -This document describes how Azure deployment outputs are mapped to automation script parameters. +This document describes how deployment outputs and azd environment values are used by the postprovision automation scripts. ## Overview -The postprovision automation scripts consume deployment outputs via the `AZURE_OUTPUTS_JSON` environment variable, which is automatically populated by `azd` after infrastructure provisioning. This ensures scripts operate on the actual deployed resources rather than requiring manual configuration. - -## Output Mapping - -### Core Infrastructure - -| Bicep Output | Script Variable | Used By | Purpose | -|-------------|-----------------|---------|---------| -| `resourceGroupName` | `resourceGroup` | Multiple | Resource group for all operations | -| `subscriptionId` | `subscriptionId` | Multiple | Azure subscription ID | -| `location` | `location` | Multiple | Azure region | - -### Microsoft Fabric - -| Bicep Output | Script Variable | Used By | Purpose | -|-------------|-----------------|---------|---------| -| `fabricCapacityModeOut` | `fabricCapacityMode` | Multiple Fabric scripts | Resolved mode from `fabricCapacityPreset` (`create`, `byo`, `none`) | -| `fabricWorkspaceModeOut` | `fabricWorkspaceMode` | Multiple Fabric scripts | Resolved mode from `fabricWorkspacePreset` (`create`, `byo`, `none`) | -| `fabricCapacityId` | `FABRIC_CAPACITY_ID` | `ensure_active_capacity.ps1` | ARM resource ID of Fabric capacity | -| `fabricCapacityResourceIdOut` | `fabricCapacityId` | `create_fabric_workspace.ps1` | Resource ID for capacity assignment | -| `fabricWorkspaceIdOut` | `FABRIC_WORKSPACE_ID` | Multiple Fabric scripts | Existing or created Fabric workspace ID | -| `fabricWorkspaceNameOut` | `FABRIC_WORKSPACE_NAME` | Multiple Fabric scripts | Target workspace name | -| `desiredFabricWorkspaceName` | `FABRIC_WORKSPACE_NAME` | Multiple Fabric scripts | Back-compat alias for `fabricWorkspaceName` | -| `desiredFabricDomainName` | `domainName` | `create_fabric_domain.ps1` | Target domain name | -| `fabricCapacityName` | - | - | Display name (optional) | - -### AI Search (for OneLake Indexing) - -| Bicep Output | Script Variable | Used By | Purpose | -|-------------|-----------------|---------|---------| -| `aiSearchName` | `aiSearchName` | OneLake indexing scripts | AI Search service name | -| `aiSearchResourceGroup` | `aiSearchResourceGroup` | OneLake indexing scripts | Resource group containing AI Search | -| `aiSearchSubscriptionId` | `aiSearchSubscriptionId` | OneLake indexing scripts | Subscription for AI Search | -| `aiSearchAdditionalAccessObjectIds` | `aiSearchAdditionalAccessObjectIds` | RBAC scripts | Optional Entra principals granted Search roles | - -### Microsoft Foundry - -| Bicep Output | Script Variable | Used By | Purpose | -|-------------|-----------------|---------|---------| -| `aiFoundryProjectName` | `aiFoundryName` | `06_setup_ai_foundry_search_rbac.ps1` | Microsoft Foundry project name | -| `aiFoundryServicesName` | `aiServicesName` | RBAC scripts | Cognitive Services account name | - -### Purview Integration - -| Bicep Output | Script Variable | Used By | Purpose | -|-------------|-----------------|---------|---------| -| `purviewAccountName` | `purviewAccountName` | Purview automation scripts | **User-provided** Purview account (auto-derived from `purviewAccountResourceId` if not set) | -| `purviewResourceGroup` | `purviewResourceGroup` | Purview automation scripts | Resource group containing the Purview account | -| `purviewSubscriptionId` | `purviewSubscriptionId` | Purview automation scripts | Subscription containing the Purview account | - -> **Note**: Purview is NOT provisioned by this template. Supply the existing account details via parameters; if only `purviewAccountResourceId` is provided, the deployment now derives the name, resource group, and subscription automatically for the scripts. - -### Lakehouse Configuration - -| Bicep Output | Script Variable | Used By | Purpose | -|-------------|-----------------|---------|---------| -| `lakehouseNames` | `LAKEHOUSE_NAMES` | `create_lakehouses.ps1` | Comma-separated lakehouse names (default: bronze,silver,gold) | -| `documentLakehouseName` | `documentLakehouse` | `materialize_document_folders.ps1` | Target lakehouse for documents (default: bronze) | - -## Script Resolution Logic - -Scripts follow this resolution order for configuration: - -1. **AZURE_OUTPUTS_JSON** - Primary source (populated by `azd` after deployment) -2. **Environment variables** - Explicit overrides (e.g., `FABRIC_WORKSPACE_NAME`) -3. **azd env get-value** - Individual value queries -4. **`.azure//.env`** - Local environment file -5. **`infra/*.bicepparam`** - Parameter file defaults -6. **Script defaults** - Hardcoded fallbacks - -This ensures maximum flexibility while prioritizing deployed resource information. +The postprovision scripts resolve values in this order: + +1. **AZURE_OUTPUTS_JSON** - Outputs from [infra/main.bicep](../infra/main.bicep), populated by azd after provisioning. +2. **azd env values** - Values from `azd env get-values` (for example `AZURE_RESOURCE_GROUP`, `AZURE_SUBSCRIPTION_ID`, `AZURE_LOCATION`, plus any explicit overrides). +3. **Environment variables** - Explicit overrides (for example `FABRIC_WORKSPACE_NAME`). +4. **.azure//.env** - Local env file populated by azd. +5. **infra/main.bicepparam** - Parameter defaults. +6. **Script defaults** - Hardcoded fallbacks. + +This keeps automation aligned with the deployed resources while still allowing overrides. + +## Outputs emitted by infra/main.bicep + +These outputs are the only values guaranteed to appear in `AZURE_OUTPUTS_JSON` for this repo. Scripts read them directly or fall back to azd env values when needed. + +| Bicep Output | Typical Usage | Notes | +|---|---|---| +| `aiSearchName` | OneLake indexing scripts | AI Search service name | +| `aiSearchResourceId` | OneLake indexing scripts | AI Search ARM resource ID | +| `aiSearchAdditionalAccessObjectIds` | RBAC scripts | Optional Entra object IDs to grant Search roles | +| `aiFoundryProjectName` | AI Foundry RBAC scripts | Project name hint for discovery | +| `fabricCapacityModeOut` | Fabric scripts | Resolved mode (`create`, `byo`, `none`) | +| `fabricWorkspaceModeOut` | Fabric scripts | Resolved mode (`create`, `byo`, `none`) | +| `fabricCapacityResourceIdOut` | Fabric scripts | Capacity ARM resource ID (create/byo) | +| `fabricCapacityId` | Fabric scripts | Alias of capacity ARM resource ID | +| `fabricCapacityName` | Fabric scripts | Capacity name (derived or provided) | +| `fabricWorkspaceNameOut` | Fabric scripts | Workspace name (create/byo) | +| `fabricWorkspaceIdOut` | Fabric scripts | Workspace ID when BYO | +| `desiredFabricWorkspaceName` | Fabric scripts | Desired workspace name (used as fallback) | +| `desiredFabricDomainName` | Fabric scripts | Desired domain name (used as fallback) | +| `purviewAccountResourceId` | Purview scripts | Existing Purview account resource ID | +| `purviewCollectionName` | Purview scripts | Collection name override | +| `postgreSqlServerNameOut` | Mirroring scripts | PostgreSQL server name | +| `postgreSqlServerResourceId` | Mirroring scripts | PostgreSQL server resource ID | +| `postgreSqlServerFqdn` | Mirroring scripts | PostgreSQL server FQDN | +| `postgreSqlSystemAssignedPrincipalId` | Mirroring scripts | Server managed identity principal ID | +| `postgreSqlAdminSecretName` | Mirroring scripts | Key Vault secret name for admin password | +| `postgreSqlAdminLoginOut` | Mirroring scripts | Admin username | +| `postgreSqlFabricUserNameOut` | Mirroring scripts | Fabric mirroring username | +| `postgreSqlFabricUserSecretNameOut` | Mirroring scripts | Fabric user password secret name | +| `postgreSqlMirrorConnectionModeOut` | Mirroring scripts | `fabricUser` or `admin` | +| `postgreSqlMirrorConnectionUserNameOut` | Mirroring scripts | Effective mirroring username | +| `postgreSqlMirrorConnectionSecretNameOut` | Mirroring scripts | Effective mirroring secret name | +| `virtualNetworkResourceId` | Networking scripts | VNet ARM resource ID | +| `peSubnetResourceId` | Networking scripts | Private endpoint subnet ID | +| `jumpboxSubnetResourceId` | Networking scripts | Jumpbox subnet ID | +| `agentSubnetResourceId` | Networking scripts | Agent subnet ID | +| `keyVaultResourceId` | Mirroring scripts | Key Vault resource ID | +| `storageAccountResourceId` | OneLake indexing scripts | Storage account resource ID | + +## Values resolved from azd env (not outputs) + +These values are not emitted by `infra/main.bicep`, but scripts resolve them from `azd env get-values` or environment variables: + +- `AZURE_RESOURCE_GROUP`, `AZURE_SUBSCRIPTION_ID`, `AZURE_LOCATION` +- `aiSearchResourceGroup`, `aiSearchSubscriptionId` +- `aiFoundryName`, `aiFoundryResourceGroup`, `aiFoundrySubscriptionId` +- `purviewAccountName`, `purviewResourceGroup`, `purviewSubscriptionId` + +If `purviewAccountResourceId` is available, Purview scripts derive the name, resource group, and subscription from that resource ID automatically. ## Example: Script Consumption -When `azd up` completes, it sets: +When `azd up` completes, it sets `AZURE_OUTPUTS_JSON` with the outputs above. For example: ```bash export AZURE_OUTPUTS_JSON='{ - "fabricCapacityId": {"type":"String","value":"/subscriptions/.../fabricCapacities/fabric-xyz"}, + "fabricCapacityId": {"type":"String","value":"/subscriptions/.../providers/Microsoft.Fabric/capacities/fabric-xyz"}, "fabricCapacityModeOut": {"type":"String","value":"create"}, "fabricWorkspaceModeOut": {"type":"String","value":"create"}, - "fabricWorkspaceNameOut": {"type":"String","value":"workspace-myenv"}, - "fabricWorkspaceIdOut": {"type":"String","value":""}, "desiredFabricWorkspaceName": {"type":"String","value":"workspace-myenv"}, "aiSearchName": {"type":"String","value":"search-xyz"}, - "aiSearchResourceGroup": {"type":"String","value":"rg-ai-landing-zone"}, - ... + "aiSearchResourceId": {"type":"String","value":"/subscriptions/.../providers/Microsoft.Search/searchServices/search-xyz"}, + "purviewAccountResourceId": {"type":"String","value":"/subscriptions/.../providers/Microsoft.Purview/accounts/purview-xyz"} }' ``` -Scripts parse this JSON: +Scripts parse this JSON as needed, for example: ```powershell -# From create_fabric_workspace.ps1 if (-not $WorkspaceName -and $env:AZURE_OUTPUTS_JSON) { - try { + try { $out = $env:AZURE_OUTPUTS_JSON | ConvertFrom-Json - $WorkspaceName = $out.fabricWorkspaceNameOut.value - if (-not $WorkspaceName) { - $WorkspaceName = $out.desiredFabricWorkspaceName.value - } + $WorkspaceName = $out.desiredFabricWorkspaceName.value } catch {} } ``` -## Benefits - - **No manual configuration** - Scripts automatically use deployed resources - **Type safety** - Bicep outputs are strongly typed - **Traceability** - Clear mapping from infrastructure to automation - **Flexibility** - Can still override via environment variables - **Error prevention** - Reduces risk of mismatched resource names - ## Verification -After deployment, verify outputs: +After deployment, verify outputs and azd values: ```bash -# View all outputs azd env get-values - -# View specific output azd env get-value fabricCapacityId -azd env get-value fabricCapacityModeOut azd env get-value fabricWorkspaceModeOut azd env get-value aiSearchName ``` ## Related Files -- **Infrastructure**: `/infra/main.bicep` -- **Parameters**: `/infra/main.bicepparam` -- **Automation Workflow**: `/azure.yaml` (postprovision hooks) -- **Scripts**: `/scripts/automationScripts/` - -## Next Steps - -1. Deploy infrastructure: `azd up` -2. Verify outputs: `azd env get-values` -3. Postprovision scripts run automatically using these outputs -4. For Purview features, manually set: `azd env set purviewAccountName ` +- **Infrastructure**: /infra/main.bicep +- **Parameters**: /infra/main.bicepparam +- **Automation Workflow**: /azure.yaml (postprovision hooks) +- **Scripts**: /scripts/automationScripts/ diff --git a/docs/DeploymentGuide.md b/docs/deploymentguide.md similarity index 98% rename from docs/DeploymentGuide.md rename to docs/deploymentguide.md index 5304018..37a9402 100644 --- a/docs/DeploymentGuide.md +++ b/docs/deploymentguide.md @@ -394,6 +394,6 @@ After deployment: ## Additional Resources -- [Required Roles & Scopes](./Required_roles_scopes_resources.md) -- [Parameter Guide](./PARAMETER_GUIDE.md) - includes model deployment configuration -- [Accessing Private Resources](./ACCESSING_PRIVATE_RESOURCES.md) +- [Required Roles & Scopes](./required_roles_scopes_resources.md) +- [Parameter Guide](./parameter_guide.md) - includes model deployment configuration +- [Accessing Private Resources](./accessing_private_resources.md) diff --git a/docs/faq.md b/docs/faq.md index 365c3a9..24f29c6 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -4,10 +4,10 @@ Microsoft Foundry creates **separate managed identities** for the Foundry account and for each project. Azure RBAC permissions do **not** cascade from the account to its projects, so a role assignment that targets the account identity does not automatically grant the same access to the project identity. -The post-provision script `scripts/automationScripts/OneLakeIndex/06_setup_ai_foundry_search_rbac.ps1` therefore resolves **both** identities: +The post-provision script `scripts/automationScripts/OneLakeIndex/06_setup_ai_foundry_search_rbac.ps1` resolves **both** identities: -- `aiFoundryIdentity` → the Microsoft Foundry **account** managed identity -- `projectPrincipalId` → the Microsoft Foundry **project** managed identity +- `aiFoundryIdentity` -> the Microsoft Foundry **account** managed identity +- `projectPrincipalId` -> the Microsoft Foundry **project** managed identity It then assigns the required Azure AI Search roles to every principal it finds. If the script cannot resolve the project identity, it logs a warning and only the account identity receives the roles. In that case, re-run the script once the project identity exists or assign the roles manually. @@ -46,39 +46,12 @@ az role assignment create \ Because the knowledge source uses the **project** identity when it ingests data, those roles must be granted to the project principal even if the account identity already has them. -## How do I integrate an existing Microsoft Foundry project into the AI Landing Zone? - -Integrating the Microsoft Foundry project model (Cognitive Services account plus project) into an AI Landing Zone is a matter of extending the landing zone controls so the project runs entirely inside the isolated estate. Work through these considerations: - -1. **Locate the project**: Record the account and project resource IDs, region, and tenant. Confirm the region aligns with the landing zone virtual network and private DNS footprint so private endpoints can be created without cross-region limitations. -2. **Carve out network space**: Add a dedicated subnet (or set of subnets) in the landing zone virtual network for the Foundry managed network. Apply the landing zone NSG, UDR, and firewall baselines. If the project already uses managed network isolation, update it to target the new subnet; otherwise plan for a fresh isolated project and migrate assets with export/import tooling. -3. **Bring dependencies private**: For every service the project consumes (Azure AI Search, Storage, Key Vault, App Configuration, Cosmos DB, etc.), provision or reuse private endpoints in the landing zone subnet and link the associated private DNS zones to both the landing zone VNet and the Foundry managed subnet. Validate DNS resolution from that subnet before switching project connections to private FQDNs. -4. **Assign least privilege**: The updated architecture surfaces separate managed identities for the account and each project. Grant only the required RBAC roles (for example, `Search Service Contributor` plus `Search Index Data Reader` on search and `Storage Blob Data Reader` on storage) to both identities as needed, and double-check that Defender or conditional access policies in the landing zone allow them to authenticate. -5. **Control outbound access**: Align the project managed outbound configuration with the landing zone egress model by allowing only the Microsoft service tags and explicit endpoints the project requires, forcing all other traffic through the landing zone firewall or NVA. -6. **Validate end to end**: Use Azure AI Studio diagnostics to confirm private endpoint reachability, DNS resolution, role assignments, and content ingestion. Re-run prompt flows, indexing pipelines, and other workloads to ensure they operate entirely within the landing zone boundaries. - -## What is the recommended migration approach when moving to the landing-zone project? - -Follow these steps to migrate assets from an existing project into the landing-zone instance: - -- **Export configuration**: Capture project metadata, workspace settings, prompt catalogs, content filters, evaluation templates, and managed endpoints with `az cognitiveservices account project export` (preview) or an ARM template export. Back up deployment policies and rate-limit settings. -- **Move custom models**: Download fine-tuned model artifacts from Azure AI Studio > Models or via the Foundry REST APIs, including versions, tokenizer configs, and training logs. Re-run the training jobs in the landing-zone project so lineage and monitoring start fresh. -- **Rebuild data connections**: Enumerate Cognitive Services connections, Key Vault references, search indexes, and storage links. Re-create matching private endpoints, DNS links, and connection objects in the landing-zone project, preferring managed identities over secrets. -- **Reapply automation**: Update Git-connected prompt flows and CI/CD pipelines (Azure DevOps or GitHub Actions) with the new project resource IDs, environment variables, and service connections, then replay the promotion pipelines to redeploy assets. -- **Verify and cut over**: Execute validation notebooks or automated smoke tests, confirm service quotas and feature flags match expectations, disable traffic on the old project, and repoint DNS or clients to the new endpoints. - -**Next steps** - -1. Inventory current project assets (models, flows, evaluations, connections) so nothing is missed. -2. Provision the isolated landing-zone project with required private endpoints and RBAC. -3. Run export/import scripts, validate workloads, and plan the production cutover once tests succeed. - ## How do I initialize or refresh the AI Landing Zone submodules? Run the repo-provided Git submodules command from the repository root: ```bash -cd /workspaces/Deploy-Your-AI-Application-In-Production && git submodule update --init --recursive +git submodule update --init --recursive ``` This syncs every nested submodule to the commit pinned by the main repository, ensuring the infrastructure and automation modules stay aligned. For more background, see Microsoft Learn resources on the [AI landing zone architecture](https://learn.microsoft.com/azure/cloud-adoption-framework/scenarios/ai/ai-landing-zone) and [working with Git submodules](https://learn.microsoft.com/azure/devops/repos/git/git-submodules). diff --git a/docs/images/re_use_log/logAnalytics.png b/docs/images/re_use_log/logAnalytics.png deleted file mode 100644 index 95402f8d133a44749ea4d98659bfb45f708d59ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145324 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�zi|Nx9mKNGlGsCs(S!fXpzLs6WS?os$#ZH6u*~=o<(FoffaP3@@U@F zdV^G1wgB5}Iix53LdIH|wni2fj}{iC znZD4$CPCo=K(D46&ntfNS=jaw5lbG2rmO$$CGRsdD2Tw0WgVOOpJ}!ek=F_aX_RS%vL#O@i~6 z&IdUc+r^X2e|NEVTm~dCh#S)G;u}?NVJ4@EB!nT{{=?K>)b%xmzLU&g#$;?n;x&5aW2531a^57f4z0ujxF{w<0v1k9B z`e3-R@&R#oUir8t<-<)dxUy0l%D_nQo-6M$eu-IAJ5fo#NU2J_jGUVzmM`m0*i9wn z`Uw$^qNilnxv-Ebr_otyfm@6?Pom2x%dlH(X!Fu=GHz_QHRbnB>PQ~Anex@8mrhQ# ztw(fSlWrC(9GEhq#t4>xJLsPz!fHWHLa>c)Sr_~$_PLPU-t7B!V>@PfX_v*U>x2Yh zOQ=8iri(8rSg`xhXuCtr7wa}>pIAjA9($-jZb179cYO~ z$Upw=8mZl4K(4(wAJ&2Pzp1wSOs$)R$)AHT(*`4$JQzarq4 zc;io{R6cbA-Zd~5PKx8f&0Aq8Y1mw=N3ZN+i0)R+r@6Heex_kMq(}A2`Ib^c+s;v6 zTrhbdU%Nv)!&;<#_!pBu7$??;Pi$vOFKETvqbemis>futpYL)Mn|sfe98neo zfOBuxnP(y8Qy+9bk3}eYVE>FCz1h}${h)SPfI9<1CJx0ao&`M?68d2YTqG! zUIGxq0>A+!BV)yO4SaSQz2QNsY02?c7ktRPhKB*5#oGJU8g?D}2ghV7WdA9|S3zc{ zVM7p9)m^m^*%(Q^E109-X|g7uPo@siFi>;#&7ZG!93XF?P>Cm<%tLO+FXXV}YzUbl z#QFX-8$}WOen_|ruV{-EiqeFUJ*X3yJaayz93J934${PNlePTj_?@@gesd%4+5p!d z1mDq9zI<1%<@~EKB-<*v9Y&HeTY@5Qf>J~ml+Ae7i&ldb+2HzZy9!et4$)H`)GduQ zhX-D?02WyH)6Ysm9Z2#;@U!VJ0&rj2{!5$3HPDftI@!_M!s;5QL(&OxS$Umn?kCe@ z_1aVq&qSrOCh83EL!uonbS-iFkEz<<^=3QYp-=6=dJaJK2F90@Y`rkH9mtAtU8y|ah{Z= zeexC=;7su_kvvS0>5iM!j^`egxG(7TMfmg$5L!Ni1QV3wHP(rOCv19T*5Cv>~b zMtOEUi>yokiadY8O){+9YTO8baJ1YkKP_Yh?w7YZGU0917>Ijw%@U)NB{C>EM-I?H z1s@1Zsh753$KzC32&@A=uB4rJb({+^HkOk_>z#_UF!D9ZzEWB!X3lx}HAhi6@107Y zF>vO>;jypXTU)>eczUn7xbttm^OK%34K0UsMdU1pHn{K)ADpNZFy-FGz)|t0)nXkz z;>K&3%Enr9#aIt*RUNIvU%{oIa9dB7_ndl1aqDV%y&+mw3Y4?dqHAeV+P2=0=hP)@snkPZeLS%kbjP6`Fqhbp+YX3{YT^_4ttOn$4^YsoErWXJ~{&L>u z1pu&rKLN#4)6bIS4!Tv~T$Vx8Sa?72c%G(P@*8jw95+z9!1FGupVxzU@;5K10iTft zGGPALDgU4VXPWM3`?nXeyTMmaMQpECug67fQH1SE6y7`pxum^`9^b{W2NN@&&6Tg` zy(}1dqRe(+%&i1CT1bE-B{=)NYSvaVUBcz~fFP zo}hYB8}8;q!@v|hwxbT2pkb4`>eZchV+ItSr=#!uouCi-vvt^L^?bv<6Wza*-cdcO z8W0bBL!rhMErSxJ@`vJ$eD&#Qwe%G7H9%) z8b?3*tpjXW^;y3X^w7o5pK7wNiuHs3zbWgS_bECXeyeB$iag;^&TE&Au^Ij4#G#%i z)5R0=r`0bNd_>(6El{|kY@2MD*4M-&y|?2Dkt7 z!yWYPMuGMf-HVt0`=_tE{yiV%fk>stkEIWYTfgUnWM>g#moKxP4s+}3?MGi4FVAJt z^}G1^qkr#iME74b7cT3fK#q9sC94>#Tz4WD@-}zh9;26_Z?lo3;J@D+WkI=vOFnNH zeoRTG>$lKlu`JRV@3JYzb-2@OiX3He!8P=;rhFNpQ){w%z3){9RXdQA|7+ zAfGxaGQProa3c{Ls+els(2k4B3#SaSw<>W;d~lvrDw5yWM>E&LpVa zkoxoouGZAN-b7-(x9Ch{SQ3}SuCdjXGiUj-f%Wo(3eZkeIWj7=v^cmRIgp-E@U;*G zIi}ag0!df2&wZO7@srpf+)BcZ-_Z8?BX;rpYMuk%p5y29&p9%b3vpTb6|diPsT<$T zP;Lg-_;!kE$}{D4WDHaGU%sq#Qp%dFTjv>c>)fFv%zeuKdJbPJxOe;Z!TaYz4;@yQ z%!04W(M0JCRk&gi6-uAq7({7j+H}rL6|5TN*D=-yj_s7Zg&yCMconL~O z9RF>7>ichgM}7UeaLS1^efm%_O<&3EIU@iW@IvkXPw?2Kv`(k99)LSjs`9D}0lQgYw{ zL^c`6#luRIfrmk)qjjHZZ6n?0gw}6vvp-uh7CD+lK`-IkxEM0^3Cr;9-N%k)P(1;0 z)y+-$<{jP|=rc+HyR^Se5u9a?hkxW*<(vveT)!== zF$e|ZA)M9G#wN3}m?~IaMPR!fQ!UdAXVqCRWJ&UFkw$um$eIXU3(7n@VO&#CzyZ;?G?xR+2-R$q?$6l{get2$gp&aU~IG|^E@C({M8KZw`d9NUF ztqkjm6AF&1h;EeltS{dLhskU$*BV5JwSsdbX-_{=80t6!d)JyjLf1M&x5+7a>6Gu7 z{%m3D^d7TM;pyo0&vXZnLe&}U);Qh&y%`0_Con;~mTe$Hp3yrr3@ zo$1@H0=3c2c+V~Kf_8$ANOaC@ah|xCw$Fhi-bB0dvcCA}Z0@oD?PPy&G)KG-&H%&u z{YuT?75r6HkGJK!kJIAK$^H%ayuI~G1J!AhN7r)CcJdY3ZNgY`_6*=lguC{4h!41L zsLdLs7&gWg2n#-?<1hdg`r^mYhi|5@c1ATWPC3tFIaTB$asN8%h_C-T>W{#@2Hs0vE+PMqby-%fi_mt1#Em;Cr=MT&36ya6v(`vT1=eiAvjoNh=c{hzhp!dTBTra|vca0v^XZ1JSkn{Z%ll6R?WkXKB zvH^sJg~h(BD7j?;@zy++;j1)*8Fx8Mo2F$gr~0mdYdWkIvGeBWzr`d#wL)JGp$8h< z<-|+}LdgY4bAJ^nBA9Ud^`V{JbBBvyc0Cxrk>NC@sA}9WDdqd@WGYp`QO{H)>B#rU zL_RTQwkBTKhK35b&a-z+vA3=AwRgxr2BHg0$6IbYAAy76f61RT9l-9Vo+L-*EwrOt zUs#JsW=&k0-TurH^T0}yS)B5-AVUt z-vZXW(TKb9*VuzAl*e`xQe&#;pJty-W;)fdv7F@G?t0eEp; zZq$Q0`O~7!XTPbI{E;9>BQHna{ipMVYKzMkcC&ecR!B+{bce7(CV3X~+a`E=ORWL@ z>r^2xpDWC*?YEnKGxn-?-fcB^-a&| znbyA0jO8V5Z&fgo&2#gExO^u2vNB7-$$_BQPb5pFek7>7`ZW9L8qEjY$h&y)>SyH- z4Orb^ZgLGOCjEWW@z`$`;XHmj%iwq>DRBn zt~8+;t8({Pt$e5%9sxS7;tNZQTpjjPRiS6=cLg2$#oD(-?N|HqETAAxWsIa~y)`%2 z?j;o;0pksu-rHTnoKfbn85tY8SfZHPR}RhyQ$kpprs3fRR)AYET{T9S&2D1s6t$1& zo8AY~f7KeV99G^Nt@dI8SG{=JxT_N}4R1E$R+JjfT}_$T%>g-)wgZN8je;LGKT3wb zx%3fy^?$8+H49#2X672_IzWJ<^^uS)zQ_{Us-Hwoe^p%OKP_46XcI;gkx7T#$4rh- zPtOI*lFx9Bha6ior`(Pa&>31j5qSEEqVFwE&j@mKdM-IO=|^bH0_Qy4bxmBr0 zy~u*^(>Et}>OzvQdU5$TS9yBI><(OhJ+8%6Vuu|va^Pb~t{WMN(e*Xzz!Hq~Z9)%z z$*b@LuZ=y{6Lq&_N_i!z;bX{`n>47lF;Bv;8on_}4~W5YwLheCC@a>I7&Z+-L>9U& z;oBu%f)N8WV9S*wi_ou)ndrW_Ya>a|!gQ$KM|Hck`{>18GpSG%s;>!kZ@BHa<9qtT zk@$;?U*oo8S7f{|eE|kge^f2(s!>@xlHN0M+6Ne=NR_Dj z<39th<)s3je$2pna+O1^Ounj2ontti!%zKX#>)G$D(}H#2ezot)z4={@(MsgS+ruu z%{r(FX=Jd9+DekI-+}!IS2w8|5>fQ&E3J&U`5c{*eFaCq%xwuzS*qo>JC17peF6E= z^m4MYr@a2yeq$9aF|Pt{qzi)>J3dkqq}4=oYJYaI67?PN{`IGnlMdVpI5N~`8T-{+ z+~1b<_M9^pjzKoY?f=1y+{v=MY%(BrDso(7r8r%dM|H+4&B0g{VHF|=-{yHZCILQk z&zLB$_IGJRVNd3Jr{|POqZF5TW!S&aQp>%5AGo8?E;#hBlgsm?YX35Ontx^KRL?e4 zeq7K?RVt2puJl6VjB|6;Q4y73pL#ph;PQiqqF?kFo#kVDMl~&$F{yw4>R=|8UcAEg zc{|ue9_HB8nt$z?G%#NI_pjxpX-gky-_ZyGGl2Sa9QkQB9Y1dAUJ7&I&#ma|7Fn^O z@fA=7Mbd>EBWW)Ka(n*|b8i_|Roit9gQSEYEh(UM3lfrofKo~$oq~Y0NQcrb($dn6 zG}0i_At2Hr(jXlY-`rmJecjjnyzg;*&!6u&zV$=48}{C7uXUdDoO6tE&e53pEb3+| zym4vab2JYT57Qf1{XJjCHBa2^=wpaC>8Kmdlq-dQ=W(fdm}+D)D8GFhiBF@kaWMAn zYk;X5t1uQGaiRA3x(=RQQ$yE<+^*1h-uSZbb`$&Vu!LQ^geO`@l`8Z5u~(zPSy`u_ zV!th)s~PEiSa|x&*!|?8r9|ZLV;PFOYM~*hjYO;BVQziO*@@>zdB2v3Whk&)wy_qF zLK=%n3j0`Sd1y~!^pE*-EwXU#BYS7%U6&8t51x+m74TMz&mXd2kajKQ^*A9wM}o3K z>f2An2fi%fcS#V-!bmThL~Xp$j}LTVE5#0;5BjyH_?aBTgD*sWP~?^;!}~*g#k?|V z6H=C5Y`@Qdr7M}l@|kPoNphk(afe6F+UaN+vun}gRu8qr3-%DbqFwz#WqkdBfG0bc zm?6q8?>nfBEk3Otu)k-h`Sna|)I)E4zcSz#ujoU3TTa%U1J3o?n74ru`rN^qJH!5( zT4H+(P3%l<&$Q)*TWd=fd<*io=+%fg!-;09^1a=)M71G`r^CFhC}I?z5xT4Pi|)4M zFCImg!#`>MlLxJcT6YLz4yw6V`0o0G)gO#*aM$2L=V;Y%`+D~wjLrzk_8%4>Q6*-| z2{39Qga(F%G262RBR$p<5E^};eRQpRQOson9}=B(yXc>=`bdJrHZyd){S<4()VLWRzaZ!!k71b4;w?nJf1XN&HL`}4XE`x zhQDT7M~!rChh!)s@Y? zqD}PZiHPB_quock?!CPW7VZHR#FrIp$}uXV<{~9U(L*O8+M_+Z@I-+CgY^*2!p%~( zh;5u&6%}nJrySKkjqcYwjy`FV`{nkEpIdvT*cnmR^Le0#*Tjnt3Fo-hJ@TRC4wi}? ziYPe{GF!~8ugGH750vXvmoTTJ`Cpf< zzfU!Iv$S4%x8N3LZ|vdrBa4b64Hd4|S5VYeI99RlUh%sRH*<;krwbOW_X`U~Hz?L( zPep}(Py~K(WS+_?CfZ+x0artYpG_I0A3LB9;ciLz(}J7-ADCm8+dxiAY9ARYT$a$# zu$Vnn5?I?wJLXbkq!#bW2QM zTYK~4+n~C)+qp+e-SRXAYCq+ET-L3D7b0$P@rQK+W@^1?d%uF?z9~fMQWIrX5qs3N zyE#QI)}IEuc=as0e(JiICF!6X2|n@c*yABppSV6n+afVuXU87);cSju4~9ffH?|RwzY%@Ak6YeY{Jn%-3DWMno@5?7$No_x7i`Fr+Pv#TbicyFn$% z&q>gH9(CBglh&G0H-YO;|zICh5L@npm-K*7FZ`7UV6IdqM;! zG>O~>h*IL>5Lb1@`17CX;l=<7ZDm1R$OlRFmEi`BDAncyFMV{P#1IwYofY8@rkUYf zL;dwi-@ts{7sHg-6#F$Vm$$`RPKY`Sg9UF!VfL%h!q|s+7yKcEoikXxTA+^u>mWIQ z|5HhZY>&21THUPoCG%J5^;j;OGNX6J_aAy36z8rLv@$2lt$o#CwwNB)i+EeL)5!eu zH@ii+7TBGId@A%&_KYR=(s%?3<@W6-ADBFnIe!z=pNg6|S>+Qw^vSpV!vEMbQHfiI zB80A`_*)j{r@*|43&bFedYew^qcLU2^s|a6=PPjz8pn&-ZAt3_7W2nnA1!(01k+{*cb-yk>p6K;}5ij_{MQPHrLZC`=DwOb&|(9g)6CiS|AhI*fmS6 zCTg>N1xXXq@EB%yY}SZ@v-NUbK3^$1W_z2o!DTJAx*zaebmfMorcF<*Vn#InEL-ok z8_=w1I&$XSZF?;_`CAEY#)VWrl9E#00jPnh+D3=BJeG&A|cLtIX` z;tb`vPU8v+5vq-Pv8L~c>O5&i#}ibw<-QYXeEB-}A(`5~;bEw{yd~Ox*?c)SdOeH< zi58d31RJ96iR8F@O8^tQ@sdPRCY+sT;OF{d;*Amb^Y(|cbHgVNzE2{c4vN3yfCZ@= z6Hj6-uOjt5RiiX5VVZ*cMvc&1j^5rvELX2F8bDu(bqFn_(Z|l_b*=DAZ*`zyhNv7< zOG-SQS(LfPvK*?W^BW$JJiM%bc@Rv1@@C0SWX9x@X;~aGIme3i($93SKPJwxiW3WT=a-Wem9eg-J5mN&PBVS${s?qx97Ho1j>s zfbEmVM|6?qUMl-;hGZF=neU~$X&xxTX^Mm6+Wng@(5DCSJ@tgqgG(`%Wk&8)BT zRva)*K%%hu`r zednwSA`ee~;r#Kmiu*3^KjkFVO1}=b^h8ydVYl_N@Me zdjWgmA0}O$(hy?}HGJonA^i-yk0crF1!gc{Ctp&bA~~8b=1+BQC2I#KdsW?nYGiIj zmGm6_Oi4#gilX{(yuw(l>V;0h)HZ-#zuF?2-f1V!wzl-QIs^#`jdt{(ciJ9>r`peo zh)iT0Jv;At|D43n*B&AaARM&C=vZjuoXKJVkjcjh& z6WMhJ)3j~U#!&0GGz_MPv966-Y9fYcFw#7HAnK8d-Ouw+MD@-uo@}|Tf3AenX}TGi zzvf!y==3Hf#WSeI?cLXdSJCIJR}v7XV8a=~e{Cv7)J5JlnT?H2P8d&DM1)Q20t2!9 z=g4SRu>JgK1z%l^{jfD_Z~5IMMweobJq`YABP~b?&nO!g!_$l zfL=GzZO{*y{2*lA_809?;T!CQO0X=Ko)1EGfv0j>hyBxY3b3T&qD1BVxuC2dS`If4 zTqNIG>PrFltoA6+u;)(~p|(YJ2tItSrnE#^fO4;WN43`KK2zGD1!|zN{dUToag7l0U{@Zk}~bWdTn^*YW*?}G36rd ztoTOC9n~q-tl`7$NrBFXskMj~_zFHnHh`ZC*(}U@S8OR8@d0V0`}yE`LNSEP>ovF0 z_K7U~lyvYur(j=@Eyv7aEC`xyqO&(Am&(G-HQXE-@bNX~1;|j$O4MibqkpqzlOwa2 z+@5n5+XP=>kThRWjEBZsSghjw>Bd(@owCYjANRV{E9nM7iQrZ=GL4tHa2;RWKJDps zgUiKV#7ndL!|qkV^~=(1-~C?nC8Jr($&EHBAY!a))WaXU&i52YmuFFNrfn&_aBR@s z_R238AF3!^!BBBx6LQB(TzeirbKWQO9sl}J0R746_w7K@M-TzY_@8qzC?y?qM`b~R z@f`3;2KV+>2;VHJ5|GAq1^4@pyU>c=R(*gq?jPVcAHw;$b$XiW&a*!M zy1KfWphqS3dRox5+W1Rk%gSt%m~UDW3Dbo&b_NeD^oTmmYN3#pnV*D|3Pt7xS zD^7ncVMSHNV}?k0NWF@4Y-4D8Y=K9_+UO+Cy=O0ix3?EZLqrr)kpm^f5| zRa3+F39oyw0hAjEnfPE~qpmY5j9jd6yR+bi8j`d4Ow!ec zf-kA;h^TeCZP;8D+9+~JmY%?gQe}@s(7{O{&cV6w#(gl8j9ZrT9;GTMD2Pnt`K|qf z1KPNro#n(zE?)NruQPOv+0I?l9xm%sM**S6vj904u%&LD;IoX3it@2})(^x93A$C# zie$4lyT=AmUIcxr==FD;J9>SpU!apCOtxv5RagFlHsgu8N5GcZ zOohp+1+y$@xc^$~PE&_}SkmL_7blddn$ptf4D!bWYV7P~wnIjC2bPAwjEJKC;Uf^Y z2aYFNs{7tWi8Tw>J<&Z;tkLuuu>n(`d{fbydZe02HS}k$n`q+BrRdPVbKk zURvwa*ivr`#QvEMf6@-bKQsx>)TJ@YOBmLBKg9J?D8(xH+9S67kB6o)u_u-;RnIPr zTee*j#ZxFFm%3=(u(?OTpJwWl6Si)>qo0otvXJvuDD?5U2?je;L`e9AgkiN}XWr`U z+Rfj)t`~EJaapga3cC_rQ&Pf{KeiW!)?7Uj=5S!(1@??bvwATZUsBVTCGQYFvmm+o zLmn-?+6ecCS36fiad78x*8XBF9sr&b>tk|}We`Bwi z@Z5mz=qS*rf!At8_Av(1=JezuSR#N+b>$#f=Dcz%@rKtsu8myfC@!vXSwz?S^omw< z*-i?zSik8S9-fjabmo7o@fI{f@+rJmLrB@*goLnq>XxI1kg!W1tc{kL&)+}WU%VnQ zcnICQ$nKfplz;rlZFAv%R%db*2N!2&wEb#eL@3dDuLgm^o6(6qr>2%xdQlREj*f16US1Fh`_tC8uY}P2{dHZPYkI@a0__s& zjm=Fv=yU`9Zkc;%WTcw653LXa0KalU_C=A5drqw0xuGaOpzSedA#aNXYs;aa|C*D3{W~PH znT%nzWG`sKBWS<3do#Mr?j8$v!HewJnlC1gs|Nad2yJ%5P|;`FUSr@57AN1kMQ{~K zTt)_i>Yn49rc|2y_wT>6-kxa)>AZgZ82lLwO-bz36Gxn}&vlS20FeA3n_WuHG@9D7-N>HHGR# z5QIAIw(nhAE2x+%ii?EVNE4d-MHYuf^gh0Mtn96onTC7$;K^-{{dagkJlNlY{iP=M zg1fuBUR&4XJGavI3A`9gyWVZ??RxKa2eQTW*llfS!3|{oO3TV?F^mcemw?xdj26iS z6Xoa645gJH2`fAZw>Br~`57_zZDx>WZr0=*ym%2_Wj(n7wy}Y%vJz*|`C$sLE}~es zK96!$`#rQy`XuX$41<4VWhJlxh^xSSb@jmD;5Vu)>}6gfBcrrW+?K1s!1h1K5XNS* z%%DlIpw%AM&n%Cy)TP@zJOrd1I?ZhXctceRmJl&m;;2RH=tV`TMVI?&xw)UzhWq;q zRq*nmO}Y?*iG@^~beQdYE1HR!*#d}qU;Wx{I{p!@XNpzK@*~21Gl-P)t?lnacBJN$ zJ<-yN3h~$exE*~xx-Lh5WCH&_l_9V1_wdHMm(cdce|AM_{8BloH!7ac7>WDFR^@zl z4!&<8LHZtOHrvDxV$1a1eV|GI{ON%uojs{}rBb2*qXO)z+NbY16JbM=4A4VRO6q}ftNl#wO+v-9?)Bd@=d3j0@Ou90l`|U#Z?cuR8|Ddv~V04Y< zaKZv!7j!}_SciO;_aWwxlatHJ%S)MME2%N}izoR+@u@i-)JREK|mA{>%kt>=P~wtB26o^JdH=G*clVA!sr}1#u#5 zkx}s`GxJr%~t zqANy6($v<@wD@TDtHG5Rd?0i68iX2?1+k%@Ektp=msYdkUvm@EAss(QYJud`o4a8uboBKJYIQ0QjS63HiM$5neo_4iW z3v`p6bN*dbKGRo3#mmQ6YS-opZX0KnKikl})7$)8Mn{RiW-GQ7Cfq^1HKm9>s(vUH zij57Yd_326Xzq=QG*Y4u*7b+&vl!RrLduNo_P<<5{Mllshfidrm!Y8{?f)$v?fuw2 zJ2A%|P|!X;YBlW`>FJ?0a$DLpg*CUP(uq)m`Qa@B$IW44=&=G<`wCJTbS2fSe?i68 z%U){rU0FUw_$u_Re}L=ewz9sCg7iK)ncHQTNchz+q&tl#=n_6YxBF#Sqht~B?1M5h zIsXft{JDH7x|VPH9%0~{V6fCZTh{94cT)`(IzEU+k?-E29vyDVO?f>8TgaWsZB}l( z1&x}Q=j7Q6$-%IIfQJH#*I5}ky>$2+DLQ1N+=2q*!;NjO<=@*oKe~*2<9BdLF3ZUj z75n4)-0oIXRCXjW{(Z~g!D%k!`@5K4u`{{4c(!~J*pM#%h=}dOJ6a-)E65iU{z4v5 zEP{k2zpYpOn4S5{l0Dnm2_viqC=~qG9D8S#OYC)dgg%U!)j~SP$8&9r($-efu05 z2iMrnUlA4-o^^ws%+#XpfzXS61Uj(jJowK$LbM%nT}IY!p{y!7Aw*KXppza40M()b?PC( zp%x~imkxa+rThwgu3Ao8n*wauTZ)qSxh==!5kqQytXKkanNpV?5_eGp?^neCyn`7L ziBcSKO<%7XrplQL5bsiQ4wtv9WsNmv)4nfc2?_}cToJpe#litU!r_9dcZ zkra#nF&awfIFA);GmH^3CQ&BxHE;6Q2+7#L4%I7yyG?Rqk4e9&3jd|Vd!V#uItYLJ z??aaOL8-cuijK;T><@cE4DNs5)4zVky!{_#B&8w1-TyfDFQDxD$Nym{rTl;Q44h)E zj*jdR~={wd4{nzjq@*2H| zJN}uWhwG&W8~M2T;uf8DA+=%nD1EP;v!T;?lBLlv8ty;^j>PT%aZ#IAlF_XOFrM9H z)d(+IWH-=ysH>$ns)<6;`Sd2=9X!&@7_2c`S1RZ#WIpuihOe)0yV`%9D@~{~d8r3c zDmIdB=?H;FdSlpKq?>GaWpobuU%9!pKotmu8>z;SmyW6`5u!Y!;J5PD;gEU!82>4+ zjru~v(vl8&4EI%$7uKtTa(f3ipj6hC%^EHcXjshu^PjpASo zP_vfY)7RGlb{2*z4MNZy3MWX4?uD|H))uLglhcP=jEfKg7g&5C=h?Whi&lRE8l-r| z>UnHOA@U4}L<@3uH6w4KTenazJUw%vVN=?tKLwLcsl}r77nApk|Gcv+O+Vni7Af$S zI(@oL-TtF_C#lo?qK%L@We+v{4)vmh{N1}@m2dJ|dTOm5Ozo{2-}#!fAm832tyF
|7M@CbQXQR?6auh8 zT1ZZS{XKerJl4Z;?4I7<_He3JC$ZJ@N@2M>}#-4?lw!EE@77 z=$TewK7(^~<%;dbIJe{0$P+>5bvVCw^peC5Z5-NlL`tc`68EaB;MCeTwC*4yP>z6{ z9E`2mrU(m_90Y99EkqRBo6I#Y$7?-IhL|N2AGi_l5KM*cyQbHPiT$8=X&dB|Bcu9_ zY>(fT&u}6Ekfd8vQ?~#KGs{=T0Du35_O9ralP<8JiKqBKi)w~v-oQECGyd{ z+fr~1^A;Lvn$2vZfZHJr4wVqj%na+V>Gy(>lHnU0XV;svEF4bvEkdb9uqGyiG%L)x z0S~|dKx-&drsdl=QYOsYen7ou*U9G@qsMQbP zVel_$YxN5m+>2H+cv{QzlT#B3YCJNqJhQ(CrYV|$s)(L%+(DhfZf|`Mf{gU6|9y-1 zfEZX{GchQoTtWOT-*a9F46kV=(HHf^z3nNofM317yK7?0QFEB}B=+|M2^kquD7_Hy zfoisrsK*-7zN6DT6dO8dNL+XC`a4Y5)p)Fvl$QthBP&SlGqkqeFSA-5{^ojO3e_26 zpaouUgKU(x_>WE;asM;0Rw671o>=H{T|;f443xDQxQ9J18rs!TL3MTITQ3s7S7Eph zJFa0y3A(WepQG(qTOUn9Ti+AXMg9)M?!#9V&SM>`31jAJ+8F3U&vTzVAdD6cEAz4k zD(tNv?sCsE`a=bUj@pmU5Ju1OB%xLnWn~gzykvJcr3HmH%cQ81d^?_lmp4?cha1xY zc8i^Dfc|)#?sx1i>E(jsW8MVJ0Yd=`Gk?f$5%011IifSRXwu;M^I%v37GXH943^PDqh%ZjSUA)?sFThcZ!$BD z(?1fu?OD>Sw7(7g&P%OoNlDoCLII0N2SYo}8gp(iHAjH~D5g;&4TvT9=Dhj-u}LMR zvtPG+$jCg_`qW&`I5Bw(*=y*d(xA46X|X zp)p~hPNlMK!=uHPHViIqZl9pwCq?=1mOQUp4rQQGQd0f`<12dpqb16Im<;Iu;v(#Yt&pR4;G?(TCNKQuQ0ji%SjY6WDr z8Qjp`;8Kh&=FpIev%~}^{k=)Vqv9HBmN*YUU|hRz`N~!!mfKZG2W?4qXlqU;h1joJP}Kej~?W zhbx7~mqJO+tAw$yOETx+dT!$}ax1~dj?qUC6L@&FiNnnbTBF`EOS-;1F{{sd+wjqQ zv$bu&Q0pNTWGdp?nUA*?uS}2=Y@}!sf3Fn}=EcK4RVCp^MsmzA2&i&$-1%0SVPUE- zV=yOzs9K?EJ?rb&fIX~?r_-OEpW%afmlYUa>k5YlHiHQmHY;nveQlRKUh5arpZr#G znf6Z#@pyT8-z@>!Xg-?H*V@_^_4e%*6ckA)h!;-})@7knc@?a4-gR|C6%`c*Mn=BB zC60FXS5-_K0KyDf^;le3u)X+%2fo^`VF?H+@bMk0jN{aohGpxq5^r^Zzd!ov!6jv_ zpy00q;&7I8_hxYrK(csMmzIhH^cR_u!hxwA+depmiMz&+|8Ba@1shh6mS6MqsFN5- ztgNj27ZvOgQBB|z2V-lO4no>mpCXcSUrxK;oa&j?Xx7cZ|sLIiN7Fs+0+=tV(_x_ibF1?+KEB1_5 z^Wkx_BZQ^bjW2iVD+)h^uVtSg%ahhGG;zI9mZWf|ZgP35S*sHcaCK{U_oIXv_4J3) z41=Si9VdI7G75fTSOH~aSFJWTP_4096_UnlG-b1tac=O`d|_gT0{Rx?ZCVbFYhz<$ zWZd?*u3Xv5XmGpNo5DwEXqev3+YV!djQ@o&pz+~b({)Bur25U!7+g@_$_|`i0Qz_X zGn<&=ru@S$8g^|R3I_)VRi3-jn&$E4y`wW;k77a}gLkEb zmX!`0i^g5nD*eq(OS;>)(RS>17V-(Q^5T`M1WX(BRaXboQF__gY&TY}L&*cUA7cGS zWN84Lck~6V@G<#T2Jg>1Z%r{*rxht2tku}=5Nu%tV8()7c1V(^Ev$`Y9-S}2-k6Sa zdkG~IhH_)lp2!yZR2z&k)r-ErA(o6d&xXjr+zT+2m8rbqe%7VXIeFV`FbU;G1up}%JVjPC92mi#oCIVJ&@_TTRrq( ze9!8YuA)@BfNg@K9hbMC5BjxPr3c~i2h!K3mHevwQB(PIZxv@aryZ|Y2qvuSR{YS19kBH_?^0}f;obWsL#%@P8816rY!f>;_QdJnes8h$NC zV86>$o2;vy-%s%3vOKgBb~_{jkKWcdoJ~^CnXg1m(5x`A1SOJ>JR+mjN#W&v)Trp! z(0@*6W}!t4WLbwFHYxs%8$oh$9CVPvH;A1Vwm%t?QUwBSss$o{WK0YiAt9mr#mRE_ z?(U-qpm_gv(3Ff63Zs5DLrkOtwnyO~rE-26ZHeueTNQ;AofkdPDlw;QZjr~XM^}Gc zmx$V=Bg;ha_`s2?Ia2!b@#2KTd#`sKxvcEk#tIcxqir#rH(tG0-M=?nQ=vooo1Zu= ze+^zNr=zgl42!?9G@`dUy($Z@%f=Z}jy_EE_ul@Bfg9-Jst@AVssG-?;OBLKLP zKU*uHlTblqEWbNQU+u442g(T@iHny@3h1j=8ovhy33oqRrt`dO${qTQ`x>v4g zkf!JAmvF$txsC5$@2nx`;x=rJL!|2OlPQNP9vZvf@gP@L(eIap;pxNYbrE#9IX%De z15+bTI$qS890q2LH5Au_87_Aa6@zEBjqz8YHdTJ{JdD_ywq5G(d3W&fS)UZt3a?Io zT>S@+dRUmyecIZD0?Ru2-4~!LnyycW^MOQ93hI~fPcC0sFU?}JY5vAPSm>y5ud?Ct z;$H|;43)^LoGF=;!%E9tTzfUtDHz?7cujjHt@>knw|9PaBI>LYm!uj!Zm%F!z=z(q z9}Dv{@;F);`~3qIdOEr=9xU4Mr#!r#7#~K<@p*8xH$s9E^U)JQaL;|j;lCaBre(@N z)(ox1YzGA~yf!JU=y(DUHnzgCf@PV88UT`GTNnKeo%ncmH#6t@6SC{&gA}JOFq! z^>uZrHm9Zx3=Mr?Xq(n*I%#VIk3|HeeP@F=k{#b;E>5dxhA_K@9}iJy>KStp@WG6T z7o_4C#bFfKYu$A0e8e#F7K~!ZYipWYTdx2@%Q9R7eG&zZk0eHH29eZFamIy4UW&`h z^Aaq`DQ2As9m&~2xbZ92tuxr1;Hz=kV*s{NEnkqn?S2~&Js2n`D0aj{G9|X;CeZ$^ z5L`P?u5^M~PmILmu6zVrEYzhPAtVdSsh(0gI+Wnk84d_8J%AtA(KwT)urZr~RG)L$ zq6`fS8!FOv1}^x<(blx2nVBcnGJqoP=c7`HEc5+)N??$0fJ@6~Al$jk_Q{|x)78VI z=;+sA(z^%=G_pr9b5AicaCZn~HV z10$Q8f`T_VIEoQ83cI0pm;t#K9>6%`FtXx%y1YX}DUgldT%)olNJG(gw)HzzUfRoQf|s^pY{Kd#h^H5R}1tC9x zf=fD_(#H?mOZ|n{X@y0N`w7p;=qR^K_XwReqP~_u!I%TD@%Zy;O7MS``*fmQR$>_O z?j2E~)<<;2+k(w#y8>#yT7m!SY%kMriC3{+*v0wj6(qBPi(*xuf!2-ZBZ!>%{sr!J zjjNyf&y^`*TgaA(D;*~%-l(~6kO`O#--plI` zGF5v=@x8ELR!{7FdzXqPa7;kj1pqn#9nt64*F(0Z>lgP|?;LL)&BDB9c0c|83= zBRh-&H31JuEJ(Fp51e2l@_;jsU-%mv8^46PuY=U-3R3U!p*A3cc!h;U=_Mr*F$EXR zd0(G#El%3ROkV%URox7pu68@ardHEcxQvP^t?|C1!Vaf6%F24-m>h+?=i>if+Et?} z3yT9X+pG+8i~I+iMI?2%xi+xnnF(qX`roBLy{(|iV(3`pTkC`F$U73LE&r=p--CX? z#OBScJE7gZ@)e!x5+jQ=pO<|5w)d2SF|KmTe3Z`qF}L5wTQfTNy1`;+HPXSvzV|mH zb-D1|ZVh?-SQQUCZ{ZsSkD?t(42O+|IN!7yHs1XF*ZKbB$2pG;?rykTYfXKY>63M> zSfo$bw0MBjGBO{)n+A3t;(OGwvk>HKjIO8cbN#W0ePUvN}h5K7O@R^H+BM~uL zSP-8i>Gg)rD8H43Zms(bo&=m1x7}UXfEuin)A*46VGb&ABm_if3?@;faH+~o<$y}O zRj6A}!nd)RUXpt0-(D~wdy4>vLX=W2mhFL=8N<)M$LtYUAuZq!1YI<{qJFUW-Dv!80)GRlm0+nu}+?yeN{rx*<;p|zzEaMSmjkZ zIke?I$+QIuyZ87fMSEvwP!bXR&ZlUysxAMynrYLE*)l5VhTjUW0krtG-cn3qVc}qn z-J(W#u*dZ8EP?RwaD*JI$@3h*q4Ju#K<|2IC=dxNUVN3-d9eS4RiRWp{gZE1$I!^I zZ(SWaXmRZJKI590{cMsjGcZ8Hz`#hm=_i6D?0$?7M6Y6y0su)~xpzlFe$6O!XiIwVTYm)Wtjl}9 ztWJG{qfu4flDyaSUkeSM|5~di2z;~vv;X@?Gow+Of+ql!+8wyX!CGqbtQVSb?5>v# z4i4h;)1ETL+}6|{hnZaJ;^6sc7W^s=JA#Q97#=|EF=&ljL~sY-!|VfYhJi`q9C605 z$Z1wNGJ|Rh7R$i~H#~Rl7nBef(TP6TrUHI@M)6iGe-;KU_$Ep%_fx~?hyx2E9Y*Jq%Aj!9tl9;7X)YL6QzbCvFy6RFd&WRGdhsGkB?tL2};)W@Zp1r0Vp=X zmzt;|uFC`y3#&ch>oa^xcbW)lk^7JgnD8W|(GdN9K9hl_UUZ5AdYqDt|E#^cM1LqZ z&exGxv6mOv4_gfCzd7AxLi4B+>$sr3dt6u?RYQ&x$sTRISVS7{DNIuJIZ@!=#I;y| zarb&!>+Lb+jPyr#zZ3+u_cxE(#T-((|OLU42RL znL4m_cPsWdKZe;;h%_QAj=;|K1AF546|5@eLt#n(ehNB+jDm3bwtPuR1z(B!R9Yqu z?B&k!mZ>?@vw88ySkhY|$}cqRgu8C{rvEm6=Fq$P7%j<$(QtQ&>^-#wHYqFB{`3yf zHM5E1jM}SHW5TWXm9TMatU`A%GOv>*1ueGi$*{&fIIVM%O%T)9!8zwPHsEzGt~oAx z%!fbtA6YKV1m>hqfl=v!J=W){k~bJ~*4$lRHE$H6c}rJKjEl|?Xe@ufDJ+VyNh(dp zoM~l)j}J<*F`S~wik7ueFD4mZ4_OY~Pnh7x7fO96vE^yTeXw%#1K=+{K7I}$B~~?{ z{iQ?6e9|QYt{{Q?Dr>Q)HGpz$0TIpf^fE*B&NfJhb^h1G0crt{Q{Km1`Ci|Or!^~H zaYOxn9g;5~1OQ7g4hc=%1f|yL(YC(H>cV$4At4)nK34>Z@4knN?tUToa$`I^eq0lT zC6rV`j^5d3U3jto%)KrN*qyF^=Hu^3R4V$##ZB@1${vOZZ&7wge1%SndvX4krRMLY zj+zt}X(-{qNdEKCL+}!6o%#gxer%d$=uiQ7Z{@`;OD|F1ri~BI)WN)gf zbcofV^ss5=^{S7PXxF&c9oz;Ain)QPpMy-a8cr&1RFo;t{UUR%+&|vY(@{`m00mP# zmmwV}@O_i-y~O-AH%I7vW{Xjs|+SR3{qp<%ZNyP0gxNLbtmfG_9v$T$G26V78 zgbXA8u61I~S{Qg-qJV_Ye|yyOcy=%y=z;b%G4=V55K<&0Dk`e|wNVP02&&Glri=T4 z=_Aqw!p?}cLVHuP8%ts%IFtgIdp|ju%H{P1?Dd;@7x(CZ_W`6rV|W74;`W#n{yPm=qPksJovX|>g>;-zht&$daF#N^w$ zFf3&Ix+SZH{1?m~tXSkp$mw38!sWv;*L=8?fqC~E@93M5m}~vQ@`&<(P&K-F^btl~ z;`5bN6U?Su`Qry>bS$?qrm1k->ra_H@R7A^DUVkN9pcGzji*=6k~Y*5C)VeZwiw23 zY0(B!nv&h;_IFCl%UjdiWVq}{n3mkbXs&eF;nJj}9=;3I8wPC@xGd|fMO7LZcZNS0 zND=AiC}C${AO{}o9j6CCg-pwD`?|aD>*-M!>(u&IRPZhd!CX?#z8Y)x$WT2Ci=6K{ zYMyHFtf#gQVBx0PfV@M)?Zq`UebevjvPw!auUgJ(JGy#%?<*=^!@N{&3ap>zVlT=jznZZ$(`G`Ox?XT#i7c0l%V$o|B0& zmA_1}Rdn3m`fy%^D<>$9i?I-%JBv!*rsC^$`*78G-6^j4bh@~hj>$bF_TXq^d zVQy6P8tzfqq9Q@!SJwcI3RQvkisXFz`nTP%;N(8 zV66(HmDa$UJ{O!76j~PvAjRb@jVTybBLW$N*#7*HcVN+=p%aFU)-3 zwRB!6_)VAK4Zhf*`YADAbCJ^@nPuVq9pWq+-p> z^(hMj>jOK`*K?xS&?EdwqoSb$8Az_wKD~X1weiDz*eBfiknAQfzQ#I_l?&( zqUsdWjQUJkM?QTjj*(H-wg~E`h=J^OE zj<3&I8Ti+LdE`nm4?n*Q=&eBsjHt$`g^x+AUcLmIaOP~qq-#(Mne}NmcXwmqQJ-O_ zIxNGV4VLJ8ZA?`Xf@Jdw64V`~7JFv<>)&VqRt^jgmj)e0{`YYMP%N8&FVA}QEh{4@ zC*Z~%v$XI`dZe2i9Eu5^TD{-D-+<)Vuc1NYE$lIW_b!y%xRb<$7QdQ=Md3fO$m7ck znMd5~s%+E^Us`^8j*)FWFD$$>qTU8816NHHH}x$O$K2 zM`zL*_)pmwf-F7yFCp3g`WY#@IFL`=|2G1H_?Zz5EDP0_uU|VlxQ-6cc=J;_Ns#h0FzFv>3lG&a3E&Em zk~Z1|#4$EIdzGiL$;F~uTPuX>Ti{Ke3i%5C=gPsO4Ce>`LnMl4_h!?uB%@^lV}NrZ z;nYIPGPTqB@}6&?x4eYNe|^@I2m39ku%lpZ`}WSSX-1r}EA}Sl!W}a`vj#n@oY#}6 zeQ{MXZYuN7LwtC?rf8sdk-JC z{>Hjvx_xFVOu@GVuId{iXh*ASbL z(lXlw9sWyq$)xYnZ}?jeHgyS>y6LfPT|_e!`_}oR_T5`+&Mhauh4cf7hlj_GbLG0S z@}0Ti8g$Wwjbmwv$=178nh_#yhX%h7sdAOGf`Qil=Yh$z>}`D_bx&VXPj9E?DXv0V zSv|u(%MvXkBkA|r+Am#Q9STiehO=LAGCj?)FWu5>${0z$EBBlJTSvLiUF&%+#Gv>- zFaVDxxZuxwv%EhdC}n5XEGWLpYgK@(D=BDizZY4cY2qTQ_)w7ZwRVjrh)2kYx*^i zCK(U*%%HMUt_lkbY(^~F7SW#(FHO2zROYl!(gIXIre@`H;?JQs=rtS9cIDW#%26TL zBIIXO2G{lbB}%SFi~hI|E>d+73P`J|QsmUbEx+-2GCMvPUqkoix3?oW8;`Htd%1zH zfsB+Q?BWX`%i=eEuLmz*R@IOL#BhANUQ%YaC~ZgFkd5Gid*iv|c`e==baR#=D3K(a z#+JS1fsQ|lz6q+i%WU4H*>qGdwQ2omRvtso0V`(JF^KnLl3s11tOm z*8Lw%U4tW!1N^I`J|);YK-vbtr{&6uxp9jx&CYs>9D+UHHXHa1yBkdF|Lk7c=Ywi0 z2zCjyfZ9hog8DVMwHhZe;Zu;42hX?pFSr=d{p?Q;FztzJE3(#p6X17s0>q`FKq&#l zO4{a++XnhGEH^hdV1zBS52{|R*PkHv4urI21@27F!gwx8jPi~#1iZ^kTQdq4G;d- zTN>376ZPiz3o($?0NplT4jckdps&8^?QY5b*vC;kFjz?r7^KKQdZcHF$YJ`)P| zY3a!!?sr4K%;t?-^tpFXYe zx@=4n1%4hTBSN>yqEXD2o5}yJ&dGB8`}g@TQS`8jiQtcJ{|Q>UsLjkLmxb2qj$UcP zs7o{2d5iFJc}cl&a*pqV!+nx^DW7`f=<%>~^BP?8CB3}FP|?ulKMtIV>+9=lQNwmi zkb)WhuG;){c-RUXQt(O0gW=nxgOmjZXD7I~3&ywVkmIUc0D=RjmQc62QMQ1ZBN-1cMO3JAx1(h7&95KJ$F3to!kLNKyWM#E< zbv>-bBkEi;2G~OGio5MKF9r1pPKMGjj~?WC-dGzBe)wVyBHa0dMgOO6{vjJ*KF;B`Ppg4+(*kN9y{?@n+yTT@` ztlK_^QP};OlK|Nn8Uq7^fbAR-gyay=i~*3U;i8w9mj_#@-c(ofhf|B>3&&MYi&Z`! zZG~%H3Zarx$PrVcSjVThn8V7-N-tqX0Jflc6`rD?pyPnhEVKL!Ag!mxo^S5HbRBnL zywOlC(o(2WCUDM0wYhnaUXta1o0M?KZIaO4jfbQjMS0Go7#GS`xBIjpwfdg3z~!!g zt5J|TB-MRkM?)N|cYpNBHysRutEGKnR~6L#GyU@z$kmB2E=Ab5DZ)IlC1u@7Tm{JQ z(;;&RKk9_4$?&X&iA{hs!uT?~1w;cc890xM($kTlcgHmF_F1J$OL zXXi(=0>C6d6qs9C$rXO)SAF76bK}NUpxF>m7zYOjVDacc&CM+=WVlnya9?H<99;$x zGQ#FMbime4lUfIM1qB6+oV!74aERqKGz*ujn^;EHnC#GKZM0Fr-Fpw8ZML40`u z_$vEBfP|WBi)g@zxHC9mb(Yd#QAb9-<@@Au7eH{SbIVt=e0M`H&{|mj_{H~h{&Pw zQtt#Wv~}NcZ(vmov1w}kZPs}8B@%45@{M8jl7W;WVJsR-pR@Dx>r_;@FjjL5mUBKg z*)L(K7wcSAP*4Ee3Li*wDZpJ2&j3`Jb`!tee(4cn-_zFC7NAvR6V)~mA3ofIV2y$_ z{PpYn=4KeZOn5VdHc5Y+mNbdi2q`7VCO}__`=zgsm{Lg#lrT6TCTknXQz?5fErht( zOad2xrO|^%3O2-b01-V{X{iC>Qj#HB0ygpii#=ZF#0vZfm&q6W{jF&V*tD%#Wpx8q z%JzB!Xt31*w&k{v7%bJoxC@nbThh-Kyxi!x zmmMizV%##e;Fl{r`@r%+(1q;(VeGx*xqkb%aV1ogN=5@kgY0Bwl@S$53XxqYdu5Lz zQC5;IlG3oVBQs=2$=);BTh{M*^|?OZ`}*C#$NhWU{<(U5F5csOo#%5L&*M1GjFo+x z^-l)>mUImCQ%zvj8~39h88>;TMfyQ+@c6aZ*ki>nJL^-YoW%yLUaD}B{zy#pbXmOe zl$AyzL+i=cBFgtv8`gY}WHztTKBcPn3y6(7T>Pg#F>RgS`0ES_{)1&-_o-sUza|cb zrV@uq)WRd3>8Yvrku8v_9$~zCYk6&Dkr*y&WEv4VE!oCnl2~T8ulkSNhZ|$u*7p&_ zq_scud?qctM2TZ$WYqHU7A2(d>H|JJ*XrNs*T+Z_8pXiiV9zvzidVO5iQ*XZw(6%L zA&&qw_q%PZFoQF?i;3Ycx zwq`{oC9lv>dW4ckSP{LJRJ}Vt6Zbj0xTIkV*X0p0>nc${NbSmKdXQ5?SeUZd2z@}nAnX$i5%y;_9u+bfV`u#vv*>`AJ7ef z|9}gA7Jp4Vf1;MCt=)lPqEw`y>9;~RH>mR|K${}Duk!X11aF&h0Lx%o&^t6lZf9p# z6D2}JoHJ0?Ex=)IP!7??IKXjn0=9)8*s5`;h(rrHzAkEwk=FF%U^9E*jr3QSriq>m z^m7!Cl+@JJ&ht%bsG2<9zC9kV5WX1?B}&xpQff>Irdo(~B-jPpXpX&f+gPu%W)?Dw zIyu61EcgNz?99lo&(7?>Oc#~#Vj9_INgcKb3DXFrB^@0EOF-Jn>Z3jF0}5*9B1qYf zft+~a_07Sv(mRBCzmQAc@J)LiNp?Gn-(#|$>R^71d*Ozm2OuE6*f;E|^B>HACqPi#WsCiIk%a@z4KE#r@wg2uP zrD0!J{OX1)9&oB#?g{PlOSJix`e7#~kh;LN=8_B_^2d$w=BM#J5)v$zj5BR$(V*{A z+jF?KQYG0OHIlfC3+TdUt`qvKx#T;>#-+u@o7maeF{&fo9!-yin=l(NFfb4n7Cs~@ zsu?bL{aDapVPOV5tkkqLWr@&u^mmP~&ohWv9U)eon0!xkqcmw`_z)m2rMj;M18kbRj` z!srLv++;U1dOA>hpM6a7@u8&SRDWqAWILvgkc=szSP`!ByKgt;%!A(M+nbB@R})l; zlO`u6<(~8SjNg6%Uh^)o!9q%MY9?PCt-~l;lwu@kGyMGh%fWH${e{cOm6V{z*iT|H zH=>O32qh~1M>x9ZK0T^}Iam)JIGrJe&gzo?3Mh}MgrV_WKA3#r~5>}N7d8p zq{GCgYUb%v-7ORp9Bx8_f^}|@jm`9D&pxQKB0%eGbfT>}W+5fNbSsaWD*wV+o3O}NT&uk*G(96d0!kU6CjhOOEsJEx*KECcl&q2f0%f9i~UN2>)r9DVX<0Cpg zfF7tLz$J@C3WEUvF_#7@_uWkHRgjN}!KA`5m_EK-%FWzovWahd zK{}pSF*e!L(8U>9;j*i2o|$MyzNY6u)(3Xq)KsrDP>qq%(N}<0-07DEPMx|OcMfEU z+b2k~kd3ry@nM0QQ=^DQ34g!pEZKJSwH|k4=?@;=vkiF^i!AyT=Cxz&#ZgbDWdEObCDH_?&of3G&xk zE(R{mN3zGNrM-#;dcK`;LR+)l|CPuJ#4L}%yVAuT6^6Vg%cRveNi+N9`JniBX2u+Mkmr^+ zrZxscby97|H54KRcQ`mW5V;>n|8vu5NjHB``V!UCHXD@t?&btx$^GgHS%|;kR`xAc znu4g-_;1$~ftfJ1G;J{Ga|%ZXdXM{letuQ=W1^#(Kp4b5vy?Y)7bpa4(jk($^VnaW z`uQm>EzN3z>l!gofYS@J>(W^ZS7N|-=FI&WpWWB+&O7lS@w92fNmW?_w+@A!NW{vz zFHF92!kQ?Qv{SUFK@Z&;W3-;>*?hM!w7ne8@FuN&PY&A zia2RH1D4QJs5#KmgLs6-lYLl(Rx)(+nZc^y*n|Y1ckkHF<}T%4g+`MaVfmbe1usku zuHMw}MN?RWQ41Ei1Wh)fzM*Us;UMw^R0TJvkUzmiVm*3PcG;OD_=4r~%q@a9*X_0; zj`Zq|x845Jsl9%&41see7LE)h`IaqPyc2@0tgU+yOi+gDBWH)5zIOteBQ`T~lGLm$ zx;cX21H9%X>P7qOs?(EC@Wg@C3>GrtS%5M|$p!J9t^+Oh#G z>~MK|TDn>Ov+kuf_lESNt2LFCvTtJ-ER5Dqs{av$Mx<}p(+3Tdcaz)d19W#R=-de* zljoZ^FO!0T_(6Hugw`TaCwu4jc*U~8>d;i(_02h-K5cJ1Eh@^&&p|mmR96O0R;t77 z?bS&)>FItY$!$zFkGc#cxVn|2T<&UpCo3Qz5UeA3_s*TYkrC?Pcke|*!)fJ8Abj{O z?XmWi?H|tVdHy=*%tZUu8yhcQPFx#)85_&a?Z|`ZMkqu&9@l;GcxZCuR+WXYFs;^a zhN7;yL6NZ0_x;(?*Fav#WU2lmq5AH6?!YVoggb@;9ndv555O)sa^y%+cQ-SzPg%75 z_uUO`v1Ndh^r9IBdC&t*KcTgVJQ36veX;4skIR`x*AS&(II`Cvz8Q0Iw3U8vwJ@tBw*UW z(c@iqMMdhAq=iY;q%?{I_87^*v0#|YNzEi}T#~z7y}Vx44x*do=#RU_QSP?3wk`R$ zI3~!*d$o0Up=%^Vk#7RCD-@=5H81pGNF%Abu}yMAG>4wy;kc-6Nv5U2SF2l>)zrK` zIl8I|nKx{9+i=;nxY@%(Qh1Am#PD=kj%8V0sg#V2)QvolOd%>Ok!WM_HA$)-X_u`x800D>cC>E1XtRo8thuiC$dT?Qp zzi`1FhXMqw=Yt;_=Rk;i;`8JUj_{W+U-k?~G@{rA&J-0D^(0D%D6LZPwG<@;=Vj7vmM?f5g6JFK#-% zk8Ljc{*{|n+|N$Ve#>a9Zul<#f{Gx+1@>I+cw8m(#pmE^eVMw5RI|DoW4MUbkL(}i z_a6m6;dJ@EFZN?sHP13=ruqi@OYZb2-aZR~3mkYpZ)VEfLZ23J^QoF*52ybNK)`9f83S$%Oz6 zuvAaP;tIxxSDrEK+ZT)d1-w+mpIK7-bxaBv90LP4A%@)sB%~ZE23<)!)u&KGOpn)- z;tq`i3L;yVf`@8hW3xBqZ!z8AXpYp$d#a)Q+vNV@A{j~%k>yQoQBhJC&wk6QPSv>c zO;z|qCJVvLF)eH-Cu^gz$7uK$u7Li#B( z|Ew&*2tm{B_$8drVo>@};n~AI3?qc2>T19O`pSF&k$PeOU1qz#kA{X-J}Kncvnw~d zlAfIcuM0_0*s@P1t)e?sPa4PtmNWZnC^z`FcejC#k0^cU*s*h$=@}TjG3z>e-Lgw| z1I11+OrvD0u%mZCbvYR7QfB#X{607=a02&9=<-fVJEqY-s^_M=9rW5=KW<19Pj;Ri^(qW!AX0H`o)yScgn29Is$NYpOq=Deh#sv z`>z7zHKeBX%eWxZ&3^Hmd=}z=dNhkyu4i8T=Ih(6JWBpz zx%oZErz#oba&qWj$h~F}wwpMZW!6fiRL?zv7U%J=dzksZgXV$-6&uP#+WbuOJ9mg9 zf%tb8^jGB3b089P>lFAB{{=nT>C^i+)>n7{dOpOc0xxo0GIOo*4Y9AG+j{_-KIp>? z;4cH5WCz>^{@4yQAjGyIwh=}>(<2S6V_lyQWSe!mqhCfhe>hP7Wm1wKPQ%j5Nr|C>HvP;g)^t>BrjC7DJ6YN8>(KmYic_3 z?faEd=H_VDQ+yQtk&&-ntW~>!1bj$H$-mnrrvKa8Rrw=l?&u40OV$;+TTk1EM0xzo z;vp?+PS!3(P0VXE!do2rkVtKl&v4?2xCt;vP%ARh!b&(u_2l!90h-QPcxuw!sUub8Fe6sU=1 zlb)##;fj5~Jk$DVs<3-*Zcf+KbO+$^;!Icgk#K8$dWdp97BAxNHcU_uuATazou6kr z9s&0o!hB5P;eD>{?pgDi8Z13Vry8$(d>VS(3wDYz{s}(i<34V}YnN{nIPaSsu9eo8 z+6<}2?t=$?C7V|t6SXAjH~j@W>ngFyt-E*AGUMV(NR|2?;-D2b&>)M6J{V z05HdTcqfy%;}-P7HY49Qf%;T+KQ<7naq280+h!PT z{Qk*>8QEEI83-^K8E^|v4=o$TRXk6%X;XvckGw$HDSQ*rdt$WQ zkB<&r{X^_Ug6fOfWc#1o93I1|oqsAC8~5N7-46($CPhZa1ZS3Up3^2Z(@ntsbUxM36AVTP{bw$p z_;I9&MTs(u?t&#}AW4$xe2;FtU%KAimWdc&mj~=Q>sOzA8fs%`?zkGhM}(iBhQn2J zlc=WV-1^*i{b9?$d88eMXCQ`{PoM50>PKLSCt?(_)Vd}nzWB=9qE&WKQ4!POe3-*Q zu&R9No<1gSyIIe%rDwjR+SCpRNE%ovax5y6GR%Dr2UQYFvW1+yk0{shG(pn+7O!}K zz(zzy)pn;c0uh6yq{Ym@`K9G$P%{~8)jhz_p$H&VHL(E0=L2sOYrl=lD#l_qN|XkLg>JcI zCa6XTcNpN%eao}M2zg%E4v<_DXgGpjNpbPn!Ozk-H&>OP8y2ausAXm@9uZ)tc^Mb5a#;6g8+05DP)Rd(>h3oFHc_s26$`J^>8VWatM>dv+d3o9Xka2@w;YDZy z&_EJL`gUE^HtcQ7AUmlO!TQTHgD~nSf4Gh2OVh3YU5s$g(+2*M3NeB^&~-^eukz1a zeJ%p}T7RTA0!$>#fBvDmyi-TTdSeR@509ntV*;^tzJsbt{$ky^EAD%nj8yli;TS86 zI8FDFxvtHtfr_p>Gf?>rTT#g2con}c01uyBYqv92RwqNawa7b{^>BIPIchoS)uwu? zpyMunMuLJ%6L}(+)!alb{JjyPi|QYTw7E~~-~Te>9EGsx7&~~Qo9+9qBqEDu=Oj7(PU9mvWtCN_f^Y^0#smq1o8%odDw_3pBpp%nme;Ql{6J4da- zeW`;oyYZixAAm)z-}>>EuvVfdJW-czK?(mYSxa>K1Z_lcFzrr83HQLb zbJtpQxwa1pF^L%MU6-);F4jPe^!Ml01{>_1Vav8TD=Q0ku9U$@CJi$~A`YXRV&K&! zDGy+#MlU*jg7`51{LJm-{ojad%KQHJ|I*+_@_*|X_wUOR|NDP(mHYqx&edL-_<^GJ zW(tPYW`4_S@5w4|#SV^I+;OFUxbrv5)94q+&1TB?RxGD0a^JW!K*92@3I{%W`ZHY9ZIjI#sqr7yqsK#Ky+a6aMM3Z~Q-U zYR2yQHB3UXF?U|(?A2ox>rU*jmbm_pz1}+(;Hz}5%Y$lgOfH)H)lIlt61(8zn~M$a z4JekPL@7|>YA0R26fL|76++DmA!Y*K&dbZosniU@0Il0?Wk(^Wb|Pnbq6RH^xhzZT zE&v0zwviUaO<9OJ%xqDL7AwX-Ed`k5WDWty^ou<80Gll*qUr(cyHZkkuyS!T`|$;F zwdml+_$h=4&D#*Z=;UeWn^@D}(GohmdodcZX_?76A8xSwr+Wp(epCSKUt->mH%IyC z*}pgUzIyWVWB9()wxe6%VB>pGU^{dS<&T5jrW#Iahebs6wVN9j)lX*GXR5*D9B#s5 zA2hBPf14Vdoy}tb1$lVV3-{gM3J1H$zIL@{o*dNg&Y$S>!qa{WMKN-%O>E7rl0eOxn}(C__7&8APh zIM|Q!EHxvw=#k%nw41-K{5`?%r2hIV{@v)_UFx6-G~`+vKHNgVdgA1j>(POMn^Fd= z8{+?3{k~F+{5hpja1bY)@8J<>M z9XfP8s>#$OD7T4UaCUZnerx-GT)9xulwG@jX9KeVfSJEx+`fNdfzUYNa3ch4v0*z`)&KT_1_VH75de~xwJlES^Y>K^SZ z_(B3Sa7UH_bMHieTreuin#dK>8(RDIhU*yNy;db*-rdCr3owg&cOOCw#6p;C!yga( zfZC)bl?hvG$!b+N=hG>vHOG5=H>VCQb<+7iyL9nlDa`M;v`0#qM!=n<@AuIzA(Exe z0>8r^6znT!nz+{wJ_-Qt%^F#Ewt-Lof8lmcM|O4u)rNj;TV@XPL3b2wl%vYK z618TBW0ueeOwVIr2b*KO5wAhHP)ng3^CN2JorKwi=mI79M6CS$7gN{2A6Fv&?{~Ki zJj#6Bifh8tG5qpNLDocNP-RQ$w!V#G=UBz?nj~TKPE6G?5-o$x8`*=g|C2>LtEeai ziN*hq`I%ZSaT&s^fd8^3y~_O|EF2Jr{PGGYfCNxv+?-Szs&nZ+D1h)4b0?Er_x|0S zT-@DFjo9_d;O(V%Cb#3GShN3Ha=hO|QdC+>N}zNeUQ`e8HyGImpfT=&3!^;S?H?xB z_jVN`%ZiDK!?1lDC8f{mA)ADsL|jKXO+|vOnd3NqVr8$5_~Ta z>mglO1^2*PzF7K^?raFxx1Wums=0x9CRwCec$V~_m<hTDdudoY7*S}#LtSV(G%rZ z4~Q7+^>zP?7h7Uioz+rwa>8V5Th-qG zRBgbr`w~(L%Ts%oi#14q7CbB{kRWPJpcsRvF?y99a=3QS^n+T8zyFKVhcOK}&!bI3 zLca6h7T|@q4uAT3RD7BIpsMhDKNti?&k>Gkb)bjQ4J8y`Q&9{``3&Arr0I zEuRn)5;(}5;(jhDa_sc}UOl}#Evbjm06$;r!AuYR^YqYvJIqHgkCBs9f4D~WV6^1Y zLCFWq^fhuD>oEpjyaV0OB!Uoj&iCNs`}gziOm^)gTv^oB+2Dt(dw`^8VC3o%Z%Pt6 zIy(3J_UK57RuAq9UU~JX|yz8KsW|;;Eli}pe)E-yxyTL1@ ze-`v?7d<^h?&8L`0|oI0d(ENyg5U9j|D{E71XfLja_S74&n;*3K#E}>_N3(@CEvN% zM4A&Zp!1V$ag<1RuS1_T$SnSds&Sa<_r;t0p*d~KF!k~FmLo{Z+}fYE;_L`FCncEl zH-069l_k;X5(a13YX}9DSt_a^!Bi=y>kA4t5mr<{w^-A3A12^lv*Mx$9u`9}>EJi) zO@_Ti*JsCGIojEQ55{|U{5*j{n6#Q41t6Fa7=;!@2yfDh)w8%~>ietP*@Ne}!mgCf zf8Cgjp7@X{C@S*VP2{sIx+7Q)c9fA2CQCPR?9ZpxM2KtVix5$7W6R$n;BWRH@p~ce zM7OazOHELB2xcgD4y+IZkJn3ATzc``x|B{E5>U1--)Dd?(JK&jiHFxtxCpvcd`T&A z$LR+Ftqg)nrRafr{shDusBF2}oEUc;5Zp}Ck>kn;D8Mr)J^hfZ1)R?b+YXc}(8x9G z!JY-2;4^1?zT+|r02qL&uO`P4SrJ}wEzODB&`MZlUYI{{s;Kcz^%UpKzT5TDT=Fi7 zL`Mzle@~j@N_ul)d@Xr~)KCqx-JxM((!M#6q;O1HSN9N>LQmi53C(eTRyg-Z!Z#iz z5FakDAtEQ9H(jFP9bLjc{GsHDg2Ej;s165TPzFwewS#%6?u5RyG%HYG+0?z!%;D?x zT`qER*Y3F-+_>a;_;ALNEl-&?KcsV{c<_+L-JMOAjP}3@Ho7yrWB{7j|4@_tu=fBi zczAO$c8!=sXsdM{B3_^y4Xw$) zF6IMD>FS)EIt5We=lqUva{6k2W_rj$R*Ddr3j4TManvFc3wc&nR>GlS^6#&_nVOwV zFZaqjTq}p_Zn}{qR~j8L)qxXG5iz6XWQ|P%41&c@AViG)eE8! z(NOXlH!nCLqSnG;*ZaxoqHm$vM30l4&rIi27Bbx1dp0kxy?1a>(aLJ(nt0KIY5Ni; z?PK~YiK1^_TnA#7M?K zFYZHI9r`_Mywmr9B$G~eb=)DvV>H0!Z-KZ+Gk{U@+|2#k76H5$8nc_4ut65Dp zA*EdU@qiTMHIEklG%UsRY(t!aqZ!;^>8=pZQe&a2aMc*)t0*?N_XJIb1p z1qHIpP_yucANtiH62hfZzGT_Im5Q_Wt8yGD%|}I#M@|;3_{Yh)mA6(t1`Im=Q`4Zb zy8gHXw?DVmM67<%4oau7OiLS+OpUqwU4m7FJ^b{&WXaJ#jwmT1JBpQgyvJXWs1V!^RD@q3!BBmI z;g2E)WMuz!kQVpu-A#Q?cDj^0Fj4z+1n}->5fK-#>+P+_)R|nDc8iILjV;VhPxnsb zwOjFxvs8z0--09*y}*OHIVwl-79a3G`v(SWYTdccV3;Y=DgxH5J7)IQLp67`9R{yG zS=y_I%Vx6NG1{j=*kd^^O7w22&dXMNXzEXn`Za*i2{!RsB$+0@@SWO-gJKM2&yIGd z+tq7->iF*Yl7mc>gyxuPine0Yhhl9dEfK!P z^R_-?RT;a2Z@ux2e0)qcYTCMBRo9VqhD){FT%s!O9>?Xb`s%dn|8`X}$%jQm!a)XY zOi<%ta$DV2aT~#OckU)N9%wJdSUF#R3@*iBfl+7!)=O~ffP85^4ngYVtP zI5>PZk^bS=U*fHZcYCvsKb~gy?n_{G8mJrwRx2a(kRQKaag6v1|NdB7Xi{d8yt$!B z#XkOM+t;-Ht2}(od}ZY(0`~Xr?Omqh5H|`r`l@O$^VE7GkO<<<{6-JkYF5+LI*h(Q z-U@PEY;TQfLNHind0_~m9s3RWYiy!jBIwnk2CLWZIv=&pgnIoBF}zZ7+MnG`quFWl7A}HPh!6{}V0MGBp+uO0wv1m%Qn6dY1u->y5R8}E zKVOv-yHf+7J{`4PBf@-lf_Eg%wF^0EOP9$5oDbAX>y+^ssO=yBV4uETx!vZ&P=8J} zm!ZI+ZX?~*h1Wo||NXH@pvm0P(W+pZOwy!Y-7Ql%iRpxTQZ)KmSHM+zL? zvD2^^^-oyMUJ@4;4x94c3Y(vrZ=pIvZp^M<<7+jE$l%Yo5+3SR0QN{kzZqWK%4a!8 zbh;Gg!^+>80SYVq>K))h|v}FEYB< z;ioKlurIIF(s@~zro{b;h~NenCMotk547_a+IZ1benI#?sPaEgIK_`J=KYdIG|M;q|`hg~`hcg-I3RpG6-nQ=f{pZ0)mM>)bvr zYV#5jIFr`=@Q#k^-@og^_+EH>TRaO2GIanFtv~bne1>WJqy0_NTNBhmL%ntmt}f52 z&6Cslcs3=dTADXIy`$9#N_D&k8uls4wK*Ea{X+WXzQ3|M-1}jnd(wDFK_lbNC2UKZ zJKg>Wk-EY)nIrZ)Go_5Jx?MW2=h>RZ!i;iZ>T4-AjcQ7AQa!WVYV74pg#|9e3v0v* z{A$joc3K=utInk>BZZMk-UHWgtmVURnvrK zDbv_fJ3W<|o?g!0YaxGjK{M<3M*5;4LJijCc1_qNNEYiQW>4wOk7}m}z@UblYNXz~ zx3neGn7(sj`~tpeqE2B~h&5({HYZ!Zl&8%Z0y!6hjf19!mo z&uaKM3aY9h21bX;77KgU@_rVm>2#wy$eApd__00de7j(hY0Pp$zuy4?{e`jXVYP*q z-k1qSUQgAPU}P;ZU9>y1AvYc`NHAdj{mE77XmsZ)N*K()`}?4&*aJhvGe4PjG$wFO&sVBo#V>$e*kE!L+dUSwa#fR=i}@hL9c zocYQckZRT$;L9LUqx$qDm4`^W;p}Ka$}R?muS?UDCvC?x!Ooa%OGtV8V$C@A(;00u z?#cW$K`6`yp<*+vi8QdZvg&J6(;g~%K$?~R@ZlCF*BoXY*WCNq&IE!{p1*u7nVXx+ z<(KU3+Nir6beNe1KOl{HiHDibZ*~yoTd%DKPhdN>{n5nLI!a>G!V?orxcHB|zoO8m zOuzT>aVA5IYqRo=MKDkH(6Qe{O>I~1@8Sct{gp&@K5b?`_Z@1ql(}M*g2+)Kltc9f zQ$2@qD^%di#y9&U%??6Tku`Yh=1tz4Q>S(3cn@x`{0hs2n*Kef0RS)!{B)s-c?j+WYHk2blW=eYK5j}5(9$;jw4b|c9Nx%DD*m-~r6D=}a9{J3R$@wW%U62F$D1@I3 zCgLp|U%sd4bW5!4UL z#uJzK&5gANVSf^j86*~iP>6=KdqBg*Eqi1J^o>fu&wpQYws>M@LiQpYRg=9t-KQL zI<&8Zm4!iUK=#kPQy3}5E8%-HM{cm+ycD^x4_s2ojm65h9p5uKh-a|jGQ$o{({V}3 z?letMc;uj>WmX860{8S6t~ey{ERML-xhIVlzbi=)g+(~OA)o%2-w)+vzQxHY(K#>E ziaXBf4kd<130rQykKm{LN``QMJdan%eQT^Hu{@^&5SQYNe&6-=#lrVCqaES(@rvm$ zhBNBlsvyn{)vlEYwk>I(GTJ9Tq$m-&kZaogcv9&Vwt1pFU6D! z2e-zXXfUqhT8wewZs%D6;yk^u>^B9GN=I_2R_9FRvqGJkfXB<8E>4TYA)OhCTiX`k z-Sf*ZTCa}Ynq7N!=Ic20GPA#q$KWdja4aSr@7=xImtp!eCV_oYZDcrY%6BqOs%>Av zxJ7R=T0E-2$i@h;3x`_r%iDL@(3!g}chRYTb`*e!EhCRUeYmE$v(`^ZVp%dz;>r~j zhoNx8ZB(45K1Yv5-x~eF4jzyfM9vNPgze(XL+ze>XF1B{nB8uw$@}^Eu%ZN7o~0+2 zlgPVHG{;U(RUSuW$&T-qIe7ZdP-KdT&B(6r5*ty5F^n?yGFzk{<$rC-?$U>-X{_C_ zaI%3_j$vcM2?`1y?az+WT@N3V! z{LH&@y^em`vSU6(G{$P}y#*x{cNNJNcTR@Cqo)H$@^qzId}Ak2cw8eoH@?UVsbtm- zdU|@t1!odwW*yB$kQR8vog{Qab>3e=NyJ`?MvITi1CbEqbg8%gjN6i~RhzltPnbsL z4-Kt4XPK>SyAPrSG%kQ2oJ`B)f+`<^?Lv}gH$G${wOM>(QqS%Nu15i}emyVF4@5Oo z{S`M)w(7sR5d2E2>uo* zxGj@*1kD0CkS5(}ophpzD9RMnjo`@V=jX4_eCGNF&sL+KRGB`V_nNgbjJu-|p`P0I zx^rpgUO5Wtz?hZYWSinxIRD(FWYI(yDR?IhYXuVrPuqh|3AcA8rFyAhQ+T($triDu zF4e7DLUYv!j2@X}zx*K)uO`YkwEe)pTe!Pk>PaKY8ainu-w-)*^_{$hg#hLr`e?hg zqn?g7i8w8Af^UazOlyMYPim*PC1CaT70!Q)N21Oi*D`o$+L>hWMb;8a;d@|lr_0J*d}pVrL+|qNm=(sL zXIoF++P0NVOuuDd;LEjn#RG-NbFc56 zpY0!bT+Qm@v@mwQHX;_;@aU(m@1AI@>&zG%tj`aBt*eXIDJ=N$j!8dTF??nd{r5)j zCq9jC^(0Ooii3PH8@3ms_g=f?<+Y}*HA~&zq=WchCW8+PGK##IojzN61(w4qS4`4?;7!lWe5@7? zwITY|D+N+ydG-(RdO^|Am#i97exC|uQ+CG(Xc^qJwDhO1_t#yuj?Ws1LiCN?ski5i zj68p4T!hzI?Avzg*k`)FE-qq~Uw>CxSlXIy_*lZmTO(77(ocr->C>l0q||c*%fsZBGhfb^l#I^KaHLF) zy*%sx9wJ4p^l!EtWLwBjI@KKn*rWKX@r{GQ4|zzDzrwZ9L&n{E3$hiGkPyP-keS78DX@ac zDGG_d;j^Ytm!t8RdGhB!a`p}m4LEtP;^H3r`5k3t^|&XwE{~FEeESQxg)Z&!>?c2< zP`1~;)?#aG_38HEjewIPBE545Vmeuxm%HU2TzbKsBJP|t9t#JR?;9B*Ng8Q_GZc>k z7|!0f!HM~R-naDnXMuseVSnv7{&Q@s+B<)`CzTmZwYW3Zbl;0`Nuy8Ipt?dGRU*z& zAy}2wGa2bsm&P{nlg_mEHvf0Ym~+?^os)SRpJY=oII$8u{Gio(h}U39;ZecrNnFwq zeCi`Uqg2`=NkU@s#RJ3e7M&Zd#^u9I&&!s2vOsE(C+wppUa z=SPkxsC6??D31TDwTmm#UGd&PA@8PV1(m_EYxin@`!lgLh|jU-+7=QeWg}X75)dZIef`>A$->jdeKKA4@)YTbzd5l28j~v&DUdUFA%o-RRI_cKc zA=T_M(sLHaP9Cf<0ya0!(hvobe=ot!cOgk$)=pt5Tca-f(j zljTn9E$?uHHe45=ElIdsEPD9xgUP~`Xk6?@V{@%|VMZgvcx)oB)#Ud2>M~MQi-e9p zLN0ij#RA=hk+u41EuCuEts2=zkb4ygP)`3>;$g{-{138i?nzHSKIk)Ec>h5so$}{X z)J=sxb!R0^gd^*g$DC6&ijB%rHLlwUzG&1={xP*;9Vd_y5*1a0`?{v4r;AfHL~K_X zE{1aI0Ydj>UfcX5N&mh@aZ>)(U$shZ_z--Y0g^Wmhsb^T= zuIEDCtgy#`{6#18{X*Z;S5^+c?|vx(o1Iy_JA+)6D(#fCc7@EO3DbytOEdFK&70w>b%J9oB=ii&N|4L$l7j?~u^r0QheU z<98AyxNeoLk8@ho7nob>Gw#L&Mm<+}ak`ml_pgbG;t;OL>)8`>Gc&U}PaPk?yok@~ zCnq)-8&G+zPd2y?I2g1j*VSGMuxI@X3k$U*CD)foF$2xia=Lu^E^z4LXOo55^9T0r ztNgK{h}!8yl%jmjZAmi!gCZqG$C-w4L{aKk4Sd}#f!jG}W@jBHmWSCjGT5*ea)k4E zt%nryey{(DOmUd(wA+?+JxJNyx4g5nbAf4TFS}IU!nkP#y5AB2xy!Rd1fv5tpfi`0 zxN_ZG4|bvif2$1dFg?#z1!u$UOo)+`n>*B(r!`q?&{doa?FV zvW>keZ+?7XfY}ci2&FeqD!;jK4b;*K_vKmBn0k;8KbkC`yj>S|HYi?kdSQV(!+cXk zeSShhLeltX{RJrNN&iSzL1*{1s;aMLny|9+fS8`oeyT_6_fZT(|L13QpFe->W|QuM zPQJNFLPL^m{BZo6r&D;=FJV<5TAx;<70uW%A)3hqiu(Zx{MMRRUw?ed|1;m2)4@S~ zc7|_wkk7=4>Y@^TwCYm51_moI&bU2tk%tc@T-}R{0}bnI*K>^oTvp~+It2_%OIaEI z%v7&Bj~Qya%+nrIbc~r;?34o!c5DV^XxPblH?f75ept!-#qxaq@;1g<3e0u;vFVE3 zI^SxcBW!HmJVSMF(1392WcePJCZDZqH&;PHTo5K20Ftq$nFslf$7r&8OTGAPMlK@e zJ=vWSFwkm9p?5NzH(Wuitx%HYc!3fkpTW5R+|XC7u-rqM|M@e-e}0{5DMS7JrK0`i zG<)`x9N$=UqhkEC=lG?lp0)Eq+qUMfUo9zo>o~t3L--gF&*K42NP880!M@(NGNhhY z(lw6}kjncjM}a%lJw5EN}`xCCED0>tYoxv8m+w>t*t6e8ITkZFcTpEM!gVovH&ZT(4a0qI_I@0?&)+%)4 zITbbauutyn))QJ0=YzTo@m*>nB?ExQOqX@!dj4S_9j>5IiNE~n<~9OQ!HNWihgZI? zWF|_-WYJr~Y7#LqN*hsiFY=~3`^sQI5vEtFZ7>^OizYlFiFZ}a!O2Ml>ORMcF` zEqU~KjJC1=)J4a-re8T>3eFA5`xQ64jL&Sm-^nQU6)r_}=^oL84RR}@f<8sT%Dr&= zcmvlDvGpYy|JwhKzgCntK0mzq0Ow@xQRo?5Y%=icZOkK0@e;w)y(|WyOka$$^aT!t zHYW=lc+N3eNzIeRaW#>Qt+%%-lu?T9cke?19%f^0rl0ws=UO@FttnT`xh1~yP{zQt zJ<0CB3nmtp4JyoG?onwqH|1t$=xXx6V`COmXD&6;Rp&Nem+g4${GRcakAp~j`;t@c z^sS1^n>(X;XKEQ7Zu>g(4|CrReD-2EY&{?db!aGM!Rsr7RaYsKhg$x-k6~f4%Yme- z;@dr~7fu&3Kd8^zG2}t<+kj7#eWJJOkJ{0xryOdhXVfP`2Kj$p1O= z#6LQE(RM-ZlF3-=39qTh?X)Law7X{nOej~osU^Q>ojcUz@#M7NTD*05u0ormZ;nr^ zubIjVqoXbdRVF~Gx$ZpI>m_q8`6#@Ae9GKGE;pSlh&&$6AP;bt(QbEIoWTFg$ccSo zR%o20r`ZFf>&S@{ReV%xPcrf5={}tp#{IuO*jLF0b6S&4V8KJ6wF}i4ym|V6{W^~x zcl_K>y356?|F!Kf_n}_;>^{zHS>2pU`!7te%mE2fv30#TVWz?u`S_4_*NTXY5aoGQL0`YCojr!GDIWEE^7Xo%iUW zih>;U>5CWREj2_!sHr)^#|N^@-iq7$282&UEYu@YR>8oh zzh$w@DTPZ2e~;1qtl@D#*lOXrT$O>rL6N@`7Vp(zS~U9@Sw4%3QovwMD^iaE-~a>I z{SZoQrzNFq^FdC=*BNgcm{g8e*oVo)Sum>X2Lk07PTR0CJ5O!GWbz353m{w%A#1AW zBo=T$@Cva>Cgf2#9OdT^LNS3pq3=JZF5A2ECZUG`wqk`gATJAk+*k*8odPj4Gcy96 zNCrLa1#a4cw1Qpv)kCewj?;jM@WLPw2*Ej~8Z05kzayOi!N&brfFk5hTMcYwcA47^ zuJ~K9Xu=eZRXqQl+;A25Er1E~u*ql;evJB%yJqdqLDkY%Ou_sbiW(Bk?JIE0O}CjF znts@kP!(Z`9F}^$On*z!s^{DDq5d*hn(E!lqkZr`J`!Pk31&Av3L7+7uR!XwjeGF7 zQ(k|({pb4~J9h%%i%t`dnQYGvfQwL+9J9-FJfmkZG4&UDK0kc>_BaVNLVX+SlN)!s z3&aW3l>~>GQzOG06mY9wjYq*{V7vbD2(nB(NK{z`AWQ9&bQJ?v7KF+ew8TMpEjU_% zdBBzNXgt??cnQN>-pE?9*&2S_y0mN;(?I#N#HH7h9)kSaU{;_Y!}3evxC6Nj9d}^H zl}X_cm|@Vah%^lD#wnAQr(R5sUu|p2aP4!gP`9&zXcq7@ylF4?JmC(6S<>WaP8;*EW2)(>~U?;PdFwRKD-3V)O^ES{qJFl`uX=TeTM(T z6@a>eCM`u+6B#3;4+h&GJa_;vsZfG7fTLI+E$%SaAV(ZW66|Bbh~PQ5Rs~xdXlMzDZb*uqmQpl+yF!&=xp19AA1fiARwCxmVr04DJg^9Bd;v!m7KRneo z7d=4hlEhHs+4JWzK+XStk{|DB@R#bGbX281;0_P_@Dv5TJ zctc2|rEyIONKZhmfT7)I`j;w-$Z|7Vw;)TIjOIlVw@j$^Rx5Ta5+&6Ma)Ld(j5v1^lD?qU_$;Eyg z#IY|%*8<2EcQ;DzVPdL-C6W2cVig{cH=K)rE#-|;nI{9Ku6X*l0QgT7ukP%8A;;wK z5FD&XEGuDfi781Xg68(M(RB^zBi^f}L~A9&9};sU>xmALyd|B%;bA#fR|zNsz8Gb; z^+o9ay}xmt%iY5xO(O~Xq&>+B)QBVs_zIS`w(ru3*v4{>q%0ZPZl@Bya` zt4`!ZymRZ54`i5~=d4WRr2%gdwkS$YdU?p13xIV^gn$o3EYm+!l$4V+1T@>39nP7< zWes+iwrMEDFNAPbBGvOzkpdRND9}V`J`GBDS`Jk}9Tlpn;OX*!R)L_gcQm~SD3GPK zb$aCH2#&HJ;EP!Si-n4Kd?ELGGSgv#Z_Z*zP7sp@{M9~OWsi1QxZdSFtB4O=jY(e_ zF#LnxxARezX5DUjeLl#3c`$Sz#C;%=`oDL(j`jm~66Baq>S0qjFLv7fVIcyM%=)$M z+ds2pkP+cxIV8+D1O{8#s?A3)JTJn- z#j54mqhd%2H_y~q#;{KqX$K=fB7xhE3;pp0WO(WF<);P=I0tBHKxumwv?s7hy!D~YW=kk}c%7FlP=*#E0`G&z9<=KJhDy1a`S~{>IzV+~vA*g6 z$$}idIJ;&RCkdRz;rsv{<2hWtNjR%wWw1^nw)0-XB3diIIq;n?gmU|#nt)MjdaFtE zx?FTa%C%kGJ)@&f($o1-NrKwEn^QgYK43FK=?;H|vj~lhcE!;71NW&&eStRrJAh~= zm$?S1c94_oI~#J{WD&`xr>CEDs7;tJ+XWKGXjP=ywGZJvSDI*+V1R;fgYD~ID_FQX zLx~lH-iY6I)jryBl>M~L2vKpnE&9;NfmmRhvZIu3Z+}&&7&hun+}7gDC`t!QoPw1J zQ3TS1D*q&QONC=J2+I(~KZXw;m^WuUx6_WrZ%w}Xeh*Qx0GLE)zng*KjwXL5RvL&j z%(fpJb6djbLJCL#UfsszQ!)&+@d1heB3%D?^Ks{oijE$BM*wdEF|8n^f4@7e=6zK?jx>zJ~<5->>@Dvb%(vdjBI=XNyZ2RfE zImdpQ1@=y!@k*jVDg@r1CEcE6tU2Y`$Wyqgzi7M$%MGi(FWd4ufM4bDyuf28-2Cu4 zP|dXs{u9zwf_Ze95C?20I?lt-CHAiOMf@7Lx3Gv9!c`u`G$OivZEfxS<=D-LU<0sx zvt0q)iIwp}(ScNAYg$+a{2nHrqs=Mx*qy|RjpweOi)q+Vs*9S8__$?1Q*@NDya;hb z(nT4G&F!9&lETG<`VqE2ZQ7(THuEN+wj_;fcYc^Zz|@XN1bCyA>-hz(nC*nYX$xS+ z8-#FBn9KP7K5{eGVK8TXEvfK@I?RwvK^hH0Qrk^KLyRI@VKfZM0T(0_9jJV=GC#r! zM_`iwd}@j&pGL7i46^= zkPv25fWxfuEN76sX3OYxh)BugIC9viIeCX#61^sZ40yC=qU2|A#W4;St}T6tD4`6C zO4yPU3xo8Gsm}q&a{&PX!zfeLlT@?JI-gYD>_hd$i7^v81~}lJLl8g`U`2m|aED2i z90pi`t#1Jk#sLCQf=W)2;M$;E$K8zqSpGDpMKex1N`%c@P&S!##ob1drZBp$27KQEjaswg&p#mK;k~ z4%Nh-cJo4X(}Czi3H#W!6(Va49Loup%vbrRpCM+&1aVs2DFguE9$H!=9)Tg=Lv)we zV_6-5kDwEQf=d#M12sCq>_i*046DV~Y;$Im{;zC)HHu9Yk&bod(ZdTtv-l^T38r}# zuz-f|^TjJy_5gD%{R`V0Szv^S5f$;L*lZ|OC=gpw%bSq3gQAGX_!TA&t#~(3WiswG zxFf-K%)PH6KDdk-JpyTCW23O+92*?Vi9vKT=Q^#JH3aqhKzY^7N30?We{8W-w++{Y zvDQ*_EDC4Nkia+KCFlZK9S68tz<1h=H^o!0gU=#C+QJh?(`be4+U|%gf30@-2=l0@&Fs1luBjFaVz@x; zAv_K!Ad#KGotYsj6*PZ}o&?lm71UAVo>Z9={+XZ_{{2~%Mq%fHGW(?clr9EeqyKcL zR$8Ft^Ub}bUElWl-(UUbe>?sk_TD?L=YQ`X49M_f6`~7-9$K!Edj}4^h z{^wT{_y3#kaOd*V6ukK7-@AKPH6Tyhf@a=;MT^LJ|NgQJ_$~1L=XaVaSE{VITOoM2 z_`@Tu%F_P;;MeZ{6e^LPTsKxX|dtvz+H`f zm!3WEVakj?`NZQ(xx#U@5&!r~u`$)lp7gquYdh~IuEgD=d2z31T+5eMVFg|D%DJAt zGPF$l4Vvyk$twl`qR{n$7Srj#VGfhVkKKs2`45QE)kEx+rsI~@;7s<@kdit_FhgnE^ZyofPbJU2dw6tqXw0^L<2Pc?Und`G^!|i-S z`T7o+pzyD+6z0LZHwV0X;#2&6y|ohOx+m=)G728?P%N*$;t?fZ`q`;StaaeW97<= z3Z92}3-N`xyp~`-lV38~@O-gP?_w0i#pnmAJXfUxtj45Wb5J8tBF&HTw`Ho@1)5ZddUpn%>qBTvRG9SFUVRpk+IOI2)Z7 zCtoTgy@IRm31K~EdG3)~){sJA;VR!9RdI^Mvxynpxq#EQ@W_Q`9y_*%$vyjiSIOIR z3A<6~zp)JxMF$jTTgFmwK6&3w+lhA}AeIQj;}QL6R^TnQ@aX|_mBAUQ2M9tR zL)I73p$fgb6YDa6je;myj~zRRe;;^Ji1b?4;Sm{55ssS=9?<-n{86T;AYs7z1H%Ly zq_ajSAzn>lx5~ab<6q`F0;Mm$%6#>*HMVFLlJXRWIGIK4C`ZaKW zdTy3qQ1n8Jim%u)RkKVu_ep}GT(`zpa}-0deTpdCKsQlqhPaGU5oKMrp9bs)3PjK+ z&~Agsk36@J)TPfwR^;-ZI4xeXxFGxlota)~;Bidp{P8V95R$zf;G{&+S7U}d5Vw6N zn0_X4`_+)SjStj%JB%^HfTe9m3*227jiGT%`ZLr@5WXBnLZ`vEw{%i->x&57X$gIT znWwX_vbw{wbK7P2xy+@JPY;lqF9^rR111hb8B{2!+|U^bK(dOgkW&Cq`+qH4O-1!m z{UQ^>v$!QBnxhA%<8(;>3DN{{bB7Uxv*Op3K5law=FzQ+Z%)6y6yN=TQ`l>2hy3R+ zm;3&4GO{PV*K{1c=_U{$NNvJe-l-*|65HLSRaCMf_Y?o8=YDo6C#Wy-_%%{X*Qu*# zO#Z%UxUVY5^#IF5zhF8x$m$>+boz*i{i-)KOeSm3L_CyLc}r zKSaei*6zVKQvb1n=pM-jcEIW2x`{zKkm!&nNNhj#l<;bh*W>pPLKEA!3J&R8wsuJ! zg+95*WEwg3C!SXWf(-mM!KxW|aeCZWA@*V^Vk5jW=YP?0&}I7zw|2T^-@24wxIHUS zR5vN>@B8M&4HczWA&N*X%>)*|MfG}`TYI>S`4r3^Sco*@jM9ozZK*X(2-r|>xFF_Q;PuCl{JTrr44{Z)qVcSGR?kQ#k=2XNmb?rHCembwHp4qo5CaDo{ z5r7qUYQ}RvzNXMCNKI@+rIlyd_5Ap;zW~}nkjcEII+C=fD(RrPLFecKyGdL$#aQIH zYh{Md4;V>@WI996t2{B@v)`yC5D-bt{B)?vW8+h&)?i$DIQ|ZJJwoC@DzX|<4y?6A zXBgDv0`_m;vBR%Qzg)FC^6&Rt7-OoIUBh1}s8SSvk#8~M!=)%n_xhVcL!)o&-)Ibm zl{XE5Ii2}Y>ahECYffF%4lJCpufIai`)9iKS{dN?9Cm*Wd3yLb`#Kwyl;ndQ-Pd$N zT~rbc^BM{{nIyIUvapC7?`ynsxI$7^TH0Ob9d5I)U^DKD+J8B~Zg~Ux`pVd|T*&Ft zQIQb`0*<4Ml+Av#%1{WnyL+<4S@p17Y=egL}4*vfjsr{II3L!gP0}()f}S52o#LxiZCpC@~%;7Ay-Txzap|D?Y>~1N>!z{5DakRkzYW`oP(7> z0AEam%J(nXw@43=?-_s`Y#FGb0Cee9wjZ}mL4|sp8nyh$d$s1O!HQ^-HhngL&$0P8U4<8499_=cLxGdyfT)XR0_iwl9hxiY@>p8so zE>zWl6!C-aYA`E{4AP+h^h@AOGE}WzyBoxdxLB_Udidqn!~GZ$7pT|TlJU`?kpZ)Z zR9yc)`A3JYFuCjWDhM(Q$0xO}57E-A9x*Q9pO8)S(RaJmGs+gwt5oK3uBF_oEr458 z^mzd%E5wzlI`kmoO8rGVfFHt$MQ*FhB4^3L$+FPmR-aM^+d zDSQ7+@SeAKxc$8iM{X~yL5M5%AXA_r z$ODh9GBPH;bghIMc#MMkcm|U0$s<(ML>gnKRw2puiOH2^$v>2uDFuYg&XtGrPevv6FJvvHVEx{U3-x;$ zJn%r%bXPXAkEDpFZDpqUbbDY(aII6yV}1UqL+)y9D>9QSLY^;}da2EG1<9W7Y`ZgC z8Fo@JKXc-#PW;`w2Uib$34Z?aW%}a6w!om6`Hmn&L{qMFxlsl1WJa48>K}ri82|$P z&{YC2_T4*oR=s?gU_bN3FG4MiR&~Z~)F>vS7|r1@go}$1%>S%4GjG0Wl5H_L&|E?+ z0oZhr?i+nyTJLAdC`@x}rG%{Fp6{(^W1BxX2;QQUDCvvw4y(PjPV~S(6*_kd zr9Q)PDJnv_!0WE{&@4_nv2HQp;@bC0p=X+m1^APTo@BIBJfnon|&nM*`O|E8nph)_3y9Xe>ufG|9PAL;WMdDLUR=_fH<9?(LcIx+w z%`}l+-5GLpiRROB2E&~`T1=+Nh#qK;d#|(yXswqP`LRB(AEEjs(gDP)TuoZbF8Y|5 z((%0lwdh^K!nFe9Y5(z0=HlROW7>!pN>7M$s2T`?0P*2qos&veS6AHC%B`?`A>Zyg zITWy-vWOB6JC+*?YvA9o@<49gHQbhqy2J}t20zdj5GCw(^R+;MSFs*2{$8{rgdm>3 z@qkfIKzeLPU7t%6@MVq)H~a6Ks;h2kTI^^{S4Rh#76wnyN)YIJcoNn>zuf}?7!R}% z!lEa*vdQR^e95I3RQBd?xy#%x)Lf`2k+T&l^m`!SLYSD|sZO1fdP>qgvGac7Y4%m8 zOs(Iw9gfZNP*VkoG;eBssuQ*VVD6uRcBNnn8HnM>doMi#t5pTG?=-hB4+`p0P`A+d z8j>GfZI}m^3A_0#88ejooxqder2r!`#MeIn2M6FQ9DF=f3q(H*ro6v=*%GD*#mZjw zbbpA~>w~5eeoYJyzkp62Zl03CUuz~^f4ufXn3Wiw8X0njYm!ixq0*lH(MvYu&LJh_ z2Y&jkI|z^Bfwu=F9g)WcJ9INQ_Mv6|J~UJTrUjs#KJI(0k$rZ9s^C>m1C}#!2Fktx zN>5mWQosvPppoerDc}qT6Y3TmM_pb3kP7*;lwmNm?4_4noJKJtKqpi#>!ayy; z5NRT2AsO;zv+(XGM6uMFrH5-5*K{RN^fbQP(I6*8T9847P;q&HjaMN$fK3ClC*8?( zIFGy|Cr+&9_N77@0Mr$9oe*v?varxngCjM!uy~250B74xqI`LE+~X_acL9>iq45K@ z`~pnQV;&U-Fy@4RM^{UrWAsJeqE!6w7lAG1;jI9xjt@%GVj^(^E2$a2|5jNUAe|Px z62ckq3IS3y1#}5aZAn5pbClA%tVX`~z^Dn}Nn->;Q~FNA7LdP64G- zCzv-noM?Qq^40Nspm{Je;Ex=D z0_cD717^WttX*u~Z;{I0NKC`D2putbELG!LYax)Oz{r7k;ol+>ABdeXsbnX@P4-%w zQV^3gwlhgAfw|@xt}`MFCYTH<+CWOS6~`|^SA~Z|5?GOIHJsYB4g~D zR>OxrX53t@=dKE>M0x!{Es2_s>^0E5z(4rCB_^4`vAqDlhp9u)e@*noYvocx3bXbT zBo3dj(E*^)W9UODXb1wdL%{&^+ztxjH6PAiWikMD10;YU`RLn0C228H66F`s?4Ug+ zR3@|qP+Dls<`h)|Mu)(N69XJ{#(OIfH+%z!$#9*HH^jl3;K45?8b51Z%+^4H1PB7C z-Ht$Wg^!ht!JRZ&d{{$u$8Q}cH(|e6po6s9bBw2*;w zPVd;dmBi^#$Onm^yfZmq-!qWpGR0}Q)v^bUu69g5L*lXr`jNB)Xx;`hX9-ON-Wwyb z0&s|&iPJs~QG!!5-w|RY2DmiP0Hd2cNYdX7eF5*HG?EeN(J5kSm|Mh!2y8{(0XC|| z+_VKa6k`-pb`6OpMt&N=G43<)oYK?lmYbh=O14{}=Y8x;H%4_$jGrR%7QW2sn}7gD z=Ef>IdV2KzST}Awzq5NMj$gP-6`|Hc2hv?Pmj>$UEu^Rfs3pjhd_ni8SauO3-{}Of(4V1Ps~}prfrMIO@dll4 zaXL*4A`GCcYF2+mBZUXT1k@6MIZR+pRDedrz^|{n+dV01DukD9slcAQfK%rMa>B%a z^@fuFlHCVEKs~6&7q~HMo#eBKt_l*ATIV;561ai*_TNk*BztNq1d*!`;b;vG4o0I| z-?a>5SLY)-7M|jKfe3&|Ly3~25ogU&R3RYH5}m+%r6rh=%cC=^$7@$z_oeq?J!%|h z`=*5}MyI^C{=7DnL4eGYzB|T-bG=FQP@`D}Mu-eu{n#bf+23E<$0ZZzl8cElyo(JJ z@^l-{1x>IX7)2__LFkPRcob$8nr$*#yQIz1hn56D12fhnM%2kc-xf2o^6}D=arHzl zH`%j1)aT%7?PCxR3N)QF3cs14x5$fv!nSX4p&4xNkIK+HQL)YwH3+0mbJg%a{YJ_< zW~shw?eTJ8=~qkIxq{_aWper|-Bax0fs0fuep8)s(#IWQ%gchE=bwfC4cIO&u4l>` zj-Eq)d%H4chxE&SRs(Z&$N*QIGPZHoxxa7%Bf{6BY}ZZ2bLWU}63Z9wrvt8AIb73cZj&Ko$7%HX# zSU8~~Zo`$}B`53&;1qhtM7kv9lCrXlA5t|_5VaT|P=uIJMQiqF6s{~7f({OHlZJm$ z%kFeGgQle)MUA*pFrygX_b%Sn*lVBEP7hl4$?PW*woK<$kYs(Tk9XtjhGM836^#Pu zY%-0vz^i`oQ@-(mcuWaJ<%xxyjXDgRF;Q>=s@C;WAzCN|pTdhaQ1cvb?we3n(=v!4 z+K1#7*f1^vQVNvVZf`WCFjQ7*aZV8XFXM-i{u1xe^n>hRB4VIr`;KWq;E8tOQU;P~ z-0-nz(4h%sAhckx0c!azssIaxXzZp=ojghlXfEBnaH@ful(0LWJt+^H@yPgiSnmam zV0~TPk?hVQTr+`x0y_SLUNmPS*Q3qn4k!0}bv5khmq%x9=cYT>*@{4gqlvH#gSr=z z;48kw9(G2OM&cKidw-vuY4(6CR#1-Xe;(FKtiugw@HLqZGRyYnkq#iG#M8%Gtmd2P_h+SiEEB1cTsk=K`6To zt|HV@#MksjKIjz4b5xV5gXb;DXS;P#3BqN#-_yPb&f}IObG1+e+B9{oqo$?BpBE)X z9x^vTTu~BbEY##OxLaT9&-Y>8HRdtiL4ooCFB`S*p#Sni&|+hMtVK9nlH-E+g9lfT zuD(|-^_#^~O2mw}V)i5p^asMk+DvMg~2IA!cbx|f895+ zu0~PFd~lcwH(D`FHv$OB#rW_e#>VfX7BPr| zQHFDi&~~@V&;o$AS1uxmy#1Vhg4d-Ti``By*e6Lc8K+k#2o zbprFLL^g;yCCCsW z03p2c7cVkFx1fXG8;uwfI3abXk>f;r&{xvf7_jB2`xjI`(C-$3bw<7i-^tUb!*F;4 zmKys^udsgo`cKeck$GA8Jj5ryDY{8#JwMG&`ZP2+m>rLhuj1%=Lo}jTC4v}iO3^q0 zAqy$C$xmA9jekpe6u=B=LUaMMNaN(`MXbGtettxh2mdiY*%OZa0;zUG^1$}7S9{Wi zGD89JA|kEA)HbM<1dpL~&LbGGl&d5N9qZFK_Eanaz3o6T$_?3)8IWRx*^)3eadB~Y zF>iG6l0`hKq2F}lO~_A^(uFR%f?+v3*f&BgLf!KtV=}ehU&?5>m}2QBVA>=m9Z(bX z00)LnptwcOQ^3_d(jC%kJy)!nY@EQ%+%=(x=lON|YC>po`5&C+l3{^^TM)*)gf6H@ zxcK-g_-S$vIv&VI6%s!(t+V0*=nxezEhcFvr*sPiKi+&mulrFSuk9$d$judFA}Nh2 z&_YWuE_k!#-pSDOKxAF{TaI&O_OnrO?$w3~c~MG{#2Xl@-YlArqRV(B5;8NLZ-qY6 z;a+sos;YrJrU{ol5t%251*K&t#g+j(n;Reu%Ln?wF2%jAi)WF&T*V)4ApvY}AB&Lj zRm3NK7*LKiyU~`b9^V&w=-jzSbQ~?&{*v=nD#jIq&52X5K0(>#iHIl93d#9z3|8Xa zMiVwdk=2GUV&+{1@jz$u{W_0JXoyE&hA?@+__CaZP?i-Xwh)W~3l+a(#5#qam30Rs zHq&t#SB-XD@}1@wQ;;f}t5UGs!uND6C96)|l~L*e*AE}BwC4RJj1LAh5`&Pb8hgE5 zEfpT`Qv(T}Cxo7zMqRZ$ z*0I)9yCU$V>mmXMrt2m)ZQOWlYBtphc3^F7mpG$YE*2qEZQ!}T0=hq2v^Ex-#QJ`l%#*Z4C*+ga5ps~%p|lJU+3wkrLQgB@cRKLJTpwAiDM z;EM4(aVE0|(kfFRqqTJq-u)hx@&OY)#z-)b+hw2plAYR*)0V_3Aw219q^-&Htq_mp zUf93CznUa50EHk;kSB@$_P8|dLvTTWzt!VQS>qjF@^bn@819ycewrG|#pC2PA24uO zy#JCftF(Fa>VM*QKj_f@7X^-g zKoe)&Gsicm4gJW03Q#{u{zpof)#IIKAC1DwrLBxty$?*&wjK2`mmEWJs_$ZFy2>bq zhm9f`mGe|;Ue4cypIeR5Ky}|TWNe|Q|5ded)v8sBg8PYeq~Xo7mX8Kp>cPS2-x>s( z-{_XWTytzVD?-trb?vt13#(R>PY<}n)7zuUy= zI!?DEdUqlLPU=nNsv?u_T$`fYyqW(YNii_0F@{RGj5SpHD_$p}t*+G8w&q<~?pu}6 zs$_?BmNUqVTDQ|0PJEDu>{TX&qJ+#JG9pM%P*i27a=L~=)n?0O%MKGczgGJ!rb76G zBPW;r*O$&>Te%je?-~NbQ8DxEYKfl008hq3^jPk-r9Z3E zmUzLtT#r+4;l~a8gR`UNZp_ok(_(MxlKq>0IM)`OA2vOP`4SJ*7FUef>c(j*(~jBc zjy2yX;Sx2YL~}ySO7+@WHSeGKm+1jq7#i|H7=z;o;oyjR_n^Iu|)V$x@XHfrPlWDXm3Rs_x0xvq~GW{G=)$PukEyHF; z0D-;h*KJDCmye^d@NEexgn-Uiq-O(tQF(b{BOeKxQ1;B9huZN{iuTHitl{NLgtEUd z`j{O^x{H%LIWGfU4ouYbNp^f@8S@90(T3_Q_V)aXjxxoz69%x8i@swT_bFIMTEK%C zbQJ=TdlgW&g5S1R(+KDqJjI{qzSaHl;PTV**zG<3u?DQcUI zL0TKVxX6JO>(9TTffyp*w(L3UVC`9y0Bc#k>67(z z`QyV=PxinG)98_G zLd6Xo^(!?Wd%$#nZfYifNwXs~6-p+Dxse@jFZ7M3CMeHhNiVHtbbJyR@|gml-+m}# zYPvtZVj4KbOxEJ86cSoDp}KXMy3@UMDY**&3tBanspt~an>gNHRkkgx$Jbx0{oGTj zb(B+Y&c&Vc`q7^jyLyK&wat8LW1bxM4)G{0C#%iAY#wgL@mjO9_Vk=-O{_k@>Fl=} zStJ1=F&U7@k(o7QnFXcU(zR@X0mX>)X$++y73e^U%8R>!bnaEjeDF+A?@Jg)L1ko+ z$i!Bs`L&p!A%OJM2d`zHeu;9lBfT|XN2=8{R|cE-028j1=cd1>UqPU22(8gpT|s5I zagP$sbpW+6doZAymdAei!B{o(voD790-%PWFsX`mc5EE^cmBa*%yDx_UjYNBTOUpX1j0p>uY~pz=ePnb&!n z{~^xcXJZ#C|9^1dP6GP0^xWyl9N5Zx;3z)inK zpHaU&Snm560`1^Fao0MNCeIEy52u1&v$xa-gAQ(-8z;mapAr~~tQ4pCXJF+U2cL8} zfM<-eAJI0fd4CdxE%vR$k32>~66!`O1&TO72UF8<02>`@FObVW*E;h?qXqlNSD{oz z^@x7 zrI^ckAhbte(l^ek`@g#F2?7*U@NLog1p4)70?wky1vqASYPT=9!kc47j}p^c={Ih? zBDUC7@|4h^ElyT6^&<5!@^W0q%0m;$25h_Rqd~~h?=lJ950&xy0z)0TO!hRMuxp+y zP;+B=mMpMX_5kV#Sd2tcQXVi%JVyqCpyQ-52Lh5?uFaY>UW==atc4@<&6(oXkf|N= zofC9iJJr+wf?~iUAT2E_1}kv1Jb7f}#Gc|b3>c`3t>qVa#w5I;h9w>&`g3Tu-9v;( z;((Sfk-~`TZ%%Z{y=I;qlQog@4>>FLGJ~&$U`bZi%i^>wd3_bl%Fx5#cFh}Qml=NI z>nYpo$>|pF%c^$D`_;LVjA_3HTBg}V%kRz$nIHekdV&4u(kB1+{|E>D79}TzX{HHF z-ceTOjuVz#wPFQ-Wpdyuo!ohY^X0iur;55|k8za6H43Mvu2M+asqGkKBw%lOb~!`t zr`uF3ZfOUXJK38Vq|FoP=kKot+QnpKLOZ2C8+ujMb}BpZK@2rgU=vClT%AQrLInr3 z)r_tQevEPs{>NGavN|y8y|=FAu7Hu^B{rKP-kNxMx|ZHEO}tCs(7#`~Wg3xp`|5nR zEiO)a_&mTYNw>Q--Gi8ds$sytcXu@bJ-p;B+5d(^{t+DP;~FIPPP4Dw*nG+JQy^#Ju9Ku-Pr0$v&*s5M&qFjPDqW z`l-sodZb%r3x3IIq+9SZK>x)F3x@&%UYW5afcJi$pPJHanzkdyJIs2v7vD1szNnu! zdxqE`ON}X0$R%m*gzbJ!aehi38TvZZc892TMN&k((wff00H^nz-rH)tm+to?m|={> zoXMdK;Tt5o6gzDvl2LFDw7d0$V2Eh$pOgvHPwWZqU?B>tQA^tYEZR|2ctRg9f>27_ zzC{89UP#tC4P+pKlOZ2JR`kH)u2rG0?7Qprcb-i@NY97^%t;W zK#Z!j@q#P~>wwrM7tjmnsmH&m<0eYLgfuMUdns^+xuU9ErpZJZuUCGh`0+aXsf?-K zrWB9d;H5RND8JaS6n3x%td8V-Cp~YN+IN_!UV(31Ox7M~#dHskBF@uq{51rSIvL-1 z?){Q3VVCB_*EqH7zU%e}f>-!J+pISsqGP0}}aY^idP2+t4j3=!LR3dRco=H@_)Z#oPK@7i> zL#+jP&&ElUHJ+1zuZnzEta{|SlYk!@HiSkXQ>=+fm+y1WP$)hB9cQ>0-+3$*anX35VN)YlU#a}w7)@a*Ip z`n{N?`P0oU&Zwsxf<~yWh`nWac=f5rJGE+Ud7yS{f1<3;9oEpL-;Bs5?mdFOj2dh? zn2WfK%r~6pbiR#ZQrep%H>}9lp!&o1aQR^C{%DN*;!JI_S_eU3J4_n3CXhsu;2*>x zDu$b?0c&L1XU{+6#VM0^u6IuYz9S~q$SVcd;11_t0djf?155Z90kh0|ksj1XHLm$w64ZZpmH1>V zBbo8Pb}xQZNeMS38t4G4%ZKQNVXwa#z>`LGl_({hud_p}o-7CUquge8*JAn?IyCXK zMu1Sxeqz|vp|(+H2n=OB!un&Zzy7aKS-DoQOH&dKLI3$Vel z!b&XgUMZ4?fu3IHqd{{qp}YFqz1-J1mf6QD^N|tFUUIeIaet#?s3GbnSQ#1ht$#9U zr2|iLAK5Wj0u=B;z9$O{;EoS@YuXX}$ZVD+l=#J_sn{lkhZ6hIXFi61as5%Gd#Kn4t7PC-opg zDCKp5E`2D`QC^597c!AFI>d#K)Q5+#;Lj23pqT9qY{<0QT6$JiBgBD6e-Ru6Joo_U zEHn9c9_*=z^Qx%If6^Bs;GEh#!M%Mub6Xqx13dqv5+Rq4OS9M31kcE12^1s6B`FWd zf1Z_Q5B9ygb3UHE!1w?fd$0ZEwH#wVXnuh&t9#LBmJc7`sD|Ew0JDZSbmWvRu(Ywt z%f%36CpD;dhygaS7Fg&tEv+k8!hHMr-7Jv60L&~!%@kbIvSjOBRSa1^lIu9ckM%F* zpc0vWS`(!5p`Z&BnGO-ctF6tJ>kjdp!l!of)T!O$)b=vL>cOz19dwV`1^quvw=h2z z4ccSs-nVyZ0UZV2jh-7bD`!U?foz?-dSzTA!*uC$n71`^=uUyu)Ja=G(@w|Cd@}xxT*Uc^M<$D{8keSsAtm%WQAkVlYChe#u&O`=wa6Ik&aJ60f?;} zk`a82xD4vv(&15jXl#Wqwky~X17-DLWwtdD&IQ_NZejDZM4&m_JIKt#<2;W`#7xh%L5bhOv5S!_2m^`9Xna9{8p2(=iNj) z+y4}=>YRoIB@$FwkmutQJXmKf2a|!1zx~s@!?Yx!wb8KVZ8?GoAs5;#I97Y4-?PQ9 z6~ZjpAD<2t@XxvREoE1T9fgz?eJd3?BXCfDu&g#j@R%P-L$x&RWNyF*0YTRXhcUfJ z(R;U8BCml~hVzYLcsbb275tbbk=9cOQjJN#$v=uSm!Ak~Lf1)kqSo25HGKubV7E<{ zGONg!JSBpQ@2-mBUo)GVO9KUN!CkB(mSx5ETj~y*?)IObmL-2k93UW+R;V=5^#e6P zOw{D&|ad$CS>iph|Lp39FH5!+cIy>*tKju+q@nY1bntLtg&C*;S!z0nxi~ zYOdnwEgGv>+unK4_qJPgPnWWKTm9i%Y>H-eoIk4a@|cB{l?%+bKv97#I9Qx2<{ZY% zt*pA*WH_`s-=0kr%5sr~&<4Ok2Q(Bo#4bP^*5W)Q(dcKWY{sR*X&}6NT95x3ud9G{ zLe{nBrB{P`?!z|B$Jrsi^(HJUh!4)D!$f*hm6l;T zuDWD{_~rvnpI0ZJV{++>ifd||;HZgKJ36u<&tWtooJqv+vD(O2Xk{S9*Kd23hkUc^ zh(mdZS{eJv;b_6Kw~9%;)sf2j)$g8Q*LZP?5;S?f#iwN5GDOOEu^zORqr z@<5!R@$seoJJWOaw{sU#V@!(fDp{%V=;6cfb*hE$F6 zuizW2Z76aoMz(Cwo ztwshl=_0=)FFjBN6?7kgT>y!Na=023`KqH^E+-n=l+jTLH~u+YX7rOQ^{-whH+M~B ziTgyKh@XGqFPpyA>%3oleiHE{WuK!g+VK9wo4Z_G7iRR8qAzBT@uh!y^tyyeuv-qH z2`7E?<;-oS1zrPA!C8?mWw2q7aum_7oE%c<1SOT_7+5kOX`Rx;fg(o%VhOabm<>?; zEkcDc`gDwHX$%uO0!+g$NYuE27hTT1?F@VjX+5n~0&e;XBxA?Xpr) zG%Yz9Q=asN)vRtb1+tBfxz11B{XIpC?O<%{flU+DC>xXgpT6O|K-{uT+J6ohe6%E+ z%*lz3FJro0$m6B57`qhm!e;Qozp!()`y?Rw+HJRSyW z4+a-mq{OTA=eV6m$+6Q_8PTt=p*ckTB4ZpA6K-7pVf;scKC%7 zCBUKj6F)E1&K>EFA-P*Z+w%mR-Lyl6X+LCEP4)h6PY@&d=H;BSH=OWcmF) z>1*&W_|b`UX79{`BZk1`EyWU?+%lrN6-lRUk$hQKpwIefT16S3R|?vqpfXhIk2k~~ z`?9mEhn^FdFYgrLVxgkBgb5##$M1guGM3f>uT{Kt(FSBPC;&bJej@x$A3{Omzi&%w zCEUBpVCTsp-$##5Vb1`s3RRn(OipveErf7~S)cSdao#X>&H7Q@(qJKKyaxyykD#ks z!UXjTyc^(F2Mp@e{x6)@40rRUS$uqxM{CJDVu6yj|brt)?4Br`~J_L9VOC4Gi1QoACJp4VV!*HFhY2hWYM_P~{No3^7A;hfImoikDbjNA>?2mA{~Ovy zki<-XK^$i-$PT4rWG#6blE{y)AIT}FD^_tNw{<{iVw#)ML)lCqedd<%J{s~=f5q3+}k8wJmZACq{2L< zUq}~>8gH3divIwd#Cvb6bYON}zkYv!P8}F8GfV}6bU~Cunlz7WO{L5B{oc|gcwb1l z$e3!8RX61z=6=h8%ZNTaiXVcM$`_Dvk#q=>`}Mz##9D~BX_9yUiRoJslY>(6@V!Ud z>yourK;@-f0q>7t_46Ge*X}=wRN8Ppb^OWAC@~aUkYU_{=HW5shJEkrD}mc7-fU5F zI*9KR1p<#SsdNkWMA)0KSQu-_%YlO!lo8V*EQhy>;Y3`u-=N_!QaCiaYbA)q>im0$EG8Y#d!OKZc%?`%znM_SftI9mOO1w$p zVuqfO2P2O**n1F;HzA5JM?$wP*Nvk`jmQ8pq9uWr?;sm{87_=N1D6wKMhbE&zSTm} zbqXQ7ahKn}_f#!GmTylTfBs*Gh5Ofw!LR>czticpvyN9K^)a->qVE5CkiS2Xg=(ZPm?+sY z+zNJn3T5WO1M_{%?%AKIhAI=~jBuO(D9@ zFrm0brEe<&QiIy<3{3h2cLjV=Fdk5Dm;mUehv`3DNn$S&VA6yp_bP5$+&mwp|sfKMB~l?HZ!oWxI290p>jz|kDQ#KSi|9O~VR z!qHn|;UGwXl7115xi)Pc%)t>M!901|HpcCpMf7tU#;BEy+7A5VLxEs=C?#}FVpGm5 z6I^iQ)B-tmKKfkVAMEn?gT~4=vn$wSoO1lQ3iP_Cp6o4pRuwMdx{$78g=XM?h7>7) zp`e;5f1w|;1@TF%u1&rGut#Qf1xq+yg;vnF#We)+`d6FZZO2n5z0^N45zJrvi)$Wo zm8Rj8MxjQ4ap+%&ez1U&2Cjn4FDV262M$_?R2w?Fw!{t!><)tBsKh4$J%tX3yjAizlaS@MwJZm=!Du4T#fQXjA~r^(KjGbWDJJek5%j4j!hCHb zKMjWV6xGF>Lnu+lepZR97*26_Qy_xsEyKRt85HBo;{0 ze1@a9y}vyNd?Yg*`^{-K;U}x!z(S4GC3VE|q%1CQK1=S4sYNx8O4~;suWqq4Hev@Fb$Xs@OWYb52>+W^J>3M zg}A9(vyS60G+JFS0F|Wn0hV}xJob*)|KUr#i>!yGMWzzl#jw!?TAr)lV(AFhSF)0N zddt2?6pIw#pj)EN9r15SX9?U74{>m>RcDpP@XT-TFZtG(G!Pl)K3?4~82t)1EI6=9 z6*pDEi^CO_Fe`zGzlBbJ)1C`RZ>l1sl%G1wDc}xNtCO81YWLJ}ApA^OgH^gUbN;RB z>opjQM0il4Nad=x7F>8JGGsNl&f6eiXKA7oK3o^ zHNZ?;Oip?QzYxhpMiODjqwTWl@fZJLf5*z0TuPdW`C@ambr~!+41AY+>#P-l(&wQy z@)w_3VQFdQFqm>!e4_JEn!zJ3uG)4?*e&o1f+jT49;`ct#&@IR1sz)7E-d>8Vp`JS zW9&PHYU*REPd>Raz$nB2XrQ`mVPC?H+Ncd@4vNADxs`X|WJ@4XFsKC+Gye0uQR(pF zt$Xt(j09{NetTcU8oHQy;}fD&t^2>-Lmb>_O`{o!I)ZKWCeAGin8&yTM)uORu>-^Z zoLK$-Y>=+3A6nHzdfi>if0iLXgBk4LfHua;g3s>6hd~ket7{{W=A9ZZZ>n)z_l*~P z6DeKxYkGQkysE++(;(DD?b=Bw^-xO&8s`6EJYew)pSUHGFB(*~c{?oz!B;%UhnDk}B#R69L-zz31GG zHZEx-c*%mIVjAy{hl}S=y63;hyu#LgFYtI?+5^?Ff85E+l4ADbWfbuEcYBQLV`Br_ zW4AkZ?&t_7-Ns9STp!^xVh4PDeUG}RXX=nx;$P!GO7IC!yG{;@?Y$pqvSK=;VT{|NuA`tio%uF8XNv{p5L!uyW=ZDtZlN3I5XHOot-Ovh@5+@q zA*n$Rua9Ln~~A`)zDE~Mn+~8yR(y~uCCkT#~b5oB#$@b_g%eYuC|YO7wyrStOItJ2cbn7F}; zYTVcTUDOBX1-S8E6EoDSTIAr=Wxmrkc?N7ZR3#~zN!WJp0n@tc8m`Tt{Camm$}`RLBtY+k(_VW@FP z(?t+CGjfK(IMef6v>$`e_t^7=hO?2?Pp+-+b$I;v@r#ci&o`)U|LgFl{I#rKvNSZ% z2%gG~c>-SZm26M>>UCK>YstMjAK8>@Lyhm|!>Kg!l~Z!bRQfBa67w8Kv<-AF*jMLW z#%%TdMm03Yj~`#1%6wZ9)achdk1y3#xp#cIwYp0$@$cKWoT9MH^5ewlk*LcI2kj>J z0+RIreUAsf9NtYN8)s-Fnt5?|fA88XrT+f@^^8*#I}`%R!w8;rCPrhe$QS(e7NQi=s+Njk0ESV zMP6$wCjxx(;1R&!Ngadf1jS}41ud;Dkc?Bke!1l-ugZe-9P1+fE_il^Dyf;jAozdk zP7EO}_Ml{^CmYR()*f*OR7y%pOv0vYSN1*I|3v+!>#yfhf`atO6LN!0zQtwM#31?d z?nZt1h5%|Z~oqz%qv)_hzGra;UfEw9Xmg;efxIu zP>@mPYLIvN+QeW}%B@(HB!1)RyTaYA_9`kWB-#%g--tK#;vam zdBnxpq~?ZKG$fgMBQjcY!eAcUJRNv?WQMpOJSGu59A;l%^#k7&fOwz8`~yKd^70JC zhU^H%tYOjHyLT@Q91zEma`??q=(L<1aT0;tzbAt2MKWrLUmBW<@0AZfV_w=({5K8} zmk_eAM6&W{nBeF*IA~GSlBm{J=PCA5oEzaeoUJ|h>|t7DPX;J-N>)}&N9WezTa<{C zJznbD!W17Lp9>G_8GH@KcTmkPMhNA% zYzlqFz!YQ(1f)u zIV*YNgki6?`;cmU4Ub(QZbjPLGVOPka@}(MfdODA4YyzZcff}@)VKN3yFc- zjTNh2;Te4$UlSw6nWYqMEU%}>jctv<&hl+P-4CpC5?-CxB${u?kqk*in?sf24;nNsF;5`>{9<$~6;Pv(K^IL@nO-)S=FFQ5rNqSeE3*%`KTfKX5D6Y<$pJc@X z(!6rzHXIf&P&5L(-_(dnBg7sIL$*xcmZoBk9pWr{!}v+0kwDwLi~2Fo(munV1%Da320Wl4=DQ2(Z-umiiG|mEGo}Upb2LZ4L-lNpW zs!80#XD?n{jWQ)lsbqhI}K){2zFM7Q8z4j9HD;KH=sbtCf#+oX|| zl_e)5NSUK(=xAtYzW4U77Qfcp3*4mu2-g>ME!IOV>`=t-+`ISo>(|?Vev1IFZw`+L z7r;#bK)JXD5%g<>nu2T-O32jG$$6-$d7b2uAXp7j89`vS-neTwZ{AFS!!-8e$76Wv zAX#sJ{(KA$(^u%%;q4?TN@vcWKR+1X)6>%-wK%g5&bT~h8ylPBh&PE=&$tiE>3*{o zHp>*%Chzuk?LaA44k-SQ!56%Y0>Qm_EXQ!Xhx{6{JA8PVOMN<(m%Kax*R;A|mGk#K zmt8s&v679pBJZ}6P-T1s)pC035hHV28XMh<=36)EsMLJkxlZs3!(;9Fi92si!Va-E zN>$u=e`IY-3&S0oTF#uLdsN-i7hlBq?&7%eM6-F~&fOB;!!d8TV-KADG?cj}>D>L{ zXqB~0KNk|RlsPwkm+})FG4N{`es6SlcAZD1$x)gdMGyWgE0YH!`;SIYyifP{8h2WI zO-Sf)z10e=0thPke^y7WV)VLl7^mjq)+s1MpgEcTcwIW58Og1b-BD zXJn66ltWHAO$(;) z3Oi=mH*8przKJ+IA*zN9o`M1?)~iXWg60vk<)~dfbJlLs%yACer< zULbszL~THIjErll5w=~kJWoPGykU#ng4+SUWFpaj;OS|B0cCiEcMFK$wO7~SnX`7} z@$9F;A*#GGN1Vjv7qMdon>}zGZAC?3Iygwg-j%P7YQrZ_aczAU-DxR;=>vEv9<0Kq z-=Vrjo$MYIq?VF15YJ#7qme~RL0na^b)JRj%--JqQ(@t1Pzg8h-J?Q&z+FUM!BVsi zMVK4pv#2p(6m^84cD1Oe==#5wH71$KK)6p5AmG?u4e|FO1jmzY1e|b{{Wq0^csJM# zM@VhE$R`!08Gxb(m3=5~FCt`0})0mM9s>|YB6(X9cMdo77n3mCvS#)fMns} zEg<>!J^%}G(S|NvU0p2{WW6;B0ZM1%-)jauj?B;YS^FhELf~>E%d4oU4Jx-jSj?$& zxb$kQ;!G5{N0*-R~&JRq?`;zc$tXDEv zL;wl!b&ZW4wfVXd*B9>@0TDOEj_$r!JExmv0^Wchbx2&{CEHSL?h)8W?q%&FGxq?7 z0~hlsfsp9JbmPmdx(;xQO!tO*6WbqIP6#-M6r%t^9VER0c@8V^TCRgJ#YvA*;9Lx2 zAzcSwzj>pL&km>V=l7T5am3$+D}Y$}u@bS-aji1A%Cckh4o*Jv z=Ap}|8K0OCu=xc2)qgnTPX-INPx*F^1K2=w5!s%C7R8)C? zKB%cl6?8!;%CD>SA6_dV5CnQ3*U(dXH-I>esM))9R=TD=X;?6MmWXn3a_k z9o9O$%uwa>siPDWW@6h}q?8VsoR?jxq7$MN*mhbrz}%AWC+Grg5{ zOg3^hbO9G`D$+UFH|A}m?RFjTJxw`&)%d|yn_Yvi8Sf3COt5($^-^h3WTPNiv{{E? zzkn&?3szbjKQ#s5ADpAf*CyW8DLo@=6`47x+n3{lZja5kw_b~c14^9cVPV^PfBeY) z@?|}a2MD`1jCES3Tr8YR>zlp5apOi(7{w+f$;7B<>`t#Zx2d+l+sf9qrXl8oPIq_r zn*^YMODka_FAiwqX-;fV9?*fp02a55jOrlL+H-9&Y?8Y}65 z0koZoQI{Y&7$Bn_zPa!`GgH#o*f^~0OI6h^G@uZnow<1NE((g5&`Q*O(53D!5AuMX zG8Bz!X$3Ymu6&$U=i5933Yaktq0DpE4lUXqr{dPqQW`fmH~Z<~m;1-hqklOQaXvO7 z!40o;Kh5iNR9Io+PSa=ly3e4XDRE8&@cQcQ+g`J&moH-1A3yH8N#u3Ydz#N%+?O3v zmZ5wd%yvpb#F{gJPl^eRrr?d7e?TSOGrQY5oQ9yupZ)ncYa`HZIUyupjzbi)H~)`i_}1{vut_od~} z?lKJ*pNS`0`+3&Ko4!Eb`qIYps)@;39M~8rfq4UrZ#xq#buBR|>x`=E0}Mye%DGK- zIR@a~i??raM^gb7ID(Vz1(2dEMH>ii1LMn19-d9L6Lz&zn|AHo36i>oJ|^_+@!AGm zY*hUA%W5Ut(qL;^v(Ip$Ax)|dw>2s0u+jYfdNwXWpJmj!N=0ykU))6rU2@^kE0Xg3qO5k zw&@D*+1+Q5vc~OI4l7%=rZ|keA zh-!*Z@1C|fk+DW&h3dk!n<1noXnkXx-0TrTX|yVbiE6k?4J4KI$@BV2C9L}R*uz6M zWh}J68Y%t;qU6mdjC7`DlQsVk>zGB@bg$%VwKwmT`#Yc}+3lpAiF*aQ->-TAgts68 z6px@?jw?mg3rC?A#!&gggtl?bO60kE-n_XSh0)QMXTGC5`g$QF77?nJx&IGuZvu|x z+U^fOr6Of0V+zd~LL?*-LX)ISnTH1RP-Lda5TaHYN)aj{Av2k$N7E7!)>39sH5@_?!bP9J6#E+n>6NVGAypY9m~L;)!*NLKvRyZPM`LR;ZpsxE6`rlF&E@6Gpbmhx4>N}^YaC--hA0~VEe5mS zg}l?!ge|`{$R9HG7af;-6Dm1kWgHZk#v4W}U6p4Nx~i z15@|@W=wjS!I5HSy&EOdyVu)=oWi9!cI@hJJO}8EBJJwc2Qkhm4`Tmk zjD3TH*9n@|-p&P`>j7mbP{&Dp1U_>2+a%A01`D z3f%sb7ngrMtx5p%0T2;`p5ISfM2MojA$={at!01^2fK+cyp&|201`(O7bApG%eZPBm>C%_qmX}qg@!_bgtI`5 zvx#OYrq)n^M^Q+Q55QejZS4RobQEx1XiS>Z&ofxJ<&bWfG|BjMTIuKL5XC@+0~XQ% z-4{-4;N3u#u?;$_m7JWMDDk$S!^OUahDa7y6f7MXL58Q4oLMvs42ai|f?1t{-F0+# z;=fvEq5vCt@}&;}Lq>*%6maRxEG*zbIoSkti!p)D(A+!#TC>vTX0102G&Bc5x#N7b zZhvtNmE3`34I%=^fyN?}Ur-=jT2|Ke<%=JFmKhrf4&3J4i#wJsT?)BMAU=gG1kN*~ zQ$Nmvd1hp1$A@A-Dw+!aganZ0gB>B2H4iq9_jmELHeU=pk|w-y6_*2Dm&t-=eTLQ+ z!lN-52J-oX1)<(K-`56Ns53pDmK=F{-ak-yxt0SjRhL->&rV(iL*q^7 zxDHo_6GDf+A{B^Pi7PM zW*|(w&gN~$_{wXWxQtB>u%bm?GdXbFNdmI0t80df7%VKl6^Rkg?)VB!7_UNhg^{IY z5XO(FNl2yY3i@HCV!%1@y{`WLsqCiLfN%;?ZctxWDspEvV2s{paoB=LBZ?|4 z7Ga4C{h1TDp8Z0I5pwLkFu*SsxL3C%nn`qkuQ{6_@-qfJ;U64ad1I42SXsem3x{XM za(48F&OKW~A=u5PAT*c3u!@Xk*QIaRfg=#yK{vs%+-Jl@Ip~ayjZ+ccZ5O2vR6E1^ z69+yDzMX=h;e*EW9^hn zs~HhQbm$`3G2;;(Z@Dhs7Ab)w&?N^96P%r$ot>Al8hS7WNn4!xHEkA}W(6}dUPKoL zW8Ms*JJ97g0m+Uz!E;%uY^NXI6srKb7!045!=YJ+L0Wd!8n{-3$UrZH`lJM_*7nO= z&WO8r3HY;1SlIcQmlZ@(NV%a#7SvMso)oYuk={g*JOclBO9IW`6kcsN)~5`=rL2Jg zH^i@sP^sib@&Y~~3-N<_=2R{|^aC|MI4Kz+L{xZj_~*k&VW(2%hdYFK?JAG@lwY5x zygn`Umk4dv3fjFZa_`6~36{0%>}}?_eZ9=_f)O6jkhyP$x|mWgK>4uiMo-8~xAL>$ zosm(ErYBChN|>~A}bNT8wR6%}15u8gm!B&Zp~XVd-D^6ax={@D0ntGJE&t;>6>cJ5EO1a_x8H;kt#V5*!#UCQj9V z9h}LSb8fr!BkGai%+9I=%PDhg3Zpqn3JN8VgaHyDsGuQm+xak;SzVOMbd$%dwIh1Ed&_gjvuon4*^q1h4nSj3NJSiR@KdJpyW_m4_^ z_;A@Hj_Vn}d~Sz@0Ugn50o$V{koM1SrC#~E1)BV!G8N#>k52N^$*|$1bjvf`^fqcA zgObt?vj~6xC)!5juKeTmB;fyaZDKv~$xA0Y-2G4nmwbG+hD+eCNng;qmo+uK;XQ+& zxZWNQ9+|KX87`Wk=Lu>F>$fF2=8Kzv>OH|CoNB=+X#%fNUKfrNBPKbKCp05(!z| zql@RU3z%ivG^cXaw0*J#4U;^PY0+rfyUGjEQQz2q&CiUO)^EUfH2u=R8n}Ok%$DrF z;5j*WG@-$Kr~ddlDQ!`Ji>)Yvz=UCNb#S(uYcakTJ{$XP7iJU(m#Yfnkgr2<{#!fT zX+l_Qy86C)tts={G*U^IY;~w8{rT8u;6&Qa6w;VuE>HHS6dUf*8EO*wvvd0U?$Mcn z_GB`Qn?6%JIa4s<{3jc=3py(M7+${}MtK4!-)el}Qv6mp^vI|}HNH@d84Lo-c$;2T zR9m&YJtxLN@X*4F`JrLWX-Cbz)3tMTHyA;DS5@tvMv*)^KeL6rCGqwndy6A5eCL&C za#!x`-e2qAH9(nrQ)s|d2m{-fR1KPaetd?4D8d>L_TDKDFL%(*OE!?89tREVb7QzJ#OXZv+K zfOLqV)9uoY+t2BCBlIWZ%{6O`byQW?ST9@l04o~ebnQWLn-wvq#|XZ2Wh2d0Z+HzJ z3Q_FXBLiN`AsF5>Jw6zSiMNJt`s>k4Fo#z$6?*dGgYJ=82mrqK8LsJmx`j#k_Rv#Fd)_K>5k}QO6L@PbF3549GH||9VHW93#X?oFy)UHIm3r` z4r%rcXK${(IFzE3O^=Z=WuWUuYsC2tPsP)RdCcvBo^zf@>B;ynvd3?_^4C7s&x;n> zhf6wd)mYzrwCGm2G{fp1JMeem!S&imBj+?_6?0!x$Ec=O#-ca$_H7#TOl`(xvRXwR z_sNfKl9+m|@fK5}+x&T%UXpHX`fvJ!0Zps}lU*O|L)w`j+AqnG~1`S$p!FXItQg{D%(@ z#()7W%}F!43ae+bmYkedUA&_W#=3Pj>tg&ysY|o0?pY^zczM@^vn`XQ zSxL_deZY>PviF4x&rR|@<|Jbv#%WIVaL?_z6BQ*m?pCb{JSD5~{K=$-?q8>~q5^_1 zB5)8}>JFoKeoPf(y^048TYl_SIYkHfh=$#=CFWrLdPD z0P2Vo3cIIKDNQi6LoJW|I^q>^EPo1Be)wBv+LfQFsvTvEZzwo66 z#o01Pam`VuQ1|GN1(|c8eC2Qw_IcAw zCondhnfZxr?^l-O80DWoe_m0NW)RYU;U8sFuv}AITsT-My602Lu&mxmFRTx?BS$7AJv9~cWcSH=$=|p&_NA^V+or=e?WV9VJ*V$Xef^>1N0w3!Djqx#ryA$u zM?KQ-3VO+S%6}3qNJitguYJByaCHMl0^h^j%0E3{P2+W$jG?LG_Qd>m&mh$}rNEh# z`1r{$cC`7)d)(54gXy79;nG>fC-oLIg%Y$tM1S&BslZrTO1i7J;W&S7Ny!q{OpCxO z5x`vFQ!7B`d!zNV`29dkO%t-)IRDsoZ%rK`wBDO0N~n_0@}0ah=MiNkJs^dzs}TNH zuRH?33!y=D6bveO(3smd)JIQ6C0{OG$0a#q2h9$alak?y2hG)*+i%GN^rFm9kj=$) zs7UqP!_O%0hk^7q~Ys%Ndji_ofM$WN?gkF_*hgBz$UXN+PsnJouj#1^+Wrg z@YoMGvKYAk@@ZU7^Jg`x>Y#>Xsi1ZHSW!XuwYtt-qg|zj7W#?NIo~HQ;~7e(Md>&>%#-&)ON3er z#kLpF6bc)gZa>QZ~MUb^D9 ze1}UH$~#MjH;ao?*VlV`zEwY06zxFHqBrJuOOCAFEh5%9ZL{uc{QdjufLz1x=ag?_!WSOx{C1$qs- z4%3xn47?W}D7&Vvk%72p#2dI>K?P%`7hykQ9m20Ec=WwNZucL?5N?sSuq}oPXeCp}{n9c@}L$Ax? zWnGMM`>p<$y)3MS!0b{jfBm@dO&6sCt6Lrg*A$O`i8y~@MO48|=fKCcP;wcNClk>Q ze>@xFj*H_lI6*~uWK^N>vZ|mz&6QQ;W3)onYvfA#f(R!F?}NMd~& z?=#&3FR*=W1sAEYr@0!JWQs(M&rw}Ywvr||58CIKhg8CS>r1Mrcc{!=K>!ry{n8^1 zxa{_;TQ?(<^bWecbG)kLvrnGNTjmSreD_66Rl|=)U3M0@=!WE2)}6XqMoY98R#r?^ zUFVX1+%~B_@wF{sR=40}=*MA^&`*u&_o#(lqrr*v{Iwj~1x0Z7BOgnblgEHaJDt?* zQ@$2;RH;w6VKYs9ne?kEb8TXxX;Qsus?|3Q^6RMSzxzcdtq0V9-6z`J7f4H-^(Pmc zXEy~WUhkEZ?E0QNHf3K@N*f(S_*e3pj^_zILWBa2rDd7Eaqew{sk_J*ru*8|WG=pY zciD%K{iSI=J9c@LF8kh|xr%LewtTZ~!ij)q;LiL~Y;wc%U3VutsE4IpTd9_~G{dw@ z)%w1)n>#L)()R+sTd(S%y?4vMuq~KtpD(6#81`oK-}vK?rKe7v;t5rxp)Wjb6eEc} z@Z;&zwhj$kmnO)VuN8zm*RlLimJ4z8|=l;z>$`@{E2)rF|ZW|VrMH8|6Rh2Jq! z@I$oV7`5d;?+-+%9htbU`AlCH!3oTHv=Wk%1lGY-ti*Q$Xioi#mv>~KbBo}4v$H-^ zSDhX{d}x}?5W*=Bh*;~idRy74*EOFwJ3R8&j;SEqU~w-h2Ihvbv>FMgg3aRZCVy-p}=^ zuC-kK>)8!^U)xkfWvR@}B)^n#M`^ft?+<12HpkKiWTD{!~!tBGY>D`QrE4LGh_6``dy?{%q;TQ({t5LU5vOl9c4gcO2vse{hH9AdW0xS#DptLm#?aOuFkI|V|4s#cozykt?`d?IqU zE_vc}ZE^jbhth;w#k8$mWARx#ahh#S#ITC=3hAW)=vbOByT?v7Nko|WY zTt;WDt^*$K#DKjU)n`yJmcq;c3&T=&4vy|f8x5i}DZbdw^uDR7%4VNqHww9_PVZ|7 zKP@h%c+QSeTes01_$Fpmm!xTvR%{0L%O`j#IHao`zs7ESd%1?2&9CFqoG$|XN*XGb zuaePO=I%Zh@3HXCq!n6?=8qpuzBLM<9Z~!us7JsC;W#C-d{GilZXN>SK<5t@0Bxe7 ziOH5{RUa}|K#;xT%yXG9AEoW57)jIkH2IrhNvY4SJ$rmmF}}UGpRQ=kbAn3+Uj^N= zEs!G~)uj3dvQ+kijdT}sH{h%HzDb=FB2(wH!*Sj!8U+G$vKo{p4b9JXQ-( zj4MHu=`Pz&3XDDklpQ=``R&N#Y(I*$mL5l(pq9_^t9_tBbcWNHhKM6x4m++sKgIvA!OVacDlp z;G4N}eZ5%m$5(h>hl=BJHCADxbE@Bb3?qLqnz(XiIHa7-z=OA)|9M(kNm#OuuwgNU zcvi^UYZ`d01W91|F^>+0DaPz1=&%qqXHxDzY4`Ih3*us^^6_NWvI=;i^D=J71FN+_ z4fL}@eIY4gugQx2_hqri}vRk2Fnue86on`rHMWm!d>V@9yi2d#ySymms_^2k; zjmbi~hR>mtxqn}p=mpQSv88>2PJ5`P?IxaSYrTB4pNgTc&am&kw9{j1REfe^6rK*z z@{iBFQ?;I+mr>V8EbK&NRB%YVvNu}ObG3Ju%QDuza9*`R%85e0O+2bHAtAQ)V%AsV z&mT=b>pJoET5Y&n@F$Eh0teh&Xtf@RHrJIS#RIDnkeNI6_1mK-MjBMa#eoY=eMi?f zC0vkSQ^IpLpMPIGMwkbAl`a)Et)n=7*gX|);Bf_QB(h$-aqqg)eOB4+klH}CV)bRv z(2bJ(XwE8+;uMd8<{m9<5B*NSC};>d5Y#oE*aEZBblZg(n!H z_)eXwHz%uw_E%ws6sCUpK${?sL<%%osqiG+h>_nGefcj4!JjWKcE&EnyUC#lr$00zy*-@y;7!Vtp8mmKcpeS_#eqkV?1+9 z;6fYNu7D{cCD`#tUhUWo1xmdrKyI21!m%cXFPA!m2XzI{4xPVq3;)z@EvC?3YR zYpq7vXe4)3I@sGsX&uu@P?PZJmUz*US(e&`5<6sZp4Xq-2E1(YnS#yd0PsR@iA7X$ zK9Kcjnb1MOaCs#`qEym1J!vS*NX|^|`56PZ*?Ogs!FtXDmqMQ$YKHQAK>3W{G^c?C zRs3?CQ`oaz7iV%n>mKw+e%!}P%lz*2e@fc1|DFU<7pM##xeq-G8f=UgDgocR=+pV@ zqu{d~$P{P}W3d?I{66~0u|p;;eqc;QObl9O&Y`P*kbs}!c*aZs9bJG(gpezja zt2b~TyXU`r72Bfs4TDM9CGFXd&)IQ++AV{!W!D3o#G@5oP9!x@F4OPdFX8-^;Z39r zRO5Dtu3{%J1GwUe;f~cIfHisz3_2&1iA$`vV z`+KPP`T5b1_z#AFrkBS#Zwt^6mIVF(dXFx@g+9Yz=yR%2r#hoO7LX);Lk+d(6-+%nf@Qnw+Ah<7nPQ%if|D<6A|uL0Ln z8N}s-F^7P@+X8d^Ado>v#sT%RH|%maUQw@6!^#6m`vE9tKYm=ksidSi$BwF-t32l6 zLv}1QOv>E%$E`m)zxHjPd--RJ^7~)oUQy@A+jk_0Nn08?q-6#9_$+UHs^_PkwBQX* zacAchDsuW2PR(9~mwPoWC+CO6vzzo*Q;6`>?dQ*vA9f{Of$z+CsBOXfm|$7c(Qs0q zzDfUoKY*uy5)(;ZjZP<+V{&wnv(4E#w>i~riII>#w$HAU6e?Q{(dE{yE7tET~wQp7?AmI-|S%R*VFOZ?sv^qQ}6F~Wfgt&XsR!g zWoP5RQM~Zo+DIq>Ej|JRH!Sqiay zi^{ftytgXLKlri5@BDuzS${96|2+Xcp3?olwhYV1)gSuZ?Tj-+?pq8rqic9rh)(^^ z>-n&R;F;s(>Z?>OYF2cBEQpFSe}W5L=YM#4v3O~=*Ln2F(yfIMgc-r0u!fOsDi zD)9&4)2(n)OVz`$PlE3vs|CyhGs?VnY zr98k}0xnYoj|$xxkx~FAB2GepW48qL1%A!U^gw@tE{EYn`Z;B|Kb_xuR1=YL!H|C6 zOW*HC5F<34gt-SUNSvT5pvE-WrCmg!3k*Oqr}6ykuUhq|?o5y3FrHCs5@T0>Jd8nwF z*#PJV;OCejp!C8v51|v9IbOXN+D8r@I^g0mEhF}0(JHNac2N(|fS8)K$OWr^;d~MM zzga~40a~A>&~f*76v?2~utn&v87+C})o&7C3zFPHVV0C%V?wRJS(#XZ@mx@#tp#~m z_<@Uhah2PM&|iacq2VN?HY9j}IOgE~^?`Tbu|EtAO3*eMRR*owg~zZJdwrVyz)849 zi8Xo!*Lub8=(!bfHV}0hwp@HJ$m31megeqE*0ct5+)xy3=H*>!p2-121>u>YL@mFE z=L=&pXc75JMto3;+uAk|yCnYae>9!$I{TXW!1G!(xM+O#+2V+W0;3cjG1!Q_0s{jh z!b3y14N+6jhmsEti7Li1g9z`!fL!*9iYzTQ)Hu?RdH)@zpRpFfP+UyhvhsrGmaJ@R43_KNFT=JX-6ChLv zS%@T*&CuK8rouAjv@kbmWNj^|AAaPEt5y4py_g{OqrA)S(YMg*-qh681Mn}@J=5Da z>Ui|y4>BSJ@pX11ow8sH4kW0>Dur+Xp5F2Khs=yB_#gPHyD&DgY5EiCrtsK?>%`kf zKjeGO&%;AztU|y?H|m6pDdwK45I-nxaae-5y#jGPa)Q@GXqT0hMRamclaniYe$LMf z2R2QdeeE&)CQ>}5%_bq<-H$@Z5(CdU78t0Mup0r1#Nv6qCKP*3f98k?_lYwM5auL? zK?J@UHe!sSrYDwW_}L!u3VjChAz|OoNDR(I?Fnl=M08UaXL2(-nt=i>SZRpNJPq9L zVAhSNM!JZP67WY(6v!ZA0fYog4iCUC<8qoHcHyP?xDdp!j0FS+jzV)qY%rK1n6CA} z{f~?cQCNXiKxxjt-%SF+7T(}Q2yTG+tv)#8F=SC18RDc;!K|Tj5Z-lQ;C8S|;BqRCzYX4>Ko6os?Ho(taSxQMkaCpUK!Rt#`)mjt%0AxWpxw-am! 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 lPu=Q4tYK~_G-}mbF|%>Zmc65-+!{;w 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�?s_J7|SCjE)Hb+m4|?W;n(y$ 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�kd(iHEGCQ_?qn?X{x1#{BRv8;R4`PaG5b*r+e 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�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 z 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 diff --git a/docs/images/re_use_log/logAnalyticsList.png b/docs/images/re_use_log/logAnalyticsList.png deleted file mode 100644 index 6dcf4640bc03f2808f206adde737acfeb144e606..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90986 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

@@ -396,4 +396,4 @@ After deployment: - [Required Roles & Scopes](./required_roles_scopes_resources.md) - [Parameter Guide](./parameter_guide.md) - includes model deployment configuration -- [Accessing Private Resources](./accessing_private_resources.md) +- [Accessing Private Resources](./ACCESSING_PRIVATE_RESOURCES.md) diff --git a/docs/required_roles_scopes_resources.md b/docs/required_roles_scopes_resources.md index 3480959..ef097e9 100644 --- a/docs/required_roles_scopes_resources.md +++ b/docs/required_roles_scopes_resources.md @@ -16,7 +16,7 @@ If you cannot grant role assignments at the scope where resources are created, t The full list of default role assignments is maintained in the AI Landing Zone submodule. These assignments change if you modify `containerAppsList` or disable services. -- Default role assignment matrix: [submodules/ai-landing-zone/README.md](../submodules/ai-landing-zone/README.md) +- Default role assignment matrix: [AI Landing Zone README](https://github.com/Azure/ai-landing-zone/blob/main/README.md) - Default container app roles (driven by `containerAppsList`): [infra/main.bicepparam](../infra/main.bicepparam) ## Required resource providers