From 29fea10f042892739853c5e00dd5fd41ff304bfb Mon Sep 17 00:00:00 2001 From: Harmanpreet-Microsoft Date: Mon, 20 Apr 2026 12:54:10 +0530 Subject: [PATCH 1/4] Validate and fix local development setup - Add scripts/validate-prerequisites.ps1 to check tool versions, git submodules, Azure auth, azd environment config, subscription alignment, and Fabric/Purview feature readiness before deployment - Add .env.example as a documented reference of all environment variables used by azd and infra/main.bicepparam (not auto-loaded by azd) - Add docs/local_development.md with step-by-step local setup guide, deployment instructions, troubleshooting, and recommended first-run config - Update .devcontainer/devcontainer.json with postCreateCommand to auto-init submodules and run prerequisite validation on container creation - Update README.md to reference the new local development guide Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .devcontainer/devcontainer.json | 3 +- .env.example | 73 +++++++ README.md | 1 + docs/local_development.md | 336 +++++++++++++++++++++++++++++ scripts/validate-prerequisites.ps1 | 331 ++++++++++++++++++++++++++++ 5 files changed, 743 insertions(+), 1 deletion(-) create mode 100644 .env.example create mode 100644 docs/local_development.md create mode 100644 scripts/validate-prerequisites.ps1 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 41e0c36..c245c7a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -10,5 +10,6 @@ "ghcr.io/azure/azure-dev/azd:latest": {}, "ghcr.io/devcontainers/features/azure-cli:1": {}, "ghcr.io/devcontainers/features/powershell:2.0.0": {} - } + }, + "postCreateCommand": "git submodule update --init --recursive && pwsh ./scripts/validate-prerequisites.ps1 -SkipAzureChecks" } diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..8ff67cd --- /dev/null +++ b/.env.example @@ -0,0 +1,73 @@ +# ============================================================ +# Environment Variable Reference for Deploy-Your-AI-Application-In-Production +# ============================================================ +# +# This file is a REFERENCE ONLY. It is NOT auto-loaded by azd. +# +# The primary way to set these values is via Azure Developer CLI: +# azd env new +# azd env set +# +# Use this file to understand which variables are available and +# what they control. Copy values into your azd environment with +# `azd env set` or edit `infra/main.bicepparam` for source-controlled defaults. +# ============================================================ + +# ---- Required ---- + +# Azure subscription ID for deployment +AZURE_SUBSCRIPTION_ID= + +# Azure region (e.g., eastus2). Check service availability before choosing. +AZURE_LOCATION=eastus2 + +# Cosmos DB location override (leave empty to use AZURE_LOCATION) +AZURE_COSMOS_LOCATION= + +# Entra object ID of the deploying identity (user, group, or service principal). +# Set this if Microsoft Graph lookup is blocked in your tenant. +AZURE_PRINCIPAL_ID= + +# Principal type for RBAC assignments (User, Group, or ServicePrincipal) +AZURE_PRINCIPAL_TYPE=User + +# ---- Network-Isolated Deployments (Jump VM credentials) ---- + +# Jump box VM admin username (default: testvmuser) +VM_ADMIN_USERNAME=testvmuser + +# Jump box VM admin password (use a strong password) +VM_ADMIN_PASSWORD= + +# ---- PostgreSQL (when deployPostgreSql = true) ---- + +# PostgreSQL admin password. If not set, a random password is generated and stored in Key Vault. +POSTGRES_ADMIN_PASSWORD= + +# ---- Fabric Configuration ---- + +# Fabric capacity mode: 'create', 'byo', or 'none' +fabricCapacityMode=create + +# ---- Feature Flags ---- + +# Deploy VM Key Vault (set to 'false' to skip) +DEPLOY_VM_KEY_VAULT=true + +# Deploy VNet subnets (set to 'false' to skip) +DEPLOY_SUBNETS=true + +# Enable side-by-side deployment mode +SIDE_BY_SIDE=true + +# Use User-Assigned Identity instead of system-assigned +USE_UAI=false + +# Use Container App API Key authentication +USE_CAPP_API_KEY=false + +# Enable agentic retrieval in AI Foundry +ENABLE_AGENTIC_RETRIEVAL=false + +# Existing VNet resource ID (when useExistingVNet = true in main.bicepparam) +EXISTING_VNET_RESOURCE_ID= diff --git a/README.md b/README.md index f9e6dba..f38d887 100644 --- a/README.md +++ b/README.md @@ -272,6 +272,7 @@ Supporting documentation | Document | Description | |----------|-------------| +| [Local Development Setup](./docs/local_development.md) | Set up your local environment, validate prerequisites, and deploy | | [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 | diff --git a/docs/local_development.md b/docs/local_development.md new file mode 100644 index 0000000..05ea08a --- /dev/null +++ b/docs/local_development.md @@ -0,0 +1,336 @@ +# Local Development Setup Guide + +This guide walks you through setting up your local machine to work with and deploy this solution accelerator. + +> **Note:** This repository is an Infrastructure-as-Code (IaC) project. "Local development" means preparing your environment to run `azd up` and deploy Azure resources. There are no local UI, backend API, or agent flows to run independently. + +--- + +## Quick Start + +```powershell +# 1. Clone with submodules +git clone --recurse-submodules https://github.com/microsoft/Deploy-Your-AI-Application-In-Production.git +cd Deploy-Your-AI-Application-In-Production + +# 2. Validate your environment +pwsh ./scripts/validate-prerequisites.ps1 + +# 3. Authenticate +azd auth login +az login + +# 4. Create and configure an environment +azd env new +azd env set AZURE_LOCATION eastus2 + +# 5. Review parameters and deploy +# Edit infra/main.bicepparam as needed (see Parameter Configuration below) +azd up +``` + +--- + +## Prerequisites + +### Required Tools + +| Tool | Minimum Version | Installation | +|------|----------------|--------------| +| **Git** | Latest | [Install Git](https://git-scm.com/downloads) | +| **Azure CLI (`az`)** | 2.61.0+ | [Install Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) | +| **Azure Developer CLI (`azd`)** | 1.15.0+ (≠ 1.23.9) | [Install azd](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd) | +| **PowerShell (`pwsh`)** | 7.0+ | [Install PowerShell](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) | +| **Bicep CLI** | 0.33.0+ | Bundled with `az` or [install standalone](https://learn.microsoft.com/azure/azure-resource-manager/bicep/install) | + +> **Windows users:** Run commands from PowerShell 7+ (not Windows PowerShell 5.1) so that `pwsh` is available on PATH. + +### Validate Prerequisites + +Run the validation script to check all tools, submodules, Azure authentication, and deployment readiness: + +```powershell +pwsh ./scripts/validate-prerequisites.ps1 +``` + +The script checks: +- Tool versions (az, azd, pwsh, bicep, git) +- Git submodule initialization +- Azure login status +- azd environment configuration +- Subscription alignment between `az` and `azd` +- Fabric/Purview feature flag readiness +- Quota check reminders + +To skip Azure authentication checks (e.g., in CI or offline environments): + +```powershell +pwsh ./scripts/validate-prerequisites.ps1 -SkipAzureChecks +``` + +--- + +## Step-by-Step Setup + +### 1. Clone the Repository + +Clone with submodules (the AI Landing Zone submodule is required): + +```bash +git clone --recurse-submodules https://github.com/microsoft/Deploy-Your-AI-Application-In-Production.git +cd Deploy-Your-AI-Application-In-Production +``` + +If you already cloned without submodules: + +```bash +git submodule update --init --recursive +``` + +### 2. Authenticate with Azure + +```bash +# Login to Azure CLI +az login + +# Login to Azure Developer CLI +azd auth login + +# Verify your subscription +az account show +``` + +If you need to target a specific tenant: + +```bash +azd auth login --tenant-id +az login --tenant +``` + +### 3. Create an azd Environment + +```bash +azd env new +azd env set AZURE_SUBSCRIPTION_ID +azd env set AZURE_LOCATION eastus2 +``` + +### 4. Configure Parameters + +The primary configuration file is `infra/main.bicepparam`. It reads values from both the file itself and azd environment variables (via `readEnvironmentVariable()`). + +**Setting values via azd (recommended for secrets and per-user overrides):** + +```bash +azd env set VM_ADMIN_USERNAME "youradminuser" +azd env set VM_ADMIN_PASSWORD "Use-A-Strong-Password-Here!" +azd env set POSTGRES_ADMIN_PASSWORD "Another-Strong-Password!" +``` + +**Setting values in `main.bicepparam` (for shared team defaults):** + +Edit `infra/main.bicepparam` directly. Key parameters to review: + +| Parameter | Purpose | When to Change | +|-----------|---------|----------------| +| `fabricCapacityPreset` | Fabric mode: `create`, `byo`, `none` | Set to `none` for first run without Fabric | +| `fabricCapacityAdmins` | UPN emails of Fabric admins | Required when `fabricCapacityPreset='create'` | +| `purviewAccountResourceId` | Existing Purview account ARM ID | Leave empty to skip Purview integration | +| `networkIsolation` | Enable private networking | Set to `false` for simpler first deployment | +| `deployPostgreSql` | Deploy PostgreSQL Flexible Server | Set to `false` if not needed | + +> **Reference:** See [`.env.example`](../.env.example) for a complete list of environment variables and their descriptions. This file is a documentation reference — it is not auto-loaded by azd. + +For detailed parameter documentation, see [Parameter Guide](./parameter_guide.md). + +### 5. Check Azure OpenAI Quota + +Before deploying, verify you have sufficient quota in your target region: + +```powershell +pwsh ./scripts/quota_check.ps1 +``` + +Or follow the [Quota Check Guide](./quota_check.md). + +### 6. Deploy + +```bash +azd up +``` + +This runs preprovision → Bicep deployment → postprovision hooks (~45–60 minutes). See the [Deployment Guide](./deploymentguide.md) for full details. + +### 7. Verify the Deployment + +Follow [Post Deployment Steps](./post_deployment_steps.md) to validate all components. + +--- + +## Deploying Local Changes + +After making changes to infrastructure code or automation scripts: + +### Redeploy Infrastructure Changes + +```bash +# Full redeploy (preprovision + infra + postprovision) +azd up + +# Infrastructure only (no hooks) +azd provision + +# Postprovision hooks only (after manual infra changes) +azd hooks run postprovision +``` + +### Re-run a Specific Postprovision Script + +```powershell +# Load environment values into your shell +$values = azd env get-values --output json | ConvertFrom-Json +$values.PSObject.Properties | ForEach-Object { Set-Item "env:$($_.Name)" $_.Value } + +# Run the specific script +pwsh ./scripts/automationScripts/.ps1 +``` + +### Validate Bicep Changes Locally + +```powershell +# Compile and check for errors +az bicep build --file infra/main.bicep + +# Preview what would change (requires an existing deployment) +azd provision --preview +``` + +### Update the AI Landing Zone Submodule + +```bash +cd submodules/ai-landing-zone +git pull origin main +cd ../.. +git add submodules/ai-landing-zone +git commit -m "Update AI Landing Zone submodule" +``` + +--- + +## Using Dev Containers / Codespaces + +For a pre-configured environment with all tools installed: + +- **GitHub Codespaces:** Click [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/microsoft/Deploy-Your-AI-Application-In-Production) +- **VS Code Dev Containers:** Click [![Open in Dev Containers](https://img.shields.io/static/v1?style=for-the-badge&label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/Deploy-Your-AI-Application-In-Production) + +The dev container automatically installs Azure CLI, azd, PowerShell, and Docker, and initializes git submodules on creation. + +--- + +## Troubleshooting + +### `pwsh` Not Found + +PowerShell 7+ must be installed and available as `pwsh` on your PATH. + +- **Windows:** Install from the [Microsoft Store](https://aka.ms/PSWindows) or [GitHub releases](https://github.com/PowerShell/PowerShell/releases) +- **macOS:** `brew install powershell` +- **Linux:** Follow the [Linux install guide](https://learn.microsoft.com/powershell/scripting/install/installing-powershell-on-linux) + +### Submodule Errors During Preprovision + +If preprovision fails with template or file-not-found errors: + +```bash +git submodule update --init --recursive +``` + +### azd Version 1.23.9 Is Incompatible + +This specific version has a known issue. Upgrade to the latest: + +```bash +azd version upgrade +``` + +### Subscription Mismatch Between `az` and `azd` + +If the validation script warns about subscription alignment: + +```bash +# Set az CLI to match your azd environment +az account set --subscription +``` + +### Bicep Compilation Errors + +```powershell +# Upgrade Bicep to the latest version +az bicep upgrade + +# Verify version +az bicep version +``` + +### Fabric Capacity Creation Fails + +- Ensure `fabricCapacityAdmins` contains at least one valid UPN or Entra object ID +- The deploying identity needs **Fabric Administrator** role +- For first-time deployments, consider setting `fabricCapacityPreset = 'none'` + +### Purview Steps Fail + +- The deploying identity needs **Purview Collection Admin** on the target collection +- Verify the `purviewAccountResourceId` is correct and accessible from your subscription + +### Template Spec 4MB Limit Error + +The AI Landing Zone submodule may be out of date: + +```bash +cd submodules/ai-landing-zone +git pull origin main +cd ../.. +azd up +``` + +### Deployment Takes Too Long or Times Out + +- Typical deployment time: 45–60 minutes +- Network-isolated deployments take longer due to private endpoint provisioning +- Check Azure Portal → Activity Log for specific error details + +--- + +## Recommended First-Run Configuration + +For the lowest-risk first deployment: + +```bash +azd env new my-first-deploy +azd env set AZURE_LOCATION eastus2 +``` + +Then in `infra/main.bicepparam`, temporarily set: + +```bicep +var fabricCapacityPreset = 'none' // Skip Fabric +param purviewAccountResourceId = '' // Skip Purview +param networkIsolation = false // Simpler networking +``` + +Once the base deployment succeeds, re-enable features incrementally. + +--- + +## Additional Resources + +| Resource | Description | +|----------|-------------| +| [Deployment Guide](./deploymentguide.md) | Full deployment instructions | +| [Parameter Guide](./parameter_guide.md) | All deployment parameters and toggles | +| [Post Deployment Steps](./post_deployment_steps.md) | Verify your deployment | +| [Quota Check Guide](./quota_check.md) | Check Azure OpenAI quota | +| [`.env.example`](../.env.example) | Environment variable reference | +| [FAQ](./faq.md) | Frequently asked questions | diff --git a/scripts/validate-prerequisites.ps1 b/scripts/validate-prerequisites.ps1 new file mode 100644 index 0000000..a1d51f8 --- /dev/null +++ b/scripts/validate-prerequisites.ps1 @@ -0,0 +1,331 @@ +<# +.SYNOPSIS + Validates local development prerequisites and deployment readiness. + +.DESCRIPTION + Checks that required tools are installed with compatible versions, + git submodules are initialized, Azure authentication is active, + and the azd environment is configured for deployment. + + Run this script before 'azd up' to catch common setup issues early. + +.EXAMPLE + pwsh ./scripts/validate-prerequisites.ps1 +#> + +param( + [switch]$SkipAzureChecks +) + +$ErrorActionPreference = 'Continue' + +$script:errors = @() +$script:warnings = @() + +function Write-Check { + param([string]$Name, [string]$Status, [string]$Detail) + switch ($Status) { + 'PASS' { Write-Host " [PASS] $Name" -ForegroundColor Green; if ($Detail) { Write-Host " $Detail" -ForegroundColor DarkGray } } + 'FAIL' { Write-Host " [FAIL] $Name" -ForegroundColor Red; if ($Detail) { Write-Host " $Detail" -ForegroundColor Red }; $script:errors += "$Name`: $Detail" } + 'WARN' { Write-Host " [WARN] $Name" -ForegroundColor Yellow; if ($Detail) { Write-Host " $Detail" -ForegroundColor Yellow }; $script:warnings += "$Name`: $Detail" } + 'SKIP' { Write-Host " [SKIP] $Name" -ForegroundColor DarkGray; if ($Detail) { Write-Host " $Detail" -ForegroundColor DarkGray } } + } +} + +function Test-VersionAtLeast { + param([string]$Current, [string]$Minimum) + try { + $cur = [System.Version]($Current -replace '[^0-9.]', '' -replace '^\.' -replace '\.$') + $min = [System.Version]($Minimum -replace '[^0-9.]', '' -replace '^\.' -replace '\.$') + return $cur -ge $min + } + catch { return $false } +} + +$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path + +Write-Host "" +Write-Host "============================================" -ForegroundColor Cyan +Write-Host " Local Development Prerequisites Validation" -ForegroundColor Cyan +Write-Host "============================================" -ForegroundColor Cyan +Write-Host "" + +# ------------------------------------------------------- +# Section 1: Required Tools +# ------------------------------------------------------- +Write-Host "1. Required Tools" -ForegroundColor White +Write-Host " ---------------" -ForegroundColor DarkGray + +# Git +$git = Get-Command git -ErrorAction SilentlyContinue +if ($git) { + $gitVersion = (git --version 2>$null) -replace 'git version\s*', '' + Write-Check "Git" "PASS" "Version: $gitVersion" +} +else { + Write-Check "Git" "FAIL" "Not found. Install from https://git-scm.com/downloads" +} + +# Azure CLI +$az = Get-Command az -ErrorAction SilentlyContinue +if ($az) { + $azVersionRaw = (az version 2>$null | ConvertFrom-Json).'azure-cli' + if (Test-VersionAtLeast $azVersionRaw '2.61.0') { + Write-Check "Azure CLI" "PASS" "Version: $azVersionRaw (>= 2.61.0)" + } + else { + Write-Check "Azure CLI" "FAIL" "Version $azVersionRaw found, but >= 2.61.0 is required. Update: https://learn.microsoft.com/cli/azure/install-azure-cli" + } +} +else { + Write-Check "Azure CLI" "FAIL" "Not found. Install from https://learn.microsoft.com/cli/azure/install-azure-cli" +} + +# Azure Developer CLI (azd) +$azd = Get-Command azd -ErrorAction SilentlyContinue +if ($azd) { + $azdVersionRaw = (azd version 2>$null) -replace '\(.*', '' | ForEach-Object { $_.Trim() } + if (Test-VersionAtLeast $azdVersionRaw '1.15.0') { + if ($azdVersionRaw -match '1\.23\.9') { + Write-Check "Azure Developer CLI (azd)" "FAIL" "Version $azdVersionRaw is known to be incompatible (excluded in azure.yaml). Please upgrade." + } + else { + Write-Check "Azure Developer CLI (azd)" "PASS" "Version: $azdVersionRaw (>= 1.15.0)" + } + } + else { + Write-Check "Azure Developer CLI (azd)" "FAIL" "Version $azdVersionRaw found, but >= 1.15.0 is required. Update: https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd" + } +} +else { + Write-Check "Azure Developer CLI (azd)" "FAIL" "Not found. Install from https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd" +} + +# PowerShell +$psVersion = $PSVersionTable.PSVersion +if ($psVersion.Major -ge 7) { + Write-Check "PowerShell" "PASS" "Version: $psVersion (>= 7.0)" +} +else { + Write-Check "PowerShell" "FAIL" "Version $psVersion found, but >= 7.0 is required. Install from https://learn.microsoft.com/powershell/scripting/install/installing-powershell" +} + +# Bicep CLI (check both standalone and az bicep) +$bicepVersion = $null +$bicep = Get-Command bicep -ErrorAction SilentlyContinue +if ($bicep) { + $bicepVersionRaw = (bicep --version 2>$null) -replace '.*Bicep CLI\s*', '' -replace '\s.*', '' + $bicepVersion = $bicepVersionRaw +} +if (-not $bicepVersion -and $az) { + try { + $azBicepRaw = (az bicep version 2>$null) + if ($azBicepRaw -match '(\d+\.\d+\.\d+)') { + $bicepVersion = $Matches[1] + } + } + catch {} +} +if ($bicepVersion) { + if (Test-VersionAtLeast $bicepVersion '0.33.0') { + Write-Check "Bicep CLI" "PASS" "Version: $bicepVersion (>= 0.33.0)" + } + else { + Write-Check "Bicep CLI" "FAIL" "Version $bicepVersion found, but >= 0.33.0 is required. Update: az bicep upgrade" + } +} +else { + Write-Check "Bicep CLI" "WARN" "Not detected (standalone or via az bicep). azd will auto-install if needed, but verify with: az bicep version" +} + +Write-Host "" + +# ------------------------------------------------------- +# Section 2: Repository State +# ------------------------------------------------------- +Write-Host "2. Repository State" -ForegroundColor White +Write-Host " ----------------" -ForegroundColor DarkGray + +# Git submodules +$submodulePath = Join-Path $repoRoot 'submodules' 'ai-landing-zone' +$submoduleGitExists = Test-Path (Join-Path $submodulePath '.git') +$submoduleBicepExists = Test-Path (Join-Path $submodulePath 'main.bicep') +if ($submoduleGitExists -or $submoduleBicepExists) { + Write-Check "Git submodules" "PASS" "ai-landing-zone submodule is initialized" +} +else { + Write-Check "Git submodules" "FAIL" "Submodule not initialized. Run: git submodule update --init --recursive" +} + +# Check for uncommitted .bicepparam changes +try { + Push-Location $repoRoot + $paramStatus = git status --porcelain -- 'infra/main.bicepparam' 2>$null + if ($paramStatus) { + Write-Check "Parameter file" "WARN" "infra/main.bicepparam has uncommitted changes — ensure your local overrides are intentional" + } + else { + Write-Check "Parameter file" "PASS" "infra/main.bicepparam is clean" + } + Pop-Location +} +catch { + Pop-Location + Write-Check "Parameter file" "SKIP" "Could not check git status" +} + +Write-Host "" + +# ------------------------------------------------------- +# Section 3: Azure & Deployment Readiness +# ------------------------------------------------------- +if ($SkipAzureChecks) { + Write-Host "3. Azure & Deployment Readiness (SKIPPED)" -ForegroundColor DarkGray + Write-Host "" +} +else { + Write-Host "3. Azure & Deployment Readiness" -ForegroundColor White + Write-Host " ----------------------------" -ForegroundColor DarkGray + + # Azure login + $loggedIn = $false + if ($az) { + try { + $account = az account show 2>$null | ConvertFrom-Json + if ($account) { + $loggedIn = $true + Write-Check "Azure login" "PASS" "Signed in as $($account.user.name) on subscription '$($account.name)'" + } + else { + Write-Check "Azure login" "FAIL" "Not logged in. Run: az login" + } + } + catch { + Write-Check "Azure login" "FAIL" "Not logged in. Run: az login && azd auth login" + } + } + else { + Write-Check "Azure login" "SKIP" "Azure CLI not installed" + } + + # azd environment + if ($azd) { + try { + $envListRaw = azd env list --output json 2>$null + if ($envListRaw) { + $envList = $envListRaw | ConvertFrom-Json + $defaultEnv = $envList | Where-Object { $_.IsDefault -eq $true } + if ($defaultEnv) { + Write-Check "azd environment" "PASS" "Active environment: $($defaultEnv.Name)" + + # Check critical env vars + $envValues = azd env get-values --output json 2>$null | ConvertFrom-Json + if ($envValues) { + if ($envValues.AZURE_LOCATION) { + Write-Check "AZURE_LOCATION" "PASS" "$($envValues.AZURE_LOCATION)" + } + else { + Write-Check "AZURE_LOCATION" "FAIL" "Not set. Run: azd env set AZURE_LOCATION " + } + if ($envValues.AZURE_SUBSCRIPTION_ID) { + # Cross-check with az account + if ($loggedIn -and $account -and $account.id -ne $envValues.AZURE_SUBSCRIPTION_ID) { + Write-Check "Subscription alignment" "WARN" "azd env targets $($envValues.AZURE_SUBSCRIPTION_ID) but az CLI is on $($account.id). Run: az account set --subscription $($envValues.AZURE_SUBSCRIPTION_ID)" + } + else { + Write-Check "Subscription alignment" "PASS" "azd env and az CLI target the same subscription" + } + } + else { + Write-Check "AZURE_SUBSCRIPTION_ID" "WARN" "Not explicitly set in azd env. azd will use the default az CLI subscription." + } + } + } + else { + Write-Check "azd environment" "WARN" "No default environment selected. Run: azd env new or azd env select " + } + } + else { + Write-Check "azd environment" "WARN" "No environments found. Run: azd env new " + } + } + catch { + Write-Check "azd environment" "WARN" "Could not query azd environments. You may need to run: azd env new " + } + } + else { + Write-Check "azd environment" "SKIP" "azd not installed" + } + + # Fabric / Purview feature readiness + Write-Host "" + Write-Host "4. Feature-Specific Readiness" -ForegroundColor White + Write-Host " --------------------------" -ForegroundColor DarkGray + + $bicepParamFile = Join-Path $repoRoot 'infra' 'main.bicepparam' + if (Test-Path $bicepParamFile) { + $paramContent = Get-Content $bicepParamFile -Raw + + # Check Fabric preset + if ($paramContent -match "fabricCapacityMode.*'create'") { + $fabricAdminsSet = $paramContent -match "fabricCapacityAdmins\s*=\s*\[(?!\s*\])" + if ($fabricAdminsSet) { + Write-Check "Fabric capacity (create mode)" "PASS" "fabricCapacityAdmins is configured" + } + else { + Write-Check "Fabric capacity (create mode)" "WARN" "fabricCapacityPreset='create' but fabricCapacityAdmins appears empty. Capacity creation will fail." + } + Write-Check "Fabric permissions" "WARN" "Ensure the deploying identity has Fabric Administrator role for workspace creation" + } + elseif ($paramContent -match "fabricCapacityMode.*'none'") { + Write-Check "Fabric capacity" "PASS" "Fabric is disabled (fabricCapacityPreset='none')" + } + else { + Write-Check "Fabric capacity" "PASS" "Fabric is set to BYO mode" + } + + # Check Purview + if ($paramContent -match "purviewAccountResourceId\s*=\s*'[^']+'") { + Write-Check "Purview integration" "PASS" "purviewAccountResourceId is set" + Write-Check "Purview permissions" "WARN" "Ensure the deploying identity has Purview Collection Admin on the target collection" + } + else { + Write-Check "Purview integration" "PASS" "Purview is not configured (steps will be skipped)" + } + + # Quota reminder + Write-Check "Azure OpenAI quota" "WARN" "Run the quota check before deploying: pwsh ./scripts/quota_check.ps1 or see docs/quota_check.md" + } + else { + Write-Check "Parameter file" "FAIL" "infra/main.bicepparam not found at expected path" + } +} + +# ------------------------------------------------------- +# Summary +# ------------------------------------------------------- +Write-Host "" +Write-Host "============================================" -ForegroundColor Cyan +Write-Host " Summary" -ForegroundColor Cyan +Write-Host "============================================" -ForegroundColor Cyan + +if ($script:errors.Count -eq 0 -and $script:warnings.Count -eq 0) { + Write-Host " All checks passed. You are ready to deploy!" -ForegroundColor Green +} +else { + if ($script:errors.Count -gt 0) { + Write-Host " $($script:errors.Count) error(s) found — fix these before deploying:" -ForegroundColor Red + foreach ($e in $script:errors) { Write-Host " - $e" -ForegroundColor Red } + } + if ($script:warnings.Count -gt 0) { + Write-Host " $($script:warnings.Count) warning(s) — review before deploying:" -ForegroundColor Yellow + foreach ($w in $script:warnings) { Write-Host " - $w" -ForegroundColor Yellow } + } +} + +Write-Host "" + +if ($script:errors.Count -gt 0) { + exit 1 +} +exit 0 From f4db7ca5d1aab355b9fdfb86adfb674cdb7c9610 Mon Sep 17 00:00:00 2001 From: Harmanpreet-Microsoft Date: Fri, 24 Apr 2026 18:26:19 +0530 Subject: [PATCH 2/4] fix: use workspace ID for Fabric RBAC in BYO mode The RBAC script resolved workspace name from desiredFabricWorkspaceName before FABRIC_WORKSPACE_NAME. In BYO mode these differ, causing the Fabric API lookup to fail silently and skip the Contributor role grant. The OneLake indexer then fails with 'access to the workspace was denied'. Changes: - 01_setup_rbac.ps1: Prefer FABRIC_WORKSPACE_NAME over desiredFabricWorkspaceName. Resolve FABRIC_WORKSPACE_ID and pass it. - setup_ai_services_rbac.ps1: Accept -FabricWorkspaceId parameter. Use it directly for role assignment, skip displayName lookup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../OneLakeIndex/01_setup_rbac.ps1 | 34 +++++++--- .../OneLakeIndex/setup_ai_services_rbac.ps1 | 64 ++++++++++++------- 2 files changed, 64 insertions(+), 34 deletions(-) diff --git a/scripts/automationScripts/OneLakeIndex/01_setup_rbac.ps1 b/scripts/automationScripts/OneLakeIndex/01_setup_rbac.ps1 index 1507992..ce2e3c6 100644 --- a/scripts/automationScripts/OneLakeIndex/01_setup_rbac.ps1 +++ b/scripts/automationScripts/OneLakeIndex/01_setup_rbac.ps1 @@ -96,11 +96,12 @@ try { 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'] } + # Prefer FABRIC_WORKSPACE_NAME (actual BYO name) over desiredFabricWorkspaceName (requested name that may differ in BYO mode) 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 -and $outputs -and $outputs.desiredFabricWorkspaceName -and $outputs.desiredFabricWorkspaceName.value) { $fabricWorkspaceName = $outputs.desiredFabricWorkspaceName.value } + if (-not $fabricWorkspaceName) { $fabricWorkspaceName = $env_vars['desiredFabricWorkspaceName'] } 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'))) { @@ -109,6 +110,14 @@ try { } } if (-not $fabricWorkspaceName -and $env:AZURE_ENV_NAME) { $fabricWorkspaceName = "workspace-$($env:AZURE_ENV_NAME.Trim())" } + + # Resolve Fabric workspace ID for direct role assignment (avoids fragile displayName lookup) + $fabricWorkspaceId = '' + if (-not $fabricWorkspaceId) { $fabricWorkspaceId = $env_vars['FABRIC_WORKSPACE_ID'] } + if (-not $fabricWorkspaceId) { $fabricWorkspaceId = $env:FABRIC_WORKSPACE_ID } + if (-not $fabricWorkspaceId) { $fabricWorkspaceId = Get-AzdEnvValue -Key 'FABRIC_WORKSPACE_ID' } + if (-not $fabricWorkspaceId) { $fabricWorkspaceId = Get-AzdEnvValue -Key 'fabricWorkspaceIdOut' } + if (-not $fabricWorkspaceId -and $outputs -and $outputs.fabricWorkspaceIdOut -and $outputs.fabricWorkspaceIdOut.value) { $fabricWorkspaceId = $outputs.fabricWorkspaceIdOut.value } if (-not $aiSearchResourceId -and $outputs -and $outputs.aiSearchResourceId -and $outputs.aiSearchResourceId.value) { $aiSearchResourceId = $outputs.aiSearchResourceId.value } if (-not $aiSearchResourceId) { $aiSearchResourceId = $env_vars['aiSearchResourceId'] } @@ -182,6 +191,7 @@ try { Warn " AI Foundry: not detected" } Log " Fabric Workspace: $fabricWorkspaceName" + if ($fabricWorkspaceId) { Log " Fabric Workspace ID: $fabricWorkspaceId" } if ($principalId) { Log " Principal ID: $principalId" } # Setup RBAC permissions @@ -190,13 +200,17 @@ try { Log "🔐 Setting up RBAC permissions for OneLake indexing..." try { - & "$PSScriptRoot/setup_ai_services_rbac.ps1" ` - -ExecutionManagedIdentityPrincipalId $principalId ` - -AISearchName $aiSearchName ` - -AIFoundryName $aiFoundryName ` - -AIFoundryResourceGroup $aiFoundryResourceGroup ` - -AISearchResourceGroup $aiSearchResourceGroup ` - -FabricWorkspaceName $fabricWorkspaceName + $rbacArgs = @{ + ExecutionManagedIdentityPrincipalId = $principalId + AISearchName = $aiSearchName + AIFoundryName = $aiFoundryName + AIFoundryResourceGroup = $aiFoundryResourceGroup + AISearchResourceGroup = $aiSearchResourceGroup + FabricWorkspaceName = $fabricWorkspaceName + } + if ($fabricWorkspaceId) { $rbacArgs['FabricWorkspaceId'] = $fabricWorkspaceId } + + & "$PSScriptRoot/setup_ai_services_rbac.ps1" @rbacArgs Log "✅ RBAC configuration completed successfully" Log "✅ Managed identity can now access AI Search and AI Foundry" @@ -204,7 +218,7 @@ try { } catch { Warn "RBAC setup failed: $_" Log "You can run RBAC setup manually later with:" - Log " ./scripts/OneLakeIndex/setup_ai_services_rbac.ps1 -ExecutionManagedIdentityPrincipalId '$principalId' -AISearchName '$aiSearchName' -AIFoundryName '$aiFoundryName' -FabricWorkspaceName '$fabricWorkspaceName'" + Log " ./scripts/OneLakeIndex/setup_ai_services_rbac.ps1 -ExecutionManagedIdentityPrincipalId '$principalId' -AISearchName '$aiSearchName' -AIFoundryName '$aiFoundryName' -FabricWorkspaceName '$fabricWorkspaceName' -FabricWorkspaceId '$fabricWorkspaceId'" throw } } diff --git a/scripts/automationScripts/OneLakeIndex/setup_ai_services_rbac.ps1 b/scripts/automationScripts/OneLakeIndex/setup_ai_services_rbac.ps1 index 9cac831..7cd9807 100644 --- a/scripts/automationScripts/OneLakeIndex/setup_ai_services_rbac.ps1 +++ b/scripts/automationScripts/OneLakeIndex/setup_ai_services_rbac.ps1 @@ -14,7 +14,9 @@ param( [Parameter(Mandatory = $false)] [string]$AISearchResourceGroup = "", [Parameter(Mandatory = $false)] - [string]$FabricWorkspaceName = "" + [string]$FabricWorkspaceName = "", + [Parameter(Mandatory = $false)] + [string]$FabricWorkspaceId = "" ) Set-StrictMode -Version Latest @@ -341,7 +343,7 @@ try { } # Setup Fabric workspace permissions for OneLake access - if ($FabricWorkspaceName) { + if ($FabricWorkspaceId -or $FabricWorkspaceName) { Log "Setting up Fabric workspace permissions..." # Get Fabric access token @@ -355,23 +357,40 @@ try { # Create Fabric headers $fabricHeaders = New-SecureHeaders -Token $fabricToken - # Find the workspace - $workspacesUrl = "https://api.fabric.microsoft.com/v1/workspaces" - $workspacesResponse = Invoke-SecureRestMethod -Uri $workspacesUrl -Headers $fabricHeaders -Method Get - - # Debug: Log available workspaces and their properties - Log "Available workspaces:" - foreach ($ws in $workspacesResponse.value) { - Log " - Name: '$($ws.displayName)' ID: $($ws.id)" + $workspaceId = $null + + # Use workspace ID directly if provided (avoids fragile displayName lookup, critical for BYO workspaces) + if ($FabricWorkspaceId) { + $workspaceId = $FabricWorkspaceId.Trim() + Log "Using provided Fabric workspace ID: $workspaceId" } - - # Find workspace by displayName only (name property may not exist) - $workspace = $workspacesResponse.value | Where-Object { $_.displayName -eq $FabricWorkspaceName } - - if ($workspace) { - $workspaceId = $workspace.id - Log "Found Fabric workspace: $FabricWorkspaceName (ID: $workspaceId)" + + # Fall back to displayName lookup if no ID provided + if (-not $workspaceId) { + # Find the workspace + $workspacesUrl = "https://api.fabric.microsoft.com/v1/workspaces" + $workspacesResponse = Invoke-SecureRestMethod -Uri $workspacesUrl -Headers $fabricHeaders -Method Get + + # Debug: Log available workspaces and their properties + Log "Available workspaces:" + foreach ($ws in $workspacesResponse.value) { + Log " - Name: '$($ws.displayName)' ID: $($ws.id)" + } + + # Find workspace by displayName only (name property may not exist) + $workspace = $workspacesResponse.value | Where-Object { $_.displayName -eq $FabricWorkspaceName } + if ($workspace) { + $workspaceId = $workspace.id + Log "Found Fabric workspace: $FabricWorkspaceName (ID: $workspaceId)" + } else { + Warn "Could not find Fabric workspace: '$FabricWorkspaceName'" + Log "Available workspace names: $($workspacesResponse.value.displayName -join ', ')" + Log "Make sure the workspace name matches exactly (case-sensitive)" + } + } + + if ($workspaceId) { # Add the managed identity as a workspace member with Contributor role $roleAssignmentUrl = "https://api.fabric.microsoft.com/v1/workspaces/$workspaceId/roleAssignments" $rolePayload = @{ @@ -382,7 +401,7 @@ try { role = "Contributor" } | ConvertTo-Json -Depth 3 - Log "Assigning Contributor role to managed identity in workspace..." + Log "Assigning Contributor role to managed identity in workspace $workspaceId..." try { Invoke-SecureRestMethod -Uri $roleAssignmentUrl -Headers @{ Authorization = "Bearer $fabricToken" @@ -399,10 +418,6 @@ try { Log " 2. Add managed identity $ExecutionManagedIdentityPrincipalId as Contributor" } } - } else { - Warn "Could not find Fabric workspace: '$FabricWorkspaceName'" - Log "Available workspace names: $($workspacesResponse.value.displayName -join ', ')" - Log "Make sure the workspace name matches exactly (case-sensitive)" } } } catch { @@ -423,8 +438,9 @@ try { Log " - AI Foundry project identity has Search roles" } } - if ($FabricWorkspaceName) { - Log " - Contributor on Fabric workspace $FabricWorkspaceName" + if ($FabricWorkspaceId -or $FabricWorkspaceName) { + $wsLabel = if ($FabricWorkspaceId) { "Fabric workspace ID $FabricWorkspaceId" } else { "Fabric workspace $FabricWorkspaceName" } + Log " - Contributor on $wsLabel" } } catch { From e3699709f5b93917d29a3a24dd65f73c084109d2 Mon Sep 17 00:00:00 2001 From: Harmanpreet-Microsoft Date: Fri, 24 Apr 2026 18:36:08 +0530 Subject: [PATCH 3/4] revert: restore devcontainer.json, .env.example, README.md, local_development.md to origin/dev state Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .devcontainer/devcontainer.json | 3 +- .env.example | 73 ------- README.md | 1 - docs/local_development.md | 336 -------------------------------- 4 files changed, 1 insertion(+), 412 deletions(-) delete mode 100644 .env.example delete mode 100644 docs/local_development.md diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c245c7a..41e0c36 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -10,6 +10,5 @@ "ghcr.io/azure/azure-dev/azd:latest": {}, "ghcr.io/devcontainers/features/azure-cli:1": {}, "ghcr.io/devcontainers/features/powershell:2.0.0": {} - }, - "postCreateCommand": "git submodule update --init --recursive && pwsh ./scripts/validate-prerequisites.ps1 -SkipAzureChecks" + } } diff --git a/.env.example b/.env.example deleted file mode 100644 index 8ff67cd..0000000 --- a/.env.example +++ /dev/null @@ -1,73 +0,0 @@ -# ============================================================ -# Environment Variable Reference for Deploy-Your-AI-Application-In-Production -# ============================================================ -# -# This file is a REFERENCE ONLY. It is NOT auto-loaded by azd. -# -# The primary way to set these values is via Azure Developer CLI: -# azd env new -# azd env set -# -# Use this file to understand which variables are available and -# what they control. Copy values into your azd environment with -# `azd env set` or edit `infra/main.bicepparam` for source-controlled defaults. -# ============================================================ - -# ---- Required ---- - -# Azure subscription ID for deployment -AZURE_SUBSCRIPTION_ID= - -# Azure region (e.g., eastus2). Check service availability before choosing. -AZURE_LOCATION=eastus2 - -# Cosmos DB location override (leave empty to use AZURE_LOCATION) -AZURE_COSMOS_LOCATION= - -# Entra object ID of the deploying identity (user, group, or service principal). -# Set this if Microsoft Graph lookup is blocked in your tenant. -AZURE_PRINCIPAL_ID= - -# Principal type for RBAC assignments (User, Group, or ServicePrincipal) -AZURE_PRINCIPAL_TYPE=User - -# ---- Network-Isolated Deployments (Jump VM credentials) ---- - -# Jump box VM admin username (default: testvmuser) -VM_ADMIN_USERNAME=testvmuser - -# Jump box VM admin password (use a strong password) -VM_ADMIN_PASSWORD= - -# ---- PostgreSQL (when deployPostgreSql = true) ---- - -# PostgreSQL admin password. If not set, a random password is generated and stored in Key Vault. -POSTGRES_ADMIN_PASSWORD= - -# ---- Fabric Configuration ---- - -# Fabric capacity mode: 'create', 'byo', or 'none' -fabricCapacityMode=create - -# ---- Feature Flags ---- - -# Deploy VM Key Vault (set to 'false' to skip) -DEPLOY_VM_KEY_VAULT=true - -# Deploy VNet subnets (set to 'false' to skip) -DEPLOY_SUBNETS=true - -# Enable side-by-side deployment mode -SIDE_BY_SIDE=true - -# Use User-Assigned Identity instead of system-assigned -USE_UAI=false - -# Use Container App API Key authentication -USE_CAPP_API_KEY=false - -# Enable agentic retrieval in AI Foundry -ENABLE_AGENTIC_RETRIEVAL=false - -# Existing VNet resource ID (when useExistingVNet = true in main.bicepparam) -EXISTING_VNET_RESOURCE_ID= diff --git a/README.md b/README.md index f38d887..f9e6dba 100644 --- a/README.md +++ b/README.md @@ -272,7 +272,6 @@ Supporting documentation | Document | Description | |----------|-------------| -| [Local Development Setup](./docs/local_development.md) | Set up your local environment, validate prerequisites, and deploy | | [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 | diff --git a/docs/local_development.md b/docs/local_development.md deleted file mode 100644 index 05ea08a..0000000 --- a/docs/local_development.md +++ /dev/null @@ -1,336 +0,0 @@ -# Local Development Setup Guide - -This guide walks you through setting up your local machine to work with and deploy this solution accelerator. - -> **Note:** This repository is an Infrastructure-as-Code (IaC) project. "Local development" means preparing your environment to run `azd up` and deploy Azure resources. There are no local UI, backend API, or agent flows to run independently. - ---- - -## Quick Start - -```powershell -# 1. Clone with submodules -git clone --recurse-submodules https://github.com/microsoft/Deploy-Your-AI-Application-In-Production.git -cd Deploy-Your-AI-Application-In-Production - -# 2. Validate your environment -pwsh ./scripts/validate-prerequisites.ps1 - -# 3. Authenticate -azd auth login -az login - -# 4. Create and configure an environment -azd env new -azd env set AZURE_LOCATION eastus2 - -# 5. Review parameters and deploy -# Edit infra/main.bicepparam as needed (see Parameter Configuration below) -azd up -``` - ---- - -## Prerequisites - -### Required Tools - -| Tool | Minimum Version | Installation | -|------|----------------|--------------| -| **Git** | Latest | [Install Git](https://git-scm.com/downloads) | -| **Azure CLI (`az`)** | 2.61.0+ | [Install Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) | -| **Azure Developer CLI (`azd`)** | 1.15.0+ (≠ 1.23.9) | [Install azd](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd) | -| **PowerShell (`pwsh`)** | 7.0+ | [Install PowerShell](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) | -| **Bicep CLI** | 0.33.0+ | Bundled with `az` or [install standalone](https://learn.microsoft.com/azure/azure-resource-manager/bicep/install) | - -> **Windows users:** Run commands from PowerShell 7+ (not Windows PowerShell 5.1) so that `pwsh` is available on PATH. - -### Validate Prerequisites - -Run the validation script to check all tools, submodules, Azure authentication, and deployment readiness: - -```powershell -pwsh ./scripts/validate-prerequisites.ps1 -``` - -The script checks: -- Tool versions (az, azd, pwsh, bicep, git) -- Git submodule initialization -- Azure login status -- azd environment configuration -- Subscription alignment between `az` and `azd` -- Fabric/Purview feature flag readiness -- Quota check reminders - -To skip Azure authentication checks (e.g., in CI or offline environments): - -```powershell -pwsh ./scripts/validate-prerequisites.ps1 -SkipAzureChecks -``` - ---- - -## Step-by-Step Setup - -### 1. Clone the Repository - -Clone with submodules (the AI Landing Zone submodule is required): - -```bash -git clone --recurse-submodules https://github.com/microsoft/Deploy-Your-AI-Application-In-Production.git -cd Deploy-Your-AI-Application-In-Production -``` - -If you already cloned without submodules: - -```bash -git submodule update --init --recursive -``` - -### 2. Authenticate with Azure - -```bash -# Login to Azure CLI -az login - -# Login to Azure Developer CLI -azd auth login - -# Verify your subscription -az account show -``` - -If you need to target a specific tenant: - -```bash -azd auth login --tenant-id -az login --tenant -``` - -### 3. Create an azd Environment - -```bash -azd env new -azd env set AZURE_SUBSCRIPTION_ID -azd env set AZURE_LOCATION eastus2 -``` - -### 4. Configure Parameters - -The primary configuration file is `infra/main.bicepparam`. It reads values from both the file itself and azd environment variables (via `readEnvironmentVariable()`). - -**Setting values via azd (recommended for secrets and per-user overrides):** - -```bash -azd env set VM_ADMIN_USERNAME "youradminuser" -azd env set VM_ADMIN_PASSWORD "Use-A-Strong-Password-Here!" -azd env set POSTGRES_ADMIN_PASSWORD "Another-Strong-Password!" -``` - -**Setting values in `main.bicepparam` (for shared team defaults):** - -Edit `infra/main.bicepparam` directly. Key parameters to review: - -| Parameter | Purpose | When to Change | -|-----------|---------|----------------| -| `fabricCapacityPreset` | Fabric mode: `create`, `byo`, `none` | Set to `none` for first run without Fabric | -| `fabricCapacityAdmins` | UPN emails of Fabric admins | Required when `fabricCapacityPreset='create'` | -| `purviewAccountResourceId` | Existing Purview account ARM ID | Leave empty to skip Purview integration | -| `networkIsolation` | Enable private networking | Set to `false` for simpler first deployment | -| `deployPostgreSql` | Deploy PostgreSQL Flexible Server | Set to `false` if not needed | - -> **Reference:** See [`.env.example`](../.env.example) for a complete list of environment variables and their descriptions. This file is a documentation reference — it is not auto-loaded by azd. - -For detailed parameter documentation, see [Parameter Guide](./parameter_guide.md). - -### 5. Check Azure OpenAI Quota - -Before deploying, verify you have sufficient quota in your target region: - -```powershell -pwsh ./scripts/quota_check.ps1 -``` - -Or follow the [Quota Check Guide](./quota_check.md). - -### 6. Deploy - -```bash -azd up -``` - -This runs preprovision → Bicep deployment → postprovision hooks (~45–60 minutes). See the [Deployment Guide](./deploymentguide.md) for full details. - -### 7. Verify the Deployment - -Follow [Post Deployment Steps](./post_deployment_steps.md) to validate all components. - ---- - -## Deploying Local Changes - -After making changes to infrastructure code or automation scripts: - -### Redeploy Infrastructure Changes - -```bash -# Full redeploy (preprovision + infra + postprovision) -azd up - -# Infrastructure only (no hooks) -azd provision - -# Postprovision hooks only (after manual infra changes) -azd hooks run postprovision -``` - -### Re-run a Specific Postprovision Script - -```powershell -# Load environment values into your shell -$values = azd env get-values --output json | ConvertFrom-Json -$values.PSObject.Properties | ForEach-Object { Set-Item "env:$($_.Name)" $_.Value } - -# Run the specific script -pwsh ./scripts/automationScripts/.ps1 -``` - -### Validate Bicep Changes Locally - -```powershell -# Compile and check for errors -az bicep build --file infra/main.bicep - -# Preview what would change (requires an existing deployment) -azd provision --preview -``` - -### Update the AI Landing Zone Submodule - -```bash -cd submodules/ai-landing-zone -git pull origin main -cd ../.. -git add submodules/ai-landing-zone -git commit -m "Update AI Landing Zone submodule" -``` - ---- - -## Using Dev Containers / Codespaces - -For a pre-configured environment with all tools installed: - -- **GitHub Codespaces:** Click [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/microsoft/Deploy-Your-AI-Application-In-Production) -- **VS Code Dev Containers:** Click [![Open in Dev Containers](https://img.shields.io/static/v1?style=for-the-badge&label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/Deploy-Your-AI-Application-In-Production) - -The dev container automatically installs Azure CLI, azd, PowerShell, and Docker, and initializes git submodules on creation. - ---- - -## Troubleshooting - -### `pwsh` Not Found - -PowerShell 7+ must be installed and available as `pwsh` on your PATH. - -- **Windows:** Install from the [Microsoft Store](https://aka.ms/PSWindows) or [GitHub releases](https://github.com/PowerShell/PowerShell/releases) -- **macOS:** `brew install powershell` -- **Linux:** Follow the [Linux install guide](https://learn.microsoft.com/powershell/scripting/install/installing-powershell-on-linux) - -### Submodule Errors During Preprovision - -If preprovision fails with template or file-not-found errors: - -```bash -git submodule update --init --recursive -``` - -### azd Version 1.23.9 Is Incompatible - -This specific version has a known issue. Upgrade to the latest: - -```bash -azd version upgrade -``` - -### Subscription Mismatch Between `az` and `azd` - -If the validation script warns about subscription alignment: - -```bash -# Set az CLI to match your azd environment -az account set --subscription -``` - -### Bicep Compilation Errors - -```powershell -# Upgrade Bicep to the latest version -az bicep upgrade - -# Verify version -az bicep version -``` - -### Fabric Capacity Creation Fails - -- Ensure `fabricCapacityAdmins` contains at least one valid UPN or Entra object ID -- The deploying identity needs **Fabric Administrator** role -- For first-time deployments, consider setting `fabricCapacityPreset = 'none'` - -### Purview Steps Fail - -- The deploying identity needs **Purview Collection Admin** on the target collection -- Verify the `purviewAccountResourceId` is correct and accessible from your subscription - -### Template Spec 4MB Limit Error - -The AI Landing Zone submodule may be out of date: - -```bash -cd submodules/ai-landing-zone -git pull origin main -cd ../.. -azd up -``` - -### Deployment Takes Too Long or Times Out - -- Typical deployment time: 45–60 minutes -- Network-isolated deployments take longer due to private endpoint provisioning -- Check Azure Portal → Activity Log for specific error details - ---- - -## Recommended First-Run Configuration - -For the lowest-risk first deployment: - -```bash -azd env new my-first-deploy -azd env set AZURE_LOCATION eastus2 -``` - -Then in `infra/main.bicepparam`, temporarily set: - -```bicep -var fabricCapacityPreset = 'none' // Skip Fabric -param purviewAccountResourceId = '' // Skip Purview -param networkIsolation = false // Simpler networking -``` - -Once the base deployment succeeds, re-enable features incrementally. - ---- - -## Additional Resources - -| Resource | Description | -|----------|-------------| -| [Deployment Guide](./deploymentguide.md) | Full deployment instructions | -| [Parameter Guide](./parameter_guide.md) | All deployment parameters and toggles | -| [Post Deployment Steps](./post_deployment_steps.md) | Verify your deployment | -| [Quota Check Guide](./quota_check.md) | Check Azure OpenAI quota | -| [`.env.example`](../.env.example) | Environment variable reference | -| [FAQ](./faq.md) | Frequently asked questions | From cdf838cd65af086ddcb73631fe920bc54beb0761 Mon Sep 17 00:00:00 2001 From: Harmanpreet-Microsoft Date: Mon, 27 Apr 2026 14:44:55 +0530 Subject: [PATCH 4/4] remove: delete validate-prerequisites.ps1 script --- scripts/validate-prerequisites.ps1 | 331 ----------------------------- 1 file changed, 331 deletions(-) delete mode 100644 scripts/validate-prerequisites.ps1 diff --git a/scripts/validate-prerequisites.ps1 b/scripts/validate-prerequisites.ps1 deleted file mode 100644 index a1d51f8..0000000 --- a/scripts/validate-prerequisites.ps1 +++ /dev/null @@ -1,331 +0,0 @@ -<# -.SYNOPSIS - Validates local development prerequisites and deployment readiness. - -.DESCRIPTION - Checks that required tools are installed with compatible versions, - git submodules are initialized, Azure authentication is active, - and the azd environment is configured for deployment. - - Run this script before 'azd up' to catch common setup issues early. - -.EXAMPLE - pwsh ./scripts/validate-prerequisites.ps1 -#> - -param( - [switch]$SkipAzureChecks -) - -$ErrorActionPreference = 'Continue' - -$script:errors = @() -$script:warnings = @() - -function Write-Check { - param([string]$Name, [string]$Status, [string]$Detail) - switch ($Status) { - 'PASS' { Write-Host " [PASS] $Name" -ForegroundColor Green; if ($Detail) { Write-Host " $Detail" -ForegroundColor DarkGray } } - 'FAIL' { Write-Host " [FAIL] $Name" -ForegroundColor Red; if ($Detail) { Write-Host " $Detail" -ForegroundColor Red }; $script:errors += "$Name`: $Detail" } - 'WARN' { Write-Host " [WARN] $Name" -ForegroundColor Yellow; if ($Detail) { Write-Host " $Detail" -ForegroundColor Yellow }; $script:warnings += "$Name`: $Detail" } - 'SKIP' { Write-Host " [SKIP] $Name" -ForegroundColor DarkGray; if ($Detail) { Write-Host " $Detail" -ForegroundColor DarkGray } } - } -} - -function Test-VersionAtLeast { - param([string]$Current, [string]$Minimum) - try { - $cur = [System.Version]($Current -replace '[^0-9.]', '' -replace '^\.' -replace '\.$') - $min = [System.Version]($Minimum -replace '[^0-9.]', '' -replace '^\.' -replace '\.$') - return $cur -ge $min - } - catch { return $false } -} - -$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path - -Write-Host "" -Write-Host "============================================" -ForegroundColor Cyan -Write-Host " Local Development Prerequisites Validation" -ForegroundColor Cyan -Write-Host "============================================" -ForegroundColor Cyan -Write-Host "" - -# ------------------------------------------------------- -# Section 1: Required Tools -# ------------------------------------------------------- -Write-Host "1. Required Tools" -ForegroundColor White -Write-Host " ---------------" -ForegroundColor DarkGray - -# Git -$git = Get-Command git -ErrorAction SilentlyContinue -if ($git) { - $gitVersion = (git --version 2>$null) -replace 'git version\s*', '' - Write-Check "Git" "PASS" "Version: $gitVersion" -} -else { - Write-Check "Git" "FAIL" "Not found. Install from https://git-scm.com/downloads" -} - -# Azure CLI -$az = Get-Command az -ErrorAction SilentlyContinue -if ($az) { - $azVersionRaw = (az version 2>$null | ConvertFrom-Json).'azure-cli' - if (Test-VersionAtLeast $azVersionRaw '2.61.0') { - Write-Check "Azure CLI" "PASS" "Version: $azVersionRaw (>= 2.61.0)" - } - else { - Write-Check "Azure CLI" "FAIL" "Version $azVersionRaw found, but >= 2.61.0 is required. Update: https://learn.microsoft.com/cli/azure/install-azure-cli" - } -} -else { - Write-Check "Azure CLI" "FAIL" "Not found. Install from https://learn.microsoft.com/cli/azure/install-azure-cli" -} - -# Azure Developer CLI (azd) -$azd = Get-Command azd -ErrorAction SilentlyContinue -if ($azd) { - $azdVersionRaw = (azd version 2>$null) -replace '\(.*', '' | ForEach-Object { $_.Trim() } - if (Test-VersionAtLeast $azdVersionRaw '1.15.0') { - if ($azdVersionRaw -match '1\.23\.9') { - Write-Check "Azure Developer CLI (azd)" "FAIL" "Version $azdVersionRaw is known to be incompatible (excluded in azure.yaml). Please upgrade." - } - else { - Write-Check "Azure Developer CLI (azd)" "PASS" "Version: $azdVersionRaw (>= 1.15.0)" - } - } - else { - Write-Check "Azure Developer CLI (azd)" "FAIL" "Version $azdVersionRaw found, but >= 1.15.0 is required. Update: https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd" - } -} -else { - Write-Check "Azure Developer CLI (azd)" "FAIL" "Not found. Install from https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd" -} - -# PowerShell -$psVersion = $PSVersionTable.PSVersion -if ($psVersion.Major -ge 7) { - Write-Check "PowerShell" "PASS" "Version: $psVersion (>= 7.0)" -} -else { - Write-Check "PowerShell" "FAIL" "Version $psVersion found, but >= 7.0 is required. Install from https://learn.microsoft.com/powershell/scripting/install/installing-powershell" -} - -# Bicep CLI (check both standalone and az bicep) -$bicepVersion = $null -$bicep = Get-Command bicep -ErrorAction SilentlyContinue -if ($bicep) { - $bicepVersionRaw = (bicep --version 2>$null) -replace '.*Bicep CLI\s*', '' -replace '\s.*', '' - $bicepVersion = $bicepVersionRaw -} -if (-not $bicepVersion -and $az) { - try { - $azBicepRaw = (az bicep version 2>$null) - if ($azBicepRaw -match '(\d+\.\d+\.\d+)') { - $bicepVersion = $Matches[1] - } - } - catch {} -} -if ($bicepVersion) { - if (Test-VersionAtLeast $bicepVersion '0.33.0') { - Write-Check "Bicep CLI" "PASS" "Version: $bicepVersion (>= 0.33.0)" - } - else { - Write-Check "Bicep CLI" "FAIL" "Version $bicepVersion found, but >= 0.33.0 is required. Update: az bicep upgrade" - } -} -else { - Write-Check "Bicep CLI" "WARN" "Not detected (standalone or via az bicep). azd will auto-install if needed, but verify with: az bicep version" -} - -Write-Host "" - -# ------------------------------------------------------- -# Section 2: Repository State -# ------------------------------------------------------- -Write-Host "2. Repository State" -ForegroundColor White -Write-Host " ----------------" -ForegroundColor DarkGray - -# Git submodules -$submodulePath = Join-Path $repoRoot 'submodules' 'ai-landing-zone' -$submoduleGitExists = Test-Path (Join-Path $submodulePath '.git') -$submoduleBicepExists = Test-Path (Join-Path $submodulePath 'main.bicep') -if ($submoduleGitExists -or $submoduleBicepExists) { - Write-Check "Git submodules" "PASS" "ai-landing-zone submodule is initialized" -} -else { - Write-Check "Git submodules" "FAIL" "Submodule not initialized. Run: git submodule update --init --recursive" -} - -# Check for uncommitted .bicepparam changes -try { - Push-Location $repoRoot - $paramStatus = git status --porcelain -- 'infra/main.bicepparam' 2>$null - if ($paramStatus) { - Write-Check "Parameter file" "WARN" "infra/main.bicepparam has uncommitted changes — ensure your local overrides are intentional" - } - else { - Write-Check "Parameter file" "PASS" "infra/main.bicepparam is clean" - } - Pop-Location -} -catch { - Pop-Location - Write-Check "Parameter file" "SKIP" "Could not check git status" -} - -Write-Host "" - -# ------------------------------------------------------- -# Section 3: Azure & Deployment Readiness -# ------------------------------------------------------- -if ($SkipAzureChecks) { - Write-Host "3. Azure & Deployment Readiness (SKIPPED)" -ForegroundColor DarkGray - Write-Host "" -} -else { - Write-Host "3. Azure & Deployment Readiness" -ForegroundColor White - Write-Host " ----------------------------" -ForegroundColor DarkGray - - # Azure login - $loggedIn = $false - if ($az) { - try { - $account = az account show 2>$null | ConvertFrom-Json - if ($account) { - $loggedIn = $true - Write-Check "Azure login" "PASS" "Signed in as $($account.user.name) on subscription '$($account.name)'" - } - else { - Write-Check "Azure login" "FAIL" "Not logged in. Run: az login" - } - } - catch { - Write-Check "Azure login" "FAIL" "Not logged in. Run: az login && azd auth login" - } - } - else { - Write-Check "Azure login" "SKIP" "Azure CLI not installed" - } - - # azd environment - if ($azd) { - try { - $envListRaw = azd env list --output json 2>$null - if ($envListRaw) { - $envList = $envListRaw | ConvertFrom-Json - $defaultEnv = $envList | Where-Object { $_.IsDefault -eq $true } - if ($defaultEnv) { - Write-Check "azd environment" "PASS" "Active environment: $($defaultEnv.Name)" - - # Check critical env vars - $envValues = azd env get-values --output json 2>$null | ConvertFrom-Json - if ($envValues) { - if ($envValues.AZURE_LOCATION) { - Write-Check "AZURE_LOCATION" "PASS" "$($envValues.AZURE_LOCATION)" - } - else { - Write-Check "AZURE_LOCATION" "FAIL" "Not set. Run: azd env set AZURE_LOCATION " - } - if ($envValues.AZURE_SUBSCRIPTION_ID) { - # Cross-check with az account - if ($loggedIn -and $account -and $account.id -ne $envValues.AZURE_SUBSCRIPTION_ID) { - Write-Check "Subscription alignment" "WARN" "azd env targets $($envValues.AZURE_SUBSCRIPTION_ID) but az CLI is on $($account.id). Run: az account set --subscription $($envValues.AZURE_SUBSCRIPTION_ID)" - } - else { - Write-Check "Subscription alignment" "PASS" "azd env and az CLI target the same subscription" - } - } - else { - Write-Check "AZURE_SUBSCRIPTION_ID" "WARN" "Not explicitly set in azd env. azd will use the default az CLI subscription." - } - } - } - else { - Write-Check "azd environment" "WARN" "No default environment selected. Run: azd env new or azd env select " - } - } - else { - Write-Check "azd environment" "WARN" "No environments found. Run: azd env new " - } - } - catch { - Write-Check "azd environment" "WARN" "Could not query azd environments. You may need to run: azd env new " - } - } - else { - Write-Check "azd environment" "SKIP" "azd not installed" - } - - # Fabric / Purview feature readiness - Write-Host "" - Write-Host "4. Feature-Specific Readiness" -ForegroundColor White - Write-Host " --------------------------" -ForegroundColor DarkGray - - $bicepParamFile = Join-Path $repoRoot 'infra' 'main.bicepparam' - if (Test-Path $bicepParamFile) { - $paramContent = Get-Content $bicepParamFile -Raw - - # Check Fabric preset - if ($paramContent -match "fabricCapacityMode.*'create'") { - $fabricAdminsSet = $paramContent -match "fabricCapacityAdmins\s*=\s*\[(?!\s*\])" - if ($fabricAdminsSet) { - Write-Check "Fabric capacity (create mode)" "PASS" "fabricCapacityAdmins is configured" - } - else { - Write-Check "Fabric capacity (create mode)" "WARN" "fabricCapacityPreset='create' but fabricCapacityAdmins appears empty. Capacity creation will fail." - } - Write-Check "Fabric permissions" "WARN" "Ensure the deploying identity has Fabric Administrator role for workspace creation" - } - elseif ($paramContent -match "fabricCapacityMode.*'none'") { - Write-Check "Fabric capacity" "PASS" "Fabric is disabled (fabricCapacityPreset='none')" - } - else { - Write-Check "Fabric capacity" "PASS" "Fabric is set to BYO mode" - } - - # Check Purview - if ($paramContent -match "purviewAccountResourceId\s*=\s*'[^']+'") { - Write-Check "Purview integration" "PASS" "purviewAccountResourceId is set" - Write-Check "Purview permissions" "WARN" "Ensure the deploying identity has Purview Collection Admin on the target collection" - } - else { - Write-Check "Purview integration" "PASS" "Purview is not configured (steps will be skipped)" - } - - # Quota reminder - Write-Check "Azure OpenAI quota" "WARN" "Run the quota check before deploying: pwsh ./scripts/quota_check.ps1 or see docs/quota_check.md" - } - else { - Write-Check "Parameter file" "FAIL" "infra/main.bicepparam not found at expected path" - } -} - -# ------------------------------------------------------- -# Summary -# ------------------------------------------------------- -Write-Host "" -Write-Host "============================================" -ForegroundColor Cyan -Write-Host " Summary" -ForegroundColor Cyan -Write-Host "============================================" -ForegroundColor Cyan - -if ($script:errors.Count -eq 0 -and $script:warnings.Count -eq 0) { - Write-Host " All checks passed. You are ready to deploy!" -ForegroundColor Green -} -else { - if ($script:errors.Count -gt 0) { - Write-Host " $($script:errors.Count) error(s) found — fix these before deploying:" -ForegroundColor Red - foreach ($e in $script:errors) { Write-Host " - $e" -ForegroundColor Red } - } - if ($script:warnings.Count -gt 0) { - Write-Host " $($script:warnings.Count) warning(s) — review before deploying:" -ForegroundColor Yellow - foreach ($w in $script:warnings) { Write-Host " - $w" -ForegroundColor Yellow } - } -} - -Write-Host "" - -if ($script:errors.Count -gt 0) { - exit 1 -} -exit 0