Skip to content

Commit 1fbb362

Browse files
Merge pull request #575 from microsoft/psl-hb-us-41641
refactor: Migrate Content Understanding from preview to GA and consolidate AI Services account
2 parents 259610e + 8aa9a61 commit 1fbb362

14 files changed

Lines changed: 378 additions & 3595 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ jobs:
146146
--parameters \
147147
solutionName="${{ env.ENVIRONMENT_NAME }}" \
148148
enablePrivateNetworking="false" \
149-
contentUnderstandingLocation="WestUS" \
150149
deploymentType="GlobalStandard" \
151150
gptModelName="gpt-5.1" \
152151
gptModelVersion="2025-11-13" \

docs/CustomizingAzdParameters.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,15 @@ By default this template will use the environment name as the prefix to prevent
1111
| -------------------------------------- | ------- | --------------------------- | ------------------------------------------------------------------------------------- |
1212
| `AZURE_ENV_NAME` | string | `cps` | Sets the environment name prefix for all Azure resources (3-20 characters). |
1313
| `AZURE_LOCATION` | string | `eastus2` | Sets the primary Azure region for resource deployment. Allowed: `australiaeast`, `centralus`, `eastasia`, `eastus2`, `japaneast`, `northeurope`, `southeastasia`, `uksouth`. |
14-
| `AZURE_ENV_CU_LOCATION` | string | `WestUS` | Sets the location for the Azure AI Content Understanding service. Allowed: `WestUS`, `SwedenCentral`, `AustraliaEast`. |
15-
| `AZURE_ENV_AI_SERVICE_LOCATION` | string | `eastus` | Sets the location for Azure AI Services (OpenAI) deployment. |
16-
| `AZURE_ENV_MODEL_DEPLOYMENT_TYPE` | string | `GlobalStandard` | Defines the model deployment type. Allowed: `Standard`, `GlobalStandard`. |
14+
| `AZURE_ENV_AI_SERVICE_LOCATION` | string | `eastus2` | Sets the location for Azure AI Services. This single account hosts both Azure OpenAI and Content Understanding. Allowed: `australiaeast`, `eastus`, `eastus2`, `japaneast`, `southcentralus`, `southeastasia`, `swedencentral`, `uksouth`, `westeurope`, `westus`, `westus3`. |
15+
| `AZURE_ENV_MODEL_DEPLOYMENT_TYPE` | string | `GlobalStandard` | Defines the model deployment type. Allowed: `Standard`, `GlobalStandard`.<br>**Note:** the `azd` location-picker filters regions using the `usageName` metadata on `azureAiServiceLocation` in `infra/main.bicep` (currently `OpenAI.GlobalStandard.gpt-5.1,300`). If you set this parameter to `Standard`, also edit that metadata to `OpenAI.Standard.gpt-5.1,300` so the picker shows the correct subset of regions. |
1716
| `AZURE_ENV_GPT_MODEL_NAME` | string | `gpt-5.1` | Specifies the GPT model name. Default: `gpt-5.1`. |
1817
| `AZURE_ENV_GPT_MODEL_VERSION` | string | `2025-11-13` | Specifies the GPT model version. |
1918
| `AZURE_ENV_GPT_MODEL_CAPACITY` | integer | `300` | Sets the model capacity (minimum 1). Default: 300. Optimal: 500 for multi-document claim processing. |
2019
| `AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT` | string | `cpscontainerreg.azurecr.io` | Sets the public container image endpoint for pulling pre-built images. |
2120
| `AZURE_ENV_IMAGETAG` | string | `latest_v2` | Sets the container image tag (e.g., `latest_v2`, `dev`, `demo`, `hotfix`). |
22-
| `AZURE_ENV_EXISTING_LOG_ANALYTICS_WORKSPACE_RID` | string | Guide to get your [Existing Workspace Resource ID](/docs/re-use-log-analytics.md) | Reuses an existing Log Analytics Workspace instead of provisioning a new one. |
23-
| `AZURE_EXISTING_AIPROJECT_RESOURCE_ID` | string | Guide to get your [Existing AI Project Resource ID](/docs/re-use-foundry-project.md) | Reuses an existing AI Foundry and AI Foundry Project instead of creating a new one. |
21+
| `AZURE_ENV_EXISTING_LOG_ANALYTICS_WORKSPACE_RID` | string | Guide to get your [Existing Workspace Resource ID](re-use-log-analytics.md) | Reuses an existing Log Analytics Workspace instead of provisioning a new one. |
22+
| `AZURE_EXISTING_AIPROJECT_RESOURCE_ID` | string | Guide to get your [Existing AI Project Resource ID](re-use-foundry-project.md) | Reuses an existing AI Foundry and AI Foundry Project instead of creating a new one. |
2423
| `AZURE_ENV_VM_SIZE` | string | `Standard_D2s_v5` | Overrides the jumpbox VM size (private networking only). Default: `Standard_D2s_v5`. |
2524

