diff --git a/scenarios/secure-baseline-multitenant/README.md b/scenarios/secure-baseline-multitenant/README.md index bedf75ca..84b6f8ae 100644 --- a/scenarios/secure-baseline-multitenant/README.md +++ b/scenarios/secure-baseline-multitenant/README.md @@ -1,6 +1,6 @@ # Multitenant App Service Secure Baseline -This reference architecture shows how to run a web-app workload on Azure App Services in a secure configuration. This secure baseline follow [Defence in Depth](https://learn.microsoft.com/en-us/shows/azure-videos/defense-in-depth-security-in-azure) approach to protect AppService workload against cloud vulnerabilities along with additional [Well-Architected Framework](https://learn.microsoft.com/en-us/azure/architecture/framework/) pillars to enable a resilient solution. +This reference architecture shows how to run a web-app workload on Azure App Services in a secure configuration. This secure baseline follow [Defense in Depth](https://learn.microsoft.com/en-us/shows/azure-videos/defense-in-depth-security-in-azure) approach to protect AppService workload against cloud vulnerabilities along with additional [Well-Architected Framework](https://learn.microsoft.com/en-us/azure/architecture/framework/) pillars to enable a resilient solution. ## Quick deployment to Azure You can deploy the current LZA directly in your azure subscription by hitting the button below or using Azure Dev CLI. @@ -33,6 +33,7 @@ You can deploy the current LZA directly in your azure subscription by hitting th * App Services use [Virtual Network (VNet) Integration](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration#regional-virtual-network-integration) to connect to backend Azure services over a private VNet. * [Azure Cache for Redis](https://azure.microsoft.com/services/cache/) provides a high-performance distributed cache for output, session, and general-purpose caching. * [Azure SQL DB](https://azure.microsoft.com/en-us/products/azure-sql/database/) provides a fully managed relational database service for back-end application services. +* [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview) provides REST API access to OpenAI's powerful language models including the GPT-4, GPT-3.5-Turbo, and Embeddings model series. * [Private Endpoints](https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview) allow connections to Azure services from private VNets, and allow the public endpoints on these services to be disabled. * [Azure private DNS](https://learn.microsoft.com/en-us/azure/dns/private-dns-overview) automatically configures and updates the DNS records required by private endpoint services. * [Azure Key Vault](https://azure.microsoft.com/services/key-vault/) securely stores secrets and certificates to be accessed by Azure services. @@ -57,7 +58,7 @@ For network and subnet topology details, see the [Azure sample template](https:/ ## Scenario details The scenario describes a secure baseline that allows you to have a protect environment and a good starting point for designing your solution. -Defence in depth is a security strategy that involves implementing multiple layers of defence at different points within a network or system. The idea is that if one layer of defence is breached, the next layer will be able to prevent an attacker from gaining access to sensitive information or critical systems. +Defense in depth is a security strategy that involves implementing multiple layers of defense at different points within a network or system. The idea is that if one layer of defense is breached, the next layer will be able to prevent an attacker from gaining access to sensitive information or critical systems. This approach is a key point that drives the architecture decisions -> * Use isolated network layers for the different components. * Use protected AD based access via Managed Identity (where possible). diff --git a/scenarios/secure-baseline-multitenant/azure-resource-manager/README.md b/scenarios/secure-baseline-multitenant/azure-resource-manager/README.md index 9f0d76e7..47971c08 100644 --- a/scenarios/secure-baseline-multitenant/azure-resource-manager/README.md +++ b/scenarios/secure-baseline-multitenant/azure-resource-manager/README.md @@ -15,7 +15,7 @@ Alternatively, you can clone the repo and follow the instractions below ## Deploy the App Service Landing Zone ARM template file Before deploying the Bicep IaC artifacts, you need to review and customize the values of the parameters in the [main.parameters.jsonc](main.parameters.jsonc) file. -The table below summurizes the avaialble parameters and the possible values that can be set. +The table below summarizes the available parameters and the possible values that can be set. | Name | Description | Example | @@ -40,6 +40,7 @@ The table below summurizes the avaialble parameters and the possible values that |deployAzureSql|Feature Flag: Deploy (or not) an Azure SQL with default database| |deployAppConfig|Feature Flag: Deploy (or not) an Azure app configuration| |deployJumpHost|Feature Flag: Deploy (or not) an Azure virtual machine (to be used as jumphost)| +|deployOpenAi|Feature Flag: Deploy (or not) an Azure OpenAI account. ATTENTION: At the time of writing, [OpenAI is in preview](https://learn.microsoft.com/azure/ai-services/openai/chatgpt-quickstart#prerequisites) and available in limited regions. |false |sqlServerAdministrators|The Azure Active Directory (AAD) administrator group used for SQL Server authentication. The Azure AD group must be created before running deployment. This has three values that need to be filled, as shown below
**login**: the name of the AAD Group
**sid**: the object id of the AAD Group
**tenantId**: The tenantId of the AAD || After the parameters have been initialized, you can deploy the Landing Zone Accelerator resources with the following `az cli` command: diff --git a/scenarios/secure-baseline-multitenant/azure-resource-manager/main-portal-ux.json b/scenarios/secure-baseline-multitenant/azure-resource-manager/main-portal-ux.json index ea15ce3c..231e5305 100644 --- a/scenarios/secure-baseline-multitenant/azure-resource-manager/main-portal-ux.json +++ b/scenarios/secure-baseline-multitenant/azure-resource-manager/main-portal-ux.json @@ -957,7 +957,7 @@ "label": "Deploy Redis", "subLabel": "", "defaultValue": "false", - "toolTip": "set to true if you want to a redis cache", + "toolTip": "set to true if you want to deploy a redis cache", "constraints": { "required": false, "allowedValues": [ @@ -1022,6 +1022,48 @@ }, "infoMessages": [], "visible": true + }, + { + "name": "openAiInfoTitle", + "type": "Microsoft.Common.Section", + "label": "Azure OpenAI Feature Flag", + "elements": [] + }, + { + "name": "openAiInfo", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Please be informed that currently Azure OpenAI is in preview and only available in limited regions. Also your azure subscription needs to be allow-listed.", + "link": { + "label": "Azure OpenAI Prerequisites", + "uri": "https://learn.microsoft.com/azure/ai-services/openai/chatgpt-quickstart#prerequisites" + } + } + }, + { + "name": "deployOpenAI", + "type": "Microsoft.Common.DropDown", + "label": "Deploy OpenAI", + "subLabel": "what is that??", + "defaultValue": "false", + "toolTip": "set to true if you want to deploy OpenAI", + "constraints": { + "required": false, + "allowedValues": [ + { + "label": "true", + "value": true + }, + { + "label": "false", + "value": false + } + ], + "validations": [] + }, + "infoMessages": [], + "visible": true } ] } @@ -1055,7 +1097,7 @@ "deployRedis": "[steps('flags').deployRedis]", "deployAppConfig": "[steps('flags').deployAppConfig]", "autoApproveAfdPrivateEndpoint": "[steps('flags').autoApproveAfdPrivateEndpoint]", - + "deployOpenAI": "[steps('flags').deployOpenAI]", "githubRepository": "[if( equals ( steps('extra').jumpBoxGithubRunnerSection.deployGitHubRunner, true), steps('extra').jumpBoxGithubRunnerSection.githubRepositoryUrl , '' )]", "githubToken": "[if( equals ( steps('extra').jumpBoxGithubRunnerSection.deployGitHubRunner, true), steps('extra').jumpBoxGithubRunnerSection.githubToken , '' )]", "adoOrganization": "[if( equals ( steps('extra').jumpBoxAzDoAgentSection.deployAzDoSelfHostedAgent, true), steps('extra').jumpBoxAzDoAgentSection.adoOrganization , '' )]", diff --git a/scenarios/secure-baseline-multitenant/azure-resource-manager/main.json b/scenarios/secure-baseline-multitenant/azure-resource-manager/main.json index f195f2ce..0fb27d3e 100644 --- a/scenarios/secure-baseline-multitenant/azure-resource-manager/main.json +++ b/scenarios/secure-baseline-multitenant/azure-resource-manager/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "14977110681381139912" + "version": "0.22.6.54827", + "templateHash": "16705310804684473871" } }, "parameters": { @@ -207,6 +207,20 @@ "description": "set to true if you want to deploy a jumpbox/devops VM" } }, + "deployOpenAi": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "set to true if you want to deploy a openai VM" + } + }, + "deployOpenAiGptModel": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Deploy (or not) a model on the openAI Account. This is used only as a sample to show how to deploy a model on the OpenAI account." + } + }, "githubRepository": { "type": "string", "defaultValue": "", @@ -330,8 +344,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "1979265534967934369" + "version": "0.22.6.54827", + "templateHash": "4653287124364209339" } }, "parameters": { @@ -1111,6 +1125,11 @@ "nameUnique": "[if(endsWith(take(replace(variables('nut'), variables('ph'), 'dnsrec'), 50), variables('d')), take(replace(variables('nut'), variables('ph'), 'dnsrec'), sub(50, 1)), take(replace(variables('nut'), variables('ph'), 'dnsrec'), 50))]", "slug": "dnsrec" }, + "openAiDeployment": { + "name": "[if(endsWith(take(replace(variables('nt'), variables('ph'), 'oaidep'), 50), variables('d')), take(replace(variables('nt'), variables('ph'), 'oaidep'), sub(50, 1)), take(replace(variables('nt'), variables('ph'), 'oaidep'), 50))]", + "nameUnique": "[if(endsWith(take(replace(variables('nut'), variables('ph'), 'oaidep'), 50), variables('d')), take(replace(variables('nut'), variables('ph'), 'oaidep'), sub(50, 1)), take(replace(variables('nut'), variables('ph'), 'oaidep'), 50))]", + "slug": "oaidep" + }, "pointToSiteVpnGateway": { "name": "[if(endsWith(take(replace(variables('nt'), variables('ph'), 'vpngw'), 80), variables('d')), take(replace(variables('nt'), variables('ph'), 'vpngw'), sub(80, 1)), take(replace(variables('nt'), variables('ph'), 'vpngw'), 80))]", "nameUnique": "[if(endsWith(take(replace(variables('nut'), variables('ph'), 'vpngw'), 80), variables('d')), take(replace(variables('nut'), variables('ph'), 'vpngw'), sub(80, 1)), take(replace(variables('nut'), variables('ph'), 'vpngw'), 80))]", @@ -1503,8 +1522,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "12984224661558000879" + "version": "0.22.6.54827", + "templateHash": "6164280833125289566" } }, "parameters": { @@ -1899,8 +1918,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "4064709153537522093" + "version": "0.22.6.54827", + "templateHash": "11030415745656825690" } }, "parameters": { @@ -2035,8 +2054,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "6389026189341578887" + "version": "0.22.6.54827", + "templateHash": "18249007994089590825" } }, "parameters": { @@ -2145,8 +2164,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "8724449987696627131" + "version": "0.22.6.54827", + "templateHash": "13078266356101405252" } }, "parameters": { @@ -2339,8 +2358,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "11535560972989472774" + "version": "0.22.6.54827", + "templateHash": "7950762402225783762" } }, "parameters": { @@ -2496,8 +2515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "15323422695799345105" + "version": "0.22.6.54827", + "templateHash": "13538279806026434176" } }, "parameters": { @@ -2760,8 +2779,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "8724449987696627131" + "version": "0.22.6.54827", + "templateHash": "13078266356101405252" } }, "parameters": { @@ -3072,6 +3091,12 @@ "deployAppConfig": { "value": "[parameters('deployAppConfig')]" }, + "deployOpenAi": { + "value": "[parameters('deployOpenAi')]" + }, + "deployOpenAiGptModel": { + "value": "[parameters('deployOpenAiGptModel')]" + }, "autoApproveAfdPrivateEndpoint": { "value": "[parameters('autoApproveAfdPrivateEndpoint')]" }, @@ -3100,8 +3125,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "1518181767354561309" + "version": "0.22.6.54827", + "templateHash": "2333769453532271009" } }, "parameters": { @@ -3189,6 +3214,19 @@ "description": "Deploy (or not) an Azure virtual machine (to be used as jumphost)" } }, + "deployOpenAi": { + "type": "bool", + "metadata": { + "description": "Deploy (or not) an Azure OpenAI account. ATTENTION: At the time of writing this, OpenAI is in preview and only available in limited regions: look here: https://learn.microsoft.com/azure/ai-services/openai/chatgpt-quickstart#prerequisites" + } + }, + "deployOpenAiGptModel": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Deploy (or not) a model on the openAI Account. This is used only as a sample to show how to deploy a model on the OpenAI account." + } + }, "githubRepository": { "type": "string", "defaultValue": "", @@ -3326,7 +3364,9 @@ "frontDoorWaf": "[parameters('naming').frontDoorFirewallPolicy.name]", "routeTable": "[parameters('naming').routeTable.name]", "routeEgressLockdown": "[format('{0}-egress-lockdown', parameters('naming').route.name)]", - "idAfdApprovePeAutoApprover": "[take(format('{0}-AfdApprovePe', parameters('naming').userAssignedManagedIdentity.name), 128)]" + "idAfdApprovePeAutoApprover": "[take(format('{0}-AfdApprovePe', parameters('naming').userAssignedManagedIdentity.name), 128)]", + "openAiAccount": "[parameters('naming').cognitiveAccount.nameUnique]", + "openAiDeployment": "[parameters('naming').openAiDeployment.name]" }, "udrRoutes": [ { @@ -3404,8 +3444,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "4064709153537522093" + "version": "0.22.6.54827", + "templateHash": "11030415745656825690" } }, "parameters": { @@ -3541,8 +3581,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "2580239868289231046" + "version": "0.22.6.54827", + "templateHash": "2518125350103842528" } }, "parameters": { @@ -3653,8 +3693,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "11535560972989472774" + "version": "0.22.6.54827", + "templateHash": "7950762402225783762" } }, "parameters": { @@ -3818,8 +3858,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "17746697918973691672" + "version": "0.22.6.54827", + "templateHash": "14616135619412445013" } }, "parameters": { @@ -3900,8 +3940,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "351369789614429753" + "version": "0.22.6.54827", + "templateHash": "5170989495095778148" } }, "parameters": { @@ -4063,8 +4103,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "5362294501967708784" + "version": "0.22.6.54827", + "templateHash": "1858027801977913465" } }, "parameters": { @@ -4192,8 +4232,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "16757856721763746334" + "version": "0.22.6.54827", + "templateHash": "515879137059342444" } }, "parameters": { @@ -4385,8 +4425,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "960377053318251904" + "version": "0.22.6.54827", + "templateHash": "7356212633426704316" } }, "parameters": { @@ -4572,8 +4612,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "6964356398637818331" + "version": "0.22.6.54827", + "templateHash": "8177651313894344378" } }, "parameters": { @@ -4762,8 +4802,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "16499077276740623926" + "version": "0.22.6.54827", + "templateHash": "14884493940271521338" } }, "parameters": { @@ -5130,8 +5170,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "4151561885012978576" + "version": "0.22.6.54827", + "templateHash": "860119083651993361" } }, "parameters": { @@ -5550,8 +5590,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "4678926577241725449" + "version": "0.22.6.54827", + "templateHash": "2403871696889962391" } }, "parameters": { @@ -5710,8 +5750,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "17610178805028045872" + "version": "0.22.6.54827", + "templateHash": "5914243688373707722" } }, "parameters": { @@ -6091,8 +6131,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "12141117254902367852" + "version": "0.22.6.54827", + "templateHash": "12149900229898989241" } }, "parameters": { @@ -6327,8 +6367,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "16624770513957439457" + "version": "0.22.6.54827", + "templateHash": "10555888960994130887" } }, "parameters": { @@ -6439,8 +6479,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "5362294501967708784" + "version": "0.22.6.54827", + "templateHash": "1858027801977913465" } }, "parameters": { @@ -6568,8 +6608,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "16757856721763746334" + "version": "0.22.6.54827", + "templateHash": "515879137059342444" } }, "parameters": { @@ -6708,8 +6748,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "16757856721763746334" + "version": "0.22.6.54827", + "templateHash": "515879137059342444" } }, "parameters": { @@ -6842,8 +6882,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "11016487736119141662" + "version": "0.22.6.54827", + "templateHash": "14940987358308253574" } }, "parameters": { @@ -7030,8 +7070,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "5362294501967708784" + "version": "0.22.6.54827", + "templateHash": "1858027801977913465" } }, "parameters": { @@ -7153,8 +7193,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "16757856721763746334" + "version": "0.22.6.54827", + "templateHash": "515879137059342444" } }, "parameters": { @@ -7282,8 +7322,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "13945851285616680724" + "version": "0.22.6.54827", + "templateHash": "11562630491400954103" } }, "parameters": { @@ -7408,8 +7448,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "13945851285616680724" + "version": "0.22.6.54827", + "templateHash": "11562630491400954103" } }, "parameters": { @@ -7532,8 +7572,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "13945851285616680724" + "version": "0.22.6.54827", + "templateHash": "11562630491400954103" } }, "parameters": { @@ -7658,8 +7698,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "13945851285616680724" + "version": "0.22.6.54827", + "templateHash": "11562630491400954103" } }, "parameters": { @@ -7844,8 +7884,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "15774402054966915173" + "version": "0.22.6.54827", + "templateHash": "13569317092130297378" } }, "parameters": { @@ -8328,8 +8368,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "9443579810642006669" + "version": "0.22.6.54827", + "templateHash": "12476181127364111574" } }, "parameters": { @@ -8498,8 +8538,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "13533098897900792860" + "version": "0.22.6.54827", + "templateHash": "11952711678765047217" } }, "parameters": { @@ -8666,8 +8706,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "831346153285897056" + "version": "0.22.6.54827", + "templateHash": "5313083394471156958" } }, "parameters": { @@ -8928,8 +8968,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "4541805467666285522" + "version": "0.22.6.54827", + "templateHash": "16542889521229310255" } }, "parameters": { @@ -9067,8 +9107,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "16624770513957439457" + "version": "0.22.6.54827", + "templateHash": "10555888960994130887" } }, "parameters": { @@ -9179,8 +9219,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "13945851285616680724" + "version": "0.22.6.54827", + "templateHash": "11562630491400954103" } }, "parameters": { @@ -9304,8 +9344,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "13945851285616680724" + "version": "0.22.6.54827", + "templateHash": "11562630491400954103" } }, "parameters": { @@ -9430,8 +9470,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "13945851285616680724" + "version": "0.22.6.54827", + "templateHash": "11562630491400954103" } }, "parameters": { @@ -9588,8 +9628,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "10269755258310872269" + "version": "0.22.6.54827", + "templateHash": "15477717073139120284" } }, "parameters": { @@ -9687,8 +9727,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "11733225414564442312" + "version": "0.22.6.54827", + "templateHash": "11418129739523871235" } }, "parameters": { @@ -10007,8 +10047,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "5362294501967708784" + "version": "0.22.6.54827", + "templateHash": "1858027801977913465" } }, "parameters": { @@ -10136,8 +10176,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "16757856721763746334" + "version": "0.22.6.54827", + "templateHash": "515879137059342444" } }, "parameters": { @@ -10349,8 +10389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "13964324975529071265" + "version": "0.22.6.54827", + "templateHash": "11184930415629419883" } }, "parameters": { @@ -10471,8 +10511,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "14572902388080328941" + "version": "0.22.6.54827", + "templateHash": "12528687580765097259" } }, "parameters": { @@ -10714,8 +10754,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "5362294501967708784" + "version": "0.22.6.54827", + "templateHash": "1858027801977913465" } }, "parameters": { @@ -10843,8 +10883,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "16757856721763746334" + "version": "0.22.6.54827", + "templateHash": "515879137059342444" } }, "parameters": { @@ -10956,6 +10996,1334 @@ "dependsOn": [ "[resourceId('Microsoft.Resources/deployments', 'vnetSpoke-Deployment')]" ] + }, + { + "condition": "[parameters('deployOpenAi')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-openAiModule-Deployment', variables('resourceNames').openAiAccount), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('resourceNames').openAiAccount]" + }, + "deploymentName": { + "value": "[variables('resourceNames').openAiDeployment]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "vnetHubResourceId": { + "value": "[parameters('vnetHubResourceId')]" + }, + "subnetPrivateEndpointId": { + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', split(format('{0}/{1}', reference(resourceId('Microsoft.Resources/deployments', 'vnetSpoke-Deployment'), '2022-09-01').outputs.vnetName.value, variables('resourceNames').snetPe), '/')[0], split(format('{0}/{1}', reference(resourceId('Microsoft.Resources/deployments', 'vnetSpoke-Deployment'), '2022-09-01').outputs.vnetName.value, variables('resourceNames').snetPe), '/')[1])]" + }, + "virtualNetworkLinks": { + "value": [ + { + "vnetName": "[reference(resourceId('Microsoft.Resources/deployments', 'vnetSpoke-Deployment'), '2022-09-01').outputs.vnetName.value]", + "vnetId": "[reference(resourceId('Microsoft.Resources/deployments', 'vnetSpoke-Deployment'), '2022-09-01').outputs.vnetId.value]", + "registrationEnabled": false + }, + { + "vnetName": "[variables('vnetHubSplitTokens')[8]]", + "vnetId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('vnetHubSplitTokens')[2], variables('vnetHubSplitTokens')[4]), 'Microsoft.Network/virtualNetworks', variables('vnetHubSplitTokens')[8])]", + "registrationEnabled": false + } + ] + }, + "logAnalyticsWsId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'logAnalyticsWs-Deployment'), '2022-09-01').outputs.logAnalyticsWsId.value]" + }, + "deployOpenAiGptModel": { + "value": "[parameters('deployOpenAiGptModel')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.22.6.54827", + "templateHash": "4414713660837448554" + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 2, + "maxLength": 64, + "metadata": { + "description": "Required. Name of the OpenAI Account. Must be globally unique. Only alphanumeric characters and hyphens are allowed. The value must be 2-64 characters long and cannot start or end with a hyphen" + } + }, + "deploymentName": { + "type": "string", + "defaultValue": "testGPT35", + "minLength": 2, + "maxLength": 64, + "metadata": { + "description": "Required. Name of the sample deployment. Deployment Name can have only letters and numbers, no spaces. Hyphens (\"-\") and underscores (\"_\") may be used, except as ending characters." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Optional. The location to deploy the Redis cache service." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "virtualNetworkLinks": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain vnetName, vnetId, registrationEnabled" + } + }, + "subnetPrivateEndpointId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Default is empty. If empty no Private endpoint will be created for the resoure. Otherwise, the subnet where the private endpoint will be attached to" + } + }, + "vnetHubResourceId": { + "type": "string", + "metadata": { + "description": "if empty, private dns zone will be deployed in the current RG scope" + } + }, + "logAnalyticsWsId": { + "type": "string", + "metadata": { + "description": "An existing Log Analytics WS Id for creating app Insights, diagnostics etc." + } + }, + "deployOpenAiGptModel": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Deploy (or not) a model on the openAI Account. This is used only as a sample to show how to deploy a model on the OpenAI account." + } + } + }, + "variables": { + "vnetHubSplitTokens": "[if(not(empty(parameters('vnetHubResourceId'))), split(parameters('vnetHubResourceId'), '/'), array(''))]", + "openAiDnsZoneName": "privatelink.openai.azure.com" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('openAI-{0}-Deployment', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "hasPrivateLinks": { + "value": "[not(empty(parameters('subnetPrivateEndpointId')))]" + }, + "diagnosticSettings": { + "value": [ + { + "name": "OpenAI-Default-Diag", + "workspaceResourceId": "[parameters('logAnalyticsWsId')]" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.22.6.54827", + "templateHash": "913461557728548634" + }, + "name": "Cognitive Services", + "description": "This module deploys a Cognitive Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "memberName": { + "type": "string" + }, + "privateIpAddress": { + "type": "string" + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Manual PrivateLink Service Connections." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to 'AllMetrics' to collect all metrics." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 2, + "maxLength": 64, + "metadata": { + "description": "Required. Name of the OpenAI Account. Must be globally unique. Only alphanumeric characters and hyphens are allowed. The value must be 2-64 characters long and cannot start or end with a hyphen" + } + }, + "kind": { + "type": "string", + "defaultValue": "OpenAI", + "allowedValues": [ + "AnomalyDetector", + "Bing.Autosuggest.v7", + "Bing.CustomSearch", + "Bing.EntitySearch", + "Bing.Search.v7", + "Bing.SpellCheck.v7", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "Personalizer", + "QnAMaker", + "SpeechServices", + "TextAnalytics", + "TextTranslation", + "OpenAI" + ], + "metadata": { + "description": "Default is OpenAI. Kind of the Cognitive Services. Find available Kind-SKUs compination by running `az cognitiveservices account list-skus --kind OpenAI --location EASTUS2` Check here: https://learn.microsoft.com/azure/ai-services/create-account-bicep?tabs=CLI." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "C2", + "C3", + "C4", + "F0", + "F1", + "S", + "S0", + "S1", + "S10", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8", + "S9" + ], + "metadata": { + "description": "Optional. Default is S0 for OpenAI. SKU of the Cognitive Services resource. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "customSubDomainName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + } + }, + "networkAcls": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + }, + "hasPrivateLinks": { + "type": "bool", + "metadata": { + "description": "Whether the resource has private links or not" + } + }, + "systemAssignedIdentity": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional, default is true. Enables system assigned managed identity on the resource." + } + }, + "userAssignedIdentities": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Conditional. The ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "allowedFqdnList": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of allowed FQDN." + } + }, + "apiProperties": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The API properties for special APIs." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." + } + }, + "cMKKeyVaultResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty." + } + }, + "cMKKeyName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The name of the customer managed key to use for encryption. Cannot be deployed together with the parameter 'systemAssignedIdentity' enabled." + } + }, + "cMKUserAssignedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty." + } + }, + "cMKKeyVersion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, latest is used." + } + }, + "dynamicThrottlingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to enable dynamic throttling." + } + }, + "migrationToken": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource migration token." + } + }, + "restore": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." + } + }, + "restrictOutboundNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Restrict outbound network access." + } + }, + "userOwnedStorage": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The storage accounts for this resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "identityType": "[if(parameters('systemAssignedIdentity'), if(not(empty(parameters('userAssignedIdentities'))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(parameters('userAssignedIdentities'))), 'UserAssigned', 'None'))]", + "identity": "[if(not(equals(variables('identityType'), 'None')), createObject('type', variables('identityType'), 'userAssignedIdentities', if(not(empty(parameters('userAssignedIdentities'))), parameters('userAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", + "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", + "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", + "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", + "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", + "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", + "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", + "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", + "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", + "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", + "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", + "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", + "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", + "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", + "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", + "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", + "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", + "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", + "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", + "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", + "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(parameters('cMKKeyVaultResourceId'))), not(empty(parameters('cMKKeyName'))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/')), if(not(empty(parameters('cMKKeyName'))), parameters('cMKKeyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "defaultTelemetry": { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(parameters('cMKKeyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2021-10-01", + "subscriptionId": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(if(not(empty(parameters('cMKKeyVaultResourceId'))), parameters('cMKKeyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(parameters('cMKUserAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), parameters('cMKUserAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), parameters('cMKUserAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(if(not(empty(parameters('cMKUserAssignedIdentityResourceId'))), parameters('cMKUserAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "cognitiveServices": { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2023-05-01", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "identity": "[variables('identity')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "properties": { + "customSubDomainName": "[if(not(empty(parameters('customSubDomainName'))), parameters('customSubDomainName'), parameters('name'))]", + "networkAcls": "[if(not(empty(parameters('networkAcls'))), createObject('defaultAction', if(contains(parameters('networkAcls'), 'defaultAction'), parameters('networkAcls').defaultAction, null()), 'virtualNetworkRules', if(contains(parameters('networkAcls'), 'virtualNetworkRules'), parameters('networkAcls').virtualNetworkRules, createArray()), 'ipRules', if(contains(parameters('networkAcls'), 'ipRules'), parameters('networkAcls').ipRules, createArray())), null())]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(parameters('hasPrivateLinks'), empty(parameters('networkAcls'))), 'Disabled', null()))]", + "allowedFqdnList": "[parameters('allowedFqdnList')]", + "apiProperties": "[parameters('apiProperties')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "encryption": "[if(not(empty(parameters('cMKKeyName'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', reference('cMKUserAssignedIdentity').clientId, 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('cMKKeyName'), 'keyVersion', if(not(empty(parameters('cMKKeyVersion'))), parameters('cMKKeyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", + "migrationToken": "[if(not(empty(parameters('migrationToken'))), parameters('migrationToken'), null())]", + "restore": "[parameters('restore')]", + "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", + "userOwnedStorage": "[if(not(empty(parameters('userOwnedStorage'))), parameters('userOwnedStorage'), null())]", + "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "cognitiveServices_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "cognitiveServices" + ] + }, + "cognitiveServices_diagnosticSettings": { + "copy": { + "name": "cognitiveServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "metrics": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics', 'timeGrain', null(), 'enabled', true())))]", + "logs": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'AllLogs', 'enabled', true())))]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "cognitiveServices" + ] + }, + "cognitiveServices_roleAssignments": { + "copy": { + "name": "cognitiveServices_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "cognitiveServices" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cognitive services account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the cognitive services account." + }, + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the cognitive services account was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The service endpoint of the cognitive services account." + }, + "value": "[reference('cognitiveServices').endpoint]" + }, + "systemAssignedPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[if(and(parameters('systemAssignedIdentity'), contains(reference('cognitiveServices', '2023-05-01', 'full').identity, 'principalId')), reference('cognitiveServices', '2023-05-01', 'full').identity.principalId, '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('cognitiveServices', '2023-05-01', 'full').location]" + } + } + } + } + }, + { + "condition": "[parameters('deployOpenAiGptModel')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('GPT-{0}-Deployment', parameters('name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "openAiName": { + "value": "[parameters('name')]" + }, + "deploymentName": { + "value": "[parameters('deploymentName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.22.6.54827", + "templateHash": "7356525871301939205" + } + }, + "parameters": { + "openAiName": { + "type": "string", + "maxLength": 64, + "metadata": { + "description": "Required. Name of the existing OpenAI Account" + } + }, + "deploymentName": { + "type": "string", + "minLength": 2, + "maxLength": 64, + "metadata": { + "description": "Required. Deployment Name can have only letters and numbers, no spaces. Hyphens (\"-\") and underscores (\"_\") may be used, except as ending characters." + } + }, + "modelName": { + "type": "string", + "defaultValue": "gpt-35-turbo", + "metadata": { + "description": "The model name to be deployed. The model name can be found in the OpenAI portal." + } + }, + "modelVersion": { + "type": "string", + "defaultValue": "0613", + "metadata": { + "description": "The model version to be deployed. At the time of writing this is the latest version is eastus2." + } + } + }, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('openAiName'), parameters('deploymentName'))]", + "sku": { + "name": "Standard", + "capacity": 1 + }, + "properties": { + "raiPolicyName": "Microsoft.Default", + "model": { + "format": "OpenAI", + "name": "[parameters('modelName')]", + "version": "[parameters('modelVersion')]" + } + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('openAI-{0}-Deployment', parameters('name')))]" + ] + }, + { + "condition": "[not(empty(parameters('subnetPrivateEndpointId')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-PrivateDnsZoneDeployment', replace(variables('openAiDnsZoneName'), '.', '-')), 64)]", + "subscriptionId": "[variables('vnetHubSplitTokens')[2]]", + "resourceGroup": "[variables('vnetHubSplitTokens')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('openAiDnsZoneName')]" + }, + "virtualNetworkLinks": { + "value": "[parameters('virtualNetworkLinks')]" + }, + "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.22.6.54827", + "templateHash": "1858027801977913465" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Private DNS Zone Service. For az private endpoints you might find info here: https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns#azure-services-dns-zone-configuration" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "virtualNetworkLinks": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain vnetName, vnetId, registrationEnabled" + } + }, + "aRecords": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of A records to be added to the DNS Zone" + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "global", + "tags": "[parameters('tags')]" + }, + { + "copy": { + "name": "privateDnsZoneLink", + "count": "[length(parameters('virtualNetworkLinks'))]" + }, + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2018-09-01", + "name": "[format('{0}/{1}', parameters('name'), format('{0}-link', parameters('virtualNetworkLinks')[copyIndex()].vnetName))]", + "location": "global", + "properties": { + "registrationEnabled": "[parameters('virtualNetworkLinks')[copyIndex()].registrationEnabled]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkLinks')[copyIndex()].vnetId]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + ] + }, + { + "copy": { + "name": "dnsARecord", + "count": "[length(parameters('aRecords'))]" + }, + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('name'), parameters('aRecords')[copyIndex()].name)]", + "properties": { + "ttl": 60, + "aRecords": [ + { + "ipv4Address": "[parameters('aRecords')[copyIndex()].ipv4Address]" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + ] + } + ], + "outputs": { + "privateDnsZonesId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + } + } + } + } + }, + { + "condition": "[not(empty(parameters('subnetPrivateEndpointId')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('pe-{0}-Deployment', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[take(format('pe-{0}', parameters('name')), 64)]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "privateDnsZonesId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('vnetHubSplitTokens')[2], variables('vnetHubSplitTokens')[4]), 'Microsoft.Resources/deployments', take(format('{0}-PrivateDnsZoneDeployment', replace(variables('openAiDnsZoneName'), '.', '-')), 64)), '2022-09-01').outputs.privateDnsZonesId.value]" + }, + "privateLinkServiceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('openAI-{0}-Deployment', parameters('name'))), '2022-09-01').outputs.resourceId.value]" + }, + "snetId": { + "value": "[parameters('subnetPrivateEndpointId')]" + }, + "subresource": { + "value": "account" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.22.6.54827", + "templateHash": "515879137059342444" + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 2, + "maxLength": 64, + "metadata": { + "description": "Required. Name of your pruvate endpoint. Must begin with a letter or number, end with a letter, number or underscore, and may contain only letters, numbers, underscores, periods, or hyphens." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Location for all resources." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "snetId": { + "type": "string", + "metadata": { + "description": "The subnet resource ID where the nic of the PE will be attached to" + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "The resource id of private link service. The resource ID of the Az Resource that we need to attach the pe to." + } + }, + "subresource": { + "type": "string", + "metadata": { + "description": "The resource that the private endpoint will be attached to, as shown in https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview#private-link-resource" + } + }, + "privateDnsZonesId": { + "type": "string", + "metadata": { + "description": "Id of the relevant private DNS Zone, so that the PE can create an A record for the implicitly created nic" + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "subnet": { + "id": "[parameters('snetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "[format('pl-{0}', parameters('name'))]", + "properties": { + "privateLinkServiceId": "[parameters('privateLinkServiceId')]", + "groupIds": [ + "[parameters('subresource')]" + ] + } + } + ] + } + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('name'), 'dnsgroupname')]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "config1", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZonesId')]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + ] + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('openAI-{0}-Deployment', parameters('name')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('vnetHubSplitTokens')[2], variables('vnetHubSplitTokens')[4]), 'Microsoft.Resources/deployments', take(format('{0}-PrivateDnsZoneDeployment', replace(variables('openAiDnsZoneName'), '.', '-')), 64))]" + ] + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'logAnalyticsWs-Deployment')]", + "[resourceId('Microsoft.Resources/deployments', 'vnetSpoke-Deployment')]" + ] } ], "outputs": { @@ -11001,8 +12369,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "4741460206960601395" + "version": "0.22.6.54827", + "templateHash": "12559704120310779635" } }, "parameters": { @@ -11060,8 +12428,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "9683203958437324298" + "version": "0.22.6.54827", + "templateHash": "4009938085889713028" } }, "parameters": { @@ -11140,8 +12508,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.20.4.51522", - "templateHash": "9683203958437324298" + "version": "0.22.6.54827", + "templateHash": "4009938085889713028" } }, "parameters": { diff --git a/scenarios/secure-baseline-multitenant/azure-resource-manager/main.parameters.jsonc b/scenarios/secure-baseline-multitenant/azure-resource-manager/main.parameters.jsonc index bd179aee..57e3481a 100644 --- a/scenarios/secure-baseline-multitenant/azure-resource-manager/main.parameters.jsonc +++ b/scenarios/secure-baseline-multitenant/azure-resource-manager/main.parameters.jsonc @@ -3,8 +3,8 @@ "contentVersion": "1.0.0.0", "parameters": { // max length: 10. Suffix that will be used to name the resources in a pattern like - - "workloadName" : { - "value": "appSvc-LZA" + "workloadName": { + "value": "app-openai" }, //Required. The name of the environmentName (e.g. "dev", "test", "prod", "preprod", "staging", "uat", "dr", "qa"). Up to 8 characters long. "environmentName": { @@ -23,25 +23,29 @@ }, // Feature Flags // set to true if you want to intercept all outbound traffic with azure firewall - "enableEgressLockdown" : { + "enableEgressLockdown": { "value": true }, // set to true if you want to a redis cache "deployRedis": { - "value": true + "value": false }, // set to true if you want to deploy a azure SQL server and default database "deployAzureSql": { - "value": true + "value": false }, // set to true if you want to deploy application configuration "deployAppConfig": { - "value": true + "value": false }, // set to true if you want to deploy a jumpbox/devops VM "deployJumpHost": { "value": true }, + // Deploy (or not) an Azure OpenAI account. ATTENTION: At the time of writing , OpenAI is in preview and only available in limited regions: look here: https://learn.microsoft.com/azure/ai-services/openai/chatgpt-quickstart#prerequisites + "deployOpenAi": { + "value": false + }, // set to true if you want to auto approve the Private Endpoint of the AFD Premium "autoApproveAfdPrivateEndpoint": { "value": true @@ -74,14 +78,13 @@ "subnetSpokePrivateEndpointAddressSpace": { "value": "10.240.11.0/24" }, - // Defines the name, tier, size, family and capacity of the App Service Plan. Plans ending to _AZ, are deplying at least three instances in three Availability Zones. EP* is only for functions' + // Defines the name, tier, size, family and capacity of the App Service Plan. Plans ending to _AZ, are deploying at least three instances in three Availability Zones. EP* is only for functions' // select one from: 'S1', 'S2', 'S3', 'P1V3', 'P2V3', 'P3V3', 'P1V3_AZ', 'P2V3_AZ', 'P3V3_AZ' "webAppPlanSku": { "value": "S1" }, // two options: Windows or Linux - "webAppBaseOs" : - { + "webAppBaseOs": { "value": "Windows" }, // admin username of the VM agent deployed in the Spoke @@ -97,7 +100,7 @@ "value": { "deployment": "bicep" } - }, + }, // The Azure Active Directory (AAD) administrator group used for SQL Server authentication "sqlServerAdministrators": { "value": { diff --git a/scenarios/secure-baseline-multitenant/bicep/README.md b/scenarios/secure-baseline-multitenant/bicep/README.md index 51b38b3c..9a245ead 100644 --- a/scenarios/secure-baseline-multitenant/bicep/README.md +++ b/scenarios/secure-baseline-multitenant/bicep/README.md @@ -13,7 +13,7 @@ Before deploying the Bicep IaC artifacts, you need to review and customize the v > **Note** Azure Developer CLI (azd) is also supported as a deployment method. Since azd CLI does not support parameter files with *jsonc* extension, we provide a simple json parameter file (which does not contain inline comments) -The table below summurizes the avaialble parameters and the possible values that can be set. +The table below summarizes the available parameters and the possible values that can be set. | Name | Description | Example | @@ -22,7 +22,7 @@ The table below summurizes the avaialble parameters and the possible values that |location|Azure region where the resources will be deployed in|| |environment|Required. The name of the environment (e.g. "dev", "test", "prod", "preprod", "staging", "uat", "dr", "qa"). Up to 8 characters long.|| |vnetHubResourceId|If empty, then a new hub will be created. If you select not to deploy a new Hub resource group, set the resource id of the Hub Virtual Network that you want to peer to. In that case, no new hub will be created and a peering will be created between the new spoke and and existing hub vnet|/subscriptions// resourceGroups//providers/ Microsoft.Network/virtualNetworks/| -|firewallInternalIp|If you select to create a new Hub, the UDR for locking the egress traffic will be created as well, no matter what value you set to that variable. However, if you select to connect to an existing hub, then you need to provide the internal IP of the azure firewal so that the deployment can create the UDR for locking down egress traffic. If not given, no UDR will be created|| +|firewallInternalIp|If you select to create a new Hub, the UDR for locking the egress traffic will be created as well, no matter what value you set to that variable. However, if you select to connect to an existing hub, then you need to provide the internal IP of the azure firewall so that the deployment can create the UDR for locking down egress traffic. If not given, no UDR will be created|| |vnetHubAddressSpace|If you deploy a new hub, you need to set the appropriate CIDR of the newly created Hub virtual network|10.242.0.0/20| |subnetHubFirewallAddressSpace|CIDR of the subnet that will host the azure Firewall|10.242.0.0/26| |subnetHubBastionAddressSpace|CIDR of the subnet that will host the Bastion Service|10.242.0.64/26| @@ -30,7 +30,7 @@ The table below summurizes the avaialble parameters and the possible values that |subnetSpokeAppSvcAddressSpace|CIDR of the subnet that will hold the app services plan|10.240.0.0/26| |subnetSpokeDevOpsAddressSpace|CIDR of the subnet that will hold devOps agents etc|10.240.10.128/26| |subnetSpokePrivateEndpointAddressSpace|CIDR of the subnet that will hold the private endpoints of the supporting services|10.240.11.0/24| -|webAppPlanSku|Defines the name, tier, size, family and capacity of the App Service Plan. Plans ending to _AZ, are deplying at least three instances in three Availability Zones. select one from: 'S1', 'S2', 'S3', 'P1V3', 'P2V3', 'P3V3', 'P1V3_AZ', 'P2V3_AZ', 'P3V3_AZ' || +|webAppPlanSku|Defines the name, tier, size, family and capacity of the App Service Plan. Plans ending to _AZ, are deploying at least three instances in three Availability Zones. select one from: 'S1', 'S2', 'S3', 'P1V3', 'P2V3', 'P3V3', 'P1V3_AZ', 'P2V3_AZ', 'P3V3_AZ' || |webAppBaseOs|The OS for the App service plan. Two options available: Windows or Linux|| |resourceTags|Resource tags that we might need to add to all resources (i.e. Environment, Cost center, application name etc)|"resourceTags": {
"value": {
"deployment": "bicep",
"key1": "value1"
}
} | |enableEgressLockdown|Feature Flag: te (or not) a UDR for the App Service Subnet, to route all egress traffic through Hub Azure Firewall| @@ -38,6 +38,7 @@ The table below summurizes the avaialble parameters and the possible values that |deployAzureSql|Feature Flag: Deploy (or not) an Azure SQL with default database| |deployAppConfig|Feature Flag: Deploy (or not) an Azure app configuration| |deployJumpHost|Feature Flag: Deploy (or not) an Azure virtual machine (to be used as jumphost)| +|deployOpenAi|Feature Flag: Deploy (or not) an Azure OpenAI account. ATTENTION: At the time of writing, [OpenAI is in preview](https://learn.microsoft.com/azure/ai-services/openai/chatgpt-quickstart#prerequisites) and available in limited regions. |false |autoApproveAfdPrivateEndpoint|Default value: true. Set to true if you want to auto approve the Private Endpoint of the AFD Premium. See details [regarding approving the App Service private endpoint connection from Front Door](#approve-the-app-service-private-endpoint-connection-from-front-door-in-the-azure-portal) | false |sqlServerAdministrators|The Azure Active Directory (AAD) administrator group used for SQL Server authentication. The Azure AD group must be created before running deployment. This has three values that need to be filled, as shown below
**login**: the name of the AAD Group
**sid**: the object id of the AAD Group
**tenantId**: The tenantId of the AAD || @@ -66,7 +67,7 @@ az deployment sub create ` --name $deploymentName ` --parameters ./main.parameters.jsonc ``` -### Azure Devloper CLI (azd) +### Azure Developer CLI (azd) 1. [Install the Azure Developer CLI](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd?tabs=localinstall%2Cwindows%2Cbrew%2Cdeb) 2. Login to azure from your terminal. You can do this by running `azd auth login`. If no web browser is available or the web browser fails to open, you may force device code flow with `azd auth login --use-device-code` 3. Run `azd up` in the correct folder (/scenarios/secure-baseline-multitenant). This will start the Azure infrastructure provisioning process. The first time you run it, you will be asked to give some information, i.e. environmentName, subscription ID etc @@ -78,7 +79,7 @@ az deployment sub create ` The approval of the App Service private endpoint connection from Azure Front Door can be automated with the deployment, if you set the param `autoApproveAfdPrivateEndpoint` to `true`. If you do so then you need to know: - The automatic deployment uses the [deploymentScripts Resource](https://learn.microsoft.com/en-us/azure/templates/microsoft.resources/deploymentscripts?pivots=deployment-language-bicep). The deployment script service requires two supporting resources for script execution and troubleshooting: a storage account and a container instance. The two automatically-created supporting resources are usually deleted by the script service when the deployment script execution gets in a terminal state. You are billed for the supporting resources until they are deleted. -- The deployment script resource is only available in the regions where Azure Container Instance is available, see [Azure Container Instances Availabiility by Region](https://azure.microsoft.com/en-us/explore/global-infrastructure/products-by-region/?products=container-instances®ions=asia-pacific-east,asia-pacific-southeast,australia-central,australia-central-2,australia-east,australia-southeast,brazil-south,brazil-southeast,canada-central,canada-east,central-india,china-east,china-east-2,china-east-3,china-non-regional,china-north,china-north-2,china-north-3,europe-north,europe-west,france-central,france-south,germany-north,germany-west-central,japan-east,japan-west,korea-central,korea-south,norway-east,norway-west,qatar-central,south-africa-north,south-africa-west,south-india,sweden-central,sweden-south,switzerland-north,switzerland-west,uae-north,uae-central,united-kingdom-south,united-kingdom-west,us-central,us-dod-central,us-dod-east,us-east,us-east-2,us-north-central,us-south-central,us-west,us-west-2,us-west-3,us-west-central,usgov-arizona,usgov-non-regional,usgov-texas,usgov-virginia,west-india,poland-central) +- The deployment script resource is only available in the regions where Azure Container Instance is available, see [Azure Container Instances Availability by Region](https://azure.microsoft.com/en-us/explore/global-infrastructure/products-by-region/?products=container-instances®ions=asia-pacific-east,asia-pacific-southeast,australia-central,australia-central-2,australia-east,australia-southeast,brazil-south,brazil-southeast,canada-central,canada-east,central-india,china-east,china-east-2,china-east-3,china-non-regional,china-north,china-north-2,china-north-3,europe-north,europe-west,france-central,france-south,germany-north,germany-west-central,japan-east,japan-west,korea-central,korea-south,norway-east,norway-west,qatar-central,south-africa-north,south-africa-west,south-india,sweden-central,sweden-south,switzerland-north,switzerland-west,uae-north,uae-central,united-kingdom-south,united-kingdom-west,us-central,us-dod-central,us-dod-east,us-east,us-east-2,us-north-central,us-south-central,us-west,us-west-2,us-west-3,us-west-central,usgov-arizona,usgov-non-regional,usgov-texas,usgov-virginia,west-india,poland-central) - A User Assigned Managed Identity will be created (with name *id-WORKLOADNAME-ENVIRONMENT-REGION-AfdApprovePe*) that it will be given Contributor role on the spoke resource group. This identity will be used to approve the Private Endpoint connection If before deployment you set the param `autoApproveAfdPrivateEndpoint` to `false` (because you want to manually approve the Private Endpoint) then you need to complete the next manual step to approve the private endpoint connection. @@ -104,7 +105,7 @@ Go to the portal, find the spoke resource group you have just deployed, and iden ### Connect to the Jumpbox VM (deployed in the spoke resource group) -You can connect to the jumpbox win 11 VM only through bastion. The default parameters deploy a Bastion in Standard SKU, with native client support enabled. The jumpbox VM is AADJoined by default. This means that you can connect to the jumpbox, either with the local user/password compination (azureuser is the default username) or with a valid AAD account. In certain circumastances your organization may not allow the device to be enrolled. If the jumpbox VM is AAD joined and properly intune enrolled, you can use native rdp client to connect by running the below Az CLI commands +You can connect to the jumpbox win 11 VM only through bastion. The default parameters deploy a Bastion in Standard SKU, with native client support enabled. The jumpbox VM is AADJoined by default. This means that you can connect to the jumpbox, either with the local user/password combination (azureuser is the default username) or with a valid AAD account. In certain circumstances your organization may not allow the device to be enrolled. If the jumpbox VM is AAD joined and properly intune enrolled, you can use native rdp client to connect by running the below Az CLI commands From a PowerShell terminal, connect to the DevOps VM using your Azure AD credentials (or Windows Hello). diff --git a/scenarios/secure-baseline-multitenant/bicep/deploy.spoke.bicep b/scenarios/secure-baseline-multitenant/bicep/deploy.spoke.bicep index ba4162f7..1b6217ac 100644 --- a/scenarios/secure-baseline-multitenant/bicep/deploy.spoke.bicep +++ b/scenarios/secure-baseline-multitenant/bicep/deploy.spoke.bicep @@ -42,6 +42,12 @@ param deployAppConfig bool @description('Deploy (or not) an Azure virtual machine (to be used as jumphost)') param deployJumpHost bool +@description('Deploy (or not) an Azure OpenAI account. ATTENTION: At the time of writing this, OpenAI is in preview and only available in limited regions: look here: https://learn.microsoft.com/azure/ai-services/openai/chatgpt-quickstart#prerequisites') +param deployOpenAi bool + +@description('Deploy (or not) a model on the openAI Account. This is used only as a sample to show how to deploy a model on the OpenAI account.') +param deployOpenAiGptModel bool = false + // post deployment specific parameters for the jumpBox @description('The URL of the Github repository to use for the Github Actions Runner. This parameter is optional. If not provided, the Github Actions Runner will not be installed. If this parameter is provided, then github_token must also be provided.') param githubRepository string = '' @@ -113,6 +119,8 @@ var resourceNames = { routeTable: naming.routeTable.name routeEgressLockdown: '${naming.route.name}-egress-lockdown' idAfdApprovePeAutoApprover: take('${naming.userAssignedManagedIdentity.name}-AfdApprovePe', 128) + openAiAccount: naming.cognitiveAccount.nameUnique + openAiDeployment: naming.openAiDeployment.name } var udrRoutes = [ @@ -355,6 +363,20 @@ module sqlServerAndDefaultDb 'modules/sql-database.module.bicep' = if (deployAzu } } +module openAi 'modules/open-ai.module.bicep'= if(deployOpenAi) { + name: take('${resourceNames.openAiAccount}-openAiModule-Deployment', 64) + params: { + name: resourceNames.openAiAccount + deploymentName: resourceNames.openAiDeployment + location: location + tags: tags + vnetHubResourceId: vnetHubResourceId + subnetPrivateEndpointId: snetPe.id + virtualNetworkLinks: virtualNetworkLinks + logAnalyticsWsId: logAnalyticsWs.outputs.logAnalyticsWsId + deployOpenAiGptModel: deployOpenAiGptModel + } +} output vnetSpokeName string = vnetSpoke.outputs.vnetName output vnetSpokeId string = vnetSpoke.outputs.vnetId diff --git a/scenarios/secure-baseline-multitenant/bicep/main.bicep b/scenarios/secure-baseline-multitenant/bicep/main.bicep index 28fb22d8..dd797da0 100644 --- a/scenarios/secure-baseline-multitenant/bicep/main.bicep +++ b/scenarios/secure-baseline-multitenant/bicep/main.bicep @@ -88,6 +88,12 @@ param deployAppConfig bool = false @description('set to true if you want to deploy a jumpbox/devops VM') param deployJumpHost bool = true +@description('set to true if you want to deploy a openai VM') +param deployOpenAi bool = false + +@description('Deploy (or not) a model on the openAI Account. This is used only as a sample to show how to deploy a model on the OpenAI account.') +param deployOpenAiGptModel bool = false + // post deployment specific parameters for the jumpBox @description('The URL of the Github repository to use for the Github Actions Runner. This parameter is optional. If not provided, the Github Actions Runner will not be installed. If this parameter is provided, then github_token must also be provided.') param githubRepository string = '' @@ -210,6 +216,8 @@ module spoke 'deploy.spoke.bicep' = { deployRedis: deployRedis deployAzureSql: deployAzureSql deployAppConfig: deployAppConfig + deployOpenAi: deployOpenAi + deployOpenAiGptModel: deployOpenAiGptModel autoApproveAfdPrivateEndpoint: autoApproveAfdPrivateEndpoint githubRepository: githubRepository githubToken: githubToken @@ -234,6 +242,7 @@ module peerings 'modules/peerings.deployment.bicep' = { // Telemetry Deployment @description('Enable usage and telemetry feedback to Microsoft.') var telemetryId = 'cf7e9f0a-f872-49db-b72f-f2e318189a6d-${location}-msb' +#disable-next-line no-deployments-resources resource telemetrydeployment 'Microsoft.Resources/deployments@2021-04-01' = if (enableTelemetry) { name: telemetryId location: location diff --git a/scenarios/secure-baseline-multitenant/bicep/main.parameters.json b/scenarios/secure-baseline-multitenant/bicep/main.parameters.json index 91e90770..929358bb 100644 --- a/scenarios/secure-baseline-multitenant/bicep/main.parameters.json +++ b/scenarios/secure-baseline-multitenant/bicep/main.parameters.json @@ -32,6 +32,9 @@ "deployJumpHost": { "value": false }, + "deployOpenAi": { + "value": false + }, "autoApproveAfdPrivateEndpoint": { "value": true }, diff --git a/scenarios/secure-baseline-multitenant/bicep/main.parameters.jsonc b/scenarios/secure-baseline-multitenant/bicep/main.parameters.jsonc index bd179aee..c84e70d8 100644 --- a/scenarios/secure-baseline-multitenant/bicep/main.parameters.jsonc +++ b/scenarios/secure-baseline-multitenant/bicep/main.parameters.jsonc @@ -4,7 +4,7 @@ "parameters": { // max length: 10. Suffix that will be used to name the resources in a pattern like - "workloadName" : { - "value": "appSvc-LZA" + "value": "app-openai" }, //Required. The name of the environmentName (e.g. "dev", "test", "prod", "preprod", "staging", "uat", "dr", "qa"). Up to 8 characters long. "environmentName": { @@ -28,20 +28,24 @@ }, // set to true if you want to a redis cache "deployRedis": { - "value": true + "value": false }, // set to true if you want to deploy a azure SQL server and default database "deployAzureSql": { - "value": true + "value": false }, // set to true if you want to deploy application configuration "deployAppConfig": { - "value": true + "value": false }, // set to true if you want to deploy a jumpbox/devops VM "deployJumpHost": { "value": true }, + // Deploy (or not) an Azure OpenAI account. ATTENTION: At the time of writing , OpenAI is in preview and only available in limited regions: look here: https://learn.microsoft.com/azure/ai-services/openai/chatgpt-quickstart#prerequisites + "deployOpenAi": { + "value": false + }, // set to true if you want to auto approve the Private Endpoint of the AFD Premium "autoApproveAfdPrivateEndpoint": { "value": true @@ -74,7 +78,7 @@ "subnetSpokePrivateEndpointAddressSpace": { "value": "10.240.11.0/24" }, - // Defines the name, tier, size, family and capacity of the App Service Plan. Plans ending to _AZ, are deplying at least three instances in three Availability Zones. EP* is only for functions' + // Defines the name, tier, size, family and capacity of the App Service Plan. Plans ending to _AZ, are deploying at least three instances in three Availability Zones. EP* is only for functions' // select one from: 'S1', 'S2', 'S3', 'P1V3', 'P2V3', 'P3V3', 'P1V3_AZ', 'P2V3_AZ', 'P3V3_AZ' "webAppPlanSku": { "value": "S1" diff --git a/scenarios/secure-baseline-multitenant/bicep/modules/open-ai.module.bicep b/scenarios/secure-baseline-multitenant/bicep/modules/open-ai.module.bicep new file mode 100644 index 00000000..928999be --- /dev/null +++ b/scenarios/secure-baseline-multitenant/bicep/modules/open-ai.module.bicep @@ -0,0 +1,85 @@ +@description('Required. Name of the OpenAI Account. Must be globally unique. Only alphanumeric characters and hyphens are allowed. The value must be 2-64 characters long and cannot start or end with a hyphen') +@minLength(2) +@maxLength(64) +param name string + +@description('Required. Name of the sample deployment. Deployment Name can have only letters and numbers, no spaces. Hyphens ("-") and underscores ("_") may be used, except as ending characters.') +@minLength(2) +@maxLength(64) +param deploymentName string = 'testGPT35' + +@description('Optional. The location to deploy the Redis cache service.') +param location string + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain vnetName, vnetId, registrationEnabled') +param virtualNetworkLinks array = [] + +@description('Default is empty. If empty no Private endpoint will be created for the resoure. Otherwise, the subnet where the private endpoint will be attached to') +param subnetPrivateEndpointId string = '' + +@description('if empty, private dns zone will be deployed in the current RG scope') +param vnetHubResourceId string + +@description('An existing Log Analytics WS Id for creating app Insights, diagnostics etc.') +param logAnalyticsWsId string + +@description('Deploy (or not) a model on the openAI Account. This is used only as a sample to show how to deploy a model on the OpenAI account.') +param deployOpenAiGptModel bool = false + +var vnetHubSplitTokens = !empty(vnetHubResourceId) ? split(vnetHubResourceId, '/') : array('') +var openAiDnsZoneName = 'privatelink.openai.azure.com' + +module openAI '../../../shared/bicep/cognitive-services/open-ai.bicep' = { + name: 'openAI-${name}-Deployment' + params: { + name: name + location: location + tags: tags + hasPrivateLinks: !empty(subnetPrivateEndpointId) + diagnosticSettings: [ + { + name: 'OpenAI-Default-Diag' + workspaceResourceId: logAnalyticsWsId + } + ] + } +} + +module gpt35TurboDeployment '../../../shared/bicep/cognitive-services/open-ai.Gpt.deployment.bicep' = if (deployOpenAiGptModel) { + name: 'GPT-${name}-Deployment' + params: { + openAiName: name + deploymentName: deploymentName + } + dependsOn:[ + openAI + ] +} + +module openAiPrivateDnsZone '../../../shared/bicep/private-dns-zone.bicep' = if ( !empty(subnetPrivateEndpointId) ) { + // conditional scope is not working: https://github.com/Azure/bicep/issues/7367 + //scope: empty(vnetHubResourceId) ? resourceGroup() : resourceGroup(vnetHubSplitTokens[2], vnetHubSplitTokens[4]) + scope: resourceGroup(vnetHubSplitTokens[2], vnetHubSplitTokens[4]) + name: take('${replace(openAiDnsZoneName, '.', '-')}-PrivateDnsZoneDeployment', 64) + params: { + name: openAiDnsZoneName + virtualNetworkLinks: virtualNetworkLinks + tags: tags + } +} + +module peOpenAI '../../../shared/bicep/private-endpoint.bicep' = if ( !empty(subnetPrivateEndpointId) ) { + name: take('pe-${name}-Deployment', 64) + params: { + name: take('pe-${name}', 64) + location: location + tags: tags + privateDnsZonesId: openAiPrivateDnsZone.outputs.privateDnsZonesId + privateLinkServiceId: openAI.outputs.resourceId + snetId: subnetPrivateEndpointId + subresource: 'account' + } +} diff --git a/scenarios/shared/bicep/cognitive-services/open-ai.Gpt.deployment.bicep b/scenarios/shared/bicep/cognitive-services/open-ai.Gpt.deployment.bicep new file mode 100644 index 00000000..1265c7b6 --- /dev/null +++ b/scenarios/shared/bicep/cognitive-services/open-ai.Gpt.deployment.bicep @@ -0,0 +1,36 @@ +// OpenAI Bicep Module +@description('Required. Name of the existing OpenAI Account') +@maxLength(64) +param openAiName string + +@description('Required. Deployment Name can have only letters and numbers, no spaces. Hyphens ("-") and underscores ("_") may be used, except as ending characters.') +@minLength(2) +@maxLength(64) +param deploymentName string + +@description('The model name to be deployed. The model name can be found in the OpenAI portal.') +param modelName string = 'gpt-35-turbo' + +@description('The model version to be deployed. At the time of writing this is the latest version is eastus2.') +param modelVersion string = '0613' + +resource openAi 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { + name: openAiName +} + +resource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = { + name: deploymentName + parent: openAi + sku: { + name: 'Standard' + capacity: 1 + } + properties: { + raiPolicyName: 'Microsoft.Default' + model: { + format: 'OpenAI' + name: modelName + version: modelVersion + } + } +} diff --git a/scenarios/shared/bicep/cognitive-services/open-ai.bicep b/scenarios/shared/bicep/cognitive-services/open-ai.bicep new file mode 100644 index 00000000..7f833036 --- /dev/null +++ b/scenarios/shared/bicep/cognitive-services/open-ai.bicep @@ -0,0 +1,462 @@ +metadata name = 'Cognitive Services' +metadata description = 'This module deploys a Cognitive Service.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Name of the OpenAI Account. Must be globally unique. Only alphanumeric characters and hyphens are allowed. The value must be 2-64 characters long and cannot start or end with a hyphen') +@maxLength(64) +@minLength(2) +param name string + +@description('Default is OpenAI. Kind of the Cognitive Services. Find available Kind-SKUs compination by running `az cognitiveservices account list-skus --kind OpenAI --location EASTUS2` Check here: https://learn.microsoft.com/azure/ai-services/create-account-bicep?tabs=CLI.') +@allowed([ + 'AnomalyDetector' + 'Bing.Autosuggest.v7' + 'Bing.CustomSearch' + 'Bing.EntitySearch' + 'Bing.Search.v7' + 'Bing.SpellCheck.v7' + 'CognitiveServices' + 'ComputerVision' + 'ContentModerator' + 'CustomVision.Prediction' + 'CustomVision.Training' + 'Face' + 'FormRecognizer' + 'ImmersiveReader' + 'Internal.AllInOne' + 'LUIS' + 'LUIS.Authoring' + 'Personalizer' + 'QnAMaker' + 'SpeechServices' + 'TextAnalytics' + 'TextTranslation' + 'OpenAI' +]) +param kind string = 'OpenAI' + +@description('Optional. Default is S0 for OpenAI. SKU of the Cognitive Services resource. Use \'Get-AzCognitiveServicesAccountSku\' to determine a valid combinations of \'kind\' and \'SKU\' for your Azure region.') +@allowed([ + 'C2' + 'C3' + 'C4' + 'F0' + 'F1' + 'S' + 'S0' + 'S1' + 'S10' + 'S2' + 'S3' + 'S4' + 'S5' + 'S6' + 'S7' + 'S8' + 'S9' +]) +param sku string = 'S0' + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@description('Conditional. Subdomain name used for token-based authentication. Required if \'networkAcls\' or \'privateEndpoints\' are set.') +param customSubDomainName string = '' + +@description('Optional. A collection of rules governing the accessibility from specific network locations.') +param networkAcls object = {} + +@description('Whether the resource has private links or not') +param hasPrivateLinks bool + +// @description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +// param privateEndpoints privateEndpointType + +@description('Optional, default is true. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = true + +@description('Conditional. The ID(s) to assign to the resource. Required if a user assigned identity is used for encryption.') +param userAssignedIdentities object = {} + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments roleAssignmentType + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. List of allowed FQDN.') +param allowedFqdnList array = [] + +@description('Optional. The API properties for special APIs.') +param apiProperties object = {} + +@description('Optional. Allow only Azure AD authentication. Should be enabled for security reasons.') +param disableLocalAuth bool = true + +@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \'cMKKeyName\' is not empty.') +param cMKKeyVaultResourceId string = '' + +@description('Optional. The name of the customer managed key to use for encryption. Cannot be deployed together with the parameter \'systemAssignedIdentity\' enabled.') +param cMKKeyName string = '' + +@description('Conditional. User assigned identity to use when fetching the customer managed key. Required if \'cMKKeyName\' is not empty.') +param cMKUserAssignedIdentityResourceId string = '' + +@description('Optional. The version of the customer managed key to reference for encryption. If not provided, latest is used.') +param cMKKeyVersion string = '' + +@description('Optional. The flag to enable dynamic throttling.') +param dynamicThrottlingEnabled bool = false + +@description('Optional. Resource migration token.') +param migrationToken string = '' + +@description('Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists.') +param restore bool = false + +@description('Optional. Restrict outbound network access.') +param restrictOutboundNetworkAccess bool = true + +@description('Optional. The storage accounts for this resource.') +param userOwnedStorage array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = false + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var builtInRoleNames = { + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services Custom Vision Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3') + 'Cognitive Services Custom Vision Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f') + 'Cognitive Services Custom Vision Labeler': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c') + 'Cognitive Services Custom Vision Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73') + 'Cognitive Services Custom Vision Trainer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b') + 'Cognitive Services Data Reader (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c') + 'Cognitive Services Face Recognizer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7') + 'Cognitive Services Immersive Reader User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d') + 'Cognitive Services Language Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498') + 'Cognitive Services Language Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e') + 'Cognitive Services Language Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8') + 'Cognitive Services LUIS Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8') + 'Cognitive Services LUIS Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226') + 'Cognitive Services LUIS Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27') + 'Cognitive Services Metrics Advisor Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a') + 'Cognitive Services Metrics Advisor User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8') + 'Cognitive Services OpenAI Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442') + 'Cognitive Services OpenAI User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd') + 'Cognitive Services QnA Maker Editor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025') + 'Cognitive Services QnA Maker Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126') + 'Cognitive Services Speech Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181') + 'Cognitive Services Speech User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +#disable-next-line no-deployments-resources +resource defaultTelemetry 'Microsoft.Resources/deployments@2022-09-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { + name: last(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : 'dummyVault'), '/'))! + scope: resourceGroup(split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '//'), '/')[2], split((!empty(cMKKeyVaultResourceId) ? cMKKeyVaultResourceId : '////'), '/')[4]) + + resource cMKKey 'keys@2023-02-01' existing = if (!empty(cMKKeyName)) { + name: !empty(cMKKeyName) ? cMKKeyName : 'dummyKey' + } +} + +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(cMKUserAssignedIdentityResourceId)) { + name: last(split((!empty(cMKUserAssignedIdentityResourceId) ? cMKUserAssignedIdentityResourceId : 'dummyMsi'), '/'))! + scope: resourceGroup(split((!empty(cMKUserAssignedIdentityResourceId) ? cMKUserAssignedIdentityResourceId : '//'), '/')[2], split((!empty(cMKUserAssignedIdentityResourceId) ? cMKUserAssignedIdentityResourceId : '////'), '/')[4]) +} + +resource cognitiveServices 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + kind: kind + identity: identity + location: location + tags: tags + sku: { + name: sku + } + properties: { + customSubDomainName: !empty(customSubDomainName) ? customSubDomainName : name // otherwise you get error that account doesn't have CustomSubDomainName (Code: AccountCustomSubDomainNameNotSet + networkAcls: !empty(networkAcls) ? { + defaultAction: contains(networkAcls, 'defaultAction') ? networkAcls.defaultAction : null + virtualNetworkRules: contains(networkAcls, 'virtualNetworkRules') ? networkAcls.virtualNetworkRules : [] + ipRules: contains(networkAcls, 'ipRules') ? networkAcls.ipRules : [] + } : null + // publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) && empty(networkAcls) ? 'Disabled' : null) + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (hasPrivateLinks && empty(networkAcls) ? 'Disabled' : null) + allowedFqdnList: allowedFqdnList + apiProperties: apiProperties + disableLocalAuth: disableLocalAuth + encryption: !empty(cMKKeyName) ? { + keySource: 'Microsoft.KeyVault' + keyVaultProperties: { + identityClientId: cMKUserAssignedIdentity.properties.clientId + keyVaultUri: cMKKeyVault.properties.vaultUri + keyName: cMKKeyName + keyVersion: !empty(cMKKeyVersion) ? cMKKeyVersion : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) + } + } : null + migrationToken: !empty(migrationToken) ? migrationToken : null + restore: restore + restrictOutboundNetworkAccess: restrictOutboundNetworkAccess + userOwnedStorage: !empty(userOwnedStorage) ? userOwnedStorage : null + dynamicThrottlingEnabled: dynamicThrottlingEnabled + } +} + +resource cognitiveServices_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot delete or modify the resource or child resources.' + } + scope: cognitiveServices +} + +resource cognitiveServices_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + metrics: diagnosticSetting.?metricCategories ?? [ + { + category: 'AllMetrics' + timeGrain: null + enabled: true + } + ] + logs: diagnosticSetting.?logCategoriesAndGroups ?? [ + { + categoryGroup: 'AllLogs' + enabled: true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: cognitiveServices +}] + +// TODO: We need to implement this CARMEL variation for our private endpoints +// module cognitiveServices_privateEndpoints '../../network/private-endpoint/main.bicep' = [for (privateEndpoint, index) in (privateEndpoints ?? []): { +// name: '${uniqueString(deployment().name, location)}-cognitiveServices-PrivateEndpoint-${index}' +// params: { +// groupIds: [ +// privateEndpoint.?service ?? 'account' +// ] +// name: privateEndpoint.?name ?? 'pep-${last(split(cognitiveServices.id, '/'))}-${privateEndpoint.?service ?? 'account'}-${index}' +// serviceResourceId: cognitiveServices.id +// subnetResourceId: privateEndpoint.subnetResourceId +// enableDefaultTelemetry: privateEndpoint.?enableDefaultTelemetry ?? enableReferencedModulesTelemetry +// location: privateEndpoint.?location ?? reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location +// lock: privateEndpoint.?lock ?? lock +// privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName +// privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds +// roleAssignments: privateEndpoint.?roleAssignments +// tags: privateEndpoint.?tags ?? tags +// manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections +// customDnsConfigs: privateEndpoint.?customDnsConfigs +// ipConfigurations: privateEndpoint.?ipConfigurations +// applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds +// customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName +// } +// }] + +resource cognitiveServices_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(cognitiveServices.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] : roleAssignment.roleDefinitionIdOrName + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: cognitiveServices +}] + +@description('The name of the cognitive services account.') +output name string = cognitiveServices.name + +@description('The resource ID of the cognitive services account.') +output resourceId string = cognitiveServices.id + +@description('The resource group the cognitive services account was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The service endpoint of the cognitive services account.') +output endpoint string = cognitiveServices.properties.endpoint + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(cognitiveServices.identity, 'principalId') ? cognitiveServices.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = cognitiveServices.location + +// =============== // +// Definitions // +// =============== // + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type roleAssignmentType = { + @description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') + roleDefinitionIdOrName: string + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device' | null)? + + @description('Optional. The description of the role assignment.') + description: string? + + @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +}[]? + +type privateEndpointType = { + @description('Optional. The name of the private endpoint.') + name: string? + + @description('Optional. The location to deploy the private endpoint to.') + location: string? + + @description('Optional. The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob".') + service: string? + + @description('Required. Resource ID of the subnet where the endpoint needs to be created.') + subnetResourceId: string + + @description('Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided.') + privateDnsZoneGroupName: string? + + @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneResourceIds: string[]? + + @description('Optional. Custom DNS configurations.') + customDnsConfigs: { + fqdn: string? + ipAddresses: string[] + }[]? + + @description('Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints.') + ipConfigurations: { + name: string + groupId: string + memberName: string + privateIpAddress: string + }[]? + + @description('Optional. Application security groups in which the private endpoint IP configuration is included.') + applicationSecurityGroupResourceIds: string[]? + + @description('Optional. The custom name of the network interface attached to the private endpoint.') + customNetworkInterfaceName: string? + + @description('Optional. Specify the type of lock.') + lock: lockType + + @description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') + roleAssignments: roleAssignmentType + + @description('Optional. Tags to be applied on all resources/resource groups in this deployment.') + tags: object? + + @description('Optional. Manual PrivateLink Service Connections.') + manualPrivateLinkServiceConnections: array? + + @description('Optional. Enable/Disable usage telemetry for module.') + enableTelemetry: bool? +}[]? + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to \'\' to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to \'AllLogs\' to collect all logs.') + categoryGroup: string? + }[]? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to \'\' to disable log collection.') + metricCategories: { + @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to \'AllMetrics\' to collect all metrics.') + category: string + }[]? + + @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') + logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics' | null)? + + @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + workspaceResourceId: string? + + @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + storageAccountResourceId: string? + + @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') + eventHubAuthorizationRuleResourceId: string? + + @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +}[]? diff --git a/scenarios/shared/bicep/naming.module.bicep b/scenarios/shared/bicep/naming.module.bicep index 11b33d50..5ce91308 100644 --- a/scenarios/shared/bicep/naming.module.bicep +++ b/scenarios/shared/bicep/naming.module.bicep @@ -761,7 +761,12 @@ name: endsWith(take(replace(nt, ph, 'dnsrec'), 50), d) ? take(replace(nt, ph, 'dnsrec'), 50-1) : take(replace(nt, ph, 'dnsrec'), 50) nameUnique: endsWith(take(replace(nut, ph, 'dnsrec'), 50), d) ? take(replace(nut, ph, 'dnsrec'), 50-1) : take(replace(nut, ph, 'dnsrec'), 50) slug: 'dnsrec' - } + } + openAiDeployment: { + name: endsWith(take(replace(nt, ph, 'oaidep'), 50), d) ? take(replace(nt, ph, 'oaidep'), 50-1) : take(replace(nt, ph, 'oaidep'), 50) + nameUnique: endsWith(take(replace(nut, ph, 'oaidep'), 50), d) ? take(replace(nut, ph, 'oaidep'), 50-1) : take(replace(nut, ph, 'oaidep'), 50) + slug: 'oaidep' + } pointToSiteVpnGateway: { name: endsWith(take(replace(nt, ph, 'vpngw'), 80), d) ? take(replace(nt, ph, 'vpngw'), 80-1) : take(replace(nt, ph, 'vpngw'), 80) nameUnique: endsWith(take(replace(nut, ph, 'vpngw'), 80), d) ? take(replace(nut, ph, 'vpngw'), 80-1) : take(replace(nut, ph, 'vpngw'), 80) diff --git a/scenarios/shared/bicep/role-assignments/role-assignment.bicep b/scenarios/shared/bicep/role-assignments/role-assignment.bicep index 343ce9a0..5b5ca371 100644 --- a/scenarios/shared/bicep/role-assignments/role-assignment.bicep +++ b/scenarios/shared/bicep/role-assignments/role-assignment.bicep @@ -31,6 +31,7 @@ param roledescription string = '' // leave these for loggin in the portal // var segment = [for (item, index) in range(1,last) : item == 1 ? '${segments[0]}/${segments[item]}/${items[index]}/' : item != last ? '${segments[item]}/${items[index]}/' : '${segments[item]}/${items[index]}' ] // var resourceid = replace(replace(replace(string(string(segment)), '","', ''), '["', ''), '"]', '') // currently no join() method // // ---------------------------------------------- +#disable-next-line no-deployments-resources resource resourceRoleAssignment 'Microsoft.Resources/deployments@2021-04-01' = { name: name // take('RA-${principalId}-${roleDefinitionId}-${last(split(resourceId,'/'))}',64) properties: {