Skip to content

Commit 1247ab6

Browse files
Merge branch 'dev' into psl-smoketesting
2 parents 6a20bde + 66f6c82 commit 1247ab6

45 files changed

Lines changed: 2320 additions & 125 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: AZD Template Validation
2+
on:
3+
schedule:
4+
- cron: '30 1 * * 4' # Every Thursday at 7:00 AM IST (1:30 AM UTC)
5+
workflow_dispatch:
6+
7+
permissions:
8+
contents: read
9+
id-token: write
10+
pull-requests: write
11+
12+
jobs:
13+
template_validation:
14+
runs-on: ubuntu-latest
15+
name: azd template validation
16+
environment: production
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Set timestamp
21+
run: echo "HHMM=$(date -u +'%H%M')" >> $GITHUB_ENV
22+
23+
- uses: microsoft/template-validation-action@v0.4.3
24+
with:
25+
validateAzd: ${{ vars.TEMPLATE_VALIDATE_AZD }}
26+
validateTests: ${{ vars.TEMPLATE_VALIDATE_TESTS }}
27+
useDevContainer: ${{ vars.TEMPLATE_USE_DEV_CONTAINER }}
28+
id: validation
29+
env:
30+
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
31+
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
32+
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
33+
AZURE_ENV_NAME: azd-${{ vars.AZURE_ENV_NAME }}-${{ env.HHMM }}
34+
AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}
35+
AZURE_ENV_AI_SERVICE_LOCATION: ${{ vars.AZURE_LOCATION }}
36+
AZURE_ENV_MODEL_CAPACITY: 1 # keep low to avoid potential quota issues
37+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38+
39+
- name: print result
40+
run: cat ${{ steps.validation.outputs.resultFile }}

.github/workflows/azure-dev.yaml

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,57 @@
1-
name: Azure Template Validation
1+
name: Azure Dev Deploy
2+
23
on:
34
workflow_dispatch:
45

56
permissions:
67
contents: read
78
id-token: write
8-
pull-requests: write
99

1010
jobs:
11-
template_validation_job:
12-
environment: production
11+
deploy:
1312
runs-on: ubuntu-latest
14-
name: Template validation
15-
13+
environment: production
14+
env:
15+
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
16+
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
17+
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
18+
AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }}
19+
AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}
20+
AZURE_ENV_MODEL_CAPACITY: 1 # keep low to avoid potential quota issues
21+
AZURE_DEV_COLLECT_TELEMETRY: ${{ vars.AZURE_DEV_COLLECT_TELEMETRY }}
1622
steps:
17-
# Step 1: Checkout the code from your repository
18-
- name: Checkout code
19-
uses: actions/checkout@v5
20-
21-
# Step 2: Validate the Azure template using microsoft/template-validation-action
22-
- name: Validate Azure Template
23-
uses: microsoft/template-validation-action@v0.4.3
24-
id: validation
23+
- name: Checkout Code
24+
uses: actions/checkout@v4
25+
26+
- name: Set timestamp and env name
27+
run: |
28+
HHMM=$(date -u +'%H%M')
29+
echo "AZURE_ENV_NAME=azd-${{ vars.AZURE_ENV_NAME }}-${HHMM}" >> $GITHUB_ENV
30+
31+
- name: Install azd
32+
uses: Azure/setup-azd@v2
33+
34+
- name: Login to Azure
35+
uses: azure/login@v2
2536
with:
26-
useDevContainer: false
27-
env:
28-
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
29-
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
30-
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
31-
AZURE_ENV_NAME: ${{ secrets.AZURE_ENV_NAME }}
32-
AZURE_LOCATION: ${{ secrets.AZURE_LOCATION }}
33-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34-
AZURE_DEV_COLLECT_TELEMETRY: ${{ vars.AZURE_DEV_COLLECT_TELEMETRY }}
35-
36-
# Step 3: Print the result of the validation
37-
- name: Print result
38-
run: cat ${{ steps.validation.outputs.resultFile }}
37+
client-id: ${{ secrets.AZURE_CLIENT_ID }}
38+
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
39+
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
40+
41+
- name: Login to AZD
42+
shell: bash
43+
run: |
44+
azd auth login \
45+
--client-id "$AZURE_CLIENT_ID" \
46+
--federated-credential-provider "github" \
47+
--tenant-id "$AZURE_TENANT_ID"
48+
49+
- name: Provision and Deploy
50+
shell: bash
51+
run: |
52+
if ! azd env select "$AZURE_ENV_NAME"; then
53+
azd env new "$AZURE_ENV_NAME" --subscription "$AZURE_SUBSCRIPTION_ID" --location "$AZURE_LOCATION" --no-prompt
54+
fi
55+
azd config set defaults.subscription "$AZURE_SUBSCRIPTION_ID"
56+
azd env set AZURE_ENV_AI_SERVICE_LOCATION="$AZURE_LOCATION"
57+
azd up --no-prompt

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,8 @@ Follow the quick deploy steps on the deployment guide to deploy this solution
278278