2625
## How to Set a Parameter

docs/LocalDevelopmentSetup.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Example resource names from deployment:
160160
- App Configuration: `appcs-{suffix}.azconfig.io`
161161
- Cosmos DB: `cosmos-{suffix}.documents.azure.com`
162162
- Storage Account: `st{suffix}.queue.core.windows.net`
163-
- Content Understanding: `aicu-{suffix}.cognitiveservices.azure.com`
163+
- Content Understanding: `aif-{suffix}.cognitiveservices.azure.com`
164164

165165
### Required Azure RBAC Permissions
166166

docs/TroubleShootingSteps.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ Use these as quick reference guides to unblock your deployments.
128128
| **RouteTableCannotBeAttachedForAzureBastionSubnet** | Route table attached to Azure Bastion subnet | This error occurs because Azure Bastion subnet (`AzureBastionSubnet`) has a platform restriction that prevents route tables from being attached.<br><br>**How to reproduce:**<br><ul><li>In `virtualNetwork.bicep`, add `attachRouteTable: true` to the `AzureBastionSubnet` configuration:<br>`{ name: 'AzureBastionSubnet', addressPrefixes: ['10.0.10.0/26'], attachRouteTable: true }`</li><li>Add a Route Table module to the template</li><li>Update subnet creation to attach route table conditionally:<br>`routeTableResourceId: subnet.?attachRouteTable == true ? routeTable.outputs.resourceId : null`</li><li>Deploy the template → Azure throws `RouteTableCannotBeAttachedForAzureBastionSubnet`</li></ul><br>**Resolution:**<br><ul><li>Remove the `attachRouteTable: true` flag from `AzureBastionSubnet` configuration</li><li>Ensure no route table is associated with `AzureBastionSubnet`</li><li>Route tables can only be attached to other subnets, not `AzureBastionSubnet`</li><li>For more details, refer to [Azure Bastion subnet requirements](https://learn.microsoft.com/en-us/azure/bastion/configuration-settings#subnet)</li></ul> |
129129
| **VMSizeIsNotPermittedToEnableAcceleratedNetworking** | VM size does not support accelerated networking | This error occurs when you attempt to enable accelerated networking on a VM size that does not support it. This deployment's jumpbox VM **requires** accelerated networking.<br><br>**Default VM size:** `Standard_D2s_v5` — supports accelerated networking.<br><br>**How this error happens:**<br><ul><li>You override the VM size (via `AZURE_ENV_VM_SIZE`) with a size that doesn't support accelerated networking (e.g., `Standard_A2m_v2`, A-series, or B-series VMs)</li><li>Azure rejects the deployment with `VMSizeIsNotPermittedToEnableAcceleratedNetworking`</li></ul><br>**Resolution:**<br><ul><li>Use the default `Standard_D2s_v5` (recommended)</li><li>If overriding VM size, choose one that supports accelerated networking:<br>`Standard_D2s_v4`, `Standard_D2as_v5` (AMD), `Standard_D2s_v3`</li><li>Verify VM size supports accelerated networking:<br>`az vm list-skus --location <region> --size <vm-size> --query "[?capabilities[?name=='AcceleratedNetworkingEnabled' && value=='True']]"`</li><li>Avoid A-series and B-series VMs — they do not support accelerated networking</li><li>See [VM sizes with accelerated networking](https://learn.microsoft.com/en-us/azure/virtual-network/accelerated-networking-overview)</li></ul> |
130130
| **NetworkSecurityGroupNotCompliantForAzureBastionSubnet** / **SecurityRuleParameterContainsUnsupportedValue** | NSG rules blocking required Azure Bastion ports | This error occurs when the Network Security Group (NSG) attached to `AzureBastionSubnet` explicitly denies inbound TCP ports 443 and/or 4443, which Azure Bastion requires for management and tunneling.<br><br>**How to reproduce:**<br><ul><li>Deploy the template with `enablePrivateNetworking=true` so the virtualNetwork module creates `AzureBastionSubnet` and a Network Security Group that denies ports 443 and 4443</li><li>Attempt to deploy Azure Bastion into that subnet</li><li>During validation, Bastion detects the deny rules and fails with `NetworkSecurityGroupNotCompliantForAzureBastionSubnet`</li></ul><br>**Resolution:**<br><ul><li>**Remove or modify deny rules** for ports 443 and 4443 in the NSG attached to `AzureBastionSubnet`</li><li>**Ensure required inbound rules** per [Azure Bastion NSG requirements](https://learn.microsoft.com/en-us/azure/bastion/bastion-nsg)</li><li>**Use Bicep conditions** to skip NSG attachments for `AzureBastionSubnet` if deploying Bastion</li><li>**Validate the NSG configuration** before deploying Bastion into the subnet</li></ul> |
131-
| **403 Forbidden - Content Understanding** | Azure AI Content Understanding returns 403 Forbidden in WAF (private networking) deployment | This error occurs when the **Azure AI Content Understanding** service returns a `403 Forbidden` response during document processing in a **WAF-enabled (private networking)** deployment.<br><br>**Why this happens:**<br>In WAF deployments (`enablePrivateNetworking=true`), the Content Understanding AI Services account (`aicu-<suffix>`) is configured with `publicNetworkAccess: Disabled`. All traffic must flow through the **private endpoint** (`pep-aicu-<suffix>`) and resolve via private DNS zones (`privatelink.cognitiveservices.azure.com`, `privatelink.services.ai.azure.com`, `privatelink.contentunderstanding.ai.azure.com`). If any part of this chain is misconfigured, the request either reaches the public endpoint (which is blocked) or fails to route entirely, resulting in a 403.<br><br>**Common causes:**<br><ul><li>Private DNS zones not linked to the VNet — DNS resolution falls back to the public IP, which is blocked</li><li>Private endpoint connection is not in **Approved** state</li><li>Content Understanding is deployed in a different region (`contentUnderstandingLocation`, defaults to `WestUS`) than the main deployment — the private endpoint still works cross-region, but DNS misconfiguration is more likely</li><li>Container Apps are not injected into the VNet or are on a subnet that cannot reach the private endpoint</li><li>Managed Identity used by the Container App does not have the required **Cognitive Services User** role on the Content Understanding resource</li></ul><br>**Resolution:**<br><ul><li>**Verify private endpoint status:**<br>`az network private-endpoint show --name pep-aicu-<suffix> --resource-group <rg-name> --query "privateLinkServiceConnections[0].privateLinkServiceConnectionState.status"`<br>Expected: `Approved`</li><li>**Verify private DNS zone VNet links:**<br>`az network private-dns zone list --resource-group <rg-name> -o table`<br>Ensure `privatelink.cognitiveservices.azure.com`, `privatelink.services.ai.azure.com`, and `privatelink.contentunderstanding.ai.azure.com` all have VNet links</li><li>**Test DNS resolution from the jumpbox VM** (inside the VNet):<br>`nslookup aicu-<suffix>.cognitiveservices.azure.com`<br>Should resolve to a private IP (e.g., `10.x.x.x`), NOT a public IP</li><li>**Verify RBAC role assignments:** Ensure the Container App managed identity has **Cognitive Services User** role on the Content Understanding resource:<br>`az role assignment list --scope /subscriptions/<sub-id>/resourceGroups/<rg-name>/providers/Microsoft.CognitiveServices/accounts/aicu-<suffix> --query "[?roleDefinitionName=='Cognitive Services User']" -o table`</li><li>**Check Container App VNet integration:** Confirm the Container App Environment is deployed into the VNet and can reach the backend subnet where the private endpoint resides</li><li>**Redeploy if needed:**<br>`azd up`</li></ul><br>**Reference:**<br><ul><li>[Configure private endpoints for Azure AI Services](https://learn.microsoft.com/en-us/azure/ai-services/cognitive-services-virtual-networks)</li><li>[Azure Private DNS zones](https://learn.microsoft.com/en-us/azure/dns/private-dns-overview)</li></ul> |
131+
| **403 Forbidden - Content Understanding** | Azure AI Content Understanding returns 403 Forbidden in WAF (private networking) deployment | This error occurs when the **Azure AI Content Understanding** API on the unified AI Services account (`aif-<suffix>`) returns a `403 Forbidden` response during document processing in a **WAF-enabled (private networking)** deployment.<br><br>**Why this happens:**<br>As of the CU GA migration, Content Understanding shares the same Azure AI Services account as Azure OpenAI (`aif-<suffix>`). In WAF deployments (`enablePrivateNetworking=true`), that account is configured with `publicNetworkAccess: Disabled`. All traffic must flow through the unified private endpoint (`pep-aiservices-<suffix>`) and resolve via four private DNS zones: `privatelink.cognitiveservices.azure.com`, `privatelink.openai.azure.com`, `privatelink.services.ai.azure.com`, and `privatelink.contentunderstanding.ai.azure.com`. If any link in this chain is misconfigured, the request either reaches the public endpoint (blocked) or fails to route, resulting in a 403.<br><br>**Common causes:**<br><ul><li>Private DNS zones not linked to the VNet — DNS resolution falls back to the public IP, which is blocked</li><li>Private endpoint connection is not in **Approved** state</li><li>Container Apps are not injected into the VNet or are on a subnet that cannot reach the private endpoint</li><li>Managed Identity used by the Container App does not have the required **Cognitive Services User** role on the unified AI Services account</li><li>Reusing an existing AI Foundry project (`existingFoundryProjectResourceId`): the repo no longer creates a CU-specific PE; the existing account must have its own private endpoint covering the four DNS zones above</li></ul><br>**Resolution:**<br><ul><li>**Verify private endpoint status:**<br>`az network private-endpoint show --name pep-aiservices-<suffix> --resource-group <rg-name> --query "privateLinkServiceConnections[0].privateLinkServiceConnectionState.status"`<br>Expected: `Approved`</li><li>**Verify private DNS zone VNet links:**<br>`az network private-dns zone list --resource-group <rg-name> -o table`<br>Ensure `privatelink.cognitiveservices.azure.com`, `privatelink.openai.azure.com`, `privatelink.services.ai.azure.com`, and `privatelink.contentunderstanding.ai.azure.com` all have VNet links</li><li>**Test DNS resolution from the jumpbox VM** (inside the VNet):<br>`nslookup aif-<suffix>.cognitiveservices.azure.com`<br>Should resolve to a private IP (e.g., `10.x.x.x`), NOT a public IP</li><li>**Verify RBAC role assignments:** ensure the Container App managed identity has **Cognitive Services User** role on the unified account:<br>`az role assignment list --scope /subscriptions/<sub-id>/resourceGroups/<rg-name>/providers/Microsoft.CognitiveServices/accounts/aif-<suffix> --query "[?roleDefinitionName=='Cognitive Services User']" -o table`</li><li>**Check Container App VNet integration:** confirm the Container App Environment is deployed into the VNet and can reach the backend subnet where the private endpoint resides</li><li>**Redeploy if needed:**<br>`azd up`</li></ul><br>**Reference:**<br><ul><li>[Configure private endpoints for Azure AI Services](https://learn.microsoft.com/en-us/azure/ai-services/cognitive-services-virtual-networks)</li><li>[Azure Private DNS zones](https://learn.microsoft.com/en-us/azure/dns/private-dns-overview)</li></ul> |
132132

133133
---------------------------------
134134

docs/re-use-foundry-project.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
[← Back to *DEPLOYMENT* guide](/docs/DeploymentGuide.md#deployment-options--steps)
1+
[← Back to *DEPLOYMENT* guide](./DeploymentGuide.md#deployment-options--steps)
22

33
# Reusing an Existing Azure AI Foundry Project
44
To configure your environment to use an existing Azure AI Foundry Project, follow these steps:
5+
6+
> **⚠️ Region requirement**
7+
>
8+
> The existing Foundry project must reside in a region that supports **both** the GPT model deployed by this accelerator (default `gpt-5.1` with `GlobalStandard` deployment type) **and** Azure AI Content Understanding (GA).<br>
9+
> Supported regions: `australiaeast`, `eastus`, `eastus2`, `japaneast`, `southcentralus`, `southeastasia`, `swedencentral`, `uksouth`, `westeurope`, `westus`, `westus3`.<br>
10+
> If the existing project is in a different region, deployment will fail or the application will not work correctly.
11+
512
---
613
### 1. Go to Azure Portal
714
Go to https://portal.azure.com
@@ -41,4 +48,4 @@ azd env set AZURE_EXISTING_AIPROJECT_RESOURCE_ID '<Existing Foundry Project Reso
4148
Replace `<Existing Foundry Project Resource ID>` with the value obtained from Step 5.
4249

4350
### 7. Continue Deployment
44-
Proceed with the next steps in the [deployment guide](/docs/DeploymentGuide.md#deployment-options--steps).
51+
Proceed with the next steps in the [deployment guide](./DeploymentGuide.md#deployment-options--steps).

0 commit comments

Comments
 (0)