diff --git a/deployers/azure.yaml b/deployers/azure.yaml index 90a40cc9..4ab51aa9 100644 --- a/deployers/azure.yaml +++ b/deployers/azure.yaml @@ -4,21 +4,37 @@ metadata: infra: provider: bicep path: bicep +services: + web: + project: ../application/single_app + language: python + host: appservice + docker: + context: ../../ + dockerfile: application/single_app/Dockerfile hooks: postprovision: + # this is run after the infrastructure has been provisioned but before services are deployed. + # primary use is to configure application settings and permissions posix: shell: sh run: | + set -e + + echo "========================================" + echo "POST-PROVISION: Starting configuration" + echo "========================================" + # Set up variables - + export var_acrName=${var_acrname} export var_configureApplication=${var_configureApplication} export var_cosmosDb_uri=${var_cosmosDb_uri} + export var_cosmosDb_accountName=${var_cosmosDb_accountName} export var_subscriptionId=${AZURE_SUBSCRIPTION_ID} export var_rgName=${var_rgName} export var_keyVaultUri=${var_keyVaultUri} - + export var_keyVaultName=${var_keyVaultName} export var_authenticationType=${var_authenticationType} - export var_openAIEndpoint=${var_openAIEndpoint} export var_openAIResourceGroup=${var_openAIResourceGroup} export var_openAIGPTModel=${var_openAIGPTModel} @@ -27,6 +43,7 @@ hooks: export var_contentSafetyEndpoint=${var_contentSafetyEndpoint} export var_searchServiceEndpoint=${var_searchServiceEndpoint} export var_documentIntelligenceServiceEndpoint=${var_documentIntelligenceServiceEndpoint} + export var_redisCacheHostName=${var_redisCacheHostName} export var_videoIndexerName=${var_videoIndexerName} export var_deploymentLocation=${var_deploymentLocation} export var_videoIndexerAccountId=${var_videoIndexerAccountId} @@ -34,43 +51,219 @@ hooks: # Execute post-configuration script if enabled if [ "${var_configureApplication}" = "true" ]; then - echo "Grant permissions to CosmosDB for post deployment steps..." - bash ./bicep/cosmosDb-postDeployPerms.sh - echo "Running post-deployment configuration..." - python3 -m pip install --user -r ./bicep/requirements.txt - python3 ./bicep/postconfig.py - echo "Post-deployment configuration completed." - echo "Restarting web service to apply new settings..." - az webapp restart --name ${var_webService} --resource-group ${var_rgName} - echo "Web service restarted." + echo "" + echo "[1/4] Granting permissions to CosmosDB..." + if bash ./bicep/cosmosDb-postDeployPerms.sh; then + echo "✓ CosmosDB permissions granted successfully" + else + echo "✗ ERROR: Failed to grant CosmosDB permissions" >&2 + exit 1 + fi + + echo "" + echo "[2/4] Installing Python dependencies..." + if python3 -m pip install --user -r ./bicep/requirements.txt > /dev/null 2>&1; then + echo "✓ Dependencies installed successfully" + else + echo "✗ ERROR: Failed to install Python dependencies" >&2 + exit 1 + fi + + echo "" + echo "[3/4] Running post-deployment configuration..." + if python3 ./bicep/postconfig.py; then + echo "✓ Post-deployment configuration completed" + else + echo "✗ ERROR: Post-deployment configuration failed" >&2 + exit 1 + fi + + echo "" + echo "[4/4] Restarting web service to apply settings..." + if az webapp restart --name ${var_webService} --resource-group ${var_rgName}; then + echo "✓ Web service restarted successfully" + else + echo "✗ ERROR: Failed to restart web service" >&2 + exit 1 + fi + + echo "" + echo "========================================" + echo "POST-PROVISION: Completed successfully" + echo "========================================" else - echo "Skipping post-deployment configuration (var_configureApplication is not true)" + echo "" + echo "ℹ Skipping post-deployment configuration (var_configureApplication is not true)" + echo "" + echo "========================================" + echo "POST-PROVISION: Completed (skipped)" + echo "========================================" fi predeploy: + # this is run after infrastructure and postprovisioning but before service deployment + # primary use is to build and push container images posix: shell: sh run: | - # Build and push Docker image to ACR + set -e + + # Error handling function + cleanup_on_error() { + local exit_code=$? + echo "" + echo "✗ ERROR: Deployment failed at step: $1" >&2 + echo "Attempting to restart web service..." >&2 + az webapp start --name ${var_webService} --resource-group ${var_rgName} 2>/dev/null || true + exit ${exit_code} + } + + echo "========================================" + echo "PRE-DEPLOY: Building and pushing image" + echo "========================================" + cd .. timestamp="$(date +"%Y%m%d-%H%M%S")" - echo "Stopping web service prior to deployment..." - az webapp stop --name ${var_webService} --resource-group ${var_rgName} - echo "Building Docker image..." - docker build -f application/single_app/Dockerfile -t ${var_containerRegistry}/${var_imageName}:${timestamp} . - docker tag ${var_containerRegistry}/${var_imageName}:${timestamp} ${var_containerRegistry}/${var_imageName}:latest - echo "Logging in to ACR..." - az acr login --name ${var_acrName} - echo "Pushing image to ACR..." - docker push ${var_containerRegistry}/${var_imageName}:latest - docker push ${var_containerRegistry}/${var_imageName}:${timestamp} - echo "Restarting web service..." - az webapp start --name ${var_webService} --resource-group ${var_rgName} -services: - web: - project: ../application/single_app - language: python - host: appservice - docker: - context: ../../ - dockerfile: application/single_app/Dockerfile \ No newline at end of file + echo "" + echo "Deployment timestamp: ${timestamp}" + echo "Image: ${var_containerRegistry}/${var_imageName}:${timestamp}" + + echo "" + echo "[1/6] Stopping web service..." + if az webapp stop --name ${var_webService} --resource-group ${var_rgName}; then + echo "✓ Web service stopped successfully" + else + echo "✗ ERROR: Failed to stop web service" >&2 + exit 1 + fi + + echo "" + echo "[2/6] Building Docker image..." + echo "Context: $(pwd)" + echo "Dockerfile: application/single_app/Dockerfile" + if docker build -f application/single_app/Dockerfile \ + -t ${var_containerRegistry}/${var_imageName}:${timestamp} . ; then + echo "✓ Docker image built successfully" + else + cleanup_on_error "Docker build" + fi + + echo "" + echo "[3/6] Tagging image as latest..." + if docker tag ${var_containerRegistry}/${var_imageName}:${timestamp} \ + ${var_containerRegistry}/${var_imageName}:latest ; then + echo "✓ Image tagged successfully" + else + cleanup_on_error "Docker tag" + fi + + echo "" + echo "[4/6] Logging in to ACR (${var_acrName})..." + if az acr login --name ${var_acrName}; then + echo "✓ ACR login successful" + else + cleanup_on_error "ACR login" + fi + + echo "" + echo "[5/6] Pushing images to ACR..." + echo " → Pushing latest tag..." + if docker push ${var_containerRegistry}/${var_imageName}:latest; then + echo " ✓ Latest tag pushed successfully" + else + cleanup_on_error "Docker push (latest)" + fi + + echo " → Pushing timestamped tag..." + if docker push ${var_containerRegistry}/${var_imageName}:${timestamp}; then + echo " ✓ Timestamped tag pushed successfully" + else + cleanup_on_error "Docker push (timestamp)" + fi + + echo "" + echo "[6/6] Restarting web service..." + if az webapp start --name ${var_webService} --resource-group ${var_rgName}; then + echo "✓ Web service restarted successfully" + else + echo "✗ ERROR: Failed to restart web service" >&2 + exit 1 + fi + + echo "" + echo "========================================" + echo "PRE-DEPLOY: Completed successfully" + echo "========================================" + + postup: + # this is the final step to run after everything else is done + # primary use is disable public network access if private endpoints are used + posix: + shell: sh + run: | + set -e + + echo "========================================" + echo "POST-UP: Final configuration" + echo "========================================" + + if [ "${var_enablePrivateNetworking}" = "true" ]; then + echo "" + echo "Configuring private networking..." + + echo "" + echo "[1/4] Disabling public network access for CosmosDB..." + if az cosmosdb update --name ${var_cosmosDb_accountName} \ + --resource-group ${var_rgName} \ + --public-network-access Disabled > /dev/null; then + echo "✓ CosmosDB public access disabled" + else + echo "✗ ERROR: Failed to disable CosmosDB public access" >&2 + exit 1 + fi + + echo "" + echo "[2/4] Disabling public network access for Key Vault..." + if az keyvault update --name ${var_keyVaultName} \ + --resource-group ${var_rgName} \ + --public-network-access Disabled > /dev/null; then + echo "✓ Key Vault public access disabled" + else + echo "✗ ERROR: Failed to disable Key Vault public access" >&2 + exit 1 + fi + + echo "" + echo "[3/4] Disabling public network access for Azure Container Registry..." + if az acr update --name ${var_acrName} \ + --resource-group ${var_rgName} \ + --public-network-enabled false > /dev/null; then + echo "✓ ACR public access disabled" + else + echo "✗ ERROR: Failed to disable ACR public access" >&2 + exit 1 + fi + + echo "" + echo "[4/4] Disabling public network access for Web Application..." + if az resource update --name ${var_webService} \ + --resource-group ${var_rgName} \ + --resource-type "Microsoft.Web/sites" \ + --set properties.publicNetworkAccess=Disabled; then + echo "✓ Web Application public access disabled" + else + echo "✗ ERROR: Failed to disable Web Application public access" >&2 + exit 1 + fi + + echo "" + echo "✓ Private networking configured successfully" + else + echo "" + echo "ℹ Skipping private networking configuration (var_enablePrivateNetworking is not true)" + fi + + echo "" + echo "========================================" + echo "✓ DEPLOYMENT COMPLETED SUCCESSFULLY" + echo "========================================" \ No newline at end of file diff --git a/deployers/azurecli/appRegistrationRoles.json b/deployers/azurecli/appRegistrationRoles.json index 5fb198cd..233a5b98 100644 --- a/deployers/azurecli/appRegistrationRoles.json +++ b/deployers/azurecli/appRegistrationRoles.json @@ -46,5 +46,22 @@ "id": "b0288440-5195-4264-9a33-cf9a4635d634", "isEnabled": true, "value": "ExternalApi" + }, + { + "allowedMemberTypes": [ "User" ], + "description": "Full administrative access to Control Center features", + "displayName": "Control Center Admin", + "id": "fad9b386-9392-4f15-b6df-6b47d8f1e75c", + "isEnabled": true, + "value": "ControlCenterAdmin" + }, + { + "allowedMemberTypes": [ "User" ], + "description": "Read-only access to Control Center dashboard and metrics", + "displayName": "Control Center Dashboard Reader", + "id": "6399b062-9114-49ec-a291-c445a0b2b33e", + "isEnabled": true, + "value": "ControlCenterDashboardReader" } + ] diff --git a/deployers/bicep/OneClickDeploy.md b/deployers/bicep/OneClickDeploy.md index 0c5c931b..d89c7dd6 100644 --- a/deployers/bicep/OneClickDeploy.md +++ b/deployers/bicep/OneClickDeploy.md @@ -8,9 +8,9 @@ There are pre-deploy manual steps that must be completed first. After you have deployed, there are additional manual steps that will need to be completed as well. -[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fsimplechat%2Frefs%2Fheads%2Finfra-deployer-gunger%2Fdeployers%2Fbicep%2Fmain.json) +[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fsimplechat%2Frefs%2Fheads%2Fmain%2Fdeployers%2Fbicep%2Fmain.json) -[![Deploy to Azure](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fsimplechat%2Frefs%2Fheads%2Finfra-deployer-gunger%2Fdeployers%2Fbicep%2Fmain.json) +[![Deploy to Azure](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fsimplechat%2Frefs%2Fheads%2Fmain%2Fdeployers%2Fbicep%2Fmain.json) ## How to Use diff --git a/deployers/bicep/README.md b/deployers/bicep/README.md index c2c51bf0..0cf883af 100644 --- a/deployers/bicep/README.md +++ b/deployers/bicep/README.md @@ -3,27 +3,57 @@ >Strongly encourage administrators to use Visual Studio Code and Dev Containers for this deployment type. ## Table of Contents
-- [Deployment Variables](##Deployment_Variables) -- [Deployment Process](##Deployment_Process) - - [Pre-Configuration](###Pre-Configuration) - - [Create the application registration](####Create_the_application_registration) - - [Deployment Process](###Deployment_Process) - - [Configure AZD Environment](####Configure_AZD_Environment) - - [Deployment Prompts](####Deployment_Prompts) - - [Post Deployment Tasks](###Post_Deployment_Tasks) -- [Cleanup / Deprovision](##Cleanup_/_Deprovisioning) -- [Workarounds](##Workarounds) +- [Deployment Variables](#Deployment-Variables) +- [Prerequisites](#Prerequisites) +- [Deployment Process](#Deployment-Process) + - [Pre-Configuration](#Pre-Configuration) + - [Create the application registration](#Create-the-application-registration) + - [Deployment Process](#Deployment-Process-1) + - [Configure AZD Environment](#Configure-AZD-Environment) + - [Deployment Prompts](#Deployment-Prompts) + - [Post Deployment Tasks](#Post-Deployment-Tasks) +- [Cleanup / Deprovision](#Cleanup-/-Deprovisioning) +- [Helpful Info](#Helpful-Info) + - [Private Networking](#Private-Networking) +- [Azure Government (USGov) Considerations](#Azure-Government-USGov-Considerations) +- [Frequently Asked Questions](#Frequently-Asked-Questions) +- [Troubleshooting](#Troubleshooting) --- ## Deployment Variables -The folloiwng variables will be used within this document: +The following variables will be used within this document: - *\* - This will become the beginning of each of the objects created. Minimum of 3 characters, maximum of 12 characters. No Spaces or special characters. - *\* - This will be used as part of the object names as well as with the AZD environments. **Example:** *dev/qa/prod*. - *\* - Options will be *AzureCloud | AzureUSGovernment* - *\* - Should be presented in the form *imageName:label* **Example:** *simple-chat:latest* +--- + +## Prerequisites + +Before deploying, ensure you have: + +1. **Azure Subscription** with Owner or Contributor permissions +2. **Azure CLI** (version 2.50.0 or later) +3. **Azure Developer CLI (azd)** (version 1.5.0 or later) +4. **Docker** installed and running (for container builds) +5. **PowerShell** (for the Entra app registration script) +6. **Permissions to create an Entra ID Application Registration** (or coordinate with your Entra admin) + +### Required Azure Resource Providers +Ensure the following resource providers are registered in your subscription: +- `Microsoft.Web` +- `Microsoft.DocumentDB` +- `Microsoft.CognitiveServices` +- `Microsoft.Search` +- `Microsoft.Storage` +- `Microsoft.KeyVault` +- `Microsoft.ContainerRegistry` +- `Microsoft.Insights` +- `Microsoft.OperationalInsights` + ## Deployment Process @@ -31,7 +61,7 @@ The below steps cover the process to deploy the Simple Chat application to an Az ### Pre-Configuration: -The following procedure must be completed with a user that has permissions to create an application registration in the users Entra tenanat. If this procedure is to be completed by a different user, the following files should be provided: +The following procedure must be completed with a user that has permissions to create an application registration in the users Entra tenant. If this procedure is to be completed by a different user, the following files should be provided: `./deployers/Initialize-EntraApplication.ps1`
`./deployers/azurecli/appRegistrationRoles.json` @@ -95,17 +125,63 @@ Using the bash terminal in Visual Studio Code `azd env new ` - Use the same value for the \ that was used in the application registration. -`azd env select ` - select the new environment +`azd env select ` - select the new environment. + +`azd provision --preview` - identify what will be deployed with the current configuration. `azd up` - This step will begin the deployment process. +#### Service Limitations of USGovCloud + +> ⚠️ **Important:** Review this section carefully before deploying to Azure Government. + +- **Services NOT available in Azure Government:** + - Azure Video Indexer - Set `deployVideoIndexerService` to `false` + +- **SKU Restrictions:** + - **GlobalStandard SKU is NOT available** - Azure OpenAI models must use `Standard` SKU instead + - Default deployment uses `GlobalStandard` - override `gptModels` and `embeddingModels` parameters + +- **Model Availability:** + - Verify the `gptModels` and `embeddingModels` model names and versions are available in your target USGov region + - Model availability may differ from Azure Commercial - check [Azure OpenAI Service models](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models) + +- **Limited Regional Availability:** + - ContentSafety - typically only USGov Virginia, USGov Arizona + - SpeechService - verify feature availability (Neural voices may be limited) + - DocumentIntelligence - prebuilt models may differ + +**Example USGov Model Configuration Override:** +```json +{ + "gptModels": [ + { + "modelName": "gpt-4o", + "modelVersion": "2024-05-13", + "skuName": "Standard", + "skuCapacity": 100 + } + ], + "embeddingModels": [ + { + "modelName": "text-embedding-ada-002", + "modelVersion": "2", + "skuName": "Standard", + "skuCapacity": 100 + } + ] +} +``` + #### Deployment Prompts > For each of the following parameters ensure the value noted in *\* matches settings as noted above. +> If you are unsure what a parameter is used for, see specific help for each parameter by entering "?" at that prompt. - Select an Azure Subscription to use: *\* -Provisioning may take between 10-40 minutes depending on the options selected. +Provisioning may take between 5-40 minutes depending on the options selected. On the completion of the deployment, a URL will be presented, the user may use to access the site. @@ -127,33 +205,29 @@ On the completion of the deployment, a URL will be presented, the user may use t ### Post Deployment Tasks: -Once logged in to the newly deployed application with admin credentials, the application will need to be set up with several configurations: +Once logged in to the newly deployed application with admin credentials, review the application configuration in the Admin Settings: -1. AI Models > GPT Configuration & Embeddings Configuration. Application is pre-configured with the chosen security model (key / managed identity). Select "Test GPT Connection" and "Test Embedding Connection" to verify connection. +1. Admin Settings > AI Models > GPT Configuration & Embeddings Configuration. Application is pre-configured with the chosen security model (key / managed identity). Select "Test GPT Connection" and "Test Embedding Connection" to verify connection. - > Known Bug: User will be unable to Fetch GPT or Embedding models.
-Workaround: Set configurations in CosmosDB. For details see [Workarounds](##Workarounds) below. +1. Admin Settings > Scale > Redis Cache (if enabled) - Select "Test Redis Connection" + +1. Admin Settings > Workspaces > Multi-Modal Vision Analysis - Select "Test Vision Analysis" + +1. Admin Settings > Search & Extract > Azure AI Search + > Known Bug: Unable to test "Managed Identity" authentication type. Must use "Key" for validation but application will run under Managed Identity" -1. Logging > Application Insights Logging > "Enable Application Insights Global Logging - Set to "ON" -1. Citations > Ehnahced Citations > "Enable Enhanced Citations" - Set to "ON" - - Configure "All Filetypes" - - "Storage Account Authentication Type" = Managed Identity - - "Storage Account Blob Endpoint" = "https://\\sa.blob.core.windows.net" (or appropiate domain if in Azure Gov.) -1. Safety > Conversation Archiving > "Enable Conversation Archiving" - Set to "ON" -1. Search & Extract > Azure AI Search - - "Search Endpoint" = "https://\-\-search.search.windows.net" (or appropiate domain if in Azure Gov.) - > Known Bug: Unable to configure "Managed Identity" authentication type. Must use "Key" - "Authentication Type" - Key - "Search Key" - *Pre-populated from key vault value*. - At the top of the Admin Page you'll see warning boxes indicating Index Schema Mismatch. - Click "Create user Index" - Click "Create group Index" - - Click "Create public Index" -1. Search & Extract > Document Intelligence - - "Document Intelligence Endpoint" = "https://\-\-docintel.cognitiveservices.azure.com/" (or appropiate domain if in Azure Gov.) - - "Authentication Type" - Managed Identity + - Click "Create public Index" + - Select "Test Azure AI Search Connection" + +1. Search & Extract > Document Intelligence - Select "Test Document Intelligence Connection" + -User shoud now be able to fully use Simple Chat application. +User should now be able to fully use Simple Chat application. --- ## Cleanup / Deprovisioning @@ -163,67 +237,178 @@ User shoud now be able to fully use Simple Chat application. `cd ./deployers`
`azd down --purge` - This will delete all deployed resource for this solution and purge key vault, document intelligence, OpenAI services. +--- +## Helpful Info + +- If Key based authentication is selected, ensure keys are rotated per organizational requirements. + +- If a deployment failure is encountered, often times, rerunning the deployment will clear the temporary error. + +- When private networking is selected, 0.0.0.0 (representing the internal Azure Services) is added to the CosmosDB firewall in addition to any IP's added to the 'allowedIpAddresses' parameter. Users are encouraged to include the IP address of the deployment server in the 'allowedIpAddresses. This becomes not-applicable on completion of the deployment when CosmosDB, Key Vault, Azure Container Registry and the Web Application is configured for private networking only. If the 'allowedIpAddresses parameter is not used, the administrator can manually add in the deployment server IP address to the Settings > Networking section of the coresponding service(s) and rerun the deployment. + +- To evaluate any infrastructure changes between versions, with AZD the user can run: +`azd provision --preview` + +### Private Networking + +When private networking is configured, access from the developers workstation to push updates and new Azure configurations will be blocked. In addition, testing the web application when not on a VPN attached to the private network subnet is expected to be blocked. + +During initial deployment, if post an error is raised "failed running post hooks: 'postprovision'" the deployment is being blocked from executing scripts against the CosmosDB service. Ensure the deployment workstation IP address is added to the "allowedIPAddresses" parameter. Similar messages may be seen from the Azure Container Registry Service. + +When private networking is enabled, to test the web applicaiton, users may configure a VPN into the deployed vNet (space is provided for this) or the administration may adjust the networking limitations to the deployed website. This may be accomplished with the following script: + +`az webapp update --name --app --resource-group --rg --public-network-access Enabled;` + +To permit redeployment of Azure infrastructure services, the following script may be used to enable access when private networking is enabled. + +``` +az cosmosdb update --name --cosmos --resource-group --rg --public-network-access enabled +az keyvault update --name --kv --resource-group --rg --public-network-access enabled +az acr update --name acr --resource-group --rg --public-network-enabled true +az resource update --name --app --resource-group --rg --resource-type "Microsoft.Web/sites" --set properties.publicNetworkAccess=Enabled +``` + +--- + +## Azure Government (USGov) Considerations + +### Services Deployed + +| Service | Azure Commercial | Azure Government | Notes | +|---------|------------------|------------------|-------| +| App Service | ✅ | ✅ | Premium V3 tier | +| Cosmos DB | ✅ | ✅ | Serverless mode | +| Azure OpenAI | ✅ | ✅ | Standard SKU only in USGov | +| Azure AI Search | ✅ | ✅ | Basic tier | +| Document Intelligence | ✅ | ✅ | Limited regions | +| Storage Account | ✅ | ✅ | Standard LRS | +| Key Vault | ✅ | ✅ | Standard tier | +| Container Registry | ✅ | ✅ | Basic tier | +| Application Insights | ✅ | ✅ | | +| Log Analytics | ✅ | ✅ | | +| Content Safety | ✅ | ⚠️ Limited | Not all regions | +| Speech Service | ✅ | ⚠️ Limited | Feature restrictions | +| Video Indexer | ✅ | ❌ Not Available | | +| Redis Cache | ✅ | ✅ | Standard tier | + +### Endpoint Differences + +The deployment automatically handles the following endpoint differences: +- ACR Domain: `.azurecr.io` → `.azurecr.us` +- Entra Login: `login.microsoftonline.com` → `login.microsoftonline.us` +- OpenID Issuer: `sts.windows.net` → `login.microsoftonline.us` +- Private DNS Zones: Automatically configured for USGov --- -## Workarounds - -- Fetching GPT and Embedding Models. - - Grant the current user data access to Cosmos DB from a BASH command shell - - `PRINCIPAL_ID=$(az ad signed-in-user show --query id --output tsv)` - - `az cosmosdb sql role assignment create --account-name --cosmos --resource-group --rg --principal-id $PRINCIPAL_ID --scope "/" --role-definition-id 00000000-0000-0000-0000-000000000002` - - Open CosmosDB in Azure Portal and connect to the `--cosmos` service. - - Data Explorer > SimpleChat > settings > items - - Replace the following values: - ``` - "gpt_model": { - "selected": [], - "all": [] - }, - ``` - - with - - ``` - "gpt_model": { - "selected": [ - { - "deploymentName": "gpt-4o", - "modelName": "gpt-4o" - } - ], - "all": [ - { - "deploymentName": "gpt-4o", - "modelName": "gpt-4o" - } - ] - }, - ``` - - and - - ``` - "embedding_model": { - "selected": [], - "all": [] - }, - ``` - - with - - ``` - "embedding_model": { - "selected": [ - "deploymentName": "text-embedding-3-small", - "modelName": "text-embedding-3-small" - ], - "all": [ - "deploymentName": "text-embedding-3-small", - "modelName": "text-embedding-3-small" - ] - }, - ``` - - - Update settings in the Cosmos UI and click Save. - - Refresh web page and you shound now be able to Test the GPT and Embedding models. + +## Frequently Asked Questions + +### General Questions + +**Q: How long does deployment take?** +A: Initial deployment typically takes 15-40 minutes depending on options selected. Subsequent deployments are faster. + +**Q: What Azure permissions do I need?** +A: You need Owner or Contributor role on the target subscription, plus ability to create Entra ID app registrations (or work with your Entra admin). + +**Q: Can I deploy to an existing resource group?** +A: No, the deployment creates a new resource group named `--rg`. + +**Q: What is the default authentication type?** +A: You can choose between `key` (API keys stored in Key Vault) or `managed_identity` (recommended for production). + +### Model Configuration + +**Q: How do I customize which GPT models are deployed?** +A: Override the `gptModels` parameter with your desired configuration: +```json +[ + { + "modelName": "gpt-4o", + "modelVersion": "2024-11-20", + "skuName": "GlobalStandard", + "skuCapacity": 100 + } +] +``` + +**Q: What's the difference between GlobalStandard and Standard SKU?** +A: `GlobalStandard` provides access to Azure's global AI infrastructure with higher availability but is not available in Azure Government. `Standard` is region-specific and is required for USGov deployments. + +### Networking + +**Q: Can I deploy without private networking initially and add it later?** +A: Yes, set `enablePrivateNetworking` to `false` initially. You can enable it later but this requires re-running the deployment. + +**Q: Why do I need to add my IP address to allowedIpAddresses?** +A: During deployment, scripts need to access Cosmos DB and other services. Your IP must be allowed through the firewall temporarily. + +### Costs + +**Q: What's the estimated monthly cost?** +A: Base infrastructure (without optional services) costs approximately: +- App Service Plan (P1v3): ~$150/month +- Cosmos DB (Serverless): Pay-per-request +- Azure OpenAI: Pay-per-token +- Azure AI Search (Basic): ~$70/month +- Other services: Variable based on usage + +### Upgrading + +**Q: How do I upgrade to a new version?** +A: Run `azd up` again from the updated codebase. Use `azd provision --preview` to review changes first. + +--- + +## Troubleshooting + +### Common Deployment Errors + +**Error: "failed running post hooks: 'postprovision'"** +- **Cause:** Deployment scripts cannot access Cosmos DB or other services +- **Solution:** Add your IP address to the `allowedIpAddresses` parameter and redeploy + +**Error: "The subscription is not registered to use namespace 'Microsoft.CognitiveServices'"** +- **Cause:** Required resource provider not registered +- **Solution:** Run `az provider register --namespace Microsoft.CognitiveServices` + +**Error: "Quota exceeded for deployment"** +- **Cause:** Azure OpenAI quota limits reached +- **Solution:** Request quota increase or reduce `skuCapacity` in model configuration + +**Error: "InvalidTemplateDeployment - GlobalStandard SKU not available"** +- **Cause:** Attempting USGov deployment with GlobalStandard SKU +- **Solution:** Use `Standard` SKU for all models in Azure Government + +**Error: "Resource 'Microsoft.VideoIndexer/accounts' not found"** +- **Cause:** Video Indexer not available in region (especially USGov) +- **Solution:** Set `deployVideoIndexerService` to `false` + +### Post-Deployment Issues + +**Issue: Cannot access the web application** +- Verify the Entra app registration is configured correctly +- Check that admin consent was granted for API permissions +- Ensure users are assigned to the enterprise application + +**Issue: "Test Connection" fails in Admin Settings** +- For Managed Identity: Wait 5-10 minutes for role assignments to propagate +- For Key Authentication: Verify secrets exist in Key Vault +- Check Application Insights for detailed error logs + +**Issue: AI Search shows "Index Schema Mismatch"** +- This is expected on first deployment +- Click "Create user Index", "Create group Index", "Create public Index" in Admin Settings + +### Logs and Diagnostics + +Enable diagnostic logging by setting `enableDiagLogging` to `true`. Logs are sent to: +- Log Analytics Workspace: `--logs` +- Application Insights: `--ai` + +View application logs: +```bash +az webapp log tail --name --app --resource-group --rg +``` + diff --git a/deployers/bicep/main.bicep b/deployers/bicep/main.bicep index 86e0cfaa..b336aee9 100644 --- a/deployers/bicep/main.bicep +++ b/deployers/bicep/main.bicep @@ -76,6 +76,10 @@ param specialTags object = {} - Default is false''') param enableDiagLogging bool +@description('''Enable private endpoints and virtual network integration for deployed resources. +- Default is false''') +param enablePrivateNetworking bool + @description('''Array of GPT model names to deploy to the OpenAI resource.''') param gptModels array = [ { @@ -107,7 +111,20 @@ param embeddingModels array = [ skuCapacity: 150 } ] + //---------------- +// allowed IP addresses for resources +@description('''Comma separated list of IP addresses or ranges to allow access to resources when private networking is enabled. +Leave blank if not using private networking. +- Format for single IP: 'x.x.x.x' +- Format for range: 'x.x.x.x/y' +- Example: 1.2.3.4, 2.3.4.5/32 +''') +param allowedIpAddresses string +var allowedIpAddressesSplit = empty(allowedIpAddresses) ? [] : split(allowedIpAddresses!, ',') +var allowedIpAddressesArray = [for ip in allowedIpAddressesSplit: trim(ip)] +//---------------- + // optional services @description('''Enable deployment of Content Safety service and related resources. @@ -136,6 +153,15 @@ var acrCloudSuffix = cloudEnvironment == 'AzureCloud' ? '.azurecr.io' : '.azurec var acrName = toLower('${appName}${environment}acr') var containerRegistry = '${acrName}${acrCloudSuffix}' var containerImageName = '${containerRegistry}/${imageName}' +var vNetName = '${appName}-${environment}-vnet' +var allowedIpsForCosmos = union(['0.0.0.0'], allowedIpAddressesArray) +var cosmosDbIpRules = [for ip in allowedIpsForCosmos: { + ipAddressOrRange: ip +}] +var acrIpRules = [for ip in allowedIpAddressesArray: { + action: 'Allow' + value: ip +}] //========================================================= // Resource group deployment @@ -146,6 +172,34 @@ resource rg 'Microsoft.Resources/resourceGroups@2022-09-01' = { tags: tags } +//========================================================= +// Create Virtual Network if private networking is enabled +//========================================================= +module virtualNetwork 'modules/virtualNetwork.bicep' = if (enablePrivateNetworking) { + scope: rg + name: 'virtualNetwork' + params: { + location: location + vNetName: vNetName + addressSpaces: ['10.0.0.0/21'] + subnetConfigs: [ + { + name: 'AppServiceIntegration' // this subnet name must be present for app service vnet integration + addressPrefix: '10.0.0.0/24' + enablePrivateEndpointNetworkPolicies: true + enablePrivateLinkServiceNetworkPolicies: true + } + { + name: 'PrivateEndpoints' // this subnet name must be present if private endpoints are to be used + addressPrefix: '10.0.2.0/24' + enablePrivateEndpointNetworkPolicies: true + enablePrivateLinkServiceNetworkPolicies: true + } + ] + tags: tags + } +} + //========================================================= // Create log analytics workspace //========================================================= @@ -221,6 +275,8 @@ module cosmosDB 'modules/cosmosDb.bicep' = { keyVault: keyVault.outputs.keyVaultName authenticationType: authenticationType configureApplicationPermissions: configureApplicationPermissions + enablePrivateNetworking: enablePrivateNetworking + allowedIpAddresses: cosmosDbIpRules } } @@ -240,6 +296,8 @@ module acr 'modules/azureContainerRegistry.bicep' = { keyVault: keyVault.outputs.keyVaultName authenticationType: authenticationType configureApplicationPermissions: configureApplicationPermissions + enablePrivateNetworking: enablePrivateNetworking + allowedIpAddresses: acrIpRules } } @@ -260,6 +318,8 @@ module searchService 'modules/search.bicep' = { keyVault: keyVault.outputs.keyVaultName authenticationType: authenticationType configureApplicationPermissions: configureApplicationPermissions + + enablePrivateNetworking: enablePrivateNetworking } } @@ -280,6 +340,8 @@ module docIntel 'modules/documentIntelligence.bicep' = { keyVault: keyVault.outputs.keyVaultName authenticationType: authenticationType configureApplicationPermissions: configureApplicationPermissions + + enablePrivateNetworking: enablePrivateNetworking } } @@ -300,6 +362,8 @@ module storageAccount 'modules/storageAccount.bicep' = { keyVault: keyVault.outputs.keyVaultName authenticationType: authenticationType configureApplicationPermissions: configureApplicationPermissions + + enablePrivateNetworking: enablePrivateNetworking } } @@ -323,6 +387,8 @@ module openAI 'modules/openAI.bicep' = { gptModels: gptModels embeddingModels: embeddingModels + + enablePrivateNetworking: enablePrivateNetworking } } @@ -369,6 +435,10 @@ module appService 'modules/appService.bicep' = { enterpriseAppClientSecret: enterpriseAppClientSecret authenticationType: authenticationType keyVaultUri: keyVault.outputs.keyVaultUri + + enablePrivateNetworking: enablePrivateNetworking + #disable-next-line BCP318 // expect one value to be null if private networking is disabled + appServiceSubnetId: enablePrivateNetworking? virtualNetwork.outputs.appServiceSubnetId : '' } } @@ -393,6 +463,8 @@ module contentSafety 'modules/contentSafety.bicep' = if (deployContentSafety) { keyVault: keyVault.outputs.keyVaultName authenticationType: authenticationType configureApplicationPermissions: configureApplicationPermissions + + enablePrivateNetworking: enablePrivateNetworking } } @@ -413,6 +485,8 @@ module redisCache 'modules/redisCache.bicep' = if (deployRedisCache) { keyVault: keyVault.outputs.keyVaultName authenticationType: authenticationType configureApplicationPermissions: configureApplicationPermissions + + //enablePrivateNetworking: enablePrivateNetworking } } @@ -433,6 +507,8 @@ module speechService 'modules/speechService.bicep' = if (deploySpeechService) { keyVault: keyVault.outputs.keyVaultName authenticationType: authenticationType configureApplicationPermissions: configureApplicationPermissions + + enablePrivateNetworking: enablePrivateNetworking } } @@ -452,6 +528,8 @@ module videoIndexerService 'modules/videoIndexer.bicep' = if (deployVideoIndexer storageAccount: storageAccount.outputs.name openAiServiceName: openAI.outputs.openAIName + + enablePrivateNetworking: enablePrivateNetworking } } @@ -472,52 +550,96 @@ module setPermissions 'modules/setPermissions.bicep' = if (configureApplicationP openAIName: openAI.outputs.openAIName docIntelName: docIntel.outputs.documentIntelligenceServiceName storageAccountName: storageAccount.outputs.name + searchServiceName: searchService.outputs.searchServiceName + #disable-next-line BCP318 // expect one value to be null speechServiceName: deploySpeechService ? speechService.outputs.speechServiceName : '' + #disable-next-line BCP318 // expect one value to be null + redisCacheName: deployRedisCache ? redisCache.outputs.redisCacheName : '' + #disable-next-line BCP318 // expect one value to be null + contentSafetyName: deployContentSafety ? contentSafety.outputs.contentSafetyName : '' + #disable-next-line BCP318 // expect one value to be null + videoIndexerName: deployVideoIndexerService ? videoIndexerService.outputs.videoIndexerServiceName : '' + } +} + +//========================================================= +// configure private networking +//========================================================= +module privateNetworking 'modules/privateNetworking.bicep' = if (enablePrivateNetworking) { + name: 'privateNetworking' + scope: rg + params: { + + #disable-next-line BCP318 // value can't be null based on enablePrivateNetworking condition + virtualNetworkId: virtualNetwork.outputs.vNetId + #disable-next-line BCP318 // value can't be null based on enablePrivateNetworking condition + privateEndpointSubnetId: virtualNetwork.outputs.privateNetworkSubnetId + + location: location + appName: appName + environment: environment + tags: tags + + keyVaultName: keyVault.outputs.keyVaultName + cosmosDBName: cosmosDB.outputs.cosmosDbName + acrName: acr.outputs.acrName searchServiceName: searchService.outputs.searchServiceName + docIntelName: docIntel.outputs.documentIntelligenceServiceName + storageAccountName: storageAccount.outputs.name + openAIName: openAI.outputs.openAIName + webAppName: appService.outputs.name + #disable-next-line BCP318 // expect one value to be null contentSafetyName: deployContentSafety ? contentSafety.outputs.contentSafetyName : '' #disable-next-line BCP318 // expect one value to be null + speechServiceName: deploySpeechService ? speechService.outputs.speechServiceName : '' + #disable-next-line BCP318 // expect one value to be null videoIndexerName: deployVideoIndexerService ? videoIndexerService.outputs.videoIndexerServiceName : '' } } + //========================================================= // output values //========================================================= -// output required for both predeploy and postprovision scripts in azure.yaml -output var_rgName string = rgName -// output values required for predeploy script in azure.yaml -output var_webService string = appService.outputs.name -output var_imageName string = contains(imageName, ':') ? split(imageName, ':')[0] : imageName -output var_imageTag string = split(imageName, ':')[1] -output var_containerRegistry string = containerRegistry -output var_acrName string = toLower('${appName}${environment}acr') // output values required for postprovision script in azure.yaml +output var_acrName string = toLower('${appName}${environment}acr') +output var_authenticationType string = toLower(authenticationType) +output var_blobStorageEndpoint string = storageAccount.outputs.endpoint output var_configureApplication bool = configureApplicationPermissions -output var_keyVaultUri string = keyVault.outputs.keyVaultUri +#disable-next-line BCP318 // expect one value to be null +output var_contentSafetyEndpoint string = deployContentSafety ? contentSafety.outputs.contentSafetyEndpoint : '' +output var_cosmosDb_accountName string = cosmosDB.outputs.cosmosDbName output var_cosmosDb_uri string = cosmosDB.outputs.cosmosDbUri -output var_subscriptionId string = subscription().subscriptionId -output var_authenticationType string = toLower(authenticationType) +output var_deploymentLocation string = rg.location +output var_documentIntelligenceServiceEndpoint string = docIntel.outputs.documentIntelligenceServiceEndpoint +output var_keyVaultName string = keyVault.outputs.keyVaultName +output var_keyVaultUri string = keyVault.outputs.keyVaultUri output var_openAIEndpoint string = openAI.outputs.openAIEndpoint -output var_openAIResourceGroup string = openAI.outputs.openAIResourceGroup //may be able to remove output var_openAIGPTModels array = gptModels +output var_openAIResourceGroup string = openAI.outputs.openAIResourceGroup //may be able to remove output var_openAIEmbeddingModels array = embeddingModels -output var_blobStorageEndpoint string = storageAccount.outputs.endpoint #disable-next-line BCP318 // expect one value to be null -output var_contentSafetyEndpoint string = deployContentSafety ? contentSafety.outputs.contentSafetyEndpoint : '' -output var_deploymentLocation string = rg.location +output var_redisCacheHostName string = deployRedisCache ? redisCache.outputs.redisCacheHostName : '' +output var_rgName string = rgName output var_searchServiceEndpoint string = searchService.outputs.searchServiceEndpoint -output var_documentIntelligenceServiceEndpoint string = docIntel.outputs.documentIntelligenceServiceEndpoint -output var_videoIndexerName string = deployVideoIndexerService #disable-next-line BCP318 // expect one value to be null - ? videoIndexerService.outputs.videoIndexerServiceName - : '' -output var_videoIndexerAccountId string = deployVideoIndexerService +output var_speechServiceEndpoint string = deploySpeechService ? speechService.outputs.speechServiceEndpoint : '' +output var_subscriptionId string = subscription().subscriptionId #disable-next-line BCP318 // expect one value to be null - ? videoIndexerService.outputs.videoIndexerAccountId - : '' +output var_videoIndexerAccountId string = deployVideoIndexerService ? videoIndexerService.outputs.videoIndexerAccountId : '' #disable-next-line BCP318 // expect one value to be null -output var_speechServiceEndpoint string = deploySpeechService ? speechService.outputs.speechServiceEndpoint : '' +output var_videoIndexerName string = deployVideoIndexerService ? videoIndexerService.outputs.videoIndexerServiceName : '' + +// output values required for predeploy script in azure.yaml +output var_containerRegistry string = containerRegistry +output var_imageName string = contains(imageName, ':') ? split(imageName, ':')[0] : imageName +output var_imageTag string = split(imageName, ':')[1] +output var_webService string = appService.outputs.name + +// output values required for postup script in azure.yaml +output var_enablePrivateNetworking bool = enablePrivateNetworking + diff --git a/deployers/bicep/main.json b/deployers/bicep/main.json new file mode 100644 index 00000000..a3e17b22 --- /dev/null +++ b/deployers/bicep/main.json @@ -0,0 +1,7879 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "12894563845993036077" + } + }, + "parameters": { + "location": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "The Azure region where resources will be deployed. \n- Region must align to the target cloud environment" + } + }, + "cloudEnvironment": { + "type": "string", + "allowedValues": [ + "AzureCloud", + "AzureUSGovernment" + ], + "metadata": { + "description": "The target Azure Cloud environment.\n- Accepted values are: AzureCloud, AzureUSGovernment\n- Default is AzureCloud" + } + }, + "appName": { + "type": "string", + "minLength": 3, + "maxLength": 12, + "metadata": { + "description": "The name of the application to be deployed. \n- Name may only contain letters and numbers\n- Between 3 and 12 characters in length \n- No spaces or special characters" + } + }, + "environment": { + "type": "string", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "The dev/qa/prod environment or as named in your environment. This will be used to create resource group names and tags.\n- Must be between 2 and 10 characters in length\n- No spaces or special characters" + } + }, + "azdEnvironmentName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "metadata": { + "description": "Name of the AZD environment" + } + }, + "imageName": { + "type": "string", + "metadata": { + "description": "The name of the container image to deploy to the web app.\n- should be in the format :" + } + }, + "enterpriseAppClientId": { + "type": "string", + "metadata": { + "description": "Azure AD Application Client ID for enterprise authentication.\n- Should be the client ID of the registered Azure AD application" + } + }, + "enterpriseAppServicePrincipalId": { + "type": "string", + "metadata": { + "description": "Azure AD Application Service Principal Id for the enterprise application.\n- Should be the Service Principal ID of the registered Azure AD application" + } + }, + "enterpriseAppClientSecret": { + "type": "securestring", + "metadata": { + "description": "Azure AD Application Client Secret for enterprise authentication.\n- Required if enableEnterpriseApp is true\n- Should be created in Azure AD App Registration and passed via environment variable\n- Will be stored securely in Azure Key Vault during deployment" + } + }, + "authenticationType": { + "type": "string", + "allowedValues": [ + "key", + "managed_identity" + ], + "metadata": { + "description": "Authentication type for resources that support Managed Identity or Key authentication.\n- Key: Use access keys for authentication (application keys will be stored in Key Vault)\n- managed_identity: Use Managed Identity for authentication" + } + }, + "configureApplicationPermissions": { + "type": "bool", + "metadata": { + "description": "Configure permissions (based on authenticationType) for the deployed web application to access required resources.\n" + } + }, + "specialTags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional object containing additional tags to apply to all resources." + } + }, + "enableDiagLogging": { + "type": "bool", + "metadata": { + "description": "Enable diagnostic logging for resources deployed in the resource group. \n- All content will be sent to the deployed Log Analytics workspace\n- Default is false" + } + }, + "enablePrivateNetworking": { + "type": "bool", + "metadata": { + "description": "Enable private endpoints and virtual network integration for deployed resources. \n- Default is false" + } + }, + "gptModels": { + "type": "array", + "defaultValue": [ + { + "modelName": "gpt-4.1", + "modelVersion": "2025-04-14", + "skuName": "GlobalStandard", + "skuCapacity": 150 + }, + { + "modelName": "gpt-4o", + "modelVersion": "2024-11-20", + "skuName": "GlobalStandard", + "skuCapacity": 100 + } + ], + "metadata": { + "description": "Array of GPT model names to deploy to the OpenAI resource." + } + }, + "embeddingModels": { + "type": "array", + "defaultValue": [ + { + "modelName": "text-embedding-3-small", + "modelVersion": "1", + "skuName": "GlobalStandard", + "skuCapacity": 150 + }, + { + "modelName": "text-embedding-3-large", + "modelVersion": "1", + "skuName": "GlobalStandard", + "skuCapacity": 150 + } + ], + "metadata": { + "description": "Array of embedding model names to deploy to the OpenAI resource." + } + }, + "allowedIpAddresses": { + "type": "array", + "defaultValue": [ + { + "ipAddressOrRange": "0.0.0.0" + } + ] + }, + "deployContentSafety": { + "type": "bool", + "metadata": { + "description": "Enable deployment of Content Safety service and related resources.\n- Default is false" + } + }, + "deployRedisCache": { + "type": "bool", + "metadata": { + "description": "Enable deployment of Azure Cache for Redis and related resources.\n- Default is false" + } + }, + "deploySpeechService": { + "type": "bool", + "metadata": { + "description": "Enable deployment of Azure Speech service and related resources.\n- Default is false" + } + }, + "deployVideoIndexerService": { + "type": "bool", + "metadata": { + "description": "Enable deployment of Azure Video Indexer service and related resources.\n- Default is false" + } + } + }, + "variables": { + "rgName": "[format('{0}-{1}-rg', parameters('appName'), parameters('environment'))]", + "requiredTags": { + "application": "[parameters('appName')]", + "environment": "[parameters('environment')]", + "azd-env-name": "[parameters('azdEnvironmentName')]" + }, + "tags": "[union(variables('requiredTags'), parameters('specialTags'))]", + "acrCloudSuffix": "[if(equals(parameters('cloudEnvironment'), 'AzureCloud'), '.azurecr.io', '.azurecr.us')]", + "acrName": "[toLower(format('{0}{1}acr', parameters('appName'), parameters('environment')))]", + "containerRegistry": "[format('{0}{1}', variables('acrName'), variables('acrCloudSuffix'))]", + "containerImageName": "[format('{0}/{1}', variables('containerRegistry'), parameters('imageName'))]", + "vNetName": "[format('{0}-{1}-vnet', parameters('appName'), parameters('environment'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2022-09-01", + "name": "[variables('rgName')]", + "location": "[parameters('location')]", + "tags": "[variables('tags')]" + }, + { + "condition": "[parameters('enablePrivateNetworking')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "virtualNetwork", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "vNetName": { + "value": "[variables('vNetName')]" + }, + "addressSpaces": { + "value": [ + "10.0.0.0/21" + ] + }, + "subnetConfigs": { + "value": [ + { + "name": "AppServiceIntegration", + "addressPrefix": "10.0.0.0/24", + "enablePrivateEndpointNetworkPolicies": true, + "enablePrivateLinkServiceNetworkPolicies": true + }, + { + "name": "PrivateEndpoints", + "addressPrefix": "10.0.2.0/24", + "enablePrivateEndpointNetworkPolicies": true, + "enablePrivateLinkServiceNetworkPolicies": true + } + ] + }, + "tags": { + "value": "[variables('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "10221795613826494860" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "vNetName": { + "type": "string" + }, + "addressSpaces": { + "type": "array" + }, + "subnetConfigs": { + "type": "array" + }, + "tags": { + "type": "object" + } + }, + "variables": { + "copy": [ + { + "name": "subnetIds", + "count": "[length(parameters('subnetConfigs'))]", + "input": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vNetName'), parameters('subnetConfigs')[copyIndex('subnetIds')].name)]" + }, + { + "name": "subnetNames", + "count": "[length(parameters('subnetConfigs'))]", + "input": "[parameters('subnetConfigs')[copyIndex('subnetNames')].name]" + } + ], + "appServiceIntegrationSubnetIndex": "[indexOf(variables('subnetNames'), 'AppServiceIntegration')]", + "privateEndpointIndex": "[indexOf(variables('subnetNames'), 'PrivateEndpoints')]" + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2021-05-01", + "name": "[parameters('vNetName')]", + "location": "[parameters('location')]", + "properties": { + "copy": [ + { + "name": "subnets", + "count": "[length(parameters('subnetConfigs'))]", + "input": { + "name": "[parameters('subnetConfigs')[copyIndex('subnets')].name]", + "properties": { + "addressPrefix": "[parameters('subnetConfigs')[copyIndex('subnets')].addressPrefix]", + "privateEndpointNetworkPolicies": "[if(parameters('subnetConfigs')[copyIndex('subnets')].enablePrivateEndpointNetworkPolicies, 'Enabled', 'Disabled')]", + "privateLinkServiceNetworkPolicies": "[if(parameters('subnetConfigs')[copyIndex('subnets')].enablePrivateLinkServiceNetworkPolicies, 'Enabled', 'Disabled')]", + "delegations": "[if(equals(parameters('subnetConfigs')[copyIndex('subnets')].name, 'AppServiceIntegration'), createArray(createObject('name', 'delegation', 'properties', createObject('serviceName', 'Microsoft.Web/serverFarms'))), createArray())]" + } + } + } + ], + "addressSpace": { + "addressPrefixes": "[parameters('addressSpaces')]" + } + }, + "tags": "[parameters('tags')]" + } + ], + "outputs": { + "vNetId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vNetName'))]" + }, + "privateNetworkSubnetId": { + "type": "string", + "value": "[if(equals(variables('privateEndpointIndex'), -1), '', variables('subnetIds')[variables('privateEndpointIndex')])]" + }, + "appServiceSubnetId": { + "type": "string", + "value": "[if(equals(variables('appServiceIntegrationSubnetIndex'), -1), '', variables('subnetIds')[variables('appServiceIntegrationSubnetIndex')])]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "logAnalytics", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "16638534490731333297" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2022-10-01", + "name": "[toLower(format('{0}-{1}-la', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "properties": { + "sku": { + "name": "PerGB2018" + } + }, + "tags": "[parameters('tags')]" + } + ], + "outputs": { + "logAnalyticsId": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', toLower(format('{0}-{1}-la', parameters('appName'), parameters('environment'))))]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "applicationInsights", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "16543999957549068652" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "logAnalyticsId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[toLower(format('{0}-{1}-ai', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[parameters('logAnalyticsId')]" + }, + "tags": "[parameters('tags')]" + } + ], + "outputs": { + "appInsightsName": { + "type": "string", + "value": "[toLower(format('{0}-{1}-ai', parameters('appName'), parameters('environment')))]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "keyVault", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "3503710577838836741" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2024-11-01", + "name": "[toLower(format('{0}-{1}-kv', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "accessPolicies": [], + "enabledForDeployment": false, + "enabledForDiskEncryption": false, + "enabledForTemplateDeployment": false, + "publicNetworkAccess": "Enabled", + "enableRbacAuthorization": true + }, + "tags": "[parameters('tags')]" + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', toLower(format('{0}-{1}-kv', parameters('appName'), parameters('environment'))))]", + "name": "[toLower(format('{0}-diagnostics', toLower(format('{0}-{1}-kv', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardLogCategories.value]", + "metrics": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardMetricsCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]", + "[resourceId('Microsoft.KeyVault/vaults', toLower(format('{0}-{1}-kv', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + } + ], + "outputs": { + "keyVaultId": { + "type": "string", + "value": "[resourceId('Microsoft.KeyVault/vaults', toLower(format('{0}-{1}-kv', parameters('appName'), parameters('environment'))))]" + }, + "keyVaultName": { + "type": "string", + "value": "[toLower(format('{0}-{1}-kv', parameters('appName'), parameters('environment')))]" + }, + "keyVaultUri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', toLower(format('{0}-{1}-kv', parameters('appName'), parameters('environment')))), '2024-11-01').vaultUri]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "condition": "[not(empty(parameters('enterpriseAppClientSecret')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storeEnterpriseAppSecret", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "secretName": { + "value": "enterprise-app-client-secret" + }, + "secretValue": { + "value": "[parameters('enterpriseAppClientSecret')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "18077870757859157550" + } + }, + "parameters": { + "keyVaultName": { + "type": "string" + }, + "secretName": { + "type": "string" + }, + "secretValue": { + "type": "securestring" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2025-05-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretName'))]", + "properties": { + "value": "[parameters('secretValue')]" + } + } + ], + "outputs": { + "SecretUri": { + "type": "string", + "value": "[format('{0}secrets/{1}', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2025-05-01').vaultUri, parameters('secretName'))]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "cosmosDB", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + }, + "keyVault": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "authenticationType": { + "value": "[parameters('authenticationType')]" + }, + "configureApplicationPermissions": { + "value": "[parameters('configureApplicationPermissions')]" + }, + "enablePrivateNetworking": { + "value": "[parameters('enablePrivateNetworking')]" + }, + "allowedIpAddresses": { + "value": "[parameters('allowedIpAddresses')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "15046922718277168026" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "authenticationType": { + "type": "string" + }, + "configureApplicationPermissions": { + "type": "bool" + }, + "enablePrivateNetworking": { + "type": "bool" + }, + "allowedIpAddresses": { + "type": "array", + "defaultValue": [] + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2023-04-15", + "name": "[toLower(format('{0}-{1}-cosmos', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "kind": "GlobalDocumentDB", + "properties": { + "publicNetworkAccess": "Enabled", + "databaseAccountOfferType": "Standard", + "capabilities": [ + { + "name": "EnableServerless" + } + ], + "isVirtualNetworkFilterEnabled": "[if(parameters('enablePrivateNetworking'), true(), false())]", + "ipRules": "[if(parameters('enablePrivateNetworking'), parameters('allowedIpAddresses'), createArray())]", + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + } + }, + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", + "apiVersion": "2023-04-15", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-cosmos', parameters('appName'), parameters('environment'))), 'SimpleChat')]", + "properties": { + "resource": { + "id": "SimpleChat" + }, + "options": {} + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', toLower(format('{0}-{1}-cosmos', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers", + "apiVersion": "2023-04-15", + "name": "[format('{0}/{1}/{2}', toLower(format('{0}-{1}-cosmos', parameters('appName'), parameters('environment'))), 'SimpleChat', 'settings')]", + "properties": { + "resource": { + "id": "settings", + "partitionKey": { + "paths": [ + "/id" + ] + } + }, + "options": {} + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', toLower(format('{0}-{1}-cosmos', parameters('appName'), parameters('environment'))), 'SimpleChat')]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', toLower(format('{0}-{1}-cosmos', parameters('appName'), parameters('environment'))))]", + "name": "[toLower(format('{0}-diagnostics', toLower(format('{0}-{1}-cosmos', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardLogCategories.value]", + "metrics": [] + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', toLower(format('{0}-{1}-cosmos', parameters('appName'), parameters('environment'))))]", + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + }, + { + "condition": "[and(equals(parameters('authenticationType'), 'key'), parameters('configureApplicationPermissions'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storeCosmosDbSecret", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVault')]" + }, + "secretName": { + "value": "cosmos-db-key" + }, + "secretValue": { + "value": "[listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', toLower(format('{0}-{1}-cosmos', parameters('appName'), parameters('environment')))), '2023-04-15').primaryMasterKey]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "18077870757859157550" + } + }, + "parameters": { + "keyVaultName": { + "type": "string" + }, + "secretName": { + "type": "string" + }, + "secretValue": { + "type": "securestring" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2025-05-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretName'))]", + "properties": { + "value": "[parameters('secretValue')]" + } + } + ], + "outputs": { + "SecretUri": { + "type": "string", + "value": "[format('{0}secrets/{1}', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2025-05-01').vaultUri, parameters('secretName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', toLower(format('{0}-{1}-cosmos', parameters('appName'), parameters('environment'))))]" + ] + } + ], + "outputs": { + "cosmosDbName": { + "type": "string", + "value": "[toLower(format('{0}-{1}-cosmos', parameters('appName'), parameters('environment')))]" + }, + "cosmosDbUri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', toLower(format('{0}-{1}-cosmos', parameters('appName'), parameters('environment')))), '2023-04-15').documentEndpoint]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "azureContainerRegistry", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "acrName": { + "value": "[variables('acrName')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + }, + "keyVault": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "authenticationType": { + "value": "[parameters('authenticationType')]" + }, + "configureApplicationPermissions": { + "value": "[parameters('configureApplicationPermissions')]" + }, + "enablePrivateNetworking": { + "value": "[parameters('enablePrivateNetworking')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "7200761421625936333" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "acrName": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "authenticationType": { + "type": "string" + }, + "configureApplicationPermissions": { + "type": "bool" + }, + "enablePrivateNetworking": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2025-04-01", + "name": "[parameters('acrName')]", + "location": "[parameters('location')]", + "sku": { + "name": "[if(parameters('enablePrivateNetworking'), 'Premium', 'Standard')]" + }, + "properties": { + "adminUserEnabled": true, + "publicNetworkAccess": "Enabled" + }, + "tags": "[parameters('tags')]" + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('acrName'))]", + "name": "[toLower(format('{0}-diagnostics', parameters('acrName')))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardLogCategories.value]", + "metrics": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardMetricsCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName'))]", + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + }, + { + "condition": "[and(equals(parameters('authenticationType'), 'key'), parameters('configureApplicationPermissions'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storeContainerRegistrySecret", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVault')]" + }, + "secretName": { + "value": "container-registry-key" + }, + "secretValue": { + "value": "[listCredentials(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), '2025-04-01').passwords[0].value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "18077870757859157550" + } + }, + "parameters": { + "keyVaultName": { + "type": "string" + }, + "secretName": { + "type": "string" + }, + "secretValue": { + "type": "securestring" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2025-05-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretName'))]", + "properties": { + "value": "[parameters('secretValue')]" + } + } + ], + "outputs": { + "SecretUri": { + "type": "string", + "value": "[format('{0}secrets/{1}', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2025-05-01').vaultUri, parameters('secretName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName'))]" + ] + } + ], + "outputs": { + "acrName": { + "type": "string", + "value": "[parameters('acrName')]" + }, + "acrResourceGroup": { + "type": "string", + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "searchService", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + }, + "keyVault": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "authenticationType": { + "value": "[parameters('authenticationType')]" + }, + "configureApplicationPermissions": { + "value": "[parameters('configureApplicationPermissions')]" + }, + "enablePrivateNetworking": { + "value": "[parameters('enablePrivateNetworking')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "12409981559885193891" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "authenticationType": { + "type": "string" + }, + "configureApplicationPermissions": { + "type": "bool" + }, + "enablePrivateNetworking": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.Search/searchServices", + "apiVersion": "2025-05-01", + "name": "[toLower(format('{0}-{1}-search', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "sku": { + "name": "basic" + }, + "properties": { + "hostingMode": "default", + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), 'Disabled', 'Enabled')]", + "replicaCount": 1, + "partitionCount": 1, + "authOptions": { + "aadOrApiKey": { + "aadAuthFailureMode": "http403" + } + }, + "disableLocalAuth": false + }, + "tags": "[parameters('tags')]" + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Search/searchServices/{0}', toLower(format('{0}-{1}-search', parameters('appName'), parameters('environment'))))]", + "name": "[toLower(format('{0}-diagnostics', toLower(format('{0}-{1}-search', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardLogCategories.value]", + "metrics": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardMetricsCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]", + "[resourceId('Microsoft.Search/searchServices', toLower(format('{0}-{1}-search', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + }, + { + "condition": "[and(equals(parameters('authenticationType'), 'key'), parameters('configureApplicationPermissions'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storeSearchServiceSecret", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVault')]" + }, + "secretName": { + "value": "search-service-key" + }, + "secretValue": { + "value": "[listAdminKeys(resourceId('Microsoft.Search/searchServices', toLower(format('{0}-{1}-search', parameters('appName'), parameters('environment')))), '2025-05-01').primaryKey]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "18077870757859157550" + } + }, + "parameters": { + "keyVaultName": { + "type": "string" + }, + "secretName": { + "type": "string" + }, + "secretValue": { + "type": "securestring" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2025-05-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretName'))]", + "properties": { + "value": "[parameters('secretValue')]" + } + } + ], + "outputs": { + "SecretUri": { + "type": "string", + "value": "[format('{0}secrets/{1}', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2025-05-01').vaultUri, parameters('secretName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Search/searchServices', toLower(format('{0}-{1}-search', parameters('appName'), parameters('environment'))))]" + ] + } + ], + "outputs": { + "searchServiceName": { + "type": "string", + "value": "[toLower(format('{0}-{1}-search', parameters('appName'), parameters('environment')))]" + }, + "searchServiceEndpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Search/searchServices', toLower(format('{0}-{1}-search', parameters('appName'), parameters('environment')))), '2025-05-01').endpoint]" + }, + "searchServiceAuthencationType": { + "type": "string", + "value": "[parameters('authenticationType')]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "docIntel", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + }, + "keyVault": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "authenticationType": { + "value": "[parameters('authenticationType')]" + }, + "configureApplicationPermissions": { + "value": "[parameters('configureApplicationPermissions')]" + }, + "enablePrivateNetworking": { + "value": "[parameters('enablePrivateNetworking')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1403194626393613544" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "authenticationType": { + "type": "string" + }, + "configureApplicationPermissions": { + "type": "bool" + }, + "enablePrivateNetworking": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-06-01", + "name": "[toLower(format('{0}-{1}-docintel', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "kind": "FormRecognizer", + "sku": { + "name": "S0" + }, + "properties": { + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), 'Disabled', 'Enabled')]", + "customSubDomainName": "[toLower(format('{0}-{1}-docintel', parameters('appName'), parameters('environment')))]" + }, + "tags": "[parameters('tags')]" + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', toLower(format('{0}-{1}-docintel', parameters('appName'), parameters('environment'))))]", + "name": "[toLower(format('{0}-diagnostics', toLower(format('{0}-{1}-docintel', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardLogCategories.value]", + "metrics": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardMetricsCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]", + "[resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-docintel', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + }, + { + "condition": "[and(equals(parameters('authenticationType'), 'key'), parameters('configureApplicationPermissions'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storeDocumentIntelligenceSecret", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVault')]" + }, + "secretName": { + "value": "document-intelligence-key" + }, + "secretValue": { + "value": "[listKeys(resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-docintel', parameters('appName'), parameters('environment')))), '2025-06-01').key1]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "18077870757859157550" + } + }, + "parameters": { + "keyVaultName": { + "type": "string" + }, + "secretName": { + "type": "string" + }, + "secretValue": { + "type": "securestring" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2025-05-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretName'))]", + "properties": { + "value": "[parameters('secretValue')]" + } + } + ], + "outputs": { + "SecretUri": { + "type": "string", + "value": "[format('{0}secrets/{1}', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2025-05-01').vaultUri, parameters('secretName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-docintel', parameters('appName'), parameters('environment'))))]" + ] + } + ], + "outputs": { + "documentIntelligenceServiceName": { + "type": "string", + "value": "[toLower(format('{0}-{1}-docintel', parameters('appName'), parameters('environment')))]" + }, + "diagnosticLoggingEnabled": { + "type": "bool", + "value": "[parameters('enableDiagLogging')]" + }, + "documentIntelligenceServiceEndpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-docintel', parameters('appName'), parameters('environment')))), '2025-06-01').endpoint]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storageAccount", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + }, + "keyVault": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "authenticationType": { + "value": "[parameters('authenticationType')]" + }, + "configureApplicationPermissions": { + "value": "[parameters('configureApplicationPermissions')]" + }, + "enablePrivateNetworking": { + "value": "[parameters('enablePrivateNetworking')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1065428217831578633" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "authenticationType": { + "type": "string" + }, + "configureApplicationPermissions": { + "type": "bool" + }, + "enablePrivateNetworking": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[toLower(format('{0}{1}sa', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "sku": { + "name": "Standard_LRS" + }, + "kind": "StorageV2", + "properties": { + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), 'Disabled', 'Enabled')]", + "accessTier": "Hot", + "allowBlobPublicAccess": false, + "allowSharedKeyAccess": true, + "isHnsEnabled": true + }, + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2023-01-01", + "name": "[format('{0}/{1}', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment'))), 'default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2023-01-01", + "name": "[format('{0}/{1}/{2}', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment'))), 'default', 'user-documents')]", + "properties": { + "publicAccess": "None" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment'))), 'default')]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2023-01-01", + "name": "[format('{0}/{1}/{2}', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment'))), 'default', 'group-documents')]", + "properties": { + "publicAccess": "None" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment'))), 'default')]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment'))))]", + "name": "[toLower(format('{0}-diagnostics', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": [], + "metrics": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.transactionMetricsCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]", + "[resourceId('Microsoft.Storage/storageAccounts', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment'))), 'default')]", + "name": "[toLower(format('{0}-blob-diagnostics', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardLogCategories.value]", + "metrics": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.transactionMetricsCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment'))), 'default')]", + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]", + "[resourceId('Microsoft.Storage/storageAccounts', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + }, + { + "condition": "[and(equals(parameters('authenticationType'), 'key'), parameters('configureApplicationPermissions'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storeStorageAccountSecret", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVault')]" + }, + "secretName": { + "value": "storage-account-key" + }, + "secretValue": { + "value": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment')))), '2022-09-01').keys[0].value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "18077870757859157550" + } + }, + "parameters": { + "keyVaultName": { + "type": "string" + }, + "secretName": { + "type": "string" + }, + "secretValue": { + "type": "securestring" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2025-05-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretName'))]", + "properties": { + "value": "[parameters('secretValue')]" + } + } + ], + "outputs": { + "SecretUri": { + "type": "string", + "value": "[format('{0}secrets/{1}', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2025-05-01').vaultUri, parameters('secretName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment'))))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[toLower(format('{0}{1}sa', parameters('appName'), parameters('environment')))]" + }, + "endpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Storage/storageAccounts', toLower(format('{0}{1}sa', parameters('appName'), parameters('environment')))), '2022-09-01').primaryEndpoints.blob]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "openAI", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + }, + "keyVault": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "authenticationType": { + "value": "[parameters('authenticationType')]" + }, + "configureApplicationPermissions": { + "value": "[parameters('configureApplicationPermissions')]" + }, + "gptModels": { + "value": "[parameters('gptModels')]" + }, + "embeddingModels": { + "value": "[parameters('embeddingModels')]" + }, + "enablePrivateNetworking": { + "value": "[parameters('enablePrivateNetworking')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "17726947983911725769" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "authenticationType": { + "type": "string" + }, + "configureApplicationPermissions": { + "type": "bool" + }, + "gptModels": { + "type": "array" + }, + "embeddingModels": { + "type": "array" + }, + "enablePrivateNetworking": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2024-10-01", + "name": "[toLower(format('{0}-{1}-openai', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "kind": "OpenAI", + "sku": { + "name": "S0" + }, + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), 'Disabled', 'Enabled')]", + "customSubDomainName": "[toLower(format('{0}-{1}-openai', parameters('appName'), parameters('environment')))]" + }, + "tags": "[parameters('tags')]" + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', toLower(format('{0}-{1}-openai', parameters('appName'), parameters('environment'))))]", + "name": "[toLower(format('{0}-diagnostics', toLower(format('{0}-{1}-openai', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardLogCategories.value]", + "metrics": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardMetricsCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]", + "[resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-openai', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + }, + { + "copy": { + "name": "aiModel", + "count": "[length(concat(parameters('gptModels'), parameters('embeddingModels')))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "[format('model-{0}-{1}', replace(concat(parameters('gptModels'), parameters('embeddingModels'))[copyIndex()].modelName, '.', '-'), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "parent": { + "value": "[toLower(format('{0}-{1}-openai', parameters('appName'), parameters('environment')))]" + }, + "modelName": { + "value": "[concat(parameters('gptModels'), parameters('embeddingModels'))[copyIndex()].modelName]" + }, + "modelVersion": { + "value": "[concat(parameters('gptModels'), parameters('embeddingModels'))[copyIndex()].modelVersion]" + }, + "skuName": { + "value": "[concat(parameters('gptModels'), parameters('embeddingModels'))[copyIndex()].skuName]" + }, + "skuCapacity": { + "value": "[concat(parameters('gptModels'), parameters('embeddingModels'))[copyIndex()].skuCapacity]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "3668428745262748302" + } + }, + "parameters": { + "parent": { + "type": "string" + }, + "modelName": { + "type": "string" + }, + "modelVersion": { + "type": "string" + }, + "skuName": { + "type": "string" + }, + "skuCapacity": { + "type": "int" + } + }, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2025-06-01", + "name": "[format('{0}/{1}', parameters('parent'), parameters('modelName'))]", + "properties": { + "model": { + "format": "OpenAI", + "name": "[parameters('modelName')]", + "version": "[parameters('modelVersion')]" + } + }, + "sku": { + "name": "[parameters('skuName')]", + "capacity": "[parameters('skuCapacity')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-openai', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "condition": "[and(equals(parameters('authenticationType'), 'key'), parameters('configureApplicationPermissions'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storeOpenAISecret", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVault')]" + }, + "secretName": { + "value": "openAi-key" + }, + "secretValue": { + "value": "[listKeys(resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-openai', parameters('appName'), parameters('environment')))), '2024-10-01').key1]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "18077870757859157550" + } + }, + "parameters": { + "keyVaultName": { + "type": "string" + }, + "secretName": { + "type": "string" + }, + "secretValue": { + "type": "securestring" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2025-05-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretName'))]", + "properties": { + "value": "[parameters('secretValue')]" + } + } + ], + "outputs": { + "SecretUri": { + "type": "string", + "value": "[format('{0}secrets/{1}', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2025-05-01').vaultUri, parameters('secretName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-openai', parameters('appName'), parameters('environment'))))]" + ] + } + ], + "outputs": { + "openAIName": { + "type": "string", + "value": "[toLower(format('{0}-{1}-openai', parameters('appName'), parameters('environment')))]" + }, + "openAIResourceGroup": { + "type": "string", + "value": "[resourceGroup().name]" + }, + "openAIEndpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-openai', parameters('appName'), parameters('environment')))), '2024-10-01').endpoint]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "appServicePlan", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "16108907418935593505" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2022-03-01", + "name": "[toLower(format('{0}-{1}-asp', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "sku": { + "name": "P1v3", + "tier": "PremiumV3", + "size": "P1v3", + "capacity": 1 + }, + "kind": "app,linux,container", + "properties": { + "reserved": true, + "perSiteScaling": false, + "maximumElasticWorkerCount": 1, + "hyperV": false, + "targetWorkerCount": 0, + "targetWorkerSizeId": 0 + }, + "tags": "[parameters('tags')]" + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Web/serverfarms/{0}', toLower(format('{0}-{1}-asp', parameters('appName'), parameters('environment'))))]", + "name": "[toLower(format('{0}-diagnostics', toLower(format('{0}-{1}-asp', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": [], + "metrics": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardMetricsCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/serverfarms', toLower(format('{0}-{1}-asp', parameters('appName'), parameters('environment'))))]", + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + } + ], + "outputs": { + "appServicePlanId": { + "type": "string", + "value": "[resourceId('Microsoft.Web/serverfarms', toLower(format('{0}-{1}-asp', parameters('appName'), parameters('environment'))))]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "appService", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "acrName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'azureContainerRegistry'), '2025-04-01').outputs.acrName.value]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + }, + "appServicePlanId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'appServicePlan'), '2025-04-01').outputs.appServicePlanId.value]" + }, + "containerImageName": { + "value": "[variables('containerImageName')]" + }, + "azurePlatform": { + "value": "[parameters('cloudEnvironment')]" + }, + "cosmosDbName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'cosmosDB'), '2025-04-01').outputs.cosmosDbName.value]" + }, + "searchServiceName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'searchService'), '2025-04-01').outputs.searchServiceName.value]" + }, + "openAiServiceName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'openAI'), '2025-04-01').outputs.openAIName.value]" + }, + "openAiResourceGroupName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'openAI'), '2025-04-01').outputs.openAIResourceGroup.value]" + }, + "documentIntelligenceServiceName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'docIntel'), '2025-04-01').outputs.documentIntelligenceServiceName.value]" + }, + "appInsightsName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'applicationInsights'), '2025-04-01').outputs.appInsightsName.value]" + }, + "enterpriseAppClientId": { + "value": "[parameters('enterpriseAppClientId')]" + }, + "enterpriseAppClientSecret": { + "value": "[parameters('enterpriseAppClientSecret')]" + }, + "authenticationType": { + "value": "[parameters('authenticationType')]" + }, + "keyVaultUri": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultUri.value]" + }, + "enablePrivateNetworking": { + "value": "[parameters('enablePrivateNetworking')]" + }, + "appServiceSubnetId": "[if(parameters('enablePrivateNetworking'), createObject('value', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'virtualNetwork'), '2025-04-01').outputs.appServiceSubnetId.value), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "5779075922193615045" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + }, + "acrName": { + "type": "string" + }, + "appServicePlanId": { + "type": "string" + }, + "containerImageName": { + "type": "string" + }, + "azurePlatform": { + "type": "string" + }, + "cosmosDbName": { + "type": "string" + }, + "searchServiceName": { + "type": "string" + }, + "openAiServiceName": { + "type": "string" + }, + "openAiResourceGroupName": { + "type": "string" + }, + "documentIntelligenceServiceName": { + "type": "string" + }, + "appInsightsName": { + "type": "string" + }, + "enterpriseAppClientId": { + "type": "string", + "defaultValue": "" + }, + "authenticationType": { + "type": "string" + }, + "enterpriseAppClientSecret": { + "type": "securestring", + "defaultValue": "" + }, + "keyVaultUri": { + "type": "string" + }, + "enablePrivateNetworking": { + "type": "bool" + }, + "appServiceSubnetId": { + "type": "string", + "defaultValue": "" + } + }, + "variables": { + "acrDomain": "[if(equals(parameters('azurePlatform'), 'AzureUSGovernment'), '.azurecr.us', '.azurecr.io')]" + }, + "resources": [ + { + "type": "Microsoft.Web/sites", + "apiVersion": "2022-03-01", + "name": "[toLower(format('{0}-{1}-app', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "kind": "app,linux,container", + "properties": { + "serverFarmId": "[parameters('appServicePlanId')]", + "virtualNetworkSubnetId": "[if(not(equals(parameters('appServiceSubnetId'), '')), parameters('appServiceSubnetId'), null())]", + "publicNetworkAccess": "Enabled", + "vnetImagePullEnabled": "[if(parameters('enablePrivateNetworking'), true(), false())]", + "siteConfig": { + "linuxFxVersion": "[format('DOCKER|{0}', parameters('containerImageName'))]", + "acrUseManagedIdentityCreds": true, + "acrUserManagedIdentityID": "", + "alwaysOn": true, + "ftpsState": "Disabled", + "healthCheckPath": "/external/healthcheck", + "appSettings": "[flatten(createArray(createArray(createObject('name', 'AZURE_ENDPOINT', 'value', if(equals(parameters('azurePlatform'), 'AzureUSGovernment'), 'usgovernment', 'public')), createObject('name', 'SCM_DO_BUILD_DURING_DEPLOYMENT', 'value', 'false'), createObject('name', 'AZURE_COSMOS_ENDPOINT', 'value', reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDbName')), '2023-04-15').documentEndpoint), createObject('name', 'AZURE_COSMOS_AUTHENTICATION_TYPE', 'value', toLower(parameters('authenticationType')))), if(equals(parameters('authenticationType'), 'key'), createArray(createObject('name', 'AZURE_COSMOS_KEY', 'value', format('@Microsoft.KeyVault(SecretUri={0}secrets/cosmos-db-key)', parameters('keyVaultUri')))), createArray()), createArray(createObject('name', 'TENANT_ID', 'value', tenant().tenantId), createObject('name', 'CLIENT_ID', 'value', parameters('enterpriseAppClientId')), createObject('name', 'SECRET_KEY', 'value', if(not(empty(parameters('enterpriseAppClientSecret'))), parameters('enterpriseAppClientSecret'), format('@Microsoft.KeyVault(SecretUri={0}secrets/enterprise-app-client-secret)', parameters('keyVaultUri')))), createObject('name', 'MICROSOFT_PROVIDER_AUTHENTICATION_SECRET', 'value', format('@Microsoft.KeyVault(SecretUri={0}secrets/enterprise-app-client-secret)', parameters('keyVaultUri'))), createObject('name', 'DOCKER_REGISTRY_SERVER_URL', 'value', format('https://{0}{1}', parameters('acrName'), variables('acrDomain')))), if(equals(parameters('authenticationType'), 'key'), createArray(createObject('name', 'DOCKER_REGISTRY_SERVER_USERNAME', 'value', listCredentials(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), '2025-04-01').username)), createArray()), if(equals(parameters('authenticationType'), 'key'), createArray(createObject('name', 'DOCKER_REGISTRY_SERVER_PASSWORD', 'value', format('@Microsoft.KeyVault(SecretUri={0}secrets/container-registry-key)', parameters('keyVaultUri')))), createArray()), createArray(createObject('name', 'WEBSITE_AUTH_AAD_ALLOWED_TENANTS', 'value', tenant().tenantId), createObject('name', 'AZURE_OPENAI_RESOURCE_NAME', 'value', parameters('openAiServiceName')), createObject('name', 'AZURE_OPENAI_RESOURCE_GROUP_NAME', 'value', parameters('openAiResourceGroupName')), createObject('name', 'AZURE_OPENAI_URL', 'value', reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('openAiServiceName')), '2024-10-01').endpoint), createObject('name', 'AZURE_SEARCH_SERVICE_NAME', 'value', parameters('searchServiceName'))), if(equals(parameters('authenticationType'), 'key'), createArray(createObject('name', 'AZURE_SEARCH_API_KEY', 'value', format('@Microsoft.KeyVault(SecretUri={0}secrets/search-service-key)', parameters('keyVaultUri')))), createArray()), createArray(createObject('name', 'AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT', 'value', reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('documentIntelligenceServiceName')), '2025-06-01').endpoint)), if(equals(parameters('authenticationType'), 'key'), createArray(createObject('name', 'AZURE_DOCUMENT_INTELLIGENCE_API_KEY', 'value', format('@Microsoft.KeyVault(SecretUri={0}secrets/document-intelligence-key)', parameters('keyVaultUri')))), createArray()), createArray(createObject('name', 'APPINSIGHTS_INSTRUMENTATIONKEY', 'value', reference(resourceId('Microsoft.Insights/components', parameters('appInsightsName')), '2020-02-02').InstrumentationKey), createObject('name', 'APPLICATIONINSIGHTS_CONNECTION_STRING', 'value', reference(resourceId('Microsoft.Insights/components', parameters('appInsightsName')), '2020-02-02').ConnectionString), createObject('name', 'APPINSIGHTS_PROFILERFEATURE_VERSION', 'value', '1.0.0'), createObject('name', 'APPINSIGHTS_SNAPSHOTFEATURE_VERSION', 'value', '1.0.0'), createObject('name', 'APPLICATIONINSIGHTS_CONFIGURATION_CONTENT', 'value', ''), createObject('name', 'ApplicationInsightsAgent_EXTENSION_VERSION', 'value', '~3'), createObject('name', 'DiagnosticServices_EXTENSION_VERSION', 'value', '~3'), createObject('name', 'InstrumentationEngine_EXTENSION_VERSION', 'value', 'disabled'), createObject('name', 'SnapshotDebugger_EXTENSION_VERSION', 'value', 'disabled'), createObject('name', 'XDT_MicrosoftApplicationInsights_BaseExtensions', 'value', 'disabled'), createObject('name', 'XDT_MicrosoftApplicationInsights_Mode', 'value', 'recommended'), createObject('name', 'XDT_MicrosoftApplicationInsights_PreemptSdk', 'value', 'disabled'))))]" + }, + "clientAffinityEnabled": false, + "httpsOnly": true + }, + "identity": { + "type": "SystemAssigned" + }, + "tags": "[union(parameters('tags'), createObject('azd-service-name', 'web'))]" + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-app', parameters('appName'), parameters('environment'))), 'logs')]", + "properties": { + "httpLogs": { + "fileSystem": { + "enabled": true, + "retentionInDays": 7, + "retentionInMb": 35 + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', toLower(format('{0}-{1}-app', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Web/sites/{0}', toLower(format('{0}-{1}-app', parameters('appName'), parameters('environment'))))]", + "name": "[toLower(format('{0}-diagnostics', toLower(format('{0}-{1}-app', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.webAppLogCategories.value]", + "metrics": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardMetricsCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]", + "[resourceId('Microsoft.Web/sites', toLower(format('{0}-{1}-app', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-app', parameters('appName'), parameters('environment'))), 'authsettingsV2')]", + "properties": { + "globalValidation": { + "requireAuthentication": true, + "unauthenticatedClientAction": "RedirectToLoginPage", + "redirectToProvider": "azureActiveDirectory" + }, + "identityProviders": { + "azureActiveDirectory": { + "enabled": true, + "registration": { + "openIdIssuer": "[if(equals(parameters('azurePlatform'), 'AzureUSGovernment'), format('https://login.microsoftonline.us/{0}/', tenant().tenantId), format('https://sts.windows.net/{0}/', tenant().tenantId))]", + "clientId": "[parameters('enterpriseAppClientId')]", + "clientSecretSettingName": "MICROSOFT_PROVIDER_AUTHENTICATION_SECRET" + }, + "validation": { + "jwtClaimChecks": {}, + "allowedAudiences": [ + "[format('api://{0}', parameters('enterpriseAppClientId'))]", + "[parameters('enterpriseAppClientId')]" + ] + }, + "isAutoProvisioned": false + } + }, + "login": { + "routes": { + "logoutEndpoint": "/.auth/logout" + }, + "tokenStore": { + "enabled": true, + "tokenRefreshExtensionHours": 72, + "fileSystem": { + "directory": "/home/data/.auth" + } + }, + "preserveUrlFragmentsForLogins": false, + "allowedExternalRedirectUrls": [], + "cookieExpiration": { + "convention": "FixedTime", + "timeToExpiration": "08:00:00" + }, + "nonce": { + "validateNonce": true, + "nonceExpirationInterval": "00:05:00" + } + }, + "httpSettings": { + "requireHttps": true, + "routes": { + "apiPrefix": "/.auth" + }, + "forwardProxy": { + "convention": "NoProxy" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', toLower(format('{0}-{1}-app', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[toLower(format('{0}-{1}-app', parameters('appName'), parameters('environment')))]" + }, + "defaultHostName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Web/sites', toLower(format('{0}-{1}-app', parameters('appName'), parameters('environment')))), '2022-03-01').defaultHostName]" + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.Web/sites', toLower(format('{0}-{1}-app', parameters('appName'), parameters('environment'))))]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'azureContainerRegistry')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'applicationInsights')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'appServicePlan')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'cosmosDB')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'docIntel')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'openAI')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'searchService')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'virtualNetwork')]" + ] + }, + { + "condition": "[parameters('deployContentSafety')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "contentSafety", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + }, + "keyVault": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "authenticationType": { + "value": "[parameters('authenticationType')]" + }, + "configureApplicationPermissions": { + "value": "[parameters('configureApplicationPermissions')]" + }, + "enablePrivateNetworking": { + "value": "[parameters('enablePrivateNetworking')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "15792786587982505631" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "authenticationType": { + "type": "string" + }, + "configureApplicationPermissions": { + "type": "bool" + }, + "enablePrivateNetworking": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-06-01", + "name": "[toLower(format('{0}-{1}-contentsafety', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "kind": "ContentSafety", + "sku": { + "name": "S0" + }, + "properties": { + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), 'Disabled', 'Enabled')]", + "customSubDomainName": "[toLower(format('{0}-{1}-contentsafety', parameters('appName'), parameters('environment')))]" + }, + "tags": "[parameters('tags')]" + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', toLower(format('{0}-{1}-contentsafety', parameters('appName'), parameters('environment'))))]", + "name": "[toLower(format('{0}-diagnostics', toLower(format('{0}-{1}-contentsafety', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardLogCategories.value]", + "metrics": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardMetricsCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-contentsafety', parameters('appName'), parameters('environment'))))]", + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + }, + { + "condition": "[and(equals(parameters('authenticationType'), 'key'), parameters('configureApplicationPermissions'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storeContentSafetySecret", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVault')]" + }, + "secretName": { + "value": "content-safety-key" + }, + "secretValue": { + "value": "[listKeys(resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-contentsafety', parameters('appName'), parameters('environment')))), '2025-06-01').key1]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "18077870757859157550" + } + }, + "parameters": { + "keyVaultName": { + "type": "string" + }, + "secretName": { + "type": "string" + }, + "secretValue": { + "type": "securestring" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2025-05-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretName'))]", + "properties": { + "value": "[parameters('secretValue')]" + } + } + ], + "outputs": { + "SecretUri": { + "type": "string", + "value": "[format('{0}secrets/{1}', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2025-05-01').vaultUri, parameters('secretName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-contentsafety', parameters('appName'), parameters('environment'))))]" + ] + } + ], + "outputs": { + "contentSafetyName": { + "type": "string", + "value": "[toLower(format('{0}-{1}-contentsafety', parameters('appName'), parameters('environment')))]" + }, + "contentSafetyEndpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-contentsafety', parameters('appName'), parameters('environment')))), '2025-06-01').endpoint]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "condition": "[parameters('deployRedisCache')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "redisCache", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + }, + "keyVault": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "authenticationType": { + "value": "[parameters('authenticationType')]" + }, + "configureApplicationPermissions": { + "value": "[parameters('configureApplicationPermissions')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "10855483054816904335" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "authenticationType": { + "type": "string" + }, + "configureApplicationPermissions": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.Cache/redis", + "apiVersion": "2024-11-01", + "name": "[toLower(format('{0}-{1}-redis', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "properties": { + "sku": { + "name": "Standard", + "family": "C", + "capacity": 0 + }, + "enableNonSslPort": false, + "minimumTlsVersion": "1.2", + "redisConfiguration": { + "aad-enabled": "true" + } + }, + "tags": "[parameters('tags')]" + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Cache/redis/{0}', toLower(format('{0}-{1}-redis', parameters('appName'), parameters('environment'))))]", + "name": "[toLower(format('{0}-diagnostics', toLower(format('{0}-{1}-redis', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardLogCategories.value]", + "metrics": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardMetricsCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]", + "[resourceId('Microsoft.Cache/redis', toLower(format('{0}-{1}-redis', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + }, + { + "condition": "[and(equals(parameters('authenticationType'), 'key'), parameters('configureApplicationPermissions'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storeRedisCacheSecret", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVault')]" + }, + "secretName": { + "value": "redis-cache-key" + }, + "secretValue": { + "value": "[listKeys(resourceId('Microsoft.Cache/redis', toLower(format('{0}-{1}-redis', parameters('appName'), parameters('environment')))), '2024-11-01').primaryKey]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "18077870757859157550" + } + }, + "parameters": { + "keyVaultName": { + "type": "string" + }, + "secretName": { + "type": "string" + }, + "secretValue": { + "type": "securestring" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2025-05-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretName'))]", + "properties": { + "value": "[parameters('secretValue')]" + } + } + ], + "outputs": { + "SecretUri": { + "type": "string", + "value": "[format('{0}secrets/{1}', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2025-05-01').vaultUri, parameters('secretName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Cache/redis', toLower(format('{0}-{1}-redis', parameters('appName'), parameters('environment'))))]" + ] + } + ], + "outputs": { + "redisCacheName": { + "type": "string", + "value": "[toLower(format('{0}-{1}-redis', parameters('appName'), parameters('environment')))]" + }, + "redisCacheHostName": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Cache/redis', toLower(format('{0}-{1}-redis', parameters('appName'), parameters('environment')))), '2024-11-01').hostName]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "condition": "[parameters('deploySpeechService')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "speechService", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + }, + "keyVault": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "authenticationType": { + "value": "[parameters('authenticationType')]" + }, + "configureApplicationPermissions": { + "value": "[parameters('configureApplicationPermissions')]" + }, + "enablePrivateNetworking": { + "value": "[parameters('enablePrivateNetworking')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "12749834719068646180" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "authenticationType": { + "type": "string" + }, + "configureApplicationPermissions": { + "type": "bool" + }, + "enablePrivateNetworking": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2024-10-01", + "name": "[toLower(format('{0}-{1}-speech', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "kind": "SpeechServices", + "sku": { + "name": "S0" + }, + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), 'Disabled', 'Enabled')]", + "customSubDomainName": "[toLower(format('{0}-{1}-speech', parameters('appName'), parameters('environment')))]" + }, + "tags": "[parameters('tags')]" + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', toLower(format('{0}-{1}-speech', parameters('appName'), parameters('environment'))))]", + "name": "[toLower(format('{0}-diagnostics', toLower(format('{0}-{1}-speech', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardLogCategories.value]", + "metrics": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.standardMetricsCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]", + "[resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-speech', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + }, + { + "condition": "[and(equals(parameters('authenticationType'), 'key'), parameters('configureApplicationPermissions'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storeSpeechServiceSecret", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[parameters('keyVault')]" + }, + "secretName": { + "value": "speech-service-key" + }, + "secretValue": { + "value": "[listKeys(resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-speech', parameters('appName'), parameters('environment')))), '2024-10-01').key1]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "18077870757859157550" + } + }, + "parameters": { + "keyVaultName": { + "type": "string" + }, + "secretName": { + "type": "string" + }, + "secretValue": { + "type": "securestring" + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2025-05-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretName'))]", + "properties": { + "value": "[parameters('secretValue')]" + } + } + ], + "outputs": { + "SecretUri": { + "type": "string", + "value": "[format('{0}secrets/{1}', reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2025-05-01').vaultUri, parameters('secretName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-speech', parameters('appName'), parameters('environment'))))]" + ] + } + ], + "outputs": { + "speechServiceName": { + "type": "string", + "value": "[toLower(format('{0}-{1}-speech', parameters('appName'), parameters('environment')))]" + }, + "speechServiceEndpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', toLower(format('{0}-{1}-speech', parameters('appName'), parameters('environment')))), '2024-10-01').endpoint]" + }, + "speechServiceAuthenticationType": { + "type": "string", + "value": "[parameters('authenticationType')]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]" + ] + }, + { + "condition": "[parameters('deployVideoIndexerService')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "videoIndexerService", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "enableDiagLogging": { + "value": "[parameters('enableDiagLogging')]" + }, + "logAnalyticsId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.logAnalyticsId.value]" + }, + "storageAccount": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'storageAccount'), '2025-04-01').outputs.name.value]" + }, + "openAiServiceName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'openAI'), '2025-04-01').outputs.openAIName.value]" + }, + "enablePrivateNetworking": { + "value": "[parameters('enablePrivateNetworking')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "5008986833957108258" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "enableDiagLogging": { + "type": "bool" + }, + "logAnalyticsId": { + "type": "string" + }, + "storageAccount": { + "type": "string" + }, + "openAiServiceName": { + "type": "string" + }, + "enablePrivateNetworking": { + "type": "bool" + } + }, + "resources": [ + { + "type": "Microsoft.VideoIndexer/accounts", + "apiVersion": "2025-04-01", + "name": "[toLower(format('{0}-{1}-video', parameters('appName'), parameters('environment')))]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), 'Disabled', 'Enabled')]", + "storageServices": { + "resourceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccount'))]" + }, + "openAiServices": { + "resourceId": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('openAiServiceName'))]" + } + }, + "tags": "[parameters('tags')]" + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.VideoIndexer/accounts/{0}', toLower(format('{0}-{1}-video', parameters('appName'), parameters('environment'))))]", + "name": "[toLower(format('{0}-diagnostics', toLower(format('{0}-{1}-video', parameters('appName'), parameters('environment')))))]", + "properties": { + "workspaceId": "[parameters('logAnalyticsId')]", + "logs": "[reference(resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs'), '2025-04-01').outputs.limitedLogCategories.value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'diagnosticConfigs')]", + "[resourceId('Microsoft.VideoIndexer/accounts', toLower(format('{0}-{1}-video', parameters('appName'), parameters('environment'))))]" + ] + }, + { + "condition": "[parameters('enableDiagLogging')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "diagnosticConfigs", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "14992132232820252472" + } + }, + "variables": { + "standardRetentionPolicy": { + "enabled": false, + "days": 0 + }, + "standardLogCategories": [ + { + "categoryGroup": "Audit", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "limitedLogCategories": [ + { + "categoryGroup": "allLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "standardMetricsCategories": [ + { + "category": "AllMetrics", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "transactionMetricsCategories": [ + { + "category": "Transaction", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ], + "webAppLogCategories": [ + { + "category": "AppServiceAntivirusScanAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceHTTPLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceConsoleLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAppLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceFileAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceIPSecAuditLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServicePlatformLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + }, + { + "category": "AppServiceAuthenticationLogs", + "enabled": true, + "retentionPolicy": "[variables('standardRetentionPolicy')]" + } + ] + }, + "resources": [], + "outputs": { + "limitedLogCategories": { + "type": "array", + "value": "[variables('limitedLogCategories')]" + }, + "standardRetentionPolicy": { + "type": "object", + "value": "[variables('standardRetentionPolicy')]" + }, + "standardLogCategories": { + "type": "array", + "value": "[variables('standardLogCategories')]" + }, + "standardMetricsCategories": { + "type": "array", + "value": "[variables('standardMetricsCategories')]" + }, + "transactionMetricsCategories": { + "type": "array", + "value": "[variables('transactionMetricsCategories')]" + }, + "webAppLogCategories": { + "type": "array", + "value": "[variables('webAppLogCategories')]" + } + } + } + } + } + ], + "outputs": { + "videoIndexerServiceName": { + "type": "string", + "value": "[toLower(format('{0}-{1}-video', parameters('appName'), parameters('environment')))]" + }, + "videoIndexerAccountId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.VideoIndexer/accounts', toLower(format('{0}-{1}-video', parameters('appName'), parameters('environment')))), '2025-04-01').accountId]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'logAnalytics')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'openAI')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'storageAccount')]" + ] + }, + { + "condition": "[parameters('configureApplicationPermissions')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "setPermissions", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "webAppName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'appService'), '2025-04-01').outputs.name.value]" + }, + "authenticationType": { + "value": "[parameters('authenticationType')]" + }, + "enterpriseAppServicePrincipalId": { + "value": "[parameters('enterpriseAppServicePrincipalId')]" + }, + "keyVaultName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "cosmosDBName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'cosmosDB'), '2025-04-01').outputs.cosmosDbName.value]" + }, + "acrName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'azureContainerRegistry'), '2025-04-01').outputs.acrName.value]" + }, + "openAIName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'openAI'), '2025-04-01').outputs.openAIName.value]" + }, + "docIntelName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'docIntel'), '2025-04-01').outputs.documentIntelligenceServiceName.value]" + }, + "storageAccountName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'storageAccount'), '2025-04-01').outputs.name.value]" + }, + "searchServiceName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'searchService'), '2025-04-01').outputs.searchServiceName.value]" + }, + "speechServiceName": "[if(parameters('deploySpeechService'), createObject('value', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'speechService'), '2025-04-01').outputs.speechServiceName.value), createObject('value', ''))]", + "redisCacheName": "[if(parameters('deployRedisCache'), createObject('value', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'redisCache'), '2025-04-01').outputs.redisCacheName.value), createObject('value', ''))]", + "contentSafetyName": "[if(parameters('deployContentSafety'), createObject('value', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'contentSafety'), '2025-04-01').outputs.contentSafetyName.value), createObject('value', ''))]", + "videoIndexerName": "[if(parameters('deployVideoIndexerService'), createObject('value', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'videoIndexerService'), '2025-04-01').outputs.videoIndexerServiceName.value), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "17165122895470513967" + } + }, + "parameters": { + "webAppName": { + "type": "string" + }, + "authenticationType": { + "type": "string" + }, + "keyVaultName": { + "type": "string" + }, + "enterpriseAppServicePrincipalId": { + "type": "string" + }, + "cosmosDBName": { + "type": "string" + }, + "acrName": { + "type": "string" + }, + "openAIName": { + "type": "string" + }, + "docIntelName": { + "type": "string" + }, + "storageAccountName": { + "type": "string" + }, + "speechServiceName": { + "type": "string" + }, + "searchServiceName": { + "type": "string" + }, + "redisCacheName": { + "type": "string" + }, + "contentSafetyName": { + "type": "string" + }, + "videoIndexerName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('keyVaultName'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'kv-secrets-user')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", + "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2022-03-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[equals(parameters('authenticationType'), 'managed_identity')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('cosmosDBName'))]", + "name": "[guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'cosmos-contributor')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2022-03-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[equals(parameters('authenticationType'), 'managed_identity')]", + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2023-04-15", + "name": "[format('{0}/{1}', parameters('cosmosDBName'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'cosmos-data-contributor'))]", + "properties": { + "roleDefinitionId": "[format('{0}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002', resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')))]", + "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2022-03-01', 'full').identity.principalId]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName'))]" + } + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('acrName'))]", + "name": "[guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'acr-pull-role')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", + "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2022-03-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[equals(parameters('authenticationType'), 'managed_identity')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('openAIName'))]", + "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('openAIName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'openai-user')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2022-03-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[equals(parameters('authenticationType'), 'managed_identity')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('openAIName'))]", + "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('openAIName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'enterpriseApp-CognitiveServicesOpenAIUserRole')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "principalId": "[parameters('enterpriseAppServicePrincipalId')]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[equals(parameters('authenticationType'), 'managed_identity')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('docIntelName'))]", + "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('docIntelName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'doc-intel-user')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2022-03-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[equals(parameters('authenticationType'), 'managed_identity')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'storage-blob-data-contributor')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2022-03-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[and(not(equals(parameters('speechServiceName'), '')), equals(parameters('authenticationType'), 'managed_identity'))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('speechServiceName'))]", + "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('speechServiceName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'speech-service-user')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2022-03-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[equals(parameters('authenticationType'), 'managed_identity')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('searchServiceName'))]", + "name": "[guid(resourceId('Microsoft.Search/searchServices', parameters('searchServiceName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'search-index-data-contributor')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7')]", + "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2022-03-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[equals(parameters('authenticationType'), 'managed_identity')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('searchServiceName'))]", + "name": "[guid(resourceId('Microsoft.Search/searchServices', parameters('searchServiceName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'search-service-contributor')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2022-03-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[and(not(equals(parameters('contentSafetyName'), '')), equals(parameters('authenticationType'), 'managed_identity'))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('contentSafetyName'))]", + "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('contentSafetyName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'content-safety-user')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2022-03-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[not(equals(parameters('videoIndexerName'), ''))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), resourceId('Microsoft.VideoIndexer/accounts', parameters('videoIndexerName')), 'video-indexer-storage-blob-data-contributor')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "principalId": "[reference(resourceId('Microsoft.VideoIndexer/accounts', parameters('videoIndexerName')), '2025-04-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[not(equals(parameters('videoIndexerName'), ''))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('openAIName'))]", + "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('openAIName')), resourceId('Microsoft.VideoIndexer/accounts', parameters('videoIndexerName')), 'video-indexer-cog-services-contributor')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "principalId": "[reference(resourceId('Microsoft.VideoIndexer/accounts', parameters('videoIndexerName')), '2025-04-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[not(equals(parameters('videoIndexerName'), ''))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('openAIName'))]", + "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('openAIName')), resourceId('Microsoft.VideoIndexer/accounts', parameters('videoIndexerName')), 'video-indexer-cog-services-user')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "principalId": "[reference(resourceId('Microsoft.VideoIndexer/accounts', parameters('videoIndexerName')), '2025-04-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "condition": "[not(equals(parameters('redisCacheName'), ''))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Cache/redis/{0}', parameters('redisCacheName'))]", + "name": "[guid(resourceId('Microsoft.Cache/redis', parameters('redisCacheName')), resourceId('Microsoft.Web/sites', parameters('webAppName')), 'redis-cache-contributor')]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2022-03-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'azureContainerRegistry')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'appService')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'contentSafety')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'cosmosDB')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'docIntel')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'openAI')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'redisCache')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'searchService')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'speechService')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'storageAccount')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'videoIndexerService')]" + ] + }, + { + "condition": "[parameters('enablePrivateNetworking')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "privateNetworking", + "resourceGroup": "[variables('rgName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualNetworkId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'virtualNetwork'), '2025-04-01').outputs.vNetId.value]" + }, + "privateEndpointSubnetId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'virtualNetwork'), '2025-04-01').outputs.privateNetworkSubnetId.value]" + }, + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "tags": { + "value": "[variables('tags')]" + }, + "keyVaultName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "cosmosDBName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'cosmosDB'), '2025-04-01').outputs.cosmosDbName.value]" + }, + "acrName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'azureContainerRegistry'), '2025-04-01').outputs.acrName.value]" + }, + "searchServiceName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'searchService'), '2025-04-01').outputs.searchServiceName.value]" + }, + "docIntelName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'docIntel'), '2025-04-01').outputs.documentIntelligenceServiceName.value]" + }, + "storageAccountName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'storageAccount'), '2025-04-01').outputs.name.value]" + }, + "openAIName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'openAI'), '2025-04-01').outputs.openAIName.value]" + }, + "webAppName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'appService'), '2025-04-01').outputs.name.value]" + }, + "contentSafetyName": "[if(parameters('deployContentSafety'), createObject('value', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'contentSafety'), '2025-04-01').outputs.contentSafetyName.value), createObject('value', ''))]", + "speechServiceName": "[if(parameters('deploySpeechService'), createObject('value', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'speechService'), '2025-04-01').outputs.speechServiceName.value), createObject('value', ''))]", + "videoIndexerName": "[if(parameters('deployVideoIndexerService'), createObject('value', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'videoIndexerService'), '2025-04-01').outputs.videoIndexerServiceName.value), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "3584007603660948740" + } + }, + "parameters": { + "virtualNetworkId": { + "type": "string" + }, + "privateEndpointSubnetId": { + "type": "string" + }, + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "keyVaultName": { + "type": "string" + }, + "cosmosDBName": { + "type": "string" + }, + "acrName": { + "type": "string" + }, + "searchServiceName": { + "type": "string" + }, + "docIntelName": { + "type": "string" + }, + "storageAccountName": { + "type": "string" + }, + "openAIName": { + "type": "string" + }, + "webAppName": { + "type": "string" + }, + "contentSafetyName": { + "type": "string" + }, + "speechServiceName": { + "type": "string" + }, + "videoIndexerName": { + "type": "string" + } + }, + "variables": { + "$fxv#0": { + "azurecloud": { + "aisearch": "privatelink.search.windows.net", + "blobStorage": "privatelink.blob.core.windows.net", + "cognitiveServices": "privatelink.cognitiveservices.azure.com", + "containerRegistry": "privatelink.azurecr.io", + "cosmosDb": "privatelink.documents.azure.com", + "keyVault": "privatelink.vaultcore.azure.net", + "openAi": "privatelink.openai.azure.com", + "webSites": "privatelink.azurewebsites.net" + }, + "azureusgovernment": { + "aisearch": "privatelink.search.azure.us", + "blobStorage": "privatelink.blob.core.usgovcloudapi.net", + "cognitiveServices": "privatelink.cognitiveservices.azure.us", + "containerRegistry": "privatelink.azurecr.us", + "cosmosDb": "privatelink.documents.azure.us", + "keyVault": "privatelink.vaultcore.azure.us", + "openAi": "privatelink.openai.azure.us", + "webSites": "privatelink.azurewebsites.us" + } + }, + "cloudName": "[toLower(environment().name)]", + "privateDnsZoneData": "[variables('$fxv#0')]", + "aiSearchDnsZoneName": "[variables('privateDnsZoneData')[variables('cloudName')].aisearch]", + "blobStorageDnsZoneName": "[variables('privateDnsZoneData')[variables('cloudName')].blobStorage]", + "cognitiveServicesDnsZoneName": "[variables('privateDnsZoneData')[variables('cloudName')].cognitiveServices]", + "containerRegistryDnsZoneName": "[variables('privateDnsZoneData')[variables('cloudName')].containerRegistry]", + "cosmosDbDnsZoneName": "[variables('privateDnsZoneData')[variables('cloudName')].cosmosDb]", + "keyVaultDnsZoneName": "[variables('privateDnsZoneData')[variables('cloudName')].keyVault]", + "openAiDnsZoneName": "[variables('privateDnsZoneData')[variables('cloudName')].openAi]", + "webSitesDnsZoneName": "[variables('privateDnsZoneData')[variables('cloudName')].webSites]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "keyVaultDNSZone", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "zoneName": { + "value": "[variables('keyVaultDnsZoneName')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "name": { + "value": "kv" + }, + "vNetId": { + "value": "[parameters('virtualNetworkId')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "6963784194716310503" + } + }, + "parameters": { + "zoneName": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "name": { + "type": "string" + }, + "vNetId": { + "type": "string" + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('zoneName')]", + "location": "global", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('zoneName'), toLower(format('{0}-{1}-{2}-pe-dnszonelink', parameters('appName'), parameters('environment'), parameters('name'))))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vNetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + ] + } + ], + "outputs": { + "privateDnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + }, + "privateDnsZoneName": { + "type": "string", + "value": "[parameters('zoneName')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "keyVaultPE", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "kv" + }, + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "serviceResourceID": { + "value": "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName'))]" + }, + "subnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "groupIDs": { + "value": [ + "vault" + ] + }, + "privateDnsZoneIds": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', 'keyVaultDNSZone'), '2025-04-01').outputs.privateDnsZoneId.value]" + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1838240609393686445" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "serviceResourceID": { + "type": "string" + }, + "subnetId": { + "type": "string" + }, + "groupIDs": { + "type": "array" + }, + "privateDnsZoneIds": { + "type": "array", + "defaultValue": [] + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "condition": "[greater(length(parameters('privateDnsZoneIds')), 0)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))), 'default')]", + "properties": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDnsZoneIds'))]", + "input": { + "name": "[last(split(parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-05-01", + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "location": "[parameters('location')]", + "properties": { + "subnet": { + "id": "[parameters('subnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceID')]", + "groupIds": "[parameters('groupIDs')]" + } + } + ], + "customNetworkInterfaceName": "[toLower(format('{0}-{1}-{2}-nic', parameters('appName'), parameters('environment'), parameters('name')))]" + }, + "tags": "[parameters('tags')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyVaultDNSZone')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "cosmosDbDNSZone", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "zoneName": { + "value": "[variables('cosmosDbDnsZoneName')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "name": { + "value": "cosmosDb" + }, + "vNetId": { + "value": "[parameters('virtualNetworkId')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "6963784194716310503" + } + }, + "parameters": { + "zoneName": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "name": { + "type": "string" + }, + "vNetId": { + "type": "string" + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('zoneName')]", + "location": "global", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('zoneName'), toLower(format('{0}-{1}-{2}-pe-dnszonelink', parameters('appName'), parameters('environment'), parameters('name'))))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vNetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + ] + } + ], + "outputs": { + "privateDnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + }, + "privateDnsZoneName": { + "type": "string", + "value": "[parameters('zoneName')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "cosmosDbPE", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "cosmosDb" + }, + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "serviceResourceID": { + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName'))]" + }, + "subnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "groupIDs": { + "value": [ + "sql" + ] + }, + "privateDnsZoneIds": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', 'cosmosDbDNSZone'), '2025-04-01').outputs.privateDnsZoneId.value]" + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1838240609393686445" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "serviceResourceID": { + "type": "string" + }, + "subnetId": { + "type": "string" + }, + "groupIDs": { + "type": "array" + }, + "privateDnsZoneIds": { + "type": "array", + "defaultValue": [] + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "condition": "[greater(length(parameters('privateDnsZoneIds')), 0)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))), 'default')]", + "properties": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDnsZoneIds'))]", + "input": { + "name": "[last(split(parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-05-01", + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "location": "[parameters('location')]", + "properties": { + "subnet": { + "id": "[parameters('subnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceID')]", + "groupIds": "[parameters('groupIDs')]" + } + } + ], + "customNetworkInterfaceName": "[toLower(format('{0}-{1}-{2}-nic', parameters('appName'), parameters('environment'), parameters('name')))]" + }, + "tags": "[parameters('tags')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'cosmosDbDNSZone')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "acrDNSZone", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "zoneName": { + "value": "[variables('containerRegistryDnsZoneName')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "name": { + "value": "acr" + }, + "vNetId": { + "value": "[parameters('virtualNetworkId')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "6963784194716310503" + } + }, + "parameters": { + "zoneName": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "name": { + "type": "string" + }, + "vNetId": { + "type": "string" + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('zoneName')]", + "location": "global", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('zoneName'), toLower(format('{0}-{1}-{2}-pe-dnszonelink', parameters('appName'), parameters('environment'), parameters('name'))))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vNetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + ] + } + ], + "outputs": { + "privateDnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + }, + "privateDnsZoneName": { + "type": "string", + "value": "[parameters('zoneName')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "acrPE", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "acr" + }, + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "serviceResourceID": { + "value": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName'))]" + }, + "subnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "groupIDs": { + "value": [ + "registry" + ] + }, + "privateDnsZoneIds": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', 'acrDNSZone'), '2025-04-01').outputs.privateDnsZoneId.value]" + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1838240609393686445" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "serviceResourceID": { + "type": "string" + }, + "subnetId": { + "type": "string" + }, + "groupIDs": { + "type": "array" + }, + "privateDnsZoneIds": { + "type": "array", + "defaultValue": [] + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "condition": "[greater(length(parameters('privateDnsZoneIds')), 0)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))), 'default')]", + "properties": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDnsZoneIds'))]", + "input": { + "name": "[last(split(parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-05-01", + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "location": "[parameters('location')]", + "properties": { + "subnet": { + "id": "[parameters('subnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceID')]", + "groupIds": "[parameters('groupIDs')]" + } + } + ], + "customNetworkInterfaceName": "[toLower(format('{0}-{1}-{2}-nic', parameters('appName'), parameters('environment'), parameters('name')))]" + }, + "tags": "[parameters('tags')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'acrDNSZone')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "searchServiceDNSZone", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "zoneName": { + "value": "[variables('aiSearchDnsZoneName')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "name": { + "value": "searchService" + }, + "vNetId": { + "value": "[parameters('virtualNetworkId')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "6963784194716310503" + } + }, + "parameters": { + "zoneName": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "name": { + "type": "string" + }, + "vNetId": { + "type": "string" + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('zoneName')]", + "location": "global", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('zoneName'), toLower(format('{0}-{1}-{2}-pe-dnszonelink', parameters('appName'), parameters('environment'), parameters('name'))))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vNetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + ] + } + ], + "outputs": { + "privateDnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + }, + "privateDnsZoneName": { + "type": "string", + "value": "[parameters('zoneName')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "searchServicePE", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "searchService" + }, + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "serviceResourceID": { + "value": "[resourceId('Microsoft.Search/searchServices', parameters('searchServiceName'))]" + }, + "subnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "groupIDs": { + "value": [ + "searchService" + ] + }, + "privateDnsZoneIds": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', 'searchServiceDNSZone'), '2025-04-01').outputs.privateDnsZoneId.value]" + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1838240609393686445" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "serviceResourceID": { + "type": "string" + }, + "subnetId": { + "type": "string" + }, + "groupIDs": { + "type": "array" + }, + "privateDnsZoneIds": { + "type": "array", + "defaultValue": [] + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "condition": "[greater(length(parameters('privateDnsZoneIds')), 0)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))), 'default')]", + "properties": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDnsZoneIds'))]", + "input": { + "name": "[last(split(parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-05-01", + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "location": "[parameters('location')]", + "properties": { + "subnet": { + "id": "[parameters('subnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceID')]", + "groupIds": "[parameters('groupIDs')]" + } + } + ], + "customNetworkInterfaceName": "[toLower(format('{0}-{1}-{2}-nic', parameters('appName'), parameters('environment'), parameters('name')))]" + }, + "tags": "[parameters('tags')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'searchServiceDNSZone')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "docIntelDNSZone", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "zoneName": { + "value": "[variables('cognitiveServicesDnsZoneName')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "name": { + "value": "docIntelService" + }, + "vNetId": { + "value": "[parameters('virtualNetworkId')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "6963784194716310503" + } + }, + "parameters": { + "zoneName": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "name": { + "type": "string" + }, + "vNetId": { + "type": "string" + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('zoneName')]", + "location": "global", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('zoneName'), toLower(format('{0}-{1}-{2}-pe-dnszonelink', parameters('appName'), parameters('environment'), parameters('name'))))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vNetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + ] + } + ], + "outputs": { + "privateDnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + }, + "privateDnsZoneName": { + "type": "string", + "value": "[parameters('zoneName')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "docIntelPE", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "docIntelService" + }, + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "serviceResourceID": { + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('docIntelName'))]" + }, + "subnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "groupIDs": { + "value": [ + "account" + ] + }, + "privateDnsZoneIds": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', 'docIntelDNSZone'), '2025-04-01').outputs.privateDnsZoneId.value]" + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1838240609393686445" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "serviceResourceID": { + "type": "string" + }, + "subnetId": { + "type": "string" + }, + "groupIDs": { + "type": "array" + }, + "privateDnsZoneIds": { + "type": "array", + "defaultValue": [] + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "condition": "[greater(length(parameters('privateDnsZoneIds')), 0)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))), 'default')]", + "properties": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDnsZoneIds'))]", + "input": { + "name": "[last(split(parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-05-01", + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "location": "[parameters('location')]", + "properties": { + "subnet": { + "id": "[parameters('subnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceID')]", + "groupIds": "[parameters('groupIDs')]" + } + } + ], + "customNetworkInterfaceName": "[toLower(format('{0}-{1}-{2}-nic', parameters('appName'), parameters('environment'), parameters('name')))]" + }, + "tags": "[parameters('tags')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'docIntelDNSZone')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storageAccountDNSZone", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "zoneName": { + "value": "[variables('blobStorageDnsZoneName')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "name": { + "value": "storage" + }, + "vNetId": { + "value": "[parameters('virtualNetworkId')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "6963784194716310503" + } + }, + "parameters": { + "zoneName": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "name": { + "type": "string" + }, + "vNetId": { + "type": "string" + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('zoneName')]", + "location": "global", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('zoneName'), toLower(format('{0}-{1}-{2}-pe-dnszonelink', parameters('appName'), parameters('environment'), parameters('name'))))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vNetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + ] + } + ], + "outputs": { + "privateDnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + }, + "privateDnsZoneName": { + "type": "string", + "value": "[parameters('zoneName')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "storageAccountPE", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "storageAccount" + }, + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "serviceResourceID": { + "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" + }, + "subnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "groupIDs": { + "value": [ + "blob" + ] + }, + "privateDnsZoneIds": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', 'storageAccountDNSZone'), '2025-04-01').outputs.privateDnsZoneId.value]" + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1838240609393686445" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "serviceResourceID": { + "type": "string" + }, + "subnetId": { + "type": "string" + }, + "groupIDs": { + "type": "array" + }, + "privateDnsZoneIds": { + "type": "array", + "defaultValue": [] + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "condition": "[greater(length(parameters('privateDnsZoneIds')), 0)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))), 'default')]", + "properties": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDnsZoneIds'))]", + "input": { + "name": "[last(split(parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-05-01", + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "location": "[parameters('location')]", + "properties": { + "subnet": { + "id": "[parameters('subnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceID')]", + "groupIds": "[parameters('groupIDs')]" + } + } + ], + "customNetworkInterfaceName": "[toLower(format('{0}-{1}-{2}-nic', parameters('appName'), parameters('environment'), parameters('name')))]" + }, + "tags": "[parameters('tags')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'storageAccountDNSZone')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "openAiDNSZone", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "zoneName": { + "value": "[variables('openAiDnsZoneName')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "name": { + "value": "openAiService" + }, + "vNetId": { + "value": "[parameters('virtualNetworkId')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "6963784194716310503" + } + }, + "parameters": { + "zoneName": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "name": { + "type": "string" + }, + "vNetId": { + "type": "string" + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('zoneName')]", + "location": "global", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('zoneName'), toLower(format('{0}-{1}-{2}-pe-dnszonelink', parameters('appName'), parameters('environment'), parameters('name'))))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vNetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + ] + } + ], + "outputs": { + "privateDnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + }, + "privateDnsZoneName": { + "type": "string", + "value": "[parameters('zoneName')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "openAiPE", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "openAiService" + }, + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "serviceResourceID": { + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('openAIName'))]" + }, + "subnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "groupIDs": { + "value": [ + "account" + ] + }, + "privateDnsZoneIds": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', 'openAiDNSZone'), '2025-04-01').outputs.privateDnsZoneId.value]" + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1838240609393686445" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "serviceResourceID": { + "type": "string" + }, + "subnetId": { + "type": "string" + }, + "groupIDs": { + "type": "array" + }, + "privateDnsZoneIds": { + "type": "array", + "defaultValue": [] + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "condition": "[greater(length(parameters('privateDnsZoneIds')), 0)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))), 'default')]", + "properties": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDnsZoneIds'))]", + "input": { + "name": "[last(split(parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-05-01", + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "location": "[parameters('location')]", + "properties": { + "subnet": { + "id": "[parameters('subnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceID')]", + "groupIds": "[parameters('groupIDs')]" + } + } + ], + "customNetworkInterfaceName": "[toLower(format('{0}-{1}-{2}-nic', parameters('appName'), parameters('environment'), parameters('name')))]" + }, + "tags": "[parameters('tags')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'openAiDNSZone')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "webAppDNSZone", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "zoneName": { + "value": "[variables('webSitesDnsZoneName')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "name": { + "value": "webApp" + }, + "vNetId": { + "value": "[parameters('virtualNetworkId')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "6963784194716310503" + } + }, + "parameters": { + "zoneName": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "name": { + "type": "string" + }, + "vNetId": { + "type": "string" + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('zoneName')]", + "location": "global", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('zoneName'), toLower(format('{0}-{1}-{2}-pe-dnszonelink', parameters('appName'), parameters('environment'), parameters('name'))))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vNetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + ] + } + ], + "outputs": { + "privateDnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('zoneName'))]" + }, + "privateDnsZoneName": { + "type": "string", + "value": "[parameters('zoneName')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "webAppPE", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "webApp" + }, + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "serviceResourceID": { + "value": "[resourceId('Microsoft.Web/sites', parameters('webAppName'))]" + }, + "subnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "groupIDs": { + "value": [ + "sites" + ] + }, + "privateDnsZoneIds": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', 'webAppDNSZone'), '2025-04-01').outputs.privateDnsZoneId.value]" + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1838240609393686445" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "serviceResourceID": { + "type": "string" + }, + "subnetId": { + "type": "string" + }, + "groupIDs": { + "type": "array" + }, + "privateDnsZoneIds": { + "type": "array", + "defaultValue": [] + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "condition": "[greater(length(parameters('privateDnsZoneIds')), 0)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))), 'default')]", + "properties": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDnsZoneIds'))]", + "input": { + "name": "[last(split(parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-05-01", + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "location": "[parameters('location')]", + "properties": { + "subnet": { + "id": "[parameters('subnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceID')]", + "groupIds": "[parameters('groupIDs')]" + } + } + ], + "customNetworkInterfaceName": "[toLower(format('{0}-{1}-{2}-nic', parameters('appName'), parameters('environment'), parameters('name')))]" + }, + "tags": "[parameters('tags')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'webAppDNSZone')]" + ] + }, + { + "condition": "[not(equals(parameters('contentSafetyName'), ''))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "contentSafetyPE", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "contentSafety" + }, + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "serviceResourceID": { + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('contentSafetyName'))]" + }, + "subnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "groupIDs": { + "value": [ + "account" + ] + }, + "privateDnsZoneIds": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', 'docIntelDNSZone'), '2025-04-01').outputs.privateDnsZoneId.value]" + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1838240609393686445" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "serviceResourceID": { + "type": "string" + }, + "subnetId": { + "type": "string" + }, + "groupIDs": { + "type": "array" + }, + "privateDnsZoneIds": { + "type": "array", + "defaultValue": [] + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "condition": "[greater(length(parameters('privateDnsZoneIds')), 0)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))), 'default')]", + "properties": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDnsZoneIds'))]", + "input": { + "name": "[last(split(parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-05-01", + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "location": "[parameters('location')]", + "properties": { + "subnet": { + "id": "[parameters('subnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceID')]", + "groupIds": "[parameters('groupIDs')]" + } + } + ], + "customNetworkInterfaceName": "[toLower(format('{0}-{1}-{2}-nic', parameters('appName'), parameters('environment'), parameters('name')))]" + }, + "tags": "[parameters('tags')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'docIntelDNSZone')]" + ] + }, + { + "condition": "[not(equals(parameters('speechServiceName'), ''))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "speechServicePE", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "speechService" + }, + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "serviceResourceID": { + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('speechServiceName'))]" + }, + "subnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "groupIDs": { + "value": [ + "account" + ] + }, + "privateDnsZoneIds": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', 'docIntelDNSZone'), '2025-04-01').outputs.privateDnsZoneId.value]" + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1838240609393686445" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "serviceResourceID": { + "type": "string" + }, + "subnetId": { + "type": "string" + }, + "groupIDs": { + "type": "array" + }, + "privateDnsZoneIds": { + "type": "array", + "defaultValue": [] + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "condition": "[greater(length(parameters('privateDnsZoneIds')), 0)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))), 'default')]", + "properties": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDnsZoneIds'))]", + "input": { + "name": "[last(split(parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-05-01", + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "location": "[parameters('location')]", + "properties": { + "subnet": { + "id": "[parameters('subnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceID')]", + "groupIds": "[parameters('groupIDs')]" + } + } + ], + "customNetworkInterfaceName": "[toLower(format('{0}-{1}-{2}-nic', parameters('appName'), parameters('environment'), parameters('name')))]" + }, + "tags": "[parameters('tags')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'docIntelDNSZone')]" + ] + }, + { + "condition": "[not(equals(parameters('videoIndexerName'), ''))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "videoIndexerPE", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "videoIndexerService" + }, + "location": { + "value": "[parameters('location')]" + }, + "appName": { + "value": "[parameters('appName')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "serviceResourceID": { + "value": "[resourceId('Microsoft.VideoIndexer/accounts', parameters('videoIndexerName'))]" + }, + "subnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "groupIDs": { + "value": [ + "account" + ] + }, + "privateDnsZoneIds": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', 'docIntelDNSZone'), '2025-04-01').outputs.privateDnsZoneId.value]" + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "1838240609393686445" + } + }, + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "appName": { + "type": "string" + }, + "environment": { + "type": "string" + }, + "serviceResourceID": { + "type": "string" + }, + "subnetId": { + "type": "string" + }, + "groupIDs": { + "type": "array" + }, + "privateDnsZoneIds": { + "type": "array", + "defaultValue": [] + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "condition": "[greater(length(parameters('privateDnsZoneIds')), 0)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-05-01", + "name": "[format('{0}/{1}', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))), 'default')]", + "properties": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDnsZoneIds'))]", + "input": { + "name": "[last(split(parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name'))))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-05-01", + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "location": "[parameters('location')]", + "properties": { + "subnet": { + "id": "[parameters('subnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "[toLower(format('{0}-{1}-{2}-pe', parameters('appName'), parameters('environment'), parameters('name')))]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceID')]", + "groupIds": "[parameters('groupIDs')]" + } + } + ], + "customNetworkInterfaceName": "[toLower(format('{0}-{1}-{2}-nic', parameters('appName'), parameters('environment'), parameters('name')))]" + }, + "tags": "[parameters('tags')]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'docIntelDNSZone')]" + ] + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'azureContainerRegistry')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'appService')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'contentSafety')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'cosmosDB')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'docIntel')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'openAI')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName'))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'searchService')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'speechService')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'storageAccount')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'videoIndexerService')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'virtualNetwork')]" + ] + } + ], + "outputs": { + "var_acrName": { + "type": "string", + "value": "[toLower(format('{0}{1}acr', parameters('appName'), parameters('environment')))]" + }, + "var_authenticationType": { + "type": "string", + "value": "[toLower(parameters('authenticationType'))]" + }, + "var_blobStorageEndpoint": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'storageAccount'), '2025-04-01').outputs.endpoint.value]" + }, + "var_configureApplication": { + "type": "bool", + "value": "[parameters('configureApplicationPermissions')]" + }, + "var_contentSafetyEndpoint": { + "type": "string", + "value": "[if(parameters('deployContentSafety'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'contentSafety'), '2025-04-01').outputs.contentSafetyEndpoint.value, '')]" + }, + "var_cosmosDb_accountName": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'cosmosDB'), '2025-04-01').outputs.cosmosDbName.value]" + }, + "var_cosmosDb_uri": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'cosmosDB'), '2025-04-01').outputs.cosmosDbUri.value]" + }, + "var_deploymentLocation": { + "type": "string", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', variables('rgName')), '2022-09-01', 'full').location]" + }, + "var_documentIntelligenceServiceEndpoint": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'docIntel'), '2025-04-01').outputs.documentIntelligenceServiceEndpoint.value]" + }, + "var_keyVaultName": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultName.value]" + }, + "var_keyVaultUri": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'keyVault'), '2025-04-01').outputs.keyVaultUri.value]" + }, + "var_openAIEndpoint": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'openAI'), '2025-04-01').outputs.openAIEndpoint.value]" + }, + "var_openAIGPTModels": { + "type": "array", + "value": "[parameters('gptModels')]" + }, + "var_openAIResourceGroup": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'openAI'), '2025-04-01').outputs.openAIResourceGroup.value]" + }, + "var_openAIEmbeddingModels": { + "type": "array", + "value": "[parameters('embeddingModels')]" + }, + "var_redisCacheHostName": { + "type": "string", + "value": "[if(parameters('deployRedisCache'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'redisCache'), '2025-04-01').outputs.redisCacheHostName.value, '')]" + }, + "var_rgName": { + "type": "string", + "value": "[variables('rgName')]" + }, + "var_searchServiceEndpoint": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'searchService'), '2025-04-01').outputs.searchServiceEndpoint.value]" + }, + "var_speechServiceEndpoint": { + "type": "string", + "value": "[if(parameters('deploySpeechService'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'speechService'), '2025-04-01').outputs.speechServiceEndpoint.value, '')]" + }, + "var_subscriptionId": { + "type": "string", + "value": "[subscription().subscriptionId]" + }, + "var_videoIndexerAccountId": { + "type": "string", + "value": "[if(parameters('deployVideoIndexerService'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'videoIndexerService'), '2025-04-01').outputs.videoIndexerAccountId.value, '')]" + }, + "var_videoIndexerName": { + "type": "string", + "value": "[if(parameters('deployVideoIndexerService'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'videoIndexerService'), '2025-04-01').outputs.videoIndexerServiceName.value, '')]" + }, + "var_containerRegistry": { + "type": "string", + "value": "[variables('containerRegistry')]" + }, + "var_imageName": { + "type": "string", + "value": "[if(contains(parameters('imageName'), ':'), split(parameters('imageName'), ':')[0], parameters('imageName'))]" + }, + "var_imageTag": { + "type": "string", + "value": "[split(parameters('imageName'), ':')[1]]" + }, + "var_webService": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgName')), 'Microsoft.Resources/deployments', 'appService'), '2025-04-01').outputs.name.value]" + }, + "var_enablePrivateNetworking": { + "type": "bool", + "value": "[parameters('enablePrivateNetworking')]" + } + } +} \ No newline at end of file diff --git a/deployers/bicep/main.parameters.json b/deployers/bicep/main.parameters.json index e819bca9..dbd83d22 100644 --- a/deployers/bicep/main.parameters.json +++ b/deployers/bicep/main.parameters.json @@ -2,6 +2,7 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { + "location": { "value": "${AZURE_LOCATION}" }, @@ -11,26 +12,61 @@ "appName": { "value": "${env_DEPLOYMENT_APPNAME}" }, + "environment": { + "value": "${environment}" + }, "azdEnvironmentName": { "value": "${AZURE_ENV_NAME}" }, - "specialTags": { - "value": { - "Project": "SimpleChat", - "SystemOwner": "Steve Carroll" - } + "imageName": { + "value": "${CONTAINER_IMAGE_NAME}" }, "enterpriseAppClientId": { "value": "${ENTERPRISE_APP_CLIENT_ID}" }, + "enterpriseAppServicePrincipalId": { + "value": "${ENTERPRISE_APP_SERVICE_PRINCIPAL_ID}" + }, "enterpriseAppClientSecret": { "value": "${ENTERPRISE_APP_CLIENT_SECRET}" }, - "imageName": { - "value": "${CONTAINER_IMAGE_NAME}" - }, "authenticationType": { "value": "${AUTHENTICATION_TYPE}" + }, + "configureApplicationPermissions": { + "value": "${CONFIGURE_APPLICATION_PERMISSIONS}" + }, + "specialTags": { + "value": { + "Project": "SimpleChat" + } + }, + "enableDiagLogging": { + "value": "${ENABLE_DIAG_LOGGING}" + }, + "enablePrivateNetworking": { + "value": "${ENABLE_PRIVATE_NETWORKING}" + }, + "gptModels": { + "value": "${GPT_MODELS}" + }, + "embeddingModels": { + "value": "${EMBEDDING_MODELS}" + }, + "allowedIpAddresses": { + "value": "${ALLOWED_IP_RANGES}" + }, + "deployContentSafety": { + "value": "${DEPLOY_CONTENT_SAFETY}" + }, + "deployRedisCache": { + "value": "${DEPLOY_REDIS_CACHE}" + }, + "deploySpeechService": { + "value": "${DEPLOY_SPEECH_SERVICE}" + }, + "deployVideoIndexerService": { + "value": "${DEPLOY_VIDEO_INDEXER_SERVICE}" } } } \ No newline at end of file diff --git a/deployers/bicep/modules/appService.bicep b/deployers/bicep/modules/appService.bicep index 4f70f03a..5d9aa471 100644 --- a/deployers/bicep/modules/appService.bicep +++ b/deployers/bicep/modules/appService.bicep @@ -24,6 +24,8 @@ param authenticationType string @secure() param enterpriseAppClientSecret string = '' param keyVaultUri string +param enablePrivateNetworking bool +param appServiceSubnetId string = '' // Import diagnostic settings configurations module diagnosticConfigs 'diagnosticSettings.bicep' = if (enableDiagLogging) { @@ -62,6 +64,11 @@ resource webApp 'Microsoft.Web/sites@2022-03-01' = { kind: 'app,linux,container' properties: { serverFarmId: appServicePlanId + + virtualNetworkSubnetId: appServiceSubnetId != '' ? appServiceSubnetId : null + publicNetworkAccess: 'Enabled' // configuration is set in post provision step in azure.yaml with post deployment script + vnetImagePullEnabled: enablePrivateNetworking ? true : false + siteConfig: { linuxFxVersion: 'DOCKER|${containerImageName}' acrUseManagedIdentityCreds: true @@ -198,7 +205,7 @@ resource authSettings 'Microsoft.Web/sites/config@2022-03-01' = { azureActiveDirectory: { enabled: true registration: { - openIdIssuer: 'https://sts.windows.net/${tenant().tenantId}/' + openIdIssuer: azurePlatform == 'AzureUSGovernment' ? 'https://login.microsoftonline.us/${tenant().tenantId}/' : 'https://sts.windows.net/${tenant().tenantId}/' clientId: enterpriseAppClientId clientSecretSettingName: 'MICROSOFT_PROVIDER_AUTHENTICATION_SECRET' } diff --git a/deployers/bicep/modules/appServicePlan.bicep b/deployers/bicep/modules/appServicePlan.bicep index 15e5a1ce..e6be76e3 100644 --- a/deployers/bicep/modules/appServicePlan.bicep +++ b/deployers/bicep/modules/appServicePlan.bicep @@ -32,6 +32,7 @@ resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { targetWorkerCount: 0 targetWorkerSizeId: 0 } + tags: tags } diff --git a/deployers/bicep/modules/azureContainerRegistry.bicep b/deployers/bicep/modules/azureContainerRegistry.bicep index 4023447e..d4ed9b6d 100644 --- a/deployers/bicep/modules/azureContainerRegistry.bicep +++ b/deployers/bicep/modules/azureContainerRegistry.bicep @@ -11,6 +11,9 @@ param keyVault string param authenticationType string param configureApplicationPermissions bool +param enablePrivateNetworking bool +param allowedIpAddresses array = [] + // Import diagnostic settings configurations module diagnosticConfigs 'diagnosticSettings.bicep' = if (enableDiagLogging) { name: 'diagnosticConfigs' @@ -22,11 +25,15 @@ resource acr 'Microsoft.ContainerRegistry/registries@2025-04-01' = { location: location sku: { - name: 'Standard' + name: enablePrivateNetworking ? 'Premium' : 'Standard' } properties: { adminUserEnabled: true - publicNetworkAccess: 'Enabled' + publicNetworkAccess: 'Enabled' // configuration is set in post provision step in azure.yaml with post deployment script + networkRuleSet: enablePrivateNetworking ? { + defaultAction: 'Deny' + ipRules: allowedIpAddresses + } : null } tags: tags } diff --git a/deployers/bicep/modules/contentSafety.bicep b/deployers/bicep/modules/contentSafety.bicep index 59c40125..02ba06c7 100644 --- a/deployers/bicep/modules/contentSafety.bicep +++ b/deployers/bicep/modules/contentSafety.bicep @@ -12,6 +12,8 @@ param keyVault string param authenticationType string param configureApplicationPermissions bool +param enablePrivateNetworking bool + // Import diagnostic settings configurations module diagnosticConfigs 'diagnosticSettings.bicep' = if (enableDiagLogging) { name: 'diagnosticConfigs' @@ -26,7 +28,7 @@ resource contentSafety 'Microsoft.CognitiveServices/accounts@2025-06-01' = { name: 'S0' } properties: { - publicNetworkAccess: 'Enabled' + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' customSubDomainName: toLower('${appName}-${environment}-contentsafety') } tags: tags diff --git a/deployers/bicep/modules/cosmosDb.bicep b/deployers/bicep/modules/cosmosDb.bicep index 67ca669a..abb6f674 100644 --- a/deployers/bicep/modules/cosmosDb.bicep +++ b/deployers/bicep/modules/cosmosDb.bicep @@ -12,6 +12,9 @@ param keyVault string param authenticationType string param configureApplicationPermissions bool +param enablePrivateNetworking bool +param allowedIpAddresses array = [] + // Import diagnostic settings configurations module diagnosticConfigs 'diagnosticSettings.bicep' = if (enableDiagLogging) { name: 'diagnosticConfigs' @@ -23,12 +26,16 @@ resource cosmosDb 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = { location: location kind: 'GlobalDocumentDB' properties: { + publicNetworkAccess: 'Enabled' // configuration is set in post provision step in azure.yaml with post deployment script databaseAccountOfferType: 'Standard' capabilities: [ { name: 'EnableServerless' } ] + isVirtualNetworkFilterEnabled: enablePrivateNetworking ? true : false + ipRules: enablePrivateNetworking ? allowedIpAddresses : [] + locations: [ { locationName: location @@ -85,8 +92,8 @@ resource cosmosDiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-pre //========================================================= // store cosmos db keys in key vault if using key authentication and configure app permissions = true //========================================================= -module storeEnterpriseAppSecret 'keyVault-Secrets.bicep' = if (authenticationType == 'key' && configureApplicationPermissions) { - name: 'storeEnterpriseAppSecret' +module storeCosmosDbSecret 'keyVault-Secrets.bicep' = if (authenticationType == 'key' && configureApplicationPermissions) { + name: 'storeCosmosDbSecret' params: { keyVaultName: keyVault secretName: 'cosmos-db-key' diff --git a/deployers/bicep/modules/documentIntelligence.bicep b/deployers/bicep/modules/documentIntelligence.bicep index 70b343e3..b340c795 100644 --- a/deployers/bicep/modules/documentIntelligence.bicep +++ b/deployers/bicep/modules/documentIntelligence.bicep @@ -12,6 +12,8 @@ param keyVault string param authenticationType string param configureApplicationPermissions bool +param enablePrivateNetworking bool + // Import diagnostic settings configurations module diagnosticConfigs 'diagnosticSettings.bicep' = if (enableDiagLogging) { name: 'diagnosticConfigs' @@ -26,7 +28,7 @@ resource docIntel 'Microsoft.CognitiveServices/accounts@2025-06-01' = { name: 'S0' } properties: { - publicNetworkAccess: 'Enabled' + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' customSubDomainName: toLower('${appName}-${environment}-docintel') } tags: tags diff --git a/deployers/bicep/modules/keyVault.bicep b/deployers/bicep/modules/keyVault.bicep index 6b2786de..384fe398 100644 --- a/deployers/bicep/modules/keyVault.bicep +++ b/deployers/bicep/modules/keyVault.bicep @@ -27,7 +27,7 @@ resource kv 'Microsoft.KeyVault/vaults@2024-11-01' = { enabledForDeployment: false enabledForDiskEncryption: false enabledForTemplateDeployment: false - publicNetworkAccess: 'Enabled' + publicNetworkAccess: 'Enabled' // configuration is set in post provision step in azure.yaml with post deployment script enableRbacAuthorization: true } tags: tags diff --git a/deployers/bicep/modules/openAI.bicep b/deployers/bicep/modules/openAI.bicep index 9a04b79d..a5d2e1b6 100644 --- a/deployers/bicep/modules/openAI.bicep +++ b/deployers/bicep/modules/openAI.bicep @@ -15,6 +15,8 @@ param configureApplicationPermissions bool param gptModels array param embeddingModels array +param enablePrivateNetworking bool + // Import diagnostic settings configurations module diagnosticConfigs 'diagnosticSettings.bicep' = if (enableDiagLogging) { name: 'diagnosticConfigs' @@ -32,7 +34,7 @@ resource openAI 'Microsoft.CognitiveServices/accounts@2024-10-01' = { type: 'SystemAssigned' } properties: { - publicNetworkAccess: 'Enabled' + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' customSubDomainName: toLower('${appName}-${environment}-openai') } tags: tags diff --git a/deployers/bicep/modules/privateDNS.bicep b/deployers/bicep/modules/privateDNS.bicep new file mode 100644 index 00000000..9306b6cc --- /dev/null +++ b/deployers/bicep/modules/privateDNS.bicep @@ -0,0 +1,29 @@ +targetScope = 'resourceGroup' + +param zoneName string +param appName string +param environment string +param name string +param vNetId string +param tags object + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: zoneName + location: 'global' + tags: tags +} + +resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + name: toLower('${appName}-${environment}-${name}-pe-dnszonelink') + parent: privateDnsZone + location: 'global' + properties: { + registrationEnabled: false + virtualNetwork: { + id: vNetId + } + } +} + +output privateDnsZoneId string = privateDnsZone.id +output privateDnsZoneName string = privateDnsZone.name diff --git a/deployers/bicep/modules/privateDNSZones.json b/deployers/bicep/modules/privateDNSZones.json new file mode 100644 index 00000000..66d22738 --- /dev/null +++ b/deployers/bicep/modules/privateDNSZones.json @@ -0,0 +1,22 @@ +{ + "azurecloud": { + "aisearch": "privatelink.search.windows.net", + "blobStorage": "privatelink.blob.core.windows.net", + "cognitiveServices": "privatelink.cognitiveservices.azure.com", + "containerRegistry": "privatelink.azurecr.io", + "cosmosDb": "privatelink.documents.azure.com", + "keyVault": "privatelink.vaultcore.azure.net", + "openAi": "privatelink.openai.azure.com", + "webSites": "privatelink.azurewebsites.net" + }, + "azureusgovernment": { + "aisearch": "privatelink.search.azure.us", + "blobStorage": "privatelink.blob.core.usgovcloudapi.net", + "cognitiveServices": "privatelink.cognitiveservices.azure.us", + "containerRegistry": "privatelink.azurecr.us", + "cosmosDb": "privatelink.documents.azure.us", + "keyVault": "privatelink.vaultcore.azure.us", + "openAi": "privatelink.openai.azure.us", + "webSites": "privatelink.azurewebsites.us" + } +} \ No newline at end of file diff --git a/deployers/bicep/modules/privateEndpoint.bicep b/deployers/bicep/modules/privateEndpoint.bicep new file mode 100644 index 00000000..aaa52da0 --- /dev/null +++ b/deployers/bicep/modules/privateEndpoint.bicep @@ -0,0 +1,56 @@ +targetScope = 'resourceGroup' + +param name string +param location string +param appName string +param environment string +param serviceResourceID string +param subnetId string +param groupIDs array +param privateDnsZoneIds array = [] +param tags object + +resource dnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-05-01' = if (length(privateDnsZoneIds) > 0) { + name: 'default' + parent: privateEndpoint + properties: { + privateDnsZoneConfigs: [ + for zoneId in privateDnsZoneIds: { + name: last(split(zoneId, '/')) + properties: { + privateDnsZoneId: zoneId + } + } + ] + } +} + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2021-05-01' = { + name: toLower('${appName}-${environment}-${name}-pe') + location: location + properties: { + subnet: { + id: subnetId + } + privateLinkServiceConnections: [ + { + name: toLower('${appName}-${environment}-${name}-pe') + properties: { + privateLinkServiceId: serviceResourceID + groupIds: groupIDs + } + } + ] + customNetworkInterfaceName: toLower('${appName}-${environment}-${name}-nic') + } + tags: tags +} + +// @description('Private endpoint resource ID') +// output privateEndpointId string = privateEndpoint.id + +// @description('Private endpoint name') +// output privateEndpointName string = privateEndpoint.name + +// @description('Private IP address assigned to the private endpoint') +// output privateIpAddress string = privateEndpoint.properties.customDnsConfigs[0].ipAddresses[0] diff --git a/deployers/bicep/modules/privateNetworking.bicep b/deployers/bicep/modules/privateNetworking.bicep new file mode 100644 index 00000000..abf5ac39 --- /dev/null +++ b/deployers/bicep/modules/privateNetworking.bicep @@ -0,0 +1,449 @@ +targetScope = 'resourceGroup' + +param virtualNetworkId string +param privateEndpointSubnetId string + +param location string +param appName string +param environment string +param tags object + +param keyVaultName string +param cosmosDBName string +param acrName string +param searchServiceName string +param docIntelName string +param storageAccountName string +param openAIName string +param webAppName string + + +// // redis cache +param contentSafetyName string +param speechServiceName string +param videoIndexerName string + +//========================================================= +// privateDNSZoneNames +var cloudName = toLower(az.environment().name) +var privateDnsZoneData = loadJsonContent('privateDNSZones.json') + +var aiSearchDnsZoneName = privateDnsZoneData[cloudName].aisearch +var blobStorageDnsZoneName = privateDnsZoneData[cloudName].blobStorage +var cognitiveServicesDnsZoneName = privateDnsZoneData[cloudName].cognitiveServices +var containerRegistryDnsZoneName = privateDnsZoneData[cloudName].containerRegistry +var cosmosDbDnsZoneName = privateDnsZoneData[cloudName].cosmosDb +var keyVaultDnsZoneName = privateDnsZoneData[cloudName].keyVault +var openAiDnsZoneName = privateDnsZoneData[cloudName].openAi +var webSitesDnsZoneName = privateDnsZoneData[cloudName].webSites + +//========================================================= +// key vault +//========================================================= +resource kv 'Microsoft.KeyVault/vaults@2025-05-01' existing = { + name: keyVaultName +} + +module keyVaultDNSZone 'privateDNS.bicep' = { + name: 'keyVaultDNSZone' + params: { + zoneName: keyVaultDnsZoneName + appName: appName + environment: environment + name: 'kv' + vNetId: virtualNetworkId + tags: tags + } +} + +module keyVaultPE 'privateEndpoint.bicep' = { + name: 'keyVaultPE' + dependsOn: [ + kv + keyVaultDNSZone + ] + params: { + name: 'kv' + location: location + appName: appName + environment: environment + serviceResourceID: kv.id + subnetId: privateEndpointSubnetId + groupIDs: [ + 'vault' + ] + privateDnsZoneIds: [ + keyVaultDNSZone.outputs.privateDnsZoneId + ] + tags: tags + } +} +//========================================================= +// cosmos db +//========================================================= +resource cosmosDb 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' existing = { + name: cosmosDBName +} + +module cosmosDbDNSZone 'privateDNS.bicep' = { + name: 'cosmosDbDNSZone' + params: { + zoneName: cosmosDbDnsZoneName + appName: appName + environment: environment + name: 'cosmosDb' + vNetId: virtualNetworkId + tags: tags + } +} + +module cosmosDbPE 'privateEndpoint.bicep' = { + name: 'cosmosDbPE' + dependsOn: [ + cosmosDb + cosmosDbDNSZone + ] + params: { + name: 'cosmosDb' + location: location + appName: appName + environment: environment + serviceResourceID: cosmosDb.id + subnetId: privateEndpointSubnetId + groupIDs: [ + 'sql' + ] + privateDnsZoneIds: [ + cosmosDbDNSZone.outputs.privateDnsZoneId + ] + tags: tags + } +} +//========================================================= +// azure container registry +//========================================================= +resource acr 'Microsoft.ContainerRegistry/registries@2025-04-01' existing = { + name: acrName +} + +module acrDNSZone 'privateDNS.bicep' = { + name: 'acrDNSZone' + params: { + zoneName: containerRegistryDnsZoneName + appName: appName + environment: environment + name: 'acr' + vNetId: virtualNetworkId + tags: tags + } +} + +module acrPE 'privateEndpoint.bicep' = { + name: 'acrPE' + dependsOn: [ + acr + acrDNSZone + ] + params: { + name: 'acr' + location: location + appName: appName + environment: environment + serviceResourceID: acr.id + subnetId: privateEndpointSubnetId + groupIDs: [ + 'registry' + ] + privateDnsZoneIds: [ + acrDNSZone.outputs.privateDnsZoneId + ] + tags: tags + } +} +//========================================================= +// search service +//========================================================= +resource searchService 'Microsoft.Search/searchServices@2025-05-01' existing = { + name: searchServiceName +} + +module searchServiceDNSZone 'privateDNS.bicep' = { + name: 'searchServiceDNSZone' + params: { + zoneName: aiSearchDnsZoneName + appName: appName + environment: environment + name: 'searchService' + vNetId: virtualNetworkId + tags: tags + } +} + +module searchServicePE 'privateEndpoint.bicep' = { + name: 'searchServicePE' + dependsOn: [ + searchService + searchServiceDNSZone + ] + params: { + name: 'searchService' + location: location + appName: appName + environment: environment + serviceResourceID: searchService.id + subnetId: privateEndpointSubnetId + groupIDs: [ + 'searchService' + ] + privateDnsZoneIds: [ + searchServiceDNSZone.outputs.privateDnsZoneId + ] + tags: tags + } +} +//========================================================= +// document intelligence service +//========================================================= +resource docIntelService 'Microsoft.CognitiveServices/accounts@2024-10-01' existing = if (docIntelName != '') { + name: docIntelName +} + +module docIntelDNSZone 'privateDNS.bicep' = { + name: 'docIntelDNSZone' + params: { + zoneName: cognitiveServicesDnsZoneName + appName: appName + environment: environment + name: 'docIntelService' + vNetId: virtualNetworkId + tags: tags + } +} + +module docIntelPE 'privateEndpoint.bicep' = { + name: 'docIntelPE' + dependsOn: [ + docIntelService + docIntelDNSZone + ] + params: { + name: 'docIntelService' + location: location + appName: appName + environment: environment + serviceResourceID: docIntelService.id + subnetId: privateEndpointSubnetId + groupIDs: [ + 'account' + ] + privateDnsZoneIds: [ + docIntelDNSZone.outputs.privateDnsZoneId + ] + tags: tags + } +} +//========================================================= +// storage account +//========================================================= +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' existing = { + name: storageAccountName +} + +module storageAccountDNSZone 'privateDNS.bicep' = { + name: 'storageAccountDNSZone' + params: { + zoneName: blobStorageDnsZoneName + appName: appName + environment: environment + name: 'storage' + vNetId: virtualNetworkId + tags: tags + } +} + +module storageAccountPE 'privateEndpoint.bicep' = { + name: 'storageAccountPE' + dependsOn: [ + storageAccount + storageAccountDNSZone + ] + params: { + name: 'storageAccount' + location: location + appName: appName + environment: environment + serviceResourceID: storageAccount.id + subnetId: privateEndpointSubnetId + groupIDs: [ + 'blob' + ] + privateDnsZoneIds: [ + storageAccountDNSZone.outputs.privateDnsZoneId + ] + tags: tags + } +} +//========================================================= +resource openAiService 'Microsoft.CognitiveServices/accounts@2024-10-01' existing = { + name: openAIName +} + +module openAiDNSZone 'privateDNS.bicep' = { + name: 'openAiDNSZone' + params: { + zoneName: openAiDnsZoneName + appName: appName + environment: environment + name: 'openAiService' + vNetId: virtualNetworkId + tags: tags + } +} + +module openAiPE 'privateEndpoint.bicep' = { + name: 'openAiPE' + dependsOn: [ + openAiService + openAiDNSZone + ] + params: { + name: 'openAiService' + location: location + appName: appName + environment: environment + serviceResourceID: openAiService.id + subnetId: privateEndpointSubnetId + groupIDs: [ + 'account' + ] + privateDnsZoneIds: [ + openAiDNSZone.outputs.privateDnsZoneId + ] + tags: tags + } +} +//========================================================= +// web app +//========================================================= +resource webApp 'Microsoft.Web/sites@2022-03-01' existing = { + name: webAppName +} + +module webAppDNSZone 'privateDNS.bicep' = { + name: 'webAppDNSZone' + params: { + zoneName: webSitesDnsZoneName + appName: appName + environment: environment + name: 'webApp' + vNetId: virtualNetworkId + tags: tags + } +} + +module webAppPE 'privateEndpoint.bicep' = { + name: 'webAppPE' + dependsOn: [ + webApp + webAppDNSZone + ] + params: { + name: 'webApp' + location: location + appName: appName + environment: environment + serviceResourceID: webApp.id + subnetId: privateEndpointSubnetId + groupIDs: [ + 'sites' + ] + privateDnsZoneIds: [ + webAppDNSZone.outputs.privateDnsZoneId + ] + tags: tags + } +} +//========================================================= +// content safety service - Optional +//========================================================= +resource contentSafety 'Microsoft.CognitiveServices/accounts@2025-06-01' existing = if (contentSafetyName != '') { + name: contentSafetyName +} + +module contentSafetyPE 'privateEndpoint.bicep' = if (contentSafetyName != '') { + name: 'contentSafetyPE' + dependsOn: [ + contentSafety + ] + params: { + name: 'contentSafety' + location: location + appName: appName + environment: environment + serviceResourceID: contentSafety.id + subnetId: privateEndpointSubnetId + groupIDs: [ + 'account' + ] + privateDnsZoneIds: [ + docIntelDNSZone.outputs.privateDnsZoneId + ] + tags: tags + } +} +//========================================================= +// speech service - Optional +//========================================================= +resource speechService 'Microsoft.CognitiveServices/accounts@2024-10-01' existing = if (speechServiceName != '') { + name: speechServiceName +} + +module speechServicePE 'privateEndpoint.bicep' = if (speechServiceName != '') { + name: 'speechServicePE' + dependsOn: [ + speechService + ] + params: { + name: 'speechService' + location: location + appName: appName + environment: environment + serviceResourceID: speechService.id + subnetId: privateEndpointSubnetId + groupIDs: [ + 'account' + ] + privateDnsZoneIds: [ + docIntelDNSZone.outputs.privateDnsZoneId + ] + tags: tags + } +} +//========================================================= +// video indexer service - Optional +//========================================================= +resource videoIndexerService 'Microsoft.VideoIndexer/accounts@2025-04-01' existing = if (videoIndexerName != '') { + name: videoIndexerName +} + +module videoIndexerPE 'privateEndpoint.bicep' = if (videoIndexerName != '') { + name: 'videoIndexerPE' + dependsOn: [ + videoIndexerService + ] + params: { + name: 'videoIndexerService' + location: location + appName: appName + environment: environment + serviceResourceID: videoIndexerService.id + subnetId: privateEndpointSubnetId + groupIDs: [ + 'account' + ] + privateDnsZoneIds: [ + docIntelDNSZone.outputs.privateDnsZoneId + ] + tags: tags + } +} diff --git a/deployers/bicep/modules/redisCache.bicep b/deployers/bicep/modules/redisCache.bicep index faab2e23..e782318a 100644 --- a/deployers/bicep/modules/redisCache.bicep +++ b/deployers/bicep/modules/redisCache.bicep @@ -62,3 +62,4 @@ module redisCacheSecret 'keyVault-Secrets.bicep' = if (authenticationType == 'ke } output redisCacheName string = redisCache.name +output redisCacheHostName string = redisCache.properties.hostName diff --git a/deployers/bicep/modules/search.bicep b/deployers/bicep/modules/search.bicep index 2786b91e..c474ade5 100644 --- a/deployers/bicep/modules/search.bicep +++ b/deployers/bicep/modules/search.bicep @@ -12,6 +12,8 @@ param keyVault string param authenticationType string param configureApplicationPermissions bool +param enablePrivateNetworking bool + // Import diagnostic settings configurations module diagnosticConfigs 'diagnosticSettings.bicep' = if (enableDiagLogging) { name: 'diagnosticConfigs' @@ -27,7 +29,7 @@ resource searchService 'Microsoft.Search/searchServices@2025-05-01' = { properties: { #disable-next-line BCP036 // template is incorrect hostingMode: 'default' - publicNetworkAccess: 'Enabled' + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' replicaCount: 1 partitionCount: 1 authOptions: { @@ -54,7 +56,7 @@ resource searchDiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-pre //========================================================= // store search Service keys in key vault if using key authentication and configure app permissions = true //========================================================= -module searchServiceSecret 'keyVault-Secrets.bicep' = if (configureApplicationPermissions) { +module searchServiceSecret 'keyVault-Secrets.bicep' = if (authenticationType == 'key' && configureApplicationPermissions) { name: 'storeSearchServiceSecret' params: { keyVaultName: keyVault diff --git a/deployers/bicep/modules/setPermissions.bicep b/deployers/bicep/modules/setPermissions.bicep index 33564d8e..675c12b9 100644 --- a/deployers/bicep/modules/setPermissions.bicep +++ b/deployers/bicep/modules/setPermissions.bicep @@ -11,6 +11,7 @@ param docIntelName string param storageAccountName string param speechServiceName string param searchServiceName string +param redisCacheName string param contentSafetyName string param videoIndexerName string @@ -50,6 +51,10 @@ resource searchService 'Microsoft.Search/searchServices@2025-05-01' existing = { name: searchServiceName } +resource redisCache 'Microsoft.Cache/Redis@2024-11-01' existing = if (redisCacheName != '') { + name: redisCacheName +} + resource contentSafety 'Microsoft.CognitiveServices/accounts@2025-06-01' existing = if (contentSafetyName != '') { name: contentSafetyName } @@ -269,3 +274,17 @@ resource videoIndexerStorageCogServicesUserRole 'Microsoft.Authorization/roleAss principalType: 'ServicePrincipal' } } + +// grant the managed identity access to redis cache as a Redis Cache Contributor +resource redisCacheContributorRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (redisCacheName != '') { + name: guid(redisCache.id, webApp.id, 'redis-cache-contributor') + scope: redisCache + properties: { + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'e0f68234-74aa-48ed-b826-c38b57376e17' + ) + principalId: webApp.identity.principalId + principalType: 'ServicePrincipal' + } +} diff --git a/deployers/bicep/modules/speechService.bicep b/deployers/bicep/modules/speechService.bicep index 17391ac7..d7f1de1f 100644 --- a/deployers/bicep/modules/speechService.bicep +++ b/deployers/bicep/modules/speechService.bicep @@ -12,6 +12,8 @@ param keyVault string param authenticationType string param configureApplicationPermissions bool +param enablePrivateNetworking bool + // Import diagnostic settings configurations module diagnosticConfigs 'diagnosticSettings.bicep' = if (enableDiagLogging) { name: 'diagnosticConfigs' @@ -29,7 +31,7 @@ resource speechService 'Microsoft.CognitiveServices/accounts@2024-10-01' = { type: 'SystemAssigned' } properties: { - publicNetworkAccess: 'Enabled' + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' customSubDomainName: toLower('${appName}-${environment}-speech') } tags: tags diff --git a/deployers/bicep/modules/storageAccount.bicep b/deployers/bicep/modules/storageAccount.bicep index 7e10ccae..cf38ac52 100644 --- a/deployers/bicep/modules/storageAccount.bicep +++ b/deployers/bicep/modules/storageAccount.bicep @@ -12,6 +12,8 @@ param keyVault string param authenticationType string param configureApplicationPermissions bool +param enablePrivateNetworking bool + // Import diagnostic settings configurations module diagnosticConfigs 'diagnosticSettings.bicep' = if (enableDiagLogging) { name: 'diagnosticConfigs' @@ -26,7 +28,9 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { name: 'Standard_LRS' } kind: 'StorageV2' + properties: { + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' accessTier: 'Hot' allowBlobPublicAccess: false allowSharedKeyAccess: true diff --git a/deployers/bicep/modules/videoIndexer.bicep b/deployers/bicep/modules/videoIndexer.bicep index 8f41544a..786d7108 100644 --- a/deployers/bicep/modules/videoIndexer.bicep +++ b/deployers/bicep/modules/videoIndexer.bicep @@ -11,6 +11,8 @@ param logAnalyticsId string param storageAccount string param openAiServiceName string +param enablePrivateNetworking bool + // Import diagnostic settings configurations module diagnosticConfigs 'diagnosticSettings.bicep' = if (enableDiagLogging) { name: 'diagnosticConfigs' @@ -33,7 +35,7 @@ resource videoIndexerService 'Microsoft.VideoIndexer/accounts@2025-04-01' = { type: 'SystemAssigned' } properties: { - publicNetworkAccess: 'Enabled' + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' storageServices: { resourceId: storage.id } diff --git a/deployers/bicep/modules/virtualNetwork.bicep b/deployers/bicep/modules/virtualNetwork.bicep new file mode 100644 index 00000000..03952410 --- /dev/null +++ b/deployers/bicep/modules/virtualNetwork.bicep @@ -0,0 +1,44 @@ +targetScope = 'resourceGroup' + +param location string +param vNetName string +param addressSpaces array +param subnetConfigs array +param tags object + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-05-01' = { + name: vNetName + location: location + properties: { + addressSpace: { + addressPrefixes: addressSpaces + } + subnets: [for subnet in subnetConfigs: { + name: subnet.name + properties: { + addressPrefix: subnet.addressPrefix + privateEndpointNetworkPolicies: subnet.enablePrivateEndpointNetworkPolicies ? 'Enabled' : 'Disabled' + privateLinkServiceNetworkPolicies: subnet.enablePrivateLinkServiceNetworkPolicies ? 'Enabled' : 'Disabled' + delegations: subnet.name == 'AppServiceIntegration' ? [ + { + name: 'delegation' + properties: { + serviceName: 'Microsoft.Web/serverFarms' + } + } + ] : [] + } + }] + } + tags: tags +} + +var subnetIds = [for subnet in subnetConfigs: resourceId('Microsoft.Network/virtualNetworks/subnets', vNetName, subnet.name)] +var subnetNames = [for subnet in subnetConfigs: subnet.name] +var appServiceIntegrationSubnetIndex = indexOf(subnetNames, 'AppServiceIntegration') +var privateEndpointIndex = indexOf(subnetNames, 'PrivateEndpoints') + +// output results +output vNetId string = virtualNetwork.id +output privateNetworkSubnetId string = privateEndpointIndex == -1 ? '' : subnetIds[privateEndpointIndex] +output appServiceSubnetId string = appServiceIntegrationSubnetIndex == -1 ? '' : subnetIds[appServiceIntegrationSubnetIndex] diff --git a/deployers/bicep/params/scjulie1.bicepparam b/deployers/bicep/params/scjulie1.bicepparam deleted file mode 100644 index 6b115dc3..00000000 --- a/deployers/bicep/params/scjulie1.bicepparam +++ /dev/null @@ -1,18 +0,0 @@ -using '../main.bicep' - -param azurePlatform = 'AzureUSGovernment' // or 'AzureCloud' -param tenantId = '' // Provide via --parameters or a secure way -param location = 'usgovvirginia' // or your preferred region -param resourceOwnerId = 'johndoe@domain.com' -param environment = 'sbx' -param baseName = 'julie1' -param acrName = 'acr8000' -param acrResourceGroupName = 'sc-emma1-sbx1-rg' // RG of your ACR -param imageName = 'simple-chat:2025-05-15_7' // Be specific with tags -param useExistingOpenAiInstance = true -param existingAzureOpenAiResourceName = 'gregazureopenai1' // if useExistingOpenAiInstance is true -param existingAzureOpenAiResourceGroupName = 'azureopenairg' // if useExistingOpenAiInstance is true -param appRegistrationClientId = 'a9acf8e2-441d-4aca-84f6-a83b3e820644' // scbingo1-ar -param appRegistrationClientSecret = '' // Provide via --parameters or a secure way -param appRegistrationSpObjectId = '364c5131-27b3-4ac1-bf95-0bd55106a109' -// Other SKUs can be overridden if needed diff --git a/deployers/bicep/postconfig.py b/deployers/bicep/postconfig.py index 44406da2..5a12b56f 100644 --- a/deployers/bicep/postconfig.py +++ b/deployers/bicep/postconfig.py @@ -45,8 +45,8 @@ var_blobStorageEndpoint = os.getenv("var_blobStorageEndpoint") var_contentSafetyEndpoint = os.getenv("var_contentSafetyEndpoint") var_searchServiceEndpoint = os.getenv("var_searchServiceEndpoint") -var_documentIntelligenceServiceEndpoint = os.getenv( - "var_documentIntelligenceServiceEndpoint") +var_documentIntelligenceServiceEndpoint = os.getenv("var_documentIntelligenceServiceEndpoint") +var_redisCacheHostName = os.getenv("var_redisCacheHostName") var_videoIndexerName = os.getenv("var_videoIndexerName") var_videoIndexerLocation = os.getenv("var_deploymentLocation") var_videoIndexerAccountId = os.getenv("var_videoIndexerAccountId") @@ -133,7 +133,7 @@ item["enable_content_safety"] = True item["content_safety_endpoint"] = var_contentSafetyEndpoint item["content_safety_authentication_type"] = var_authenticationType -if keyvault_client: +if keyvault_client and var_authenticationType == "key": try: contentSafety_key_secret = keyvault_client.get_secret( "content-safety-key") @@ -143,13 +143,27 @@ print( f"Warning: Could not retrieve content-safety-key from Key Vault: {e}") +# Redis Cache Configuration +if var_redisCacheHostName and var_redisCacheHostName.strip(): + item["enable_redis_cache"] = True +item["redis_url"] = var_redisCacheHostName +item["redis_auth_type"] = var_authenticationType +if keyvault_client and var_authenticationType == "key": + try: + redis_key_secret = keyvault_client.get_secret("redis-cache-key") + item["redis_key"] = redis_key_secret.value + print("Retrieved redis cache key from Key Vault") + except Exception as e: + print( + f"Warning: Could not retrieve redis-cache-key from Key Vault: {e}") + # Safety > Conversation Archiving item["enable_conversation_archiving"] = True # Search and Extract > Azure AI Search item["azure_ai_search_endpoint"] = var_searchServiceEndpoint item["azure_ai_search_authentication_type"] = var_authenticationType -if keyvault_client: +if keyvault_client and var_authenticationType == "key": try: search_key_secret = keyvault_client.get_secret("search-service-key") item["azure_ai_search_key"] = search_key_secret.value @@ -161,7 +175,7 @@ # Search and Extract > Azure Document Intelligence item["azure_document_intelligence_endpoint"] = var_documentIntelligenceServiceEndpoint item["azure_document_intelligence_authentication_type"] = var_authenticationType -if keyvault_client: +if keyvault_client and var_authenticationType == "key": try: documentIntelligence_key_secret = keyvault_client.get_secret( "document-intelligence-key") @@ -186,7 +200,7 @@ item["enable_audio_file_support"] = True item["speech_service_endpoint"] = var_speechServiceEndpoint item["speech_service_location"] = var_speechServiceLocation -if keyvault_client: +if keyvault_client and var_authenticationType == "key": try: speech_key_secret = keyvault_client.get_secret("speech-service-key") item["speech_service_key"] = speech_key_secret.value