279279
<br/>
280280

281+
> **Note**: Some tenants may have additional security restrictions that run periodically and could impact the application (e.g., blocking public network access). If you experience issues or the application stops working, check if these restrictions are the cause. In such cases, consider deploying the WAF-supported version to ensure compliance. To configure, [Click here](./docs/DeploymentGuide.md#31-choose-deployment-type-optional).
282+
281283
> ⚠️ **Important: Check Azure OpenAI Quota Availability**
282284
<br/>To ensure sufficient quota is available in your subscription, please follow [quota check instructions guide](./docs/quota_check.md) before you deploy the solution.
283285

azure.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ name: content-processing
55

66
requiredVersions:
77
azd: '>= 1.18.0 != 1.23.9'
8+
bicep: '>= 0.33.0'
89

910
metadata:
1011
template: content-processing@1.0

azure_custom.yaml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
2+
# Custom AZD configuration for Content Processing Solution Accelerator.
3+
# Use this file to build and deploy your own modified code using AZD.
4+
# This file works with infra/main_custom.bicep which uses placeholder container images
5+
# that AZD replaces with your custom-built images from source.
6+
#
7+
# Usage:
8+
# 1. Copy this file to azure.yaml (or rename it)
9+
# 2. Ensure infra/main_custom.bicep is referenced (rename to main.bicep or update infra path)
10+
# 3. Run: azd up
11+
#
12+
# For more information, see the Deployment Guide in docs/DeploymentGuide.md
13+
name: content-processing
14+
15+
requiredVersions:
16+
azd: '>= 1.18.0 != 1.23.9'
17+
18+
metadata:
19+
template: content-processing@1.0
20+
name: content-processing@1.0
21+
22+
# infra:
23+
# path: infra
24+
# module: main_custom
25+
26+
services:
27+
contentprocessor:
28+
project: ./src/ContentProcessor
29+
language: py
30+
host: containerapp
31+
docker:
32+
path: ./Dockerfile
33+
image: contentprocessor
34+
registry: ${AZURE_CONTAINER_REGISTRY_ENDPOINT}
35+
remoteBuild: true
36+
37+
contentprocessorapi:
38+
project: ./src/ContentProcessorAPI
39+
language: py
40+
host: containerapp
41+
docker:
42+
path: ./Dockerfile
43+
image: contentprocessorapi
44+
registry: ${AZURE_CONTAINER_REGISTRY_ENDPOINT}
45+
remoteBuild: true
46+
47+
contentprocessorweb:
48+
project: ./src/ContentProcessorWeb
49+
language: js
50+
host: containerapp
51+
docker:
52+
path: ./Dockerfile
53+
image: contentprocessorweb
54+
registry: ${AZURE_CONTAINER_REGISTRY_ENDPOINT}
55+
remoteBuild: true
56+
57+
contentprocessorworkflow:
58+
project: ./src/ContentProcessorWorkflow
59+
language: py
60+
host: containerapp
61+
docker:
62+
path: ./Dockerfile
63+
image: contentprocessorworkflow
64+
registry: ${AZURE_CONTAINER_REGISTRY_ENDPOINT}
65+
remoteBuild: true
66+
67+
hooks:
68+
postprovision:
69+
posix:
70+
shell: sh
71+
run: sed -i 's/\r$//' ./infra/scripts/post_deployment.sh; bash ./infra/scripts/post_deployment.sh
72+
interactive: true
73+
windows:
74+
shell: pwsh
75+
run: ./infra/scripts/post_deployment.ps1
76+
interactive: true

docs/DeploymentGuide.md

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ This guide walks you through deploying the Content Processing Solution Accelerat
66

77
🆘 **Need Help?** If you encounter any issues during deployment, check our [Troubleshooting Guide](./TroubleShootingSteps.md) for solutions to common problems.
88

9+
> **Note**: Some tenants may have additional security restrictions that run periodically and could impact the application (e.g., blocking public network access). If you experience issues or the application stops working, check if these restrictions are the cause. In such cases, consider deploying the WAF-supported version to ensure compliance. To configure, [Click here](#31-choose-deployment-type-optional).
10+
911
## Step 1: Prerequisites & Setup
1012

1113
### 1.1 Azure Account Requirements
@@ -506,34 +508,29 @@ Now that your deployment is complete and tested, explore these resources:
506508
507509
---
508510
509-
## Advanced: Deploy Local Code Changes
511+
## Advanced: Deploy Local Changes
510512
511-
Use this method to quickly deploy code changes from your local machine to your existing Azure deployment without re-provisioning infrastructure.
513+
If you've made local modifications to the code and want to deploy them to Azure, follow these steps to swap the configuration files:
512514
513515
> **Note:** To set up and run the application locally for development, see the [Local Development Setup Guide](./LocalDevelopmentSetup.md).
514516
515-
### How it Works
516-
This process will:
517-
1. Rebuild the Docker containers locally using your modified source code.
518-
2. Push the new images to your Azure Container Registry (ACR).
519-
3. Restart the Azure Container Apps to pick up the new images.
517+
### Step 1: Rename Azure Configuration Files
520518
521-
### Prerequisites
522-
- **Docker Desktop** must be installed and running.
523-
- You must have an active deployment environment selected (`azd env select <env-name>`).
519+
**In the root directory:**
520+
1. Rename `azure.yaml` to `azure_custom2.yaml`
521+
2. Rename `azure_custom.yaml` to `azure.yaml`
524522
525-
### Deployment Steps
523+
### Step 2: Rename Infrastructure Files
526524
527-
Run the build and push script for your operating system:
525+
**In the `infra` directory:**
526+
1. Rename `main.bicep` to `main_custom2.bicep`
527+
2. Rename `main_custom.bicep` to `main.bicep`
528528
529-
**Linux/macOS:**
530-
```bash
531-
./infra/scripts/docker-build.sh
532-
```
529+
### Step 3: Deploy Changes
533530
534-
**Windows (PowerShell):**
535-
```powershell
536-
./infra/scripts/docker-build.ps1
531+
Run the deployment command:
532+
```shell
533+
azd up
537534
```
538535
539-
> **Note:** These scripts will deploy your local code changes instead of pulling from the GitHub repository.
536+
> **Note:** These custom files are configured to deploy your local code changes instead of pulling from the GitHub repository.

docs/TroubleShootingSteps.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Use these as quick reference guides to unblock your deployments.
2727
| **InternalSubscriptionIsOverQuotaForSku** | Subscription quota exceeded for the requested SKU | [View Solution](#quota--capacity-limitations) |
2828
| **InvalidResourceGroup** | Invalid resource group configuration | [View Solution](#resource-group--deployment-management) |
2929
| **RequestDisallowedByPolicy** | Azure Policy blocking the requested operation | [View Solution](#subscription--access-issues) |
30+
| **403 Forbidden - Content Understanding** | Content Understanding returns 403 in WAF/private networking deployment | [View Solution](#network--infrastructure-configuration) |
3031

3132
## 📖 Table of Contents
3233

@@ -127,6 +128,7 @@ Use these as quick reference guides to unblock your deployments.
127128
| **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> |
128129
| **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> |
129130
| **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> |
130132

131133
---------------------------------
132134

infra/main.bicep

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,5 +1890,8 @@ output CONTAINER_REGISTRY_NAME string = avmContainerRegistry.outputs.name
18901890
@description('The login server of the Azure Container Registry.')
18911891
output CONTAINER_REGISTRY_LOGIN_SERVER string = avmContainerRegistry.outputs.loginServer
18921892

1893+
@description('The name of the Content Understanding AI Services account.')
1894+
output CONTENT_UNDERSTANDING_ACCOUNT_NAME string = avmAiServices_cu.outputs.name
1895+
18931896
@description('The resource group the resources were deployed into.')
18941897
output AZURE_RESOURCE_GROUP string = resourceGroup().name

0 commit comments

Comments
 (0)