[
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n\t\"name\": \"Getting Started\",\n\t\"image\": \"mcr.microsoft.com/devcontainers/universal:2\",\n\t\"hostRequirements\": {\n\t  \"cpus\": 4\n\t},\n\t\"features\": {\n\t\t\"ghcr.io/devcontainers/features/common-utils:2\": {\n\t\t\t\"configureZshAsDefaultShell\": true,\n\t\t\t\"installOhMyZsh\": true,\n\t\t\t\"installOhMyZshConfig\": true\n\t\t},\n\t\t\"ghcr.io/stuartleeks/dev-container-features/shell-history:0\": {},\n\t\t\"ghcr.io/devcontainers/features/azure-cli:1\": {\n\t\t\t\"installBicep\": true,\n\t\t\t\"version\": \"latest\"\n\t\t},\n\t\t\"ghcr.io/stuartleeks/dev-container-features/azure-cli-persistence:0\": {},\n\t\t\"ghcr.io/devcontainers/features/terraform:1\": {}\n\t},\n\t\"waitFor\": \"onCreateCommand\",\n\t\"customizations\": {\n\t\t\"vscode\": {\n\t\t\t\"extensions\": [\n\t\t\t\t\"eamodio.gitlens\",\n\t\t\t\t\"GitHub.copilot\",\n\t\t\t\t\"Gruntfuggly.todo-tree\",\n\t\t\t\t\"ionutvmi.path-autocomplete\",\n\t\t\t\t\"mechatroner.rainbow-csv\",\n\t\t\t\t\"ms-vsliveshare.vsliveshare\",\n\t\t\t\t\"redhat.vscode-yaml\",\n\t\t\t\t\"timonwong.shellcheck\",\n\t\t\t\t\"GitHub.vscode-pull-request-github\",\n\t\t\t\t\"humao.rest-client\",\n\t\t\t\t\"ms-azuretools.vscode-bicep\",\n\t\t\t\t\"ms-azuretools.vscode-azureterraform\",\n\t\t\t\t\"azapi-vscode.azapi\"\n\t\t\t],\n\t\t\t\"settings\": {\n\t\t\t\t\"files.insertFinalNewline\": true,\n\t\t\t\t\"github.copilot.enable\": {\n\t\t\t\t\t\"markdown\": true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\t\"remoteEnv\": {\n        \"HOST_PROJECT_PATH\": \"${localWorkspaceFolder}\"\n    },\n\t\"mounts\": [\n\t\t// map host ssh to container\n\t\t\"source=${env:HOME}${env:USERPROFILE}/.ssh,target=/home/codespace/.ssh,type=bind,consistency=cached\"\n\t]\n}\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Set bash scripts to use LF line endings regardless of the platform\n# used to clone the code\n*.sh text eol=lf\n"
  },
  {
    "path": ".github/workflows/lza-validate-multiregion-apim.yml",
    "content": "# This is a basic workflow to help you get started with Actions\n\nname: LZA-Validation-Multi-Region-APIM\n\n# Controls when the workflow will run\non:\n  # Triggers the workflow on push or pull request events but only for the \"main\" branch\n  push:\n  pull_request:\n   branches: [ \"main\" ]\n\n  # Allows you to run this workflow manually from the Actions tab\n  workflow_dispatch:\n\n# A workflow run is made up of one or more jobs that can run sequentially or in parallel\njobs:\n  # This workflow contains a single job called \"build\"\n  build:\n    # The type of runner that the job will run on\n    runs-on: ubuntu-latest\n\n    # Steps represent a sequence of tasks that will be executed as part of the job\n    steps:\n      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it\n      - uses: actions/checkout@v4\n      - uses: hashicorp/setup-terraform@v3\n\n      # Runs a set of commands using the runners shell\n      - name: Run deploy-apim-baseline script\n        run: |\n          echo Performing Validation....\n          ls -la\n          cd scenarios/scripts/terraform\n          cp sample-multi-region.env .env\n          ./deploy-apim-baseline.sh --validate-commit\n"
  },
  {
    "path": ".github/workflows/lza-validate-singleregion-apim.yml",
    "content": "# This is a basic workflow to help you get started with Actions\n\nname: LZA-Validation-Single-Region-APIM\n\n# Controls when the workflow will run\non:\n  # Triggers the workflow on push or pull request events but only for the \"main\" branch\n  push:\n  pull_request:\n   branches: [ \"main\" ]\n\n  # Allows you to run this workflow manually from the Actions tab\n  workflow_dispatch:\n\n# A workflow run is made up of one or more jobs that can run sequentially or in parallel\njobs:\n  # This workflow contains a single job called \"build\"\n  build:\n    # The type of runner that the job will run on\n    runs-on: ubuntu-latest\n\n    # Steps represent a sequence of tasks that will be executed as part of the job\n    steps:\n      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it\n      - uses: actions/checkout@v4\n      - uses: hashicorp/setup-terraform@v3\n\n      # Runs a set of commands using the runners shell\n      - name: Run deploy-apim-baseline script \n        run: |\n          echo Performing Validation....\n          ls -la\n          cd scenarios/scripts/terraform\n          cp sample.env .env\n          ./deploy-apim-baseline.sh --validate-commit\n"
  },
  {
    "path": ".gitignore",
    "content": "/.vs/ProjectSettings.json\n/.vs/slnx.sqlite\ndeployment/bicep/parameters.local.json\ndeployment/bicep/localparam*.json\ndeployment/bicep/localmain.bicep\ndeployment/bicep/localtestscript.ps1\n\n# Terraform section\n# Local .terraform directories\n**/.terraform/*\n\noutput.json\nparameters.json\n\n# .tfstate files\n*.tfstate\n*.tfstate.*\n*.tfplan\n\n# Crash log files\ncrash.log\ncrash.*.log\n\n# Exclude all .tfvars files, which are likely to contain sensitive data, such as\n# password, private keys, and other secrets. These should not be part of version\n# control as they are data points which are potentially sensitive and subject\n# to change depending on the environment.\n*.tfvars\n*.tfvars.json\n\n# Ignore override files as they are usually used to override resources locally and so\n# are not checked in\noverride.tf\noverride.tf.json\n*_override.tf\n*_override.tf.json\n\n# Include override files you do wish to add to version control using negated pattern\n# !example_override.tf\n\n# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan\n# example: *tfplan*\n\n# Ignore CLI configuration files\n.terraformrc\nterraform.rc\n\n# Don't copy local lock\n*.terraform.lock.hcl\n\n# Azure Functions localsettings file\nlocal.settings.json\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# DNX\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# TODO: Comment the next line if you want to checkin your web deploy settings\n# but database connection strings (with potential passwords) will be unencrypted\n#*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n# NuGet v3's project.json files produces more ignoreable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\nnode_modules/\norleans.codegen.cs\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n# CodeRush\n.cr/\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n!.vscode/*.code-snippets\n\n#Local .terraform directories\n**/.terraform/*\n.terraform/\n\n# .tfstate files\n*.tfstate\n*.tfstate.*\n# tf plan files\n*.plan\n\n# Crash log files\ncrash.log\n\n# Ignore override files as they are usually used to override resources locally\noverride.tf\noverride.tf.json\n*_override.tf\n*_override.tf.json\n\n**/azuredeploy.parameters.json\nscenarios/.env\nscenarios/apim-baseline/bicep/parameters.json\n\n# Rest client test files\n*.http\n\n*-backend.hcl\nscenarios/scripts/terraform/apim-self-signed.crt\nscenarios/scripts/terraform/apim-self-signed.key\nscenarios/scripts/terraform/tmp-self-signed-cert.conf\nscenarios/scripts/terraform/.env\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "- repo: https://github.com/antonbabenko/pre-commit-terraform\n  rev: v1.76.0\n  hooks:\n    - id: terraform_fmt\n    - id: terraform_docs"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Microsoft Open Source Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\n\nResources:\n\n- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)\n- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)\n- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns\n"
  },
  {
    "path": "LICENSE",
    "content": "    MIT License\n\n    Copyright (c) Microsoft Corporation.\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in all\n    copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n    SOFTWARE\n"
  },
  {
    "path": "README.md",
    "content": "# Azure API Management Landing Zone Accelerator\n\nAzure API Management Landing Zone Accelerator provides packaged guidance with reference architecture and reference implementation along with design guidance recommendations and considerations on critical design areas for provisioning APIM with a secure baseline. They are aligned with industry proven practices, such as those presented in [Azure landing zones](https://learn.microsoft.com/azure/cloud-adoption-framework/ready/landing-zone/) guidance in the Cloud Adoption Framework.\n\n## Reference Architecture\n\n![image](/docs/images/apim-secure-baseline.jpg)\n\n## :mag: Design areas\n\nThe enterprise architecture is broken down into six different design areas, where you can find the links to each at:\n| Design Area|Considerations|Recommendations|\n|:--------------:|:--------------:|:--------------:|\n| Identity and Access Management|[Design Considerations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/api-management/identity-and-access-management#design-considerations)|[Design Recommendations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/api-management/identity-and-access-management#design-recommendations)|\n| Network Topology and Connectivity|[Design Considerations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/api-management/network-topology-and-connectivity#design-considerations)|[Design Recommendations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/api-management/network-topology-and-connectivity#design-recommendations)|\n| Security|[Design Considerations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/api-management/security#design-considerations)|[Design Recommendations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/api-management/security#design-recommendations)|\n| Management|[Design Considerations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/api-management/management#design-considerations)|[Design Recommendations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/api-management/management#design-recommendation)|\n| Governance|[Design Considerations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/api-management/governance#design-considerations)|[Design Recommendations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/api-management/governance#design-recommendations)|\n| Platform Automation and DevOps|[Design Considerations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/api-management/platform-automation-and-devops#design-considerations)|[Design Recommendations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/api-management/platform-automation-and-devops#design-recommendations)|\n\n## :rocket: Deployment scenarios\n\nThis repo contains the Azure landing zone accelerator's reference implementations, all with supporting *Infrastructure as Code* artifacts. The scenarios covered are:\n\n### :arrow_forward: [Scenario 1: Azure API Management - Secure Baseline](scenarios/apim-baseline/README.md)\n\nDeploys APIM with a secure baseline configuration with no backends and a sample API. \n\n### :arrow_forward: [Scenario 2: Azure API Management - Function Backend](scenarios/workload-functions/README.md)\n\nOn top of the secure baseline, deploys a private Azure function as a backend and provision APIs in APIM to access the function.\n\n### :arrow_forward: [Scenario 3: Azure API Management - Gen AI Backend](scenarios/workload-genai/README.md)\n\nOn top of the secure baseline, deploys private Azure OpenAI endpoints (3 endpoints) as backend and provision API that can handle [multiple use cases.](./scenarios/workload-genai/README.md#scenarios-handled-by-this-accelerator)\n\n*More reference implementation scenarios will be added as they become available.*\n\n### Supported Regions\n\nSome of the new Azure OpenAI policies are not available in al the regions yet. If you see the deployment failures, try chosing a different region. The following regions are more likely to work.\n\n```shell\naustraliacentral, australiaeast, australiasoutheast, brazilsouth, eastasia, francecentral, germanywestcentral, koreacentral, northeurope, southeastasia, southcentralus, uksouth, ukwest, westeurope, westus2, westus3\n```\n\n## Got a feedback\n\nPlease leverage issues if you have any feedback or request on how we can improve on this repository.\n\n---\n\n## Data Collection\n\nThe software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft's privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkId=521839. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.\n\n### Telemetry Configuration\n\nTelemetry collection is on by default.\n\nTo opt-out, set the variable ENABLE_TELEMETRY to `false` in [.env](./scenarios/.env) file.\n\n---\n\n## Contributing\n\nThis project welcomes contributions and suggestions.  Most contributions require you to agree to a\nContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\nthe rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.\n\nWhen you submit a pull request, a CLA bot will automatically determine whether you need to provide\na CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions\nprovided by the bot. You will only need to do this once across all repos using our CLA.\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\n\n## Trademarks\n\nThis project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft \ntrademarks or logos is subject to and must follow \n[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\nAny use of third-party trademarks or logos are subject to those third-party's policies.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->\n\n# Security\n\nMicrosoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).\n\nIf you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://learn.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.\n\n## Reporting Security Issues\n\n**Please do not report security vulnerabilities through public GitHub issues.**\n\nInstead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).\n\nIf you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).\n\nAdditional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).\n\nPlease include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:\n\n    * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)\n    * Full paths of source file(s) related to the manifestation of the issue\n    * The location of the affected source code (tag/branch/commit or direct URL)\n    * Any special configuration required to reproduce the issue\n    * Step-by-step instructions to reproduce the issue\n    * Proof-of-concept or exploit code (if possible)\n    * Impact of the issue, including how an attacker might exploit the issue\n\nThis information will help us triage your report more quickly.\n\nIf you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.\n\n## Preferred Languages\n\nWe prefer all communications to be in English.\n\n## Policy\n\nMicrosoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).\n\n<!-- END MICROSOFT SECURITY.MD BLOCK -->"
  },
  {
    "path": "SUPPORT.md",
    "content": "# Support\n\n## How to file issues and get help\n\nThis project uses GitHub Issues to track bugs and feature requests. Please search the existing\nissues before filing new issues to avoid duplicates.  For new issues, file your bug or\nfeature request as a new Issue.\n\n## Microsoft Support Policy\n\nSupport for this **PROJECT or PRODUCT** is limited to the resources listed above.\n"
  },
  {
    "path": "scenarios/apim-baseline/README.md",
    "content": "# Scenario 1: Azure API Management - Secure Baseline\n\nThis reference implementation demonstrates a *secure baseline infrastructure architecture* for provisioning [Azure API Management](https://learn.microsoft.com/azure/api-management/). Specifically this scenario addresses deploying Azure API Management into a [virtual network](https://learn.microsoft.com/azure/api-management/api-management-using-with-internal-vnet?tabs=stv2), in an internal mode where you can only access the API Management endpoints like API gateway, developer portal, Direct management and Git within a VNet whose access you control.\n\nBy the end of this deployment guide, you would have deployed an \"internal mode\" Azure API Management premium instance.\n\n![Architectural diagram showing an Azure API Management deployment in a virtual network.](../../docs/images/apim-secure-baseline.jpg)\n\n## Core architecture components\n\n- Azure API Management (Developer SKU)\n- Azure Virtual Networks\n- Azure Application Gateway (with Web Application Firewall)\n- Azure Standard Public IP\n- Azure Key Vault\n- Azure Private Endpoint\n- Azure Private DNS Zones\n- Log Analytics Workspace\n- Azure Application Insights\n\n## Deploy the reference implementation\n\nThis reference implementation is provided with the following infrastructure as code options. Select the deployment guide you are interested in. They both deploy the same implementation.\n\n:arrow_forward: [Bicep-based deployment guide](./bicep/README.md)\n:arrow_forward: [Terraform-based deployment guide](./terraform/README.md)\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/README.md",
    "content": "# Azure API Management - Secure Baseline [Bicep]\n\n**Note:**\nThis template may be out of date temporarily.  Please use the [Terraform version](../terraform/README.md) of this scenario for the most up-to-date implementation.\n\n\nThis is the Bicep-based deployment guide for [Scenario 1: Azure API Management - Secure Baseline](../README.md).\n\n## Prerequisites\n\nThis is the starting point for the instructions on deploying this reference implementation. There is required access and tooling you'll need in order to accomplish this.\n\n- An Azure subscription\n- The following resource providers [registered](https://learn.microsoft.com/azure/azure-resource-manager/management/resource-providers-and-types#register-resource-provider):\n  - `Microsoft.ApiManagement`\n  - `Microsoft.Network`\n  - `Microsoft.KeyVault`\n- The user or service principal initiating the deployment process must have the [owner role](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner) at the subscription level to have the ability to create resource groups and to delegate access to others (Azure Managed Identities created from the IaC deployment).\n- Access to Bash command line to run the deployment script.\n- Latest [Azure CLI installed](https://learn.microsoft.com/cli/azure/install-azure-cli?view=azure-cli-latest) (must be at least 2.40), or you can perform this from Azure Cloud Shell by clicking below.\n\n  [![Launch Azure Cloud Shell](https://learn.microsoft.com/azure/includes/media/cloud-shell-try-it/launchcloudshell.png)](https://shell.azure.com)\n- JQ command line JSON processor installed\n\n   ```bash\n   sudo apt-get install jq\n   ```\n\n## Steps\n\n1. Clone/download this repo locally, or even better fork this repository.\n\n   ```bash\n   git clone https://github.com/Azure/apim-landing-zone-accelerator.git\n   cd apim-landing-zone-accelerator/scenarios/scripts\n   ```\n\n1. Log into Azure from the AZ CLI and select your subscription.\n\n   ```bash\n   az login\n   ```\n\n1. Review and update deployment parameters.\n\n   Copy the `sample.env` into a new file called `.env` in the same directory.\n\n   ```bash\n   The [**.env**](../../.env) parameter file is where you can customize your deployment. The defaults are a suitable starting point, but feel free to adjust any to fit your requirements.\n\n   **Deployment parameters**\n\n   | Name  | Description | Default | Example(s) |\n   | :---- | :---------- | :------ | :--------- |\n   | `AZURE_LOCATION` | The Azure location to deploy to. | **eastus** | **westus** |\n   | `RESOURCE_NAME_PREFIX` | A suffix for naming. | **apimdemo** | **appname** |\n   | `ENVIRONMENT_TAG` | A tag that will be included in the naming. | **dev** | **stage** |\n   | `APPGATEWAY_FQDN` | The Azure location to deploy to. | **apim.example.com** | **my.org.com** |\n   | `CERT_TYPE` | selfsigned will create a self-signed certificate for the APPGATEWAY_FQDN. custom will use an existing certificate in pfx format that needs to be available in the [certs](../../certs) folder and named appgw.pfx | **selfsigned** | **custom** |\n   | `CERT_PWD` | The password for the pfx certificate. Only required if CERT_TYPE is custom. | **N/A** | **password123** |\n   | `RANDOM_IDENTIFIER` | Optional 3 character random string to ensure deployments are unique. Automatically assigned if not provided | **abc** | **pqr** |\n\n1. Deploy the reference implementation.\n\n   Run the following command to deploy the APIM baseline\n\n    ```bash\n    ./scripts/bicep/deploy-apim-baseline.sh\n    ```\n\nTest the echo api using the generated command from the output\n\n## Troubleshooting\n\nIf you see the message `-bash: ./deploy-apim-baseline.sh: /bin/bash^M: bad interpreter: No such file or directory` when running the script, you can fix this by running the following command:\n\n   ```bash\n    sed -i -e 's/\\r$//' deploy-apim-baseline.sh\n   ```\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/apim/apim.bicep",
    "content": "targetScope='resourceGroup'\n\n/*\n * Input parameters\n*/\n\n@description('The name of the API Management resource to be created.')\nparam apimName            string\n\n@description('The subnet resource id to use for APIM.')\n@minLength(1)\nparam apimSubnetId string\n\n@description('The email address of the publisher of the APIM resource.')\n@minLength(1)\nparam publisherEmail string = 'apim@contoso.com'\n\n@description('Company name of the publisher of the APIM resource.')\n@minLength(1)\nparam publisherName string = 'Contoso'\n\n@description('The pricing tier of the APIM resource.')\nparam skuName string = 'Developer'\n\n@description('The instance size of the APIM resource.')\nparam capacity int = 1\n\n@description('Location for Azure resources.')\nparam location string = resourceGroup().location\n\nparam appInsightsName string\nparam appInsightsId string\nparam appInsightsInstrumentationKey string\n\nparam keyVaultName                  string\nparam keyVaultResourceGroupName     string\n\nparam vnetName string\nparam networkingResourceGroupName string\nparam apimRG string\nvar echoSubscriptionKey = guid('echoPrimaryKey')\n/*\n * Resources\n*/\n\nvar apimIdentityName = 'identity-${apimName}'\n\nresource apimIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {\n  name: apimIdentityName\n  location: location\n}\n\nresource apimName_resource 'Microsoft.ApiManagement/service@2020-12-01' = {\n  name: apimName\n  location: location\n  sku:{\n    capacity: capacity\n    name: skuName\n  }\n  identity: {\n    type:'UserAssigned'\n    userAssignedIdentities: {\n      '${apimIdentity.id}': {}\n    }\n  }\n  properties:{\n    virtualNetworkType: 'Internal'\n    publisherEmail: publisherEmail\n    publisherName: publisherName\n    virtualNetworkConfiguration: {\n      subnetResourceId: apimSubnetId\n    }\n    apiVersionConstraint: {\n      minApiVersion: '2019-12-01'\n    }\n  }\n}\n\nresource echoSubscription 'Microsoft.ApiManagement/service/subscriptions@2020-12-01' = {\n  parent: apimName_resource\n  name: 'Echo'\n  properties: {\n    displayName: 'Echo'\n    scope: '/products/starter'\n    primaryKey: echoSubscriptionKey\n  }\n}\n\nresource apimName_appInsightsLogger_resource 'Microsoft.ApiManagement/service/loggers@2021-08-01' = {\n  parent: apimName_resource\n  name: appInsightsName\n  properties: {\n    loggerType: 'applicationInsights'\n    resourceId: appInsightsId\n    credentials: {\n      instrumentationKey: appInsightsInstrumentationKey\n    }\n  }\n}\n\nresource apimName_applicationinsights 'Microsoft.ApiManagement/service/diagnostics@2021-08-01' = {\n  parent: apimName_resource\n  name: 'applicationinsights'\n  properties: {\n    loggerId: apimName_appInsightsLogger_resource.id\n    alwaysLog: 'allErrors'\n    sampling: {\n      percentage: 100\n      samplingType: 'fixed'\n    }\n    metrics: true\n  }\n}\n\nmodule kvaccess './modules/kvaccess.bicep' = {\n  name: 'kvaccess'\n  scope: resourceGroup(keyVaultResourceGroupName)\n  params: {\n    managedIdentity:    apimIdentity\n    keyVaultName:       keyVaultName\n  }\n}\n\n//Creation of private DNS zones\nmodule dnsZoneModule './modules/dnsrecords.bicep'  = {\n  name: 'apimDnsRecordsDeploy'\n  scope: resourceGroup(networkingResourceGroupName)\n  dependsOn: [\n    apimName_resource\n  ]\n  params: {\n    vnetName: vnetName\n    apimName: apimName\n    apimRG: apimRG\n    networkingResourceGroupName: networkingResourceGroupName  \n  }\n}\n\noutput apimStarterSubscriptionKey string = echoSubscriptionKey\noutput apimIdentityName string = apimIdentityName\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/apim/modules/dnsrecords.bicep",
    "content": "\nparam vnetName                  string\nparam networkingResourceGroupName string\nparam apimName                  string\nparam apimRG                    string\n\n\nresource apim 'Microsoft.ApiManagement/service@2020-12-01' existing = {\n  name: apimName\n  scope: resourceGroup(apimRG)\n}\n\nmodule dnsZone '../../shared/modules/dnszone.bicep' = {\n  name: 'apimDnsZoneDeploy'\n  params: {\n    vnetName: vnetName\n    networkingResourceGroupName: networkingResourceGroupName\n    domain: '${apimName}.azure-api.net'\n  }\n}\n\nresource apimDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = {\n  name: '${apimName}.azure-api.net'\n}\n\n\nresource gatewayRecord 'Microsoft.Network/privateDnsZones/A@2020-06-01' = {\n  parent: apimDnsZone\n  name: '@'\n  dependsOn: [\n    apim\n    dnsZone\n  ]\n  properties: {\n    aRecords: [\n      {\n        ipv4Address: apim.properties.privateIPAddresses[0]\n      }\n    ]\n    ttl: 36000\n  }\n}\n\nresource developerRecord 'Microsoft.Network/privateDnsZones/A@2020-06-01' = {\n  parent: apimDnsZone\n  name: 'developer'\n  dependsOn: [\n    apim\n    dnsZone\n  ]\n  properties: {\n    aRecords: [\n      {\n        ipv4Address: apim.properties.privateIPAddresses[0]\n      }\n    ]\n    ttl: 36000\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/apim/modules/kvaccess.bicep",
    "content": "param keyVaultName            string\nparam managedIdentity         object   \n\nresource accessPolicyGrant 'Microsoft.KeyVault/vaults/accessPolicies@2019-09-01' = {\n  name: '${keyVaultName}/add'\n  properties: {\n    accessPolicies: [\n      {\n        objectId: managedIdentity.properties.principalId\n        tenantId: managedIdentity.properties.tenantId\n        permissions: {\n          secrets: [ \n            'get' \n            'list'\n          ]\n          certificates: [\n            'get'\n            'list'\n          ]\n        }                  \n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/gateway/appgw.bicep",
    "content": "/*\n * Input parameters\n*/\n@description('The name of the Application Gateawy to be created.')\nparam appGatewayName string\n\n@description('The FQDN of the Application Gateawy.Must match the TLS Certificate.')\nparam appGatewayFQDN string\n\n@description('The location of the Application Gateawy to be created')\nparam location string = resourceGroup().location\n\n@description('The subnet resource id to use for Application Gateway.')\nparam appGatewaySubnetId string\n\n@description('Set to selfsigned if self signed certificates should be used for the Application Gateway. Set to custom and pass the CertData and CertKey if custom certificates should be used.')\nparam appGatewayCertType string\n\n@description('The backend URL of the APIM.')\nparam primaryBackendEndFQDN string\n\n@description('The Url for the APIM Health Probe.')\nparam probeUrl string = '/status-0123456789abcdef'\n\nparam appGatewayPublicIpName string\nparam keyVaultName string\nparam keyVaultResourceGroupName string\n\nparam deploymentIdentityName string\nparam deploymentSubnetId     string\nparam deploymentStorageName    string\n\nparam certKey string\nparam certData string\n\nvar appGatewayIdentityId = 'identity-${appGatewayName}'\nvar appGatewayFirewallPolicy = 'waf-${appGatewayName}'\n\nresource appGatewayIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {\n  name: appGatewayIdentityId\n  location: location\n}\n\nmodule certificate './modules/certificate.bicep' = {\n  name: 'certificate'\n  scope: resourceGroup(keyVaultResourceGroupName)\n  params: {\n    managedIdentity: appGatewayIdentity\n    deploymentIdentityName: deploymentIdentityName\n    deploymentSubnetId: deploymentSubnetId\n    deploymentStorageName: deploymentStorageName\n    keyVaultName: keyVaultName\n    location: location\n    appGatewayFQDN: appGatewayFQDN\n    appGatewayCertType: appGatewayCertType\n    certKey: certKey\n    certData: certData\n  }\n}\n\nresource appGatewayPublicIPAddress 'Microsoft.Network/publicIPAddresses@2019-09-01' existing = {\n  name: appGatewayPublicIpName\n}\n\nresource appgw_waf_Pol 'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies@2021-08-01' = {\n  name: appGatewayFirewallPolicy\n  location: location\n  properties: {\n    policySettings: {\n      requestBodyCheck: true\n      maxRequestBodySizeInKb: 128\n      fileUploadLimitInMb: 100\n      state: 'Enabled'\n      mode: 'detection'\n    }\n    managedRules: {\n      managedRuleSets: [\n        {\n          ruleSetType: 'OWASP'\n          ruleSetVersion: '3.2'\n        }\n      ]\n    }\n  }\n}\n\nresource appGatewayName_resource 'Microsoft.Network/applicationGateways@2019-09-01' = {\n  name: appGatewayName\n  location: location\n  dependsOn: [\n    certificate\n  ]\n  identity: {\n    type: 'UserAssigned'\n    userAssignedIdentities: {\n      '${appGatewayIdentity.id}': {}\n    }\n  }\n  properties: {\n    sku: {\n      name: 'WAF_v2'\n      tier: 'WAF_v2'\n    }\n    gatewayIPConfigurations: [\n      {\n        name: 'appGatewayIpConfig'\n        properties: {\n          subnet: {\n            id: appGatewaySubnetId\n          }\n        }\n      }\n    ]\n    sslCertificates: [\n      {\n        name: appGatewayFQDN\n        properties: {\n          keyVaultSecretId: certificate.outputs.secretUri\n        }\n      }\n    ]\n    sslPolicy: {\n      minProtocolVersion: 'TLSv1_2'\n      policyType: 'Custom'\n      cipherSuites: [\n        'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256'\n        'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384'\n        'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'\n        'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'\n        'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256'\n        'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384'\n        'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256'\n        'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384'\n      ]\n    }\n    trustedRootCertificates: []\n    frontendIPConfigurations: [\n      {\n        name: 'appGwPublicFrontendIp'\n        properties: {\n          privateIPAllocationMethod: 'Dynamic'\n          publicIPAddress: {\n            id: appGatewayPublicIPAddress.id\n          }\n        }\n      }\n    ]\n    frontendPorts: [\n      {\n        name: 'port_443'\n        properties: {\n          port: 443\n        }\n      }\n    ]\n    backendAddressPools: [\n      {\n        name: 'apim'\n        properties: {\n          backendAddresses: [\n            {\n              fqdn: primaryBackendEndFQDN\n            }\n          ]\n        }\n      }\n      {\n        name: 'sink-hole'\n        properties: {\n          backendAddresses: []\n        }\n      }\n    ]\n    backendHttpSettingsCollection: [\n      {\n        name: 'apim-demo-apis-https'\n        properties: {\n          port: 443\n          protocol: 'Https'\n          cookieBasedAffinity: 'Disabled'\n          hostName: primaryBackendEndFQDN\n          pickHostNameFromBackendAddress: false\n          requestTimeout: 20\n          probe: {\n            id: resourceId('Microsoft.Network/applicationGateways/probes', appGatewayName, 'apim-demo-apis-https')\n          }\n        }\n      }\n    ]\n    httpListeners: [\n      {\n        name: 'apim-demo-apis-https'\n        properties: {\n          frontendIPConfiguration: {\n            id: resourceId(\n              'Microsoft.Network/applicationGateways/frontendIPConfigurations',\n              appGatewayName,\n              'appGwPublicFrontendIp'\n            )\n          }\n          frontendPort: {\n            id: resourceId('Microsoft.Network/applicationGateways/frontendPorts', appGatewayName, 'port_443')\n          }\n          protocol: 'Https'\n          sslCertificate: {\n            id: resourceId('Microsoft.Network/applicationGateways/sslCertificates', appGatewayName, appGatewayFQDN)\n          }\n          hostnames: [\n            appGatewayFQDN\n          ]\n          requireServerNameIndication: false\n        }\n      }\n    ]\n    urlPathMaps: [\n      {\n        name: 'urlPathMapApim'\n        properties: {\n          defaultBackendAddressPool: {\n            id: resourceId(\n              'Microsoft.Network/applicationGateways/backendAddressPools',\n              appGatewayName,\n              'apim'\n            )\n          }\n          defaultBackendHttpSettings: {\n            id: resourceId(\n              'Microsoft.Network/applicationGateways/backendHttpSettingsCollection',\n              appGatewayName,\n              'apim-demo-apis-https'\n            )\n          }\n          pathRules: [\n            {\n              name: 'echo-api'\n              properties: {\n                paths: [\n                  '/echo/*'\n                ]\n                backendAddressPool: {\n                  id: resourceId(\n                    'Microsoft.Network/applicationGateways/backendAddressPools',\n                    appGatewayName,\n                    'apim'\n                  )\n                }\n                backendHttpSettings: {\n                  id: resourceId(\n                    'Microsoft.Network/applicationGateways/backendHttpSettingsCollection',\n                    appGatewayName,\n                    'apim-demo-apis-https'\n                  )\n                }\n              }\n            }\n            {\n              name: 'hello-api'\n              properties: {\n                paths: [\n                  '/hello*'\n                ]\n                backendAddressPool: {\n                  id: resourceId(\n                    'Microsoft.Network/applicationGateways/backendAddressPools',\n                    appGatewayName,\n                    'apim'\n                  )\n                }\n                backendHttpSettings: {\n                  id: resourceId(\n                    'Microsoft.Network/applicationGateways/backendHttpSettingsCollection',\n                    appGatewayName,\n                    'apim-demo-apis-https'\n                  )\n                }\n              }\n            }\n            {\n              name: 'openai-api'\n              properties: {\n                paths: [\n                  '/openai/*'\n                ]\n                backendAddressPool: {\n                  id: resourceId(\n                    'Microsoft.Network/applicationGateways/backendAddressPools',\n                    appGatewayName,\n                    'apim'\n                  )\n                }\n                backendHttpSettings: {\n                  id: resourceId(\n                    'Microsoft.Network/applicationGateways/backendHttpSettingsCollection',\n                    appGatewayName,\n                    'apim-demo-apis-https'\n                  )\n                }\n              }\n            }            \n            {\n              name: 'default'\n              properties: {\n                paths: [\n                  '/*'\n                ]\n                backendAddressPool: {\n                  id: resourceId(\n                    'Microsoft.Network/applicationGateways/backendAddressPools',\n                    appGatewayName,\n                    'sink-hole'\n                  )\n                }\n                backendHttpSettings: {\n                  id: resourceId(\n                    'Microsoft.Network/applicationGateways/backendHttpSettingsCollection',\n                    appGatewayName,\n                    'apim-demo-apis-https'\n                  )\n                }\n              }\n            }            \n          ]\n        }\n      }\n    ]\n    requestRoutingRules: [\n      {\n        name: 'apim-demo-apis'\n        properties: {\n          ruleType: 'PathBasedRouting'\n          priority: 100\n          urlPathMap: {\n            id: resourceId('Microsoft.Network/applicationGateways/urlPathMaps', appGatewayName, 'urlPathMapApim')\n          }\n          httpListener: {\n            id: resourceId(\n              'Microsoft.Network/applicationGateways/httpListeners',\n              appGatewayName,\n              'apim-demo-apis-https'\n            )\n          }\n        }\n      }\n    ]\n    probes: [\n      {\n        name: 'apim-demo-apis-https'\n        properties: {\n          protocol: 'Https'\n          host: primaryBackendEndFQDN\n          path: probeUrl\n          interval: 30\n          timeout: 30\n          unhealthyThreshold: 3\n          pickHostNameFromBackendHttpSettings: false\n          minServers: 0\n          match: {\n            statusCodes: [\n              '200-399'\n            ]\n          }\n        }\n      }\n    ]\n    rewriteRuleSets: []\n    redirectConfigurations: []\n    firewallPolicy: {\n      id: appgw_waf_Pol.id\n    }\n    enableHttp2: true\n    autoscaleConfiguration: {\n      minCapacity: 2\n      maxCapacity: 3\n    }\n  }\n}\n\noutput appGatewayPublicIpAddress string = appGatewayPublicIPAddress.properties.ipAddress\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/gateway/modules/certificate.bicep",
    "content": "param keyVaultName            string\nparam managedIdentity         object      \nparam location                string\nparam appGatewayFQDN          string\nparam certKey                 string\nparam certData                string\nparam appGatewayCertType      string\nparam deploymentIdentityName  string\nparam deploymentSubnetId      string\nparam deploymentStorageName   string\n\nvar secretName = replace(appGatewayFQDN,'.', '-')\nvar subjectName='CN=${appGatewayFQDN}'\n\nvar certPwd = appGatewayCertType == 'selfsigned' ? 'null' : certKey\nvar certDataString = appGatewayCertType == 'selfsigned' ? 'null' : certData\n\nresource deploymentIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' existing = {\n  name: deploymentIdentityName\n}\n\nresource accessPolicyGrantForCertificate 'Microsoft.KeyVault/vaults/accessPolicies@2019-09-01' = {\n  name: '${keyVaultName}/add'\n  properties: {\n    accessPolicies: [\n      {\n        objectId: managedIdentity.properties.principalId\n        tenantId: managedIdentity.properties.tenantId\n        permissions: {\n          secrets: [ \n            'get' \n            'list'\n          ]\n          certificates: [\n            'import'\n            'get'\n            'list'\n            'update'\n            'create'\n          ]\n        }                  \n      }\n      {\n        objectId: deploymentIdentity.properties.principalId\n        tenantId: deploymentIdentity.properties.tenantId\n        permissions: {\n          secrets: [ \n            'get' \n            'list'\n          ]\n          certificates: [\n            'import'\n            'get'\n            'list'\n            'update'\n            'create'\n          ]\n        }                  \n      }\n    ]\n  }\n}\n\nresource appGatewayCertificate 'Microsoft.Resources/deploymentScripts@2023-08-01' = {\n  name: '${secretName}-certificate'\n  dependsOn: [\n    accessPolicyGrantForCertificate\n  ]\n  location: location \n  identity: {\n    type: 'userAssigned'\n    userAssignedIdentities: {\n      '${deploymentIdentity.id}': {}\n    }\n  }\n  kind: 'AzurePowerShell'\n  properties: {\n    azPowerShellVersion: '6.6'\n    storageAccountSettings: {\n      storageAccountName: deploymentStorageName\n    }\n    containerSettings: {\n      subnetIds: [\n        {\n          id: deploymentSubnetId\n        }\n      ]\n    }     \n    arguments: ' -vaultName ${keyVaultName} -certificateName ${secretName} -subjectName ${subjectName} -certPwd ${certPwd} -certDataString ${certDataString} -certType ${appGatewayCertType}'\n    scriptContent: '''\n      param(\n      [string] [Parameter(Mandatory=$true)] $vaultName,\n      [string] [Parameter(Mandatory=$true)] $certificateName,\n      [string] [Parameter(Mandatory=$true)] $subjectName,\n      [string] [Parameter(Mandatory=$true)] $certPwd,\n      [string] [Parameter(Mandatory=$true)] $certDataString,\n      [string] [Parameter(Mandatory=$true)] $certType\n      )\n\n      $ErrorActionPreference = 'Stop'\n      $DeploymentScriptOutputs = @{}\n      if ($certType -eq 'selfsigned') {\n        $policy = New-AzKeyVaultCertificatePolicy -SubjectName $subjectName -IssuerName Self -ValidityInMonths 12 -Verbose\n        \n        # private key is added as a secret that can be retrieved in the ARM template\n        Add-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName -CertificatePolicy $policy -Verbose\n        \n        $newCert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName\n\n        # it takes a few seconds for KeyVault to finish\n        $tries = 0\n        do {\n          Write-Host 'Waiting for certificate creation completion...'\n          Start-Sleep -Seconds 10\n          $operation = Get-AzKeyVaultCertificateOperation -VaultName $vaultName -Name $certificateName\n          $tries++\n\n          if ($operation.Status -eq 'failed')\n          {\n          throw 'Creating certificate $certificateName in vault $vaultName failed with error $($operation.ErrorMessage)'\n          }\n\n          if ($tries -gt 120)\n          {\n          throw 'Timed out waiting for creation of certificate $certificateName in vault $vaultName'\n          }\n        } while ($operation.Status -ne 'completed')\t\t\n      }\n      else {\n        $ss = Convertto-SecureString -String $certPwd -AsPlainText -Force; \n        Import-AzKeyVaultCertificate -Name $certificateName -VaultName $vaultName -CertificateString $certDataString -Password $ss\n      }\n      '''\n    retentionInterval: 'P1D'\n  }\n}\n\nmodule appGatewaySecretsUri 'certificateSecret.bicep' = {\n  name: '${secretName}-certificate'\n  dependsOn: [\n    appGatewayCertificate\n  ]\n  params: {\n    keyVaultName: keyVaultName\n    secretName: secretName\n  }\n}\n\noutput secretUri string = appGatewaySecretsUri.outputs.secretUri\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/gateway/modules/certificateSecret.bicep",
    "content": "param keyVaultName string\nparam secretName   string\n\nresource keyVaultCertificate 'Microsoft.KeyVault/vaults/secrets@2021-06-01-preview' existing = {\n  name: '${keyVaultName}/${secretName}'\n}\n\noutput secretUri string = keyVaultCertificate.properties.secretUriWithVersion\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/main.bicep",
    "content": "targetScope = 'subscription'\n\n// Parameters\n@description('A short name for the workload being deployed alphanumberic only')\n@maxLength(8)\nparam workloadName string\n\n@description('The environment for which the deployment is being executed')\n@allowed([\n  'dev'\n  'uat'\n  'prod'\n  'dr'\n])\nparam environment string\n\nparam identifier string\n\n@description('The FQDN for the Application Gateway. Example - api.contoso.com.')\nparam appGatewayFqdn string\n\n@description('The password for the TLS certificate for the Application Gateway.  The pfx file needs to be copied to scenarios/apim-baseline/bicep/gateway/certs/appgw.pfx')\nparam certKey string = 'placeholder'\nparam certData string = 'placeholder'\n\n@description('Set to selfsigned if self signed certificates should be used for the Application Gateway. Set to custom and copy the pfx file to scenarios/apim-baseline/bicep/gateway/certs/appgw.pfx if custom certificates are to be used')\nparam appGatewayCertType string\n\nparam location string = deployment().location\n\n@description('Enable sending usage and telemetry feedback to Microsoft.')\nparam enableTelemetry bool = true\nvar telemetryId = 'ab1e5729-7452-41b2-9fbb-945cc51d9cd0-${location}-apimsb-main'\n\n// Variables\nvar resourceSuffix = '${workloadName}-${environment}-${location}-${identifier}'\nvar networkingResourceGroupName = 'rg-networking-${resourceSuffix}'\nvar sharedResourceGroupName = 'rg-shared-${resourceSuffix}'\nvar apimResourceGroupName = 'rg-apim-${resourceSuffix}'\n\n// Resource Names\nvar apimName = 'apim-${resourceSuffix}'\nvar appGatewayName = 'appgw-${resourceSuffix}'\n\nresource networkingRG 'Microsoft.Resources/resourceGroups@2021-04-01' = {\n  name: networkingResourceGroupName\n  location: location\n}\n\nresource sharedRG 'Microsoft.Resources/resourceGroups@2021-04-01' = {\n  name: sharedResourceGroupName\n  location: location\n}\n\nresource apimRG 'Microsoft.Resources/resourceGroups@2021-04-01' = {\n  name: apimResourceGroupName\n  location: location\n}\n\nmodule networking './networking/networking.bicep' = {\n  name: 'networkingresources'\n  scope: resourceGroup(networkingRG.name)\n  params: {\n    location: location\n    resourceSuffix: resourceSuffix\n  }\n}\n\nmodule shared './shared/shared.bicep' = {\n  dependsOn: [\n    networking\n  ]\n  name: 'sharedresources'\n  scope: resourceGroup(sharedRG.name)\n  params: {\n    workloadName: workloadName\n    environment: environment\n    identifier: identifier\n    location: location\n    resourceGroupName: sharedRG.name\n    resourceSuffix: resourceSuffix\n    vnetName: networking.outputs.apimCSVNetName\n    privateEndpointSubnetid: networking.outputs.privateEndpointSubnetid\n    networkingResourceGroupName: networkingRG.name\n    deploymentSubnetId: networking.outputs.deploymentSubnetId\n  }\n}\n\nmodule apimModule 'apim/apim.bicep' = {\n  name: 'apimDeploy'\n  scope: resourceGroup(apimRG.name)\n  params: {\n    apimName: apimName\n    apimSubnetId: networking.outputs.apimSubnetid\n    location: location\n    appInsightsName: shared.outputs.appInsightsName\n    appInsightsId: shared.outputs.appInsightsId\n    appInsightsInstrumentationKey: shared.outputs.appInsightsInstrumentationKey\n    keyVaultName: shared.outputs.keyVaultName\n    keyVaultResourceGroupName: sharedRG.name\n    networkingResourceGroupName: networkingRG.name\n    apimRG: apimRG.name\n    vnetName: networking.outputs.apimCSVNetName\n  }\n}\n\nmodule appgwModule 'gateway/appgw.bicep' = {\n  name: 'appgwDeploy'\n  scope: resourceGroup(networkingRG.name)\n  dependsOn: [\n    apimModule\n  ]\n  params: {\n    appGatewayName: appGatewayName\n    appGatewayFQDN: appGatewayFqdn\n    location: location\n    appGatewaySubnetId: networking.outputs.appGatewaySubnetid\n    primaryBackendEndFQDN: '${apimName}.azure-api.net'\n    keyVaultName: shared.outputs.keyVaultName\n    keyVaultResourceGroupName: sharedRG.name\n    appGatewayCertType: appGatewayCertType\n    certKey: certKey\n    certData: certData\n    appGatewayPublicIpName: networking.outputs.appGatewayPublicIpName\n    deploymentIdentityName: shared.outputs.deploymentIdentityName\n    deploymentSubnetId: networking.outputs.deploymentSubnetId\n    deploymentStorageName: shared.outputs.deploymentStorageName\n  }\n}\n\n@description('Microsoft telemetry deployment.')\n#disable-next-line no-deployments-resources\nresource telemetrydeployment 'Microsoft.Resources/deployments@2021-04-01' = if (enableTelemetry) {\n  location: location\n  name: telemetryId\n  properties: {\n    mode: 'Incremental'\n    template: {\n      '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#'\n      contentVersion: '1.0.0.0'\n      resources: {}\n    }\n  }\n}\n\noutput resourceSuffix string = resourceSuffix\noutput networkingResourceGroupName string = networkingResourceGroupName\noutput sharedResourceGroupName string = sharedResourceGroupName\noutput apimResourceGroupName string = apimResourceGroupName\noutput apimName string = apimName\noutput apimIdentityName string = apimModule.outputs.apimIdentityName\noutput vnetId string = networking.outputs.apimCSVNetId\noutput vnetName string = networking.outputs.apimCSVNetName\noutput privateEndpointSubnetid string = networking.outputs.privateEndpointSubnetid\noutput deploymentIdentityName string = shared.outputs.deploymentIdentityName\noutput deploymentSubnetId string = networking.outputs.deploymentSubnetId\noutput deploymentStorageName string = shared.outputs.deploymentStorageName\noutput keyVaultName string = shared.outputs.keyVaultName\noutput appGatewayName string = appGatewayName\noutput appGatewayPublicIpAddress string = appgwModule.outputs.appGatewayPublicIpAddress\noutput apimStarterSubscriptionKey string = apimModule.outputs.apimStarterSubscriptionKey\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/networking/networking.bicep",
    "content": "param apimCSVNetNameAddressPrefix string = '10.2.0.0/16'\n\nparam appGatewayAddressPrefix string = '10.2.4.0/24'\nparam apimAddressPrefix string = '10.2.7.0/24'\nparam privateEndpointAddressPrefix string = '10.2.5.0/24'\nparam deploymentAddressPrefix string = '10.2.8.0/24'\n\nparam location string\n\n@description('Standardized suffix text to be added to resource names')\nparam resourceSuffix string\n\n// Variables\nvar owner = 'APIM Const Set'\n\nvar apimCSVNetName = 'vnet-apim-cs-${resourceSuffix}'\n\nvar appGatewaySubnetName = 'snet-apgw-${resourceSuffix}'\nvar apimSubnetName = 'snet-apim-${resourceSuffix}'\n\nvar appGatewaySNNSG = 'nsg-apgw-${resourceSuffix}'\nvar apimSNNSG = 'nsg-apim-${resourceSuffix}'\n\nvar privateEndpointSubnetName = 'snet-prep-${resourceSuffix}'\nvar privateEndpointSNNSG = 'nsg-prep-${resourceSuffix}'\n\nvar deploymentSubnetName = 'snet-deploy-${resourceSuffix}'\n\nvar appGatewayPublicIpName = 'pip-appgw-${resourceSuffix}'\n\n// Resources - VNet - SubNets\nresource vnetApimCs 'Microsoft.Network/virtualNetworks@2021-02-01' = {\n  name: apimCSVNetName\n  location: location\n  tags: {\n    Owner: owner\n  }\n  properties: {\n    addressSpace: {\n      addressPrefixes: [\n        apimCSVNetNameAddressPrefix\n      ]\n    }\n    enableVmProtection: false\n    enableDdosProtection: false\n    subnets: [\n      {\n        name: appGatewaySubnetName\n        properties: {\n          addressPrefix: appGatewayAddressPrefix\n          networkSecurityGroup: {\n            id: appGatewayNSG.id\n          }\n        }\n      }\n      {\n        name: apimSubnetName\n        properties: {\n          addressPrefix: apimAddressPrefix\n          networkSecurityGroup: {\n            id: apimNSG.id\n          }\n        }\n      }\n      {\n        name: privateEndpointSubnetName\n        properties: {\n          addressPrefix: privateEndpointAddressPrefix\n          networkSecurityGroup: {\n            id: privateEndpointNSG.id\n          }\n          privateEndpointNetworkPolicies: 'Disabled'\n        }\n      }\n      {\n        name: deploymentSubnetName\n        properties: {\n          addressPrefix: deploymentAddressPrefix\n          serviceEndpoints: [\n            {\n              service: 'Microsoft.Storage'\n            }\n          ]\n          delegations: [\n            {\n              name: 'Microsoft.ContainerInstance.containerGroups'\n              properties: {\n                serviceName: 'Microsoft.ContainerInstance/containerGroups'\n              }\n            }\n          ]\n        }\n      }\n    ]\n  }\n}\n\n// Network Security Groups (NSG)\n\nresource appGatewayNSG 'Microsoft.Network/networkSecurityGroups@2020-06-01' = {\n  name: appGatewaySNNSG\n  location: location\n  properties: {\n    securityRules: [\n      {\n        name: 'AllowHealthProbes'\n        properties: {\n          protocol: '*'\n          sourcePortRange: '*'\n          destinationPortRange: '65200-65535'\n          sourceAddressPrefix: 'GatewayManager'\n          destinationAddressPrefix: '*'\n          access: 'Allow'\n          priority: 100\n          direction: 'Inbound'\n        }\n      }\n      {\n        name: 'AllowClientTrafficToSubnet'\n        properties: {\n          protocol: 'Tcp'\n          sourcePortRange: '*'\n          destinationPortRanges: ['80', '443']\n          sourceAddressPrefix: '*'\n          destinationAddressPrefix: appGatewayAddressPrefix\n          access: 'Allow'\n          priority: 110\n          direction: 'Inbound'\n        }\n      }\n      {\n        name: 'AllowClientTrafficToFrontendIP'\n        properties: {\n          protocol: 'Tcp'\n          sourcePortRange: '*'\n          destinationPortRanges: ['80', '443']\n          sourceAddressPrefix: '*'\n          destinationAddressPrefix: '${pipAppGw.properties.ipAddress}/32'\n          access: 'Allow'\n          priority: 111\n          direction: 'Inbound'\n        }\n      }\n      {\n        name: 'AllowAzureLoadBalancer'\n        properties: {\n          protocol: '*'\n          sourcePortRange: '*'\n          destinationPortRange: '*'\n          sourceAddressPrefix: 'AzureLoadBalancer'\n          destinationAddressPrefix: '*'\n          access: 'Allow'\n          priority: 120\n          direction: 'Inbound'\n        }\n      }\n    ]\n  }\n}\n\nresource apimNSG 'Microsoft.Network/networkSecurityGroups@2020-06-01' = {\n  name: apimSNNSG\n  location: location\n  properties: {\n    securityRules: [\n      {\n        name: 'AllowApimManagement'\n        properties: {\n          priority: 2000\n          sourceAddressPrefix: 'ApiManagement'\n          protocol: 'Tcp'\n          destinationPortRange: '3443'\n          access: 'Allow'\n          direction: 'Inbound'\n          sourcePortRange: '*'\n          destinationAddressPrefix: 'VirtualNetwork'\n        }\n      }\n      {\n        name: 'AllowAzureLoadBalancer'\n        properties: {\n          priority: 2010\n          sourceAddressPrefix: 'AzureLoadBalancer'\n          protocol: 'Tcp'\n          destinationPortRange: '6390'\n          access: 'Allow'\n          direction: 'Inbound'\n          sourcePortRange: '*'\n          destinationAddressPrefix: 'VirtualNetwork'\n        }\n      }\n      {\n        name: 'AllowAzureTrafficManager'\n        properties: {\n          priority: 2020\n          sourceAddressPrefix: 'AzureTrafficManager'\n          protocol: 'Tcp'\n          destinationPortRange: '443'\n          access: 'Allow'\n          direction: 'Inbound'\n          sourcePortRange: '*'\n          destinationAddressPrefix: 'VirtualNetwork'\n        }\n      }\n      {\n        name: 'AllowStorage'\n        properties: {\n          priority: 2000\n          sourceAddressPrefix: 'VirtualNetwork'\n          protocol: 'Tcp'\n          destinationPortRange: '443'\n          access: 'Allow'\n          direction: 'Outbound'\n          sourcePortRange: '*'\n          destinationAddressPrefix: 'Storage'\n        }\n      }\n      {\n        name: 'AllowSql'\n        properties: {\n          priority: 2010\n          sourceAddressPrefix: 'VirtualNetwork'\n          protocol: 'Tcp'\n          destinationPortRange: '1433'\n          access: 'Allow'\n          direction: 'Outbound'\n          sourcePortRange: '*'\n          destinationAddressPrefix: 'SQL'\n        }\n      }\n      {\n        name: 'AllowKeyVault'\n        properties: {\n          priority: 2020\n          sourceAddressPrefix: 'VirtualNetwork'\n          protocol: 'Tcp'\n          destinationPortRange: '443'\n          access: 'Allow'\n          direction: 'Outbound'\n          sourcePortRange: '*'\n          destinationAddressPrefix: 'AzureKeyVault'\n        }\n      }\n      {\n        name: 'AllowMonitor'\n        properties: {\n          priority: 2030\n          sourceAddressPrefix: 'VirtualNetwork'\n          protocol: 'Tcp'\n          destinationPortRanges: ['1886', '443']\n          access: 'Allow'\n          direction: 'Outbound'\n          sourcePortRange: '*'\n          destinationAddressPrefix: 'AzureMonitor'\n        }\n      }\n    ]\n  }\n}\n\nresource privateEndpointNSG 'Microsoft.Network/networkSecurityGroups@2020-06-01' = {\n  name: privateEndpointSNNSG\n  location: location\n  properties: {\n    securityRules: []\n  }\n}\n\n// Public IP \nresource pipAppGw 'Microsoft.Network/publicIPAddresses@2023-04-01' = {\n  name: appGatewayPublicIpName\n  location: location\n  sku: {\n    name: 'Standard'\n  }\n  zones: ['1', '2', '3']\n  properties: {\n    publicIPAddressVersion: 'IPv4'\n    publicIPAllocationMethod: 'Static'\n  }\n}\n\n// Output section\noutput apimCSVNetName string = apimCSVNetName\noutput apimCSVNetId string = vnetApimCs.id\n\noutput appGatewaySubnetName string = appGatewaySubnetName\noutput apimSubnetName string = apimSubnetName\noutput privateEndpointSubnetName string = privateEndpointSubnetName\n\noutput appGatewaySubnetid string = '${vnetApimCs.id}/subnets/${appGatewaySubnetName}'\noutput apimSubnetid string = '${vnetApimCs.id}/subnets/${apimSubnetName}'\noutput privateEndpointSubnetid string = '${vnetApimCs.id}/subnets/${privateEndpointSubnetName}'\n\noutput deploymentSubnetId string = '${vnetApimCs.id}/subnets/${deploymentSubnetName}'\noutput deploymentSubnetName string = deploymentSubnetName\n\noutput publicIpAppGw string = pipAppGw.id\noutput appGatewayPublicIpName string = appGatewayPublicIpName\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/shared/modules/azmon.bicep",
    "content": "targetScope='resourceGroup'\n// Parameters\n@description('Azure location to which the resources are to be deployed')\nparam location string\n\n@description('Standardized suffix text to be added to resource names')\nparam resourceSuffix string\n\n// Variables\nvar appInsightsName = 'appi-${resourceSuffix}'\nvar logAnalyticsWorkspaceName = 'log-${resourceSuffix}'\n\n// Resources\nresource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {\n  name: logAnalyticsWorkspaceName\n  location: location\n  properties: any({\n    retentionInDays: 30\n    features: {\n      searchVersion: 1\n    }\n    sku: {\n      name: 'PerGB2018'\n    }\n  })\n}\n\n\nresource appInsights 'Microsoft.Insights/components@2020-02-02' = {\n  name: appInsightsName\n  location: location\n  kind: 'web'\n  properties: {\n    Application_Type: 'web'\n    WorkspaceResourceId: logAnalyticsWorkspace.id\n  }\n}\n\noutput appInsightsConnectionString string = appInsights.properties.ConnectionString\noutput appInsightsName string = appInsights.name\noutput appInsightsId string = appInsights.id\noutput appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/shared/modules/dnszone.bicep",
    "content": "param vnetName string\nparam networkingResourceGroupName string\nparam domain string\n\nresource vnet 'Microsoft.Network/virtualNetworks@2021-02-01' existing = {\n  name: vnetName\n  scope: resourceGroup(networkingResourceGroupName)\n}\n\nresource dnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {\n  name: domain\n  location: 'global'\n}\n\nresource vnetLinks 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {\n  name: vnetName\n  parent: dnsZone\n  location: 'global'\n  properties: {\n    virtualNetwork: {\n      id: vnet.id\n    }\n    registrationEnabled: false\n  }\n}\n\noutput dnsZoneName string = dnsZone.name\noutput dnsZoneId string = dnsZone.id\noutput vnetLinksLink string = vnetLinks.id\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/shared/modules/privatedeploy.bicep",
    "content": "param resourceSuffix string\nparam location string\nparam deploymentSubnetId string\n\nvar userAssignedIdentityName = 'mi-deploy-${resourceSuffix}'\n\nparam storageAccountName string = toLower(take(replace('stdep${resourceSuffix}', '-',''), 24))\n\nresource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {\n  name: storageAccountName\n  location: location\n  sku: {\n    name: 'Standard_LRS'\n  }\n  kind: 'StorageV2'\n  properties: {\n    networkAcls: {\n      bypass: 'AzureServices'\n      virtualNetworkRules: [\n        {\n          id: deploymentSubnetId\n          action: 'Allow'\n          state: 'Succeeded'\n        }\n      ]\n      defaultAction: 'Deny'\n    }\n  }\n}\n\nresource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {\n  name: userAssignedIdentityName\n  location: location\n}\n\nresource storageFileDataPrivilegedContributor 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {\n  name: '69566ab7-960f-475b-8e7c-b3118f30c6bd' // Storage File Data Privileged Contributor\n  scope: tenant()\n}\n\nresource roleAssignmentStorage 'Microsoft.Authorization/roleAssignments@2022-04-01' = {\n  scope: storageAccount\n\n  name: guid(storageFileDataPrivilegedContributor.id, userAssignedIdentity.id, storageAccount.id)\n  properties: {\n    principalId: userAssignedIdentity.properties.principalId\n    roleDefinitionId: storageFileDataPrivilegedContributor.id\n    principalType: 'ServicePrincipal'\n  }\n}\n\noutput deploymentIdentityName string = userAssignedIdentityName\noutput deploymentStorageName string = storageAccountName\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/shared/modules/privateendpoint.bicep",
    "content": "param privateEndpointName string\nparam groupId string\nparam location string\nparam vnetName string\nparam networkingResourceGroupName string\nparam subnetId string\nparam serviceResourceId string\nparam createDnsZone bool = true\nparam domain string\n\nresource privateEndpoint 'Microsoft.Network/privateEndpoints@2021-03-01' = {\n  name: privateEndpointName\n  location: location\n  properties: {\n    subnet: {\n      id: subnetId\n    }\n    privateLinkServiceConnections: [\n      {\n        name: privateEndpointName\n        properties: {\n          privateLinkServiceId: serviceResourceId\n          groupIds: [\n            groupId\n          ]\n        }\n      }\n    ]\n  }\n}\n\nmodule dnsZoneNew './dnszone.bicep' = if (createDnsZone == true) {\n  name: take('${replace(domain, '.', '-')}-deploy', 64)\n  params: {\n    vnetName: vnetName\n    networkingResourceGroupName: networkingResourceGroupName\n    domain: domain\n  }\n  dependsOn: [\n    privateEndpoint\n  ]\n}\n\nresource dnsZone 'Microsoft.Network/privateDnsZones@2018-09-01' existing = if (createDnsZone == false) {\n  name: domain \n}\n\nvar dnsZoneName = (createDnsZone == true) ? dnsZoneNew.outputs.dnsZoneName : dnsZone.name\nvar dnsZoneId = (createDnsZone == true) ? dnsZoneNew.outputs.dnsZoneId : dnsZone.id\n\nresource dnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2020-03-01' = {\n  name: 'default'\n  parent: privateEndpoint\n  properties: {\n    privateDnsZoneConfigs: [\n      {      \n        name: dnsZoneName\n        properties: {\n          privateDnsZoneId: dnsZoneId          \n        }\n      }\n    ]\n  }\n}\n\noutput privateEndpointId string = privateEndpoint.id\noutput dnsZoneGroupId string = dnsZoneGroup.id\n"
  },
  {
    "path": "scenarios/apim-baseline/bicep/shared/shared.bicep",
    "content": "targetScope='resourceGroup'\n// Parameters\n@description('A short name for the workload being deployed')\nparam workloadName string\n\n@description('The environment for which the deployment is being executed')\n@allowed([\n  'dev'\n  'uat'\n  'prod'\n  'dr'\n])\nparam environment string\nparam identifier string\n\n@description('Azure location to which the resources are to be deployed')\nparam location string\n\nparam vnetName string\nparam privateEndpointSubnetid string\nparam deploymentSubnetId string\nparam networkingResourceGroupName string\n\n@description('The name of the shared resource group')\nparam resourceGroupName string\n\n@description('Standardized suffix text to be added to resource names')\nparam resourceSuffix string\n\n// Variables - ensure key vault name does not end with '-'\nvar tempKeyVaultName = take('kv-${workloadName}-${environment}-${location}', 20) // Must be between 3-24 alphanumeric characters \nvar uniqueKeyVaultName = take('${tempKeyVaultName}-${identifier}', 24)\nvar keyVaultName = endsWith(uniqueKeyVaultName, '-') ? substring(uniqueKeyVaultName, 0, length(uniqueKeyVaultName) - 1) : uniqueKeyVaultName\nvar privateEndpoint_keyvault_Name = 'pep-kv-${resourceSuffix}'\n\n// Resources\nmodule appInsights './modules/azmon.bicep' = {\n  name: 'azmon'\n  scope: resourceGroup(resourceGroupName)\n  params: {\n    location: location\n    resourceSuffix: resourceSuffix\n  }\n}\n\nresource key_vault 'Microsoft.KeyVault/vaults@2023-07-01' = {\n  name: keyVaultName\n  location: location\n  properties: {\n    tenantId: subscription().tenantId\n    sku: {\n      family: 'A'\n      name: 'standard'\n    }    \n    publicNetworkAccess: 'Disabled'\n    networkAcls: {\n      bypass: 'AzureServices'\n      defaultAction: 'Deny'\n      ipRules: []\n      virtualNetworkRules: []\n    }\n    accessPolicies: [\n    ]\n  }\n}\n\nmodule keyvaultPrivateEndpoint './modules/privateendpoint.bicep' = {\n  name: privateEndpoint_keyvault_Name\n  scope: resourceGroup(networkingResourceGroupName)\n  params: {\n    location: location\n    privateEndpointName: privateEndpoint_keyvault_Name\n    groupId: 'vault'\n    serviceResourceId: key_vault.id\n    vnetName: vnetName\n    networkingResourceGroupName: networkingResourceGroupName\n    subnetId: privateEndpointSubnetid\n    domain:'privatelink.vaultcore.azure.net'\n  }\n}\n\nmodule deploy './modules/privatedeploy.bicep' = {\n  name: 'deploymenEssentials'\n  params: {\n    location: location\n    resourceSuffix: resourceSuffix\n    deploymentSubnetId: deploymentSubnetId\n  }\n}\n\n// Outputs\noutput appInsightsConnectionString string = appInsights.outputs.appInsightsConnectionString\noutput appInsightsName string=appInsights.outputs.appInsightsName\noutput appInsightsId string=appInsights.outputs.appInsightsId\noutput appInsightsInstrumentationKey string=appInsights.outputs.appInsightsInstrumentationKey\noutput keyVaultName string = key_vault.name\noutput deploymentIdentityName string = deploy.outputs.deploymentIdentityName\noutput deploymentStorageName string = deploy.outputs.deploymentStorageName\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/README.md",
    "content": "# Azure API Management - Secure Baseline [Terraform]\n- Single Region Deployment\n- Optional Multi-Region High Availability\n- Optional Zone Redundancy\n\nThis is the Terraform-based deployment guide for [Scenario 1: Azure API Management - Secure Baseline](../README.md).\n\n## Sections\n- [Prerequisites](#prerequisites)\n- [Quick Start](#quick-start)\n- [Deployment Customization](#deployment-customization)\n- [Deployment Steps](#deployment-steps)\n- [Using a Terraform AzureRM backend](#using-a-terraform-azurerm-backend)\n- [Troubleshooting](#troubleshooting)\n\n\n\n## Prerequisites\n\nThis is the starting point for the instructions on deploying this reference implementation. There is the required access and tooling you'll need in order to accomplish this.\n\n- An Azure subscription\n- The following resource providers [registered](https://learn.microsoft.com/azure/azure-resource-manager/management/resource-providers-and-types#register-resource-provider):\n  - `Microsoft.ApiManagement`\n  - `Microsoft.Network`\n  - `Microsoft.KeyVault`\n- The user or service principal initiating the deployment process must either the [owner role](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner) or the permissions below for a least privilege setup:\n\n    | Role  | Level | Why |\n    | :---- | :---------- | :------ |\n    | Contributor | Subscription | The plan needs the ability to create resource groups |\n    | User Access Administrator | Subscription | The plan delegate access to Azure Managed Identities created by the deployment. The UAA role can be scoped to just \"Storage File Data Privileged Contributor\" for security hardening.  | \n\n\n\n- Access to Bash command line to run the deployment script.\n- Latest [Azure CLI installed](https://learn.microsoft.com/cli/azure/install-azure-cli?view=azure-cli-latest) (must be at least 2.40), or you can perform this from Azure Cloud Shell by clicking below.\n\n  [![Launch Azure Cloud Shell](https://learn.microsoft.com/azure/includes/media/cloud-shell-try-it/launchcloudshell.png)](https://shell.azure.com)\n- JQ command line JSON processor installed\n\n   ```bash\n   sudo apt-get install jq\n   ```\n- Terraform installed. You can download the latest version from the [Terraform website](https://www.terraform.io/downloads.html). However, if using the dev container, this will not need to be downloaded and installed separately. \n\n## Quick Start\nThis will yield a working deployment for testing/poc, with no customizations.\n ```bash\n\n   # Login to Azure\n   az login\n\n   # Clone the repo\n   git clone https://github.com/Azure/apim-landing-zone-accelerator.git    \n\n   # Change into the scripts/terraform directory\n   cd apim-landing-zone-accelerator/scenarios/scripts/terraform\n   \n   # Use the provided sample environment file\n   cp sample.env .env  to create one and edit as needed\n\n   # Deploy the APIM baseline\n   ./deploy-apim-baseline.sh\n\n   # Follow on screen prompts\n   ```\n\n\n## Deployment Customization\n\nReview and update deployment parameters.\n\n   The **.env** parameter file is where you can customize your deployment. The defaults are a suitable starting point, but feel free to adjust any to fit your requirements.\n\n   Copy the [`sample.env`](../../scripts/terraform/sample.env) into a new file called `.env` in the same directory.\n\n   ```bash\n   cp sample.env .env\n   ```\n\n   **Deployment parameters**\n   \n   | Name  | Description | Default | Example(s) |\n   | :---- | :---------- | :------ | :--------- |\n   | `AZURE_LOCATION` | The Azure location to deploy to. | **eastus2** | **eastus2** |\n   | `MULT_REGION`| Should this deployment extend to a secondary location? |  **false**          | **true** |\n   | `AZURE_LOCATION2`| The Azure secondary location to deploy to? |  **centralus**          | **centralus** |\n   | `ZONE_REDUNDANT` | Should the deployment be zone redundant. | **false** | **true** |\n   | `RESOURCE_NAME_PREFIX` | A suffix for naming. | **apimdemo** | **appname** |\n   | `ENVIRONMENT_TAG` | A tag that will be included in the naming. | **dev** | **stage** |\n   | `APPGATEWAY_FQDN` | The Azure location to deploy to. | **apim.example.com** | **my.org.com** |\n   | `CERT_TYPE` | selfsigned will create a self-signed certificate for the APPGATEWAY_FQDN. custom will use an existing certificate in pfx format that needs to be available in the [certs](../../certs) folder and named appgw.pfx | **selfsigned** | **custom** |\n   | `CERT_PWD` | The password for the pfx certificate. Only required if CERT_TYPE is custom. | **N/A** | **password123** |\n   | `RANDOM_IDENTIFIER` | Optional 3 character random string to ensure deployments are unique. Automatically assigned if not provided | **abc** | **pqr** |\n\n   ### examples `.env` file\n   - Single region, Single Zone deployment with Developer SKU\n   ```bash\n      AZURE_LOCATION='eastus2'\n      RESOURCE_NAME_PREFIX='lzv01'\n      ENVIRONMENT_TAG='dev'\n      APPGATEWAY_FQDN='apim.example.com'\n      CERT_TYPE='selfsigned'\n      ZONE_REDUNDANT='false'\n      MULTI_REGION='false'\n      AZURE_LOCATION2=''\n   ```\n   - Single region and Zone redundant deployment with Premium SKU\n   ```bash\n      AZURE_LOCATION='eastus2'\n      RESOURCE_NAME_PREFIX='lzv01'\n      ENVIRONMENT_TAG='dev'\n      APPGATEWAY_FQDN='apim.example.com'\n      CERT_TYPE='selfsigned'\n      ZONE_REDUNDANT='true'\n      MULTI_REGION='false'\n   ```\n   - Multi-region and Zone Redundant deployment with Premium SKU\n   ```bash\n      AZURE_LOCATION='eastus2'\n      RESOURCE_NAME_PREFIX='lzv01'\n      ENVIRONMENT_TAG='dev'\n      APPGATEWAY_FQDN='apim.example.com'\n      CERT_TYPE='selfsigned'\n      ZONE_REDUNDANT='true'\n      MULTI_REGION='true'\n      AZURE_LOCATION2='centralus'\n   ```\n\n\n## Deployment Steps\n\n1. Clone/download this repo locally, or even better fork this repository.\n\n   ```bash\n   git clone https://github.com/Azure/apim-landing-zone-accelerator.git --branch wip/apim-lza    \n\n   cd apim-landing-zone-accelerator/scenarios/scripts/terraform\n   ```\n\n2. Log into Azure from the AZ CLI and select your subscription.\n\n   ```bash\n   az login\n   ```\n\n\n\n3. Deploy the reference implementation.\n\n   Run the following command to deploy the APIM baseline\n\n    ```bash\n\n     # Note, you haven't created an .env file as explained above\n     # use  cp sample.env .env  to create one and edit as needed\n\n    ./deploy-apim-baseline.sh\n    ```\nNotes:\n- During script execution, you will encounter prompts and will need to respond with a 'y' to continue.\n- If error: \"Error: .env file not found in the current directory.\", please refer to the [Deployment Configuration](#deployment-configuration) section to create the .env file.\nTest the echo api using the generated command from the output.\n\n## Using a Terraform AzureRM backend\n\nFor terraform, you have the option to setup a backend [tf backend](https://developer.hashicorp.com/terraform/language/settings/backends/configuration). As part of the repository we provide a `azure-backend-sample.sh` script. This script will create a storage account and a container to store the terraform state. You can run the script with the following command:\n\n    ```bash\n    ./azure-backend-sample.sh \\\n         --resource-group my-resource-group \\\n         --storage-account mystorageaccount \\\n         --container my-container \n    ```\n\nAn `${ENVIRONMENT_TAG}-backend.hcl` file will be created automatically in the same directory as your `.env`. The file looks like this:\n\n   ```hcl\n   resource_group_name  = \"my-resource-group\"\n   storage_account_name = \"mystorageaccount\"\n   container_name       = \"my-container\"\n   ```\n\n   Note: When using an AZURERM Backend and if your deployment is using a service principal vs a user account to login, make sure to also follow the terraform guidance here:\n   https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/service_principal_client_secret#configuring-the-service-principal-in-terraform\n\n## Troubleshooting\n\nIf you see the message `-bash: ./deploy-apim-baseline.sh: /bin/bash^M: bad interpreter: No such file or directory` when running the script, you can fix this by running the following command:\n\n   ```bash\n    sed -i -e 's/\\r$//' deploy-apim-baseline.sh\n   ```\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/backend.tf.sample",
    "content": "#   terraform {\n#     backend \"azurerm\" {\n#     # ----------------------\n#     # Will be passing in these arguments via CLI as the state file \\\n#     #  is now being overwritten via local testing environments\n#     # > https://developer.hashicorp.com/terraform/language/settings/backends/configuration#command-line-key-value-pairs\n#     # ----------------------\n#     # e.g: terraform init \\\n#     #        -backend-config=\"resource_group_name=rg-tfstate-auseast\"     \\\n#     #        -backend-config=\"storage_account_name=tfstateauseaststorage\" \\\n#     #        -backend-config=\"container_name=apimlza\"       \\\n#     #        -backend-config=\"key=terraform-apimlza-dev-v2.tfstate\"\n#     # ----------------------\n#     # resource_group_name = \"rg-tfstate-auseast\"\n#     # storage_account_name = \"tfstateauseaststorage\"\n#     # container_name       = \"apimlza\"\n#     # key                  = \"terraform-apimlza-dev-v6.tfstate\"\n#   }\n# }"
  },
  {
    "path": "scenarios/apim-baseline/terraform/main.tf",
    "content": "# This module deploys an Azure API Management (APIM) service in a single region.\nmodule \"apim_baseline_single_region\" {\n\n    count = var.multiRegionEnabled ? 0 : 1    \n\n    source = \"./single-region\"\n\n    location                    = var.location\n    workloadName                = var.workloadName\n    appGatewayFqdn              = var.appGatewayFqdn\n    appGatewayCertType          = var.appGatewayCertType\n    environment                 = var.environment\n    apimCSVNetNameAddressPrefix = var.apimCSVNetNameAddressPrefix\n    appGatewayAddressPrefix     = var.appGatewayAddressPrefix\n    apimAddressPrefix           = var.apimAddressPrefix\n    privateEndpointAddressPrefix = var.privateEndpointAddressPrefix\n    deploymentAddressPrefix     = var.deploymentAddressPrefix\n    additionalClientIds          = var.additionalClientIds \n    certificatePassword        = var.certificatePassword\n    certificatePath            = var.certificatePath\n    identifier                 = var.identifier\n    zoneRedundantEnabled       = var.zoneRedundantEnabled\n\n}\n\n\nmodule \"apim_baseline_multi_region\" {\n\n    count = var.multiRegionEnabled ? 1 : 0  \n\n    source = \"./multi-region\"\n\n    location                    = var.location\n    workloadName                = var.workloadName\n    appGatewayFqdn              = var.appGatewayFqdn\n    appGatewayCertType          = var.appGatewayCertType\n    environment                 = var.environment\n    apimCSVNetNameAddressPrefix = var.apimCSVNetNameAddressPrefix\n    appGatewayAddressPrefix     = var.appGatewayAddressPrefix\n    apimAddressPrefix           = var.apimAddressPrefix\n    privateEndpointAddressPrefix = var.privateEndpointAddressPrefix\n    deploymentAddressPrefix     = var.deploymentAddressPrefix\n    additionalClientIds          = var.additionalClientIds\n    certificatePassword        = var.certificatePassword\n    certificatePath            = var.certificatePath\n    identifier                 = var.identifier\n    apimCSVNetNameSecondAddressPrefix = var.apimCSVNetNameSecondAddressPrefix\n    appGatewaySecondAddressPrefix = var.appGatewaySecondAddressPrefix\n    apimSecondAddressPrefix = var.apimSecondAddressPrefix\n    privateEndpointSecondAddressPrefix = var.privateEndpointSecondAddressPrefix\n    deploymentSecondAddressPrefix = var.deploymentSecondAddressPrefix\n    locationSecond = var.locationSecond\n    zoneRedundantEnabled = var.zoneRedundantEnabled\n}\n\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/apim/apim.tf",
    "content": "locals {\n  apimName          = \"apim-${var.resourceSuffix}\"\n  apimPipPrimaryPip = \"pip-apim-${var.resourceSuffix}\"\n  apimIdentityName  = \"identity-${local.apimName}\"\n  skuCount          = var.zoneRedundantEnabled ? 3 : 1\n  skuNameAuto       = var.zoneRedundantEnabled ? \"Premium_${local.skuCount}\" : var.skuName\n  zones             = var.zoneRedundantEnabled ? [\"1\", \"2\", \"3\"] : null\n}\n\nresource \"azurerm_user_assigned_identity\" \"apimIdentity\" {\n  name                = local.apimIdentityName\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n}\n\ndata \"azurerm_key_vault\" \"keyVault\" {\n  name                = var.keyVaultName\n  resource_group_name = var.sharedResourceGroupName\n}\n\n#-------------------------------\n# Creation of an internal APIM instance\n#-------------------------------\nresource \"azurerm_api_management\" \"apim_internal\" {\n  name                 = local.apimName\n  location             = var.location\n  resource_group_name  = var.resourceGroupName\n  publisher_name       = var.publisherName\n  publisher_email      = var.publisherEmail\n  virtual_network_type = \"Internal\"\n\n  sku_name = local.skuNameAuto\n\n  zones = local.zones\n\n  min_api_version = \"2019-12-01\"\n\n  virtual_network_configuration {\n    subnet_id = var.apimSubnetId\n  }\n\n  identity {\n    type         = \"UserAssigned\"\n    identity_ids = [\"${azurerm_user_assigned_identity.apimIdentity.id}\"]\n  }\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\n\n#-------------------------------\n# Creation of the apim logger entity\n#-------------------------------\nresource \"azurerm_api_management_logger\" \"apim_logger\" {\n  name                = \"apim-logger\"\n  api_management_name = azurerm_api_management.apim_internal.name\n  resource_group_name = var.resourceGroupName\n  resource_id         = var.workspaceId\n\n  application_insights {\n    instrumentation_key = var.instrumentationKey\n  }\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\n#-------------------------------\n# API management service diagnostic\n#-------------------------------\nresource \"azurerm_api_management_diagnostic\" \"apim_diagnostic\" {\n  identifier               = \"applicationinsights\"\n  resource_group_name      = var.resourceGroupName\n  api_management_name      = azurerm_api_management.apim_internal.name\n  api_management_logger_id = azurerm_api_management_logger.apim_logger.id\n\n  sampling_percentage = 100.0\n  always_log_errors   = true\n  verbosity           = \"verbose\" #possible value are verbose, error, information\n\n\n  frontend_request {\n    body_bytes = 32\n    headers_to_log = [\n      \"content-type\",\n      \"accept\",\n      \"origin\",\n    ]\n  }\n\n  frontend_response {\n    body_bytes = 32\n    headers_to_log = [\n      \"content-type\",\n      \"content-length\",\n      \"origin\",\n    ]\n  }\n\n  backend_request {\n    body_bytes = 32\n    headers_to_log = [\n      \"content-type\",\n      \"accept\",\n      \"origin\",\n    ]\n  }\n\n  backend_response {\n    body_bytes = 32\n    headers_to_log = [\n      \"content-type\",\n      \"content-length\",\n      \"origin\",\n    ]\n  }\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_api_management_product\" \"starter\" {\n  display_name        = \"Starter\"\n  product_id          = \"starter\"\n  api_management_name = azurerm_api_management.apim_internal.name\n  resource_group_name = azurerm_api_management.apim_internal.resource_group_name\n  published           = true\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"random_uuid\" \"starter_key\" {\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_api_management_subscription\" \"echo\" {\n  api_management_name = azurerm_api_management.apim_internal.name\n  resource_group_name = azurerm_api_management.apim_internal.resource_group_name\n  product_id          = azurerm_api_management_product.starter.id\n  display_name        = \"Echo API\"\n  primary_key         = random_uuid.starter_key.result\n  allow_tracing       = false\n  state               = \"active\"\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\n#-------------------------------\n# Importing the Echo API into API Management\n#-------------------------------\nresource \"azurerm_api_management_api\" \"echo_api\" {\n  name                = \"echo-api\"\n  api_management_name = azurerm_api_management.apim_internal.name\n  resource_group_name = azurerm_api_management.apim_internal.resource_group_name\n  revision            = \"1\"\n  display_name        = \"Echo API\"\n  path                = \"echo\"\n  protocols           = [\"https\"]\n  service_url         = \"https://httpbin.io/anything\"\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_api_management_api_operation\" \"echo_api_operation\" {\n  api_name            = azurerm_api_management_api.echo_api.name\n  api_management_name = azurerm_api_management.apim_internal.name\n  resource_group_name = azurerm_api_management.apim_internal.resource_group_name\n  display_name        = \"Retrieve resource\"\n  method              = \"GET\"\n  url_template        = \"/resource\"\n\n  request {\n    query_parameter {\n      type          = \"string\"\n      name          = \"param1\"\n      default_value = \"sample\"\n      required      = true\n    }\n    query_parameter {\n      type     = \"number\"\n      name     = \"param2\"\n      required = false\n    }\n  }\n\n  response {\n    status_code = 200\n    description = \"A demonstration of a GET call on a sample resource. It is handled by an \\\"echo\\\" backend which returns a response equal to the request (the supplied headers and body are being returned as received).\"\n  }\n  operation_id = \"retrieve-resource\"\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n\n}\n\nresource \"azurerm_api_management_product_api\" \"echo\" {\n  api_name            = azurerm_api_management_api.echo_api.name\n  product_id          = azurerm_api_management_product.starter.product_id\n  api_management_name = azurerm_api_management.apim_internal.name\n  resource_group_name = azurerm_api_management.apim_internal.resource_group_name\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\n\nresource \"azurerm_key_vault_access_policy\" \"apim_access_policy\" {\n  key_vault_id = data.azurerm_key_vault.keyVault.id\n  tenant_id    = azurerm_user_assigned_identity.apimIdentity.tenant_id\n  object_id    = azurerm_user_assigned_identity.apimIdentity.principal_id\n\n  secret_permissions = [\n    \"Get\",\n    \"List\"\n  ]\n\n  certificate_permissions = [\n    \"Get\",\n    \"List\"\n  ]\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/apim/outputs.tf",
    "content": "output \"primaryBackendendFqdn\" {\n  value = azurerm_api_management.apim_internal.gateway_url\n}\n\noutput \"bakendUrl\" {\n  value = \"${azurerm_api_management.apim_internal.name}.azure-api.net\"\n}\n\noutput \"subscriptionKey\" {\n  value = random_uuid.starter_key.result\n}\n\noutput \"apimPrivateIp\" {\n  value = azurerm_api_management.apim_internal.private_ip_addresses[0]\n}\n\noutput \"apimName\" {\n  value = azurerm_api_management.apim_internal.name\n}\n\noutput \"apimIdentityName\" {\n  value = azurerm_user_assigned_identity.apimIdentity.name\n}"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/apim/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus\"\n}\n\nvariable \"resourceSuffix\" {\n  type        = string\n  description = \"A suffix for naming\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"resourceGroupName\" {\n  type        = string\n  description = \"The name of the resource group\"\n}\n\n#-------------------------------\n# APIM specific variables\n#-------------------------------\n\nvariable \"keyVaultName\" {\n  description = \"The name of the Key Vault\"\n  type        = string\n}\n\nvariable \"publisherName\" {\n  description = \"The name of the publisher/company\"\n  type        = string\n  default     = \"Contoso\"\n}\n\nvariable \"publisherEmail\" {\n  description = \"The email of the publisher/company; shows as administrator email in APIM\"\n  type        = string\n  default     = \"apim@contoso.com\"\n}\n\nvariable \"skuName\" {\n  description = \"String consisting of two parts separated by an underscore(_). The first part is the name, valid values include: Consumption, Developer, Basic, Standard and Premium. The second part is the capacity (e.g. the number of deployed units of the sku), which must be a positive integer (e.g. Developer_1)\"\n  type        = string\n  default     = \"Developer_1\"\n}\n\nvariable \"apimSubnetId\" {\n  description = \"The subnet id of the apim instance\"\n  type        = string\n}\n\nvariable \"workspaceId\" {\n  type        = string\n  description = \"The workspace id of the log analytics workspace\"\n}\n\nvariable \"instrumentationKey\" {\n  type        = string\n  description = \"App insights instrumentation key\"\n}\n\nvariable \"sharedResourceGroupName\" {\n  type        = string\n  description = \"The name of the shared resource group\"\n}\n\nvariable \"zoneRedundantEnabled\" {\n  description = \"Boolean to indicate if the deployment is zone redundant\"\n  type        = bool\n  default     = false\n}\n\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/dns/dnszone.tf",
    "content": "/* Creates a Private DNS ZOne, A Records and Vnet Link for each of the below endpoints\nAPI Gateway\t                contosointernalvnet.azure-api.net\nDeveloper portal\t        contosointernalvnet.portal.azure-api.net\nThe new developer portal\tcontosointernalvnet.developer.azure-api.net\nDirect management endpoint\tcontosointernalvnet.management.azure-api.net\nGit\t                        contosointernalvnet.scm.azure-api.net */\n\n#-------------------------------\n# DNS zones \n#-------------------------------\nresource \"azurerm_private_dns_zone\" \"gateway\" {\n  name                = \"azure-api.net\"\n  resource_group_name = var.resourceGroupName\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_zone\" \"dev_portal\" {\n  name                = \"portal.azure-api.net\"\n  resource_group_name = var.resourceGroupName\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_zone\" \"new_dev_portal\" {\n  name                = \"developer.azure-api.net\"\n  resource_group_name = var.resourceGroupName\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_zone\" \"mgmt_portal\" {\n  name                = \"management.azure-api.net\"\n  resource_group_name = var.resourceGroupName\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_zone\" \"scm\" {\n  name                = \"scm.azure-api.net\"\n  resource_group_name = var.resourceGroupName\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\n#-------------------------------\n# A records for the DNS zones\n#-------------------------------\nresource \"azurerm_private_dns_a_record\" \"gateway_record\" {\n  name                = lower(var.apimName)\n  zone_name           = azurerm_private_dns_zone.gateway.name\n  resource_group_name = var.resourceGroupName\n  ttl                 = 36000\n  records             = [var.apimPrivateIp]\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_a_record\" \"dev_portal_record\" {\n  name                = \"portal\"\n  zone_name           = azurerm_private_dns_zone.dev_portal.name\n  resource_group_name = var.resourceGroupName\n  ttl                 = 300\n  records             = [var.apimPrivateIp]\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_a_record\" \"new_dev_portal_record\" {\n  name                = \"developer\"\n  zone_name           = azurerm_private_dns_zone.new_dev_portal.name\n  resource_group_name = var.resourceGroupName\n  ttl                 = 300\n  records             = [var.apimPrivateIp]\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_a_record\" \"mgmt_portal_record\" {\n  name                = \"management\"\n  zone_name           = azurerm_private_dns_zone.mgmt_portal.name\n  resource_group_name = var.resourceGroupName\n  ttl                 = 300\n  records             = [var.apimPrivateIp]\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_a_record\" \"scm_record\" {\n  name                = \"scm\"\n  zone_name           = azurerm_private_dns_zone.scm.name\n  resource_group_name = var.resourceGroupName\n  ttl                 = 300\n  records             = [var.apimPrivateIp]\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\n#-------------------------------\n# Vnet links\n#-------------------------------\nresource \"azurerm_private_dns_zone_virtual_network_link\" \"gateway_vnetlink\" {\n  name                  = \"gateway-vnet-link\"\n  resource_group_name   = var.resourceGroupName\n  private_dns_zone_name = azurerm_private_dns_zone.gateway.name\n  virtual_network_id    = var.apimVnetId\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_zone_virtual_network_link\" \"dev_portal_vnetlink\" {\n  name                  = \"portal-vnet-link\"\n  resource_group_name   = var.resourceGroupName\n  private_dns_zone_name = azurerm_private_dns_zone.dev_portal.name\n  virtual_network_id    = var.apimVnetId\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_zone_virtual_network_link\" \"new_dev_portal_vnetlink\" {\n  name                  = \"dev-portal-vnet-link\"\n  resource_group_name   = var.resourceGroupName\n  private_dns_zone_name = azurerm_private_dns_zone.new_dev_portal.name\n  virtual_network_id    = var.apimVnetId\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_zone_virtual_network_link\" \"mgmt_vnetlink\" {\n  name                  = \"mgmt-vnet-link\"\n  resource_group_name   = var.resourceGroupName\n  private_dns_zone_name = azurerm_private_dns_zone.mgmt_portal.name\n  virtual_network_id    = var.apimVnetId\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_zone_virtual_network_link\" \"scm_vnetlink\" {\n  name                  = \"scm-vnet-link\"\n  resource_group_name   = var.resourceGroupName\n  private_dns_zone_name = azurerm_private_dns_zone.scm.name\n  virtual_network_id    = var.apimVnetId\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/dns/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus\"\n}\n\nvariable \"resourceSuffix\" {\n  type        = string\n  description = \"A suffix for naming\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"resourceGroupName\" {\n  type        = string\n  description = \"The name of the resource group\"\n}\n\nvariable \"apimName\" {\n  type        = string\n  description = \"The name of the API Management instance\"\n}\n\nvariable \"apimPrivateIp\" {\n  type        = string\n  description = \"The private IP address of the API Management instance\"\n}\n\nvariable \"apimVnetId\" {\n  type = string\n}\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/gateway/certificate/certificate.tf",
    "content": "##################################################\n# Tried creating the certificate using the       #\n# azurerm_key_vault_certificate resource, but it #\n# doesn't work due to keyvault being private     #\n##################################################\n\n# resource \"azurerm_key_vault_certificate\" \"kv_domain_certs\" {\n#   count        = local.isLocalCertificate ? 1 : 0\n#   name         = local.secretName\n#   key_vault_id = var.keyvaultId\n\n#   certificate {\n#     contents = filebase64(var.certificate_path)\n#     password = var.certificate_password\n#   }\n\n#   certificate_policy {\n#     issuer_parameters {\n#       name = \"Self\"\n#     }\n\n#     key_properties {\n#       exportable = true\n#       key_size   = 256\n#       key_type   = \"EC\"\n#       reuse_key  = false\n#       curve      = \"P-256\"\n#     }\n\n#     secret_properties {\n#       content_type = \"application/x-pkcs12\"\n#     }\n#   }\n\n#   lifecycle {\n#     #prevent_destroy = true\n#   }\n# }\n\n# resource \"azurerm_key_vault_certificate\" \"local_domain_certs\" {\n#   count        = !local.isLocalCertificate ? 1 : 0\n#   name         = \"generated-cert\"\n#   key_vault_id = var.keyvaultId\n\n#   certificate_policy {\n#     issuer_parameters {\n#       name = \"Self\"\n#     }\n\n#     key_properties {\n#       exportable = true\n#       key_size   = 2048\n#       key_type   = \"RSA\"\n#       reuse_key  = true\n#     }\n\n#     lifetime_action {\n#       action {\n#         action_type = \"AutoRenew\"\n#       }\n\n#       trigger {\n#         days_before_expiry = 30\n#       }\n#     }\n\n#     secret_properties {\n#       content_type = \"application/x-pkcs12\"\n#     }\n\n#     x509_certificate_properties {\n#       extended_key_usage = [\"1.3.6.1.5.5.7.3.1\"]\n#       key_usage = [\n#         \"digitalSignature\",\n#         \"keyEncipherment\"\n#       ]\n#       subject            = \"CN=${var.appGatewayFqdn}\"\n#       validity_in_months = 12\n#     }\n#   }\n\n#   lifecycle {\n#     #prevent_destroy = true\n#   }\n# }\n\n\n#########################################################\n# Tried creating the certificate using the              #\n# azurerm_resource_deployment_script_azure_power_shell  #\n# resource, but it doesn't work due to keyvault being   #\n# private. Main issue compared to bicep is the resource #\n# doesn't have the option to run from a subnet          #\n#########################################################\n\n\n\n# resource \"azurerm_resource_deployment_script_azure_power_shell\" \"appGatewayCertificate\" {\n#   name                = \"${local.secretName}-certificate\"\n#   resource_group_name = var.sharedResourceGroupName\n#   location            = var.location\n#   version             = \"6.6\"\n#   retention_interval  = \"P1D\"\n#   command_line        = \" -vaultName ${var.keyVaultName} -certificateName ${local.secretName} -subjectName ${local.subjectName} -certPwd ${local.certPwd} -certDataString ${local.certDataString} -certType ${var.appGatewayCertType}\"\n#   cleanup_preference  = \"OnSuccess\"\n#   force_update_tag    = \"1\"\n#   timeout             = \"PT30M\"\n#   # container -> doesn't have the property to tell it from which subnet to run\n#   script_content = <<EOF\n#     param(\n#       [string] [Parameter(Mandatory=$true)] $vaultName,\n#       [string] [Parameter(Mandatory=$true)] $certificateName,\n#       [string] [Parameter(Mandatory=$true)] $subjectName,\n#       [string] [Parameter(Mandatory=$true)] $certPwd,\n#       [string] [Parameter(Mandatory=$true)] $certDataString,\n#       [string] [Parameter(Mandatory=$true)] $certType\n#       )\n\n#       $ErrorActionPreference = 'Stop'\n#       $DeploymentScriptOutputs = @{}\n#       if ($certType -eq 'selfsigned') {\n#         $policy = New-AzKeyVaultCertificatePolicy -SubjectName $subjectName -IssuerName Self -ValidityInMonths 12 -Verbose\n\n#         # private key is added as a secret that can be retrieved in the ARM template\n#         Add-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName -CertificatePolicy $policy -Verbose\n\n#         $newCert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName\n\n#         # it takes a few seconds for KeyVault to finish\n#         $tries = 0\n#         do {\n#           Write-Host 'Waiting for certificate creation completion...'\n#           Start-Sleep -Seconds 10\n#           $operation = Get-AzKeyVaultCertificateOperation -VaultName $vaultName -Name $certificateName\n#           $tries++\n\n#           if ($operation.Status -eq 'failed')\n#           {\n#           throw 'Creating certificate $certificateName in vault $vaultName failed with error $($operation.ErrorMessage)'\n#           }\n\n#           if ($tries -gt 120)\n#           {\n#           throw 'Timed out waiting for creation of certificate $certificateName in vault $vaultName'\n#           }\n#         } while ($operation.Status -ne 'completed')\n#       }\n#       else {\n#         $ss = Convertto-SecureString -String $certPwd -AsPlainText -Force;\n#         Import-AzKeyVaultCertificate -Name $certificateName -VaultName $vaultName -CertificateString $certDataString -Password $ss\n#       }\n#   EOF\n\n#   identity {\n#     type = \"UserAssigned\"\n#     identity_ids = [\n#       data.azurerm_user_assigned_identity.deploymentIdentity.id\n#     ]\n#   }\n#   depends_on = [\n#     azurerm_key_vault_access_policy.user_assigned_deployment_keyvault_permissions\n#   ]\n# }\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n#########################\n# Trying azapi approach #\n#########################\n\n# locals\nlocals {\n  secretName     = replace(var.appGatewayFqdn, \".\", \"-\")\n  subjectName    = \"CN=${var.appGatewayFqdn}\"\n  certPwd        = var.appGatewayCertType == \"selfsigned\" ? \"null\" : var.certificate_password\n  certDataString = var.appGatewayCertType == \"selfsigned\" ? \"null\" : var.certificate_path\n}\n\n\n# data userasignedidentity for deployment\ndata \"azurerm_user_assigned_identity\" \"deploymentIdentity\" {\n  resource_group_name = var.sharedResourceGroupName\n  name                = var.deploymentIdentityName\n}\n\nresource \"azurerm_key_vault_access_policy\" \"user_assigned_deployment_keyvault_permissions\" {\n  key_vault_id = var.keyvaultId\n  tenant_id    = data.azurerm_user_assigned_identity.deploymentIdentity.tenant_id\n  object_id    = data.azurerm_user_assigned_identity.deploymentIdentity.principal_id\n\n  certificate_permissions = [\n    \"Import\",\n    \"Get\",\n    \"List\",\n    \"Update\",\n    \"Create\"\n  ]\n\n  secret_permissions = [\n    \"Get\",\n    \"List\",\n  ]\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\n\n# get the ide of the resource group\ndata \"azurerm_resource_group\" \"sharedResourceGroup\" {\n  name = var.sharedResourceGroupName\n}\n\n\nresource \"azapi_resource\" \"appGatewayCertificate\" {\n  type       = \"Microsoft.Resources/deploymentScripts@2023-08-01\"\n  name       = \"${local.secretName}-certificate\"\n  depends_on = [azurerm_key_vault_access_policy.user_assigned_deployment_keyvault_permissions]\n  parent_id  = data.azurerm_resource_group.sharedResourceGroup.id\n  identity {\n    type         = \"UserAssigned\"\n    identity_ids = [data.azurerm_user_assigned_identity.deploymentIdentity.id]\n  }\n\n  body = jsonencode({\n    kind     = \"AzurePowerShell\"\n    location = var.location\n\n    properties = {\n      storageAccountSettings = {\n        storageAccountName = var.deploymentStorageName\n      }\n      azPowerShellVersion = \"14.3\"\n      containerSettings = {\n        subnetIds = [\n          {\n            id = var.deploymentSubnetId\n          }\n        ]\n      }\n      arguments         = \" -vaultName ${var.keyVaultName} -certificateName ${local.secretName} -subjectName ${local.subjectName} -certPwd ${local.certPwd} -certDataString ${local.certDataString} -certType ${var.appGatewayCertType}\"\n      scriptContent     = <<-EOT\n        param(\n        [string] [Parameter(Mandatory=$true)] $vaultName,\n        [string] [Parameter(Mandatory=$true)] $certificateName,\n        [string] [Parameter(Mandatory=$true)] $subjectName,\n        [string] [Parameter(Mandatory=$true)] $certPwd,\n        [string] [Parameter(Mandatory=$true)] $certDataString,\n        [string] [Parameter(Mandatory=$true)] $certType\n        )\n\n        $ErrorActionPreference = 'Stop'\n        $DeploymentScriptOutputs = @{}\n        if ($certType -eq 'selfsigned') {\n          $policy = New-AzKeyVaultCertificatePolicy -SubjectName $subjectName -IssuerName Self -ValidityInMonths 12 -Verbose\n\n          # private key is added as a secret that can be retrieved in the ARM template\n          Add-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName -CertificatePolicy $policy -Verbose\n\n          $newCert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName\n\n          # it takes a few seconds for KeyVault to finish\n          $tries = 0\n          do {\n            Write-Host 'Waiting for certificate creation completion...'\n            Start-Sleep -Seconds 10\n            $operation = Get-AzKeyVaultCertificateOperation -VaultName $vaultName -Name $certificateName\n            $tries++\n\n            if ($operation.Status -eq 'failed')\n            {\n            throw 'Creating certificate $certificateName in vault $vaultName failed with error $($operation.ErrorMessage)'\n            }\n\n            if ($tries -gt 120)\n            {\n            throw 'Timed out waiting for creation of certificate $certificateName in vault $vaultName'\n            }\n          } while ($operation.Status -ne 'completed')\n        }\n        else {\n          $ss = Convertto-SecureString -String $certPwd -AsPlainText -Force;\n          Import-AzKeyVaultCertificate -Name $certificateName -VaultName $vaultName -CertificateString $certDataString -Password $ss\n        }\n        $certificateIdOutput = $(Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName).id\n        Write-Output \"certificateId: $certificateIdOutput\"\n        $DeploymentScriptOutputs = @{}\n        $DeploymentScriptOutputs['certificateId'] = $certificateIdOutput\n      EOT\n      retentionInterval = \"P1D\"\n    }\n  })\n  response_export_values = [\"*\"]\n}\n\noutput \"secret_id\" {\n  value = jsondecode(azapi_resource.appGatewayCertificate.output).properties.outputs.certificateId\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/gateway/certificate/providers.tf",
    "content": "terraform {\n  required_providers {\n    azapi = {\n      source  = \"azure/azapi\"\n      version = \"~> 1.0\"\n    }\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/gateway/certificate/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus\"\n}\n\nvariable \"sharedResourceGroupName\" {\n  type        = string\n  description = \"Resource group with deploymnent Identity\"\n}\n\nvariable \"keyVaultName\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"deploymentIdentityName\" {\n  type        = string\n  description = \"deployment identity name\"\n}\n\nvariable \"keyvaultId\" {\n  type        = string\n  description = \"for giving permission to deployment identity\"\n}\n\nvariable \"appGatewayFqdn\" {\n  type        = string\n  description = \"The Azure location to deploy to\"\n  default     = \"apim.example.com\"\n}\n\nvariable \"certificate_path\" {\n  type        = string\n  description = \"\"\n  default     = null\n}\n\nvariable \"certificate_password\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"appGatewayCertType\" {\n  description = \"selfsigned will create a self-signed certificate for the APPGATEWAY_FQDN. custom will use an existing certificate in pfx format that needs to be available in the [certs](../../certs) folder and named appgw.pfx \"\n  default     = \"selfsigned\"\n}\n\nvariable \"deploymentSubnetId\" {\n  type        = string\n  description = \"The subnet id where the deployment will run\"\n}\n\nvariable \"deploymentStorageName\" {\n  type        = string\n  description = \"The name of the storage account to use for deployment\"\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/gateway/gateway.tf",
    "content": "locals {\n  appGatewayName        = \"appgw-${var.resourceSuffix}\"\n  appGatewayPrimaryPip  = \"pip-appgw-${var.resourceSuffix}\"\n  appGatewayIdentityId  = \"identity-${local.appGatewayName}\"\n  httpsBackendProbeName = \"APIM\"\n  isLocalCertificate    = var.appGatewayCertType == \"custom\"\n  # certificateSecretId   = local.isLocalCertificate ? azurerm_key_vault_certificate.kv_domain_certs[0].secret_id : azurerm_key_vault_certificate.local_domain_certs[0].secret_id\n  secretName     = replace(var.appGatewayFqdn, \".\", \"-\")\n  subjectName    = \"CN=${var.appGatewayFqdn}\"\n  certPwd        = var.certificate_password\n  certDataString = var.certificate_path\n}\n\n\n\nresource \"azurerm_user_assigned_identity\" \"user_assigned_identity\" {\n  resource_group_name = var.resourceGroupName\n  location            = var.location\n\n  name = local.appGatewayIdentityId\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_key_vault_access_policy\" \"user_assigned_identity_keyvault_permissions\" {\n  key_vault_id = var.keyvaultId\n  tenant_id    = azurerm_user_assigned_identity.user_assigned_identity.tenant_id\n  object_id    = azurerm_user_assigned_identity.user_assigned_identity.principal_id\n\n  certificate_permissions = [\n    \"Import\",\n    \"Get\",\n    \"List\",\n    \"Update\",\n    \"Create\"\n  ]\n\n  secret_permissions = [\n    \"Get\",\n    \"List\",\n  ]\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\n# module \"certificate\" {\n#   source                  = \"./certificate\"\n#   location                = var.location\n#   sharedResourceGroupName = var.sharedResourceGroupName\n#   keyVaultName            = var.keyVaultName\n#   deploymentIdentityName  = var.deploymentIdentityName\n#   keyvaultId              = var.keyvaultId\n#   appGatewayFqdn          = var.appGatewayFqdn\n#   certificate_path        = var.certificate_path\n#   certificate_password    = var.certificate_password\n#   appGatewayCertType      = var.appGatewayCertType\n#   deploymentSubnetId      = var.deploymentSubnetId\n#   deploymentStorageName   = var.deploymentStorageName\n# }\n\n//Public IP\nresource \"azurerm_public_ip\" \"public_ip\" {\n  name                = local.appGatewayPrimaryPip\n  resource_group_name = var.resourceGroupName\n  location            = var.location\n  sku                 = \"Standard\"\n  sku_tier            = \"Regional\"\n  allocation_method   = \"Static\"\n  ip_version          = \"IPv4\"\n  zones               = [\"1\", \"2\", \"3\"]\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_application_gateway\" \"network\" {\n  name                = local.appGatewayName\n  resource_group_name = var.resourceGroupName\n  location            = var.location\n\n  depends_on = [\n    azurerm_key_vault_access_policy.user_assigned_identity_keyvault_permissions\n    #,module.certificate\n  ]\n\n  identity {\n    type         = \"UserAssigned\"\n    identity_ids = [azurerm_user_assigned_identity.user_assigned_identity.id]\n  }\n\n  sku {\n    name = \"WAF_v2\"\n    tier = \"WAF_v2\"\n  }\n\n  ssl_certificate {\n    name                = var.appGatewayFqdn\n    #key_vault_secret_id = \"https://${var.keyVaultName}.vault.azure.net:443/secrets/${local.secretName}\"\n    data = filebase64(local.certDataString)\n    password = local.certPwd    \n  }\n\n  gateway_ip_configuration {\n    name      = \"appGatewayIpConfig\"\n    subnet_id = var.subnetId\n  }\n\n  frontend_ip_configuration {\n    name                          = \"appGwPublicFrontendIp\"\n    private_ip_address_allocation = \"Dynamic\"\n    public_ip_address_id          = azurerm_public_ip.public_ip.id\n  }\n\n  frontend_port {\n    name = \"port_80\"\n    port = 80\n  }\n\n  frontend_port {\n    name = \"port_443\"\n    port = 443\n  }\n\n  backend_address_pool {\n    name  = \"apim\"\n    fqdns = [var.primaryBackendendFqdn]\n  }\n\n  backend_http_settings {\n    name                                = \"default\"\n    port                                = 80\n    protocol                            = \"Http\"\n    cookie_based_affinity               = \"Disabled\"\n    pick_host_name_from_backend_address = false\n    affinity_cookie_name                = \"ApplicationGatewayAffinity\"\n    request_timeout                     = 20\n\n  }\n\n  backend_http_settings {\n    name                                = \"https\"\n    port                                = 443\n    protocol                            = \"Https\"\n    cookie_based_affinity               = \"Disabled\"\n    host_name                           = var.primaryBackendendFqdn\n    pick_host_name_from_backend_address = false\n    request_timeout                     = 20\n    probe_name                          = local.httpsBackendProbeName\n  }\n\n  http_listener {\n    name                           = \"default\"\n    frontend_ip_configuration_name = \"appGwPublicFrontendIp\"\n    frontend_port_name             = \"port_80\"\n    protocol                       = \"Http\"\n    require_sni                    = false\n  }\n\n  http_listener {\n    name                           = \"https\"\n    frontend_ip_configuration_name = \"appGwPublicFrontendIp\"\n    frontend_port_name             = \"port_443\"\n    protocol                       = \"Https\"\n    require_sni                    = false\n    ssl_certificate_name           = var.appGatewayFqdn\n  }\n\n  request_routing_rule {\n    name                       = \"apim\"\n    rule_type                  = \"Basic\"\n    http_listener_name         = \"https\"\n    backend_address_pool_name  = \"apim\"\n    backend_http_settings_name = \"https\"\n    priority                   = 100\n  }\n\n  probe {\n    name                                      = \"APIM\"\n    protocol                                  = \"Https\"\n    host                                      = var.primaryBackendendFqdn\n    path                                      = var.probe_url\n    interval                                  = 30\n    timeout                                   = 30\n    unhealthy_threshold                       = 3\n    pick_host_name_from_backend_http_settings = false\n    minimum_servers                           = 0\n\n    match {\n      status_code = [\"200-399\"]\n    }\n  }\n\n  waf_configuration {\n    enabled                  = true\n    firewall_mode            = \"Detection\"\n    rule_set_type            = \"OWASP\"\n    rule_set_version         = \"3.0\"\n    request_body_check       = true\n    max_request_body_size_kb = 128\n    file_upload_limit_mb     = 100\n  }\n\n  enable_http2 = true\n\n  autoscale_configuration {\n    min_capacity = 2\n    max_capacity = 3\n  }\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/gateway/outputs.tf",
    "content": ""
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/gateway/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus\"\n}\n\nvariable \"resourceSuffix\" {\n  type        = string\n  description = \"A suffix for naming\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"resourceGroupName\" {\n  type        = string\n  description = \"The name of the resource group\"\n}\n\nvariable \"appGatewayCertType\" {\n  description = \"selfsigned will create a self-signed certificate for the APPGATEWAY_FQDN. custom will use an existing certificate in pfx format that needs to be available in the [certs](../../certs) folder and named appgw.pfx \"\n  default     = \"selfsigned\"\n}\n\nvariable \"keyvaultId\" {\n  type        = string\n  description = \"\"\n  default     = null\n}\n\nvariable \"keyVaultName\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"deploymentIdentityName\" {\n  type        = string\n  description = \"deployment identity name\"\n}\n\nvariable \"appGatewayFqdn\" {\n  type        = string\n  description = \"The Azure location to deploy to\"\n  default     = \"apim.example.com\"\n}\n\nvariable \"certificate_path\" {\n  type        = string\n  description = \"\"\n  default     = null\n}\n\nvariable \"certificate_password\" {\n  type        = string\n  sensitive   = true\n  description = \"\"\n}\n\nvariable \"subnetId\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"primaryBackendendFqdn\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"probe_url\" {\n  type        = string\n  description = \"\"\n  default     = \"/status-0123456789abcdef\"\n}\n\nvariable \"sharedResourceGroupName\" {\n  type        = string\n  description = \"Resource group with deploymnent Identity\"\n}\n\nvariable \"deploymentSubnetId\" {\n  type        = string\n  description = \"The subnet id where the deployment will run\"\n}\n\nvariable \"deploymentStorageName\" {\n  type        = string\n  description = \"The name of the storage account to use for deployment\"\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_apim/apim.tf",
    "content": "locals {\n  apimName          = \"apim-${var.resourceSuffix}\"\n  apimPipPrimaryPip = \"pip-apim-${var.resourceSuffix}\"\n  apimIdentityName  = \"identity-${local.apimName}\"\n  skuCount          = var.zoneRedundantEnabled ? 3 : 1\n  skuName           = \"Premium_${local.skuCount}\"\n  zones             = var.zoneRedundantEnabled ? [\"1\", \"2\", \"3\"] : [\"1\"]\n}\n\nresource \"azurerm_user_assigned_identity\" \"apimIdentity\" {\n  name                = local.apimIdentityName\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n}\n\ndata \"azurerm_key_vault\" \"keyVault\" {\n  name                = var.keyVaultName\n  resource_group_name = var.sharedResourceGroupName\n}\n\n#-------------------------------\n# Creation of an internal APIM instance\n#-------------------------------\nresource \"azurerm_api_management\" \"apim_internal\" {\n  name                 = local.apimName\n  location             = var.location\n  resource_group_name  = var.resourceGroupName\n  publisher_name       = var.publisherName\n  publisher_email      = var.publisherEmail\n  virtual_network_type = \"Internal\"\n\n  sku_name = local.skuName\n\n  zones = local.zones\n\n  min_api_version = \"2019-12-01\"\n\n  virtual_network_configuration {\n    subnet_id = var.apimSubnetId\n  }\n\n  identity {\n    type         = \"UserAssigned\"\n    identity_ids = [\"${azurerm_user_assigned_identity.apimIdentity.id}\"]\n  }\n\n   \n  additional_location {    \n    location    = var.locationSecond\n    zones       = local.zones\n    virtual_network_configuration {\n        subnet_id = var.apimSecondSubnetId\n    }\n  }\n\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\n\n#-------------------------------\n#Creation of the apim logger entity\n#-------------------------------\nresource \"azurerm_api_management_logger\" \"apim_logger\" {\n  name                = \"apim-logger\"\n  api_management_name = azurerm_api_management.apim_internal.name\n  resource_group_name = var.resourceGroupName\n  resource_id         = var.workspaceId\n\n  application_insights {\n    instrumentation_key = var.instrumentationKey\n  }\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\n#-------------------------------\n#API management service diagnostic\n#-------------------------------\nresource \"azurerm_api_management_diagnostic\" \"apim_diagnostic\" {\n  identifier               = \"applicationinsights\"\n  resource_group_name      = var.resourceGroupName\n  api_management_name      = azurerm_api_management.apim_internal.name\n  api_management_logger_id = azurerm_api_management_logger.apim_logger.id\n\n  sampling_percentage = 100.0\n  always_log_errors   = true\n  verbosity           = \"verbose\" #possible value are verbose, error, information\n\n\n  frontend_request {\n    body_bytes = 32\n    headers_to_log = [\n      \"content-type\",\n      \"accept\",\n      \"origin\",\n    ]\n  }\n\n  frontend_response {\n    body_bytes = 32\n    headers_to_log = [\n      \"content-type\",\n      \"content-length\",\n      \"origin\",\n    ]\n  }\n\n  backend_request {\n    body_bytes = 32\n    headers_to_log = [\n      \"content-type\",\n      \"accept\",\n      \"origin\",\n    ]\n  }\n\n  backend_response {\n    body_bytes = 32\n    headers_to_log = [\n      \"content-type\",\n      \"content-length\",\n      \"origin\",\n    ]\n  }\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\nresource \"azurerm_api_management_product\" \"starter\" {\n  display_name        = \"Starter\"\n  product_id          = \"starter\"\n  api_management_name = azurerm_api_management.apim_internal.name\n  resource_group_name = azurerm_api_management.apim_internal.resource_group_name\n  published           = true\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\nresource \"random_uuid\" \"starter_key\" {\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\nresource \"azurerm_api_management_subscription\" \"echo\" {\n  api_management_name = azurerm_api_management.apim_internal.name\n  resource_group_name = azurerm_api_management.apim_internal.resource_group_name\n  product_id          = azurerm_api_management_product.starter.id\n  display_name        = \"Echo API\"\n  primary_key         = random_uuid.starter_key.result\n  allow_tracing       = false\n  state               = \"active\"\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\n#-------------------------------\n# Importing the Echo API into API Management\n#-------------------------------\nresource \"azurerm_api_management_api\" \"echo_api\" {\n  name                = \"echo-api\"\n  api_management_name = azurerm_api_management.apim_internal.name\n  resource_group_name = azurerm_api_management.apim_internal.resource_group_name\n  revision            = \"1\"\n  display_name        = \"Echo API\"\n  path                = \"echo\"\n  protocols           = [\"https\"]\n  service_url         = \"https://httpbin.io/anything\"\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\nresource \"azurerm_api_management_api_operation\" \"echo_api_operation\" {\n  api_name            = azurerm_api_management_api.echo_api.name\n  api_management_name = azurerm_api_management.apim_internal.name\n  resource_group_name = azurerm_api_management.apim_internal.resource_group_name\n  display_name        = \"Retrieve resource\"\n  method              = \"GET\"\n  url_template        = \"/resource\"\n\n  request {\n    query_parameter {\n      type          = \"string\"\n      name          = \"param1\"\n      default_value = \"sample\"\n      required      = true\n    }\n    query_parameter {\n      type     = \"number\"\n      name     = \"param2\"\n      required = false\n    }\n  }\n\n  response {\n    status_code = 200\n    description = \"A demonstration of a GET call on a sample resource. It is handled by an \\\"echo\\\" backend which returns a response equal to the request (the supplied headers and body are being returned as received).\"\n  }\n  operation_id = \"retrieve-resource\"\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n\n}\n\nresource \"azurerm_api_management_product_api\" \"echo\" {\n  api_name            = azurerm_api_management_api.echo_api.name\n  product_id          = azurerm_api_management_product.starter.product_id\n  api_management_name = azurerm_api_management.apim_internal.name\n  resource_group_name = azurerm_api_management.apim_internal.resource_group_name\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\n\nresource \"azurerm_key_vault_access_policy\" \"apim_access_policy\" {\n  key_vault_id = data.azurerm_key_vault.keyVault.id\n  tenant_id    = azurerm_user_assigned_identity.apimIdentity.tenant_id\n  object_id    = azurerm_user_assigned_identity.apimIdentity.principal_id\n\n  secret_permissions = [\n    \"Get\",\n    \"List\"\n  ]\n\n  certificate_permissions = [\n    \"Get\",\n    \"List\"\n  ]\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_apim/outputs.tf",
    "content": "output \"primaryBackendendFqdn\" {\n  value = azurerm_api_management.apim_internal.gateway_url\n}\n\noutput \"bakendUrl\" {\n  value = \"${azurerm_api_management.apim_internal.name}.azure-api.net\"\n}\n\noutput \"subscriptionKey\" {\n  value = random_uuid.starter_key.result\n}\n\noutput \"apimPrivateIp\" {\n  value = azurerm_api_management.apim_internal.private_ip_addresses[0]\n}\n\noutput \"apimName\" {\n  value = azurerm_api_management.apim_internal.name\n}\n\noutput \"apimIdentityName\" {\n  value = azurerm_user_assigned_identity.apimIdentity.name\n}\n\noutput \"apim_regional_url_1\" {\n  value = replace(azurerm_api_management.apim_internal.gateway_regional_url,\"https://\",\"\")\n}\n\noutput \"apim_regional_url_2\" {\n  value = replace(azurerm_api_management.apim_internal.additional_location[0].gateway_regional_url,\"https://\",\"\")\n}\n\noutput \"apim_regional_IP_1\" {\n  value = azurerm_api_management.apim_internal.private_ip_addresses[0]\n}\n\noutput \"apim_regional_IP_2\" {\n  value = azurerm_api_management.apim_internal.additional_location[0].private_ip_addresses[0]\n}\n\noutput \"apim_regional_name_1\" {\n  value = split(\".\",replace(azurerm_api_management.apim_internal.gateway_regional_url,\"https://\",\"\"))[0]\n}\n\noutput \"apim_regional_name_2\" {\n  value = split(\".\",replace(azurerm_api_management.apim_internal.additional_location[0].gateway_regional_url,\"https://\",\"\"))[0]\n}\n\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_apim/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus\"\n}\n\nvariable \"resourceSuffix\" {\n  type        = string\n  description = \"A suffix for naming\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"resourceGroupName\" {\n  type        = string\n  description = \"The name of the resource group\"\n}\n\n#-------------------------------\n# APIM specific variables\n#-------------------------------\n\nvariable \"keyVaultName\" {\n  description = \"The name of the Key Vault\"\n  type        = string\n}\n\nvariable \"publisherName\" {\n  description = \"The name of the publisher/company\"\n  type        = string\n  default     = \"Contoso\"\n}\n\nvariable \"publisherEmail\" {\n  description = \"The email of the publisher/company; shows as administrator email in APIM\"\n  type        = string\n  default     = \"apim@contoso.com\"\n}\n\nvariable \"skuName\" {\n  description = \"String consisting of two parts separated by an underscore(_). The first part is the name, valid values include: Consumption, Developer, Basic, Standard and Premium. The second part is the capacity (e.g. the number of deployed units of the sku), which must be a positive integer (e.g. Developer_1)\"\n  type        = string\n  default     = \"Developer_1\"\n}\n\nvariable \"apimSubnetId\" {\n  description = \"The subnet id of the apim instance\"\n  type        = string\n}\n\nvariable \"workspaceId\" {\n  type        = string\n  description = \"The workspace id of the log analytics workspace\"\n}\n\nvariable \"instrumentationKey\" {\n  type        = string\n  description = \"App insights instrumentation key\"\n}\n\nvariable \"sharedResourceGroupName\" {\n  type        = string\n  description = \"The name of the shared resource group\"\n}\n\n# Multi-region deployment variables\nvariable \"apimSecondSubnetId\" {\n  type        = string\n  description = \"The subnet id of the second apim instance\"\n}\n\nvariable \"zoneRedundantEnabled\" {\n  type        = bool\n  description = \"Flag to enable zone redundancy\"\n  default     = false\n}\n\nvariable \"locationSecond\" {\n  type        = string\n  description = \"The Azure location for the second region in a multi-region deployment\"\n  default     = null\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_apim-dns-regional/dnszone.tf",
    "content": "/* Creates a Private DNS ZOne, A Records and Vnet Link for each of the below endpoints\nAPI Gateway\t                contosointernalvnet.azure-api.net\nDeveloper portal\t        contosointernalvnet.portal.azure-api.net\nThe new developer portal\tcontosointernalvnet.developer.azure-api.net\nDirect management endpoint\tcontosointernalvnet.management.azure-api.net\nGit\t                        contosointernalvnet.scm.azure-api.net */\n\n#-------------------------------\n# DNS zones \n#-------------------------------\nresource \"azurerm_private_dns_zone\" \"gateway\" {\n  name                = \"regional.azure-api.net\"\n  resource_group_name = var.resourceGroupName\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\n\n#-------------------------------\n# A records for the DNS zones\n#-------------------------------\nresource \"azurerm_private_dns_a_record\" \"gateway_record\" {\n  name                = lower(var.apimRegionalName)\n  zone_name           = azurerm_private_dns_zone.gateway.name\n  resource_group_name = var.resourceGroupName\n  ttl                 = 36000\n  records             = [var.apimPrivateIp]\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_a_record\" \"gateway_second_record\" {\n  name                = lower(var.apimSecondRegionalName)\n  zone_name           = azurerm_private_dns_zone.gateway.name\n  resource_group_name = var.resourceGroupName\n  ttl                 = 36000\n  records             = [var.apimSecondPrivateIp]\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\n#-------------------------------\n# Vnet links\n#-------------------------------\nresource \"azurerm_private_dns_zone_virtual_network_link\" \"gateway_vnetlink\" {\n  name                  = \"gateway-vnet-link\"\n  resource_group_name   = var.resourceGroupName\n  private_dns_zone_name = azurerm_private_dns_zone.gateway.name\n  virtual_network_id    = var.apimVnetId\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\nresource \"azurerm_private_dns_zone_virtual_network_link\" \"gateway_second_vnetlink\" {\n  name                  = \"gateway-second-vnet-link\"\n  resource_group_name   = var.resourceGroupName\n  private_dns_zone_name = azurerm_private_dns_zone.gateway.name\n  virtual_network_id    = var.apimSecondVnetId\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_apim-dns-regional/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus\"\n}\n\nvariable \"resourceSuffix\" {\n  type        = string\n  description = \"A suffix for naming\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"resourceGroupName\" {\n  type        = string\n  description = \"The name of the resource group\"\n}\n\nvariable \"apimRegionalName\" {\n  type        = string\n  description = \"The name of the API Management instance\"\n}\n\nvariable \"apimSecondRegionalName\" {\n  type        = string\n  description = \"The name of the second API Management instance (gateway)\"\n}\n\nvariable \"apimPrivateIp\" {\n  type        = string\n  description = \"The private IP address of the API Management instance\"\n}\n\nvariable \"apimSecondPrivateIp\" {\n  type        = string\n  description = \"The private IP address of the second API Management instance\"\n}\n\nvariable \"apimVnetId\" {\n  type = string\n}\n\nvariable \"apimSecondVnetId\" {\n  type = string\n}\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_gateway/certificate/certificate.tf",
    "content": "##################################################\n# Tried creating the certificate using the       #\n# azurerm_key_vault_certificate resource, but it #\n# doesn't work due to keyvault being private     #\n##################################################\n\n# resource \"azurerm_key_vault_certificate\" \"kv_domain_certs\" {\n#   count        = local.isLocalCertificate ? 1 : 0\n#   name         = local.secretName\n#   key_vault_id = var.keyvaultId\n\n#   certificate {\n#     contents = filebase64(var.certificate_path)\n#     password = var.certificate_password\n#   }\n\n#   certificate_policy {\n#     issuer_parameters {\n#       name = \"Self\"\n#     }\n\n#     key_properties {\n#       exportable = true\n#       key_size   = 256\n#       key_type   = \"EC\"\n#       reuse_key  = false\n#       curve      = \"P-256\"\n#     }\n\n#     secret_properties {\n#       content_type = \"application/x-pkcs12\"\n#     }\n#   }\n\n#   lifecycle {\n#     ##prevent_destroy = true\n#   }\n# }\n\n# resource \"azurerm_key_vault_certificate\" \"local_domain_certs\" {\n#   count        = !local.isLocalCertificate ? 1 : 0\n#   name         = \"generated-cert\"\n#   key_vault_id = var.keyvaultId\n\n#   certificate_policy {\n#     issuer_parameters {\n#       name = \"Self\"\n#     }\n\n#     key_properties {\n#       exportable = true\n#       key_size   = 2048\n#       key_type   = \"RSA\"\n#       reuse_key  = true\n#     }\n\n#     lifetime_action {\n#       action {\n#         action_type = \"AutoRenew\"\n#       }\n\n#       trigger {\n#         days_before_expiry = 30\n#       }\n#     }\n\n#     secret_properties {\n#       content_type = \"application/x-pkcs12\"\n#     }\n\n#     x509_certificate_properties {\n#       extended_key_usage = [\"1.3.6.1.5.5.7.3.1\"]\n#       key_usage = [\n#         \"digitalSignature\",\n#         \"keyEncipherment\"\n#       ]\n#       subject            = \"CN=${var.appGatewayFqdn}\"\n#       validity_in_months = 12\n#     }\n#   }\n\n#   lifecycle {\n#     ##prevent_destroy = true\n#   }\n# }\n\n\n#########################################################\n# Tried creating the certificate using the              #\n# azurerm_resource_deployment_script_azure_power_shell  #\n# resource, but it doesn't work due to keyvault being   #\n# private. Main issue compared to bicep is the resource #\n# doesn't have the option to run from a subnet          #\n#########################################################\n\n\n\n# resource \"azurerm_resource_deployment_script_azure_power_shell\" \"appGatewayCertificate\" {\n#   name                = \"${local.secretName}-certificate\"\n#   resource_group_name = var.sharedResourceGroupName\n#   location            = var.location\n#   version             = \"6.6\"\n#   retention_interval  = \"P1D\"\n#   command_line        = \" -vaultName ${var.keyVaultName} -certificateName ${local.secretName} -subjectName ${local.subjectName} -certPwd ${local.certPwd} -certDataString ${local.certDataString} -certType ${var.appGatewayCertType}\"\n#   cleanup_preference  = \"OnSuccess\"\n#   force_update_tag    = \"1\"\n#   timeout             = \"PT30M\"\n#   # container -> doesn't have the property to tell it from which subnet to run\n#   script_content = <<EOF\n#     param(\n#       [string] [Parameter(Mandatory=$true)] $vaultName,\n#       [string] [Parameter(Mandatory=$true)] $certificateName,\n#       [string] [Parameter(Mandatory=$true)] $subjectName,\n#       [string] [Parameter(Mandatory=$true)] $certPwd,\n#       [string] [Parameter(Mandatory=$true)] $certDataString,\n#       [string] [Parameter(Mandatory=$true)] $certType\n#       )\n\n#       $ErrorActionPreference = 'Stop'\n#       $DeploymentScriptOutputs = @{}\n#       if ($certType -eq 'selfsigned') {\n#         $policy = New-AzKeyVaultCertificatePolicy -SubjectName $subjectName -IssuerName Self -ValidityInMonths 12 -Verbose\n\n#         # private key is added as a secret that can be retrieved in the ARM template\n#         Add-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName -CertificatePolicy $policy -Verbose\n\n#         $newCert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName\n\n#         # it takes a few seconds for KeyVault to finish\n#         $tries = 0\n#         do {\n#           Write-Host 'Waiting for certificate creation completion...'\n#           Start-Sleep -Seconds 10\n#           $operation = Get-AzKeyVaultCertificateOperation -VaultName $vaultName -Name $certificateName\n#           $tries++\n\n#           if ($operation.Status -eq 'failed')\n#           {\n#           throw 'Creating certificate $certificateName in vault $vaultName failed with error $($operation.ErrorMessage)'\n#           }\n\n#           if ($tries -gt 120)\n#           {\n#           throw 'Timed out waiting for creation of certificate $certificateName in vault $vaultName'\n#           }\n#         } while ($operation.Status -ne 'completed')\n#       }\n#       else {\n#         $ss = Convertto-SecureString -String $certPwd -AsPlainText -Force;\n#         Import-AzKeyVaultCertificate -Name $certificateName -VaultName $vaultName -CertificateString $certDataString -Password $ss\n#       }\n#   EOF\n\n#   identity {\n#     type = \"UserAssigned\"\n#     identity_ids = [\n#       data.azurerm_user_assigned_identity.deploymentIdentity.id\n#     ]\n#   }\n#   depends_on = [\n#     azurerm_key_vault_access_policy.user_assigned_deployment_keyvault_permissions\n#   ]\n# }\n\n#########################\n# Trying azapi approach #\n#########################\n\n# locals\nlocals {\n  secretName     = replace(var.appGatewayFqdn, \".\", \"-\")\n  subjectName    = \"CN=${var.appGatewayFqdn}\"\n  certPwd        = var.appGatewayCertType == \"selfsigned\" ? \"null\" : var.certificate_password\n  certDataString = var.appGatewayCertType == \"selfsigned\" ? \"null\" : var.certificate_path\n}\n\n\n# data userasignedidentity for deployment\ndata \"azurerm_user_assigned_identity\" \"deploymentIdentity\" {\n  resource_group_name = var.sharedResourceGroupName\n  name                = var.deploymentIdentityName\n}\n\nresource \"azurerm_key_vault_access_policy\" \"user_assigned_deployment_keyvault_permissions\" {\n  key_vault_id = var.keyvaultId\n  tenant_id    = data.azurerm_user_assigned_identity.deploymentIdentity.tenant_id\n  object_id    = data.azurerm_user_assigned_identity.deploymentIdentity.principal_id\n\n  certificate_permissions = [\n    \"Import\",\n    \"Get\",\n    \"List\",\n    \"Update\",\n    \"Create\"\n  ]\n\n  secret_permissions = [\n    \"Get\",\n    \"List\",\n  ]\n\n  lifecycle {\n    ###prevent_destroy = true\n  }\n}\n\n\n# get the ide of the resource group\ndata \"azurerm_resource_group\" \"sharedResourceGroup\" {\n  name = var.sharedResourceGroupName\n}\n\n\nresource \"azapi_resource\" \"appGatewayCertificate\" {\n  type       = \"Microsoft.Resources/deploymentScripts@2023-08-01\"\n  name       = \"${local.secretName}-certificate\"\n  depends_on = [azurerm_key_vault_access_policy.user_assigned_deployment_keyvault_permissions]\n  parent_id  = data.azurerm_resource_group.sharedResourceGroup.id\n  identity {\n    type         = \"UserAssigned\"\n    identity_ids = [data.azurerm_user_assigned_identity.deploymentIdentity.id]\n  }\n\n  body = jsonencode({\n    kind     = \"AzurePowerShell\"\n    location = var.location\n\n    properties = {\n      storageAccountSettings = {\n        storageAccountName = var.deploymentStorageName\n      }\n      azPowerShellVersion = \"14.3\"\n      containerSettings = {\n        subnetIds = [\n          {\n            id = var.deploymentSubnetId\n          }\n        ]\n      }\n      arguments         = \" -vaultName ${var.keyVaultName} -certificateName ${local.secretName} -subjectName ${local.subjectName} -certPwd ${local.certPwd} -certDataString ${local.certDataString} -certType ${var.appGatewayCertType}\"\n      scriptContent     = <<-EOT\n        param(\n        [string] [Parameter(Mandatory=$true)] $vaultName,\n        [string] [Parameter(Mandatory=$true)] $certificateName,\n        [string] [Parameter(Mandatory=$true)] $subjectName,\n        [string] [Parameter(Mandatory=$true)] $certPwd,\n        [string] [Parameter(Mandatory=$true)] $certDataString,\n        [string] [Parameter(Mandatory=$true)] $certType\n        )\n\n        $ErrorActionPreference = 'Stop'\n        $DeploymentScriptOutputs = @{}\n        if ($certType -eq 'selfsigned') {\n          $policy = New-AzKeyVaultCertificatePolicy -SubjectName $subjectName -IssuerName Self -ValidityInMonths 12 -Verbose\n\n          # private key is added as a secret that can be retrieved in the ARM template\n          Add-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName -CertificatePolicy $policy -Verbose\n\n          $newCert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName\n\n          # it takes a few seconds for KeyVault to finish\n          $tries = 0\n          do {\n            Write-Host 'Waiting for certificate creation completion...'\n            Start-Sleep -Seconds 10\n            $operation = Get-AzKeyVaultCertificateOperation -VaultName $vaultName -Name $certificateName\n            $tries++\n\n            if ($operation.Status -eq 'failed')\n            {\n            throw 'Creating certificate $certificateName in vault $vaultName failed with error $($operation.ErrorMessage)'\n            }\n\n            if ($tries -gt 120)\n            {\n            throw 'Timed out waiting for creation of certificate $certificateName in vault $vaultName'\n            }\n          } while ($operation.Status -ne 'completed')\n        }\n        else {\n          $ss = Convertto-SecureString -String $certPwd -AsPlainText -Force;\n          Import-AzKeyVaultCertificate -Name $certificateName -VaultName $vaultName -CertificateString $certDataString -Password $ss\n        }\n        $certificateIdOutput = $(Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName).id\n        Write-Output \"certificateId: $certificateIdOutput\"\n        $DeploymentScriptOutputs = @{}\n        $DeploymentScriptOutputs['certificateId'] = $certificateIdOutput\n      EOT\n      retentionInterval = \"P1D\"\n    }\n  })\n  response_export_values = [\"*\"]\n}\n\noutput \"secret_id\" {\n  value = jsondecode(azapi_resource.appGatewayCertificate.output).properties.outputs.certificateId\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_gateway/certificate/providers.tf",
    "content": "terraform {\n  required_providers {\n    azapi = {\n      source  = \"azure/azapi\"\n      version = \"~> 1.0\"\n    }\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_gateway/certificate/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus\"\n}\n\nvariable \"sharedResourceGroupName\" {\n  type        = string\n  description = \"Resource group with deploymnent Identity\"\n}\n\nvariable \"keyVaultName\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"deploymentIdentityName\" {\n  type        = string\n  description = \"deployment identity name\"\n}\n\nvariable \"keyvaultId\" {\n  type        = string\n  description = \"for giving permission to deployment identity\"\n}\n\nvariable \"appGatewayFqdn\" {\n  type        = string\n  description = \"The Azure location to deploy to\"\n  default     = \"apim.example.com\"\n}\n\nvariable \"certificate_path\" {\n  type        = string\n  description = \"\"\n  default     = null\n}\n\nvariable \"certificate_password\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"appGatewayCertType\" {\n  description = \"selfsigned will create a self-signed certificate for the APPGATEWAY_FQDN. custom will use an existing certificate in pfx format that needs to be available in the [certs](../../certs) folder and named appgw.pfx \"\n  default     = \"selfsigned\"\n}\n\nvariable \"deploymentSubnetId\" {\n  type        = string\n  description = \"The subnet id where the deployment will run\"\n}\n\nvariable \"deploymentStorageName\" {\n  type        = string\n  description = \"The name of the storage account to use for deployment\"\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_gateway/gateway.tf",
    "content": "locals {\n  appGatewayName        = \"appgw-${var.resourceSuffix}\"\n  appGatewayPrimaryPip  = \"pip-appgw-${var.resourceSuffix}\"\n  appGatewayIdentityId  = \"identity-${local.appGatewayName}\"\n  httpsBackendProbeName = \"APIM\"\n  isLocalCertificate    = var.appGatewayCertType == \"custom\"\n  # certificateSecretId   = local.isLocalCertificate ? azurerm_key_vault_certificate.kv_domain_certs[0].secret_id : azurerm_key_vault_certificate.local_domain_certs[0].secret_id\n  secretName     = replace(var.appGatewayFqdn, \".\", \"-\")\n  subjectName    = \"CN=${var.appGatewayFqdn}\"\n  certPwd        = var.certificate_password\n  certDataString = var.certificate_path\n  trafficManagerName = \"${replace(var.appGatewayFqdn,\".\",\"-\")}.trafficmanager.net\"\n}\n\n\n\nresource \"azurerm_user_assigned_identity\" \"user_assigned_identity\" {\n  resource_group_name = var.resourceGroupName\n  location            = var.location\n\n  name = local.appGatewayIdentityId\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\nresource \"azurerm_key_vault_access_policy\" \"user_assigned_identity_keyvault_permissions\" {\n  key_vault_id = var.keyvaultId\n  tenant_id    = azurerm_user_assigned_identity.user_assigned_identity.tenant_id\n  object_id    = azurerm_user_assigned_identity.user_assigned_identity.principal_id\n\n  certificate_permissions = [\n    \"Import\",\n    \"Get\",\n    \"List\",\n    \"Update\",\n    \"Create\"\n  ]\n\n  secret_permissions = [\n    \"Get\",\n    \"List\",\n  ]\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\n# module \"certificate\" {\n#   source                  = \"./certificate\"\n#   location                = var.location\n#   sharedResourceGroupName = var.sharedResourceGroupName\n#   keyVaultName            = var.keyVaultName\n#   deploymentIdentityName  = var.deploymentIdentityName\n#   keyvaultId              = var.keyvaultId\n#   appGatewayFqdn          = var.appGatewayFqdn\n#   certificate_path        = var.certificate_path\n#   certificate_password    = var.certificate_password\n#   appGatewayCertType      = var.appGatewayCertType\n#   deploymentSubnetId      = var.deploymentSubnetId\n#   deploymentStorageName   = var.deploymentStorageName\n# }\n\n//Public IP\nresource \"azurerm_public_ip\" \"public_ip\" {\n  name                = local.appGatewayPrimaryPip\n  resource_group_name = var.resourceGroupName\n  location            = var.location\n  sku                 = \"Standard\"\n  sku_tier            = \"Regional\"\n  allocation_method   = \"Static\"\n  ip_version          = \"IPv4\"\n  zones               = [\"1\", \"2\", \"3\"]\n  domain_name_label   = local.appGatewayName\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\nresource \"azurerm_application_gateway\" \"network\" {\n  name                = local.appGatewayName\n  resource_group_name = var.resourceGroupName\n  location            = var.location\n\n  depends_on = [\n    azurerm_key_vault_access_policy.user_assigned_identity_keyvault_permissions\n    #,    module.certificate\n  ]\n\n  identity {\n    type         = \"UserAssigned\"\n    identity_ids = [azurerm_user_assigned_identity.user_assigned_identity.id]\n  }\n\n  sku {\n    name = \"WAF_v2\"\n    tier = \"WAF_v2\"\n  }\n\n  ssl_certificate {\n    name                = var.appGatewayFqdn\n    #key_vault_secret_id = \"https://${var.keyVaultName}.vault.azure.net:443/secrets/${local.secretName}\"\n    data = filebase64(local.certDataString)\n    password = local.certPwd\n  }\n\n  gateway_ip_configuration {\n    name      = \"appGatewayIpConfig\"\n    subnet_id = var.subnetId\n  }\n\n  frontend_ip_configuration {\n    name                          = \"appGwPublicFrontendIp\"\n    private_ip_address_allocation = \"Dynamic\"\n    public_ip_address_id          = azurerm_public_ip.public_ip.id\n  }\n\n  frontend_port {\n    name = \"port_80\"\n    port = 80\n  }\n\n  frontend_port {\n    name = \"port_443\"\n    port = 443\n  }\n\n  backend_address_pool {\n    name  = \"apim\"\n    fqdns = [var.primaryBackendendFqdn]\n  }\n\n  backend_address_pool {\n    name  = \"sink-hole\"\n    \n  }\n\n  backend_http_settings {\n    name                                = \"default\"\n    port                                = 80\n    protocol                            = \"Http\"\n    cookie_based_affinity               = \"Disabled\"\n    pick_host_name_from_backend_address = false\n    affinity_cookie_name                = \"ApplicationGatewayAffinity\"\n    request_timeout                     = 20\n\n  }\n\n  backend_http_settings {\n    name                                = \"https\"\n    port                                = 443\n    protocol                            = \"Https\"\n    cookie_based_affinity               = \"Disabled\"\n    #host_name                           = var.primaryBackendendFqdn\n    pick_host_name_from_backend_address = true\n    request_timeout                     = 20\n    probe_name                          = local.httpsBackendProbeName\n  }\n\n  http_listener {\n    name                           = \"default\"\n    frontend_ip_configuration_name = \"appGwPublicFrontendIp\"\n    frontend_port_name             = \"port_80\"\n    protocol                       = \"Http\"\n    require_sni                    = false\n  }\n\n  http_listener {\n    name                           = \"https\"\n    frontend_ip_configuration_name = \"appGwPublicFrontendIp\"\n    frontend_port_name             = \"port_443\"\n    protocol                       = \"Https\"\n    #host_name                      = var.appGatewayFqdn \n    require_sni                    = false\n    ssl_certificate_name           = var.appGatewayFqdn\n    host_names = [ \n      var.appGatewayFqdn,\n      azurerm_public_ip.public_ip.fqdn,\n      local.trafficManagerName\n      \n    ]\n  }\n\n  request_routing_rule {\n    name                       = \"apim\"\n    rule_type                  = \"PathBasedRouting\"\n    http_listener_name         = \"https\"\n    url_path_map_name          = \"urlPathMapApim\"\n    #backend_address_pool_name  = \"apim\"\n    #backend_http_settings_name = \"https\"\n    priority                   = 100\n  }\n\n  url_path_map {\n    default_backend_address_pool_name  = \"apim\"\n    default_backend_http_settings_name = \"https\"\n    name                               = \"urlPathMapApim\"\n    path_rule {\n      backend_address_pool_name  = \"apim\"\n      backend_http_settings_name = \"https\"\n      name                       = \"echo-api\"\n      paths                      = [\"/echo/*\"]\n    }\n    path_rule {\n      backend_address_pool_name  = \"apim\"\n      backend_http_settings_name = \"https\"\n      name                       = \"hello-api\"\n      paths                      = [\"/hello*\"]\n    }\n    path_rule {\n      backend_address_pool_name  = \"apim\"\n      backend_http_settings_name = \"https\"\n      name                       = \"openai-api\"\n      paths                      = [\"/openai/*\"]\n    }\n    path_rule {\n      backend_address_pool_name  = \"sink-hole\"\n      backend_http_settings_name = \"https\"\n      name                       = \"default\"\n      paths                      = [\"/*\"]\n    }\n    path_rule {\n      backend_address_pool_name  = \"apim\"\n      backend_http_settings_name = \"https\"\n      name                       = \"probe-for-traffic-manager\"\n      paths                      = [\"/status-0123456789abcdef\"]\n    }\n  }\n\n  probe {\n    name                                      = \"APIM\"\n    protocol                                  = \"Https\"\n    host                                      = var.primaryBackendendFqdn\n    path                                      = var.probe_url\n    interval                                  = 30\n    timeout                                   = 30\n    unhealthy_threshold                       = 3\n    pick_host_name_from_backend_http_settings = false\n    minimum_servers                           = 0\n\n    match {\n      status_code = [\"200-399\"]\n    }\n  }\n\n  waf_configuration {\n    enabled                  = true\n    firewall_mode            = \"Detection\"\n    rule_set_type            = \"OWASP\"\n    rule_set_version         = \"3.0\"\n    request_body_check       = true\n    max_request_body_size_kb = 128\n    file_upload_limit_mb     = 100\n  }\n\n  enable_http2 = true\n\n  autoscale_configuration {\n    min_capacity = 2\n    max_capacity = 3\n  }\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_gateway/outputs.tf",
    "content": "output \"gw_pip_id\" {\n  value = azurerm_public_ip.public_ip.id\n}\n\noutput \"gw_pip_fqdn\" {\n  value = azurerm_public_ip.public_ip.fqdn\n}\n\noutput \"app_gateway_name\" {\n  value = azurerm_application_gateway.network.name\n}"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_gateway/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus\"\n}\n\nvariable \"resourceSuffix\" {\n  type        = string\n  description = \"A suffix for naming\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"resourceGroupName\" {\n  type        = string\n  description = \"The name of the resource group\"\n}\n\nvariable \"appGatewayCertType\" {\n  description = \"selfsigned will create a self-signed certificate for the APPGATEWAY_FQDN. custom will use an existing certificate in pfx format that needs to be available in the [certs](../../certs) folder and named appgw.pfx \"\n  default     = \"selfsigned\"\n}\n\nvariable \"keyvaultId\" {\n  type        = string\n  description = \"\"\n  default     = null\n}\n\nvariable \"keyVaultName\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"deploymentIdentityName\" {\n  type        = string\n  description = \"deployment identity name\"\n}\n\nvariable \"appGatewayFqdn\" {\n  type        = string\n  description = \"The Azure location to deploy to\"\n  default     = \"apim.example.com\"\n}\n\nvariable \"certificate_path\" {\n  type        = string\n  description = \"\"\n  default     = null\n}\n\nvariable \"certificate_password\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"subnetId\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"primaryBackendendFqdn\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"probe_url\" {\n  type        = string\n  description = \"\"\n  default     = \"/status-0123456789abcdef\"\n}\n\nvariable \"sharedResourceGroupName\" {\n  type        = string\n  description = \"Resource group with deploymnent Identity\"\n}\n\nvariable \"deploymentSubnetId\" {\n  type        = string\n  description = \"The subnet id where the deployment will run\"\n}\n\nvariable \"deploymentStorageName\" {\n  type        = string\n  description = \"The name of the storage account to use for deployment\"\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_private_dns_zone/outputs.tf",
    "content": "output \"id\" {\n  description = \"Specifies the resource id of the private dns zone\"\n  value       = azurerm_private_dns_zone.private_dns_zone.id\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_private_dns_zone/privatednszone.tf",
    "content": "resource \"azurerm_private_dns_zone\" \"private_dns_zone\" {\n  name                = var.name\n  resource_group_name = var.resource_group_name\n  tags                = var.tags\n\n  lifecycle {\n    ignore_changes = [\n      tags\n    ]\n  }\n}\n\nresource \"azurerm_private_dns_zone_virtual_network_link\" \"link\" {\n  name                  = \"link_to_${lower(basename(var.virtual_networks_to_link_id))}\"\n  resource_group_name   = var.resource_group_name\n  private_dns_zone_name = azurerm_private_dns_zone.private_dns_zone.name\n  virtual_network_id    = var.virtual_networks_to_link_id\n\n  lifecycle {\n    ignore_changes = [\n      tags\n    ]\n  }\n}\n\nresource \"azurerm_private_dns_zone_virtual_network_link\" \"second_link\" {\n  count = var.multiRegionEnabled ? 1 : 0 \n  \n  name                  = \"link_to_${lower(basename(var.second_virtual_networks_to_link_id))}\"\n  resource_group_name   = var.resource_group_name\n  private_dns_zone_name = azurerm_private_dns_zone.private_dns_zone.name\n  virtual_network_id    = var.second_virtual_networks_to_link_id\n\n  lifecycle {\n    ignore_changes = [\n      tags\n    ]\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_private_dns_zone/variables.tf",
    "content": "variable \"name\" {\n  description = \"(Required) Specifies the name of the private dns zone\"\n  type        = string\n}\n\nvariable \"resource_group_name\" {\n  description = \"(Required) Specifies the resource group name of the private dns zone\"\n  type        = string\n}\n\nvariable \"tags\" {\n  description = \"(Optional) Specifies the tags of the private dns zone\"\n  default     = {}\n}\n\nvariable \"virtual_networks_to_link_id\" {\n  description = \"(Optional) Specifies the virtual networks id to which create a virtual network link\"\n  type        = string\n}\n\nvariable \"second_virtual_networks_to_link_id\" {\n  description = \"(Optional) Specifies the virtual networks id to which create a virtual network link\"\n  type        = string\n  default     = null\n}\n\nvariable \"multiRegionEnabled\" {\n  description = \"(Optional) Specifies if the multi-region is enabled\"\n  type        = bool\n  default     = false\n}\n\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_shared/azmon.tf",
    "content": "#-------------------------------\n# Creation of log analytics workspace instance\n#-------------------------------\n\nresource \"azurerm_log_analytics_workspace\" \"log_analytics_workspace\" {\n  name                = \"log-${var.resourceSuffix}\"\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n  sku                 = \"PerGB2018\"\n  retention_in_days   = 30\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n\n#-------------------------------\n# Creation of an application insight instance\n#-------------------------------\n\nresource \"azurerm_application_insights\" \"shared_apim_insight\" {\n  name                = \"appi-${var.resourceSuffix}\"\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n  application_type    = \"web\"\n  workspace_id        = azurerm_log_analytics_workspace.log_analytics_workspace.id\n\n  lifecycle {\n    ##prevent_destroy = true\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_shared/outputs.tf",
    "content": "output \"workspaceId\" {\n  description = \"The id of the workspace\"\n  value       = azurerm_log_analytics_workspace.log_analytics_workspace.id\n}\n\noutput \"instrumentationKey\" {\n  description = \"The instrumentation key of the workspace\"\n  value       = azurerm_application_insights.shared_apim_insight.instrumentation_key\n}\n\noutput \"keyVaultId\" {\n  value = azurerm_key_vault.key_vault.id\n}\n\noutput \"keyVaultName\" {\n  value = azurerm_key_vault.key_vault.name\n}\n\noutput \"deploymentIdentityName\" {\n  value = azurerm_user_assigned_identity.privatedeploymanagedidentity.name\n}\n\noutput \"deploymentStorageName\" {\n  value = azurerm_storage_account.privatedeploystorage.name\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_shared/private_endpoint/outputs.tf",
    "content": "output \"id\" {\n  description = \"Specifies the resource id of the private endpoint.\"\n  value       = azurerm_private_endpoint.private_endpoint.id\n}\n\noutput \"private_dns_zone_group\" {\n  description = \"Specifies the private dns zone group of the private endpoint.\"\n  value = azurerm_private_endpoint.private_endpoint.private_dns_zone_group\n}\n\noutput \"private_dns_zone_configs\" {\n  description = \"Specifies the private dns zone(s) configuration\"\n  value = azurerm_private_endpoint.private_endpoint.private_dns_zone_configs\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_shared/private_endpoint/privateendpoint.tf",
    "content": "resource \"azurerm_private_endpoint\" \"private_endpoint\" {\n  name                = var.name\n  location            = var.location\n  resource_group_name = var.resource_group_name\n  subnet_id           = var.subnet_id\n  tags                = var.tags\n\n  private_service_connection {\n    name                           = \"${var.name}Connection\"\n    private_connection_resource_id = var.private_connection_resource_id\n    is_manual_connection           = var.is_manual_connection\n    subresource_names              = try([var.subresource_name], null)\n    request_message                = try(var.request_message, null)\n  }\n\n  private_dns_zone_group {\n    name                 = var.private_dns_zone_group_name\n    private_dns_zone_ids = var.private_dns_zone_group_ids\n  }\n\n  lifecycle {\n    ignore_changes = [\n      tags\n    ]\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_shared/private_endpoint/variables.tf",
    "content": "variable \"name\" {\n  description = \"(Required) Specifies the name of the private endpoint. Changing this forces a new resource to be created.\"\n  type        = string\n}\n\nvariable \"resource_group_name\" {\n  description = \"(Required) The name of the resource group. Changing this forces a new resource to be created.\"\n  type        = string\n}\n\nvariable \"private_connection_resource_id\" {\n  description = \"(Required) Specifies the resource id of the private link service\"\n  type        = string \n}\n\nvariable \"location\" {\n  description = \"(Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created.\"\n  type        = string\n}\n\nvariable \"subnet_id\" {\n  description = \"(Required) Specifies the resource id of the subnet\"\n  type        = string\n}\n\nvariable \"is_manual_connection\" {\n  description = \"(Optional) Specifies whether the private endpoint connection requires manual approval from the remote resource owner.\"\n  type        = string\n  default     = false  \n}\n\nvariable \"subresource_name\" {\n  description = \"(Optional) Specifies a subresource name which the Private Endpoint is able to connect to.\"\n  type        = string\n  default     = null\n}\n\nvariable \"request_message\" {\n  description = \"(Optional) Specifies a message passed to the owner of the remote resource when the private endpoint attempts to establish the connection to the remote resource.\"\n  type        = string\n  default     = null \n}\n\nvariable \"private_dns_zone_group_name\" {\n  description = \"(Required) Specifies the Name of the Private DNS Zone Group. Changing this forces a new private_dns_zone_group resource to be created.\"\n  type        = string\n}\n\nvariable \"private_dns_zone_group_ids\" {\n  description = \"(Required) Specifies the list of Private DNS Zones to include within the private_dns_zone_group.\"\n  type        = list(string)\n}\n\nvariable \"tags\" {\n  description = \"(Optional) Specifies the tags of the network security group\"\n  default     = {}\n}\n\nvariable \"private_dns\" {\n  default = {}\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_shared/privatedeploy.tf",
    "content": "\nresource \"azurerm_storage_account\" \"privatedeploystorage\" {\n  name                     = var.storage_account_name\n  location                 = var.location\n  resource_group_name      = var.resourceGroupName\n  account_tier             = \"Standard\"\n  account_replication_type = \"LRS\"\n\n  allow_nested_items_to_be_public = false\n  shared_access_key_enabled = false\n\n  network_rules {\n    bypass         = [\"AzureServices\"]\n    default_action = \"Deny\"\n\n    virtual_network_subnet_ids = [\n      var.deploymentSubnetId\n    ]\n  }\n}\n\n# Resource: User Assigned Identity\nresource \"azurerm_user_assigned_identity\" \"privatedeploymanagedidentity\" {\n  name                = \"mi-deploy-${var.resourceSuffix}\"\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n}\n\n# Resource: Role Assignment\nresource \"azurerm_role_assignment\" \"privatedeploystorageroleassignment\" {\n  scope                = azurerm_storage_account.privatedeploystorage.id\n  role_definition_name = \"Storage File Data Privileged Contributor\"\n  principal_id         = azurerm_user_assigned_identity.privatedeploymanagedidentity.principal_id\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_shared/shared.tf",
    "content": "data \"azurerm_client_config\" \"current\" {}\n\n#-------------------------------\n# Creation of a key vault instance\n#-------------------------------\n\nresource \"azurerm_key_vault\" \"key_vault\" {\n  name                = var.keyVaultName\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n  tenant_id           = data.azurerm_client_config.current.tenant_id\n  sku_name            = var.keyVaultSku\n  # -> Bicep has keyvault as private, should we change this?\n  # -> This will need the certificate to be created through a azurerm_template_deployment resource\n  public_network_access_enabled = false\n  network_acls {\n    bypass         = \"AzureServices\"\n    default_action = \"Deny\"\n  }\n}\n\nlocals {\n  # deployment_client_ids = toset(\n  #   concat(\n  #     [data.azurerm_client_config.current.object_id],\n  #     var.additionalClientIds\n  #   )\n  # )\n  privateEndpoint_keyvault_Name = \"pep-kv-${var.resourceSuffix}\"\n  apim_cs_vnet_name             = \"vnet-apim-cs-${var.resourceSuffix}\"\n  networkingResourceGroupName   = \"rg-networking-${var.resourceSuffix}\"\n  private_endpoint_subnet_name  = \"snet-prep-${var.resourceSuffix}\"\n}\n\n# created as a seperate resource, as managed identity uses the azurerm_key_vault_access_policy as well. See note at https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_access_policy\nresource \"azurerm_key_vault_access_policy\" \"deployment_spn_access_policy\" {\n  key_vault_id = azurerm_key_vault.key_vault.id\n  tenant_id    = data.azurerm_client_config.current.tenant_id\n  object_id    = data.azurerm_client_config.current.object_id\n\n  key_permissions = [\n    \"Get\",\n  ]\n\n  secret_permissions = [\n    \"Get\",\n  ]\n\n  storage_permissions = [\n    \"Get\",\n  ]\n  certificate_permissions = [\n    \"Import\",\n    \"Get\",\n    \"List\",\n    \"Update\",\n    \"Create\"\n  ]\n}\n\ndata \"azurerm_virtual_network\" \"apim_cs_vnet\" {\n  name                = local.apim_cs_vnet_name\n  resource_group_name = local.networkingResourceGroupName\n}\n\ndata \"azurerm_subnet\" \"private_endpoint_subnet\" {\n  name                 = local.private_endpoint_subnet_name\n  resource_group_name  = local.networkingResourceGroupName\n  virtual_network_name = local.apim_cs_vnet_name\n}\n\n\nmodule \"keyvault_private_endpoint\" {\n  source                         = \"./private_endpoint\"\n  name                           = local.privateEndpoint_keyvault_Name\n  location                       = var.location\n  resource_group_name            = local.networkingResourceGroupName\n  subnet_id                      = data.azurerm_subnet.private_endpoint_subnet.id\n  private_connection_resource_id = azurerm_key_vault.key_vault.id\n  is_manual_connection           = false\n  subresource_name               = \"vault\"\n  private_dns_zone_group_name    = \"KeyVaultPrivateDnsZoneGroup\"\n  private_dns_zone_group_ids     = [var.keyVaultPrivateDnsZoneId]\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_shared/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus\"\n}\n\nvariable \"resourceSuffix\" {\n  type        = string\n  description = \"A suffix for naming\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"resourceGroupName\" {\n  type        = string\n  description = \"The name of the resource group\"\n}\n\nvariable \"keyVaultName\" {\n  type        = string\n  description = \"The name of the Key Vault\"\n}\n\nvariable \"keyVaultSku\" {\n  type        = string\n  description = \"The Name of the SKU used for this Key Vault. Possible values are standard and premium\"\n  default     = \"standard\"\n}\n\nvariable \"keyVaultPrivateDnsZoneId\" {\n  type        = string\n  description = \"The ID of the private DNS zone for the Key Vault\"\n}\n\nvariable \"additionalClientIds\" {\n  description = \"List of additional clients to add to the Key Vault access policy.\"\n  type        = list(string)\n  default     = []\n}\n\nvariable \"deploymentSubnetId\" {\n  type = string\n}\n\nvariable \"storage_account_name\" {\n  type = string\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_traffic_manager/outputs.tf",
    "content": "output \"fqdn\" {\n  value = azurerm_traffic_manager_profile.tm1.fqdn\n}"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_traffic_manager/trafficmanager.tf",
    "content": "resource \"azurerm_traffic_manager_profile\" \"tm1\" {\n  name                   = var.name\n  resource_group_name    = var.resourceGroupName\n  traffic_routing_method = \"Performance\"\n  dns_config {\n    relative_name = var.name\n    ttl           = 60\n  }\n  monitor_config {\n    expected_status_code_ranges = [\"200-299\"]\n    path                        = var.probe_url\n    port                        = 443\n    protocol                    = \"HTTPS\"\n  }\n}\n\nresource \"azurerm_traffic_manager_azure_endpoint\" \"primaryEndpoint\" {\n  name                 = var.primaryName\n  profile_id           = azurerm_traffic_manager_profile.tm1.id\n  always_serve_enabled = false\n  weight               = 100\n  target_resource_id   = var.primaryPublicIpId\n\n\n\n}\n\nresource \"azurerm_traffic_manager_azure_endpoint\" \"secondaryEndpoint\" {\n  name                 = var.secondaryName\n  profile_id           = azurerm_traffic_manager_profile.tm1.id\n  always_serve_enabled = false\n  weight               = 100\n  target_resource_id   = var.secondaryPublicIpId\n}"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/multi_traffic_manager/variables.tf",
    "content": "variable \"name\" {\n  type        = string\n  description = \"The traffic manager profile name\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"resourceGroupName\" {\n  type        = string\n  description = \"The name of the resource group\"\n}\n\n\nvariable \"primaryName\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"primaryPublicIpId\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"secondaryName\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"secondaryPublicIpId\" {\n  type        = string\n  description = \"\"\n}\n\nvariable \"probe_url\" {\n  type        = string\n  description = \"\"\n  default     = \"/status-0123456789abcdef\"\n}\n\n\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/networking/networking.tf",
    "content": "locals {\n  apim_cs_vnet_name            = \"vnet-apim-cs-${var.resourceSuffix}\"\n  appgateway_subnet_name       = \"snet-apgw-${var.resourceSuffix}\"\n  deploy_subnet_name           = \"snet-deploy-${var.resourceSuffix}\"\n  appgateway_snnsg             = \"nsg-apgw-${var.resourceSuffix}\"\n  private_endpoint_subnet_name = \"snet-prep-${var.resourceSuffix}\"\n  private_endpoint_snnsg       = \"nsg-prep-${var.resourceSuffix}\"\n  apim_subnet_name             = \"snet-apim-${var.resourceSuffix}\"\n  owner                        = \"APIM Const Set\"\n  appgateway_public_ipname     = \"pip-appgw-${var.resourceSuffix}\"\n  apim_snnsg                   = \"nsg-apim-${var.resourceSuffix}\"\n}\n\nresource \"azurerm_network_security_group\" \"appgateway_nsg\" {\n  name                = local.appgateway_snnsg\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n\n  security_rule {\n    name                       = \"AllowHealthProbesInbound\"\n    priority                   = 100\n    protocol                   = \"*\"\n    destination_port_range     = \"65200-65535\"\n    access                     = \"Allow\"\n    direction                  = \"Inbound\"\n    source_port_range          = \"*\"\n    source_address_prefix      = \"GatewayManager\"\n    destination_address_prefix = \"*\"\n  }\n\n  security_rule {\n    name                       = \"AllowTLSInbound\"\n    priority                   = 110\n    protocol                   = \"Tcp\"\n    destination_port_range     = \"443\"\n    access                     = \"Allow\"\n    direction                  = \"Inbound\"\n    source_port_range          = \"*\"\n    source_address_prefix      = \"*\"\n    destination_address_prefix = \"*\"\n  }\n\n  security_rule {\n    name                       = \"AllowHTTPInbound\"\n    priority                   = 111\n    protocol                   = \"Tcp\"\n    destination_port_range     = \"80\"\n    access                     = \"Allow\"\n    direction                  = \"Inbound\"\n    source_port_range          = \"*\"\n    source_address_prefix      = \"*\"\n    destination_address_prefix = \"*\"\n  }\n\n  security_rule {\n    name                       = \"AllowAzureLoadBalancerInbound\"\n    priority                   = 121\n    protocol                   = \"Tcp\"\n    destination_port_range     = \"*\"\n    access                     = \"Allow\"\n    direction                  = \"Inbound\"\n    source_port_range          = \"*\"\n    source_address_prefix      = \"AzureLoadBalancer\"\n    destination_address_prefix = \"*\"\n  }\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_network_security_group\" \"apim_snnsg_nsg\" {\n  name                = local.apim_snnsg\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n\n  security_rule {\n    name                       = \"AllowApimVnetInbound\"\n    priority                   = 2000\n    protocol                   = \"Tcp\"\n    destination_port_range     = \"3443\"\n    access                     = \"Allow\"\n    direction                  = \"Inbound\"\n    source_port_range          = \"*\"\n    source_address_prefix      = \"ApiManagement\"\n    destination_address_prefix = \"VirtualNetwork\"\n  }\n\n  security_rule {\n    name                       = \"apim-azure-infra-lb\"\n    priority                   = 2010\n    protocol                   = \"Tcp\"\n    destination_port_range     = \"6390\"\n    access                     = \"Allow\"\n    direction                  = \"Inbound\"\n    source_port_range          = \"*\"\n    source_address_prefix      = \"AzureLoadBalancer\"\n    destination_address_prefix = \"VirtualNetwork\"\n  }\n\n  security_rule {\n    name                       = \"apim-azure-storage\"\n    priority                   = 2000\n    protocol                   = \"Tcp\"\n    destination_port_range     = \"443\"\n    access                     = \"Allow\"\n    direction                  = \"Outbound\"\n    source_port_range          = \"*\"\n    source_address_prefix      = \"VirtualNetwork\"\n    destination_address_prefix = \"Storage\"\n  }\n\n  security_rule {\n    name                       = \"apim-azure-sql\"\n    priority                   = 2010\n    protocol                   = \"Tcp\"\n    destination_port_range     = \"1443\"\n    access                     = \"Allow\"\n    direction                  = \"Outbound\"\n    source_port_range          = \"*\"\n    source_address_prefix      = \"VirtualNetwork\"\n    destination_address_prefix = \"SQL\"\n  }\n\n  security_rule {\n    name                       = \"apim-azure-kv\"\n    priority                   = 2020\n    protocol                   = \"Tcp\"\n    destination_port_range     = \"443\"\n    access                     = \"Allow\"\n    direction                  = \"Outbound\"\n    source_port_range          = \"*\"\n    source_address_prefix      = \"VirtualNetwork\"\n    destination_address_prefix = \"AzureKeyVault\"\n  }\n\n  security_rule {\n    name                       = \"apim-azure-monitor\"\n    priority                   = 2030\n    protocol                   = \"Tcp\"\n    destination_port_range     = \"443\"\n    access                     = \"Allow\"\n    direction                  = \"Outbound\"\n    source_port_range          = \"*\"\n    source_address_prefix      = \"VirtualNetwork\"\n    destination_address_prefix = \"AzureMonitor\"\n  }\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_network_security_group\" \"private_endpoint_snnsg_nsg\" {\n  name                = local.private_endpoint_snnsg\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_virtual_network\" \"apim_cs_vnet\" {\n  name                = local.apim_cs_vnet_name\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n  address_space       = [var.apimCSVNetNameAddressPrefix]\n\n  tags = {\n    Owner = local.owner\n  }\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_subnet\" \"appgateway_subnet\" {\n  name                 = local.appgateway_subnet_name\n  resource_group_name  = var.resourceGroupName\n  virtual_network_name = azurerm_virtual_network.apim_cs_vnet.name\n  address_prefixes     = [var.appGatewayAddressPrefix]\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_subnet_network_security_group_association\" \"appgateway_subnet\" {\n  subnet_id                 = azurerm_subnet.appgateway_subnet.id\n  network_security_group_id = azurerm_network_security_group.appgateway_nsg.id\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_subnet\" \"private_endpoint_subnet\" {\n  name                 = local.private_endpoint_subnet_name\n  resource_group_name  = var.resourceGroupName\n  virtual_network_name = azurerm_virtual_network.apim_cs_vnet.name\n  address_prefixes     = [var.privateEndpointAddressPrefix]\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_subnet_network_security_group_association\" \"private_endpoint_subnet\" {\n  subnet_id                 = azurerm_subnet.private_endpoint_subnet.id\n  network_security_group_id = azurerm_network_security_group.private_endpoint_snnsg_nsg.id\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_subnet\" \"deploy_subnet\" {\n  name                 = local.deploy_subnet_name\n  resource_group_name  = var.resourceGroupName\n  virtual_network_name = azurerm_virtual_network.apim_cs_vnet.name\n  address_prefixes     = [var.deploymentAddressPrefix]\n\n  service_endpoints = [\"Microsoft.Storage\"]\n\n  delegation {\n    name = \"Microsoft.ContainerInstance.containerGroups\"\n\n    service_delegation {\n      name    = \"Microsoft.ContainerInstance/containerGroups\"\n      actions = [\"Microsoft.Network/virtualNetworks/subnets/action\"]\n    }\n  }\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_subnet\" \"apim_subnet\" {\n  name                 = local.apim_subnet_name\n  resource_group_name  = var.resourceGroupName\n  virtual_network_name = azurerm_virtual_network.apim_cs_vnet.name\n  address_prefixes     = [var.apimAddressPrefix]\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\nresource \"azurerm_subnet_network_security_group_association\" \"apim_subnet\" {\n  subnet_id                 = azurerm_subnet.apim_subnet.id\n  network_security_group_id = azurerm_network_security_group.apim_snnsg_nsg.id\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/networking/outputs.tf",
    "content": "output \"apimSubnetId\" {\n  value = azurerm_subnet.apim_subnet.id\n}\n\noutput \"appGatewaySubnetId\" {\n  value = azurerm_subnet.appgateway_subnet.id\n}\n\noutput \"apimVnetId\" {\n  value = azurerm_virtual_network.apim_cs_vnet.id\n}\n\noutput \"deploymentSubnetId\" {\n  value = azurerm_subnet.deploy_subnet.id\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/networking/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus\"\n}\n\nvariable \"resourceSuffix\" {\n  type        = string\n  description = \"A suffix for naming\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"resourceGroupName\" {\n  type        = string\n  description = \"The name of the resource group\"\n}\n\nvariable \"apimCSVNetNameAddressPrefix\" {\n  description = \"APIM CSV Net Name Address Prefix\"\n  type        = string\n}\n\nvariable \"appGatewayAddressPrefix\" {\n  description = \"App Gateway Address Prefix\"\n  type        = string\n}\n\nvariable \"apimAddressPrefix\" {\n  description = \"APIM Address Prefix\"\n  type        = string\n}\n\nvariable \"privateEndpointAddressPrefix\" {\n  description = \"Private Endpoint Address Prefix\"\n  type        = string\n}\n\nvariable \"deploymentAddressPrefix\" {\n  description = \"Deployment Address Prefix\"\n  type        = string\n}\n\n\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/shared/azmon.tf",
    "content": "#-------------------------------\n# Creation of log analytics workspace instance\n#-------------------------------\n\nresource \"azurerm_log_analytics_workspace\" \"log_analytics_workspace\" {\n  name                = \"log-${var.resourceSuffix}\"\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n  sku                 = \"PerGB2018\"\n  retention_in_days   = 30\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n\n#-------------------------------\n# Creation of an application insight instance\n#-------------------------------\n\nresource \"azurerm_application_insights\" \"shared_apim_insight\" {\n  name                = \"appi-${var.resourceSuffix}\"\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n  application_type    = \"web\"\n  workspace_id        = azurerm_log_analytics_workspace.log_analytics_workspace.id\n\n  lifecycle {\n    #prevent_destroy = true\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/shared/outputs.tf",
    "content": "output \"workspaceId\" {\n  description = \"The id of the workspace\"\n  value       = azurerm_log_analytics_workspace.log_analytics_workspace.id\n}\n\noutput \"instrumentationKey\" {\n  description = \"The instrumentation key of the workspace\"\n  value       = azurerm_application_insights.shared_apim_insight.instrumentation_key\n}\n\noutput \"keyVaultId\" {\n  value = azurerm_key_vault.key_vault.id\n}\n\noutput \"keyVaultName\" {\n  value = azurerm_key_vault.key_vault.name\n}\n\noutput \"deploymentIdentityName\" {\n  value = azurerm_user_assigned_identity.privatedeploymanagedidentity.name\n}\n\noutput \"deploymentStorageName\" {\n  value = azurerm_storage_account.privatedeploystorage.name\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/shared/private_dns_zone/outputs.tf",
    "content": "output \"id\" {\n  description = \"Specifies the resource id of the private dns zone\"\n  value       = azurerm_private_dns_zone.private_dns_zone.id\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/shared/private_dns_zone/privatednszone.tf",
    "content": "resource \"azurerm_private_dns_zone\" \"private_dns_zone\" {\n  name                = var.name\n  resource_group_name = var.resource_group_name\n  tags                = var.tags\n\n  lifecycle {\n    ignore_changes = [\n      tags\n    ]\n  }\n}\n\nresource \"azurerm_private_dns_zone_virtual_network_link\" \"link\" {\n  name                  = \"link_to_${lower(basename(var.virtual_networks_to_link_id))}\"\n  resource_group_name   = var.resource_group_name\n  private_dns_zone_name = azurerm_private_dns_zone.private_dns_zone.name\n  virtual_network_id    = var.virtual_networks_to_link_id\n\n  lifecycle {\n    ignore_changes = [\n      tags\n    ]\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/shared/private_dns_zone/variables.tf",
    "content": "variable \"name\" {\n  description = \"(Required) Specifies the name of the private dns zone\"\n  type        = string\n}\n\nvariable \"resource_group_name\" {\n  description = \"(Required) Specifies the resource group name of the private dns zone\"\n  type        = string\n}\n\nvariable \"tags\" {\n  description = \"(Optional) Specifies the tags of the private dns zone\"\n  default     = {}\n}\n\nvariable \"virtual_networks_to_link_id\" {\n  description = \"(Optional) Specifies the virtual networks id to which create a virtual network link\"\n  type        = string\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/shared/private_endpoint/outputs.tf",
    "content": "output \"id\" {\n  description = \"Specifies the resource id of the private endpoint.\"\n  value       = azurerm_private_endpoint.private_endpoint.id\n}\n\noutput \"private_dns_zone_group\" {\n  description = \"Specifies the private dns zone group of the private endpoint.\"\n  value = azurerm_private_endpoint.private_endpoint.private_dns_zone_group\n}\n\noutput \"private_dns_zone_configs\" {\n  description = \"Specifies the private dns zone(s) configuration\"\n  value = azurerm_private_endpoint.private_endpoint.private_dns_zone_configs\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/shared/private_endpoint/privateendpoint.tf",
    "content": "resource \"azurerm_private_endpoint\" \"private_endpoint\" {\n  name                = var.name\n  location            = var.location\n  resource_group_name = var.resource_group_name\n  subnet_id           = var.subnet_id\n  tags                = var.tags\n\n  private_service_connection {\n    name                           = \"${var.name}Connection\"\n    private_connection_resource_id = var.private_connection_resource_id\n    is_manual_connection           = var.is_manual_connection\n    subresource_names              = try([var.subresource_name], null)\n    request_message                = try(var.request_message, null)\n  }\n\n  private_dns_zone_group {\n    name                 = var.private_dns_zone_group_name\n    private_dns_zone_ids = var.private_dns_zone_group_ids\n  }\n\n  lifecycle {\n    ignore_changes = [\n      tags\n    ]\n  }\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/shared/private_endpoint/variables.tf",
    "content": "variable \"name\" {\n  description = \"(Required) Specifies the name of the private endpoint. Changing this forces a new resource to be created.\"\n  type        = string\n}\n\nvariable \"resource_group_name\" {\n  description = \"(Required) The name of the resource group. Changing this forces a new resource to be created.\"\n  type        = string\n}\n\nvariable \"private_connection_resource_id\" {\n  description = \"(Required) Specifies the resource id of the private link service\"\n  type        = string \n}\n\nvariable \"location\" {\n  description = \"(Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created.\"\n  type        = string\n}\n\nvariable \"subnet_id\" {\n  description = \"(Required) Specifies the resource id of the subnet\"\n  type        = string\n}\n\nvariable \"is_manual_connection\" {\n  description = \"(Optional) Specifies whether the private endpoint connection requires manual approval from the remote resource owner.\"\n  type        = string\n  default     = false  \n}\n\nvariable \"subresource_name\" {\n  description = \"(Optional) Specifies a subresource name which the Private Endpoint is able to connect to.\"\n  type        = string\n  default     = null\n}\n\nvariable \"request_message\" {\n  description = \"(Optional) Specifies a message passed to the owner of the remote resource when the private endpoint attempts to establish the connection to the remote resource.\"\n  type        = string\n  default     = null \n}\n\nvariable \"private_dns_zone_group_name\" {\n  description = \"(Required) Specifies the Name of the Private DNS Zone Group. Changing this forces a new private_dns_zone_group resource to be created.\"\n  type        = string\n}\n\nvariable \"private_dns_zone_group_ids\" {\n  description = \"(Required) Specifies the list of Private DNS Zones to include within the private_dns_zone_group.\"\n  type        = list(string)\n}\n\nvariable \"tags\" {\n  description = \"(Optional) Specifies the tags of the network security group\"\n  default     = {}\n}\n\nvariable \"private_dns\" {\n  default = {}\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/shared/privatedeploy.tf",
    "content": "\nresource \"azurerm_storage_account\" \"privatedeploystorage\" {\n  name                     = var.storage_account_name\n  location                 = var.location\n  resource_group_name      = var.resourceGroupName\n  account_tier             = \"Standard\"\n  account_replication_type = \"LRS\"\n\n  allow_nested_items_to_be_public = false\n  shared_access_key_enabled = false\n\n  network_rules {\n    bypass         = [\"AzureServices\"]\n    default_action = \"Deny\"\n\n    virtual_network_subnet_ids = [\n      var.deploymentSubnetId\n    ]\n  }\n}\n\n# Resource: User Assigned Identity\nresource \"azurerm_user_assigned_identity\" \"privatedeploymanagedidentity\" {\n  name                = \"mi-deploy-${var.resourceSuffix}\"\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n}\n\n# Resource: Role Assignment\nresource \"azurerm_role_assignment\" \"privatedeploystorageroleassignment\" {\n  scope                = azurerm_storage_account.privatedeploystorage.id\n  role_definition_name = \"Storage File Data Privileged Contributor\"\n  principal_id         = azurerm_user_assigned_identity.privatedeploymanagedidentity.principal_id\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/shared/shared.tf",
    "content": "data \"azurerm_client_config\" \"current\" {}\n\n#-------------------------------\n# Creation of a key vault instance\n#-------------------------------\n\nresource \"azurerm_key_vault\" \"key_vault\" {\n  name                = var.keyVaultName\n  location            = var.location\n  resource_group_name = var.resourceGroupName\n  tenant_id           = data.azurerm_client_config.current.tenant_id\n  sku_name            = var.keyVaultSku\n  # -> Bicep has keyvault as private, should we change this?\n  # -> This will need the certificate to be created through a azurerm_template_deployment resource\n  public_network_access_enabled = false\n  network_acls {\n    bypass         = \"AzureServices\"\n    default_action = \"Deny\"\n  }\n}\n\nlocals {\n  # deployment_client_ids = toset(\n  #   concat(\n  #     [data.azurerm_client_config.current.object_id],\n  #     var.additionalClientIds\n  #   )\n  # )\n  privateEndpoint_keyvault_Name = \"pep-kv-${var.resourceSuffix}\"\n  apim_cs_vnet_name             = \"vnet-apim-cs-${var.resourceSuffix}\"\n  networkingResourceGroupName   = \"rg-networking-${var.resourceSuffix}\"\n  private_endpoint_subnet_name  = \"snet-prep-${var.resourceSuffix}\"\n}\n\n# created as a seperate resource, as managed identity uses the azurerm_key_vault_access_policy as well. See note at https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_access_policy\nresource \"azurerm_key_vault_access_policy\" \"deployment_spn_access_policy\" {\n  key_vault_id = azurerm_key_vault.key_vault.id\n  tenant_id    = data.azurerm_client_config.current.tenant_id\n  object_id    = data.azurerm_client_config.current.object_id\n\n  key_permissions = [\n    \"Get\",\n  ]\n\n  secret_permissions = [\n    \"Get\",\n  ]\n\n  storage_permissions = [\n    \"Get\",\n  ]\n  certificate_permissions = [\n    \"Import\",\n    \"Get\",\n    \"List\",\n    \"Update\",\n    \"Create\"\n  ]\n}\n\ndata \"azurerm_virtual_network\" \"apim_cs_vnet\" {\n  name                = local.apim_cs_vnet_name\n  resource_group_name = local.networkingResourceGroupName\n}\n\ndata \"azurerm_subnet\" \"private_endpoint_subnet\" {\n  name                 = local.private_endpoint_subnet_name\n  resource_group_name  = local.networkingResourceGroupName\n  virtual_network_name = local.apim_cs_vnet_name\n}\n\nmodule \"keyvault_dns_zone\" {\n  source                      = \"./private_dns_zone\"\n  name                        = \"privatelink.vaultcore.azure.net\"\n  resource_group_name         = local.networkingResourceGroupName\n  virtual_networks_to_link_id = data.azurerm_virtual_network.apim_cs_vnet.id\n}\n\nmodule \"keyvault_private_endpoint\" {\n  source                         = \"./private_endpoint\"\n  name                           = local.privateEndpoint_keyvault_Name\n  location                       = var.location\n  resource_group_name            = local.networkingResourceGroupName\n  subnet_id                      = data.azurerm_subnet.private_endpoint_subnet.id\n  private_connection_resource_id = azurerm_key_vault.key_vault.id\n  is_manual_connection           = false\n  subresource_name               = \"vault\"\n  private_dns_zone_group_name    = \"KeyVaultPrivateDnsZoneGroup\"\n  private_dns_zone_group_ids     = [module.keyvault_dns_zone.id]\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/modules/shared/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus\"\n}\n\nvariable \"resourceSuffix\" {\n  type        = string\n  description = \"A suffix for naming\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"resourceGroupName\" {\n  type        = string\n  description = \"The name of the resource group\"\n}\n\nvariable \"keyVaultName\" {\n  type        = string\n  description = \"The name of the Key Vault\"\n}\n\nvariable \"keyVaultSku\" {\n  type        = string\n  description = \"The Name of the SKU used for this Key Vault. Possible values are standard and premium\"\n  default     = \"standard\"\n}\n\nvariable \"additionalClientIds\" {\n  description = \"List of additional clients to add to the Key Vault access policy.\"\n  type        = list(string)\n  default     = []\n}\n\nvariable \"deploymentSubnetId\" {\n  type = string\n}\n\nvariable \"storage_account_name\" {\n  type = string\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/multi-region/multi-region-main.tf",
    "content": "resource \"random_string\" \"suffix\" {\n  length = 5\n  upper = false\n  special = false\n}\n\nlocals {\n  resourceSuffix              = \"${var.workloadName}-${var.environment}-${var.location}-${var.identifier}\"\n  networkingResourceGroupName = \"rg-networking-${local.resourceSuffix}\"\n  sharedResourceGroupName     = \"rg-shared-${local.resourceSuffix}\"\n  apimResourceGroupName       = \"rg-apim-${local.resourceSuffix}\"\n  keyVaultName                = substr(lower(replace(\"kv-${var.workloadName}${random_string.suffix.result}\", \"-\", \"\")), 0, 23)\n  storageAccountName          = substr(lower(replace(\"sadep${var.workloadName}${random_string.suffix.result}\", \"-\", \"\")), 0, 21)\n  \n  # to support multi-region\n  resourceSuffix2nd              = \"${var.workloadName}-${var.environment}-${var.locationSecond}-${var.identifier}\"\n  networkingResourceGroupName2nd = \"rg-networking-${local.resourceSuffix2nd}\"\n  sharedResourceGroupName2nd     = \"rg-shared-${local.resourceSuffix2nd}\"\n  apimResourceGroupName2nd       = \"rg-apim-${local.resourceSuffix2nd}\"  \n  keyVaultName2nd                = substr(lower(replace(\"kv-${var.workloadName}${random_string.suffix.result}-2nd\", \"-\", \"\")), 0, 23)\n  storageAccountName2nd          = substr(lower(replace(\"sadep2${var.workloadName}${random_string.suffix.result}\", \"-\", \"\")), 0, 21)\n  tags = {}\n\n}\n\n\n# Global Infra\nmodule \"keyvault_dns_zone_multi\" {\n  depends_on          = [module.networking]\n  source              = \"../modules/multi_private_dns_zone\"\n  name                = \"privatelink.vaultcore.azure.net\"\n  resource_group_name = azurerm_resource_group.networking.name\n  virtual_networks_to_link_id = module.networking.apimVnetId\n  second_virtual_networks_to_link_id = module.networkingSecond.apimVnetId\n  multiRegionEnabled = var.multiRegionEnabled  \n}\n\n\n# Primary Region\nresource \"azurerm_resource_group\" \"networking\" {\n  name     = local.networkingResourceGroupName\n  location = var.location\n  tags     = local.tags\n}\n\nresource \"azurerm_resource_group\" \"shared\" {\n  name     = local.sharedResourceGroupName\n  location = var.location\n  tags     = local.tags\n}\n\nresource \"azurerm_resource_group\" \"apim\" {\n  name     = local.apimResourceGroupName\n  location = var.location\n  tags     = local.tags\n}\n\nmodule \"networking\" {\n  depends_on                   = [azurerm_resource_group.networking]\n  source                       = \"../modules/networking\"\n  location                     = var.location\n  resourceGroupName            = azurerm_resource_group.networking.name\n  resourceSuffix               = local.resourceSuffix\n  environment                  = var.environment\n  apimAddressPrefix            = var.apimAddressPrefix\n  appGatewayAddressPrefix      = var.appGatewayAddressPrefix\n  apimCSVNetNameAddressPrefix  = var.apimCSVNetNameAddressPrefix\n  privateEndpointAddressPrefix = var.privateEndpointAddressPrefix\n  deploymentAddressPrefix      = var.deploymentAddressPrefix\n}\n\n\nmodule \"shared\" {\n  depends_on           = [module.networking]\n  source               = \"../modules/multi_shared\"\n  location             = var.location\n  resourceGroupName    = azurerm_resource_group.shared.name\n  resourceSuffix       = local.resourceSuffix\n  additionalClientIds  = var.additionalClientIds\n  keyVaultName         = local.keyVaultName\n  keyVaultSku              = var.keyVaultSku\n  keyVaultPrivateDnsZoneId = module.keyvault_dns_zone_multi.id\n  deploymentSubnetId   = module.networking.deploymentSubnetId\n  storage_account_name = local.storageAccountName\n}\n\nmodule \"apim\" {\n  depends_on              = [module.shared, module.networking]\n  source                  = \"../modules/multi_apim\"\n  location                = var.location\n  resourceGroupName       = azurerm_resource_group.apim.name\n  resourceSuffix          = local.resourceSuffix\n  environment             = var.environment\n  apimSubnetId            = module.networking.apimSubnetId\n  instrumentationKey      = module.shared.instrumentationKey\n  workspaceId             = module.shared.workspaceId\n  sharedResourceGroupName = azurerm_resource_group.shared.name\n  keyVaultName            = local.keyVaultName\n  \n  apimSecondSubnetId      = module.networkingSecond.apimSubnetId\n  zoneRedundantEnabled    = var.zoneRedundantEnabled\n  locationSecond          = var.locationSecond\n}\n\nmodule \"gateway\" {\n  depends_on              = [module.networking, module.apim, module.shared]\n  source                  = \"../modules/multi_gateway\"\n  location                = var.location\n  resourceGroupName       = azurerm_resource_group.networking.name\n  resourceSuffix          = local.resourceSuffix\n  environment             = var.environment\n  appGatewayFqdn          = var.appGatewayFqdn\n  appGatewayCertType      = var.appGatewayCertType\n  certificate_password    = var.certificatePassword\n  certificate_path        = var.certificatePath\n  subnetId                = module.networking.appGatewaySubnetId\n  primaryBackendendFqdn   = module.apim.apim_regional_url_1\n  keyvaultId              = module.shared.keyVaultId\n  keyVaultName            = module.shared.keyVaultName\n  sharedResourceGroupName = azurerm_resource_group.shared.name\n  deploymentIdentityName  = module.shared.deploymentIdentityName\n  deploymentSubnetId      = module.networking.deploymentSubnetId\n  deploymentStorageName   = module.shared.deploymentStorageName\n}\n\n\nmodule \"dns\" {\n  depends_on        = [module.apim, module.gateway]\n  source            = \"../modules/dns\"\n  location          = var.location\n  resourceGroupName = azurerm_resource_group.networking.name\n  resourceSuffix    = local.resourceSuffix\n  environment       = var.environment\n  apimName          = module.apim.apimName\n  apimPrivateIp     = module.apim.apimPrivateIp\n  apimVnetId        = module.networking.apimVnetId\n  \n}\n\nmodule \"dnsRegional\" {\n  depends_on              = [module.apim, module.gatewaySecond]\n\n  source                  = \"../modules/multi_apim-dns-regional\"\n  location                = var.location\n  resourceGroupName       = azurerm_resource_group.networking.name\n  resourceSuffix          = local.resourceSuffix\n  environment             = var.environment\n  apimRegionalName        = module.apim.apim_regional_name_1\n  apimPrivateIp           = module.apim.apim_regional_IP_1\n  apimVnetId              = module.networking.apimVnetId\n  apimSecondRegionalName  = module.apim.apim_regional_name_2\n  apimSecondPrivateIp     = module.apim.apim_regional_IP_2\n  apimSecondVnetId        = module.networkingSecond.apimVnetId\n}\n\n# Secondary Region\nresource \"azurerm_resource_group\" \"networkingSecond\" {\n  name     = \"${local.networkingResourceGroupName2nd}\"\n  location = var.locationSecond\n  tags     = local.tags\n}\n\nresource \"azurerm_resource_group\" \"sharedSecond\" {\n  name     = \"${local.sharedResourceGroupName2nd}\"\n  location = var.locationSecond\n  tags     = local.tags\n}\n\nresource \"azurerm_resource_group\" \"apimSecond\" {\n  name     = \"${local.apimResourceGroupName2nd}\"\n  location = var.locationSecond\n  tags     = local.tags\n}\n\n\nmodule \"networkingSecond\" {\n  depends_on                   = [azurerm_resource_group.networkingSecond]\n  source                       = \"../modules/networking\"\n  location                     = var.locationSecond\n  resourceGroupName            = azurerm_resource_group.networkingSecond.name\n  resourceSuffix               = local.resourceSuffix2nd  \n  environment                  = var.environment\n  apimAddressPrefix            = var.apimSecondAddressPrefix\n  appGatewayAddressPrefix      = var.appGatewaySecondAddressPrefix\n  apimCSVNetNameAddressPrefix  = var.apimCSVNetNameSecondAddressPrefix\n  privateEndpointAddressPrefix = var.privateEndpointSecondAddressPrefix\n  deploymentAddressPrefix      = var.deploymentSecondAddressPrefix\n}\n\n\n\nmodule \"sharedSecond\" {\n  depends_on           = [module.networkingSecond]\n  source               = \"../modules/multi_shared\"\n  location             = var.locationSecond\n  resourceGroupName    = azurerm_resource_group.sharedSecond.name\n  resourceSuffix       = local.resourceSuffix2nd  \n  additionalClientIds  = var.additionalClientIds\n  keyVaultName         = \"${local.keyVaultName}-2nd\"\n  keyVaultSku          = var.keyVaultSku\n  keyVaultPrivateDnsZoneId = module.keyvault_dns_zone_multi.id\n  deploymentSubnetId   = module.networkingSecond.deploymentSubnetId\n  storage_account_name = local.storageAccountName2nd\n}\n\n\nmodule \"gatewaySecond\" {\n  depends_on              = [module.networkingSecond, module.apim, module.sharedSecond]\n  source                  = \"../modules/multi_gateway\"\n  location                = var.locationSecond\n  resourceGroupName       = azurerm_resource_group.networkingSecond.name\n  resourceSuffix          = local.resourceSuffix2nd\n  environment             = var.environment\n  appGatewayFqdn          = var.appGatewayFqdn\n  appGatewayCertType      = var.appGatewayCertType\n  certificate_password    = var.certificatePassword\n  certificate_path        = var.certificatePath\n  subnetId                = module.networkingSecond.appGatewaySubnetId\n  \n  primaryBackendendFqdn   = module.apim.apim_regional_url_2\n  keyvaultId              = module.sharedSecond.keyVaultId\n  keyVaultName            = module.sharedSecond.keyVaultName\n  sharedResourceGroupName = azurerm_resource_group.sharedSecond.name\n  deploymentIdentityName  = module.sharedSecond.deploymentIdentityName\n  deploymentSubnetId      = module.networkingSecond.deploymentSubnetId\n  deploymentStorageName   = module.sharedSecond.deploymentStorageName\n}\n\nmodule \"trafficmanager\" {\n  depends_on          = [module.apim, module.gateway, module.gatewaySecond]\n  source              = \"../modules/multi_traffic_manager\"\n  name                = replace(var.appGatewayFqdn,\".\",\"-\")\n  resourceGroupName   = azurerm_resource_group.networking.name\n  environment         = var.environment\n  primaryName         = module.gateway.app_gateway_name\n  primaryPublicIpId   = module.gateway.gw_pip_id\n  secondaryName       = module.gatewaySecond.app_gateway_name\n  secondaryPublicIpId = module.gatewaySecond.gw_pip_id\n}"
  },
  {
    "path": "scenarios/apim-baseline/terraform/multi-region/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus2\"\n}\n\nvariable \"locationSecond\" {\n  type        = string\n  description = \"The Azure location in which the secondary deployment is happening\"\n  default     = \"centralus\"\n}\n\n\nvariable \"workloadName\" {\n  type        = string\n  description = \"A suffix for naming\"\n  default     = \"apimdemo\"\n}\n\nvariable \"appGatewayFqdn\" {\n  type        = string\n  description = \"The Azure location to deploy to\"\n  default     = \"apim.example.com\"\n}\n\nvariable \"appGatewayCertType\" {\n  type        = string\n  description = \"selfsigned will create a self-signed certificate for the APPGATEWAY_FQDN. custom will use an existing certificate in pfx format that needs to be available in the [certs](../../certs) folder and named appgw.pfx \"\n  default     = \"selfsigned\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"keyVaultSku\" {\n  type        = string\n  description = \"The Name of the SKU used for this Key Vault. Possible values are standard and premium\"\n  default     = \"standard\"\n}\n\nvariable \"additionalClientIds\" {\n  description = \"List of additional clients to add to the Key Vault access policy.\"\n  type        = list(string)\n  default     = []\n}\n\nvariable \"certificatePassword\" {\n  description = \"Password for the certificate\"\n  type        = string\n  sensitive   = true\n  default     = \"\"\n}\n\nvariable \"certificatePath\" {\n  description = \"Path to the certificate\"\n  type        = string\n  default     = \"../../certs/appgw.pfx\"\n}\n\nvariable \"identifier\" {\n  description = \"The identifier for the resource deployments\"\n  type        = string\n}\n\n# Primary Region Network\n\nvariable \"apimCSVNetNameAddressPrefix\" {\n  description = \"APIM CSV Net Name Address Prefix\"\n  type        = string\n  default     = \"10.2.0.0/16\"\n}\n\nvariable \"appGatewayAddressPrefix\" {\n  description = \"App Gateway Address Prefix\"\n  type        = string\n  default     = \"10.2.4.0/24\"\n}\n\nvariable \"apimAddressPrefix\" {\n  description = \"APIM Address Prefix\"\n  type        = string\n  default     = \"10.2.7.0/24\"\n}\n\nvariable \"privateEndpointAddressPrefix\" {\n  description = \"Private Endpoint Address Prefix\"\n  type        = string\n  default     = \"10.2.5.0/24\"\n}\n\nvariable \"deploymentAddressPrefix\" {\n  description = \"Deployment Address Prefix\"\n  type        = string\n  default     = \"10.2.8.0/24\"\n}\n\n# HA Scenarios Variables\n\n# Secondary Region Network\n\nvariable \"apimCSVNetNameSecondAddressPrefix\" {\n  description = \"APIM CSV Net Name Address Prefix\"\n  type        = string\n  default     = \"10.3.0.0/16\"\n}\n\nvariable \"appGatewaySecondAddressPrefix\" {\n  description = \"App Gateway Address Prefix\"\n  type        = string\n  default     = \"10.3.4.0/24\"\n}\n\nvariable \"apimSecondAddressPrefix\" {\n  description = \"APIM Address Prefix\"\n  type        = string\n  default     = \"10.3.7.0/24\"\n}\n\nvariable \"privateEndpointSecondAddressPrefix\" {\n  description = \"Private Endpoint Address Prefix\"\n  type        = string\n  default     = \"10.3.5.0/24\"\n}\n\nvariable \"deploymentSecondAddressPrefix\" {\n  description = \"Deployment Address Prefix\"\n  type        = string\n  default     = \"10.3.8.0/24\"\n}\n\n# This will deploy APIM to primary region and extend a location to a secondary region.\n# This uses the Premium V1 SKU of APIM.\nvariable \"multiRegionEnabled\" {\n  description = \"Boolean to indicate if the deployment is multi-region\"\n  type        = bool\n  default     = true\n}\n\nvariable \"zoneRedundantEnabled\" {\n  description = \"Boolean to indicate if the deployment is zone redundant\"\n  type        = bool\n  default     = false\n}\n\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/provider.tf",
    "content": "terraform {\n\n  # for storage backends, see backend.tf.sample\n\n  required_providers {\n    azurerm = {\n      source  = \"hashicorp/azurerm\"\n      version = \">= 3.1\"\n    }\n    random = {\n      source  = \"hashicorp/random\"\n      version = \"~> 3.6.0\"\n    }\n    azapi = {\n      source  = \"azure/azapi\"\n      version = \"~> 1.0\"\n    }\n  }\n}\n\n# Configure the Microsft Azure provider\nprovider \"azurerm\" {\n  features {\n    resource_group {\n      prevent_deletion_if_contains_resources = false\n    }\n  }\n  use_oidc = true\n  storage_use_azuread = true\n  subscription_id = var.subscription_id\n  # client_id       = var.client_id\n  # client_secret   = var.client_secret\n  # tenant_id       = var.tenant_id\n}\n\nprovider \"azapi\" {\n  # Configuration options\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/single-region/single-region-main.tf",
    "content": "locals {\n  resourceSuffix              = \"${var.workloadName}-${var.environment}-${var.location}-${var.identifier}\"\n  networkingResourceGroupName = \"rg-networking-${local.resourceSuffix}\"\n  sharedResourceGroupName     = \"rg-shared-${local.resourceSuffix}\"\n  apimResourceGroupName       = \"rg-apim-${local.resourceSuffix}\"\n  keyVaultName                = \"kv-${var.workloadName}-${var.environment}-${var.identifier}\"\n  tags = {}\n}\n\nresource \"azurerm_resource_group\" \"networking\" {\n  name     = local.networkingResourceGroupName\n  location = var.location\n  tags     = local.tags\n}\n\nresource \"azurerm_resource_group\" \"shared\" {\n  name     = local.sharedResourceGroupName\n  location = var.location\n  tags     = local.tags\n}\n\nresource \"azurerm_resource_group\" \"apim\" {\n  name     = local.apimResourceGroupName\n  location = var.location\n  tags     = local.tags\n}\n\nmodule \"networking\" {\n  depends_on                   = [azurerm_resource_group.networking]\n  source                       = \"../modules/networking\"\n  location                     = var.location\n  resourceGroupName            = azurerm_resource_group.networking.name\n  resourceSuffix               = local.resourceSuffix\n  environment                  = var.environment\n  apimAddressPrefix            = var.apimAddressPrefix\n  appGatewayAddressPrefix      = var.appGatewayAddressPrefix\n  apimCSVNetNameAddressPrefix  = var.apimCSVNetNameAddressPrefix\n  privateEndpointAddressPrefix = var.privateEndpointAddressPrefix\n  deploymentAddressPrefix      = var.deploymentAddressPrefix\n}\n\nmodule \"shared\" {\n  depends_on           = [module.networking]\n  source               = \"../modules/shared\"\n  location             = var.location\n  resourceGroupName    = azurerm_resource_group.shared.name\n  resourceSuffix       = local.resourceSuffix\n  additionalClientIds  = var.additionalClientIds\n  keyVaultName         = local.keyVaultName\n  keyVaultSku          = var.keyVaultSku\n  deploymentSubnetId   = module.networking.deploymentSubnetId\n  storage_account_name = substr(lower(replace(\"stdep${local.resourceSuffix}\", \"-\", \"\")), 0, 21)\n}\n\nmodule \"apim\" {\n  depends_on              = [module.shared, module.networking]\n  source                  = \"../modules/apim\"\n  location                = var.location\n  resourceGroupName       = azurerm_resource_group.apim.name\n  resourceSuffix          = local.resourceSuffix\n  environment             = var.environment\n  apimSubnetId            = module.networking.apimSubnetId\n  instrumentationKey      = module.shared.instrumentationKey\n  workspaceId             = module.shared.workspaceId\n  sharedResourceGroupName = azurerm_resource_group.shared.name\n  keyVaultName            = local.keyVaultName\n  zoneRedundantEnabled    = var.zoneRedundantEnabled\n}\n\nmodule \"gateway\" {\n  depends_on              = [module.networking, module.apim, module.shared]\n  source                  = \"../modules/gateway\"\n  location                = var.location\n  resourceGroupName       = azurerm_resource_group.networking.name\n  resourceSuffix          = local.resourceSuffix\n  environment             = var.environment\n  appGatewayFqdn          = var.appGatewayFqdn\n  appGatewayCertType      = var.appGatewayCertType\n  certificate_password    = var.certificatePassword\n  certificate_path        = var.certificatePath\n  subnetId                = module.networking.appGatewaySubnetId\n  primaryBackendendFqdn   = module.apim.bakendUrl\n  keyvaultId              = module.shared.keyVaultId\n  keyVaultName            = module.shared.keyVaultName\n  sharedResourceGroupName = azurerm_resource_group.shared.name\n  deploymentIdentityName  = module.shared.deploymentIdentityName\n  deploymentSubnetId      = module.networking.deploymentSubnetId\n  deploymentStorageName   = module.shared.deploymentStorageName\n}\n\nmodule \"dns\" {\n  depends_on        = [module.apim, module.gateway]\n  source            = \"../modules/dns\"\n  location          = var.location\n  resourceGroupName = azurerm_resource_group.networking.name\n  resourceSuffix    = local.resourceSuffix\n  environment       = var.environment\n  apimName          = module.apim.apimName\n  apimPrivateIp     = module.apim.apimPrivateIp\n  apimVnetId        = module.networking.apimVnetId\n}\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/single-region/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus2\"\n}\n\nvariable \"workloadName\" {\n  type        = string\n  description = \"A suffix for naming\"\n  default     = \"apimdemo\"\n}\n\nvariable \"appGatewayFqdn\" {\n  type        = string\n  description = \"The Azure location to deploy to\"\n  default     = \"apim.example.com\"\n}\n\nvariable \"appGatewayCertType\" {\n  type        = string\n  description = \"selfsigned will create a self-signed certificate for the APPGATEWAY_FQDN. custom will use an existing certificate in pfx format that needs to be available in the [certs](../../certs) folder and named appgw.pfx \"\n  default     = \"selfsigned\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"apimCSVNetNameAddressPrefix\" {\n  description = \"APIM CSV Net Name Address Prefix\"\n  type        = string\n  default     = \"10.2.0.0/16\"\n}\n\nvariable \"appGatewayAddressPrefix\" {\n  description = \"App Gateway Address Prefix\"\n  type        = string\n  default     = \"10.2.4.0/24\"\n}\n\nvariable \"apimAddressPrefix\" {\n  description = \"APIM Address Prefix\"\n  type        = string\n  default     = \"10.2.7.0/24\"\n}\n\nvariable \"privateEndpointAddressPrefix\" {\n  description = \"Private Endpoint Address Prefix\"\n  type        = string\n  default     = \"10.2.5.0/24\"\n}\n\nvariable \"deploymentAddressPrefix\" {\n  description = \"Deployment Address Prefix\"\n  type        = string\n  default     = \"10.2.8.0/24\"\n}\n\nvariable \"keyVaultSku\" {\n  type        = string\n  description = \"The Name of the SKU used for this Key Vault. Possible values are standard and premium\"\n  default     = \"standard\"\n}\n\nvariable \"additionalClientIds\" {\n  description = \"List of additional clients to add to the Key Vault access policy.\"\n  type        = list(string)\n  default     = []\n}\n\nvariable \"certificatePassword\" {\n  description = \"Password for the certificate\"\n  type        = string\n  sensitive   = true\n  default     = \"\"\n}\n\nvariable \"certificatePath\" {\n  description = \"Path to the certificate\"\n  type        = string\n  default     = \"../../certs/appgw.pfx\"\n}\n\nvariable \"identifier\" {\n  description = \"The identifier for the resource deployments\"\n  type        = string\n}\n\nvariable \"zoneRedundantEnabled\" {\n  description = \"Boolean to indicate if the deployment is zone redundant\"\n  type        = bool\n  default     = false\n}\n\n"
  },
  {
    "path": "scenarios/apim-baseline/terraform/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus2\"\n}\n\nvariable \"workloadName\" {\n  type        = string\n  description = \"A suffix for naming\"\n  default     = \"apimdemo\"\n}\n\nvariable \"appGatewayFqdn\" {\n  type        = string\n  description = \"The Azure location to deploy to\"\n  default     = \"apim.example.com\"\n}\n\nvariable \"appGatewayCertType\" {\n  type        = string\n  description = \"selfsigned will create a self-signed certificate for the APPGATEWAY_FQDN. custom will use an existing certificate in pfx format that needs to be available in the [certs](../../certs) folder and named appgw.pfx \"\n  default     = \"selfsigned\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"keyVaultSku\" {\n  type        = string\n  description = \"The Name of the SKU used for this Key Vault. Possible values are standard and premium\"\n  default     = \"standard\"\n}\n\nvariable \"additionalClientIds\" {\n  description = \"List of additional clients to add to the Key Vault access policy.\"\n  type        = list(string)\n  default     = []\n}\n\nvariable \"certificatePassword\" {\n  description = \"Password for the certificate\"\n  type        = string\n  sensitive   = true\n  default     = \"\"\n}\n\nvariable \"certificatePath\" {\n  description = \"Path to the certificate\"\n  type        = string\n  default     = \"../../certs/appgw.pfx\"\n}\n\nvariable \"identifier\" {\n  description = \"The identifier for the resource deployments\"\n  type        = string\n}\n\n# Primary Region Network\n\nvariable \"apimCSVNetNameAddressPrefix\" {\n  description = \"APIM CSV Net Name Address Prefix\"\n  type        = string\n  default     = \"10.2.0.0/16\"\n}\n\nvariable \"appGatewayAddressPrefix\" {\n  description = \"App Gateway Address Prefix\"\n  type        = string\n  default     = \"10.2.4.0/24\"\n}\n\nvariable \"apimAddressPrefix\" {\n  description = \"APIM Address Prefix\"\n  type        = string\n  default     = \"10.2.7.0/24\"\n}\n\nvariable \"privateEndpointAddressPrefix\" {\n  description = \"Private Endpoint Address Prefix\"\n  type        = string\n  default     = \"10.2.5.0/24\"\n}\n\nvariable \"deploymentAddressPrefix\" {\n  description = \"Deployment Address Prefix\"\n  type        = string\n  default     = \"10.2.8.0/24\"\n}\n\n# HA Scenarios Variables\n\n# This will deploy APIM to primary region and extend a location to a secondary region.\n# This uses the Premium V1 SKU of APIM.\nvariable \"multiRegionEnabled\" {\n  description = \"Boolean to indicate if the deployment is multi-region\"\n  type        = bool\n  default     = false\n}\n\nvariable \"zoneRedundantEnabled\" {\n  description = \"Boolean to indicate if the deployment is zone redundant\"\n  type        = bool\n  default     = false\n}\n\nvariable \"locationSecond\" {\n  type        = string\n  description = \"The Azure location in which the secondary deployment is happening\"\n  default     = \"centralus\"\n}\n\n# Secondary Region Network\n\nvariable \"apimCSVNetNameSecondAddressPrefix\" {\n  description = \"APIM CSV Net Name Address Prefix\"\n  type        = string\n  default     = \"10.3.0.0/16\"\n}\n\nvariable \"appGatewaySecondAddressPrefix\" {\n  description = \"App Gateway Address Prefix\"\n  type        = string\n  default     = \"10.3.4.0/24\"\n}\n\nvariable \"apimSecondAddressPrefix\" {\n  description = \"APIM Address Prefix\"\n  type        = string\n  default     = \"10.3.7.0/24\"\n}\n\nvariable \"privateEndpointSecondAddressPrefix\" {\n  description = \"Private Endpoint Address Prefix\"\n  type        = string\n  default     = \"10.3.5.0/24\"\n}\n\nvariable \"deploymentSecondAddressPrefix\" {\n  description = \"Deployment Address Prefix\"\n  type        = string\n  default     = \"10.3.8.0/24\"\n}\n\nvariable \"subscription_id\" {\n  type        = string\n  description = \"The Azure subscription ID to deploy to\"\n}\n\n# To avoid Terraform missing variable warnings\nvariable \"certData\"        { default=\"\" }\nvariable \"certKey\"         { default=\"\" }\nvariable \"enableTelemetry\" { default=\"\" }\n\n\n\n"
  },
  {
    "path": "scenarios/certs/place-custom-cert-here",
    "content": ""
  },
  {
    "path": "scenarios/scripts/bicep/deploy-apim-baseline.sh",
    "content": "#!/bin/bash\n\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nif [[ -f \"$script_dir/../../.env\" ]]; then\n\techo \"Loading .env\"\n\tsource \"$script_dir/../../.env\"\nfi\n\nif [[ ${#AZURE_LOCATION} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable AZURE_LOCATION' 1>&2\n  exit 6\nelse\n  AZURE_LOCATION=\"${AZURE_LOCATION%$'\\r'}\"\nfi\n\nif [[ ${#RESOURCE_NAME_PREFIX} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable RESOURCE_NAME_PREFIX' 1>&2\n  exit 6\nelse\n  RESOURCE_NAME_PREFIX=\"${RESOURCE_NAME_PREFIX%$'\\r'}\"\nfi\n\nif [[ ${#ENVIRONMENT_TAG} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable ENVIRONMENT_TAG' 1>&2\n  exit 6\nelse\n  ENVIRONMENT_TAG=\"${ENVIRONMENT_TAG%$'\\r'}\"\nfi\n\nif [[ ${#APPGATEWAY_FQDN} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable APPGATEWAY_FQDN' 1>&2\n  exit 6\nelse\n  APPGATEWAY_FQDN=\"${APPGATEWAY_FQDN%$'\\r'}\"\nfi\n\nif [[ ${#CERT_TYPE} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable CERT_TYPE' 1>&2\n  exit 6\nelse\n  CERT_TYPE=\"${CERT_TYPE%$'\\r'}\"\nfi\n\nif [[ ${#ENABLE_TELEMETRY} -eq 0 ]]; then\n  telemetry=true\nfi\n\nif [[ \"$CERT_TYPE\" == \"selfsigned\" ]]; then\n  cert_data=''\n  cert_Pwd=''\nelse\n  cert_data=$(base64 -w 0 \"$script_dir/../../certs/appgw.pfx\")\n  cert_pwd=$(CERT_PWD)\nfi\n\nif [[ ${#RANDOM_IDENTIFIER} -eq 0 ]]; then\n  chars=\"abcdefghijklmnopqrstuvwxyz\"\n  random_string=\"\"\n  for i in {1..3}; do\n      random_char=\"${chars:RANDOM%${#chars}:1}\"\n      random_string+=\"$random_char\"\n  done\n  echo \"RANDOM_IDENTIFIER='$random_string'\" >> \"$script_dir/../../.env\"\nelse\n  random_string=\"${RANDOM_IDENTIFIER}\"\nfi\n\ncat << EOF > \"$script_dir/../../apim-baseline/bicep/parameters.json\"\n{\n  \"\\$schema\": \"https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#\",\n  \"contentVersion\": \"1.0.0.0\",\n  \"parameters\": {\n    \"workloadName\" :{\n        \"value\": \"${RESOURCE_NAME_PREFIX}\"\n    },\n    \"environment\" :{\n        \"value\": \"${ENVIRONMENT_TAG}\"\n    },\n    \"identifier\" :{\n        \"value\": \"${random_string}\"\n    },\n    \"appGatewayFqdn\" :{\n        \"value\": \"${APPGATEWAY_FQDN}\"\n    },\n    \"appGatewayCertType\" :{\n        \"value\": \"${CERT_TYPE}\"\n    },\n    \"certData\" :{\n        \"value\": \"${cert_data}\"\n    },\n    \"certKey\" :{\n        \"value\": \"${cert_pwd}\"\n    },\n    \"enableTelemetry\" :{\n        \"value\": ${telemetry}\n    }\n  }\n}\nEOF\n\ndeployment_name=\"apim-baseline-${RESOURCE_NAME_PREFIX}\"\n\ncd \"$script_dir/../../apim-baseline/bicep/\"\necho \"==\"\necho \"== Starting bicep deployment ${deployment_name}\"\necho \"==\"\noutput=$(az deployment sub create \\\n  --template-file main.bicep \\\n  --name \"$deployment_name\" \\\n  --parameters parameters.json \\\n  --location \"$AZURE_LOCATION\" \\\n  --output json)\n\necho \"== Completed bicep deployment ${deployment_name}\"\n\necho \"$output\" | jq \"[.properties.outputs | to_entries | .[] | {key:.key, value: .value.value}] | from_entries\" > \"$script_dir/../../apim-baseline/bicep/output.json\"\n\nappGatewayPublicIpAddress=$(cat \"$script_dir/../../apim-baseline/bicep/output.json\" | jq -r '.appGatewayPublicIpAddress')\napimStarterSubscriptionKey=$(cat \"$script_dir/../../apim-baseline/bicep/output.json\" | jq -r '.apimStarterSubscriptionKey')\n\ntestUri=\"curl -k -v -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${apimStarterSubscriptionKey}' https://${appGatewayPublicIpAddress}/echo/resource?param1=sample\"\necho \"Test the deployment by running the following command: ${testUri}\"\necho -e \"\\n\"\n"
  },
  {
    "path": "scenarios/scripts/bicep/deploy-workload-function.sh",
    "content": "#!/bin/bash\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nif [[ -f \"$script_dir/../../.env\" ]]; then\n\techo \"Loading .env\"\n\tsource \"$script_dir/../../.env\"\nfi\n\nif [[ -f \"$script_dir/../../apim-baseline/bicep/output.json\" ]]; then\n\techo \"Loading baseline configuration\"\n\n    while IFS='=' read -r key value; do\n        export \"$key=${value//\\\"/}\"\n    done < <(jq -r 'to_entries|map(\"\\(.key)=\\(.value|tostring)\")|.[]' \"$script_dir/../../apim-baseline/bicep/output.json\")\nelse\n    echo \"ERROR: Missing baseline configuration. Run deploy-apim-baseline.sh\" 1>&2\n    exit 6\nfi\n\nif [[ ${#AZURE_LOCATION} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable AZURE_LOCATION' 1>&2\n  exit 6\nelse\n  AZURE_LOCATION=\"${AZURE_LOCATION%$'\\r'}\"\nfi\n\nif [[ ${#RESOURCE_NAME_PREFIX} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable RESOURCE_NAME_PREFIX' 1>&2\n  exit 6\nelse\n  RESOURCE_NAME_PREFIX=\"${RESOURCE_NAME_PREFIX%$'\\r'}\"\nfi\n\nif [[ ${#ENVIRONMENT_TAG} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable ENVIRONMENT_TAG' 1>&2\n  exit 6\nelse\n  ENVIRONMENT_TAG=\"${ENVIRONMENT_TAG%$'\\r'}\"\nfi\n\nif [[ ${#ENABLE_TELEMETRY} -eq 0 ]]; then\n  telemetry=true\nfi\n\ncat << EOF > \"$script_dir/../../workload-functions/bicep/parameters.json\"\n{\n  \"\\$schema\": \"https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#\",\n  \"contentVersion\": \"1.0.0.0\",\n  \"parameters\": {\n    \"resourceSuffix\" :{\n        \"value\": \"${resourceSuffix}\"\n    },\n    \"networkingResourceGroupName\" :{\n        \"value\": \"${networkingResourceGroupName}\"\n    },\n    \"apimResourceGroupName\" :{\n        \"value\": \"${apimResourceGroupName}\"\n    },\n    \"apimName\" :{\n        \"value\": \"${apimName}\"\n    },\n    \"vnetName\" :{\n        \"value\": \"${vnetName}\"\n    },\n    \"deploymentIdentityName\" :{\n        \"value\": \"${deploymentIdentityName}\"\n    },\n    \"deploymentSubnetId\" :{\n        \"value\": \"${deploymentSubnetId}\"\n    },\n    \"deploymentStorageName\" :{\n        \"value\": \"${deploymentStorageName}\"\n    },\n    \"privateEndpointSubnetid\" :{\n        \"value\": \"${privateEndpointSubnetid}\"\n    },\n    \"sharedResourceGroupName\" :{\n        \"value\": \"${sharedResourceGroupName}\"\n    },\n    \"enableTelemetry\" :{\n        \"value\": ${telemetry}\n    }\n  }\n}\nEOF\n\ndeployment_name=\"workload-functions-${RESOURCE_NAME_PREFIX}\"\n\necho \"$deployment_name\"\ncd \"$script_dir/../../workload-functions/bicep/\"\necho \"==\"\necho \"== Starting bicep deployment ${deployment_name}\"\necho \"==\"\noutput=$(az deployment sub create \\\n  --template-file main.bicep \\\n  --name \"$deployment_name\" \\\n  --parameters parameters.json \\\n  --location \"$AZURE_LOCATION\" \\\n  --output json)\n\necho \"== Completed bicep deployment ${deployment_name}\"\n\necho \"$output\" | jq \"[.properties.outputs | to_entries | .[] | {key:.key, value: .value.value}] | from_entries\" > \"$script_dir/../../workload-functions/bicep/output.json\"\n\nAPPGATEWAY_FQDN=\"${APPGATEWAY_FQDN%$'\\r'}\"\ntestUri=\"curl -k -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${apimStarterSubscriptionKey}' https://${appGatewayPublicIpAddress}/hello?name=world\"\necho \"Test the deployment by running the following command: ${testUri}\"\necho -e \"\\n\"\n"
  },
  {
    "path": "scenarios/scripts/bicep/deploy-workload-genai.sh",
    "content": "#!/bin/bash\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nif [[ -f \"$script_dir/../../.env\" ]]; then\n\techo \"Loading .env\"\n\tsource \"$script_dir/../../.env\"\nfi\n\nif [[ ${#ENABLE_TELEMETRY} -eq 0 ]]; then\n  telemetry=true\nfi\n\nif [[ -f \"$script_dir/../../apim-baseline/bicep/output.json\" ]]; then\n\techo \"Loading baseline configuration\"\n\n    while IFS='=' read -r key value; do\n        export \"$key=${value//\\\"/}\"\n    done < <(jq -r 'to_entries|map(\"\\(.key)=\\(.value|tostring)\")|.[]' \"$script_dir/../../apim-baseline/bicep/output.json\")\nelse\n    echo \"ERROR: Missing baseline configuration. Run deploy-apim-baseline.sh\" 1>&2\n    exit 6\nfi\n\ncat << EOF > \"$script_dir/../../workload-genai/bicep/parameters.json\"\n{\n  \"\\$schema\": \"https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#\",\n  \"contentVersion\": \"1.0.0.0\",\n  \"parameters\": {\n    \"apiManagementServiceName\" :{\n        \"value\": \"${apimName}\"\n    },\n    \"resourceSuffix\" :{\n        \"value\": \"${resourceSuffix}\"\n    },\n    \"apimResourceGroupName\" :{\n        \"value\": \"${apimResourceGroupName}\"\n    },\n    \"apimIdentityName\" :{\n        \"value\": \"${apimIdentityName}\"\n    },\n    \"vnetName\" :{\n        \"value\": \"${vnetName}\"\n    },\n    \"privateEndpointSubnetid\" :{\n        \"value\": \"${privateEndpointSubnetid}\"\n    },\n    \"networkingResourceGroupName\" :{\n        \"value\": \"${networkingResourceGroupName}\"\n    },\n    \"enableTelemetry\" :{\n        \"value\": ${telemetry}\n    }\n  }\n}\nEOF\n\ndeployment_name=\"workload-genai-${RESOURCE_NAME_PREFIX}\"\n\necho \"$deployment_name\"\ncd \"$script_dir/../../workload-genai/bicep/\"\necho \"==\"\necho \"== Starting bicep deployment ${deployment_name}\"\necho \"==\"\noutput=$(az deployment sub create \\\n  --template-file main.bicep \\\n  --name \"$deployment_name\" \\\n  --parameters parameters.json \\\n  --location \"$AZURE_LOCATION\" \\\n  --output json)\n\necho \"== Completed bicep deployment ${deployment_name}\"\n\necho \"$output\" | jq \"[.properties.outputs | to_entries | .[] | {key:.key, value: .value.value}] | from_entries\" > \"$script_dir/../../workload-genai/bicep/output.json\"\n\napimSubscriptionKey=$(cat \"$script_dir/../../workload-genai/bicep/output.json\" | jq -r '.apiManagementAzureOpenAIProductSubscriptionKey')\nmultiTenantProduct1SubscriptionKey=$(cat \"$script_dir/../../workload-genai/bicep/output.json\" | jq -r '.apiManagementMultitenantProduct1SubscriptionKey')\nmultiTenantProduct2SubscriptionKey=$(cat \"$script_dir/../../workload-genai/bicep/output.json\" | jq -r '.apiManagementMultitenantProduct2SubscriptionKey')\n\ntestUri=\"curl -k -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${apimSubscriptionKey}' -H 'Content-Type: application/json' https://${appGatewayPublicIpAddress}/openai/deployments/aoai/chat/completions?api-version=2024-02-15-preview -d '{\\\"messages\\\": [{\\\"role\\\":\\\"system\\\",\\\"content\\\":\\\"You are an AI assistant that helps people find information.\\\"}]}'\"\necho \"Test the deployment by running the following command: ${testUri}\"\necho -e \"\\n\"\n\nmultiTenantProduct1TestUri=\"curl -k -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${multiTenantProduct1SubscriptionKey}' -H 'Content-Type: application/json' https://${appGatewayPublicIpAddress}/openai/deployments/aoai/chat/completions?api-version=2024-02-15-preview -d '{\\\"messages\\\": [{\\\"role\\\":\\\"system\\\",\\\"content\\\":\\\"You are an AI assistant that helps people find information.\\\"}]}'\"\necho \"Test the deployment for multi-tenant Product1 by running the following command: ${multiTenantProduct1TestUri}\"\necho -e \"\\n\"\n\nmultiTenantProduct2TestUri=\"curl -k -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${multiTenantProduct2SubscriptionKey}' -H 'Content-Type: application/json' https://${appGatewayPublicIpAddress}/openai/deployments/aoai/chat/completions?api-version=2024-02-15-preview -d '{\\\"messages\\\": [{\\\"role\\\":\\\"system\\\",\\\"content\\\":\\\"You are an AI assistant that helps people find information.\\\"}]}'\"\necho \"Test the deployment for multi-tenant Product2 by running the following command: ${multiTenantProduct2TestUri}\"\necho -e \"\\n\""
  },
  {
    "path": "scenarios/scripts/terraform/__destroy-apim-baseline.sh",
    "content": "# validate if wants to proceed\n\nsource \"./.env\"\necho \"Using TFVARS: ../../apim-baseline/terraform/${ENVIRONMENT_TAG}.tfvars\"\n\necho \"Do you want to destroy the deployment? (y/n)\"\nread -r response\n\nif [[ $response =~ ^[Yy]$ ]]; then\n\n    cd ../../apim-baseline/terraform \n    terraform destroy --auto-approve -var-file=\"${ENVIRONMENT_TAG}.tfvars\"\nelse\n\techo \"Exiting...\"\nfi\n\n"
  },
  {
    "path": "scenarios/scripts/terraform/azure-backend-sample.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# This script sets up the backend configuration for Terraform in Azure.\n# It requires the user to be logged into Azure CLI and verifies the current subscription.\n# The script accepts the following parameters:\n# --resource-group or -g: The Azure resource group name for the Terraform backend.\n# --storage-account or -s: The Azure storage account name for the Terraform backend.\n# --container or -c: The Azure storage container name for the Terraform backend.\n# --auto-confirm or -y: Automatically confirm prompts without user interaction.\n# Example execution:\n# ./azure-backend-sample.sh --resource-group my-resource-group --storage-account my-storage-account --container my-container --auto-confirm\n\n# Parse arguments\nwhile [[ $# -gt 0 ]]; do\n  case \"$1\" in\n    --resource-group|-g) TF_BACKEND_RESOURCE_GROUP_NAME=$2; shift 2 ;;\n    --storage-account|-s) TF_BACKEND_STORAGE_ACCOUNT_NAME=$2; shift 2 ;;\n    --container|-c) TF_BACKEND_CONTAINER_NAME=$2; shift 2 ;;\n    --auto-confirm|-y) auto_confirm=true; shift ;;\n    *) echo \"Invalid argument: $1\"; exit 1 ;;\n  esac\ndone\n\n# Validate required arguments\nif [[ -z \"$TF_BACKEND_RESOURCE_GROUP_NAME\" || -z \"$TF_BACKEND_STORAGE_ACCOUNT_NAME\" || -z \"$TF_BACKEND_CONTAINER_NAME\" ]]; then\n  echo \"Error: --resource-group, --storage-account, and --container are required arguments.\"\n  exit 1\nfi\n\n\n# Source .env to get Azure location\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\nenv_file=\"./.env\"\n\nif [[ -f \"$env_file\" ]]; then\n\techo \"Found .env, sourcing it...\"\n  cat \"$env_file\"\n\tsource \"$env_file\"\nelse\n  echo \"###########################\"\n  echo \"Error: .env file not found in the current directory.\"\n  echo \"###########################\"\nfi\n\n# Validate if needed environment variables are set\nif [[ -z \"$AZURE_LOCATION\" ]]; then\n  echo \"Error: AZURE_LOCATION is not set in the .env file.\"\n  exit 1\nfi\n\nif [[ -z \"$ENVIRONMENT_TAG\" ]]; then\n  echo \"Error: ENVIRONMENT_TAG is not set in the .env file.\"\n  exit 1\nfi\n\n\n\n# Ensure user is logged into Azure CLI\nif ! az account show > /dev/null 2>&1; then\n  echo \"Please log in to Azure CLI using 'az login'.\"; exit 1\nfi\n\n# Display the current subscription\naz account show --query \"{subscriptionId:id, subscriptionName:name}\" --output table\n\n# Confirm to proceed if not auto-confirmed\nif [[ $auto_confirm != true ]]; then\n  read -p \"Do you want to continue? (y/n): \" response\n  [[ $response =~ ^[Yy]$ ]] || { echo \"Exiting...\"; exit 1; }\nfi\n\n# Function to create resource group if it doesn't exist\ncreate_resource_group() {\n  echo \"Creating resource group $TF_BACKEND_RESOURCE_GROUP_NAME...\"\n  az group create --name \"$TF_BACKEND_RESOURCE_GROUP_NAME\" --location \"$AZURE_LOCATION\" > /dev/null\n}\n\n# Function to create storage account if it doesn't exist\ncreate_storage_account() {\n  echo \"Creating storage account and container $TF_BACKEND_STORAGE_ACCOUNT_NAME...\"\n  az storage account create --name \"$TF_BACKEND_STORAGE_ACCOUNT_NAME\" --resource-group \"$TF_BACKEND_RESOURCE_GROUP_NAME\" --location \"$AZURE_LOCATION\" --sku Standard_LRS > /dev/null\n  az storage container create --name \"$TF_BACKEND_CONTAINER_NAME\" --account-name \"$TF_BACKEND_STORAGE_ACCOUNT_NAME\" > /dev/null\n}\n\n# Validate or create resource group\nif [[ $(az group exists --name \"$TF_BACKEND_RESOURCE_GROUP_NAME\" --output tsv) == \"false\" ]]; then\n  if [[ $auto_confirm == true ]]; then\n    create_resource_group\n  else\n    read -p \"Resource group not found. Create it? (y/n): \" response\n    [[ $response =~ ^[Yy]$ ]] && create_resource_group || { echo \"Exiting...\"; exit 1; }\n  fi\nfi\n\n\n# Validate or create storage account\nif ! az storage account show --name \"$TF_BACKEND_STORAGE_ACCOUNT_NAME\" --resource-group \"$TF_BACKEND_RESOURCE_GROUP_NAME\" > /dev/null 2>&1; then\n  if [[ $auto_confirm == true ]]; then\n    create_storage_account\n  else\n    read -p \"Storage account not found. Create it? (y/n): \" response\n    [[ $response =~ ^[Yy]$ ]] && create_storage_account || { echo \"Exiting...\"; exit 1; }\n  fi\nfi\n\n# Validate or create container\nif ! az storage container show --name \"$TF_BACKEND_CONTAINER_NAME\" --account-name \"$TF_BACKEND_STORAGE_ACCOUNT_NAME\" > /dev/null 2>&1; then\n  if [[ $auto_confirm == true ]]; then\n    az storage container create --name \"$TF_BACKEND_CONTAINER_NAME\" --account-name \"$TF_BACKEND_STORAGE_ACCOUNT_NAME\" > /dev/null\n  else\n    read -p \"Container not found. Create it? (y/n): \" response\n    [[ $response =~ ^[Yy]$ ]] && az storage container create --name \"$TF_BACKEND_CONTAINER_NAME\" --account-name \"$TF_BACKEND_STORAGE_ACCOUNT_NAME\" > /dev/null || { echo \"Exiting...\"; exit 1; }\n  fi\nfi\n\necho \"Backend resources are ready.\"\n\n# Create needed backend.hcl file\nbackend_hcl_file=\"./${ENVIRONMENT_TAG}-backend.hcl\"\n\ncat <<EOF > \"$backend_hcl_file\"\nresource_group_name  = \"$TF_BACKEND_RESOURCE_GROUP_NAME\"\nstorage_account_name = \"$TF_BACKEND_STORAGE_ACCOUNT_NAME\"\ncontainer_name       = \"$TF_BACKEND_CONTAINER_NAME\"\nEOF\n\necho \"Backend configuration written to $backend_hcl_file.\"\n\nbackend_tf_file=\"../../apim-baseline/terraform/${ENVIRONMENT_TAG}-backend.tf\"\n\ncat <<EOF > \"$backend_tf_file\"\nterraform {\n  backend \"azurerm\" {\n    resource_group_name  = \"$TF_BACKEND_RESOURCE_GROUP_NAME\"\n    storage_account_name = \"$TF_BACKEND_STORAGE_ACCOUNT_NAME\"\n    container_name       = \"$TF_BACKEND_CONTAINER_NAME\"\n  }\n}\nEOF\necho \"Terraform backend configuration written to $backend_tf_file.\""
  },
  {
    "path": "scenarios/scripts/terraform/deploy-apim-baseline.sh",
    "content": "#!/bin/bash\n\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\nenv_file=\"./.env\"\n\nwhile [[ $# -gt 0 ]]; do\n  case \"$1\" in\n    --auto-confirm|-y) auto_confirm=true; shift 2 ;;\n    --delete-local-state|-d) delete_local_state=true; shift ;;\n    --validate-commit|-t) validate_commit=true; shift ;;\n    *) echo \"Invalid argument: $1\"; exit 1 ;;\n  esac\ndone\n\n# show a message if auto confirm or delete local state is set\nif [[ $auto_confirm == true ]]; then\n  echo \"Auto-confirmation enabled, proceeding without prompts.\"\nfi\nif [[ $delete_local_state == true ]]; then\n  echo \"Delete local state enabled, local Terraform state files will be removed.\"\nfi\n\n\n\n\nif [[ -f \"$env_file\" ]]; then\n\techo \"Found .env, sourcing it...\"\n  cat \"$env_file\"\n\tsource \"$env_file\"\nelse\n  echo \"###########################\"\n  echo \"Error: .env file not found in the current directory.\"\n  echo \"       a sample is available at ./sample.env\"\n  echo \"###########################\"\n  exit 1\nfi\n\n\nif [[ ${#RANDOM_IDENTIFIER} -eq 0 ]]; then\n  chars=\"abcdefghijklmnopqrstuvwxyz\"\n  random_string=\"\"\n  for _ in {1..3}; do\n      random_char=\"${chars:RANDOM%${#chars}:1}\"\n      random_string+=\"$random_char\"\n  done\n  echo -e \"\\nRANDOM_IDENTIFIER='$random_string'\" >> \"$env_file\"\nelse\n  random_string=\"${RANDOM_IDENTIFIER}\"\nfi\n\n#### VALIDATE VARIABLES:\n\n# params\nif [[ ${#AZURE_LOCATION} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable AZURE_LOCATION' 1>&2\n  exit 6\nelse\n  AZURE_LOCATION=\"${AZURE_LOCATION%$'\\r'}\"\nfi\n\nif [[ ${#RESOURCE_NAME_PREFIX} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable RESOURCE_NAME_PREFIX' 1>&2\n  exit 6\nelse\n  RESOURCE_NAME_PREFIX=\"${RESOURCE_NAME_PREFIX%$'\\r'}\"\nfi\n\nif [[ ${#ENVIRONMENT_TAG} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable ENVIRONMENT_TAG' 1>&2\n  exit 6\nelse\n  ENVIRONMENT_TAG=\"${ENVIRONMENT_TAG%$'\\r'}\"\nfi\n\nif [[ ${#APPGATEWAY_FQDN} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable APPGATEWAY_FQDN' 1>&2\n  exit 6\nelse\n  APPGATEWAY_FQDN=\"${APPGATEWAY_FQDN%$'\\r'}\"\nfi\n\nif [[ ${#CERT_TYPE} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable CERT_TYPE' 1>&2\n  exit 6\nelse\n  CERT_TYPE=\"${CERT_TYPE%$'\\r'}\"\nfi\n\nif [[ ${#ENABLE_TELEMETRY} -eq 0 ]]; then\n  telemetry=true\nfi\n\nif [[ \"$CERT_TYPE\" == \"selfsigned\" ]]; then\n  #cert_data=''\n  #cert_Pwd=''\n\ncat << EOF > \"$script_dir/tmp-self-signed-cert.conf\"\n\n[ req ]\ndefault_bits       = 4096\ndistinguished_name = req_distinguished_name\nreq_extensions     = req_ext\nx509_extensions    = v3_req\nprompt             = no\n\n[ req_distinguished_name ]\nCN = ${APPGATEWAY_FQDN}\n\n[ req_ext ]\nsubjectAltName = @alt_names\n\n[ v3_req ]\nsubjectAltName = @alt_names\n\n[ alt_names ]\nDNS.1 = ${APPGATEWAY_FQDN}\nEOF\n\n  openssl req -x509 -nodes -days 365 -newkey rsa:4096 \\\n  -keyout \"$script_dir/apim-self-signed.key\" \\\n  -out \"$script_dir/apim-self-signed.crt\" \\\n  -config \"$script_dir/tmp-self-signed-cert.conf\" \n\n  cert_pwd=$(tr -dc 'A-Za-z0-9!@#$%^&*()_+{}|:<>?' < /dev/urandom | head -c 16)\n  cert_file=\"$script_dir/../../certs/self-signed-cert.pfx\"\n\n  openssl pkcs12 -export \\\n    -out \"$cert_file\" \\\n    -inkey \"$script_dir/apim-self-signed.key\" \\\n    -in \"$script_dir/apim-self-signed.crt\" \\\n    -passout pass:\"$cert_pwd\"\n\n  \n\n\n\nelse\n  cert_file=\"$script_dir/../../certs/appgw.pfx\"\n  cert_pwd=$(CERT_PWD)\nfi\n\n### MULTI REGION AND ZONE REDUNDANT UPDATES\nif [[ \"$MULTI_REGION\" == \"true\" ]]; then\n  echo \"Multi Region is enabled, checking for AZURE_LOCATION2...\"\n  if [[ ${#AZURE_LOCATION2} -eq 0 ]]; then\n    echo 'ERROR: Multi Region was set to true, however environment variable AZURE_LOCATION2 is missing' 1>&2\n    exit 6\n  else\n    AZURE_LOCATION2=\"${AZURE_LOCATION2%$'\\r'}\"\n    MULTI_REGION=\"${MULTI_REGION%$'\\r'}\"\n    echo \"Multi Region is enabled, using AZURE_LOCATION2: ${AZURE_LOCATION2}\"\n  fi\nelse\n  echo \"Multi Region is not enabled, AZURE_LOCATION2 will not be used.\"\n  MULTI_REGION=\"${MULTI_REGION%$'\\r'}\"\n  AZURE_LOCATION2=\"\"\nfi\n\nif [[ ${#ZONE_REDUNDANT} -eq 0 ]]; then\n  # Assume false if not set\n  ZONE_REDUNDANT=\"false\"\nelse\n  ZONE_REDUNDANT=\"${ZONE_REDUNDANT%$'\\r'}\"\nfi\n\nif [[ $validate_commit == true ]]; then\n  echo \"Performing commit validation checks ...\"\n  echo \"Setting up ficticious subscription_id\"\n  SUBSCRIPTION_ID=\"00000000-0000-0000-0000-000000000000\"\nelse\n  ### VALIDATE IF AZ LOGIN IS REQUIRED, SHOW THE SUBSCRIPTION AND CONFIRM IF WANT TO CONTINUE\n  az account show > /dev/null\n  if [ $? -ne 0 ]; then\n    echo \"You need to login to Azure CLI. Run 'az login' and try again.\"\n    exit 6\n  fi\n  echo -e \"\\n\"\n  echo \"Currently selected subscription:\"\n  az account show --query \"{subscriptionId:id, subscriptionName:name}\" --output table\n  echo -e \"\\n\"\n  echo \"If you want to change the subscription, run 'az account set --subscription <subscriptionId>'\"\n  echo -e \"\\n\"\n\n\n  if [[ $auto_confirm == true ]]; then\n    echo \"auto-confirmation enabled ... continuing\"\n  else\n    echo \"Do you want to continue? (y/n)\"\n    read -r response\n    if [[ ! $response =~ ^[Yy]$ ]]; then\n      echo \"Exiting...\"\n      exit 6\n    fi\n  fi\n\n  # Get the current subscription ID\n  SUBSCRIPTION_ID=$(az account show --query id -o tsv)\nfi\n\n# creating tfvars\n# create tfvars\necho \"Creating terraform variables file...\"\ncat << EOF > \"$script_dir/../../apim-baseline/terraform/${ENVIRONMENT_TAG}.tfvars\"\nlocation           \t = \"${AZURE_LOCATION}\"\nworkloadName       \t = \"${RESOURCE_NAME_PREFIX}\"\nenvironment        \t = \"${ENVIRONMENT_TAG}\"\nidentifier\t\t\t     = \"${random_string}\"\nappGatewayFqdn     \t = \"${APPGATEWAY_FQDN}\"\nappGatewayCertType \t = \"${CERT_TYPE}\"\ncertificatePath      = \"${cert_file}\"\ncertificatePassword  = \"${cert_pwd}\"\nenableTelemetry    \t = \"${telemetry}\"\nmultiRegionEnabled \t = \"${MULTI_REGION}\"\nzoneRedundantEnabled = \"${ZONE_REDUNDANT}\"\nlocationSecond \t     = \"${AZURE_LOCATION2}\"\nsubscription_id      = \"${SUBSCRIPTION_ID}\"\nEOF\n\ncat \"$script_dir/../../apim-baseline/terraform/${ENVIRONMENT_TAG}.tfvars\"\n\n#### Init Terraform with Backend or local storage based on presence of backend.hcl file\nbackend_hcl_file=\"./${ENVIRONMENT_TAG}-backend.hcl\"\n\nif [[ -f \"$backend_hcl_file\" ]]; then\n  echo \"Found existing backend file, using it...\"\n\n  echo \"Copying backend file to terraform directory...\"\n  cp \"$backend_hcl_file\" \"../../apim-baseline/terraform/${ENVIRONMENT_TAG}-backend.hcl\"\n  cat \"../../apim-baseline/terraform/${ENVIRONMENT_TAG}-backend.hcl\"\n  echo \"Initializing Terraform backend...\"\n  cd \"../../apim-baseline/terraform\" || exit\n\n  echo \"==\"\n  echo \"== Starting terraform deployment baseline\"\n  echo \"==\"\n\n  # Delete local state files\n  echo \"== deleting local state files\"\n  rm -rf .terraform\n  rm -f terraform.lock.hcl\n  rm -f terraform.tfstate\n  rm -f terraform.tfstate.backup\n\n\n  terraform init \\\n    -backend-config=\"${ENVIRONMENT_TAG}-backend.hcl\" \\\n    -backend-config=\"key=${ENVIRONMENT_TAG}-baseline-lza.tfstate\"\n\n\nelse\n\n  echo \"Initializing Terraform with local backend...\"\n  cd \"../../apim-baseline/terraform\" || exit\n  terraform init -backend=false\n\nfi\n\n# Check if there is an existing local state file\nif [[ -f \"${ENVIRONMENT_TAG}.tfstate\" ]]; then\n  echo -n \"Found existing local state files...\"\n  if [[ \"$delete_local_state\" == \"true\" ]]; then\n    echo \"Deleting local Terraform state files...\"\n    rm -f \"${ENVIRONMENT_TAG}.tfplan\"\n    rm -f \"${ENVIRONMENT_TAG}.tfvars\"\n    rm -f \"${ENVIRONMENT_TAG}-backend.hcl\"\n  else\n    echo \"and reusing it. Use --delete-local-state to remove it.\"\n  fi\nfi\n\n# If the validate commit flag is set, run terraform validate and exit\nif [[ $validate_commit == true ]]; then\n  echo \"Running Terraform validate and quitting ...\"\n  terraform validate\n  if [[ $? -ne 0 ]]; then\n    echo \"Terraform validation failed.\"\n    exit 7\n  fi\n  echo \"Terraform validation succeeded.\"\n  exit 0\nfi\n\n### Create the Terraform plan\n\necho \"Creating Terraform plan...\"\nterraform plan -var-file=\"${ENVIRONMENT_TAG}.tfvars\" -out=\"${ENVIRONMENT_TAG}.tfplan\"\necho \"Terraform plan created\"\n\n\n# validate if wants to proceed\nif [[ $auto_confirm == true ]]; then\n\techo \"auto-confirmation enabled ... continuing\"\n\tresponse=\"y\"\nelse\n\techo \"Do you want to create it? (y/n)\"\n\tread -r response\nfi\n\nif [[ $response =~ ^[Yy]$ ]]; then\n\techo \"Applying Terraform plan...\"\n\tterraform apply \"${ENVIRONMENT_TAG}.tfplan\"\nelse\n\techo \"Exiting...\"\n\texit 6\nfi\n\necho \"== Completed terraform deployment\"\n\n\n# Testing the deployment\necho \"Validating deployment...\"\n  APIM_SERVICE_NAME=\"apim-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\n  APIM_RESOURCE_GROUP=\"rg-apim-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\n  NETWORK_RESOURCE_GROUP=\"rg-networking-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\n  APPGATEWAY_PIP=\"pip-appgw-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\n\n  SUBSCRIPTION_ID=$(az account show --query id -o tsv)\n  API_SUBSCRIPTION_NAME=\"Echo API\"\n\n  # Get the access token\n  echo \"Obtaining Access Token...\"\n  TOKEN=$(az account get-access-token --query accessToken --output tsv)\n\n  # get the subscription id based on the subscription display name\n  echo \"Getting API Management Subscription info ... [1/3]\"\n  API_MANAGEMENT_INFO=$(curl -s -S -H \"Authorization: Bearer $TOKEN\" \\\n    -H \"Content-Type: application/json\" \\\n    \"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$APIM_RESOURCE_GROUP/providers/Microsoft.ApiManagement/service/$APIM_SERVICE_NAME/subscriptions?api-version=2022-08-01\")\n  \n  echo \"Getting API Management Subscription info ... [2/3]\"\n  API_SUBSCRIPTION_ID=$(echo $API_MANAGEMENT_INFO | jq -r --arg API_SUBSCRIPTION_NAME \"$API_SUBSCRIPTION_NAME\" '.value[] | select(.properties.displayName == $API_SUBSCRIPTION_NAME) | .name' )\n  echo \"Getting API Management Subscription info ... [3/3]\"\n  # Call the Azure REST API to get subscription keys\n  output=$(curl -s -S -X POST -H \"Authorization: Bearer $TOKEN\" \\\n    -H \"Content-Type: application/json\" \\\n    -H \"Content-Length: 0\" \\\n    \"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$APIM_RESOURCE_GROUP/providers/Microsoft.ApiManagement/service/$APIM_SERVICE_NAME/subscriptions/$API_SUBSCRIPTION_ID/listSecrets?api-version=2022-08-01\")\n\n  # Extract the subscription keys\n  PRIMARY_KEY=$(echo \"$output\" | jq -r '.primaryKey')\n\n\n  if [[ \"$MULTI_REGION\" == \"true\" ]]; then\n    \n    APPGWNAME_DASHES=\"${APPGATEWAY_FQDN//./-}\"\n    TRAFFIC_MANAGER_FQDN=\"${APPGWNAME_DASHES}.trafficmanager.net\"\n    #testUri=\"curl -k -v https://${TRAFFIC_MANAGER_FQDN}/status-0123456789abcdef\"\n    testUri=\"curl -k -v -H 'Ocp-Apim-Subscription-Key: ${PRIMARY_KEY}' -H 'Content-Type: application/json' https://${TRAFFIC_MANAGER_FQDN}/echo/resource?param1=sample\"\n    echo \"Testing against ${TRAFFIC_MANAGER_FQDN}\"\n    eval ${testUri}\n\n    echo \"Test the deployment by running the following command: ${testUri}\"\n    echo -e \"\\n\"\n\n  else\n    \n    APPGATEWAYPUBLICIPADDRESS=$(az network public-ip show --resource-group \"$NETWORK_RESOURCE_GROUP\" --name \"$APPGATEWAY_PIP\" --query ipAddress -o tsv)\n    testUri=\"curl -k -v -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${PRIMARY_KEY}' -H 'Content-Type: application/json' https://${APPGATEWAYPUBLICIPADDRESS}/echo/resource?param1=sample\"\n    echo \"Testing against ${APPGATEWAY_FQDN}\"\n    eval ${testUri}\n\n    echo \"Test the deployment by running the following command: ${testUri}\"\n    echo -e \"\\n\"\n\n  fi\n\n\n\n\n"
  },
  {
    "path": "scenarios/scripts/terraform/deploy-workload-genai-new.sh",
    "content": "#!/bin/bash\n\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\nenv_file=\"./.env\"\n\nwhile [[ $# -gt 0 ]]; do\n  case \"$1\" in\n    --auto-confirm|-y) auto_confirm=true; shift 2 ;;\n    --delete-local-state|-d) delete_local_state=true; shift ;;\n    *) echo \"Invalid argument: $1\"; exit 1 ;;\n  esac\ndone\n\n# show a message if auto confirm or delete local state is set\nif [[ $auto_confirm == true ]]; then\n  echo \"Auto-confirmation enabled, proceeding without prompts.\"\nfi\nif [[ $delete_local_state == true ]]; then\n  echo \"Delete local state enabled, local Terraform state files will be removed.\"\nfi\n\n\n\n\nif [[ -f \"$env_file\" ]]; then\n\techo \"Found .env, sourcing it...\"\n  cat \"$env_file\"\n\tsource \"$env_file\"\nelse\n  echo \"###########################\"\n  echo \"Error: .env file not found in the current directory.\"\n  echo \"       a sample is available at ./sample.env\"\n  echo \"###########################\"\n  exit 1\nfi\n\n\nif [[ ${#RANDOM_IDENTIFIER} -eq 0 ]]; then\n  chars=\"abcdefghijklmnopqrstuvwxyz\"\n  random_string=\"\"\n  for _ in {1..3}; do\n      random_char=\"${chars:RANDOM%${#chars}:1}\"\n      random_string+=\"$random_char\"\n  done\n  echo -e \"\\nRANDOM_IDENTIFIER='$random_string'\" >> \"$env_file\"\nelse\n  random_string=\"${RANDOM_IDENTIFIER}\"\nfi\n\n#### VALIDATE VARIABLES:\n\n# params\nif [[ ${#AZURE_LOCATION} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable AZURE_LOCATION' 1>&2\n  exit 6\nelse\n  AZURE_LOCATION=\"${AZURE_LOCATION%$'\\r'}\"\nfi\n\nif [[ ${#RESOURCE_NAME_PREFIX} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable RESOURCE_NAME_PREFIX' 1>&2\n  exit 6\nelse\n  RESOURCE_NAME_PREFIX=\"${RESOURCE_NAME_PREFIX%$'\\r'}\"\nfi\n\nif [[ ${#ENVIRONMENT_TAG} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable ENVIRONMENT_TAG' 1>&2\n  exit 6\nelse\n  ENVIRONMENT_TAG=\"${ENVIRONMENT_TAG%$'\\r'}\"\nfi\n\nif [[ ${#APPGATEWAY_FQDN} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable APPGATEWAY_FQDN' 1>&2\n  exit 6\nelse\n  APPGATEWAY_FQDN=\"${APPGATEWAY_FQDN%$'\\r'}\"\nfi\n\nif [[ ${#CERT_TYPE} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable CERT_TYPE' 1>&2\n  exit 6\nelse\n  CERT_TYPE=\"${CERT_TYPE%$'\\r'}\"\nfi\n\nif [[ ${#ENABLE_TELEMETRY} -eq 0 ]]; then\n  telemetry=true\nfi\n\n\n### VALIDATE IF AZ LOGIN IS REQUIRED, SHOW THE SUBSCRIPTION AND CONFIRM IF WANT TO CONTINUE\naz account show > /dev/null\nif [ $? -ne 0 ]; then\n  echo \"You need to login to Azure CLI. Run 'az login' and try again.\"\n  exit 6\nfi\necho -e \"\\n\"\necho \"Currently selected subscription:\"\naz account show --query \"{subscriptionId:id, subscriptionName:name}\" --output table\necho -e \"\\n\"\necho \"If you want to change the subscription, run 'az account set --subscription <subscriptionId>'\"\necho -e \"\\n\"\n\n\nif [[ $auto_confirm == true ]]; then\n\techo \"auto-confirmation enabled ... continuing\"\nelse\n\techo \"Do you want to continue? (y/n)\"\n\tread -r response\n\tif [[ ! $response =~ ^[Yy]$ ]]; then\n\t\techo \"Exiting...\"\n\t\texit 6\n\tfi\nfi\n\n\n# creating tfvars\n# create tfvars\necho \"Creating terraform variables file...\"\ncat << EOF > \"$script_dir/../../workload-genai/terraform/${ENVIRONMENT_TAG}.tfvars\"\nlocation           \t= \"${AZURE_LOCATION}\"\nworkloadName       \t= \"${RESOURCE_NAME_PREFIX}\"\nenvironment\t\t\t= \"${ENVIRONMENT_TAG}\"\nidentifier\t\t\t= \"${random_string}\"\nenableTelemetry    \t= \"${telemetry}\"\nEOF\n\n\ncat \"$script_dir/../../workload-genai/terraform/${ENVIRONMENT_TAG}.tfvars\"\n\n#### Init Terraform with Backend or local storage based on presence of backend.hcl file\nbackend_hcl_file=\"./${ENVIRONMENT_TAG}-backend.hcl\"\n\nif [[ -f \"$backend_hcl_file\" ]]; then\n  echo \"Found existing backend file, using it...\"\n\n  echo \"Copying backend file to terraform directory...\"\n  cp \"$backend_hcl_file\" \"../../workload-genai/terraform/${ENVIRONMENT_TAG}-backend.hcl\"\n  cat \"../../workload-genai/terraform/${ENVIRONMENT_TAG}-backend.hcl\"\n  echo \"Initializing Terraform backend...\"\n  cd \"../../workload-genai/terraform\" || exit\n\n  echo \"==\"\n  echo \"== Starting terraform deployment baseline\"\n  echo \"==\"\n\n  # Delete local state files\n  echo \"== deleting local state files\"\n  rm -rf .terraform\n  rm -f terraform.lock.hcl\n  rm -f terraform.tfstate\n  rm -f terraform.tfstate.backup\n\n\n  terraform init \\\n    -backend-config=\"${ENVIRONMENT_TAG}-backend.hcl\" \\\n    -backend-config=\"key=${ENVIRONMENT_TAG}-baseline-lza.tfstate\"\n\n\nelse\n\n  echo \"Initializing Terraform with local backend...\"\n  cd \"../../workload-genai/terraform\" || exit\n  terraform init -backend=false\n\nfi\n\n# Check if there is an existing local state file\nif [[ -f \"${ENVIRONMENT_TAG}.tfstate\" ]]; then\n  echo -n \"Found existing local state files...\"\n  if [[ \"$delete_local_state\" == \"true\" ]]; then\n    echo \"Deleting local Terraform state files...\"\n    rm -f \"${ENVIRONMENT_TAG}.tfplan\"\n    rm -f \"${ENVIRONMENT_TAG}.tfvars\"\n    rm -f \"${ENVIRONMENT_TAG}-backend.hcl\"\n  else\n    echo \"and reusing it. Use --delete-local-state to remove it.\"\n  fi\nfi\n\necho \"Creating Terraform plan...\"\nterraform plan -var-file=\"${ENVIRONMENT_TAG}.tfvars\" -out=\"${ENVIRONMENT_TAG}.tfplan\"\necho \"Terraform plan created\"\n\n# validate if wants to proceed\nif [[ $auto_confirm == true ]]; then\n\techo \"auto-confirmation enabled ... continuing\"\n\tresponse=\"y\"\nelse\n\techo \"Do you want to create it? (y/n)\"\n\tread -r response\nfi\n\nif [[ $response =~ ^[Yy]$ ]]; then\n\techo \"Applying Terraform plan...\"\n\tterraform apply \"${ENVIRONMENT_TAG}.tfplan\"\nelse\n\techo \"Exiting...\"\n\texit 6\nfi\n\necho \"== Completed terraform deployment\"\n\n# remove the plan file, tfvars and terraform.tfstate\nrm -f \"${ENVIRONMENT_TAG}.tfplan\"\nrm -f terraform.tfstate\nrm -f \"${ENVIRONMENT_TAG}.tfvars\"\nrm -f \"${ENVIRONMENT_TAG}-backend.hcl\"\n\n# Setting variables\nAPIM_SERVICE_NAME=\"apim-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\nAPIM_RESOURCE_GROUP=\"rg-apim-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\nNETWORK_RESOURCE_GROUP=\"rg-networking-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\nAPPGATEWAY_PIP=\"pip-appgw-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\nSUBSCRIPTION_ID=$(az account show --query id -o tsv)\nAPI_SUBSCRIPTION_ID=\"aoai-product-subscription\"\nMT_PRODUCT1_SUBSCRIPTION_ID=\"multi-tenant-product1-subscription\"\nMT_PRODUCT2_SUBSCRIPTION_ID=\"multi-tenant-product2-subscription\"\n\n# Get the access token\nTOKEN=$(az account get-access-token --query accessToken --output tsv)\n\n# Call the Azure REST API to get subscription keys\noutput=$(curl -s -S -X POST -H \"Authorization: Bearer $TOKEN\" \\\n\t-H \"Content-Type: application/json\" \\\n\t-H \"Content-Length: 0\" \\\n\t\"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$APIM_RESOURCE_GROUP/providers/Microsoft.ApiManagement/service/$APIM_SERVICE_NAME/subscriptions/$API_SUBSCRIPTION_ID/listSecrets?api-version=2022-08-01\")\n\n# Extract the subscription keys\nPRIMARY_KEY=$(echo \"$output\" | jq -r '.primaryKey')\n\nAPPGATEWAYPUBLICIPADDRESS=$(az network public-ip show --resource-group \"$NETWORK_RESOURCE_GROUP\" --name \"$APPGATEWAY_PIP\" --query ipAddress -o tsv)\ntestUri=\"curl -k -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${PRIMARY_KEY}' -H 'Content-Type: application/json' https://${APPGATEWAYPUBLICIPADDRESS}/openai/deployments/gpt-35-turbo-16k/chat/completions?api-version=2024-02-15-preview -d '{\\\"messages\\\": [{\\\"role\\\":\\\"system\\\",\\\"content\\\":\\\"You are an AI assistant that helps people find information.\\\"}]}'\"\necho \"Test the deployment by running the following command: ${testUri}\"\necho -e \"\\n\"\n\n# Call the Azure REST API to get subscription key of multi-tenant product1\nmt_product1_sub_output=$(curl -s -S -X POST -H \"Authorization: Bearer $TOKEN\" \\\n\t-H \"Content-Type: application/json\" \\\n\t-H \"Content-Length: 0\" \\\n\t\"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$APIM_RESOURCE_GROUP/providers/Microsoft.ApiManagement/service/$APIM_SERVICE_NAME/subscriptions/$MT_PRODUCT1_SUBSCRIPTION_ID/listSecrets?api-version=2022-08-01\")\n\n# Extract the subscription keys\nMT_PRODUCT1_SUB_PRIMARY_KEY=$(echo \"$mt_product1_sub_output\" | jq -r '.primaryKey')\n\ntestUri=\"curl -k -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${MT_PRODUCT1_SUB_PRIMARY_KEY}' -H 'Content-Type: application/json' https://${APPGATEWAYPUBLICIPADDRESS}/openai/deployments/gpt-35-turbo-16k/chat/completions?api-version=2024-02-15-preview -d '{\\\"messages\\\": [{\\\"role\\\":\\\"system\\\",\\\"content\\\":\\\"You are an AI assistant that helps people find information.\\\"}]}'\"\necho \"Test the deployment for multi-tenant Product1 by running the following command: ${testUri}\"\necho -e \"\\n\"\n\n# Call the Azure REST API to get subscription key of multi-tenant product2\nmt_product2_sub_output=$(curl -s -S -X POST -H \"Authorization: Bearer $TOKEN\" \\\n\t-H \"Content-Type: application/json\" \\\n\t-H \"Content-Length: 0\" \\\n\t\"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$APIM_RESOURCE_GROUP/providers/Microsoft.ApiManagement/service/$APIM_SERVICE_NAME/subscriptions/$MT_PRODUCT2_SUBSCRIPTION_ID/listSecrets?api-version=2022-08-01\")\n\n# Extract the subscription keys\nMT_PRODUCT2_SUB_PRIMARY_KEY=$(echo \"$mt_product2_sub_output\" | jq -r '.primaryKey')\n\ntestUri=\"curl -k -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${MT_PRODUCT2_SUB_PRIMARY_KEY}' -H 'Content-Type: application/json' https://${APPGATEWAYPUBLICIPADDRESS}/openai/deployments/gpt-35-turbo-16k/chat/completions?api-version=2024-02-15-preview -d '{\\\"messages\\\": [{\\\"role\\\":\\\"system\\\",\\\"content\\\":\\\"You are an AI assistant that helps people find information.\\\"}]}'\"\necho \"Test the deployment for multi-tenant Product2 by running the following command: ${testUri}\"\necho -e \"\\n\"\n"
  },
  {
    "path": "scenarios/scripts/terraform/deploy-workload-genai.sh",
    "content": "#!/bin/bash\n\n#echo \"Not updated yet...\"\n#exit 0\n\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\nenv_file=\"./.env\"\n\necho \"Script directory: $script_dir\"\necho \"Current directory: $(pwd)\"\n\n# if the script is run with -y flag, it will not prompt for confirmation\nif [[ $1 == \"-y\" ]]; then\n\tauto_confirm=true\nfi\n\nif [[ -f \"$env_file\" ]]; then\n\techo \"Loading .env\"\n\t# Convert Windows line endings to Unix if needed\n\tsed -i 's/\\r$//' \"$env_file\" 2>/dev/null || true\n\tsource \"$env_file\"\nelse\n  echo \"###########################\"\n  echo \"Error: .env file not found in the current directory.\"\n  echo \"       a sample is available at ./sample.env\"\n  echo \"###########################\"\n  exit 1\nfi\n\nif [[ ${#RANDOM_IDENTIFIER} -eq 0 ]]; then\n  echo \"Please run first the deploy-apim-baseline.sh script\"\n  echo \"Error: Missing environment variable RANDOM_IDENTIFIER. Automatically created by baseline\" 1>&2\n  exit 6\nelse\n  random_string=\"${RANDOM_IDENTIFIER}\"\nfi\n\n\n#### VALIDATE VARIABLES:\nif [[ ${#AZURE_LOCATION} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable AZURE_LOCATION' 1>&2\n  exit 6\nelse\n  AZURE_LOCATION=\"${AZURE_LOCATION%$'\\r'}\"\nfi\n\n# params\nif [[ ${#AZURE_LOCATION} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable AZURE_LOCATION' 1>&2\n  exit 6\nelse\n  AZURE_LOCATION=\"${AZURE_LOCATION%$'\\r'}\"\nfi\n\nif [[ ${#RESOURCE_NAME_PREFIX} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable RESOURCE_NAME_PREFIX' 1>&2\n  exit 6\nelse\n  RESOURCE_NAME_PREFIX=\"${RESOURCE_NAME_PREFIX%$'\\r'}\"\nfi\n\nif [[ ${#ENVIRONMENT_TAG} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable ENVIRONMENT_TAG' 1>&2\n  exit 6\nelse\n  ENVIRONMENT_TAG=\"${ENVIRONMENT_TAG%$'\\r'}\"\nfi\n\nif [[ ${#APPGATEWAY_FQDN} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable APPGATEWAY_FQDN' 1>&2\n  exit 6\nelse\n  APPGATEWAY_FQDN=\"${APPGATEWAY_FQDN%$'\\r'}\"\nfi\n\nif [[ ${#CERT_TYPE} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable CERT_TYPE' 1>&2\n  exit 6\nelse\n  CERT_TYPE=\"${CERT_TYPE%$'\\r'}\"\nfi\n\nif [[ ${#ENABLE_TELEMETRY} -eq 0 ]]; then\n  telemetry=true\nfi\n\n\n\n\n\n### VALIDATE IF AZ LOGIN IS REQUIRED, SHOW THE SUBSCRIPTION AND CONFIRM IF WANT TO CONTINUE\naz account show > /dev/null\nif [ $? -ne 0 ]; then\n  echo \"You need to login to Azure CLI. Run 'az login' and try again.\"\n  exit 6\nfi\n\necho \"Using subscription:\"\naz account show --query \"{subscriptionId:id, subscriptionName:name}\" --output table\n\nif [[ $auto_confirm == true ]]; then\n\techo \"auto-confirmation enabled ... continuing\"\nelse\n\techo \"Do you want to continue? (y/n)\"\n\tread -r response\n\tif [[ ! $response =~ ^[Yy]$ ]]; then\n\t\techo \"Exiting...\"\n\t\texit 6\n\tfi\nfi\n\n\n# creating tfvars\n# create tfvars\necho \"Creating terraform variables file...\"\ncat << EOF > \"$script_dir/../../workload-genai/terraform/${ENVIRONMENT_TAG}.tfvars\"\nlocation           \t= \"${AZURE_LOCATION}\"\nworkloadName       \t= \"${RESOURCE_NAME_PREFIX}\"\nenvironment\t\t\t= \"${ENVIRONMENT_TAG}\"\nidentifier\t\t\t= \"${random_string}\"\nenableTelemetry    \t= \"${telemetry}\"\nEOF\n\necho \"Copying backend file to terraform directory...\"\nbackend_hcl_file=\"./${ENVIRONMENT_TAG}-backend.hcl\"\n\nif [[ -f \"$backend_hcl_file\" ]]; then\n  echo \"Found existing backend file, using it...\"\n  cp \"$backend_hcl_file\" \"../../workload-genai/terraform/${ENVIRONMENT_TAG}-backend.hcl\"\nelse\n  echo \"No backend file found at $backend_hcl_file, skipping backend configuration...\"\nfi\n\n\necho \"Initializing Terraform backend...\"\ncd \"$script_dir/../../workload-genai/terraform\" || exit\n\nif [[ -f \"${ENVIRONMENT_TAG}-backend.hcl\" ]]; then\n  echo \"Using backend configuration...\"\n  # Delete local state files only when using remote backend\n  rm -rf .terraform\n  rm -f terraform.lock.hcl\n  #rm -f terraform.tfstate\n  rm -f terraform.tfstate.backup\n  \n  terraform init \\\n    -backend-config=\"${ENVIRONMENT_TAG}-backend.hcl\" \\\n    -backend-config=\"key=${ENVIRONMENT_TAG}-genai-lza.tfstate\"\nelse\n  echo \"Using local state...\"\n  # Only delete .terraform directory to force re-initialization\n  rm -rf .terraform\n  rm -f terraform.lock.hcl\n  \n  terraform init\nfi\n\necho \"Creating Terraform plan...\"\nterraform plan -var-file=\"${ENVIRONMENT_TAG}.tfvars\" -out=\"${ENVIRONMENT_TAG}.tfplan\"\necho \"Terraform plan created\"\n\n# validate if wants to proceed\nif [[ $auto_confirm == true ]]; then\n\techo \"auto-confirmation enabled ... continuing\"\n\tresponse=\"y\"\nelse\n\techo \"Do you want to create it? (y/n)\"\n\tread -r response\nfi\n\nif [[ $response =~ ^[Yy]$ ]]; then\n\techo \"Applying Terraform plan...\"\n\tterraform apply \"${ENVIRONMENT_TAG}.tfplan\"\nelse\n\techo \"Exiting...\"\n\texit 6\nfi\n\necho \"== Completed terraform deployment\"\n\n# remove the plan file, tfvars and terraform.tfstate\nrm -f \"${ENVIRONMENT_TAG}.tfplan\"\nrm -f terraform.tfstate\nrm -f \"${ENVIRONMENT_TAG}.tfvars\"\nrm -f \"${ENVIRONMENT_TAG}-backend.hcl\"\n\n# Setting variables\nAPIM_SERVICE_NAME=\"apim-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\nAPIM_RESOURCE_GROUP=\"rg-apim-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\nNETWORK_RESOURCE_GROUP=\"rg-networking-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\nAPPGATEWAY_PIP=\"pip-appgw-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\nSUBSCRIPTION_ID=$(az account show --query id -o tsv)\nAPI_SUBSCRIPTION_ID=\"aoai-product-subscription\"\nMT_PRODUCT1_SUBSCRIPTION_ID=\"multi-tenant-product1-subscription\"\nMT_PRODUCT2_SUBSCRIPTION_ID=\"multi-tenant-product2-subscription\"\n\n# Get the access token\nTOKEN=$(az account get-access-token --query accessToken --output tsv)\n\n# Call the Azure REST API to get subscription keys\noutput=$(curl -s -S -X POST -H \"Authorization: Bearer $TOKEN\" \\\n\t-H \"Content-Type: application/json\" \\\n\t-H \"Content-Length: 0\" \\\n\t\"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$APIM_RESOURCE_GROUP/providers/Microsoft.ApiManagement/service/$APIM_SERVICE_NAME/subscriptions/$API_SUBSCRIPTION_ID/listSecrets?api-version=2022-08-01\")\n\n# Extract the subscription keys\nPRIMARY_KEY=$(echo \"$output\" | jq -r '.primaryKey')\n\nAPPGATEWAYPUBLICIPADDRESS=$(az network public-ip show --resource-group \"$NETWORK_RESOURCE_GROUP\" --name \"$APPGATEWAY_PIP\" --query ipAddress -o tsv)\ntestUri=\"curl -k -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${PRIMARY_KEY}' -H 'Content-Type: application/json' https://${APPGATEWAYPUBLICIPADDRESS}/openai/deployments/gpt-4o-mini/chat/completions?api-version=2024-02-15-preview -d '{\\\"messages\\\": [{\\\"role\\\":\\\"system\\\",\\\"content\\\":\\\"You are an AI assistant that helps people find information.\\\"}]}'\"\necho \"Test the deployment by running the following command: ${testUri}\"\necho -e \"\\n\"\n\n# Call the Azure REST API to get subscription key of multi-tenant product1\nmt_product1_sub_output=$(curl -s -S -X POST -H \"Authorization: Bearer $TOKEN\" \\\n\t-H \"Content-Type: application/json\" \\\n\t-H \"Content-Length: 0\" \\\n\t\"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$APIM_RESOURCE_GROUP/providers/Microsoft.ApiManagement/service/$APIM_SERVICE_NAME/subscriptions/$MT_PRODUCT1_SUBSCRIPTION_ID/listSecrets?api-version=2022-08-01\")\n\n# Extract the subscription keys\nMT_PRODUCT1_SUB_PRIMARY_KEY=$(echo \"$mt_product1_sub_output\" | jq -r '.primaryKey')\n\ntestUri=\"curl -k -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${MT_PRODUCT1_SUB_PRIMARY_KEY}' -H 'Content-Type: application/json' https://${APPGATEWAYPUBLICIPADDRESS}/openai/deployments/gpt-4o-mini/chat/completions?api-version=2024-02-15-preview -d '{\\\"messages\\\": [{\\\"role\\\":\\\"system\\\",\\\"content\\\":\\\"You are an AI assistant that helps people find information.\\\"}]}'\"\necho \"Test the deployment for multi-tenant Product1 by running the following command: ${testUri}\"\necho -e \"\\n\"\n\n# Call the Azure REST API to get subscription key of multi-tenant product2\nmt_product2_sub_output=$(curl -s -S -X POST -H \"Authorization: Bearer $TOKEN\" \\\n\t-H \"Content-Type: application/json\" \\\n\t-H \"Content-Length: 0\" \\\n\t\"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$APIM_RESOURCE_GROUP/providers/Microsoft.ApiManagement/service/$APIM_SERVICE_NAME/subscriptions/$MT_PRODUCT2_SUBSCRIPTION_ID/listSecrets?api-version=2022-08-01\")\n\n# Extract the subscription keys\nMT_PRODUCT2_SUB_PRIMARY_KEY=$(echo \"$mt_product2_sub_output\" | jq -r '.primaryKey')\n\ntestUri=\"curl -k -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${MT_PRODUCT2_SUB_PRIMARY_KEY}' -H 'Content-Type: application/json' https://${APPGATEWAYPUBLICIPADDRESS}/openai/deployments/gpt-4o-mini/chat/completions?api-version=2024-02-15-preview -d '{\\\"messages\\\": [{\\\"role\\\":\\\"system\\\",\\\"content\\\":\\\"You are an AI assistant that helps people find information.\\\"}]}'\"\necho \"Test the deployment for multi-tenant Product2 by running the following command: ${testUri}\"\necho -e \"\\n\"\n"
  },
  {
    "path": "scenarios/scripts/terraform/sample-multi-region.env",
    "content": "AZURE_LOCATION='eastus2'\nRESOURCE_NAME_PREFIX='lzv01'\nENVIRONMENT_TAG='dev'\nAPPGATEWAY_FQDN='apim.example.com'\nCERT_TYPE='selfsigned'\nZONE_REDUNDANT='false'\nMULTI_REGION='true'\nAZURE_LOCATION2='centralus'\n"
  },
  {
    "path": "scenarios/scripts/terraform/sample.backend.hcl",
    "content": "resource_group_name  = \"my-resource-group\"\nstorage_account_name = \"mystorageaccount\"\ncontainer_name       = \"my-container\"\n"
  },
  {
    "path": "scenarios/scripts/terraform/sample.env",
    "content": "AZURE_LOCATION='eastus2'\nRESOURCE_NAME_PREFIX='lzv01'\nENVIRONMENT_TAG='dev'\nAPPGATEWAY_FQDN='apim.example.com'\nCERT_TYPE='selfsigned'\nZONE_REDUNDANT='false'\nMULTI_REGION='false'\nAZURE_LOCATION2='centralus'\n"
  },
  {
    "path": "scenarios/scripts/terraform/test-apim-baseline-deployment.sh",
    "content": "#!/bin/bash\n\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\nenv_file=\"./.env\"\n\n\n\n\nif [[ -f \"$env_file\" ]]; then\n\techo \"Found .env, sourcing it...\"\n  cat \"$env_file\"\n\tsource \"$env_file\"\nelse\n  echo \"###########################\"\n  echo \"Error: .env file not found in the current directory.\"\n  echo \"       a sample is available at ./sample.env\"\n  echo \"###########################\"\n  exit 1\nfi\n\n\nif [[ ${#RANDOM_IDENTIFIER} -eq 0 ]]; then\n  chars=\"abcdefghijklmnopqrstuvwxyz\"\n  random_string=\"\"\n  for _ in {1..3}; do\n      random_char=\"${chars:RANDOM%${#chars}:1}\"\n      random_string+=\"$random_char\"\n  done\n  echo -e \"\\nRANDOM_IDENTIFIER='$random_string'\" >> \"$env_file\"\nelse\n  random_string=\"${RANDOM_IDENTIFIER}\"\nfi\n\n#### VALIDATE VARIABLES:\n\n# params\nif [[ ${#AZURE_LOCATION} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable AZURE_LOCATION' 1>&2\n  exit 6\nelse\n  AZURE_LOCATION=\"${AZURE_LOCATION%$'\\r'}\"\nfi\n\nif [[ ${#RESOURCE_NAME_PREFIX} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable RESOURCE_NAME_PREFIX' 1>&2\n  exit 6\nelse\n  RESOURCE_NAME_PREFIX=\"${RESOURCE_NAME_PREFIX%$'\\r'}\"\nfi\n\nif [[ ${#ENVIRONMENT_TAG} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable ENVIRONMENT_TAG' 1>&2\n  exit 6\nelse\n  ENVIRONMENT_TAG=\"${ENVIRONMENT_TAG%$'\\r'}\"\nfi\n\nif [[ ${#APPGATEWAY_FQDN} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable APPGATEWAY_FQDN' 1>&2\n  exit 6\nelse\n  APPGATEWAY_FQDN=\"${APPGATEWAY_FQDN%$'\\r'}\"\nfi\n\nif [[ ${#CERT_TYPE} -eq 0 ]]; then\n  echo 'ERROR: Missing environment variable CERT_TYPE' 1>&2\n  exit 6\nelse\n  CERT_TYPE=\"${CERT_TYPE%$'\\r'}\"\nfi\n\nif [[ ${#ENABLE_TELEMETRY} -eq 0 ]]; then\n  telemetry=true\nfi\n\n\n### MULTI REGION AND ZONE REDUNDANT UPDATES\nif [[ \"$MULTI_REGION\" == \"true\" ]]; then\n  echo \"Multi Region is enabled, checking for AZURE_LOCATION2...\"\n  if [[ ${#AZURE_LOCATION2} -eq 0 ]]; then\n    echo 'ERROR: Multi Region was set to true, however environment variable AZURE_LOCATION2 is missing' 1>&2\n    exit 6\n  else\n    AZURE_LOCATION2=\"${AZURE_LOCATION2%$'\\r'}\"\n    MULTI_REGION=\"${MULTI_REGION%$'\\r'}\"\n    echo \"Multi Region is enabled, using AZURE_LOCATION2: ${AZURE_LOCATION2}\"\n  fi\nelse\n  echo \"Multi Region is not enabled, AZURE_LOCATION2 will not be used.\"\n  MULTI_REGION=\"${MULTI_REGION%$'\\r'}\"\n  AZURE_LOCATION2=\"\"\nfi\n\nif [[ ${#ZONE_REDUNDANT} -eq 0 ]]; then\n  # Assume false if not set\n  ZONE_REDUNDANT=\"false\"\nelse\n  ZONE_REDUNDANT=\"${ZONE_REDUNDANT%$'\\r'}\"\nfi\n\n\n### VALIDATE IF AZ LOGIN IS REQUIRED, SHOW THE SUBSCRIPTION AND CONFIRM IF WANT TO CONTINUE\naz account show > /dev/null\nif [ $? -ne 0 ]; then\n  echo \"You need to login to Azure CLI. Run 'az login' and try again.\"\n  exit 6\nfi\necho -e \"\\n\"\necho \"Currently selected subscription:\"\naz account show --query \"{subscriptionId:id, subscriptionName:name}\" --output table\necho -e \"\\n\"\necho \"If you want to change the subscription, run 'az account set --subscription <subscriptionId>'\"\necho -e \"\\n\"\n\n\n\n# Get the current subscription ID\nSUBSCRIPTION_ID=$(az account show --query id -o tsv)\n\n\n# Testing the deployment\necho \"Validating deployment...\"\n  APIM_SERVICE_NAME=\"apim-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\n  APIM_RESOURCE_GROUP=\"rg-apim-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\n  NETWORK_RESOURCE_GROUP=\"rg-networking-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\n  APPGATEWAY_PIP=\"pip-appgw-${RESOURCE_NAME_PREFIX}-${ENVIRONMENT_TAG}-${AZURE_LOCATION}-${RANDOM_IDENTIFIER}\"\n\n  SUBSCRIPTION_ID=$(az account show --query id -o tsv)\n  API_SUBSCRIPTION_NAME=\"Echo API\"\n\n  # Get the access token\n  echo \"Obtaining Access Token...\"\n  TOKEN=$(az account get-access-token --query accessToken --output tsv)\n\n  # get the subscription id based on the subscription display name\n  echo \"Getting API Management Subscription info ... [1/3]\"\n  API_MANAGEMENT_INFO=$(curl -s -S -H \"Authorization: Bearer $TOKEN\" \\\n    -H \"Content-Type: application/json\" \\\n    \"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$APIM_RESOURCE_GROUP/providers/Microsoft.ApiManagement/service/$APIM_SERVICE_NAME/subscriptions?api-version=2022-08-01\")\n  \n  echo \"Getting API Management Subscription info ... [2/3]\"\n  API_SUBSCRIPTION_ID=$(echo $API_MANAGEMENT_INFO | jq -r --arg API_SUBSCRIPTION_NAME \"$API_SUBSCRIPTION_NAME\" '.value[] | select(.properties.displayName == $API_SUBSCRIPTION_NAME) | .name' )\n  echo \"Getting API Management Subscription info ... [3/3]\"\n  # Call the Azure REST API to get subscription keys\n  output=$(curl -s -S -X POST -H \"Authorization: Bearer $TOKEN\" \\\n    -H \"Content-Type: application/json\" \\\n    -H \"Content-Length: 0\" \\\n    \"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$APIM_RESOURCE_GROUP/providers/Microsoft.ApiManagement/service/$APIM_SERVICE_NAME/subscriptions/$API_SUBSCRIPTION_ID/listSecrets?api-version=2022-08-01\")\n\n  # Extract the subscription keys\n  PRIMARY_KEY=$(echo \"$output\" | jq -r '.primaryKey')\n\n\n  if [[ \"$MULTI_REGION\" == \"true\" ]]; then\n    \n    APPGWNAME_DASHES=\"${APPGATEWAY_FQDN//./-}\"\n    TRAFFIC_MANAGER_FQDN=\"${APPGWNAME_DASHES}.trafficmanager.net\"\n    #testUri=\"curl -k -v https://${TRAFFIC_MANAGER_FQDN}/status-0123456789abcdef\"\n    testUri=\"curl -k -v -H 'Ocp-Apim-Subscription-Key: ${PRIMARY_KEY}' -H 'Content-Type: application/json' https://${TRAFFIC_MANAGER_FQDN}/echo/resource?param1=sample\"\n    echo \"Testing against ${TRAFFIC_MANAGER_FQDN}\"\n    eval ${testUri}\n\n    echo \"Test the deployment by running the following command: ${testUri}\"\n    echo -e \"\\n\"\n\n  else\n    \n    APPGATEWAYPUBLICIPADDRESS=$(az network public-ip show --resource-group \"$NETWORK_RESOURCE_GROUP\" --name \"$APPGATEWAY_PIP\" --query ipAddress -o tsv)\n    testUri=\"curl -k -v -H 'Host: ${APPGATEWAY_FQDN}' -H 'Ocp-Apim-Subscription-Key: ${PRIMARY_KEY}' -H 'Content-Type: application/json' https://${APPGATEWAYPUBLICIPADDRESS}/echo/resource?param1=sample\"\n    echo \"Testing against ${APPGATEWAY_FQDN}\"\n    eval ${testUri}\n\n    echo \"Test the deployment by running the following command: ${testUri}\"\n    echo -e \"\\n\"\n\n  fi\n\n\n\n\n"
  },
  {
    "path": "scenarios/workload-functions/README.md",
    "content": "# Scenario 2: Azure API Management - Azure Functions as backend\n\nThis reference implementation demonstrates how to provision a single region API Management instance within an internal VNet exposed through Application Gateway for external traffic with Azure Functions as the backend (exposed through private endpoint). This scenario is built on top of the [apim secure baseline](../apim-baseline/README.md) scenario.\n\nBy the end of this deployment guide, you would have deployed an an private Azure functions backend that is available publicly through Application Gateway and manged by API Management.\n\n![Architectural diagram showing an Azure API Management deployment in a virtual network with funcations as backend.](../../docs/images/apim-workload-functions.jpg)\n\n## Core architecture components\n\n- Azure Functions\n- Azure Private Endpoint\n- Azure Private DNS Zones\n\n## Deploy the reference implementation\n\nThis reference implementation is provided with the following infrastructure as code options. Select the deployment guide you are interested in. They both deploy the same implementation.\n\n:arrow_forward: [Bicep-based deployment guide](./bicep/README.md)\n:arrow_forward: Terraform-based deployment guide (Work in progress)\n"
  },
  {
    "path": "scenarios/workload-functions/bicep/README.md",
    "content": "# Scenario 2: Azure API Management - Azure Functions as backend  [Bicep]\n\nThis is the Bicep-based deployment guide for [Scenario 2: Azure API Management - Azure Functions as backend](../README.md).\n\n## Prerequisites\n\nThis scenario requires the completion of the [Azure API Management - Secure Baseline](../apim-baseline/README.md) scenario.\n\n## Steps\n\nRun the following command to deploy the scenarios\n\n```bash\n./scripts/bicep/deploy-workload-function.sh\n```\n\nTest the hello api using hte generated command from the output\n\n## Troubleshooting\n\nIf you see the message `-bash: ./deploy-workload-function.sh: /bin/bash^M: bad interpreter: No such file or directory` when running the script, you can fix this by running the following command:\n\n   ```bash\n    sed -i -e 's/\\r$//' deploy-workload-function.sh\n   ```\n"
  },
  {
    "path": "scenarios/workload-functions/bicep/apim/config.bicep",
    "content": "param apimName string\n\nparam backendHostName string\n\nvar backendUri = '${backendHostName}/api/HttpExample'\n\nresource apiManagementInstance 'Microsoft.ApiManagement/service@2022-09-01-preview' existing = {\n  name: apimName\n}\n\nresource helloApi 'Microsoft.ApiManagement/service/apis@2020-12-01' = {\n  name: 'hello'\n  parent: apiManagementInstance\n  properties: {\n    path: 'hello'\n    apiRevision: '1'\n    displayName: 'Hello Api'\n    description: 'Hello Api'\n    subscriptionRequired: true\n    serviceUrl: backendUri\n    protocols: [\n      'https'\n    ]\n  }\n}\n\nresource helloApiPolicies 'Microsoft.ApiManagement/service/apis/policies@2020-12-01' = {\n  name: 'policy'\n  parent: helloApi\n  properties: {\n    value: '<!--\\r\\n    IMPORTANT:\\r\\n    - Policy elements can appear only within the <inbound>, <outbound>, <backend> section elements.\\r\\n    - To apply a policy to the incoming request (before it is forwarded to the backend service), place a corresponding policy element within the <inbound> section element.\\r\\n    - To apply a policy to the outgoing response (before it is sent back to the caller), place a corresponding policy element within the <outbound> section element.\\r\\n    - To add a policy, place the cursor at the desired insertion point and select a policy from the sidebar.\\r\\n    - To remove a policy, delete the corresponding policy statement from the policy document.\\r\\n    - Position the <base> element within a section element to inherit all policies from the corresponding section element in the enclosing scope.\\r\\n    - Remove the <base> element to prevent inheriting policies from the corresponding section element in the enclosing scope.\\r\\n    - Policies are applied in the order of their appearance, from the top down.\\r\\n    - Comments within policy elements are not supported and may disappear. Place your comments between policy elements or at a higher level scope.\\r\\n-->\\r\\n<policies>\\r\\n  <inbound>\\r\\n    <base />\\r\\n  </inbound>\\r\\n  <backend>\\r\\n    <base />\\r\\n  </backend>\\r\\n  <outbound>\\r\\n    <base />\\r\\n  </outbound>\\r\\n  <on-error>\\r\\n    <base />\\r\\n  </on-error>\\r\\n</policies>'\n    format: 'xml'\n  }\n}\n\nresource getOperation 'Microsoft.ApiManagement/service/apis/operations@2023-05-01-preview' = {\n  name: 'say'\n  parent: helloApi\n  properties: {\n      description: 'Say Hello'\n      displayName: 'say'\n      method: 'GET'\n      urlTemplate: '/'\n      request: {\n          description: 'Request description'\n          queryParameters: [\n              {\n                  name: 'name'\n                  required: true\n                  type: 'string'\n              }\n          ]\n      }\n  }\n}\n\n\n// Basic product\nresource basicProduct 'Microsoft.ApiManagement/service/products@2020-12-01' = {\n  name: 'hellobasic'\n  parent: apiManagementInstance\n  properties: {\n    displayName: 'hellow-basic'\n    description: 'Basic hellow product'\n    subscriptionRequired: true\n    approvalRequired: true\n    state: 'published'\n    subscriptionsLimit: 1\n    terms: 'These are the terms of use ...'\n  }\n  dependsOn: [helloApi]\n}\n\nresource basicProductPolicies 'Microsoft.ApiManagement/service/products/policies@2020-12-01' = {\n  name: 'policy'\n  parent: basicProduct\n  properties: {\n    value: '<policies>\\r\\n  <inbound>\\r\\n    <rate-limit calls=\"5\" renewal-period=\"60\" />\\r\\n    <quota calls=\"100\" renewal-period=\"604800\" />\\r\\n    <base />\\r\\n  </inbound>\\r\\n  <backend>\\r\\n    <base />\\r\\n  </backend>\\r\\n  <outbound>\\r\\n    <base />\\r\\n  </outbound>\\r\\n  <on-error>\\r\\n    <base />\\r\\n  </on-error>\\r\\n</policies>'\n    format: 'xml'\n  }\n}\n\nresource linkHelloApiToBasicProduct 'Microsoft.ApiManagement/service/products/apis@2020-12-01' = {\n  name: 'hello'\n  parent: basicProduct\n  dependsOn: [helloApi]\n}\n\nresource starterProduct 'Microsoft.ApiManagement/service/products@2020-12-01' existing = {\n  name: 'starter'\n  parent: apiManagementInstance\n}\n\nresource linkHelloApiToStarterProduct 'Microsoft.ApiManagement/service/products/apis@2020-12-01' = {\n  name: 'hello'\n  parent: starterProduct\n  dependsOn: [helloApi]\n}\n"
  },
  {
    "path": "scenarios/workload-functions/bicep/backend/backend.bicep",
    "content": "param resourceSuffix string\n\nparam vnetName string\nparam networkingResourceGroupName string\n\nparam privateEndpointSubnetid string\n\nparam location string\n\n@description('The language worker runtime to load in the function app.')\n@allowed([\n  'dotnet'\n  'node'\n  'python'\n  'java'\n])\nparam functionWorkerRuntime string = 'node'\n\n@description('Specifies the OS used for the Azure Function hosting plan.')\n@allowed([\n  'Windows'\n  'Linux'\n])\nparam functionPlanOS string = 'Windows'\n\n@description('Only required for Linux app to represent runtime stack in the format of \\'runtime|runtimeVersion\\'. For example: \\'python|3.9\\'')\nparam linuxFxVersion string = ''\n\nvar owner = 'APIM Const Set'\n\nvar storageAccounts_saapimcsbackend_name  = toLower(take(replace('stbknd${resourceSuffix}', '-',''), 24))\nvar storageAccounts_location = location\nvar storageAccounts_skuName  = 'Standard_LRS'\n\nvar storageAccounts_kind  = 'StorageV2'\nvar functionContentShareName = 'func-contents'\n\n\nvar storageAccounts_minTLSVersion = 'TLS1_2'\nvar privateEndpoint_storageaccount_queue_Name = 'pep-sa-queue-${resourceSuffix}'\nvar privateEndpoint_storageaccount_blob_Name = 'pep-sa-blob-${resourceSuffix}'\nvar privateEndpoint_storageaccount_file_Name = 'pep-sa-file-${resourceSuffix}'\nvar privateEndpoint_storageaccount_table_Name = 'pep-sa-table-${resourceSuffix}'\n\nvar serverfarms_appsvcplanAPIMCSBackend_name  = 'plan-be-${resourceSuffix}'\n\nvar serverfarms_appsvcplanAPIMCSBackend_location  = location\nvar functionAppPlanSku  = 'EP1'\nvar functionAppPlanSize  = 'EP1'\nvar functionAppPlanFamily  = 'EP'\nvar functionAppPlanTier  = 'ElasticPremium'\nvar functionAppPlanWorkerCount = 1\nvar isReserved = ((functionPlanOS == 'Linux') ? true : false)\n\nvar sites_funcappAPIMCSBackendMicroServiceA_identity = 'mi-func-code-be-${resourceSuffix}'\nvar sites_funcappAPIMCSBackendMicroServiceA_name = 'func-code-be-${resourceSuffix}'\nvar sites_funcappAPIMCSBackendMicroServiceA_location  = location\nvar sites_funcappAPIMCSBackendMicroServiceA_siteHostname   = 'func-code-be-${resourceSuffix}.azurewebsites.net'\nvar sites_funcappAPIMCSBackendMicroServiceA_repositoryHostname   = 'func-code-be-${resourceSuffix}.scm.azurewebsites.net'\nvar sites_funcappAPIMCSBackendMicroServiceA_siteName   = 'funccodebe${resourceSuffix}'\nvar privateEndpoint_funcappAPIMCSBackendMicroServiceA_name   = 'pep-func-code-be-${resourceSuffix}'\n\n\nmodule networking './modules/networking.bicep' = {\n  name: 'networkingresources'\n  scope: resourceGroup(networkingResourceGroupName)\n  params: {\n    location: location\n    resourceSuffix: resourceSuffix  \n    vnetName: vnetName\n  }\n}\n\nvar backendSubnetId = networking.outputs.backEndSubnetid\n\n//\n// Definitions\n//\n// Azure Storage Account\nresource storageAccounts_saapimcsbackend_name_resource 'Microsoft.Storage/storageAccounts@2021-06-01' = {\n  name: storageAccounts_saapimcsbackend_name\n  location: storageAccounts_location\n  tags: {\n    Owner: owner\n  }\n  sku: {\n    name: storageAccounts_skuName\n  }\n  kind: storageAccounts_kind\n  properties: {\n    minimumTlsVersion: storageAccounts_minTLSVersion\n    publicNetworkAccess: 'Disabled'\n    allowBlobPublicAccess: false\n    networkAcls: {\n      bypass: 'None'\n      defaultAction: 'Deny'\n    }\n    supportsHttpsTrafficOnly: true\n    encryption: {\n      services: {\n        file: {\n          keyType: 'Account'\n          enabled: true\n        }\n        blob: {\n          keyType: 'Account'\n          enabled: true\n        }\n      }\n      keySource: 'Microsoft.Storage'\n    }\n    accessTier: 'Hot'\n  }\n}\n\n\nmodule queueStoragePrivateEndpoint '../../../apim-baseline/bicep/shared/modules/privateendpoint.bicep' = {\n  name: privateEndpoint_storageaccount_queue_Name\n  params: {\n    location: location\n    privateEndpointName: privateEndpoint_storageaccount_queue_Name\n    domain: 'privatelink.queue.${environment().suffixes.storage}'\n    groupId: 'queue'\n    serviceResourceId: storageAccounts_saapimcsbackend_name_resource.id\n    vnetName: vnetName\n    networkingResourceGroupName: networkingResourceGroupName\n    subnetId: privateEndpointSubnetid\n  }\n}\n\nmodule blobStoragePrivateEndpoint '../../../apim-baseline/bicep/shared/modules/privateendpoint.bicep' = {\n  name: privateEndpoint_storageaccount_blob_Name\n  params: {\n    location: location\n    privateEndpointName: privateEndpoint_storageaccount_blob_Name\n    groupId: 'blob'\n    domain: 'privatelink.blob.${environment().suffixes.storage}'\n    serviceResourceId: storageAccounts_saapimcsbackend_name_resource.id\n    vnetName: vnetName\n    networkingResourceGroupName: networkingResourceGroupName\n    subnetId: privateEndpointSubnetid\n  }\n}\n\nmodule tableStoragePrivateEndpoint '../../../apim-baseline/bicep/shared/modules/privateendpoint.bicep' = {\n  name: privateEndpoint_storageaccount_table_Name\n  params: {\n    location: location\n    privateEndpointName: privateEndpoint_storageaccount_table_Name\n    groupId: 'table'\n    domain: 'privatelink.table.${environment().suffixes.storage}'\n    serviceResourceId: storageAccounts_saapimcsbackend_name_resource.id\n    vnetName: vnetName\n    networkingResourceGroupName: networkingResourceGroupName\n    subnetId: privateEndpointSubnetid\n  }\n}\n\nmodule fileStoragePrivateEndpoint '../../../apim-baseline/bicep/shared/modules/privateendpoint.bicep' = {\n  name: privateEndpoint_storageaccount_file_Name\n  params: {\n    location: location\n    privateEndpointName: privateEndpoint_storageaccount_file_Name\n    groupId: 'file'\n    domain: 'privatelink.file.${environment().suffixes.storage}'\n    serviceResourceId: storageAccounts_saapimcsbackend_name_resource.id\n    vnetName: vnetName\n    networkingResourceGroupName: networkingResourceGroupName\n    subnetId: privateEndpointSubnetid\n  }\n}\n\nresource fileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2021-04-01' = {\n  name: '${storageAccounts_saapimcsbackend_name_resource.name}/default/${functionContentShareName}'\n}\n\n// Azure Application Service Plan\nresource serverfarms_appsvcplanAPIMCSBackend_name_resource 'Microsoft.Web/serverfarms@2018-02-01' = {\n  name: serverfarms_appsvcplanAPIMCSBackend_name\n  location: serverfarms_appsvcplanAPIMCSBackend_location\n  tags: {\n    Owner: owner\n  }\n  sku: {\n    name:  functionAppPlanSku\n    tier: functionAppPlanTier\n    size: functionAppPlanSize\n    family: functionAppPlanFamily\n  }\n  kind: 'elastic'\n  properties: {\n    maximumElasticWorkerCount: functionAppPlanWorkerCount\n    reserved: isReserved\n  }\n}\n\nresource funcAppIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {\n  name: sites_funcappAPIMCSBackendMicroServiceA_identity\n  location: sites_funcappAPIMCSBackendMicroServiceA_location\n}\n\n// Azure Function App (Linux, .NET Core 3.1)\nresource sites_funcappAPIMCSBackendMicroServiceA_name_resource 'Microsoft.Web/sites@2018-11-01' = {\n  name: sites_funcappAPIMCSBackendMicroServiceA_name\n  location: sites_funcappAPIMCSBackendMicroServiceA_location\n  tags: {\n    Owner: owner\n  }\n  kind: (isReserved ? 'functionapp,linux' : 'functionapp')\n  identity: {\n    type: 'UserAssigned'\n    userAssignedIdentities: {\n      '${funcAppIdentity.id}': {}\n    }\n  }\n  properties: {\n    reserved: isReserved\n    serverFarmId: serverfarms_appsvcplanAPIMCSBackend_name_resource.id\n    enabled: true\n    hostNameSslStates: [\n      {\n        name: sites_funcappAPIMCSBackendMicroServiceA_siteHostname\n        sslState: 'Disabled'\n        hostType: 'Standard'\n      }\n      {\n        name: sites_funcappAPIMCSBackendMicroServiceA_repositoryHostname\n        sslState: 'Disabled'\n        hostType: 'Repository'\n      }\n    ]\n    siteConfig: {\n      linuxFxVersion: (isReserved ? linuxFxVersion : null)\n      appSettings: [\n        {\n          name: 'AzureWebJobsStorage'\n          value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccounts_saapimcsbackend_name};AccountKey=${storageAccounts_saapimcsbackend_name_resource.listKeys().keys[0].value}'\n        }\n        {\n          name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'\n          value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccounts_saapimcsbackend_name};AccountKey=${storageAccounts_saapimcsbackend_name_resource.listKeys().keys[0].value}'\n        }\n        {\n          name: 'FUNCTIONS_EXTENSION_VERSION'\n          value: '~4'\n        }\n        {\n          name: 'FUNCTIONS_WORKER_RUNTIME'\n          value: functionWorkerRuntime\n        }\n        {\n          name: 'WEBSITE_CONTENTOVERVNET'\n          value: '1'\n        }\n        {\n          name: 'WEBSITE_CONTENTSHARE'\n          value: functionContentShareName\n        }\n        {\n          name: 'WEBSITE_VNET_ROUTE_ALL'\n          value: '1'\n        }\n        {\n          name: 'WEBSITE_NODE_DEFAULT_VERSION'\n          value: '~20'\n        }\n      ]      \n    }\n    scmSiteAlsoStopped: false\n    clientAffinityEnabled: false\n    clientCertEnabled: false\n    hostNamesDisabled: false\n    containerSize: 1536\n    dailyMemoryTimeQuota: 0\n    httpsOnly: true\n    redundancyMode: 'None'\n  }\n  dependsOn: [\n    queueStoragePrivateEndpoint\n    blobStoragePrivateEndpoint\n    tableStoragePrivateEndpoint\n    fileStoragePrivateEndpoint\n  ]\n}\n\nresource sites_funcappAPIMCSBackendMicroServiceA_name_sites_funcappAPIMCSBackendMicroServiceA_name_azurewebsites_net 'Microsoft.Web/sites/hostNameBindings@2018-11-01' = {\n  parent: sites_funcappAPIMCSBackendMicroServiceA_name_resource\n  name: '${sites_funcappAPIMCSBackendMicroServiceA_name}.azurewebsites.net'\n  properties: {\n    siteName: sites_funcappAPIMCSBackendMicroServiceA_siteName\n    hostNameType: 'Verified'\n  }\n}\n\nresource planNetworkConfig 'Microsoft.Web/sites/networkConfig@2021-01-01' = {\n  parent: sites_funcappAPIMCSBackendMicroServiceA_name_resource\n  name: 'virtualNetwork'\n  properties: {\n    subnetResourceId: backendSubnetId\n    swiftSupported: true\n  }\n}\n\nvar privateDNSZoneName = 'privatelink.azurewebsites.net'\n\nresource vnet 'Microsoft.Network/virtualNetworks@2021-02-01' existing = {\n  name: vnetName\n  scope: resourceGroup(networkingResourceGroupName)\n}\n\nresource privateEndpoint 'Microsoft.Network/privateEndpoints@2021-03-01' = {\n  name: privateEndpoint_funcappAPIMCSBackendMicroServiceA_name\n  location: location\n  properties: {\n    subnet: {\n      id: privateEndpointSubnetid\n    }\n    privateLinkServiceConnections: [\n      {\n        name: privateEndpoint_funcappAPIMCSBackendMicroServiceA_name\n        properties: {\n          privateLinkServiceId: sites_funcappAPIMCSBackendMicroServiceA_name_resource.id\n          groupIds: [\n            'sites'\n          ]\n        }\n      }\n    ]\n  }\n}\n\nresource privateDnsZones 'Microsoft.Network/privateDnsZones@2018-09-01' = {\n  name: privateDNSZoneName\n  location: 'global'\n}\n\nresource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2018-09-01' = {\n  parent: privateDnsZones\n  name: uniqueString(vnet.id)\n  location: 'global'\n  properties: {\n    registrationEnabled: false\n    virtualNetwork: {\n      id: vnet.id\n    }\n  }\n  dependsOn: [\n    privateEndpoint\n  ]\n}\n\nresource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2020-03-01' = {\n  parent: privateEndpoint\n  name: 'default'\n  properties: {\n    privateDnsZoneConfigs: [\n      {\n        name: '${sites_funcappAPIMCSBackendMicroServiceA_siteHostname}-azurewebsites-net'\n        properties: {\n          privateDnsZoneId: privateDnsZones.id\n        }\n      }\n    ]\n  }\n  dependsOn: [\n    privateDnsZoneLink\n  ]\n}\n\noutput funcAppIdentityName string = funcAppIdentity.name\noutput funcAppName string = sites_funcappAPIMCSBackendMicroServiceA_name_resource.name\noutput backendHostName string = 'https://${sites_funcappAPIMCSBackendMicroServiceA_name}.azurewebsites.net'\n"
  },
  {
    "path": "scenarios/workload-functions/bicep/backend/modules/networking.bicep",
    "content": "param resourceSuffix string\n\nparam location string\n\nparam vnetName string\n\nparam backEndAddressPrefix string = '10.2.6.0/24'\n\nvar backEndSubnetName = 'snet-bcke-${resourceSuffix}'\nvar backEndSNNSG = 'nsg-bcke-${resourceSuffix}'\n\nresource vnet 'Microsoft.Network/virtualNetworks@2021-02-01' existing = {\n  name: vnetName\n}\n\nresource subnetBackend 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' = {\n  name: backEndSubnetName\n  parent: vnet\n  properties: {\n    addressPrefix: backEndAddressPrefix\n    delegations: [\n      {\n        name: 'delegation'\n        properties: {\n          serviceName: 'Microsoft.Web/serverfarms'\n        }\n      }\n    ]\n    privateEndpointNetworkPolicies: 'Enabled'\n    networkSecurityGroup: {\n      id: backEndNSG.id\n    }\n  }\n}\n\nresource backEndNSG 'Microsoft.Network/networkSecurityGroups@2020-06-01' = {\n  name: backEndSNNSG\n  location: location\n  properties: {\n    securityRules: [\n    ]\n  }\n}\n\noutput backEndSubnetName string = backEndSubnetName \noutput backEndSubnetid string = subnetBackend.id\n"
  },
  {
    "path": "scenarios/workload-functions/bicep/deploy/deploy.bicep",
    "content": "param resourceSuffix string\nparam location string\nparam funcAppName string\nparam deploymentIdentityName string\nparam deploymentSubnetId     string\nparam deploymentStorageName    string\nparam deploymentIdentityResourceGroupName string\n\nparam utcValue string = utcNow()\n\nresource functionApp 'Microsoft.Web/sites@2018-11-01' existing = {\n  name: funcAppName\n}\n\nresource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = {\n  scope: resourceGroup(deploymentIdentityResourceGroupName)\n  name: deploymentIdentityName\n}\n\nresource generalContributor 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {\n  name: 'b24988ac-6180-42a0-ab88-20f7382dd24c' // Storage File Data Privileged Contributor\n  scope: tenant()\n}\n\nresource roleAssignmentFunctionApp 'Microsoft.Authorization/roleAssignments@2022-04-01' = {\n  scope: functionApp\n\n  name: guid(generalContributor.id, userAssignedIdentity.id, functionApp.id)\n  properties: {\n    principalId: userAssignedIdentity.properties.principalId\n    roleDefinitionId: generalContributor.id\n    principalType: 'ServicePrincipal'\n  }\n}\n\nresource dsFunctionApp 'Microsoft.Resources/deploymentScripts@2023-08-01' = {\n  name: 'deploy-script-${resourceSuffix}'\n  location: location\n  identity: {\n    type: 'userAssigned'\n    userAssignedIdentities: {\n      '${userAssignedIdentity.id}': {}\n    }\n  }\n  kind: 'AzureCLI'  \n  properties: {\n    forceUpdateTag: utcValue\n    azCliVersion: '2.52.0'\n    storageAccountSettings: {\n      storageAccountName: deploymentStorageName\n    }\n    containerSettings: {\n      subnetIds: [\n        {\n          id: deploymentSubnetId\n        }\n      ]\n    }      \n    scriptContent: 'git clone https://github.com/Azure-Samples/functions-quickstart-javascript; cd functions-quickstart-javascript; zip -r helloworld-latest.zip .; az functionapp deployment source config-zip -g ${resourceGroup().name} -n ${funcAppName} --src helloworld-latest.zip'\n    retentionInterval: 'P1D'\n    cleanupPreference: 'OnExpiration'\n  \n  }\n  dependsOn: [\n    roleAssignmentFunctionApp\n  ]\n}\n\n"
  },
  {
    "path": "scenarios/workload-functions/bicep/deploy/modules/networking.bicep",
    "content": "param vnetName string\nparam resourceSuffix string\nparam deploymentAddressPrefix string = '10.2.8.0/24'\n\nvar deploymentSubnetName = 'snet-deploy-${resourceSuffix}'\n\nresource vnet 'Microsoft.Network/virtualNetworks@2021-02-01' existing = {\n  name: vnetName\n}\n\nresource subnetDeploy 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' = {\n  name: deploymentSubnetName\n  parent: vnet\n  properties: {\n    addressPrefix: deploymentAddressPrefix\n    serviceEndpoints: [\n      {\n        service: 'Microsoft.Storage'\n      }\n    ]\n    delegations: [\n      {\n        name: 'Microsoft.ContainerInstance.containerGroups'\n        properties: {\n          serviceName: 'Microsoft.ContainerInstance/containerGroups'\n        }\n      }\n    ]\n  }\n}\n\noutput subnetDeployId string = subnetDeploy.id\noutput subnetDeployName string = subnetDeploy.name\n"
  },
  {
    "path": "scenarios/workload-functions/bicep/main.bicep",
    "content": "targetScope='subscription'\n\nparam resourceSuffix string\nparam networkingResourceGroupName string\nparam apimResourceGroupName string\nparam apimName string\nparam vnetName string\nparam privateEndpointSubnetid string\nparam deploymentIdentityName string\nparam deploymentSubnetId     string\nparam deploymentStorageName    string\nparam sharedResourceGroupName string\n\nparam location string = deployment().location\n\n@description('Enable sending usage and telemetry feedback to Microsoft.')\nparam enableTelemetry bool = true\nvar telemetryId = 'ab1e5729-7452-41b2-9fbb-945cc51d9cd0-${location}-apimsb-functions'\n\nvar workloadResourceGroupName = 'rg-functions-${resourceSuffix}'\n\nresource workloadResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {\n  name: workloadResourceGroupName\n  location: location\n}\n\nmodule backend './backend/backend.bicep' = {\n  name: 'backendresources'\n  scope: resourceGroup(workloadResourceGroup.name)\n  params: {\n    location: location \n    resourceSuffix: resourceSuffix   \n    vnetName: vnetName\n    networkingResourceGroupName: networkingResourceGroupName\n    privateEndpointSubnetid: privateEndpointSubnetid\n  }\n}\n\nmodule deploy './deploy/deploy.bicep' = {\n  name: 'deploy'\n  scope: resourceGroup(workloadResourceGroup.name)\n  params: {\n    location: location\n    resourceSuffix: resourceSuffix\n    funcAppName: backend.outputs.funcAppName\n    deploymentIdentityName: deploymentIdentityName\n    deploymentSubnetId: deploymentSubnetId\n    deploymentStorageName: deploymentStorageName\n    deploymentIdentityResourceGroupName: sharedResourceGroupName\n  }\n  dependsOn: [\n    backend\n  ]\n}\n\nmodule apimConfig './apim/config.bicep' = {\n  name: 'apimConfig'\n  scope: resourceGroup(apimResourceGroupName)\n  params: {\n    apimName: apimName\n    backendHostName: backend.outputs.backendHostName\n  }\n  dependsOn: [\n    deploy\n  ]\n}\n\n@description('Microsoft telemetry deployment.')\n#disable-next-line no-deployments-resources\nresource telemetrydeployment 'Microsoft.Resources/deployments@2021-04-01' = if (enableTelemetry) {\n  location: location\n  name: telemetryId\n  properties: {\n    mode: 'Incremental'\n    template: {\n      '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#'\n      contentVersion: '1.0.0.0'\n      resources: {}\n    }\n  }\n}\n\noutput backendHostName string = backend.outputs.backendHostName\n"
  },
  {
    "path": "scenarios/workload-genai/README.md",
    "content": "# Scenario 3: Azure API Management - Generative AI resources as backend\n\nThis reference implementation demonstrates how to provision and interact with Generative AI resources through API Management. The implementation is on top of the [APIM baseline](./../apim-baseline/README.md) and additionally includes private deployments of Azure OpenAI endpoints, and the policies for the [following capabilities](#genai-gateway-capabilities) that are specifically tailored for GenAI use cases.\n\nBy the end of this deployment guide, you would have deployed private Azure OpenAI endpoints and an opinionated set of policies in APIM to manage traffic to these endpoints. You can then test the policies by sending requests to the APIM gateway, and can modify either to include the policy fragments [listed here](#scenarios-handled-by-this-accelerator) or to include your own custom policies.\n\n## Architecture\n\n![Architectural diagram showing an Azure API Management deployment in a virtual network with AOAI as backend.](../../docs/images/apim-workload-ai.jpeg)\n\n### Core components\n\n- Azure OpenAI endpoints\n- Azure Event Hub\n- Azure Private Endpoint\n- Azure Private DNS Zones\n\n### GenAI Gateway capabilities\n\n![GenAI capabilities](../../docs/images/genai-capabilities.jpg)\n\n## Deploy the reference implementation\n\nThis reference implementation is provided with the following infrastructure as code options. Select the deployment guide you are interested in. They both deploy the same implementation.\n\n:arrow_forward: [Bicep-based deployment guide](./bicep/README.md)\n:arrow_forward: [Terraform-based deployment guide](./terraform/README.md)\n\n## GenAI Gateway\n\nA \"GenAI Gateway\" serves as an intelligent interface/middleware that dynamically balances incoming traffic across backend resources to achieve optimizing resource utilization. In addition to load balancing, GenAI Gateway can be equipped with extra capabilities to address the challenges around billing, monitoring etc.\n\nTo read more about considerations when implementing a GenAI Gateway, see [this article](https://learn.microsoft.com/ai/playbook/technology-guidance/generative-ai/dev-starters/genai-gateway/).\n\nThis accelerator contains APIM policies showing how to implement different [GenAI Gateway capabilities](#genai-gateway-capabilities) in APIM, along with code to enable you to deploy the policies and see them in action.\n\n### Scenarios handled by this accelerator\n\nThis repo currently contains the policies showing how to implement these GenAI Gateway capabilities:\n\n| Capability                                                                      | Description                                                             |\n| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |\n| [Load balancing (round-robin)](./policies/fragments/load-balancing/README.md) | Load balance traffic across PAYG endpoints using simple and weighted round-robin algorithm. |\n| [Managing spikes with PAYG](./policies/fragments/manage-spikes-with-payg/README.md) | Manage spikes in traffic by routing traffic to PAYG endpoints when a PTU is out of capacity. |\n| [Adaptive rate limiting](./policies/fragments/rate-limiting/README.md) | Dynamically adjust rate-limits applied to different workloads|\n| [Tracking token usage](./policies/fragments/usage-tracking//README.md) | Record the token consumption for usage tracking and attribution|\n| [Multi-tenancy](./policies/multi-tenancy/README.md)| Implementing multi-tenancy using Products and Product Policies|\n\n### Test/Demo setup\n\nIf you are looking for a quick way to test or demo these capabilities with a minimalistic non production like APIM setup against a Azure OpenAI simulator, check out this repository.\n\n:arrow_forward: [APIM GenAI Gateway Toolkit](https://github.com/Azure-Samples/apim-genai-gateway-toolkit)\n\n## AI Hub Gateway capabilities\n\nLooking for comprehensive reference implementation to provision your AI Hub Gateway? Check out AI Hub Gateway scenario.\n\n:arrow_forward: [AI Hub Gateway](https://github.com/Azure-Samples/ai-hub-gateway-solution-accelerator)\n"
  },
  {
    "path": "scenarios/workload-genai/bicep/README.md",
    "content": "# Scenario 3: Azure API Management - Gen AI Backend  [Bicep]\n\nThis is the Bicep-based deployment guide for [Scenario 3: Azure API Management - Gen AI Backend](../README.md).\n\n## Prerequisites\n\nThis scenario requires the completion of the [Azure API Management - Secure Baseline](../../apim-baseline/README.md) scenario.\n\n## Steps\n\nRun the following command to deploy the scenarios\n\n```bash\n./scripts/bicep/deploy-workload-genai.sh\n```\n\nTest the hello api using hte generated command from the output\n\n## Troubleshooting\n\nIf you see the message `-bash: ./deploy-workload-genai.sh: /bin/bash^M: bad interpreter: No such file or directory` when running the script, you can fix this by running the following command:\n\n   ```bash\n    sed -i -e 's/\\r$//' deploy-workload-genai.sh\n   ```\n"
  },
  {
    "path": "scenarios/workload-genai/bicep/apim-policies/api-specs/openapi-spec.json",
    "content": "{\n    \"openapi\": \"3.0.0\",\n    \"info\": {\n      \"title\": \"Azure OpenAI Service API\",\n      \"description\": \"Azure OpenAI APIs for completions and search\",\n      \"version\": \"2024-02-01\"\n    },\n    \"servers\": [\n      {\n        \"url\": \"https://change-this.com/openai\"\n      }\n    ],\n    \"security\": [\n      {\n        \"bearer\": [\n          \"api.read\"\n        ]\n      },\n      {\n        \"apiKey\": []\n      }\n    ],\n    \"paths\": {\n      \"/deployments/{deployment-id}/completions\": {\n        \"post\": {\n          \"summary\": \"Creates a completion for the provided prompt, parameters and chosen model.\",\n          \"operationId\": \"Completions_Create\",\n          \"parameters\": [\n            {\n              \"in\": \"path\",\n              \"name\": \"deployment-id\",\n              \"required\": true,\n              \"schema\": {\n                \"type\": \"string\",\n                \"example\": \"davinci\",\n                \"description\": \"Deployment id of the model which was deployed.\"\n              }\n            },\n            {\n              \"in\": \"query\",\n              \"name\": \"api-version\",\n              \"required\": true,\n              \"schema\": {\n                \"type\": \"string\",\n                \"example\": \"2024-02-01\",\n                \"description\": \"api version\"\n              }\n            }\n          ],\n          \"requestBody\": {\n            \"required\": true,\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"prompt\": {\n                      \"description\": \"The prompt(s) to generate completions for, encoded as a string or array of strings.\\nNote that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document. Maximum allowed size of string list is 2048.\",\n                      \"oneOf\": [\n                        {\n                          \"type\": \"string\",\n                          \"default\": \"\",\n                          \"example\": \"This is a test.\",\n                          \"nullable\": true\n                        },\n                        {\n                          \"type\": \"array\",\n                          \"items\": {\n                            \"type\": \"string\",\n                            \"default\": \"\",\n                            \"example\": \"This is a test.\",\n                            \"nullable\": false\n                          },\n                          \"description\": \"Array size minimum of 1 and maximum of 2048\"\n                        }\n                      ]\n                    },\n                    \"max_tokens\": {\n                      \"description\": \"The token count of your prompt plus max_tokens cannot exceed the model's context length. Most models have a context length of 2048 tokens (except for the newest models, which support 4096). Has minimum of 0.\",\n                      \"type\": \"integer\",\n                      \"default\": 16,\n                      \"example\": 16,\n                      \"nullable\": true\n                    },\n                    \"temperature\": {\n                      \"description\": \"What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer.\\nWe generally recommend altering this or top_p but not both.\",\n                      \"type\": \"number\",\n                      \"default\": 1,\n                      \"example\": 1,\n                      \"nullable\": true\n                    },\n                    \"top_p\": {\n                      \"description\": \"An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\\nWe generally recommend altering this or temperature but not both.\",\n                      \"type\": \"number\",\n                      \"default\": 1,\n                      \"example\": 1,\n                      \"nullable\": true\n                    },\n                    \"logit_bias\": {\n                      \"description\": \"Defaults to null. Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this tokenizer tool (which works for both GPT-2 and GPT-3) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. As an example, you can pass {\\\"50256\\\" &#58; -100} to prevent the <|endoftext|> token from being generated.\",\n                      \"type\": \"object\",\n                      \"nullable\": false\n                    },\n                    \"user\": {\n                      \"description\": \"A unique identifier representing your end-user, which can help monitoring and detecting abuse\",\n                      \"type\": \"string\",\n                      \"nullable\": false\n                    },\n                    \"n\": {\n                      \"description\": \"How many completions to generate for each prompt. Minimum of 1 and maximum of 128 allowed.\\nNote: Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for max_tokens and stop.\",\n                      \"type\": \"integer\",\n                      \"default\": 1,\n                      \"example\": 1,\n                      \"nullable\": true\n                    },\n                    \"stream\": {\n                      \"description\": \"Whether to stream back partial progress. If set, tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a data: [DONE] message.\",\n                      \"type\": \"boolean\",\n                      \"nullable\": true,\n                      \"default\": false\n                    },\n                    \"logprobs\": {\n                      \"description\": \"Include the log probabilities on the logprobs most likely tokens, as well the chosen tokens. For example, if logprobs is 5, the API will return a list of the 5 most likely tokens. The API will always return the logprob of the sampled token, so there may be up to logprobs+1 elements in the response.\\nMinimum of 0 and maximum of 5 allowed.\",\n                      \"type\": \"integer\",\n                      \"default\": null,\n                      \"nullable\": true\n                    },\n                    \"suffix\": {\n                      \"type\": \"string\",\n                      \"nullable\": true,\n                      \"description\": \"The suffix that comes after a completion of inserted text.\"\n                    },\n                    \"echo\": {\n                      \"description\": \"Echo back the prompt in addition to the completion\",\n                      \"type\": \"boolean\",\n                      \"default\": false,\n                      \"nullable\": true\n                    },\n                    \"stop\": {\n                      \"description\": \"Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence.\",\n                      \"oneOf\": [\n                        {\n                          \"type\": \"string\",\n                          \"default\": \"<|endoftext|>\",\n                          \"example\": \"\\n\",\n                          \"nullable\": true\n                        },\n                        {\n                          \"type\": \"array\",\n                          \"items\": {\n                            \"type\": \"string\",\n                            \"example\": \"\\n\",\n                            \"nullable\": false\n                          },\n                          \"description\": \"Array minimum size of 1 and maximum of 4\"\n                        }\n                      ]\n                    },\n                    \"completion_config\": {\n                      \"type\": \"string\",\n                      \"nullable\": true\n                    },\n                    \"presence_penalty\": {\n                      \"description\": \"Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.\",\n                      \"type\": \"number\",\n                      \"default\": 0\n                    },\n                    \"frequency_penalty\": {\n                      \"description\": \"Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.\",\n                      \"type\": \"number\",\n                      \"default\": 0\n                    },\n                    \"best_of\": {\n                      \"description\": \"Generates best_of completions server-side and returns the \\\"best\\\" (the one with the highest log probability per token). Results cannot be streamed.\\nWhen used with n, best_of controls the number of candidate completions and n specifies how many to return - best_of must be greater than n.\\nNote: Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for max_tokens and stop. Has maximum value of 128.\",\n                      \"type\": \"integer\"\n                    }\n                  }\n                },\n                \"example\": {\n                  \"prompt\": \"Negate the following sentence.The price for bubblegum increased on thursday.\\n\\n Negated Sentence:\",\n                  \"max_tokens\": 50\n                }\n              }\n            }\n          },\n          \"responses\": {\n            \"200\": {\n              \"description\": \"OK\",\n              \"content\": {\n                \"application/json\": {\n                  \"schema\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                      \"id\": {\n                        \"type\": \"string\"\n                      },\n                      \"object\": {\n                        \"type\": \"string\"\n                      },\n                      \"created\": {\n                        \"type\": \"integer\"\n                      },\n                      \"model\": {\n                        \"type\": \"string\"\n                      },\n                      \"prompt_filter_results\": {\n                        \"$ref\": \"#/components/schemas/promptFilterResults\"\n                      },\n                      \"choices\": {\n                        \"type\": \"array\",\n                        \"items\": {\n                          \"type\": \"object\",\n                          \"properties\": {\n                            \"text\": {\n                              \"type\": \"string\"\n                            },\n                            \"index\": {\n                              \"type\": \"integer\"\n                            },\n                            \"logprobs\": {\n                              \"type\": \"object\",\n                              \"properties\": {\n                                \"tokens\": {\n                                  \"type\": \"array\",\n                                  \"items\": {\n                                    \"type\": \"string\"\n                                  }\n                                },\n                                \"token_logprobs\": {\n                                  \"type\": \"array\",\n                                  \"items\": {\n                                    \"type\": \"number\"\n                                  }\n                                },\n                                \"top_logprobs\": {\n                                  \"type\": \"array\",\n                                  \"items\": {\n                                    \"type\": \"object\",\n                                    \"additionalProperties\": {\n                                      \"type\": \"number\"\n                                    }\n                                  }\n                                },\n                                \"text_offset\": {\n                                  \"type\": \"array\",\n                                  \"items\": {\n                                    \"type\": \"integer\"\n                                  }\n                                }\n                              },\n                              \"nullable\": true\n                            },\n                            \"finish_reason\": {\n                              \"type\": \"string\"\n                            },\n                            \"content_filter_results\": {\n                              \"$ref\": \"#/components/schemas/contentFilterChoiceResults\"\n                            }\n                          }\n                        }\n                      },\n                      \"usage\": {\n                        \"type\": \"object\",\n                        \"properties\": {\n                          \"completion_tokens\": {\n                            \"type\": \"number\",\n                            \"format\": \"int32\"\n                          },\n                          \"prompt_tokens\": {\n                            \"type\": \"number\",\n                            \"format\": \"int32\"\n                          },\n                          \"total_tokens\": {\n                            \"type\": \"number\",\n                            \"format\": \"int32\"\n                          }\n                        },\n                        \"required\": [\n                          \"prompt_tokens\",\n                          \"total_tokens\",\n                          \"completion_tokens\"\n                        ]\n                      }\n                    },\n                    \"required\": [\n                      \"id\",\n                      \"object\",\n                      \"created\",\n                      \"model\",\n                      \"choices\"\n                    ]\n                  },\n                  \"example\": {\n                    \"model\": \"davinci\",\n                    \"object\": \"text_completion\",\n                    \"id\": \"cmpl-4509KAos68kxOqpE2uYGw81j6m7uo\",\n                    \"created\": 1637097562,\n                    \"choices\": [\n                      {\n                        \"index\": 0,\n                        \"text\": \"The price for bubblegum decreased on thursday.\",\n                        \"logprobs\": null,\n                        \"finish_reason\": \"stop\"\n                      }\n                    ]\n                  }\n                }\n              },\n              \"headers\": {\n                \"apim-request-id\": {\n                  \"description\": \"Request ID for troubleshooting purposes\",\n                  \"schema\": {\n                    \"type\": \"string\"\n                  }\n                }\n              }\n            },\n            \"default\": {\n              \"description\": \"Service unavailable\",\n              \"content\": {\n                \"application/json\": {\n                  \"schema\": {\n                    \"$ref\": \"#/components/schemas/errorResponse\"\n                  }\n                }\n              },\n              \"headers\": {\n                \"apim-request-id\": {\n                  \"description\": \"Request ID for troubleshooting purposes\",\n                  \"schema\": {\n                    \"type\": \"string\"\n                  }\n                }\n              }\n            }\n          },\n          \"x-ms-examples\": {\n            \"Create a completion.\": {\n              \"$ref\": \"./examples/completions.json\"\n            }\n          }\n        }\n      },\n      \"/deployments/{deployment-id}/embeddings\": {\n        \"post\": {\n          \"summary\": \"Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms.\",\n          \"operationId\": \"embeddings_create\",\n          \"parameters\": [\n            {\n              \"in\": \"path\",\n              \"name\": \"deployment-id\",\n              \"required\": true,\n              \"schema\": {\n                \"type\": \"string\",\n                \"example\": \"ada-search-index-v1\"\n              },\n              \"description\": \"The deployment id of the model which was deployed.\"\n            },\n            {\n              \"in\": \"query\",\n              \"name\": \"api-version\",\n              \"required\": true,\n              \"schema\": {\n                \"type\": \"string\",\n                \"example\": \"2024-02-01\",\n                \"description\": \"api version\"\n              }\n            }\n          ],\n          \"requestBody\": {\n            \"required\": true,\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": true,\n                  \"properties\": {\n                    \"input\": {\n                      \"description\": \"Input text to get embeddings for, encoded as a string. To get embeddings for multiple inputs in a single request, pass an array of strings. Each input must not exceed 2048 tokens in length.\\nUnless you are embedding code, we suggest replacing newlines (\\\\n) in your input with a single space, as we have observed inferior results when newlines are present.\",\n                      \"oneOf\": [\n                        {\n                          \"type\": \"string\",\n                          \"default\": \"\",\n                          \"example\": \"This is a test.\",\n                          \"nullable\": true\n                        },\n                        {\n                          \"type\": \"array\",\n                          \"minItems\": 1,\n                          \"maxItems\": 2048,\n                          \"items\": {\n                            \"type\": \"string\",\n                            \"minLength\": 1,\n                            \"example\": \"This is a test.\",\n                            \"nullable\": false\n                          }\n                        }\n                      ]\n                    },\n                    \"user\": {\n                      \"description\": \"A unique identifier representing your end-user, which can help monitoring and detecting abuse.\",\n                      \"type\": \"string\",\n                      \"nullable\": false\n                    },\n                    \"input_type\": {\n                      \"description\": \"input type of embedding search to use\",\n                      \"type\": \"string\",\n                      \"example\": \"query\"\n                    }\n                  },\n                  \"required\": [\n                    \"input\"\n                  ]\n                }\n              }\n            }\n          },\n          \"responses\": {\n            \"200\": {\n              \"description\": \"OK\",\n              \"content\": {\n                \"application/json\": {\n                  \"schema\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                      \"object\": {\n                        \"type\": \"string\"\n                      },\n                      \"model\": {\n                        \"type\": \"string\"\n                      },\n                      \"data\": {\n                        \"type\": \"array\",\n                        \"items\": {\n                          \"type\": \"object\",\n                          \"properties\": {\n                            \"index\": {\n                              \"type\": \"integer\"\n                            },\n                            \"object\": {\n                              \"type\": \"string\"\n                            },\n                            \"embedding\": {\n                              \"type\": \"array\",\n                              \"items\": {\n                                \"type\": \"number\"\n                              }\n                            }\n                          },\n                          \"required\": [\n                            \"index\",\n                            \"object\",\n                            \"embedding\"\n                          ]\n                        }\n                      },\n                      \"usage\": {\n                        \"type\": \"object\",\n                        \"properties\": {\n                          \"prompt_tokens\": {\n                            \"type\": \"integer\"\n                          },\n                          \"total_tokens\": {\n                            \"type\": \"integer\"\n                          }\n                        },\n                        \"required\": [\n                          \"prompt_tokens\",\n                          \"total_tokens\"\n                        ]\n                      }\n                    },\n                    \"required\": [\n                      \"object\",\n                      \"model\",\n                      \"data\",\n                      \"usage\"\n                    ]\n                  }\n                }\n              }\n            }\n          },\n          \"x-ms-examples\": {\n            \"Create a embeddings.\": {\n              \"$ref\": \"./examples/embeddings.json\"\n            }\n          }\n        }\n      },\n      \"/deployments/{deployment-id}/chat/completions\": {\n        \"post\": {\n          \"summary\": \"Creates a completion for the chat message\",\n          \"operationId\": \"ChatCompletions_Create\",\n          \"parameters\": [\n            {\n              \"in\": \"path\",\n              \"name\": \"deployment-id\",\n              \"required\": true,\n              \"schema\": {\n                \"type\": \"string\",\n                \"description\": \"Deployment id of the model which was deployed.\"\n              }\n            },\n            {\n              \"in\": \"query\",\n              \"name\": \"api-version\",\n              \"required\": true,\n              \"schema\": {\n                \"type\": \"string\",\n                \"example\": \"2024-02-01\",\n                \"description\": \"api version\"\n              }\n            }\n          ],\n          \"requestBody\": {\n            \"required\": true,\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/createChatCompletionRequest\"\n                }\n              }\n            }\n          },\n          \"responses\": {\n            \"200\": {\n              \"description\": \"OK\",\n              \"content\": {\n                \"application/json\": {\n                  \"schema\": {\n                    \"$ref\": \"#/components/schemas/createChatCompletionResponse\"\n                  }\n                }\n              },\n              \"headers\": {\n                \"apim-request-id\": {\n                  \"description\": \"Request ID for troubleshooting purposes\",\n                  \"schema\": {\n                    \"type\": \"string\"\n                  }\n                }\n              }\n            },\n            \"default\": {\n              \"description\": \"Service unavailable\",\n              \"content\": {\n                \"application/json\": {\n                  \"schema\": {\n                    \"$ref\": \"#/components/schemas/errorResponse\"\n                  }\n                }\n              },\n              \"headers\": {\n                \"apim-request-id\": {\n                  \"description\": \"Request ID for troubleshooting purposes\",\n                  \"schema\": {\n                    \"type\": \"string\"\n                  }\n                }\n              }\n            }\n          },\n          \"x-ms-examples\": {\n            \"Create a chat completion.\": {\n              \"$ref\": \"./examples/chat_completions.json\"\n            },\n            \"Creates a completion based on Azure Search data and system-assigned managed identity.\": {\n              \"$ref\": \"./examples/chat_completions_azure_search_minimum.json\"\n            },\n            \"Creates a completion based on Azure Search vector data, previous assistant message and user-assigned managed identity.\": {\n              \"$ref\": \"./examples/chat_completions_azure_search_advanced.json\"\n            },\n            \"Creates a completion for the provided Azure Cosmos DB.\": {\n              \"$ref\": \"./examples/chat_completions_cosmos_db.json\"\n            }\n          }\n        }\n      }\n    },\n    \"components\": {\n      \"schemas\": {\n        \"errorResponse\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"error\": {\n              \"$ref\": \"#/components/schemas/error\"\n            }\n          }\n        },\n        \"errorBase\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"code\": {\n              \"type\": \"string\"\n            },\n            \"message\": {\n              \"type\": \"string\"\n            }\n          }\n        },\n        \"error\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/errorBase\"\n            }\n          ],\n          \"properties\": {\n            \"param\": {\n              \"type\": \"string\"\n            },\n            \"type\": {\n              \"type\": \"string\"\n            },\n            \"inner_error\": {\n              \"$ref\": \"#/components/schemas/innerError\"\n            }\n          }\n        },\n        \"innerError\": {\n          \"description\": \"Inner error with additional details.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"code\": {\n              \"$ref\": \"#/components/schemas/innerErrorCode\"\n            },\n            \"content_filter_results\": {\n              \"$ref\": \"#/components/schemas/contentFilterPromptResults\"\n            }\n          }\n        },\n        \"innerErrorCode\": {\n          \"description\": \"Error codes for the inner error object.\",\n          \"enum\": [\n            \"ResponsibleAIPolicyViolation\"\n          ],\n          \"type\": \"string\",\n          \"x-ms-enum\": {\n            \"name\": \"InnerErrorCode\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"ResponsibleAIPolicyViolation\",\n                \"description\": \"The prompt violated one of more content filter rules.\"\n              }\n            ]\n          }\n        },\n        \"dalleErrorResponse\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"error\": {\n              \"$ref\": \"#/components/schemas/dalleError\"\n            }\n          }\n        },\n        \"dalleError\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/errorBase\"\n            }\n          ],\n          \"properties\": {\n            \"param\": {\n              \"type\": \"string\"\n            },\n            \"type\": {\n              \"type\": \"string\"\n            },\n            \"inner_error\": {\n              \"$ref\": \"#/components/schemas/dalleInnerError\"\n            }\n          }\n        },\n        \"dalleInnerError\": {\n          \"description\": \"Inner error with additional details.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"code\": {\n              \"$ref\": \"#/components/schemas/innerErrorCode\"\n            },\n            \"content_filter_results\": {\n              \"$ref\": \"#/components/schemas/dalleFilterResults\"\n            },\n            \"revised_prompt\": {\n              \"type\": \"string\",\n              \"description\": \"The prompt that was used to generate the image, if there was any revision to the prompt.\"\n            }\n          }\n        },\n        \"contentFilterResultBase\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"filtered\": {\n              \"type\": \"boolean\"\n            }\n          },\n          \"required\": [\n            \"filtered\"\n          ]\n        },\n        \"contentFilterSeverityResult\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/contentFilterResultBase\"\n            },\n            {\n              \"properties\": {\n                \"severity\": {\n                  \"type\": \"string\",\n                  \"enum\": [\n                    \"safe\",\n                    \"low\",\n                    \"medium\",\n                    \"high\"\n                  ],\n                  \"x-ms-enum\": {\n                    \"name\": \"ContentFilterSeverity\",\n                    \"modelAsString\": true,\n                    \"values\": [\n                      {\n                        \"value\": \"safe\",\n                        \"description\": \"General content or related content in generic or non-harmful contexts.\"\n                      },\n                      {\n                        \"value\": \"low\",\n                        \"description\": \"Harmful content at a low intensity and risk level.\"\n                      },\n                      {\n                        \"value\": \"medium\",\n                        \"description\": \"Harmful content at a medium intensity and risk level.\"\n                      },\n                      {\n                        \"value\": \"high\",\n                        \"description\": \"Harmful content at a high intensity and risk level.\"\n                      }\n                    ]\n                  }\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"severity\",\n            \"filtered\"\n          ]\n        },\n        \"contentFilterDetectedResult\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/contentFilterResultBase\"\n            },\n            {\n              \"properties\": {\n                \"detected\": {\n                  \"type\": \"boolean\"\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"detected\",\n            \"filtered\"\n          ]\n        },\n        \"contentFilterDetectedWithCitationResult\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/contentFilterDetectedResult\"\n            },\n            {\n              \"properties\": {\n                \"citation\": {\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"URL\": {\n                      \"type\": \"string\"\n                    },\n                    \"license\": {\n                      \"type\": \"string\"\n                    }\n                  }\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"detected\",\n            \"filtered\"\n          ]\n        },\n        \"contentFilterResultsBase\": {\n          \"type\": \"object\",\n          \"description\": \"Information about the content filtering results.\",\n          \"properties\": {\n            \"sexual\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"violence\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"hate\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"self_harm\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"profanity\": {\n              \"$ref\": \"#/components/schemas/contentFilterDetectedResult\"\n            },\n            \"error\": {\n              \"$ref\": \"#/components/schemas/errorBase\"\n            }\n          }\n        },\n        \"contentFilterPromptResults\": {\n          \"type\": \"object\",\n          \"description\": \"Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/contentFilterResultsBase\"\n            },\n            {\n              \"properties\": {\n                \"jailbreak\": {\n                  \"$ref\": \"#/components/schemas/contentFilterDetectedResult\"\n                }\n              }\n            }\n          ]\n        },\n        \"contentFilterChoiceResults\": {\n          \"type\": \"object\",\n          \"description\": \"Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about third party text and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/contentFilterResultsBase\"\n            },\n            {\n              \"properties\": {\n                \"protected_material_text\": {\n                  \"$ref\": \"#/components/schemas/contentFilterDetectedResult\"\n                }\n              }\n            },\n            {\n              \"properties\": {\n                \"protected_material_code\": {\n                  \"$ref\": \"#/components/schemas/contentFilterDetectedWithCitationResult\"\n                }\n              }\n            }\n          ]\n        },\n        \"promptFilterResult\": {\n          \"type\": \"object\",\n          \"description\": \"Content filtering results for a single prompt in the request.\",\n          \"properties\": {\n            \"prompt_index\": {\n              \"type\": \"integer\"\n            },\n            \"content_filter_results\": {\n              \"$ref\": \"#/components/schemas/contentFilterPromptResults\"\n            }\n          }\n        },\n        \"promptFilterResults\": {\n          \"type\": \"array\",\n          \"description\": \"Content filtering results for zero or more prompts in the request. In a streaming request, results for different prompts may arrive at different times or in different orders.\",\n          \"items\": {\n            \"$ref\": \"#/components/schemas/promptFilterResult\"\n          }\n        },\n        \"dalleContentFilterResults\": {\n          \"type\": \"object\",\n          \"description\": \"Information about the content filtering results.\",\n          \"properties\": {\n            \"sexual\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"violence\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"hate\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"self_harm\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            }\n          }\n        },\n        \"dalleFilterResults\": {\n          \"type\": \"object\",\n          \"description\": \"Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/dalleContentFilterResults\"\n            },\n            {\n              \"properties\": {\n                \"profanity\": {\n                  \"$ref\": \"#/components/schemas/contentFilterDetectedResult\"\n                },\n                \"jailbreak\": {\n                  \"$ref\": \"#/components/schemas/contentFilterDetectedResult\"\n                }\n              }\n            }\n          ]\n        },\n        \"chatCompletionsRequestCommon\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"temperature\": {\n              \"description\": \"What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\\nWe generally recommend altering this or `top_p` but not both.\",\n              \"type\": \"number\",\n              \"minimum\": 0,\n              \"maximum\": 2,\n              \"default\": 1,\n              \"example\": 1,\n              \"nullable\": true\n            },\n            \"top_p\": {\n              \"description\": \"An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\\nWe generally recommend altering this or `temperature` but not both.\",\n              \"type\": \"number\",\n              \"minimum\": 0,\n              \"maximum\": 1,\n              \"default\": 1,\n              \"example\": 1,\n              \"nullable\": true\n            },\n            \"stream\": {\n              \"description\": \"If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a `data: [DONE]` message.\",\n              \"type\": \"boolean\",\n              \"nullable\": true,\n              \"default\": false\n            },\n            \"stop\": {\n              \"description\": \"Up to 4 sequences where the API will stop generating further tokens.\",\n              \"oneOf\": [\n                {\n                  \"type\": \"string\",\n                  \"nullable\": true\n                },\n                {\n                  \"type\": \"array\",\n                  \"items\": {\n                    \"type\": \"string\",\n                    \"nullable\": false\n                  },\n                  \"minItems\": 1,\n                  \"maxItems\": 4,\n                  \"description\": \"Array minimum size of 1 and maximum of 4\"\n                }\n              ],\n              \"default\": null\n            },\n            \"max_tokens\": {\n              \"description\": \"The maximum number of tokens allowed for the generated answer. By default, the number of tokens the model can return will be (4096 - prompt tokens).\",\n              \"type\": \"integer\",\n              \"default\": 4096\n            },\n            \"presence_penalty\": {\n              \"description\": \"Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.\",\n              \"type\": \"number\",\n              \"default\": 0,\n              \"minimum\": -2,\n              \"maximum\": 2\n            },\n            \"frequency_penalty\": {\n              \"description\": \"Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.\",\n              \"type\": \"number\",\n              \"default\": 0,\n              \"minimum\": -2,\n              \"maximum\": 2\n            },\n            \"logit_bias\": {\n              \"description\": \"Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.\",\n              \"type\": \"object\",\n              \"nullable\": true\n            },\n            \"user\": {\n              \"description\": \"A unique identifier representing your end-user, which can help Azure OpenAI to monitor and detect abuse.\",\n              \"type\": \"string\",\n              \"example\": \"user-1234\",\n              \"nullable\": false\n            }\n          }\n        },\n        \"createChatCompletionRequest\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionsRequestCommon\"\n            },\n            {\n              \"properties\": {\n                \"messages\": {\n                  \"description\": \"A list of messages comprising the conversation so far. [Example Python code](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb).\",\n                  \"type\": \"array\",\n                  \"minItems\": 1,\n                  \"items\": {\n                    \"$ref\": \"#/components/schemas/chatCompletionRequestMessage\"\n                  }\n                },\n                \"data_sources\": {\n                  \"type\": \"array\",\n                  \"description\": \"  The configuration entries for Azure OpenAI chat extensions that use them.\\n  This additional specification is only compatible with Azure OpenAI.\",\n                  \"items\": {\n                    \"$ref\": \"#/components/schemas/azureChatExtensionConfiguration\"\n                  }\n                },\n                \"n\": {\n                  \"type\": \"integer\",\n                  \"minimum\": 1,\n                  \"maximum\": 128,\n                  \"default\": 1,\n                  \"example\": 1,\n                  \"nullable\": true,\n                  \"description\": \"How many chat completion choices to generate for each input message.\"\n                },\n                \"seed\": {\n                  \"type\": \"integer\",\n                  \"minimum\": -9223372036854775808,\n                  \"maximum\": 9223372036854775807,\n                  \"default\": 0,\n                  \"example\": 1,\n                  \"nullable\": true,\n                  \"description\": \"If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.\"\n                },\n                \"response_format\": {\n                  \"type\": \"object\",\n                  \"description\": \"An object specifying the format that the model must output. Used to enable JSON mode.\",\n                  \"properties\": {\n                    \"type\": {\n                      \"$ref\": \"#/components/schemas/chatCompletionResponseFormat\"\n                    }\n                  }\n                },\n                \"tools\": {\n                  \"description\": \"A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for.\",\n                  \"type\": \"array\",\n                  \"minItems\": 1,\n                  \"items\": {\n                    \"$ref\": \"#/components/schemas/chatCompletionTool\"\n                  }\n                },\n                \"tool_choice\": {\n                  \"$ref\": \"#/components/schemas/chatCompletionToolChoiceOption\"\n                },\n                \"functions\": {\n                  \"description\": \"Deprecated in favor of `tools`. A list of functions the model may generate JSON inputs for.\",\n                  \"type\": \"array\",\n                  \"minItems\": 1,\n                  \"maxItems\": 128,\n                  \"items\": {\n                    \"$ref\": \"#/components/schemas/chatCompletionFunction\"\n                  }\n                },\n                \"function_call\": {\n                  \"description\": \"Deprecated in favor of `tool_choice`. Controls how the model responds to function calls. \\\"none\\\" means the model does not call a function, and responds to the end-user. \\\"auto\\\" means the model can pick between an end-user or calling a function.  Specifying a particular function via `{\\\"name\\\":\\\\ \\\"my_function\\\"}` forces the model to call that function. \\\"none\\\" is the default when no functions are present. \\\"auto\\\" is the default if functions are present.\",\n                  \"oneOf\": [\n                    {\n                      \"type\": \"string\",\n                      \"enum\": [\n                        \"none\",\n                        \"auto\"\n                      ],\n                      \"description\": \"`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.\"\n                    },\n                    {\n                      \"type\": \"object\",\n                      \"description\": \"Specifying a particular function via `{\\\"name\\\": \\\"my_function\\\"}` forces the model to call that function.\",\n                      \"properties\": {\n                        \"name\": {\n                          \"type\": \"string\",\n                          \"description\": \"The name of the function to call.\"\n                        }\n                      },\n                      \"required\": [\n                        \"name\"\n                      ]\n                    }\n                  ]\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"messages\"\n          ]\n        },\n        \"chatCompletionResponseFormat\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"text\",\n            \"json_object\"\n          ],\n          \"default\": \"text\",\n          \"example\": \"json_object\",\n          \"nullable\": true,\n          \"description\": \"Setting to `json_object` enables JSON mode. This guarantees that the message the model generates is valid JSON.\",\n          \"x-ms-enum\": {\n            \"name\": \"ChatCompletionResponseFormat\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"text\",\n                \"description\": \"Response format is a plain text string.\"\n              },\n              {\n                \"value\": \"json_object\",\n                \"description\": \"Response format is a JSON object.\"\n              }\n            ]\n          }\n        },\n        \"chatCompletionFunction\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"name\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.\"\n            },\n            \"description\": {\n              \"type\": \"string\",\n              \"description\": \"The description of what the function does.\"\n            },\n            \"parameters\": {\n              \"$ref\": \"#/components/schemas/chatCompletionFunctionParameters\"\n            }\n          },\n          \"required\": [\n            \"name\"\n          ]\n        },\n        \"chatCompletionFunctionParameters\": {\n          \"type\": \"object\",\n          \"description\": \"The parameters the functions accepts, described as a JSON Schema object. See the [guide](/docs/guides/gpt/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.\",\n          \"additionalProperties\": true\n        },\n        \"chatCompletionRequestMessage\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"role\": {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessageRole\"\n            }\n          },\n          \"discriminator\": {\n            \"propertyName\": \"role\",\n            \"mapping\": {\n              \"system\": \"#/components/schemas/chatCompletionRequestMessageSystem\",\n              \"user\": \"#/components/schemas/chatCompletionRequestMessageUser\",\n              \"assistant\": \"#/components/schemas/chatCompletionRequestMessageAssistant\",\n              \"tool\": \"#/components/schemas/chatCompletionRequestMessageTool\",\n              \"function\": \"#/components/schemas/chatCompletionRequestMessageFunction\"\n            }\n          },\n          \"required\": [\n            \"role\"\n          ]\n        },\n        \"chatCompletionRequestMessageRole\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"system\",\n            \"user\",\n            \"assistant\",\n            \"tool\",\n            \"function\"\n          ],\n          \"description\": \"The role of the messages author.\",\n          \"x-ms-enum\": {\n            \"name\": \"ChatCompletionRequestMessageRole\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"system\",\n                \"description\": \"The message author role is system.\"\n              },\n              {\n                \"value\": \"user\",\n                \"description\": \"The message author role is user.\"\n              },\n              {\n                \"value\": \"assistant\",\n                \"description\": \"The message author role is assistant.\"\n              },\n              {\n                \"value\": \"tool\",\n                \"description\": \"The message author role is tool.\"\n              },\n              {\n                \"value\": \"function\",\n                \"description\": \"Deprecated. The message author role is function.\"\n              }\n            ]\n          }\n        },\n        \"chatCompletionRequestMessageSystem\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessage\"\n            },\n            {\n              \"type\": \"object\",\n              \"properties\": {\n                \"content\": {\n                  \"type\": \"string\",\n                  \"description\": \"The contents of the message.\",\n                  \"nullable\": true\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"content\"\n          ]\n        },\n        \"chatCompletionRequestMessageUser\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessage\"\n            },\n            {\n              \"type\": \"object\",\n              \"properties\": {\n                \"content\": {\n                  \"oneOf\": [\n                    {\n                      \"type\": \"string\",\n                      \"description\": \"The contents of the message.\"\n                    },\n                    {\n                      \"type\": \"array\",\n                      \"description\": \"An array of content parts with a defined type, each can be of type `text` or `image_url` when passing in images. You can pass multiple images by adding multiple `image_url` content parts. Image input is only supported when using the `gpt-4-visual-preview` model.\",\n                      \"minimum\": 1,\n                      \"items\": {\n                        \"$ref\": \"#/components/schemas/chatCompletionRequestMessageContentPart\"\n                      }\n                    }\n                  ],\n                  \"nullable\": true\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"content\"\n          ]\n        },\n        \"chatCompletionRequestMessageContentPart\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"type\": {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessageContentPartType\"\n            }\n          },\n          \"discriminator\": {\n            \"propertyName\": \"type\",\n            \"mapping\": {\n              \"text\": \"#/components/schemas/chatCompletionRequestMessageContentPartText\",\n              \"image_url\": \"#/components/schemas/chatCompletionRequestMessageContentPartImage\"\n            }\n          },\n          \"required\": [\n            \"type\"\n          ]\n        },\n        \"chatCompletionRequestMessageContentPartType\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"text\",\n            \"image_url\"\n          ],\n          \"description\": \"The type of the content part.\",\n          \"x-ms-enum\": {\n            \"name\": \"ChatCompletionRequestMessageContentPartType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"text\",\n                \"description\": \"The content part type is text.\"\n              },\n              {\n                \"value\": \"image_url\",\n                \"description\": \"The content part type is image_url.\"\n              }\n            ]\n          }\n        },\n        \"chatCompletionRequestMessageContentPartText\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessageContentPart\"\n            },\n            {\n              \"type\": \"object\",\n              \"properties\": {\n                \"text\": {\n                  \"type\": \"string\",\n                  \"description\": \"The text content.\"\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"text\"\n          ]\n        },\n        \"chatCompletionRequestMessageContentPartImage\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessageContentPart\"\n            },\n            {\n              \"type\": \"object\",\n              \"properties\": {\n                \"url\": {\n                  \"type\": \"string\",\n                  \"description\": \"Either a URL of the image or the base64 encoded image data.\",\n                  \"format\": \"uri\"\n                },\n                \"detail\": {\n                  \"$ref\": \"#/components/schemas/imageDetailLevel\"\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"url\"\n          ]\n        },\n        \"imageDetailLevel\": {\n          \"type\": \"string\",\n          \"description\": \"Specifies the detail level of the image.\",\n          \"enum\": [\n            \"auto\",\n            \"low\",\n            \"high\"\n          ],\n          \"default\": \"auto\",\n          \"x-ms-enum\": {\n            \"name\": \"ImageDetailLevel\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"auto\",\n                \"description\": \"The image detail level is auto.\"\n              },\n              {\n                \"value\": \"low\",\n                \"description\": \"The image detail level is low.\"\n              },\n              {\n                \"value\": \"high\",\n                \"description\": \"The image detail level is high.\"\n              }\n            ]\n          }\n        },\n        \"chatCompletionRequestMessageAssistant\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessage\"\n            },\n            {\n              \"type\": \"object\",\n              \"properties\": {\n                \"content\": {\n                  \"type\": \"string\",\n                  \"description\": \"The contents of the message.\",\n                  \"nullable\": true\n                },\n                \"tool_calls\": {\n                  \"type\": \"array\",\n                  \"description\": \"The tool calls generated by the model, such as function calls.\",\n                  \"items\": {\n                    \"$ref\": \"#/components/schemas/chatCompletionMessageToolCall\"\n                  }\n                },\n                \"context\": {\n                  \"$ref\": \"#/components/schemas/azureChatExtensionsMessageContext\"\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"content\"\n          ]\n        },\n        \"azureChatExtensionConfiguration\": {\n          \"required\": [\n            \"type\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"type\": {\n              \"$ref\": \"#/components/schemas/azureChatExtensionType\"\n            }\n          },\n          \"description\": \"  A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\\n  completions request that should use Azure OpenAI chat extensions to augment the response behavior.\\n  The use of this configuration is compatible only with Azure OpenAI.\",\n          \"discriminator\": {\n            \"propertyName\": \"type\",\n            \"mapping\": {\n              \"azure_search\": \"#/components/schemas/azureSearchChatExtensionConfiguration\",\n              \"azure_cosmos_db\": \"#/components/schemas/azureCosmosDBChatExtensionConfiguration\"\n            }\n          }\n        },\n        \"azureChatExtensionType\": {\n          \"type\": \"string\",\n          \"description\": \"  A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\\n  completions request that should use Azure OpenAI chat extensions to augment the response behavior.\\n  The use of this configuration is compatible only with Azure OpenAI.\",\n          \"enum\": [\n            \"azure_search\",\n            \"azure_cosmos_db\"\n          ],\n          \"x-ms-enum\": {\n            \"name\": \"AzureChatExtensionType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"name\": \"azureSearch\",\n                \"value\": \"azure_search\",\n                \"description\": \"Represents the use of Azure Search as an Azure OpenAI chat extension.\"\n              },\n              {\n                \"name\": \"azureCosmosDB\",\n                \"value\": \"azure_cosmos_db\",\n                \"description\": \"Represents the use of Azure Cosmos DB as an Azure OpenAI chat extension.\"\n              }\n            ]\n          }\n        },\n        \"azureSearchChatExtensionConfiguration\": {\n          \"required\": [\n            \"parameters\"\n          ],\n          \"description\": \"A specific representation of configurable options for Azure Search when using it as an Azure OpenAI chat\\nextension.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/azureChatExtensionConfiguration\"\n            },\n            {\n              \"properties\": {\n                \"parameters\": {\n                  \"$ref\": \"#/components/schemas/azureSearchChatExtensionParameters\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"azure_search\"\n        },\n        \"azureSearchChatExtensionParameters\": {\n          \"required\": [\n            \"authentication\",\n            \"endpoint\",\n            \"index_name\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"authentication\": {\n              \"oneOf\": [\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataApiKeyAuthenticationOptions\"\n                },\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions\"\n                },\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions\"\n                }\n              ]\n            },\n            \"top_n_documents\": {\n              \"type\": \"integer\",\n              \"description\": \"The configured top number of documents to feature for the configured query.\",\n              \"format\": \"int32\"\n            },\n            \"in_scope\": {\n              \"type\": \"boolean\",\n              \"description\": \"Whether queries should be restricted to use of indexed data.\"\n            },\n            \"strictness\": {\n              \"maximum\": 5,\n              \"minimum\": 1,\n              \"type\": \"integer\",\n              \"description\": \"The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.\",\n              \"format\": \"int32\"\n            },\n            \"role_information\": {\n              \"type\": \"string\",\n              \"description\": \"Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant's personality and tell it how to format responses. There's a 100 token limit for it, and it counts against the overall token limit.\"\n            },\n            \"endpoint\": {\n              \"type\": \"string\",\n              \"description\": \"The absolute endpoint path for the Azure Search resource to use.\",\n              \"format\": \"uri\"\n            },\n            \"index_name\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index to use as available in the referenced Azure Search resource.\"\n            },\n            \"fields_mapping\": {\n              \"$ref\": \"#/components/schemas/azureSearchIndexFieldMappingOptions\"\n            },\n            \"query_type\": {\n              \"$ref\": \"#/components/schemas/azureSearchQueryType\"\n            },\n            \"semantic_configuration\": {\n              \"type\": \"string\",\n              \"description\": \"The additional semantic configuration for the query.\"\n            },\n            \"filter\": {\n              \"type\": \"string\",\n              \"description\": \"Search filter.\"\n            },\n            \"embedding_dependency\": {\n              \"oneOf\": [\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataEndpointVectorizationSource\"\n                },\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataDeploymentNameVectorizationSource\"\n                }\n              ]\n            }\n          },\n          \"description\": \"Parameters for Azure Search when used as an Azure OpenAI chat extension.\"\n        },\n        \"azureSearchIndexFieldMappingOptions\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"title_field\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index field to use as a title.\"\n            },\n            \"url_field\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index field to use as a URL.\"\n            },\n            \"filepath_field\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index field to use as a filepath.\"\n            },\n            \"content_fields\": {\n              \"type\": \"array\",\n              \"description\": \"The names of index fields that should be treated as content.\",\n              \"items\": {\n                \"type\": \"string\"\n              }\n            },\n            \"content_fields_separator\": {\n              \"type\": \"string\",\n              \"description\": \"The separator pattern that content fields should use.\"\n            },\n            \"vector_fields\": {\n              \"type\": \"array\",\n              \"description\": \"The names of fields that represent vector data.\",\n              \"items\": {\n                \"type\": \"string\"\n              }\n            }\n          },\n          \"description\": \"Optional settings to control how fields are processed when using a configured Azure Search resource.\"\n        },\n        \"azureSearchQueryType\": {\n          \"type\": \"string\",\n          \"description\": \"The type of Azure Search retrieval query that should be executed when using it as an Azure OpenAI chat extension.\",\n          \"enum\": [\n            \"simple\",\n            \"semantic\",\n            \"vector\",\n            \"vector_simple_hybrid\",\n            \"vector_semantic_hybrid\"\n          ],\n          \"x-ms-enum\": {\n            \"name\": \"azureSearchQueryType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"name\": \"simple\",\n                \"value\": \"simple\",\n                \"description\": \"Represents the default, simple query parser.\"\n              },\n              {\n                \"name\": \"semantic\",\n                \"value\": \"semantic\",\n                \"description\": \"Represents the semantic query parser for advanced semantic modeling.\"\n              },\n              {\n                \"name\": \"vector\",\n                \"value\": \"vector\",\n                \"description\": \"Represents vector search over computed data.\"\n              },\n              {\n                \"name\": \"vectorSimpleHybrid\",\n                \"value\": \"vector_simple_hybrid\",\n                \"description\": \"Represents a combination of the simple query strategy with vector data.\"\n              },\n              {\n                \"name\": \"vectorSemanticHybrid\",\n                \"value\": \"vector_semantic_hybrid\",\n                \"description\": \"Represents a combination of semantic search and vector data querying.\"\n              }\n            ]\n          }\n        },\n        \"azureCosmosDBChatExtensionConfiguration\": {\n          \"required\": [\n            \"parameters\"\n          ],\n          \"description\": \"A specific representation of configurable options for Azure Cosmos DB when using it as an Azure OpenAI chat\\nextension.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/azureChatExtensionConfiguration\"\n            },\n            {\n              \"properties\": {\n                \"parameters\": {\n                  \"$ref\": \"#/components/schemas/azureCosmosDBChatExtensionParameters\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"azure_cosmos_db\"\n        },\n        \"azureCosmosDBChatExtensionParameters\": {\n          \"required\": [\n            \"authentication\",\n            \"container_name\",\n            \"database_name\",\n            \"embedding_dependency\",\n            \"fields_mapping\",\n            \"index_name\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"authentication\": {\n              \"$ref\": \"#/components/schemas/onYourDataConnectionStringAuthenticationOptions\"\n            },\n            \"top_n_documents\": {\n              \"type\": \"integer\",\n              \"description\": \"The configured top number of documents to feature for the configured query.\",\n              \"format\": \"int32\"\n            },\n            \"in_scope\": {\n              \"type\": \"boolean\",\n              \"description\": \"Whether queries should be restricted to use of indexed data.\"\n            },\n            \"strictness\": {\n              \"maximum\": 5,\n              \"minimum\": 1,\n              \"type\": \"integer\",\n              \"description\": \"The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.\",\n              \"format\": \"int32\"\n            },\n            \"role_information\": {\n              \"type\": \"string\",\n              \"description\": \"Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant's personality and tell it how to format responses. There's a 100 token limit for it, and it counts against the overall token limit.\"\n            },\n            \"database_name\": {\n              \"type\": \"string\",\n              \"description\": \"The MongoDB vCore database name to use with Azure Cosmos DB.\"\n            },\n            \"container_name\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the Azure Cosmos DB resource container.\"\n            },\n            \"index_name\": {\n              \"type\": \"string\",\n              \"description\": \"The MongoDB vCore index name to use with Azure Cosmos DB.\"\n            },\n            \"fields_mapping\": {\n              \"$ref\": \"#/components/schemas/azureCosmosDBFieldMappingOptions\"\n            },\n            \"embedding_dependency\": {\n              \"oneOf\": [\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataEndpointVectorizationSource\"\n                },\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataDeploymentNameVectorizationSource\"\n                }\n              ]\n            }\n          },\n          \"description\": \"Parameters to use when configuring Azure OpenAI On Your Data chat extensions when using Azure Cosmos DB for\\nMongoDB vCore.\"\n        },\n        \"azureCosmosDBFieldMappingOptions\": {\n          \"required\": [\n            \"content_fields\",\n            \"vector_fields\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"title_field\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index field to use as a title.\"\n            },\n            \"url_field\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index field to use as a URL.\"\n            },\n            \"filepath_field\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index field to use as a filepath.\"\n            },\n            \"content_fields\": {\n              \"type\": \"array\",\n              \"description\": \"The names of index fields that should be treated as content.\",\n              \"items\": {\n                \"type\": \"string\"\n              }\n            },\n            \"content_fields_separator\": {\n              \"type\": \"string\",\n              \"description\": \"The separator pattern that content fields should use.\"\n            },\n            \"vector_fields\": {\n              \"type\": \"array\",\n              \"description\": \"The names of fields that represent vector data.\",\n              \"items\": {\n                \"type\": \"string\"\n              }\n            }\n          },\n          \"description\": \"Optional settings to control how fields are processed when using a configured Azure Cosmos DB resource.\"\n        },\n        \"onYourDataAuthenticationOptions\": {\n          \"required\": [\n            \"type\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"type\": {\n              \"$ref\": \"#/components/schemas/onYourDataAuthenticationType\"\n            }\n          },\n          \"description\": \"The authentication options for Azure OpenAI On Your Data.\",\n          \"discriminator\": {\n            \"propertyName\": \"type\",\n            \"mapping\": {\n              \"api_key\": \"#/components/schemas/onYourDataApiKeyAuthenticationOptions\",\n              \"connection_string\": \"#/components/schemas/onYourDataConnectionStringAuthenticationOptions\",\n              \"system_assigned_managed_identity\": \"#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions\",\n              \"user_assigned_managed_identity\": \"#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions\"\n            }\n          }\n        },\n        \"onYourDataAuthenticationType\": {\n          \"type\": \"string\",\n          \"description\": \"The authentication types supported with Azure OpenAI On Your Data.\",\n          \"enum\": [\n            \"api_key\",\n            \"connection_string\",\n            \"system_assigned_managed_identity\",\n            \"user_assigned_managed_identity\"\n          ],\n          \"x-ms-enum\": {\n            \"name\": \"OnYourDataAuthenticationType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"name\": \"apiKey\",\n                \"value\": \"api_key\",\n                \"description\": \"Authentication via API key.\"\n              },\n              {\n                \"name\": \"connectionString\",\n                \"value\": \"connection_string\",\n                \"description\": \"Authentication via connection string.\"\n              },\n              {\n                \"name\": \"systemAssignedManagedIdentity\",\n                \"value\": \"system_assigned_managed_identity\",\n                \"description\": \"Authentication via system-assigned managed identity.\"\n              },\n              {\n                \"name\": \"userAssignedManagedIdentity\",\n                \"value\": \"user_assigned_managed_identity\",\n                \"description\": \"Authentication via user-assigned managed identity.\"\n              }\n            ]\n          }\n        },\n        \"onYourDataApiKeyAuthenticationOptions\": {\n          \"required\": [\n            \"key\"\n          ],\n          \"description\": \"The authentication options for Azure OpenAI On Your Data when using an API key.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/onYourDataAuthenticationOptions\"\n            },\n            {\n              \"properties\": {\n                \"key\": {\n                  \"type\": \"string\",\n                  \"description\": \"The API key to use for authentication.\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"api_key\"\n        },\n        \"onYourDataConnectionStringAuthenticationOptions\": {\n          \"required\": [\n            \"connection_string\"\n          ],\n          \"description\": \"The authentication options for Azure OpenAI On Your Data when using a connection string.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/onYourDataAuthenticationOptions\"\n            },\n            {\n              \"properties\": {\n                \"connection_string\": {\n                  \"type\": \"string\",\n                  \"description\": \"The connection string to use for authentication.\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"connection_string\"\n        },\n        \"onYourDataSystemAssignedManagedIdentityAuthenticationOptions\": {\n          \"description\": \"The authentication options for Azure OpenAI On Your Data when using a system-assigned managed identity.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/onYourDataAuthenticationOptions\"\n            }\n          ],\n          \"x-ms-discriminator-value\": \"system_assigned_managed_identity\"\n        },\n        \"onYourDataUserAssignedManagedIdentityAuthenticationOptions\": {\n          \"required\": [\n            \"managed_identity_resource_id\"\n          ],\n          \"description\": \"The authentication options for Azure OpenAI On Your Data when using a user-assigned managed identity.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/onYourDataAuthenticationOptions\"\n            },\n            {\n              \"properties\": {\n                \"managed_identity_resource_id\": {\n                  \"type\": \"string\",\n                  \"description\": \"The resource ID of the user-assigned managed identity to use for authentication.\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"user_assigned_managed_identity\"\n        },\n        \"onYourDataVectorizationSource\": {\n          \"required\": [\n            \"type\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"type\": {\n              \"$ref\": \"#/components/schemas/onYourDataVectorizationSourceType\"\n            }\n          },\n          \"description\": \"An abstract representation of a vectorization source for Azure OpenAI On Your Data with vector search.\",\n          \"discriminator\": {\n            \"propertyName\": \"type\",\n            \"mapping\": {\n              \"endpoint\": \"#/components/schemas/onYourDataEndpointVectorizationSource\",\n              \"deployment_name\": \"#/components/schemas/onYourDataDeploymentNameVectorizationSource\"\n            }\n          }\n        },\n        \"onYourDataVectorizationSourceType\": {\n          \"type\": \"string\",\n          \"description\": \"Represents the available sources Azure OpenAI On Your Data can use to configure vectorization of data for use with\\nvector search.\",\n          \"enum\": [\n            \"endpoint\",\n            \"deployment_name\"\n          ],\n          \"x-ms-enum\": {\n            \"name\": \"OnYourDataVectorizationSourceType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"name\": \"endpoint\",\n                \"value\": \"endpoint\",\n                \"description\": \"Represents vectorization performed by public service calls to an Azure OpenAI embedding model.\"\n              },\n              {\n                \"name\": \"deploymentName\",\n                \"value\": \"deployment_name\",\n                \"description\": \"Represents an Ada model deployment name to use. This model deployment must be in the same Azure OpenAI resource, but\\nOn Your Data will use this model deployment via an internal call rather than a public one, which enables vector\\nsearch even in private networks.\"\n              }\n            ]\n          }\n        },\n        \"onYourDataDeploymentNameVectorizationSource\": {\n          \"required\": [\n            \"deployment_name\"\n          ],\n          \"description\": \"The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\\non an internal embeddings model deployment name in the same Azure OpenAI resource.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/onYourDataVectorizationSource\"\n            },\n            {\n              \"properties\": {\n                \"deployment_name\": {\n                  \"type\": \"string\",\n                  \"description\": \"Specifies the name of the model deployment to use for vectorization. This model deployment must be in the same Azure OpenAI resource, but On Your Data will use this model deployment via an internal call rather than a public one, which enables vector search even in private networks.\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"deployment_name\"\n        },\n        \"onYourDataEndpointVectorizationSource\": {\n          \"required\": [\n            \"authentication\",\n            \"endpoint\"\n          ],\n          \"description\": \"The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\\non a public Azure OpenAI endpoint call for embeddings.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/onYourDataVectorizationSource\"\n            },\n            {\n              \"properties\": {\n                \"authentication\": {\n                  \"$ref\": \"#/components/schemas/onYourDataApiKeyAuthenticationOptions\"\n                },\n                \"endpoint\": {\n                  \"type\": \"string\",\n                  \"description\": \"Specifies the endpoint to use for vectorization. This endpoint must be in the same Azure OpenAI resource, but On Your Data will use this endpoint via an internal call rather than a public one, which enables vector search even in private networks.\",\n                  \"format\": \"uri\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"endpoint\"\n        },\n        \"azureChatExtensionsMessageContext\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"citations\": {\n              \"type\": \"array\",\n              \"description\": \"The data source retrieval result, used to generate the assistant message in the response.\",\n              \"items\": {\n                \"$ref\": \"#/components/schemas/citation\"\n              },\n              \"x-ms-identifiers\": []\n            },\n            \"intent\": {\n              \"type\": \"string\",\n              \"description\": \"The detected intent from the chat history, used to pass to the next turn to carry over the context.\"\n            }\n          },\n          \"description\": \"  A representation of the additional context information available when Azure OpenAI chat extensions are involved\\n  in the generation of a corresponding chat completions response. This context information is only populated when\\n  using an Azure OpenAI request configured to use a matching extension.\"\n        },\n        \"citation\": {\n          \"required\": [\n            \"content\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"content\": {\n              \"type\": \"string\",\n              \"description\": \"The content of the citation.\"\n            },\n            \"title\": {\n              \"type\": \"string\",\n              \"description\": \"The title of the citation.\"\n            },\n            \"url\": {\n              \"type\": \"string\",\n              \"description\": \"The URL of the citation.\"\n            },\n            \"filepath\": {\n              \"type\": \"string\",\n              \"description\": \"The file path of the citation.\"\n            },\n            \"chunk_id\": {\n              \"type\": \"string\",\n              \"description\": \"The chunk ID of the citation.\"\n            }\n          },\n          \"description\": \"citation information for a chat completions response message.\"\n        },\n        \"chatCompletionMessageToolCall\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"id\": {\n              \"type\": \"string\",\n              \"description\": \"The ID of the tool call.\"\n            },\n            \"type\": {\n              \"$ref\": \"#/components/schemas/toolCallType\"\n            },\n            \"function\": {\n              \"type\": \"object\",\n              \"description\": \"The function that the model called.\",\n              \"properties\": {\n                \"name\": {\n                  \"type\": \"string\",\n                  \"description\": \"The name of the function to call.\"\n                },\n                \"arguments\": {\n                  \"type\": \"string\",\n                  \"description\": \"The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.\"\n                }\n              },\n              \"required\": [\n                \"name\",\n                \"arguments\"\n              ]\n            }\n          },\n          \"required\": [\n            \"id\",\n            \"type\",\n            \"function\"\n          ]\n        },\n        \"toolCallType\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"function\"\n          ],\n          \"description\": \"The type of the tool call, in this case `function`.\",\n          \"x-ms-enum\": {\n            \"name\": \"ToolCallType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"function\",\n                \"description\": \"The tool call type is function.\"\n              }\n            ]\n          }\n        },\n        \"chatCompletionRequestMessageTool\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessage\"\n            },\n            {\n              \"type\": \"object\",\n              \"nullable\": true,\n              \"properties\": {\n                \"tool_call_id\": {\n                  \"type\": \"string\",\n                  \"description\": \"Tool call that this message is responding to.\"\n                },\n                \"content\": {\n                  \"type\": \"string\",\n                  \"description\": \"The contents of the message.\",\n                  \"nullable\": true\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"tool_call_id\",\n            \"content\"\n          ]\n        },\n        \"chatCompletionRequestMessageFunction\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessage\"\n            },\n            {\n              \"type\": \"object\",\n              \"description\": \"Deprecated. Message that represents a function.\",\n              \"nullable\": true,\n              \"properties\": {\n                \"role\": {\n                  \"type\": \"string\",\n                  \"enum\": [\n                    \"function\"\n                  ],\n                  \"description\": \"The role of the messages author, in this case `function`.\"\n                },\n                \"name\": {\n                  \"type\": \"string\",\n                  \"description\": \"The contents of the message.\"\n                },\n                \"content\": {\n                  \"type\": \"string\",\n                  \"description\": \"The contents of the message.\",\n                  \"nullable\": true\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"function_call_id\",\n            \"content\"\n          ]\n        },\n        \"createChatCompletionResponse\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionsResponseCommon\"\n            },\n            {\n              \"properties\": {\n                \"prompt_filter_results\": {\n                  \"$ref\": \"#/components/schemas/promptFilterResults\"\n                },\n                \"choices\": {\n                  \"type\": \"array\",\n                  \"items\": {\n                    \"type\": \"object\",\n                    \"allOf\": [\n                      {\n                        \"$ref\": \"#/components/schemas/chatCompletionChoiceCommon\"\n                      },\n                      {\n                        \"properties\": {\n                          \"message\": {\n                            \"$ref\": \"#/components/schemas/chatCompletionResponseMessage\"\n                          },\n                          \"content_filter_results\": {\n                            \"$ref\": \"#/components/schemas/contentFilterChoiceResults\"\n                          }\n                        }\n                      }\n                    ]\n                  }\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"id\",\n            \"object\",\n            \"created\",\n            \"model\",\n            \"choices\"\n          ]\n        },\n        \"chatCompletionResponseMessage\": {\n          \"type\": \"object\",\n          \"description\": \"A chat completion message generated by the model.\",\n          \"properties\": {\n            \"role\": {\n              \"$ref\": \"#/components/schemas/chatCompletionResponseMessageRole\"\n            },\n            \"content\": {\n              \"type\": \"string\",\n              \"description\": \"The contents of the message.\",\n              \"nullable\": true\n            },\n            \"tool_calls\": {\n              \"type\": \"array\",\n              \"description\": \"The tool calls generated by the model, such as function calls.\",\n              \"items\": {\n                \"$ref\": \"#/components/schemas/chatCompletionMessageToolCall\"\n              }\n            },\n            \"function_call\": {\n              \"$ref\": \"#/components/schemas/chatCompletionFunctionCall\"\n            },\n            \"context\": {\n              \"$ref\": \"#/components/schemas/azureChatExtensionsMessageContext\"\n            }\n          }\n        },\n        \"chatCompletionResponseMessageRole\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"assistant\"\n          ],\n          \"description\": \"The role of the author of the response message.\"\n        },\n        \"chatCompletionToolChoiceOption\": {\n          \"description\": \"Controls which (if any) function is called by the model. `none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function. Specifying a particular function via `{\\\"type\\\": \\\"function\\\", \\\"function\\\": {\\\"name\\\": \\\"my_function\\\"}}` forces the model to call that function.\",\n          \"oneOf\": [\n            {\n              \"type\": \"string\",\n              \"description\": \"`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.\",\n              \"enum\": [\n                \"none\",\n                \"auto\"\n              ]\n            },\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionNamedToolChoice\"\n            }\n          ]\n        },\n        \"chatCompletionNamedToolChoice\": {\n          \"type\": \"object\",\n          \"description\": \"Specifies a tool the model should use. Use to force the model to call a specific function.\",\n          \"properties\": {\n            \"type\": {\n              \"type\": \"string\",\n              \"enum\": [\n                \"function\"\n              ],\n              \"description\": \"The type of the tool. Currently, only `function` is supported.\"\n            },\n            \"function\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"name\": {\n                  \"type\": \"string\",\n                  \"description\": \"The name of the function to call.\"\n                }\n              },\n              \"required\": [\n                \"name\"\n              ]\n            }\n          }\n        },\n        \"chatCompletionFunctionCall\": {\n          \"type\": \"object\",\n          \"description\": \"Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.\",\n          \"properties\": {\n            \"name\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the function to call.\"\n            },\n            \"arguments\": {\n              \"type\": \"string\",\n              \"description\": \"The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.\"\n            }\n          },\n          \"required\": [\n            \"name\",\n            \"arguments\"\n          ]\n        },\n        \"chatCompletionsResponseCommon\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"id\": {\n              \"type\": \"string\",\n              \"description\": \"A unique identifier for the chat completion.\"\n            },\n            \"object\": {\n              \"$ref\": \"#/components/schemas/chatCompletionResponseObject\"\n            },\n            \"created\": {\n              \"type\": \"integer\",\n              \"format\": \"unixtime\",\n              \"description\": \"The Unix timestamp (in seconds) of when the chat completion was created.\"\n            },\n            \"model\": {\n              \"type\": \"string\",\n              \"description\": \"The model used for the chat completion.\"\n            },\n            \"usage\": {\n              \"$ref\": \"#/components/schemas/completionUsage\"\n            },\n            \"system_fingerprint\": {\n              \"type\": \"string\",\n              \"description\": \"Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\"\n            }\n          },\n          \"required\": [\n            \"id\",\n            \"object\",\n            \"created\",\n            \"model\"\n          ]\n        },\n        \"chatCompletionResponseObject\": {\n          \"type\": \"string\",\n          \"description\": \"The object type.\",\n          \"enum\": [\n            \"chat.completion\"\n          ],\n          \"x-ms-enum\": {\n            \"name\": \"ChatCompletionResponseObject\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"chat.completion\",\n                \"description\": \"The object type is chat completion.\"\n              }\n            ]\n          }\n        },\n        \"completionUsage\": {\n          \"type\": \"object\",\n          \"description\": \"Usage statistics for the completion request.\",\n          \"properties\": {\n            \"prompt_tokens\": {\n              \"type\": \"integer\",\n              \"description\": \"Number of tokens in the prompt.\"\n            },\n            \"completion_tokens\": {\n              \"type\": \"integer\",\n              \"description\": \"Number of tokens in the generated completion.\"\n            },\n            \"total_tokens\": {\n              \"type\": \"integer\",\n              \"description\": \"Total number of tokens used in the request (prompt + completion).\"\n            }\n          },\n          \"required\": [\n            \"prompt_tokens\",\n            \"completion_tokens\",\n            \"total_tokens\"\n          ]\n        },\n        \"chatCompletionTool\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"type\": {\n              \"$ref\": \"#/components/schemas/chatCompletionToolType\"\n            },\n            \"function\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"description\": {\n                  \"type\": \"string\",\n                  \"description\": \"A description of what the function does, used by the model to choose when and how to call the function.\"\n                },\n                \"name\": {\n                  \"type\": \"string\",\n                  \"description\": \"The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.\"\n                },\n                \"parameters\": {\n                  \"$ref\": \"#/components/schemas/chatCompletionFunctionParameters\"\n                }\n              },\n              \"required\": [\n                \"name\",\n                \"parameters\"\n              ]\n            }\n          },\n          \"required\": [\n            \"type\",\n            \"function\"\n          ]\n        },\n        \"chatCompletionToolType\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"function\"\n          ],\n          \"description\": \"The type of the tool. Currently, only `function` is supported.\",\n          \"x-ms-enum\": {\n            \"name\": \"ChatCompletionToolType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"function\",\n                \"description\": \"The tool type is function.\"\n              }\n            ]\n          }\n        },\n        \"chatCompletionChoiceCommon\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"index\": {\n              \"type\": \"integer\"\n            },\n            \"finish_reason\": {\n              \"type\": \"string\"\n            }\n          }\n        },\n        \"createTranslationRequest\": {\n          \"type\": \"object\",\n          \"description\": \"Translation request.\",\n          \"properties\": {\n            \"file\": {\n              \"type\": \"string\",\n              \"description\": \"The audio file to translate.\",\n              \"format\": \"binary\"\n            },\n            \"prompt\": {\n              \"type\": \"string\",\n              \"description\": \"An optional text to guide the model's style or continue a previous audio segment. The prompt should be in English.\"\n            },\n            \"response_format\": {\n              \"$ref\": \"#/components/schemas/audioResponseFormat\"\n            },\n            \"temperature\": {\n              \"type\": \"number\",\n              \"default\": 0,\n              \"description\": \"The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\"\n            }\n          },\n          \"required\": [\n            \"file\"\n          ]\n        },\n        \"audioResponse\": {\n          \"description\": \"Translation or transcription response when response_format was json\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"text\": {\n              \"type\": \"string\",\n              \"description\": \"Translated or transcribed text.\"\n            }\n          },\n          \"required\": [\n            \"text\"\n          ]\n        },\n        \"audioVerboseResponse\": {\n          \"description\": \"Translation or transcription response when response_format was verbose_json\",\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/audioResponse\"\n            },\n            {\n              \"properties\": {\n                \"task\": {\n                  \"type\": \"string\",\n                  \"description\": \"Type of audio task.\",\n                  \"enum\": [\n                    \"transcribe\",\n                    \"translate\"\n                  ],\n                  \"x-ms-enum\": {\n                    \"modelAsString\": true\n                  }\n                },\n                \"language\": {\n                  \"type\": \"string\",\n                  \"description\": \"Language.\"\n                },\n                \"duration\": {\n                  \"type\": \"number\",\n                  \"description\": \"Duration.\"\n                },\n                \"segments\": {\n                  \"type\": \"array\",\n                  \"items\": {\n                    \"$ref\": \"#/components/schemas/audioSegment\"\n                  }\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"text\"\n          ]\n        },\n        \"audioResponseFormat\": {\n          \"title\": \"AudioResponseFormat\",\n          \"description\": \"Defines the format of the output.\",\n          \"enum\": [\n            \"json\",\n            \"text\",\n            \"srt\",\n            \"verbose_json\",\n            \"vtt\"\n          ],\n          \"type\": \"string\",\n          \"x-ms-enum\": {\n            \"modelAsString\": true\n          }\n        },\n        \"createTranscriptionRequest\": {\n          \"type\": \"object\",\n          \"description\": \"Transcription request.\",\n          \"properties\": {\n            \"file\": {\n              \"type\": \"string\",\n              \"description\": \"The audio file object to transcribe.\",\n              \"format\": \"binary\"\n            },\n            \"prompt\": {\n              \"type\": \"string\",\n              \"description\": \"An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.\"\n            },\n            \"response_format\": {\n              \"$ref\": \"#/components/schemas/audioResponseFormat\"\n            },\n            \"temperature\": {\n              \"type\": \"number\",\n              \"default\": 0,\n              \"description\": \"The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\"\n            },\n            \"language\": {\n              \"type\": \"string\",\n              \"description\": \"The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency.\"\n            }\n          },\n          \"required\": [\n            \"file\"\n          ]\n        },\n        \"audioSegment\": {\n          \"type\": \"object\",\n          \"description\": \"Transcription or translation segment.\",\n          \"properties\": {\n            \"id\": {\n              \"type\": \"integer\",\n              \"description\": \"Segment identifier.\"\n            },\n            \"seek\": {\n              \"type\": \"number\",\n              \"description\": \"Offset of the segment.\"\n            },\n            \"start\": {\n              \"type\": \"number\",\n              \"description\": \"Segment start offset.\"\n            },\n            \"end\": {\n              \"type\": \"number\",\n              \"description\": \"Segment end offset.\"\n            },\n            \"text\": {\n              \"type\": \"string\",\n              \"description\": \"Segment text.\"\n            },\n            \"tokens\": {\n              \"type\": \"array\",\n              \"items\": {\n                \"type\": \"number\",\n                \"nullable\": false\n              },\n              \"description\": \"Tokens of the text.\"\n            },\n            \"temperature\": {\n              \"type\": \"number\",\n              \"description\": \"Temperature.\"\n            },\n            \"avg_logprob\": {\n              \"type\": \"number\",\n              \"description\": \"Average log probability.\"\n            },\n            \"compression_ratio\": {\n              \"type\": \"number\",\n              \"description\": \"Compression ratio.\"\n            },\n            \"no_speech_prob\": {\n              \"type\": \"number\",\n              \"description\": \"Probability of 'no speech'.\"\n            }\n          }\n        },\n        \"imageQuality\": {\n          \"description\": \"The quality of the image that will be generated.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"standard\",\n            \"hd\"\n          ],\n          \"default\": \"standard\",\n          \"x-ms-enum\": {\n            \"name\": \"Quality\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"standard\",\n                \"description\": \"Standard quality creates images with standard quality.\",\n                \"name\": \"Standard\"\n              },\n              {\n                \"value\": \"hd\",\n                \"description\": \"HD quality creates images with finer details and greater consistency across the image.\",\n                \"name\": \"HD\"\n              }\n            ]\n          }\n        },\n        \"imagesResponseFormat\": {\n          \"description\": \"The format in which the generated images are returned.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"url\",\n            \"b64_json\"\n          ],\n          \"default\": \"url\",\n          \"x-ms-enum\": {\n            \"name\": \"ImagesResponseFormat\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"url\",\n                \"description\": \"The URL that provides temporary access to download the generated images.\",\n                \"name\": \"Url\"\n              },\n              {\n                \"value\": \"b64_json\",\n                \"description\": \"The generated images are returned as base64 encoded string.\",\n                \"name\": \"Base64Json\"\n              }\n            ]\n          }\n        },\n        \"imageSize\": {\n          \"description\": \"The size of the generated images.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"1792x1024\",\n            \"1024x1792\",\n            \"1024x1024\"\n          ],\n          \"default\": \"1024x1024\",\n          \"x-ms-enum\": {\n            \"name\": \"Size\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"1792x1024\",\n                \"description\": \"The desired size of the generated image is 1792x1024 pixels.\",\n                \"name\": \"Size1792x1024\"\n              },\n              {\n                \"value\": \"1024x1792\",\n                \"description\": \"The desired size of the generated image is 1024x1792 pixels.\",\n                \"name\": \"Size1024x1792\"\n              },\n              {\n                \"value\": \"1024x1024\",\n                \"description\": \"The desired size of the generated image is 1024x1024 pixels.\",\n                \"name\": \"Size1024x1024\"\n              }\n            ]\n          }\n        },\n        \"imageStyle\": {\n          \"description\": \"The style of the generated images.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"vivid\",\n            \"natural\"\n          ],\n          \"default\": \"vivid\",\n          \"x-ms-enum\": {\n            \"name\": \"Style\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"vivid\",\n                \"description\": \"Vivid creates images that are hyper-realistic and dramatic.\",\n                \"name\": \"Vivid\"\n              },\n              {\n                \"value\": \"natural\",\n                \"description\": \"Natural creates images that are more natural and less hyper-realistic.\",\n                \"name\": \"Natural\"\n              }\n            ]\n          }\n        },\n        \"imageGenerationsRequest\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"prompt\": {\n              \"description\": \"A text description of the desired image(s). The maximum length is 4000 characters.\",\n              \"type\": \"string\",\n              \"format\": \"string\",\n              \"example\": \"a corgi in a field\",\n              \"minLength\": 1\n            },\n            \"n\": {\n              \"description\": \"The number of images to generate.\",\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"maximum\": 1,\n              \"default\": 1\n            },\n            \"size\": {\n              \"$ref\": \"#/components/schemas/imageSize\"\n            },\n            \"response_format\": {\n              \"$ref\": \"#/components/schemas/imagesResponseFormat\"\n            },\n            \"user\": {\n              \"description\": \"A unique identifier representing your end-user, which can help to monitor and detect abuse.\",\n              \"type\": \"string\",\n              \"format\": \"string\",\n              \"example\": \"user123456\"\n            },\n            \"quality\": {\n              \"$ref\": \"#/components/schemas/imageQuality\"\n            },\n            \"style\": {\n              \"$ref\": \"#/components/schemas/imageStyle\"\n            }\n          },\n          \"required\": [\n            \"prompt\"\n          ]\n        },\n        \"generateImagesResponse\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"created\": {\n              \"type\": \"integer\",\n              \"format\": \"unixtime\",\n              \"description\": \"The unix timestamp when the operation was created.\",\n              \"example\": \"1676540381\"\n            },\n            \"data\": {\n              \"type\": \"array\",\n              \"description\": \"The result data of the operation, if successful\",\n              \"items\": {\n                \"$ref\": \"#/components/schemas/imageResult\"\n              }\n            }\n          },\n          \"required\": [\n            \"created\",\n            \"data\"\n          ]\n        },\n        \"imageResult\": {\n          \"type\": \"object\",\n          \"description\": \"The image url or encoded image if successful, and an error otherwise.\",\n          \"properties\": {\n            \"url\": {\n              \"type\": \"string\",\n              \"description\": \"The image url.\",\n              \"example\": \"https://www.contoso.com\"\n            },\n            \"b64_json\": {\n              \"type\": \"string\",\n              \"description\": \"The base64 encoded image\"\n            },\n            \"content_filter_results\": {\n              \"$ref\": \"#/components/schemas/dalleContentFilterResults\"\n            },\n            \"revised_prompt\": {\n              \"type\": \"string\",\n              \"description\": \"The prompt that was used to generate the image, if there was any revision to the prompt.\"\n            },\n            \"prompt_filter_results\": {\n              \"$ref\": \"#/components/schemas/dalleFilterResults\"\n            }\n          }\n        }\n      },\n      \"securitySchemes\": {\n        \"bearer\": {\n          \"type\": \"oauth2\",\n          \"flows\": {\n            \"implicit\": {\n              \"authorizationUrl\": \"https://login.microsoftonline.com/common/oauth2/v2.0/authorize\",\n              \"scopes\": {}\n            }\n          },\n          \"x-tokenInfoFunc\": \"api.middleware.auth.bearer_auth\",\n          \"x-scopeValidateFunc\": \"api.middleware.auth.validate_scopes\"\n        },\n        \"apiKey\": {\n          \"type\": \"apiKey\",\n          \"name\": \"api-key\",\n          \"in\": \"header\"\n        }\n      }\n    }\n  }"
  },
  {
    "path": "scenarios/workload-genai/bicep/apim-policies/apiManagement.bicep",
    "content": "@description('The name of the API Management service instance')\nparam apiManagementServiceName string\n\n@description('The base url of the first Azure Open AI Service PTU deployment (e.g. https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}/)')\nparam ptuDeploymentOneBaseUrl string\n\n@description('The base url of the first Azure Open AI Service Pay-As-You-Go deployment (e.g. https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}/)')\nparam payAsYouGoDeploymentOneBaseUrl string\n\n@description('The base url of the second Azure Open AI Service Pay-As-You-Go deployment (e.g. https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}/)')\nparam payAsYouGoDeploymentTwoBaseUrl string\n\n@description('The name of the Event Hub Namespace to log to')\nparam eventHubNamespaceName string\n\n@description('The name of the Event Hub to log utilization data to')\nparam eventHubName string\nparam apimIdentityName string\n\nvar apimIdentityNameValue = 'apim-identity'\n\nresource apiManagementService 'Microsoft.ApiManagement/service@2023-05-01-preview' existing = {\n  name: apiManagementServiceName\n}\n\nresource apimIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = {\n  name: apimIdentityName\n}\n\nresource azureOpenAIApi 'Microsoft.ApiManagement/service/apis@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'azure-openai-api'\n  properties: {\n    path: '/openai'\n    displayName: 'AzureOpenAI'\n    protocols: ['https']\n    value: loadTextContent('./api-specs/openapi-spec.json')\n    format: 'openapi+json'\n  }\n}\n\nresource azureOpenAIProduct 'Microsoft.ApiManagement/service/products@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'aoai-product'\n  properties: {\n    displayName: 'aoai-product'\n    subscriptionRequired: true\n    state: 'published'\n    approvalRequired: false\n  }\n}\n\nresource multiTenantProduct1 'Microsoft.ApiManagement/service/products@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'multi-tenant-product1'\n  properties: {\n    displayName: 'multi-tenant-product1'\n    subscriptionRequired: true\n    state: 'published'\n    approvalRequired: false\n  }\n}\n\nresource multiTenantProduct2 'Microsoft.ApiManagement/service/products@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'multi-tenant-product2'\n  properties: {\n    displayName: 'multi-tenant-product2'\n    subscriptionRequired: true\n    state: 'published'\n    approvalRequired: false\n  }\n}\n\nvar azureOpenAIAPINames = [\n  azureOpenAIApi.name\n]\n\nresource azureOpenAIProductAPIAssociation 'Microsoft.ApiManagement/service/products/apis@2023-05-01-preview' = [\n  for apiName in azureOpenAIAPINames: {\n    name: '${apiManagementServiceName}/${azureOpenAIProduct.name}/${apiName}'\n  }\n]\n\nresource multiTenantProduct1APIAssociation 'Microsoft.ApiManagement/service/products/apis@2023-05-01-preview' = [\n  for apiName in azureOpenAIAPINames: {\n    name: '${apiManagementServiceName}/${multiTenantProduct1.name}/${apiName}'\n  }\n]\n\nresource multiTenantProduct2APIAssociation 'Microsoft.ApiManagement/service/products/apis@2023-05-01-preview' = [\n  for apiName in azureOpenAIAPINames: {\n    name: '${apiManagementServiceName}/${multiTenantProduct2.name}/${apiName}'\n  }\n]\n\nresource ptuBackendOne 'Microsoft.ApiManagement/service/backends@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'ptu-backend-1'\n  properties:{\n    protocol: 'http'\n    url: ptuDeploymentOneBaseUrl\n  }\n}\n\nresource payAsYouGoBackendOne 'Microsoft.ApiManagement/service/backends@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'payg-backend-1'\n  properties:{\n    protocol: 'http'\n    url: payAsYouGoDeploymentOneBaseUrl\n  }\n}\n\nresource payAsYouGoBackendTwo 'Microsoft.ApiManagement/service/backends@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'payg-backend-2'\n  properties:{\n    protocol: 'http'\n    url: payAsYouGoDeploymentTwoBaseUrl\n  }\n}\n\nresource azureOpenAIProductSubscription 'Microsoft.ApiManagement/service/subscriptions@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'aoai-product-subscription'\n  properties: {\n    displayName: 'aoai-product-subscription'\n    state: 'active'\n    scope: azureOpenAIProduct.id\n  }\n}\n\nresource multiTenantProduct1Subscription 'Microsoft.ApiManagement/service/subscriptions@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'multi-tenant-product1-subscription'\n  properties: {\n    displayName: 'multi-tenant-product1-subscription'\n    state: 'active'\n    scope: multiTenantProduct1.id\n  }\n}\n\nresource multiTenantProduct2Subscription 'Microsoft.ApiManagement/service/subscriptions@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'multi-tenant-product2-subscription'\n  properties: {\n    displayName: 'multi-tenant-product2-subscription'\n    state: 'active'\n    scope: multiTenantProduct2.id\n  }\n}\n\nresource simpleRoundRobinPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'simple-priority-weighted'\n  properties: {\n    value: loadTextContent('../../policies/fragments/load-balancing/simple-priority-weighted.xml')\n    format: 'rawxml'\n  }\n  dependsOn: [payAsYouGoBackendOne, payAsYouGoBackendTwo]\n}\n\n\n\nresource simpleRateLimitingPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'rate-limiting-by-tokens'\n  properties: {\n    value: loadTextContent('../../policies/fragments/rate-limiting/rate-limiting-by-tokens.xml')\n    format: 'rawxml'\n  }\n  dependsOn: [payAsYouGoBackendOne, ptuBackendOne]\n}\n\nresource adaptiveRateLimitingPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'adaptive-rate-limiting'\n  properties: {\n    value: loadTextContent('../../policies/fragments/rate-limiting/adaptive-rate-limiting.xml')\n    format: 'rawxml'\n  }\n  dependsOn: [payAsYouGoBackendOne, ptuBackendOne]\n}\n\nresource adaptiveRateLimitingWorkAroundPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'rate-limiting-workaround'\n  properties: {\n    value: loadTextContent('../../policies/fragments/rate-limiting/rate-limiting-workaround.xml')\n    format: 'rawxml'\n  }\n  dependsOn: [payAsYouGoBackendOne, ptuBackendOne]\n}\n\nresource usageTrackingEHPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'usage-tracking-with-eventhub'\n  properties: {\n    value: loadTextContent('../../policies/fragments/usage-tracking/usage-tracking-with-eventhub.xml')\n    format: 'rawxml'\n  }\n  dependsOn: [eventHubLogger]\n}\n\nresource usageTrackingWithAppInsightsPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'usage-tracking-with-appinsights'\n  properties: {\n    value: loadTextContent('../../policies/fragments/usage-tracking/usage-tracking-with-appinsights.xml')\n    format: 'rawxml'\n  }\n  dependsOn: [eventHubLogger]\n}\n\n//Load-balancing with Circuit Breaker policy\nmodule apiBackend './load-balancing/backends.bicep' = {\n  name: 'apiBackend'\n  params: {\n    apiManagementServiceName: apiManagementServiceName\n    backendUris: ['${ptuDeploymentOneBaseUrl}/', '${payAsYouGoDeploymentOneBaseUrl}/', '${payAsYouGoDeploymentTwoBaseUrl}/']\n  }\n}\n\nmodule apiLBPool './load-balancing/lb-pool.bicep' = {\n  name: 'apimLBPool'\n  params: {\n    apiManagementServiceName: apiManagementServiceName\n    backends: apiBackend.outputs.backendNames\n  }\n  dependsOn: [\n    apiBackend\n  ]\n}\n\n//Load the policies\nresource azureOpenAIApiPolicy 'Microsoft.ApiManagement/service/apis/policies@2023-05-01-preview' = {\n  parent: azureOpenAIApi\n  name: 'policy'\n  properties: {\n    value: loadTextContent('../../policies/genai-policy.xml')\n    format: 'rawxml'\n  }\n  dependsOn: [\n    simpleRoundRobinPolicyFragment\n    adaptiveRateLimitingPolicyFragment\n    usageTrackingWithAppInsightsPolicyFragment]\n}\n\nresource multiTenantProduct1Policy 'Microsoft.ApiManagement/service/products/policies@2024-06-01-preview' = {\n  parent: multiTenantProduct1\n  name: 'policy'\n  properties: {\n    value: loadTextContent('../../policies/multi-tenancy/multi-tenant-product1-policy.xml')\n    format: 'rawxml'\n  }\n  dependsOn: [apiBackend]\n}\n\nresource multiTenantProduct2Policy 'Microsoft.ApiManagement/service/products/policies@2024-06-01-preview' = {\n  parent: multiTenantProduct2\n  name: 'policy'\n  properties: {\n    value: loadTextContent('../../policies/multi-tenancy/multi-tenant-product2-policy.xml')\n    format: 'rawxml'\n  }\n  dependsOn: [apiBackend]\n}\n\nresource apimOpenaiApiUamiNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = {\n  name: apimIdentityNameValue\n  parent: apiManagementService\n  properties: {\n    displayName: apimIdentityNameValue\n    secret: true\n    value: apimIdentity.properties.clientId\n  }\n}\n\nresource eventHubLogger 'Microsoft.ApiManagement/service/loggers@2022-04-01-preview' = {\n  name: 'eventhub-logger'\n  parent: apiManagementService\n  properties: {\n    loggerType: 'azureEventHub'\n    description: 'Event hub logger with system-assigned managed identity'\n    credentials: {\n      endpointAddress: '${eventHubNamespaceName}.servicebus.windows.net'\n      identityClientId: apimIdentity.properties.clientId\n      name: eventHubName\n    }\n  }\n}\n\noutput apiManagementServiceName string = apiManagementService.name\noutput apiManagementAzureOpenAIProductSubscriptionKey string = azureOpenAIProductSubscription.listSecrets().primaryKey\noutput apiManagementMultitenantProduct1SubscriptionKey string = multiTenantProduct1Subscription.listSecrets().primaryKey\noutput apiManagementMultitenantProduct2SubscriptionKey string = multiTenantProduct2Subscription.listSecrets().primaryKey\n"
  },
  {
    "path": "scenarios/workload-genai/bicep/apim-policies/load-balancing/backends.bicep",
    "content": "param apiManagementServiceName string\nparam backendUris array\n\nresource apiManagementService 'Microsoft.ApiManagement/service@2023-05-01-preview' existing = {\n  name: apiManagementServiceName\n}\n\nresource backend 'Microsoft.ApiManagement/service/backends@2023-05-01-preview' = [for (backendUri, i) in backendUris: {\n  parent: apiManagementService\n  name: 'aoai-${i}'\n  properties: {\n    url: backendUri\n    protocol: 'http'\n    circuitBreaker: {\n      rules: [{\n        name: 'breakerRule'\n        failureCondition: {\n          count: 1\n          interval: 'PT1M'\n          statusCodeRanges: [ {\n            min: 429\n            max: 429\n          }]\n          errorReasons: ['timeout']\n        }\n        tripDuration: 'PT1M'\n        acceptRetryAfter: true\n      }]\n    }\n  }  \n}\n]\n\noutput backendNames array = [for i in range(0, length(backendUris)): backend[i].name]\n"
  },
  {
    "path": "scenarios/workload-genai/bicep/apim-policies/load-balancing/lb-pool.bicep",
    "content": "param apiManagementServiceName string\nparam backends array\n\nresource apiManagementService 'Microsoft.ApiManagement/service@2023-05-01-preview' existing = {\n  name: apiManagementServiceName\n}\n\nresource backend 'Microsoft.ApiManagement/service/backends@2023-05-01-preview' = {\n  parent: apiManagementService\n  name: 'aoai-lb-pool'\n  properties: {\n    title: 'aoai-lb-pool'\n    type: 'Pool'\n    pool: {\n      services: [for (backend, i) in backends: {\n        id: '/backends/${backend}'\n        priority: i%2 == 0 ? 1 : 2\n        weight: i+1\n      }]\n    }  \n  }\n} \n"
  },
  {
    "path": "scenarios/workload-genai/bicep/eventhub/eventHub.bicep",
    "content": "@description('The name of the Event Hub Namespace')\nparam eventHubNamespaceName string\n\n@description('The name of the Event Hub')\nparam eventHubName string\n\n@description('The messaging tier for Event Hub Namespace.')\n@allowed([\n  'Basic'\n  'Standard'\n])\nparam eventHubSku string = 'Standard'\n\nparam apimIdentityName string\nparam apimResourceGroupName string\n\n@description('Location for all resources.')\nparam location string = resourceGroup().location\n\nresource eventHubNamespace 'Microsoft.EventHub/namespaces@2021-11-01' = {\n  name: eventHubNamespaceName\n  location: location\n  sku: {\n    name: eventHubSku\n    tier: eventHubSku\n    capacity: 1\n  }\n  properties: {\n    isAutoInflateEnabled: false\n    maximumThroughputUnits: 0\n  }\n}\n\nresource eventHub 'Microsoft.EventHub/namespaces/eventhubs@2021-11-01' = {\n  parent: eventHubNamespace\n  name: eventHubName\n  properties: {\n    messageRetentionInDays: 7\n    partitionCount: 1\n  }\n}\n\nresource apimIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' existing = {\n  name: apimIdentityName\n  scope: resourceGroup(apimResourceGroupName)\n}\n\nresource eventHubsDataSenderRoleDefinition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {\n  name: '2b629674-e913-4c01-ae53-ef4638d8f975' // https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#azure-event-hubs-data-sender\n  scope: tenant()\n}\n\nresource assignEventHubsDataSenderToApiManagement 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {\n  name: guid(resourceGroup().id, eventHubNamespace.name, apimIdentity.id, 'assignEventHubsDataSenderToApiManagement')\n  scope: eventHubNamespace\n  properties: {\n    description: 'Assign EventHubsDataSender role to API Management'\n    principalId: apimIdentity.properties.principalId\n    principalType: 'ServicePrincipal'\n    roleDefinitionId: eventHubsDataSenderRoleDefinition.id\n  }\n}\n\noutput eventHubNamespaceName string = eventHubNamespace.name\noutput eventHubName string = eventHub.name\n"
  },
  {
    "path": "scenarios/workload-genai/bicep/main.bicep",
    "content": "targetScope = 'subscription'\n\n@description('The name of the API Management service instance')\nparam apiManagementServiceName string\n\nparam location string = deployment().location\n\nparam resourceSuffix string\nparam apimResourceGroupName string\nparam apimIdentityName string\nparam vnetName string\nparam privateEndpointSubnetid string\nparam networkingResourceGroupName string\n\n@description('Enable sending usage and telemetry feedback to Microsoft.')\nparam enableTelemetry bool = true\nvar telemetryId = 'ab1e5729-7452-41b2-9fbb-945cc51d9cd0-${location}-apimsb-genai'\n\nvar workloadResourceGroupName = 'rg-openai-${resourceSuffix}'\n\nvar eventHubNamespaceName = 'eh-ns-${resourceSuffix}'\nvar eventHubName = 'apim-utilization-reporting'\n\nvar ptuAoaiDeploymentName = 'ptu-${resourceSuffix}'\nvar paygoOneAoaiDeploymentName = 'paygo-one-${resourceSuffix}'\nvar paygoTwoAoaiDeploymentName = 'paygo-two-${resourceSuffix}'\n\nresource workloadResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {\n  name: workloadResourceGroupName\n  location: location\n}\n\nmodule eventHub 'eventhub/eventHub.bicep' = {\n  name: 'eventHubDeploy'\n  scope: resourceGroup(workloadResourceGroup.name)\n  params: {\n    eventHubName: eventHubName\n    eventHubNamespaceName: eventHubNamespaceName\n    location: location\n    apimIdentityName: apimIdentityName\n    apimResourceGroupName: apimResourceGroupName\n  }\n}\n\nvar openaiDnsZoneName = 'privatelink.openai.azure.com'\n\nmodule dnsZone '../../apim-baseline/bicep/shared/modules/dnszone.bicep' = {\n  scope: resourceGroup(workloadResourceGroup.name)\n  name: take('${replace(openaiDnsZoneName, '.', '-')}-deploy', 64)\n  params: {\n    vnetName: vnetName\n    networkingResourceGroupName: networkingResourceGroupName\n    domain: openaiDnsZoneName\n  }\n}\n\nmodule simulatedPTUDeployment './openai/openai.bicep' = {\n  name: 'simulatedPTUDeployment'\n  scope: resourceGroup(workloadResourceGroup.name)\n  params: {\n    name: ptuAoaiDeploymentName\n    location: location\n    apimIdentityName: apimIdentityName\n    apimResourceGroupName: apimResourceGroupName\n    deploymentName: 'aoai'\n    vnetName: vnetName\n    privateEndpointSubnetid: privateEndpointSubnetid\n    networkingResourceGroupName: networkingResourceGroupName\n  }\n  dependsOn: [\n    dnsZone\n  ]\n}\n\nmodule simulatedPaygoOneDeployment './openai/openai.bicep' = {\n  name: 'simulatedPaygoOneDeployment'\n  scope: resourceGroup(workloadResourceGroup.name)\n  params: {\n    name: paygoOneAoaiDeploymentName\n    location: location\n    apimIdentityName: apimIdentityName\n    apimResourceGroupName: apimResourceGroupName\n    deploymentName: 'aoai'\n    vnetName: vnetName\n    privateEndpointSubnetid: privateEndpointSubnetid\n    networkingResourceGroupName: networkingResourceGroupName\n  }\n  dependsOn: [\n    dnsZone\n  ]\n}\n\nmodule simulatedPaygoTwoDeployment './openai/openai.bicep' = {\n  name: 'simulatedPaygoTwoDeployment'\n  scope: resourceGroup(workloadResourceGroup.name)\n  params: {\n    name: paygoTwoAoaiDeploymentName\n    location: location\n    apimIdentityName: apimIdentityName\n    apimResourceGroupName: apimResourceGroupName\n    deploymentName: 'aoai'\n    vnetName: vnetName\n    privateEndpointSubnetid: privateEndpointSubnetid\n    networkingResourceGroupName: networkingResourceGroupName\n  }\n  dependsOn: [\n    dnsZone\n  ]\n}\n\nmodule apiManagement 'apim-policies/apiManagement.bicep' = {\n  name: 'apiManagementDeploy'\n  scope: resourceGroup(apimResourceGroupName)\n  params: {\n    apiManagementServiceName: apiManagementServiceName\n    ptuDeploymentOneBaseUrl: '${simulatedPTUDeployment.outputs.endpoint}openai'\n    payAsYouGoDeploymentOneBaseUrl: '${simulatedPaygoOneDeployment.outputs.endpoint}openai'\n    payAsYouGoDeploymentTwoBaseUrl: '${simulatedPaygoTwoDeployment.outputs.endpoint}openai'\n    eventHubNamespaceName: eventHub.outputs.eventHubNamespaceName\n    eventHubName: eventHub.outputs.eventHubName\n    apimIdentityName: apimIdentityName\n  }\n}\n\n@description('Microsoft telemetry deployment.')\n#disable-next-line no-deployments-resources\nresource telemetrydeployment 'Microsoft.Resources/deployments@2021-04-01' = if (enableTelemetry) {\n  location: location\n  name: telemetryId\n  properties: {\n    mode: 'Incremental'\n    template: {\n      '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#'\n      contentVersion: '1.0.0.0'\n      resources: {}\n    }\n  }\n}\n\noutput apiManagementName string = apiManagement.outputs.apiManagementServiceName\noutput apiManagementAzureOpenAIProductSubscriptionKey string = apiManagement.outputs.apiManagementAzureOpenAIProductSubscriptionKey\noutput apiManagementMultitenantProduct1SubscriptionKey string = apiManagement.outputs.apiManagementMultitenantProduct1SubscriptionKey\noutput apiManagementMultitenantProduct2SubscriptionKey string = apiManagement.outputs.apiManagementMultitenantProduct2SubscriptionKey\n"
  },
  {
    "path": "scenarios/workload-genai/bicep/openai/openai.bicep",
    "content": "@description('Name of the resource.')\nparam name string\n@description('Location to deploy the resource. Defaults to the location of the resource group.')\nparam location string = resourceGroup().location\n@description('Tags for the resource.')\nparam tags object = {}\n\nparam deploymentName string = 'aoai'\n\nparam apimIdentityName string\nparam apimResourceGroupName string\n\nparam vnetName string\nparam privateEndpointSubnetid string\nparam networkingResourceGroupName string\n\n@description('Whether to enable public network access. Defaults to Enabled.')\n@allowed([\n  'Enabled'\n  'Disabled'\n])\nparam publicNetworkAccess string = 'Disabled'\n\n@description('The model name to be deployed. The model name can be found in the OpenAI portal.')\nparam modelName string = 'gpt-35-turbo'\n\n@description('The model version to be deployed. At the time of writing this is the latest version is eastus2.')\nparam modelVersion string = '0613'\n\nresource cognitiveServices 'Microsoft.CognitiveServices/accounts@2023-10-01-preview' = {\n  name: name\n  location: location\n  tags: tags\n  kind: 'OpenAI'\n  properties: {\n    customSubDomainName: toLower(name)\n    publicNetworkAccess: publicNetworkAccess\n  }\n  sku: {\n    name: 'S0'\n  }\n}\n\nresource cognitiveServicesOpenAIUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {\n  name: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' // Cognitive Services OpenAI User\n  scope: tenant()\n}\n\nresource apimIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = {\n  scope: resourceGroup(apimResourceGroupName)\n  name: apimIdentityName\n}\n\nresource assignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {\n  name: guid(cognitiveServices.id, apimIdentity.id, cognitiveServicesOpenAIUser.id)\n  scope: cognitiveServices\n  properties: {\n    principalId: apimIdentity.properties.principalId\n    roleDefinitionId: cognitiveServicesOpenAIUser.id\n    principalType: 'ServicePrincipal'\n  }\n}\n\nresource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = {\n  name: deploymentName\n  parent: cognitiveServices \n  sku: {\n    name: 'Standard'\n    capacity: 1\n  }\n  properties: {\n    raiPolicyName: 'Microsoft.Default'\n    model: {\n      format: 'OpenAI'\n      name: modelName\n      version: modelVersion\n    }\n  }\n}\n\nvar privateEndpoint_openai_Name = 'pep-${name}'\nvar openaiDnsZoneName = 'privatelink.openai.azure.com'\n\nmodule openaiPrivateEndpoint '../../../apim-baseline/bicep/shared/modules/privateendpoint.bicep' = {\n  name: privateEndpoint_openai_Name\n  params: {\n    location: location\n    privateEndpointName: privateEndpoint_openai_Name\n    groupId: 'account'\n    serviceResourceId: cognitiveServices.id\n    vnetName: vnetName\n    networkingResourceGroupName: networkingResourceGroupName\n    subnetId: privateEndpointSubnetid\n    domain: openaiDnsZoneName\n    createDnsZone: false\n  }\n}\n\n@description('ID for the deployed Cognitive Services resource.')\noutput id string = cognitiveServices.id\n@description('Name for the deployed Cognitive Services resource.')\noutput name string = cognitiveServices.name\n@description('Endpoint for the deployed Cognitive Services resource.')\noutput endpoint string = cognitiveServices.properties.endpoint\n@description('Host for the deployed Cognitive Services resource.')\noutput host string = split(cognitiveServices.properties.endpoint, '/')[2]\n"
  },
  {
    "path": "scenarios/workload-genai/policies/fragments/load-balancing/README.md",
    "content": "# Load balancing across PAYG, PTU instances\n\n## Capability\n\nAPI Management supports the following load balancing options for backend pools:\n\n- *Round-robin:* By default, requests are distributed evenly across the backends in the pool.\n- *Weighted*: Weights are assigned to the backends in the pool, and requests are distributed across the backends based on the relative weight assigned to each backend. Use this option for scenarios such as conducting a blue-green deployment.\n- *Priority-based:* Backends are organized in priority groups, and requests are sent to the backends in order of the priority groups. Within a priority group, requests are distributed either evenly across the backends, or (if assigned) according to the relative weight assigned to each backend.\n\n## Examples\n\n### Managing spikes with PAYG\n\nThe priority based load balancing policy can be used to manage spikes in traffic by routing traffic to PAYG endpoints when a PTU is out of capacity.\n"
  },
  {
    "path": "scenarios/workload-genai/policies/fragments/load-balancing/simple-priority-weighted.xml",
    "content": "<!-- \n    Policy fragment to implement a load balancing algorithm.\n\n    Expected named values\n    - The Pool backed that load balances the endpoints.\n -->\n<fragment>\n    <!-- MSI authentication to the backends-->\n    <authentication-managed-identity resource=\"https://cognitiveservices.azure.com\" output-token-variable-name=\"msi-access-token\" client-id=\"{{apim-identity}}\" ignore-error=\"false\" />\n    <set-header name=\"Authorization\" exists-action=\"override\">\n        <value>@(\"Bearer \" + (string)context.Variables[\"msi-access-token\"])</value>\n    </set-header>\n    <set-backend-service id=\"apim-generated-policy\" backend-id=\"aoai-lb-pool\" />\n</fragment>"
  },
  {
    "path": "scenarios/workload-genai/policies/fragments/manage-spikes-with-payg/README.md",
    "content": "# Managing spike across PTU instances using PAYG deployments.\n\n## Capability\n\nIn this capability, the traffic is routed to the PTU1 instance as the primary backend. When the PTU1 instance returns a 429 Retry response the request is re-submitted to the PAYG1 instance.\n\n## How the policy works\n\n- This capability leverages the APIM [`retry` policy](https://learn.microsoft.com/en-us/azure/api-management/retry-policy)\n\n- The segment in the retry policy will execute **at least once** and when the response is null (request entering first time into the retry segment) then it will be routed to the PTU instance.\n\n- If the PTU instance responds back with 429, then the request will be routed to the PAYG instance.\n"
  },
  {
    "path": "scenarios/workload-genai/policies/fragments/manage-spikes-with-payg/retry-with-payg.xml",
    "content": "<!-- Policy fragment to use Pay As You Go (PAYG) endpoints when the PTU endpoints are busy.\n\n    should be used inside the <backend> section of the policy\n\n    Expected named values\n    - ptu-endpoint-1, payg-endpoint-1, payg-endpoint-2: The backend endpoints to be load balanced. -->\n<fragment>\n\t<!-- Exponential retry policy: If the HTTP status code of the response is 300 or greater, the request is retried. \n     Maximum retries: 3. Initial retry interval: 1 second. Maximum retry interval: 10 seconds. \n     Retry interval increase per retry: 2 seconds -->\n\t<retry condition=\"@(context.Response.StatusCode == 429)\" count=\"3\" interval=\"1\" max-interval=\"10\" delta=\"2\">\n\t\t<!-- The retry policy executes its child policies once and then retries their execution until the retry \n        condition becomes false or retry count is exhausted. -->\n\t\t<set-variable name=\"body\" value=\"@(context.Request.Body.As<string>(preserveContent: true))\" />\n\t\t<choose>\n\t\t\t<when condition=\"@(context.Response.StatusCode == 429)\">\n\t\t\t\t<!-- If the PTU returns 429, the control enters this block -->\n\t\t\t\t<!-- It is also possible to have a pool of backends and load balance them -->\n\t\t\t\t<set-variable name=\"selected-backend-id\" value=\"payg-backend-1\" />\n\t\t\t\t<set-header name=\"Authorization\" exists-action=\"override\">\n\t\t\t\t\t<value>@(\"Bearer \" + (string)context.Variables[\"msi-access-token\"])</value>\n\t\t\t\t</set-header>\n\t\t\t\t<set-backend-service backend-id=\"@((string)context.Variables[\"selected-backend-id\"])\" />\n\t\t\t</when>\n\t\t\t<otherwise>\n\t\t\t\t<!-- This is the default path of the request -->\n\t\t\t\t<!-- The backend will point to the `ptu endpoint` or it is possible to load balance multiple \n                PTU endpoints as well -->\n\t\t\t\t<set-variable name=\"selected-backend-id\" value=\"ptu-backend-1\" />\n\t\t\t\t<set-header name=\"Authorization\" exists-action=\"override\">\n\t\t\t\t\t<value>@(\"Bearer \" + (string)context.Variables[\"msi-access-token\"])</value>\n\t\t\t\t</set-header>\n\t\t\t\t<set-backend-service backend-id=\"@((string)context.Variables[\"selected-backend-id\"])\" />\n\t\t\t</otherwise>\n\t\t</choose>\n\t\t<set-body>@((string)context.Variables[\"body\"])</set-body>\n\t\t<forward-request timeout=\"120\" fail-on-error-status-code=\"true\" buffer-response=\"false\" />\n\t</retry>\n</fragment>"
  },
  {
    "path": "scenarios/workload-genai/policies/fragments/rate-limiting/README.md",
    "content": "# Rate limiting using Tokens consumed per request\n\nIn Azure OpenAI, the rate limiting policy is based on the number of tokens consumed by the requests. In this example, there are samples of simple rate limiting by tokens and adaptive rate limiting scenarios.\n\n## Rate limiting by tokens\n\nRate limiting by tokens is implemented in 2 ways here. One using `azure-openai-token-limit` and the other using `rate-limit-by-key`\n\n1. Policy reference: [`rate-limiting-by-tokens.xml`](./rate-limiting-by-tokens.xml)\n\n    In MSFT build 2024, a new policy to rate limit by tokens for both streaming and non-streaming Azure OpenAI endpoints was launched. [This policy](https://learn.microsoft.com/en-us/azure/api-management/azure-openai-token-limit-policy) allows you to set rate limits based on the number of tokens consumed by the requests.\n\n2. Policy reference: [`rate-limiting-workaround.xml`](./rate-limiting-workaround.xml)\n\n   In scenarios, where the new rate limiting policy is not available, or if you are interacting with the non AOAI endpoints, it's worth considering the existing rate limiting policy in APIM.\n\n   **Example scenarios**\n\n   - Rate limiting a DallE endpoint with the number of images. The request to the DallE endpoint, will contain the number of images that needs to be returned in the response. This information can be parsed and can be used to increment the counter.\n   - Rate limiting a non Azure OpenAI endpoint, where the response structure contains the token usage but in a different format.\n\n    **Caveats**\n\n   - This policy only applies to non-streaming requests.\n   - It operates reactively, meaning it doesn't preemptively calculate tokens but instead waits for requests to breach the rate limit before blocking subsequent requests.\n\n## Adaptive rate limiting\n\nPolicy reference: [`adaptive-rate-limiting.xml`](./adaptive-rate-limiting.xml)\n\nIn this setup, multiple services have their own rate limits. They start with default limits but can increase them dynamically within a set maximum if there's spare capacity due to low usage by other services.\n\n**Explanation**\n   The rate-limit-by-key policy in Azure API Management (APIM) is a powerful tool for controlling access to your APIs based on the number of tokens consumed. Here's a step-by-step breakdown of how it operates:\n\n   1. **Token Consumption**: When a client makes a request to your API, the response includes information about the tokens consumed by that specific request. These tokens represent the resources utilized by the request, such as data transfer or processing.\n   2. **Incrementing Rate Limit Counters**: The policy extracts the token consumption data from the response and increments the corresponding rate limit counters. These counters track the usage of resources and enforce the defined rate limits.\n   3. **Global Rate Limit Counter**: In this example, there's a global rate limit counter set to the maximum rate limit initially. With each request, this counter decreases based on the tokens consumed. It resets at regular intervals, typically every 60 seconds, ensuring that the rate limits are enforced consistently over time.\n   4. **Dynamic Local Counters**: Alongside the global counter, there are dynamic local counters (triggered for specific request if the conditions are met) with different default rate limits. These counters can be adjusted based on the availability of the global rate limit counter. If there's spare capacity due to low usage by other services, these local counters can increase within a set maximum threshold, allowing services to temporarily access more resources.\n"
  },
  {
    "path": "scenarios/workload-genai/policies/fragments/rate-limiting/adaptive-rate-limiting.xml",
    "content": "<fragment>\n    <azure-openai-token-limit counter-key=\"GlobalTokensLimit\"\n        tokens-per-minute=\"500\"\n        estimate-prompt-tokens=\"true\"\n        remaining-tokens-header-name=\"x-apim-global-remaining-tokens\"\n        remaining-tokens-variable-name=\"globalRemainingTokens\"\n        tokens-consumed-header-name=\"x-apim-global-consumed-tokens\"/>\n\n    <choose>\n        <when condition=\"@(context.Request.Headers.ContainsKey(\"x-higher-limit\"))\">\n            <set-variable name=\"higherLimit\" value=\"@{\n                var defaultHigherLimit = 300;\n                if (context.Variables[\"globalRemainingTokens\"] is int globalRemainingTokens && globalRemainingTokens > 0)\n                {\n                    defaultHigherLimit += (int)(globalRemainingTokens * 0.1); \n                }\n                return (int)defaultHigherLimit;\n                }\" />\n            <azure-openai-token-limit counter-key=\"highRateLimitSvc\"\n                tokens-per-minute=\"@((int)context.Variables[\"higherLimit\"])\"\n                estimate-prompt-tokens=\"true\"\n                remaining-tokens-header-name=\"x-apim-high-rate-remaining-tokens\"/>\n        </when>\n        <otherwise>\n            <set-variable name=\"lowerRateLimit\" value=\"@{\n                var defaultLowerRateLimit = 100;\n                if (context.Variables[\"globalRemainingTokens\"] is int globalRemainingTokens && globalRemainingTokens > 0)\n                {\n                    defaultLowerRateLimit += (int)(globalRemainingTokens * 0.1); \n                }\n                return defaultLowerRateLimit;\n                }\" />\n            <azure-openai-token-limit counter-key=\"lowRateLimitSvc\"\n                tokens-per-minute=\"@((int)context.Variables[\"lowerRateLimit\"])\"\n                estimate-prompt-tokens=\"true\"\n                remaining-tokens-header-name=\"x-apim-low-rate-remaining-tokens\"/>\n        </otherwise>\n    </choose>\n</fragment>"
  },
  {
    "path": "scenarios/workload-genai/policies/fragments/rate-limiting/rate-limiting-by-tokens.xml",
    "content": "<fragment>\n    <!-- Rate limit configuration at a subscription level. Works for Azure OpenAI endpoint, \n         both streaming and non-streaming endpoints.\n         allows 500 tokens per 60 seconds. -->\n    <azure-openai-token-limit counter-key=\"@(String.Concat(context.Subscription.Id,\"-max-token\"))\"\n        tokens-per-minute=\"500\"\n        estimate-prompt-tokens=\"true\"\n        remaining-tokens-header-name=\"x-apim-max-remaining-tokens\"\n        tokens-consumed-header-name=\"x-apim-max-consumed-tokens\"/>\n</fragment>"
  },
  {
    "path": "scenarios/workload-genai/policies/fragments/rate-limiting/rate-limiting-workaround.xml",
    "content": "<fragment>\n    <!-- This is an alternate approach for scenarios where the `azure-openai-token-limit` policy can't be used \n    ex. DallE Endpoints where the image size from the request can be used to increment the counter.\n    Non Azure openAI endpoints, where the response structure containing the token is different.-->\n\n    <!-- Rate limit configuration for global tokens. \n         Allows 500 tokens per 60 seconds. \n         The count is incremented if the response status code is between 200 and 400 by any request -->\n    \n    <rate-limit-by-key calls=\"500\"\n        renewal-period=\"60\"\n        counter-key=\"GlobalTokensLimit\"\n        increment-condition=\"@(context.Response.StatusCode >= 200 && context.Response.StatusCode < 400)\"\n        increment-count=\"@(context.Response.Body.As<JObject>(true).SelectToken(\"usage.total_tokens\").ToObject<int>())\"\n        remaining-calls-variable-name=\"globalRemainingTokens\"\n        remaining-calls-header-name=\"x-apim-global-remaining-tokens\"/>\n</fragment>"
  },
  {
    "path": "scenarios/workload-genai/policies/fragments/usage-tracking/README.md",
    "content": "# Usage tracking of tokens\n\n## Capability\n\nIn this setup, you can track the usage of your APIs by sending token usage data to Application Insights or Azure Event Hub. By adding custom dimension of SubscriptionId, TokenUsage, OperationName, RequestId in the Application Insights (or Event Hub), you can track the token usage by specific dimensions.\n\n## Using Application Insights\n\nIn MSFT build 2024, a new policy to track the token usage as metric was launched. [This policy](https://learn.microsoft.com/en-us/azure/api-management/azure-openai-emit-token-metric-policy) allows APIM to log token count metrics (Total Tokens, Prompt Tokens and Completion Tokens) to the customer's Azure Application Insights metrics tied to the customer's APIM resource.\n\nA KQL Query to list the token consumption by the subscription id is as follows:\n\n```kql\ncustomMetrics\n| extend subId = tostring(parse_json(customDimensions).SubscriptionId)\n| summarize totalValueSum = sum(valueSum) by name, subId\n\n```\n\nThis policy supports streaming endpoints in Azure OpenAI.\n\n## Using Azure Event Hub\n\n### Azure EventHub over Custom Metrics to Azure Monitor\n\nIt is also possible to track the token consumption by sending the token count as a metrics to the Azure monitor.\n\nWith Azure EventHub adds the following advantages:\n\n- It is possible to track some additional context (in form of text) other than just numbers like in metrics.\n- The event streams will be near real time, in comparison to Azure monitor.\n- Varied opportunities to consume the data from EventHub for further processing, like Azure Stream Analytics, Azure Functions, Logic Apps, etc.\n\n### How the policy works\n\n- Azure OpenAI response will contain the token usage data. This policy extracts the token usage data from the response and sends it to Azure Event Hub.\n- This policy fragment needs to be included in the `outbound` section of the APIM policy.\n\n### Caveats\n\n- This policy only applies to non-streaming requests."
  },
  {
    "path": "scenarios/workload-genai/policies/fragments/usage-tracking/usage-tracking-with-appinsights.xml",
    "content": "<fragment>\n    <azure-openai-emit-token-metric namespace=\"aoai.token.usage\">\n        <dimension name=\"SubscriptionId\" value=\"@(context.Subscription.Id)\"/>\n        <dimension name=\"ClientIP\" value=\"@(context.Request.IpAddress)\" />\n        <dimension name=\"RequestId\" value=\"@(context.Request.IpAddress)\" />\n        <dimension name=\"OperationName\" value=\"@(context.Operation.Name)\" />\n    </azure-openai-emit-token-metric>\n</fragment>"
  },
  {
    "path": "scenarios/workload-genai/policies/fragments/usage-tracking/usage-tracking-with-eventhub.xml",
    "content": "<fragment>\n    <choose>\n        <!--Chargeback model using eventhub logging \n\t\t\t\tConditional Logging to EventHub:\n\t\t\t\tLogs specific information to an EventHub if the response status code is 200.\n\t\t\t\tCaptures various request and response details like service name, gateway ID, request ID, operation name, subscription ID, product ID, business unit name, invoked OpenAI instance, and total tokens used.-->\n        <when condition=\"@(context.Response.StatusCode == 200)\">\n            <log-to-eventhub logger-id=\"eventhub-logger\">@{\n                    return new JObject(\n                        new JProperty(\"Type\", \"Message\"),\n                        new JProperty(\"EventTime\", DateTime.UtcNow.ToString()),\n                        new JProperty(\"ServiceName\", context.Deployment.ServiceName),\n                        new JProperty(\"GatewayId\", context.Deployment.Gateway.Id),\n                        new JProperty(\"RequestId\", context.RequestId),\n                        new JProperty(\"RequestIp\", context.Request.IpAddress),\n                        new JProperty(\"OperationName\", context.Operation.Name),\n                        new JProperty(\"SubscriptionId\", context.Subscription.Id),\n                        new JProperty(\"TotalTokens\",  context.Response.Body.As&lt;JObject&gt;(preserveContent: true).SelectToken(\"usage.total_tokens\").ToString())\n                    ).ToString();\n                }</log-to-eventhub>\n        </when>\n    </choose>\n</fragment>"
  },
  {
    "path": "scenarios/workload-genai/policies/genai-policy.xml",
    "content": "<policies>\n    <inbound>\n        <base />\n        \n        <!-- sets the backed to the load balanced pool-->\n        <include-fragment fragment-id=\"simple-priority-weighted\" />\n\n        <!-- Rate limiting: applies tokens constraint defined as per the fragment, doesn't set the backend-->\n        <!-- there are other variants that can be used as well-->\n        <include-fragment fragment-id=\"rate-limiting-by-tokens\" />\n\n        <!-- tracks token consumption as custom metrics in App insights -->\n        <include-fragment fragment-id=\"usage-tracking-with-appinsights\" />\n    </inbound>\n    <backend>\n        <!-- either base or the fragment needs to be present.  -->\n        <!-- <base /> -->\n\n        <!-- set the count to the number of backends-->\n        <retry condition=\"@(context.Response.StatusCode == 429)\" count=\"3\" interval=\"1\" first-fast-retry=\"true\">\n            <forward-request buffer-request-body=\"true\" />\n        </retry>\n        <!-- END: sets backend service -->\n    </backend>\n    <outbound>\n        <base />\n        <!-- tracks token consumption using the event hub, this can be used as a workaround. -->\n        <!-- <include-fragment fragment-id=\"usage-tracking-with-eventhub\" /> -->\n    </outbound>\n    <on-error>\n        <base />\n    </on-error>\n</policies>\n"
  },
  {
    "path": "scenarios/workload-genai/policies/multi-tenancy/README.md",
    "content": "# Multi-Tenancy using Azure API Management\n\nCustomers may sometimes would also like to have a multi-tenancy model on top of their backend APIs.\nThis is a typical requirement for customers/businesses operating in SaaS-based models. Multi-tenancy in such scenarios is typically defined using following two business concepts:\n\n1. **Tiers**:\nTiers govern the _quality of service_ exposed to the users based on their pricing model.\nFor instance, a _Freemium_ tier can be thought of as for consumer groups who would like to explore the service at no cost thus having very limited quota and rate limiting, likewise a _Premium_ tier can be defined for consumers who would like to have the most premium-grade service experience with the maximum possible rate limiting and quota.\n\n2. **Entitlements**: Apart from _tiers_, businesses would also like to define _entitlements_ ,which means _giving access of only selected APIs_ for a particular consumer group. For instance, access to only chat based APIs for consumer A or only image APIs for consumer B.\n\n## Initial Approach\n\nAn initial approach can be to define separate APIs for different customers based on their _tiers_ & _entitlement_ combinations by defining the policies at the API level.The following image describes this approach -\n\n![Rudimentary Solution Approach](../../../../docs/images/multi-tenancy-without-products.png)\n\n### Downsides\n\nAs we can clearly observe, this solution results in a lot of redundancy of APIs and API policies, overall resulting in a very convoluted design. The API policy code is also now bloated with unnecessary responsibilities which does not fall under the scope of API(for e.g., with the above design, any new policy we want to include has to be defined as part of the API policies). Further, it's also hard to define the entitlements using this model.\n\n## Solution Approach\n\nA better and effective solution can be built by leveraging the concept of APIM [Products](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-add-products?tabs=azure-portal&pivots=interactive), helping us to cater to our \"_entitlement_\" requirement by grouping APIs related to that specific entitlement in a logical container and cater to our requirement of \"_tier_\" by leveraging Product's policies for the respective tier (like quota, rate limiting along with the respective backend model for e.g.: either a PAYG or PTU). Lastly, by defining [subscriptions](https://learn.microsoft.com/en-us/azure/api-management/api-management-subscriptions) at the Product level and giving access of only the Product's subscriptions to the end-user group, the users can only interact with the service via the specific Product's subscription.\nFollowing design demonstrates this approach further -\n![Solution Approach using Products](../../../../docs/images/multi-tenancy-using-products.png)\n\nProduct policy essentially here is helping us to define our \"tenant\" specific policies.\n\n### Benefits\n\nThis solution not only helps to cater to the multi-tenancy requirement in an effective manner but also makes the overall design modular and extensible by having the capability to define n-number of products and APIs and their different combinations with clear separation of concerns and adherence to the DRY(Do not Repeat Yourself) principle.\n\n_Note:\nAs this a general pattern, this solution is not only limited to the GenAI backend but can be used with any general backend as well._\n\n### References\n\nFollowing blog post further describes this scenario in detail -\nhttps://devblogs.microsoft.com/ise/multitenant-genai-gateway-using-apim/\n\n## Products and Policies\n\nTo summarize:\n\n- Products: Acts as logical container of APIs for a specific consumer group (e.g., Chat APIs or Embedding APIs).\n\n- Product Policies: For defining tenant policies (e.g., rate limits, quotas).\n\nAnd as part of this capability's example scenario, we will apply a new _quota policy_ at the product level, such that if the number of requests to APIM via that Product's subscription exceed as per the defined \"calls\" attribute value, then the product policy will accordingly block the subsequent requests from that subscription until the quota is refreshed based on the defined \"renewal-period\" and \"counter-key\" attributes.\n\nFor this setup, we create two sample products(`multi-tenant-product1`,`multi-tenant-product2`) with different counter keys(`<subscription_id>-mt-product1`,`<subscription_id>-mt-product2`) respectively and with the following policies :\n\n- [`multi-tenant-product1-policy.xml`](multi-tenant-product1-policy.xml)\n- [`multi-tenant-product2-policy.xml`](multi-tenant-product2-policy.xml)\n\nThe Product Policy can thus be extended with any number of higher-level policies (for e.g., defining quota or rate limits) and any attributes (for e.g., setting the name of the backend pool) as per the respective _tenant's_ requirement.\n\n## Note\n\nThis capability/pattern is over the top of the existing core capabilities, which can be played around & tested separately and hence does not impact the existing setup.\n\nHowever, ifmulti-tenancy capability is not needed i.e. these resources created as part of our deployment, then the respective code blocks can be commented from the bicep and terraform scripts.\n"
  },
  {
    "path": "scenarios/workload-genai/policies/multi-tenancy/multi-tenant-product1-policy.xml",
    "content": "<!--\n    - Policies are applied in the order they appear.\n    - Position <base/> inside a section to inherit policies from the outer scope.\n    - Comments within policies are not preserved.\n-->\n<!-- Add policies as children to the <inbound>, <outbound>, <backend>, and <on-error> elements -->\n<policies>\n    <!-- Throttle, authorize, validate, cache, or transform the requests -->\n    <inbound>\n        <base />\n        <!-- Define the Quota/Rate limiting policies or any tenant specific policies here..\n        Here, we are using subscriptionId as counter-key but it can be any unique identifier -->\n        <quota-by-key calls=\"5\" renewal-period=\"300\" counter-key=\"@(String.Concat(context.Subscription.Id,\"-mt-product1\"))\" />\n    </inbound>\n    <!-- Control if and how the requests are forwarded to services  -->\n    <backend>\n        <base />\n    </backend>\n    <!-- Customize the responses -->\n    <outbound>\n        <base />\n    </outbound>\n    <!-- Handle exceptions and customize error responses  -->\n    <on-error>\n        <base />\n    </on-error>\n</policies>\n"
  },
  {
    "path": "scenarios/workload-genai/policies/multi-tenancy/multi-tenant-product2-policy.xml",
    "content": "<!--\n    - Policies are applied in the order they appear.\n    - Position <base/> inside a section to inherit policies from the outer scope.\n    - Comments within policies are not preserved.\n-->\n<!-- Add policies as children to the <inbound>, <outbound>, <backend>, and <on-error> elements -->\n<policies>\n    <!-- Throttle, authorize, validate, cache, or transform the requests -->\n    <inbound>\n        <base />\n        <!-- Define the Quota/Rate limiting policies or any tenant specific policies here..\n        Here, we are using subscriptionId as counter-key but it can be any unique identifier -->\n        <quota-by-key calls=\"3\" renewal-period=\"300\" counter-key=\"@(String.Concat(context.Subscription.Id,\"-mt-product2\"))\" />\n    </inbound>\n    <!-- Control if and how the requests are forwarded to services  -->\n    <backend>\n        <base />\n    </backend>\n    <!-- Customize the responses -->\n    <outbound>\n        <base />\n    </outbound>\n    <!-- Handle exceptions and customize error responses  -->\n    <on-error>\n        <base />\n    </on-error>\n</policies>\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/README.md",
    "content": "# Scenario 3: Azure API Management - Gen AI Backend  [Terraform]\n\nThis is the Terraform-based deployment guide for [Scenario 3: Azure API Management - Gen AI Backend](../README.md).\n\n## Prerequisites\n\nThis scenario requires the completion of the [Azure API Management - Secure Baseline](../../apim-baseline/README.md) scenario ([using the terraform-based deployment](../../apim-baseline/terraform/README.md)).\n\n## Steps\n\nRun the following command to deploy the scenarios\n\n```bash\n./scripts/terraform/deploy-workload-genai.sh\n```\n\nTest the hello api using the generated command from the output\n\n## Troubleshooting\n\nIf you see the message `-bash: ./deploy-workload-genai.sh: /bin/bash^M: bad interpreter: No such file or directory` when running the script, you can fix this by running the following command:\n\n   ```bash\n    sed -i -e 's/\\r$//' deploy-workload-genai.sh\n   ```\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/backend.tf.sample",
    "content": "#   terraform {\n#     backend \"azurerm\" {\n#     # ----------------------\n#     # Will be passing in these arguments via CLI as the state file \\\n#     #  is now being overwritten via local testing environments\n#     # > https://developer.hashicorp.com/terraform/language/settings/backends/configuration#command-line-key-value-pairs\n#     # ----------------------\n#     # e.g: terraform init \\\n#     #        -backend-config=\"resource_group_name=rg-tfstate-auseast\"     \\\n#     #        -backend-config=\"storage_account_name=tfstateauseaststorage\" \\\n#     #        -backend-config=\"container_name=apimlza\"       \\\n#     #        -backend-config=\"key=terraform-apimlza-dev-v2.tfstate\"\n#     # ----------------------\n#     # resource_group_name = \"rg-tfstate-auseast\"\n#     # storage_account_name = \"tfstateauseaststorage\"\n#     # container_name       = \"apimlza\"\n#     # key                  = \"terraform-apimlza-dev-v6.tfstate\"\n#   }\n# }"
  },
  {
    "path": "scenarios/workload-genai/terraform/main.tf",
    "content": "locals {\n  resourceSuffix               = \"${var.workloadName}-${var.environment}-${var.location}-${var.identifier}\"\n  networkingResourceGroupName  = \"rg-networking-${local.resourceSuffix}\"\n  apimResourceGroupName        = \"rg-apim-${local.resourceSuffix}\"\n  apimName                     = \"apim-${local.resourceSuffix}\"\n  openaiResourceGroupName      = \"rg-openai-${local.resourceSuffix}\"\n  apim_cs_vnet_name            = \"vnet-apim-cs-${local.resourceSuffix}\"\n  deploy_subnet_name           = \"snet-deploy-${local.resourceSuffix}\"\n  private_endpoint_subnet_name = \"snet-prep-${local.resourceSuffix}\"\n  eventHubNamespaceName        = \"eh-ns-${local.resourceSuffix}\"\n  apimIdentityName             = \"identity-${local.apimName}\"\n}\n\ndata \"azurerm_client_config\" \"current\" {\n}\n\ndata \"azurerm_api_management\" \"apim\" {\n  name                = local.apimName\n  resource_group_name = local.apimResourceGroupName\n}\n\ndata \"azurerm_resource_group\" \"networking\" {\n  name = local.networkingResourceGroupName\n}\n\ndata \"azurerm_resource_group\" \"apim\" {\n  name = local.apimResourceGroupName\n}\n\ndata \"azurerm_virtual_network\" \"apim_cs_vnet\" {\n  name                = local.apim_cs_vnet_name\n  resource_group_name = local.networkingResourceGroupName\n}\n\ndata \"azurerm_subnet\" \"private_endpoint_subnet\" {\n  name                 = local.private_endpoint_subnet_name\n  resource_group_name  = local.networkingResourceGroupName\n  virtual_network_name = local.apim_cs_vnet_name\n}\n\ndata \"azurerm_subnet\" \"deploy_subnet\" {\n  name                 = local.deploy_subnet_name\n  resource_group_name  = local.networkingResourceGroupName\n  virtual_network_name = local.apim_cs_vnet_name\n}\n\ndata \"azurerm_user_assigned_identity\" \"apimIdentity\" {\n  name                = local.apimIdentityName\n  resource_group_name = local.apimResourceGroupName\n}\n\nresource \"azurerm_resource_group\" \"rg\" {\n  name     = local.openaiResourceGroupName\n  location = var.location\n}\n\n\nmodule \"openai_private_dns_zone\" {\n  source                      = \"./modules/private_dns_zone\"\n  name                        = \"privatelink.openai.azure.com\"\n  resource_group_name         = azurerm_resource_group.rg.name\n  virtual_networks_to_link_id = data.azurerm_virtual_network.apim_cs_vnet.id\n}\n\nmodule \"openai_simulatedPTUDeployment_private_endpoint\" {\n  source                         = \"./modules/private_endpoint\"\n  name                           = \"pep-${module.simulatedPTUDeployment.name}\"\n  location                       = var.location\n  resource_group_name            = azurerm_resource_group.rg.name\n  subnet_id                      = data.azurerm_subnet.private_endpoint_subnet.id\n  private_connection_resource_id = module.simulatedPTUDeployment.id\n  is_manual_connection           = false\n  subresource_name               = \"account\"\n  private_dns_zone_group_name    = \"OpenAiPrivateDnsZoneGroup\"\n  private_dns_zone_group_ids     = [module.openai_private_dns_zone.id]\n}\n\nmodule \"openai_simulatedPaygoOneDeployment_private_endpoint\" {\n  source                         = \"./modules/private_endpoint\"\n  name                           = \"pep-${module.simulatedPaygoOneDeployment.name}\"\n  location                       = var.location\n  resource_group_name            = azurerm_resource_group.rg.name\n  subnet_id                      = data.azurerm_subnet.private_endpoint_subnet.id\n  private_connection_resource_id = module.simulatedPaygoOneDeployment.id\n  is_manual_connection           = false\n  subresource_name               = \"account\"\n  private_dns_zone_group_name    = \"OpenAiPrivateDnsZoneGroup\"\n  private_dns_zone_group_ids     = [module.openai_private_dns_zone.id]\n}\n\nmodule \"openai_simulatedPaygoTwoDeployment_private_endpoint\" {\n  source                         = \"./modules/private_endpoint\"\n  name                           = \"pep-${module.simulatedPaygoTwoDeployment.name}\"\n  location                       = var.location\n  resource_group_name            = azurerm_resource_group.rg.name\n  subnet_id                      = data.azurerm_subnet.private_endpoint_subnet.id\n  private_connection_resource_id = module.simulatedPaygoTwoDeployment.id\n  is_manual_connection           = false\n  subresource_name               = \"account\"\n  private_dns_zone_group_name    = \"OpenAiPrivateDnsZoneGroup\"\n  private_dns_zone_group_ids     = [module.openai_private_dns_zone.id]\n}\n\nmodule \"simulatedPTUDeployment\" {\n  source                        = \"./modules/openai\"\n  name                          = \"ptu-${local.resourceSuffix}\"\n  location                      = var.location\n  resource_group_name           = azurerm_resource_group.rg.name\n  sku_name                      = var.openai_sku_name\n  deployments                   = var.openai_deployments\n  custom_subdomain_name         = lower(\"${local.resourceSuffix}${var.openai_name}-ptu\")\n  public_network_access_enabled = var.openai_public_network_access_enabled\n  apimIdentityName              = data.azurerm_user_assigned_identity.apimIdentity.name\n  apimResourceGroupName         = local.apimResourceGroupName\n}\n\nmodule \"simulatedPaygoOneDeployment\" {\n  source                        = \"./modules/openai\"\n  name                          = \"paygo-one-${local.resourceSuffix}\"\n  location                      = var.location\n  resource_group_name           = azurerm_resource_group.rg.name\n  sku_name                      = var.openai_sku_name\n  deployments                   = var.openai_deployments\n  custom_subdomain_name         = lower(\"${local.resourceSuffix}${var.openai_name}-paygo-one\")\n  public_network_access_enabled = var.openai_public_network_access_enabled\n  apimIdentityName              = data.azurerm_user_assigned_identity.apimIdentity.name\n  apimResourceGroupName         = local.apimResourceGroupName\n}\n\nmodule \"simulatedPaygoTwoDeployment\" {\n  source                        = \"./modules/openai\"\n  name                          = \"paygo-two-${local.resourceSuffix}\"\n  location                      = var.location\n  resource_group_name           = azurerm_resource_group.rg.name\n  sku_name                      = var.openai_sku_name\n  deployments                   = var.openai_deployments\n  custom_subdomain_name         = lower(\"${local.resourceSuffix}${var.openai_name}-paygo-two\")\n  public_network_access_enabled = var.openai_public_network_access_enabled\n  apimIdentityName              = data.azurerm_user_assigned_identity.apimIdentity.name\n  apimResourceGroupName         = local.apimResourceGroupName\n}\n\nmodule \"eventHub\" {\n  source                  = \"./modules/eventhub\"\n  eventHubName            = var.eventHubName\n  eventHubNamespaceName   = local.eventHubNamespaceName\n  location                = var.location\n  apimIdentityName        = data.azurerm_user_assigned_identity.apimIdentity.name\n  apimResourceGroupName   = data.azurerm_resource_group.apim.name\n  openaiResourceGroupName = azurerm_resource_group.rg.name\n}\n\nmodule \"apiManagement\" {\n  source                         = \"./modules/apim_policies\"\n  location                       = var.location\n  openaiResourceGroupName        = local.openaiResourceGroupName\n  resourceGroupName              = local.apimResourceGroupName\n  apiManagementServiceName       = local.apimName\n  ptuDeploymentOneBaseUrl        = \"${module.simulatedPTUDeployment.endpoint}openai\"\n  payAsYouGoDeploymentOneBaseUrl = \"${module.simulatedPaygoOneDeployment.endpoint}openai\"\n  payAsYouGoDeploymentTwoBaseUrl = \"${module.simulatedPaygoTwoDeployment.endpoint}openai\"\n  eventHubNamespaceName          = module.eventHub.eventHubNamespaceName\n  eventHubName                   = module.eventHub.eventHubName\n  apimIdentityName               = data.azurerm_user_assigned_identity.apimIdentity.name\n\n  depends_on = [\n    module.eventHub\n  ]\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/apim_policies/api-specs/openapi-spec.json",
    "content": "{\n    \"openapi\": \"3.0.0\",\n    \"info\": {\n      \"title\": \"Azure OpenAI Service API\",\n      \"description\": \"Azure OpenAI APIs for completions and search\",\n      \"version\": \"2024-02-01\"\n    },\n    \"servers\": [\n      {\n        \"url\": \"https://change-this.com/openai\"\n      }\n    ],\n    \"security\": [\n      {\n        \"bearer\": [\n          \"api.read\"\n        ]\n      },\n      {\n        \"apiKey\": []\n      }\n    ],\n    \"paths\": {\n      \"/deployments/{deployment-id}/completions\": {\n        \"post\": {\n          \"summary\": \"Creates a completion for the provided prompt, parameters and chosen model.\",\n          \"operationId\": \"Completions_Create\",\n          \"parameters\": [\n            {\n              \"in\": \"path\",\n              \"name\": \"deployment-id\",\n              \"required\": true,\n              \"schema\": {\n                \"type\": \"string\",\n                \"example\": \"davinci\",\n                \"description\": \"Deployment id of the model which was deployed.\"\n              }\n            },\n            {\n              \"in\": \"query\",\n              \"name\": \"api-version\",\n              \"required\": true,\n              \"schema\": {\n                \"type\": \"string\",\n                \"example\": \"2024-02-01\",\n                \"description\": \"api version\"\n              }\n            }\n          ],\n          \"requestBody\": {\n            \"required\": true,\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"prompt\": {\n                      \"description\": \"The prompt(s) to generate completions for, encoded as a string or array of strings.\\nNote that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document. Maximum allowed size of string list is 2048.\",\n                      \"oneOf\": [\n                        {\n                          \"type\": \"string\",\n                          \"default\": \"\",\n                          \"example\": \"This is a test.\",\n                          \"nullable\": true\n                        },\n                        {\n                          \"type\": \"array\",\n                          \"items\": {\n                            \"type\": \"string\",\n                            \"default\": \"\",\n                            \"example\": \"This is a test.\",\n                            \"nullable\": false\n                          },\n                          \"description\": \"Array size minimum of 1 and maximum of 2048\"\n                        }\n                      ]\n                    },\n                    \"max_tokens\": {\n                      \"description\": \"The token count of your prompt plus max_tokens cannot exceed the model's context length. Most models have a context length of 2048 tokens (except for the newest models, which support 4096). Has minimum of 0.\",\n                      \"type\": \"integer\",\n                      \"default\": 16,\n                      \"example\": 16,\n                      \"nullable\": true\n                    },\n                    \"temperature\": {\n                      \"description\": \"What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer.\\nWe generally recommend altering this or top_p but not both.\",\n                      \"type\": \"number\",\n                      \"default\": 1,\n                      \"example\": 1,\n                      \"nullable\": true\n                    },\n                    \"top_p\": {\n                      \"description\": \"An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\\nWe generally recommend altering this or temperature but not both.\",\n                      \"type\": \"number\",\n                      \"default\": 1,\n                      \"example\": 1,\n                      \"nullable\": true\n                    },\n                    \"logit_bias\": {\n                      \"description\": \"Defaults to null. Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this tokenizer tool (which works for both GPT-2 and GPT-3) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. As an example, you can pass {\\\"50256\\\" &#58; -100} to prevent the <|endoftext|> token from being generated.\",\n                      \"type\": \"object\",\n                      \"nullable\": false\n                    },\n                    \"user\": {\n                      \"description\": \"A unique identifier representing your end-user, which can help monitoring and detecting abuse\",\n                      \"type\": \"string\",\n                      \"nullable\": false\n                    },\n                    \"n\": {\n                      \"description\": \"How many completions to generate for each prompt. Minimum of 1 and maximum of 128 allowed.\\nNote: Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for max_tokens and stop.\",\n                      \"type\": \"integer\",\n                      \"default\": 1,\n                      \"example\": 1,\n                      \"nullable\": true\n                    },\n                    \"stream\": {\n                      \"description\": \"Whether to stream back partial progress. If set, tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a data: [DONE] message.\",\n                      \"type\": \"boolean\",\n                      \"nullable\": true,\n                      \"default\": false\n                    },\n                    \"logprobs\": {\n                      \"description\": \"Include the log probabilities on the logprobs most likely tokens, as well the chosen tokens. For example, if logprobs is 5, the API will return a list of the 5 most likely tokens. The API will always return the logprob of the sampled token, so there may be up to logprobs+1 elements in the response.\\nMinimum of 0 and maximum of 5 allowed.\",\n                      \"type\": \"integer\",\n                      \"default\": null,\n                      \"nullable\": true\n                    },\n                    \"suffix\": {\n                      \"type\": \"string\",\n                      \"nullable\": true,\n                      \"description\": \"The suffix that comes after a completion of inserted text.\"\n                    },\n                    \"echo\": {\n                      \"description\": \"Echo back the prompt in addition to the completion\",\n                      \"type\": \"boolean\",\n                      \"default\": false,\n                      \"nullable\": true\n                    },\n                    \"stop\": {\n                      \"description\": \"Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence.\",\n                      \"oneOf\": [\n                        {\n                          \"type\": \"string\",\n                          \"default\": \"<|endoftext|>\",\n                          \"example\": \"\\n\",\n                          \"nullable\": true\n                        },\n                        {\n                          \"type\": \"array\",\n                          \"items\": {\n                            \"type\": \"string\",\n                            \"example\": \"\\n\",\n                            \"nullable\": false\n                          },\n                          \"description\": \"Array minimum size of 1 and maximum of 4\"\n                        }\n                      ]\n                    },\n                    \"completion_config\": {\n                      \"type\": \"string\",\n                      \"nullable\": true\n                    },\n                    \"presence_penalty\": {\n                      \"description\": \"Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.\",\n                      \"type\": \"number\",\n                      \"default\": 0\n                    },\n                    \"frequency_penalty\": {\n                      \"description\": \"Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.\",\n                      \"type\": \"number\",\n                      \"default\": 0\n                    },\n                    \"best_of\": {\n                      \"description\": \"Generates best_of completions server-side and returns the \\\"best\\\" (the one with the highest log probability per token). Results cannot be streamed.\\nWhen used with n, best_of controls the number of candidate completions and n specifies how many to return - best_of must be greater than n.\\nNote: Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for max_tokens and stop. Has maximum value of 128.\",\n                      \"type\": \"integer\"\n                    }\n                  }\n                },\n                \"example\": {\n                  \"prompt\": \"Negate the following sentence.The price for bubblegum increased on thursday.\\n\\n Negated Sentence:\",\n                  \"max_tokens\": 50\n                }\n              }\n            }\n          },\n          \"responses\": {\n            \"200\": {\n              \"description\": \"OK\",\n              \"content\": {\n                \"application/json\": {\n                  \"schema\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                      \"id\": {\n                        \"type\": \"string\"\n                      },\n                      \"object\": {\n                        \"type\": \"string\"\n                      },\n                      \"created\": {\n                        \"type\": \"integer\"\n                      },\n                      \"model\": {\n                        \"type\": \"string\"\n                      },\n                      \"prompt_filter_results\": {\n                        \"$ref\": \"#/components/schemas/promptFilterResults\"\n                      },\n                      \"choices\": {\n                        \"type\": \"array\",\n                        \"items\": {\n                          \"type\": \"object\",\n                          \"properties\": {\n                            \"text\": {\n                              \"type\": \"string\"\n                            },\n                            \"index\": {\n                              \"type\": \"integer\"\n                            },\n                            \"logprobs\": {\n                              \"type\": \"object\",\n                              \"properties\": {\n                                \"tokens\": {\n                                  \"type\": \"array\",\n                                  \"items\": {\n                                    \"type\": \"string\"\n                                  }\n                                },\n                                \"token_logprobs\": {\n                                  \"type\": \"array\",\n                                  \"items\": {\n                                    \"type\": \"number\"\n                                  }\n                                },\n                                \"top_logprobs\": {\n                                  \"type\": \"array\",\n                                  \"items\": {\n                                    \"type\": \"object\",\n                                    \"additionalProperties\": {\n                                      \"type\": \"number\"\n                                    }\n                                  }\n                                },\n                                \"text_offset\": {\n                                  \"type\": \"array\",\n                                  \"items\": {\n                                    \"type\": \"integer\"\n                                  }\n                                }\n                              },\n                              \"nullable\": true\n                            },\n                            \"finish_reason\": {\n                              \"type\": \"string\"\n                            },\n                            \"content_filter_results\": {\n                              \"$ref\": \"#/components/schemas/contentFilterChoiceResults\"\n                            }\n                          }\n                        }\n                      },\n                      \"usage\": {\n                        \"type\": \"object\",\n                        \"properties\": {\n                          \"completion_tokens\": {\n                            \"type\": \"number\",\n                            \"format\": \"int32\"\n                          },\n                          \"prompt_tokens\": {\n                            \"type\": \"number\",\n                            \"format\": \"int32\"\n                          },\n                          \"total_tokens\": {\n                            \"type\": \"number\",\n                            \"format\": \"int32\"\n                          }\n                        },\n                        \"required\": [\n                          \"prompt_tokens\",\n                          \"total_tokens\",\n                          \"completion_tokens\"\n                        ]\n                      }\n                    },\n                    \"required\": [\n                      \"id\",\n                      \"object\",\n                      \"created\",\n                      \"model\",\n                      \"choices\"\n                    ]\n                  },\n                  \"example\": {\n                    \"model\": \"davinci\",\n                    \"object\": \"text_completion\",\n                    \"id\": \"cmpl-4509KAos68kxOqpE2uYGw81j6m7uo\",\n                    \"created\": 1637097562,\n                    \"choices\": [\n                      {\n                        \"index\": 0,\n                        \"text\": \"The price for bubblegum decreased on thursday.\",\n                        \"logprobs\": null,\n                        \"finish_reason\": \"stop\"\n                      }\n                    ]\n                  }\n                }\n              },\n              \"headers\": {\n                \"apim-request-id\": {\n                  \"description\": \"Request ID for troubleshooting purposes\",\n                  \"schema\": {\n                    \"type\": \"string\"\n                  }\n                }\n              }\n            },\n            \"default\": {\n              \"description\": \"Service unavailable\",\n              \"content\": {\n                \"application/json\": {\n                  \"schema\": {\n                    \"$ref\": \"#/components/schemas/errorResponse\"\n                  }\n                }\n              },\n              \"headers\": {\n                \"apim-request-id\": {\n                  \"description\": \"Request ID for troubleshooting purposes\",\n                  \"schema\": {\n                    \"type\": \"string\"\n                  }\n                }\n              }\n            }\n          },\n          \"x-ms-examples\": {\n            \"Create a completion.\": {\n              \"$ref\": \"./examples/completions.json\"\n            }\n          }\n        }\n      },\n      \"/deployments/{deployment-id}/embeddings\": {\n        \"post\": {\n          \"summary\": \"Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms.\",\n          \"operationId\": \"embeddings_create\",\n          \"parameters\": [\n            {\n              \"in\": \"path\",\n              \"name\": \"deployment-id\",\n              \"required\": true,\n              \"schema\": {\n                \"type\": \"string\",\n                \"example\": \"ada-search-index-v1\"\n              },\n              \"description\": \"The deployment id of the model which was deployed.\"\n            },\n            {\n              \"in\": \"query\",\n              \"name\": \"api-version\",\n              \"required\": true,\n              \"schema\": {\n                \"type\": \"string\",\n                \"example\": \"2024-02-01\",\n                \"description\": \"api version\"\n              }\n            }\n          ],\n          \"requestBody\": {\n            \"required\": true,\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": true,\n                  \"properties\": {\n                    \"input\": {\n                      \"description\": \"Input text to get embeddings for, encoded as a string. To get embeddings for multiple inputs in a single request, pass an array of strings. Each input must not exceed 2048 tokens in length.\\nUnless you are embedding code, we suggest replacing newlines (\\\\n) in your input with a single space, as we have observed inferior results when newlines are present.\",\n                      \"oneOf\": [\n                        {\n                          \"type\": \"string\",\n                          \"default\": \"\",\n                          \"example\": \"This is a test.\",\n                          \"nullable\": true\n                        },\n                        {\n                          \"type\": \"array\",\n                          \"minItems\": 1,\n                          \"maxItems\": 2048,\n                          \"items\": {\n                            \"type\": \"string\",\n                            \"minLength\": 1,\n                            \"example\": \"This is a test.\",\n                            \"nullable\": false\n                          }\n                        }\n                      ]\n                    },\n                    \"user\": {\n                      \"description\": \"A unique identifier representing your end-user, which can help monitoring and detecting abuse.\",\n                      \"type\": \"string\",\n                      \"nullable\": false\n                    },\n                    \"input_type\": {\n                      \"description\": \"input type of embedding search to use\",\n                      \"type\": \"string\",\n                      \"example\": \"query\"\n                    }\n                  },\n                  \"required\": [\n                    \"input\"\n                  ]\n                }\n              }\n            }\n          },\n          \"responses\": {\n            \"200\": {\n              \"description\": \"OK\",\n              \"content\": {\n                \"application/json\": {\n                  \"schema\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                      \"object\": {\n                        \"type\": \"string\"\n                      },\n                      \"model\": {\n                        \"type\": \"string\"\n                      },\n                      \"data\": {\n                        \"type\": \"array\",\n                        \"items\": {\n                          \"type\": \"object\",\n                          \"properties\": {\n                            \"index\": {\n                              \"type\": \"integer\"\n                            },\n                            \"object\": {\n                              \"type\": \"string\"\n                            },\n                            \"embedding\": {\n                              \"type\": \"array\",\n                              \"items\": {\n                                \"type\": \"number\"\n                              }\n                            }\n                          },\n                          \"required\": [\n                            \"index\",\n                            \"object\",\n                            \"embedding\"\n                          ]\n                        }\n                      },\n                      \"usage\": {\n                        \"type\": \"object\",\n                        \"properties\": {\n                          \"prompt_tokens\": {\n                            \"type\": \"integer\"\n                          },\n                          \"total_tokens\": {\n                            \"type\": \"integer\"\n                          }\n                        },\n                        \"required\": [\n                          \"prompt_tokens\",\n                          \"total_tokens\"\n                        ]\n                      }\n                    },\n                    \"required\": [\n                      \"object\",\n                      \"model\",\n                      \"data\",\n                      \"usage\"\n                    ]\n                  }\n                }\n              }\n            }\n          },\n          \"x-ms-examples\": {\n            \"Create a embeddings.\": {\n              \"$ref\": \"./examples/embeddings.json\"\n            }\n          }\n        }\n      },\n      \"/deployments/{deployment-id}/chat/completions\": {\n        \"post\": {\n          \"summary\": \"Creates a completion for the chat message\",\n          \"operationId\": \"ChatCompletions_Create\",\n          \"parameters\": [\n            {\n              \"in\": \"path\",\n              \"name\": \"deployment-id\",\n              \"required\": true,\n              \"schema\": {\n                \"type\": \"string\",\n                \"description\": \"Deployment id of the model which was deployed.\"\n              }\n            },\n            {\n              \"in\": \"query\",\n              \"name\": \"api-version\",\n              \"required\": true,\n              \"schema\": {\n                \"type\": \"string\",\n                \"example\": \"2024-02-01\",\n                \"description\": \"api version\"\n              }\n            }\n          ],\n          \"requestBody\": {\n            \"required\": true,\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/createChatCompletionRequest\"\n                }\n              }\n            }\n          },\n          \"responses\": {\n            \"200\": {\n              \"description\": \"OK\",\n              \"content\": {\n                \"application/json\": {\n                  \"schema\": {\n                    \"$ref\": \"#/components/schemas/createChatCompletionResponse\"\n                  }\n                }\n              },\n              \"headers\": {\n                \"apim-request-id\": {\n                  \"description\": \"Request ID for troubleshooting purposes\",\n                  \"schema\": {\n                    \"type\": \"string\"\n                  }\n                }\n              }\n            },\n            \"default\": {\n              \"description\": \"Service unavailable\",\n              \"content\": {\n                \"application/json\": {\n                  \"schema\": {\n                    \"$ref\": \"#/components/schemas/errorResponse\"\n                  }\n                }\n              },\n              \"headers\": {\n                \"apim-request-id\": {\n                  \"description\": \"Request ID for troubleshooting purposes\",\n                  \"schema\": {\n                    \"type\": \"string\"\n                  }\n                }\n              }\n            }\n          },\n          \"x-ms-examples\": {\n            \"Create a chat completion.\": {\n              \"$ref\": \"./examples/chat_completions.json\"\n            },\n            \"Creates a completion based on Azure Search data and system-assigned managed identity.\": {\n              \"$ref\": \"./examples/chat_completions_azure_search_minimum.json\"\n            },\n            \"Creates a completion based on Azure Search vector data, previous assistant message and user-assigned managed identity.\": {\n              \"$ref\": \"./examples/chat_completions_azure_search_advanced.json\"\n            },\n            \"Creates a completion for the provided Azure Cosmos DB.\": {\n              \"$ref\": \"./examples/chat_completions_cosmos_db.json\"\n            }\n          }\n        }\n      }\n    },\n    \"components\": {\n      \"schemas\": {\n        \"errorResponse\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"error\": {\n              \"$ref\": \"#/components/schemas/error\"\n            }\n          }\n        },\n        \"errorBase\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"code\": {\n              \"type\": \"string\"\n            },\n            \"message\": {\n              \"type\": \"string\"\n            }\n          }\n        },\n        \"error\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/errorBase\"\n            }\n          ],\n          \"properties\": {\n            \"param\": {\n              \"type\": \"string\"\n            },\n            \"type\": {\n              \"type\": \"string\"\n            },\n            \"inner_error\": {\n              \"$ref\": \"#/components/schemas/innerError\"\n            }\n          }\n        },\n        \"innerError\": {\n          \"description\": \"Inner error with additional details.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"code\": {\n              \"$ref\": \"#/components/schemas/innerErrorCode\"\n            },\n            \"content_filter_results\": {\n              \"$ref\": \"#/components/schemas/contentFilterPromptResults\"\n            }\n          }\n        },\n        \"innerErrorCode\": {\n          \"description\": \"Error codes for the inner error object.\",\n          \"enum\": [\n            \"ResponsibleAIPolicyViolation\"\n          ],\n          \"type\": \"string\",\n          \"x-ms-enum\": {\n            \"name\": \"InnerErrorCode\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"ResponsibleAIPolicyViolation\",\n                \"description\": \"The prompt violated one of more content filter rules.\"\n              }\n            ]\n          }\n        },\n        \"dalleErrorResponse\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"error\": {\n              \"$ref\": \"#/components/schemas/dalleError\"\n            }\n          }\n        },\n        \"dalleError\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/errorBase\"\n            }\n          ],\n          \"properties\": {\n            \"param\": {\n              \"type\": \"string\"\n            },\n            \"type\": {\n              \"type\": \"string\"\n            },\n            \"inner_error\": {\n              \"$ref\": \"#/components/schemas/dalleInnerError\"\n            }\n          }\n        },\n        \"dalleInnerError\": {\n          \"description\": \"Inner error with additional details.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"code\": {\n              \"$ref\": \"#/components/schemas/innerErrorCode\"\n            },\n            \"content_filter_results\": {\n              \"$ref\": \"#/components/schemas/dalleFilterResults\"\n            },\n            \"revised_prompt\": {\n              \"type\": \"string\",\n              \"description\": \"The prompt that was used to generate the image, if there was any revision to the prompt.\"\n            }\n          }\n        },\n        \"contentFilterResultBase\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"filtered\": {\n              \"type\": \"boolean\"\n            }\n          },\n          \"required\": [\n            \"filtered\"\n          ]\n        },\n        \"contentFilterSeverityResult\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/contentFilterResultBase\"\n            },\n            {\n              \"properties\": {\n                \"severity\": {\n                  \"type\": \"string\",\n                  \"enum\": [\n                    \"safe\",\n                    \"low\",\n                    \"medium\",\n                    \"high\"\n                  ],\n                  \"x-ms-enum\": {\n                    \"name\": \"ContentFilterSeverity\",\n                    \"modelAsString\": true,\n                    \"values\": [\n                      {\n                        \"value\": \"safe\",\n                        \"description\": \"General content or related content in generic or non-harmful contexts.\"\n                      },\n                      {\n                        \"value\": \"low\",\n                        \"description\": \"Harmful content at a low intensity and risk level.\"\n                      },\n                      {\n                        \"value\": \"medium\",\n                        \"description\": \"Harmful content at a medium intensity and risk level.\"\n                      },\n                      {\n                        \"value\": \"high\",\n                        \"description\": \"Harmful content at a high intensity and risk level.\"\n                      }\n                    ]\n                  }\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"severity\",\n            \"filtered\"\n          ]\n        },\n        \"contentFilterDetectedResult\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/contentFilterResultBase\"\n            },\n            {\n              \"properties\": {\n                \"detected\": {\n                  \"type\": \"boolean\"\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"detected\",\n            \"filtered\"\n          ]\n        },\n        \"contentFilterDetectedWithCitationResult\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/contentFilterDetectedResult\"\n            },\n            {\n              \"properties\": {\n                \"citation\": {\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"URL\": {\n                      \"type\": \"string\"\n                    },\n                    \"license\": {\n                      \"type\": \"string\"\n                    }\n                  }\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"detected\",\n            \"filtered\"\n          ]\n        },\n        \"contentFilterResultsBase\": {\n          \"type\": \"object\",\n          \"description\": \"Information about the content filtering results.\",\n          \"properties\": {\n            \"sexual\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"violence\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"hate\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"self_harm\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"profanity\": {\n              \"$ref\": \"#/components/schemas/contentFilterDetectedResult\"\n            },\n            \"error\": {\n              \"$ref\": \"#/components/schemas/errorBase\"\n            }\n          }\n        },\n        \"contentFilterPromptResults\": {\n          \"type\": \"object\",\n          \"description\": \"Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/contentFilterResultsBase\"\n            },\n            {\n              \"properties\": {\n                \"jailbreak\": {\n                  \"$ref\": \"#/components/schemas/contentFilterDetectedResult\"\n                }\n              }\n            }\n          ]\n        },\n        \"contentFilterChoiceResults\": {\n          \"type\": \"object\",\n          \"description\": \"Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about third party text and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/contentFilterResultsBase\"\n            },\n            {\n              \"properties\": {\n                \"protected_material_text\": {\n                  \"$ref\": \"#/components/schemas/contentFilterDetectedResult\"\n                }\n              }\n            },\n            {\n              \"properties\": {\n                \"protected_material_code\": {\n                  \"$ref\": \"#/components/schemas/contentFilterDetectedWithCitationResult\"\n                }\n              }\n            }\n          ]\n        },\n        \"promptFilterResult\": {\n          \"type\": \"object\",\n          \"description\": \"Content filtering results for a single prompt in the request.\",\n          \"properties\": {\n            \"prompt_index\": {\n              \"type\": \"integer\"\n            },\n            \"content_filter_results\": {\n              \"$ref\": \"#/components/schemas/contentFilterPromptResults\"\n            }\n          }\n        },\n        \"promptFilterResults\": {\n          \"type\": \"array\",\n          \"description\": \"Content filtering results for zero or more prompts in the request. In a streaming request, results for different prompts may arrive at different times or in different orders.\",\n          \"items\": {\n            \"$ref\": \"#/components/schemas/promptFilterResult\"\n          }\n        },\n        \"dalleContentFilterResults\": {\n          \"type\": \"object\",\n          \"description\": \"Information about the content filtering results.\",\n          \"properties\": {\n            \"sexual\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"violence\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"hate\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            },\n            \"self_harm\": {\n              \"$ref\": \"#/components/schemas/contentFilterSeverityResult\"\n            }\n          }\n        },\n        \"dalleFilterResults\": {\n          \"type\": \"object\",\n          \"description\": \"Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/dalleContentFilterResults\"\n            },\n            {\n              \"properties\": {\n                \"profanity\": {\n                  \"$ref\": \"#/components/schemas/contentFilterDetectedResult\"\n                },\n                \"jailbreak\": {\n                  \"$ref\": \"#/components/schemas/contentFilterDetectedResult\"\n                }\n              }\n            }\n          ]\n        },\n        \"chatCompletionsRequestCommon\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"temperature\": {\n              \"description\": \"What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\\nWe generally recommend altering this or `top_p` but not both.\",\n              \"type\": \"number\",\n              \"minimum\": 0,\n              \"maximum\": 2,\n              \"default\": 1,\n              \"example\": 1,\n              \"nullable\": true\n            },\n            \"top_p\": {\n              \"description\": \"An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\\nWe generally recommend altering this or `temperature` but not both.\",\n              \"type\": \"number\",\n              \"minimum\": 0,\n              \"maximum\": 1,\n              \"default\": 1,\n              \"example\": 1,\n              \"nullable\": true\n            },\n            \"stream\": {\n              \"description\": \"If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a `data: [DONE]` message.\",\n              \"type\": \"boolean\",\n              \"nullable\": true,\n              \"default\": false\n            },\n            \"stop\": {\n              \"description\": \"Up to 4 sequences where the API will stop generating further tokens.\",\n              \"oneOf\": [\n                {\n                  \"type\": \"string\",\n                  \"nullable\": true\n                },\n                {\n                  \"type\": \"array\",\n                  \"items\": {\n                    \"type\": \"string\",\n                    \"nullable\": false\n                  },\n                  \"minItems\": 1,\n                  \"maxItems\": 4,\n                  \"description\": \"Array minimum size of 1 and maximum of 4\"\n                }\n              ],\n              \"default\": null\n            },\n            \"max_tokens\": {\n              \"description\": \"The maximum number of tokens allowed for the generated answer. By default, the number of tokens the model can return will be (4096 - prompt tokens).\",\n              \"type\": \"integer\",\n              \"default\": 4096\n            },\n            \"presence_penalty\": {\n              \"description\": \"Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.\",\n              \"type\": \"number\",\n              \"default\": 0,\n              \"minimum\": -2,\n              \"maximum\": 2\n            },\n            \"frequency_penalty\": {\n              \"description\": \"Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.\",\n              \"type\": \"number\",\n              \"default\": 0,\n              \"minimum\": -2,\n              \"maximum\": 2\n            },\n            \"logit_bias\": {\n              \"description\": \"Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.\",\n              \"type\": \"object\",\n              \"nullable\": true\n            },\n            \"user\": {\n              \"description\": \"A unique identifier representing your end-user, which can help Azure OpenAI to monitor and detect abuse.\",\n              \"type\": \"string\",\n              \"example\": \"user-1234\",\n              \"nullable\": false\n            }\n          }\n        },\n        \"createChatCompletionRequest\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionsRequestCommon\"\n            },\n            {\n              \"properties\": {\n                \"messages\": {\n                  \"description\": \"A list of messages comprising the conversation so far. [Example Python code](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb).\",\n                  \"type\": \"array\",\n                  \"minItems\": 1,\n                  \"items\": {\n                    \"$ref\": \"#/components/schemas/chatCompletionRequestMessage\"\n                  }\n                },\n                \"data_sources\": {\n                  \"type\": \"array\",\n                  \"description\": \"  The configuration entries for Azure OpenAI chat extensions that use them.\\n  This additional specification is only compatible with Azure OpenAI.\",\n                  \"items\": {\n                    \"$ref\": \"#/components/schemas/azureChatExtensionConfiguration\"\n                  }\n                },\n                \"n\": {\n                  \"type\": \"integer\",\n                  \"minimum\": 1,\n                  \"maximum\": 128,\n                  \"default\": 1,\n                  \"example\": 1,\n                  \"nullable\": true,\n                  \"description\": \"How many chat completion choices to generate for each input message.\"\n                },\n                \"seed\": {\n                  \"type\": \"integer\",\n                  \"minimum\": -9223372036854775808,\n                  \"maximum\": 9223372036854775807,\n                  \"default\": 0,\n                  \"example\": 1,\n                  \"nullable\": true,\n                  \"description\": \"If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.\"\n                },\n                \"response_format\": {\n                  \"type\": \"object\",\n                  \"description\": \"An object specifying the format that the model must output. Used to enable JSON mode.\",\n                  \"properties\": {\n                    \"type\": {\n                      \"$ref\": \"#/components/schemas/chatCompletionResponseFormat\"\n                    }\n                  }\n                },\n                \"tools\": {\n                  \"description\": \"A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for.\",\n                  \"type\": \"array\",\n                  \"minItems\": 1,\n                  \"items\": {\n                    \"$ref\": \"#/components/schemas/chatCompletionTool\"\n                  }\n                },\n                \"tool_choice\": {\n                  \"$ref\": \"#/components/schemas/chatCompletionToolChoiceOption\"\n                },\n                \"functions\": {\n                  \"description\": \"Deprecated in favor of `tools`. A list of functions the model may generate JSON inputs for.\",\n                  \"type\": \"array\",\n                  \"minItems\": 1,\n                  \"maxItems\": 128,\n                  \"items\": {\n                    \"$ref\": \"#/components/schemas/chatCompletionFunction\"\n                  }\n                },\n                \"function_call\": {\n                  \"description\": \"Deprecated in favor of `tool_choice`. Controls how the model responds to function calls. \\\"none\\\" means the model does not call a function, and responds to the end-user. \\\"auto\\\" means the model can pick between an end-user or calling a function.  Specifying a particular function via `{\\\"name\\\":\\\\ \\\"my_function\\\"}` forces the model to call that function. \\\"none\\\" is the default when no functions are present. \\\"auto\\\" is the default if functions are present.\",\n                  \"oneOf\": [\n                    {\n                      \"type\": \"string\",\n                      \"enum\": [\n                        \"none\",\n                        \"auto\"\n                      ],\n                      \"description\": \"`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.\"\n                    },\n                    {\n                      \"type\": \"object\",\n                      \"description\": \"Specifying a particular function via `{\\\"name\\\": \\\"my_function\\\"}` forces the model to call that function.\",\n                      \"properties\": {\n                        \"name\": {\n                          \"type\": \"string\",\n                          \"description\": \"The name of the function to call.\"\n                        }\n                      },\n                      \"required\": [\n                        \"name\"\n                      ]\n                    }\n                  ]\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"messages\"\n          ]\n        },\n        \"chatCompletionResponseFormat\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"text\",\n            \"json_object\"\n          ],\n          \"default\": \"text\",\n          \"example\": \"json_object\",\n          \"nullable\": true,\n          \"description\": \"Setting to `json_object` enables JSON mode. This guarantees that the message the model generates is valid JSON.\",\n          \"x-ms-enum\": {\n            \"name\": \"ChatCompletionResponseFormat\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"text\",\n                \"description\": \"Response format is a plain text string.\"\n              },\n              {\n                \"value\": \"json_object\",\n                \"description\": \"Response format is a JSON object.\"\n              }\n            ]\n          }\n        },\n        \"chatCompletionFunction\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"name\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.\"\n            },\n            \"description\": {\n              \"type\": \"string\",\n              \"description\": \"The description of what the function does.\"\n            },\n            \"parameters\": {\n              \"$ref\": \"#/components/schemas/chatCompletionFunctionParameters\"\n            }\n          },\n          \"required\": [\n            \"name\"\n          ]\n        },\n        \"chatCompletionFunctionParameters\": {\n          \"type\": \"object\",\n          \"description\": \"The parameters the functions accepts, described as a JSON Schema object. See the [guide](/docs/guides/gpt/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.\",\n          \"additionalProperties\": true\n        },\n        \"chatCompletionRequestMessage\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"role\": {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessageRole\"\n            }\n          },\n          \"discriminator\": {\n            \"propertyName\": \"role\",\n            \"mapping\": {\n              \"system\": \"#/components/schemas/chatCompletionRequestMessageSystem\",\n              \"user\": \"#/components/schemas/chatCompletionRequestMessageUser\",\n              \"assistant\": \"#/components/schemas/chatCompletionRequestMessageAssistant\",\n              \"tool\": \"#/components/schemas/chatCompletionRequestMessageTool\",\n              \"function\": \"#/components/schemas/chatCompletionRequestMessageFunction\"\n            }\n          },\n          \"required\": [\n            \"role\"\n          ]\n        },\n        \"chatCompletionRequestMessageRole\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"system\",\n            \"user\",\n            \"assistant\",\n            \"tool\",\n            \"function\"\n          ],\n          \"description\": \"The role of the messages author.\",\n          \"x-ms-enum\": {\n            \"name\": \"ChatCompletionRequestMessageRole\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"system\",\n                \"description\": \"The message author role is system.\"\n              },\n              {\n                \"value\": \"user\",\n                \"description\": \"The message author role is user.\"\n              },\n              {\n                \"value\": \"assistant\",\n                \"description\": \"The message author role is assistant.\"\n              },\n              {\n                \"value\": \"tool\",\n                \"description\": \"The message author role is tool.\"\n              },\n              {\n                \"value\": \"function\",\n                \"description\": \"Deprecated. The message author role is function.\"\n              }\n            ]\n          }\n        },\n        \"chatCompletionRequestMessageSystem\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessage\"\n            },\n            {\n              \"type\": \"object\",\n              \"properties\": {\n                \"content\": {\n                  \"type\": \"string\",\n                  \"description\": \"The contents of the message.\",\n                  \"nullable\": true\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"content\"\n          ]\n        },\n        \"chatCompletionRequestMessageUser\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessage\"\n            },\n            {\n              \"type\": \"object\",\n              \"properties\": {\n                \"content\": {\n                  \"oneOf\": [\n                    {\n                      \"type\": \"string\",\n                      \"description\": \"The contents of the message.\"\n                    },\n                    {\n                      \"type\": \"array\",\n                      \"description\": \"An array of content parts with a defined type, each can be of type `text` or `image_url` when passing in images. You can pass multiple images by adding multiple `image_url` content parts. Image input is only supported when using the `gpt-4-visual-preview` model.\",\n                      \"minimum\": 1,\n                      \"items\": {\n                        \"$ref\": \"#/components/schemas/chatCompletionRequestMessageContentPart\"\n                      }\n                    }\n                  ],\n                  \"nullable\": true\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"content\"\n          ]\n        },\n        \"chatCompletionRequestMessageContentPart\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"type\": {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessageContentPartType\"\n            }\n          },\n          \"discriminator\": {\n            \"propertyName\": \"type\",\n            \"mapping\": {\n              \"text\": \"#/components/schemas/chatCompletionRequestMessageContentPartText\",\n              \"image_url\": \"#/components/schemas/chatCompletionRequestMessageContentPartImage\"\n            }\n          },\n          \"required\": [\n            \"type\"\n          ]\n        },\n        \"chatCompletionRequestMessageContentPartType\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"text\",\n            \"image_url\"\n          ],\n          \"description\": \"The type of the content part.\",\n          \"x-ms-enum\": {\n            \"name\": \"ChatCompletionRequestMessageContentPartType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"text\",\n                \"description\": \"The content part type is text.\"\n              },\n              {\n                \"value\": \"image_url\",\n                \"description\": \"The content part type is image_url.\"\n              }\n            ]\n          }\n        },\n        \"chatCompletionRequestMessageContentPartText\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessageContentPart\"\n            },\n            {\n              \"type\": \"object\",\n              \"properties\": {\n                \"text\": {\n                  \"type\": \"string\",\n                  \"description\": \"The text content.\"\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"text\"\n          ]\n        },\n        \"chatCompletionRequestMessageContentPartImage\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessageContentPart\"\n            },\n            {\n              \"type\": \"object\",\n              \"properties\": {\n                \"url\": {\n                  \"type\": \"string\",\n                  \"description\": \"Either a URL of the image or the base64 encoded image data.\",\n                  \"format\": \"uri\"\n                },\n                \"detail\": {\n                  \"$ref\": \"#/components/schemas/imageDetailLevel\"\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"url\"\n          ]\n        },\n        \"imageDetailLevel\": {\n          \"type\": \"string\",\n          \"description\": \"Specifies the detail level of the image.\",\n          \"enum\": [\n            \"auto\",\n            \"low\",\n            \"high\"\n          ],\n          \"default\": \"auto\",\n          \"x-ms-enum\": {\n            \"name\": \"ImageDetailLevel\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"auto\",\n                \"description\": \"The image detail level is auto.\"\n              },\n              {\n                \"value\": \"low\",\n                \"description\": \"The image detail level is low.\"\n              },\n              {\n                \"value\": \"high\",\n                \"description\": \"The image detail level is high.\"\n              }\n            ]\n          }\n        },\n        \"chatCompletionRequestMessageAssistant\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessage\"\n            },\n            {\n              \"type\": \"object\",\n              \"properties\": {\n                \"content\": {\n                  \"type\": \"string\",\n                  \"description\": \"The contents of the message.\",\n                  \"nullable\": true\n                },\n                \"tool_calls\": {\n                  \"type\": \"array\",\n                  \"description\": \"The tool calls generated by the model, such as function calls.\",\n                  \"items\": {\n                    \"$ref\": \"#/components/schemas/chatCompletionMessageToolCall\"\n                  }\n                },\n                \"context\": {\n                  \"$ref\": \"#/components/schemas/azureChatExtensionsMessageContext\"\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"content\"\n          ]\n        },\n        \"azureChatExtensionConfiguration\": {\n          \"required\": [\n            \"type\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"type\": {\n              \"$ref\": \"#/components/schemas/azureChatExtensionType\"\n            }\n          },\n          \"description\": \"  A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\\n  completions request that should use Azure OpenAI chat extensions to augment the response behavior.\\n  The use of this configuration is compatible only with Azure OpenAI.\",\n          \"discriminator\": {\n            \"propertyName\": \"type\",\n            \"mapping\": {\n              \"azure_search\": \"#/components/schemas/azureSearchChatExtensionConfiguration\",\n              \"azure_cosmos_db\": \"#/components/schemas/azureCosmosDBChatExtensionConfiguration\"\n            }\n          }\n        },\n        \"azureChatExtensionType\": {\n          \"type\": \"string\",\n          \"description\": \"  A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\\n  completions request that should use Azure OpenAI chat extensions to augment the response behavior.\\n  The use of this configuration is compatible only with Azure OpenAI.\",\n          \"enum\": [\n            \"azure_search\",\n            \"azure_cosmos_db\"\n          ],\n          \"x-ms-enum\": {\n            \"name\": \"AzureChatExtensionType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"name\": \"azureSearch\",\n                \"value\": \"azure_search\",\n                \"description\": \"Represents the use of Azure Search as an Azure OpenAI chat extension.\"\n              },\n              {\n                \"name\": \"azureCosmosDB\",\n                \"value\": \"azure_cosmos_db\",\n                \"description\": \"Represents the use of Azure Cosmos DB as an Azure OpenAI chat extension.\"\n              }\n            ]\n          }\n        },\n        \"azureSearchChatExtensionConfiguration\": {\n          \"required\": [\n            \"parameters\"\n          ],\n          \"description\": \"A specific representation of configurable options for Azure Search when using it as an Azure OpenAI chat\\nextension.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/azureChatExtensionConfiguration\"\n            },\n            {\n              \"properties\": {\n                \"parameters\": {\n                  \"$ref\": \"#/components/schemas/azureSearchChatExtensionParameters\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"azure_search\"\n        },\n        \"azureSearchChatExtensionParameters\": {\n          \"required\": [\n            \"authentication\",\n            \"endpoint\",\n            \"index_name\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"authentication\": {\n              \"oneOf\": [\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataApiKeyAuthenticationOptions\"\n                },\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions\"\n                },\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions\"\n                }\n              ]\n            },\n            \"top_n_documents\": {\n              \"type\": \"integer\",\n              \"description\": \"The configured top number of documents to feature for the configured query.\",\n              \"format\": \"int32\"\n            },\n            \"in_scope\": {\n              \"type\": \"boolean\",\n              \"description\": \"Whether queries should be restricted to use of indexed data.\"\n            },\n            \"strictness\": {\n              \"maximum\": 5,\n              \"minimum\": 1,\n              \"type\": \"integer\",\n              \"description\": \"The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.\",\n              \"format\": \"int32\"\n            },\n            \"role_information\": {\n              \"type\": \"string\",\n              \"description\": \"Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant's personality and tell it how to format responses. There's a 100 token limit for it, and it counts against the overall token limit.\"\n            },\n            \"endpoint\": {\n              \"type\": \"string\",\n              \"description\": \"The absolute endpoint path for the Azure Search resource to use.\",\n              \"format\": \"uri\"\n            },\n            \"index_name\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index to use as available in the referenced Azure Search resource.\"\n            },\n            \"fields_mapping\": {\n              \"$ref\": \"#/components/schemas/azureSearchIndexFieldMappingOptions\"\n            },\n            \"query_type\": {\n              \"$ref\": \"#/components/schemas/azureSearchQueryType\"\n            },\n            \"semantic_configuration\": {\n              \"type\": \"string\",\n              \"description\": \"The additional semantic configuration for the query.\"\n            },\n            \"filter\": {\n              \"type\": \"string\",\n              \"description\": \"Search filter.\"\n            },\n            \"embedding_dependency\": {\n              \"oneOf\": [\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataEndpointVectorizationSource\"\n                },\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataDeploymentNameVectorizationSource\"\n                }\n              ]\n            }\n          },\n          \"description\": \"Parameters for Azure Search when used as an Azure OpenAI chat extension.\"\n        },\n        \"azureSearchIndexFieldMappingOptions\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"title_field\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index field to use as a title.\"\n            },\n            \"url_field\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index field to use as a URL.\"\n            },\n            \"filepath_field\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index field to use as a filepath.\"\n            },\n            \"content_fields\": {\n              \"type\": \"array\",\n              \"description\": \"The names of index fields that should be treated as content.\",\n              \"items\": {\n                \"type\": \"string\"\n              }\n            },\n            \"content_fields_separator\": {\n              \"type\": \"string\",\n              \"description\": \"The separator pattern that content fields should use.\"\n            },\n            \"vector_fields\": {\n              \"type\": \"array\",\n              \"description\": \"The names of fields that represent vector data.\",\n              \"items\": {\n                \"type\": \"string\"\n              }\n            }\n          },\n          \"description\": \"Optional settings to control how fields are processed when using a configured Azure Search resource.\"\n        },\n        \"azureSearchQueryType\": {\n          \"type\": \"string\",\n          \"description\": \"The type of Azure Search retrieval query that should be executed when using it as an Azure OpenAI chat extension.\",\n          \"enum\": [\n            \"simple\",\n            \"semantic\",\n            \"vector\",\n            \"vector_simple_hybrid\",\n            \"vector_semantic_hybrid\"\n          ],\n          \"x-ms-enum\": {\n            \"name\": \"azureSearchQueryType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"name\": \"simple\",\n                \"value\": \"simple\",\n                \"description\": \"Represents the default, simple query parser.\"\n              },\n              {\n                \"name\": \"semantic\",\n                \"value\": \"semantic\",\n                \"description\": \"Represents the semantic query parser for advanced semantic modeling.\"\n              },\n              {\n                \"name\": \"vector\",\n                \"value\": \"vector\",\n                \"description\": \"Represents vector search over computed data.\"\n              },\n              {\n                \"name\": \"vectorSimpleHybrid\",\n                \"value\": \"vector_simple_hybrid\",\n                \"description\": \"Represents a combination of the simple query strategy with vector data.\"\n              },\n              {\n                \"name\": \"vectorSemanticHybrid\",\n                \"value\": \"vector_semantic_hybrid\",\n                \"description\": \"Represents a combination of semantic search and vector data querying.\"\n              }\n            ]\n          }\n        },\n        \"azureCosmosDBChatExtensionConfiguration\": {\n          \"required\": [\n            \"parameters\"\n          ],\n          \"description\": \"A specific representation of configurable options for Azure Cosmos DB when using it as an Azure OpenAI chat\\nextension.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/azureChatExtensionConfiguration\"\n            },\n            {\n              \"properties\": {\n                \"parameters\": {\n                  \"$ref\": \"#/components/schemas/azureCosmosDBChatExtensionParameters\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"azure_cosmos_db\"\n        },\n        \"azureCosmosDBChatExtensionParameters\": {\n          \"required\": [\n            \"authentication\",\n            \"container_name\",\n            \"database_name\",\n            \"embedding_dependency\",\n            \"fields_mapping\",\n            \"index_name\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"authentication\": {\n              \"$ref\": \"#/components/schemas/onYourDataConnectionStringAuthenticationOptions\"\n            },\n            \"top_n_documents\": {\n              \"type\": \"integer\",\n              \"description\": \"The configured top number of documents to feature for the configured query.\",\n              \"format\": \"int32\"\n            },\n            \"in_scope\": {\n              \"type\": \"boolean\",\n              \"description\": \"Whether queries should be restricted to use of indexed data.\"\n            },\n            \"strictness\": {\n              \"maximum\": 5,\n              \"minimum\": 1,\n              \"type\": \"integer\",\n              \"description\": \"The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.\",\n              \"format\": \"int32\"\n            },\n            \"role_information\": {\n              \"type\": \"string\",\n              \"description\": \"Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant's personality and tell it how to format responses. There's a 100 token limit for it, and it counts against the overall token limit.\"\n            },\n            \"database_name\": {\n              \"type\": \"string\",\n              \"description\": \"The MongoDB vCore database name to use with Azure Cosmos DB.\"\n            },\n            \"container_name\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the Azure Cosmos DB resource container.\"\n            },\n            \"index_name\": {\n              \"type\": \"string\",\n              \"description\": \"The MongoDB vCore index name to use with Azure Cosmos DB.\"\n            },\n            \"fields_mapping\": {\n              \"$ref\": \"#/components/schemas/azureCosmosDBFieldMappingOptions\"\n            },\n            \"embedding_dependency\": {\n              \"oneOf\": [\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataEndpointVectorizationSource\"\n                },\n                {\n                  \"$ref\": \"#/components/schemas/onYourDataDeploymentNameVectorizationSource\"\n                }\n              ]\n            }\n          },\n          \"description\": \"Parameters to use when configuring Azure OpenAI On Your Data chat extensions when using Azure Cosmos DB for\\nMongoDB vCore.\"\n        },\n        \"azureCosmosDBFieldMappingOptions\": {\n          \"required\": [\n            \"content_fields\",\n            \"vector_fields\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"title_field\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index field to use as a title.\"\n            },\n            \"url_field\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index field to use as a URL.\"\n            },\n            \"filepath_field\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the index field to use as a filepath.\"\n            },\n            \"content_fields\": {\n              \"type\": \"array\",\n              \"description\": \"The names of index fields that should be treated as content.\",\n              \"items\": {\n                \"type\": \"string\"\n              }\n            },\n            \"content_fields_separator\": {\n              \"type\": \"string\",\n              \"description\": \"The separator pattern that content fields should use.\"\n            },\n            \"vector_fields\": {\n              \"type\": \"array\",\n              \"description\": \"The names of fields that represent vector data.\",\n              \"items\": {\n                \"type\": \"string\"\n              }\n            }\n          },\n          \"description\": \"Optional settings to control how fields are processed when using a configured Azure Cosmos DB resource.\"\n        },\n        \"onYourDataAuthenticationOptions\": {\n          \"required\": [\n            \"type\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"type\": {\n              \"$ref\": \"#/components/schemas/onYourDataAuthenticationType\"\n            }\n          },\n          \"description\": \"The authentication options for Azure OpenAI On Your Data.\",\n          \"discriminator\": {\n            \"propertyName\": \"type\",\n            \"mapping\": {\n              \"api_key\": \"#/components/schemas/onYourDataApiKeyAuthenticationOptions\",\n              \"connection_string\": \"#/components/schemas/onYourDataConnectionStringAuthenticationOptions\",\n              \"system_assigned_managed_identity\": \"#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions\",\n              \"user_assigned_managed_identity\": \"#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions\"\n            }\n          }\n        },\n        \"onYourDataAuthenticationType\": {\n          \"type\": \"string\",\n          \"description\": \"The authentication types supported with Azure OpenAI On Your Data.\",\n          \"enum\": [\n            \"api_key\",\n            \"connection_string\",\n            \"system_assigned_managed_identity\",\n            \"user_assigned_managed_identity\"\n          ],\n          \"x-ms-enum\": {\n            \"name\": \"OnYourDataAuthenticationType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"name\": \"apiKey\",\n                \"value\": \"api_key\",\n                \"description\": \"Authentication via API key.\"\n              },\n              {\n                \"name\": \"connectionString\",\n                \"value\": \"connection_string\",\n                \"description\": \"Authentication via connection string.\"\n              },\n              {\n                \"name\": \"systemAssignedManagedIdentity\",\n                \"value\": \"system_assigned_managed_identity\",\n                \"description\": \"Authentication via system-assigned managed identity.\"\n              },\n              {\n                \"name\": \"userAssignedManagedIdentity\",\n                \"value\": \"user_assigned_managed_identity\",\n                \"description\": \"Authentication via user-assigned managed identity.\"\n              }\n            ]\n          }\n        },\n        \"onYourDataApiKeyAuthenticationOptions\": {\n          \"required\": [\n            \"key\"\n          ],\n          \"description\": \"The authentication options for Azure OpenAI On Your Data when using an API key.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/onYourDataAuthenticationOptions\"\n            },\n            {\n              \"properties\": {\n                \"key\": {\n                  \"type\": \"string\",\n                  \"description\": \"The API key to use for authentication.\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"api_key\"\n        },\n        \"onYourDataConnectionStringAuthenticationOptions\": {\n          \"required\": [\n            \"connection_string\"\n          ],\n          \"description\": \"The authentication options for Azure OpenAI On Your Data when using a connection string.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/onYourDataAuthenticationOptions\"\n            },\n            {\n              \"properties\": {\n                \"connection_string\": {\n                  \"type\": \"string\",\n                  \"description\": \"The connection string to use for authentication.\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"connection_string\"\n        },\n        \"onYourDataSystemAssignedManagedIdentityAuthenticationOptions\": {\n          \"description\": \"The authentication options for Azure OpenAI On Your Data when using a system-assigned managed identity.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/onYourDataAuthenticationOptions\"\n            }\n          ],\n          \"x-ms-discriminator-value\": \"system_assigned_managed_identity\"\n        },\n        \"onYourDataUserAssignedManagedIdentityAuthenticationOptions\": {\n          \"required\": [\n            \"managed_identity_resource_id\"\n          ],\n          \"description\": \"The authentication options for Azure OpenAI On Your Data when using a user-assigned managed identity.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/onYourDataAuthenticationOptions\"\n            },\n            {\n              \"properties\": {\n                \"managed_identity_resource_id\": {\n                  \"type\": \"string\",\n                  \"description\": \"The resource ID of the user-assigned managed identity to use for authentication.\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"user_assigned_managed_identity\"\n        },\n        \"onYourDataVectorizationSource\": {\n          \"required\": [\n            \"type\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"type\": {\n              \"$ref\": \"#/components/schemas/onYourDataVectorizationSourceType\"\n            }\n          },\n          \"description\": \"An abstract representation of a vectorization source for Azure OpenAI On Your Data with vector search.\",\n          \"discriminator\": {\n            \"propertyName\": \"type\",\n            \"mapping\": {\n              \"endpoint\": \"#/components/schemas/onYourDataEndpointVectorizationSource\",\n              \"deployment_name\": \"#/components/schemas/onYourDataDeploymentNameVectorizationSource\"\n            }\n          }\n        },\n        \"onYourDataVectorizationSourceType\": {\n          \"type\": \"string\",\n          \"description\": \"Represents the available sources Azure OpenAI On Your Data can use to configure vectorization of data for use with\\nvector search.\",\n          \"enum\": [\n            \"endpoint\",\n            \"deployment_name\"\n          ],\n          \"x-ms-enum\": {\n            \"name\": \"OnYourDataVectorizationSourceType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"name\": \"endpoint\",\n                \"value\": \"endpoint\",\n                \"description\": \"Represents vectorization performed by public service calls to an Azure OpenAI embedding model.\"\n              },\n              {\n                \"name\": \"deploymentName\",\n                \"value\": \"deployment_name\",\n                \"description\": \"Represents an Ada model deployment name to use. This model deployment must be in the same Azure OpenAI resource, but\\nOn Your Data will use this model deployment via an internal call rather than a public one, which enables vector\\nsearch even in private networks.\"\n              }\n            ]\n          }\n        },\n        \"onYourDataDeploymentNameVectorizationSource\": {\n          \"required\": [\n            \"deployment_name\"\n          ],\n          \"description\": \"The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\\non an internal embeddings model deployment name in the same Azure OpenAI resource.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/onYourDataVectorizationSource\"\n            },\n            {\n              \"properties\": {\n                \"deployment_name\": {\n                  \"type\": \"string\",\n                  \"description\": \"Specifies the name of the model deployment to use for vectorization. This model deployment must be in the same Azure OpenAI resource, but On Your Data will use this model deployment via an internal call rather than a public one, which enables vector search even in private networks.\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"deployment_name\"\n        },\n        \"onYourDataEndpointVectorizationSource\": {\n          \"required\": [\n            \"authentication\",\n            \"endpoint\"\n          ],\n          \"description\": \"The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\\non a public Azure OpenAI endpoint call for embeddings.\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/onYourDataVectorizationSource\"\n            },\n            {\n              \"properties\": {\n                \"authentication\": {\n                  \"$ref\": \"#/components/schemas/onYourDataApiKeyAuthenticationOptions\"\n                },\n                \"endpoint\": {\n                  \"type\": \"string\",\n                  \"description\": \"Specifies the endpoint to use for vectorization. This endpoint must be in the same Azure OpenAI resource, but On Your Data will use this endpoint via an internal call rather than a public one, which enables vector search even in private networks.\",\n                  \"format\": \"uri\"\n                }\n              }\n            }\n          ],\n          \"x-ms-discriminator-value\": \"endpoint\"\n        },\n        \"azureChatExtensionsMessageContext\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"citations\": {\n              \"type\": \"array\",\n              \"description\": \"The data source retrieval result, used to generate the assistant message in the response.\",\n              \"items\": {\n                \"$ref\": \"#/components/schemas/citation\"\n              },\n              \"x-ms-identifiers\": []\n            },\n            \"intent\": {\n              \"type\": \"string\",\n              \"description\": \"The detected intent from the chat history, used to pass to the next turn to carry over the context.\"\n            }\n          },\n          \"description\": \"  A representation of the additional context information available when Azure OpenAI chat extensions are involved\\n  in the generation of a corresponding chat completions response. This context information is only populated when\\n  using an Azure OpenAI request configured to use a matching extension.\"\n        },\n        \"citation\": {\n          \"required\": [\n            \"content\"\n          ],\n          \"type\": \"object\",\n          \"properties\": {\n            \"content\": {\n              \"type\": \"string\",\n              \"description\": \"The content of the citation.\"\n            },\n            \"title\": {\n              \"type\": \"string\",\n              \"description\": \"The title of the citation.\"\n            },\n            \"url\": {\n              \"type\": \"string\",\n              \"description\": \"The URL of the citation.\"\n            },\n            \"filepath\": {\n              \"type\": \"string\",\n              \"description\": \"The file path of the citation.\"\n            },\n            \"chunk_id\": {\n              \"type\": \"string\",\n              \"description\": \"The chunk ID of the citation.\"\n            }\n          },\n          \"description\": \"citation information for a chat completions response message.\"\n        },\n        \"chatCompletionMessageToolCall\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"id\": {\n              \"type\": \"string\",\n              \"description\": \"The ID of the tool call.\"\n            },\n            \"type\": {\n              \"$ref\": \"#/components/schemas/toolCallType\"\n            },\n            \"function\": {\n              \"type\": \"object\",\n              \"description\": \"The function that the model called.\",\n              \"properties\": {\n                \"name\": {\n                  \"type\": \"string\",\n                  \"description\": \"The name of the function to call.\"\n                },\n                \"arguments\": {\n                  \"type\": \"string\",\n                  \"description\": \"The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.\"\n                }\n              },\n              \"required\": [\n                \"name\",\n                \"arguments\"\n              ]\n            }\n          },\n          \"required\": [\n            \"id\",\n            \"type\",\n            \"function\"\n          ]\n        },\n        \"toolCallType\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"function\"\n          ],\n          \"description\": \"The type of the tool call, in this case `function`.\",\n          \"x-ms-enum\": {\n            \"name\": \"ToolCallType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"function\",\n                \"description\": \"The tool call type is function.\"\n              }\n            ]\n          }\n        },\n        \"chatCompletionRequestMessageTool\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessage\"\n            },\n            {\n              \"type\": \"object\",\n              \"nullable\": true,\n              \"properties\": {\n                \"tool_call_id\": {\n                  \"type\": \"string\",\n                  \"description\": \"Tool call that this message is responding to.\"\n                },\n                \"content\": {\n                  \"type\": \"string\",\n                  \"description\": \"The contents of the message.\",\n                  \"nullable\": true\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"tool_call_id\",\n            \"content\"\n          ]\n        },\n        \"chatCompletionRequestMessageFunction\": {\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionRequestMessage\"\n            },\n            {\n              \"type\": \"object\",\n              \"description\": \"Deprecated. Message that represents a function.\",\n              \"nullable\": true,\n              \"properties\": {\n                \"role\": {\n                  \"type\": \"string\",\n                  \"enum\": [\n                    \"function\"\n                  ],\n                  \"description\": \"The role of the messages author, in this case `function`.\"\n                },\n                \"name\": {\n                  \"type\": \"string\",\n                  \"description\": \"The contents of the message.\"\n                },\n                \"content\": {\n                  \"type\": \"string\",\n                  \"description\": \"The contents of the message.\",\n                  \"nullable\": true\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"function_call_id\",\n            \"content\"\n          ]\n        },\n        \"createChatCompletionResponse\": {\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionsResponseCommon\"\n            },\n            {\n              \"properties\": {\n                \"prompt_filter_results\": {\n                  \"$ref\": \"#/components/schemas/promptFilterResults\"\n                },\n                \"choices\": {\n                  \"type\": \"array\",\n                  \"items\": {\n                    \"type\": \"object\",\n                    \"allOf\": [\n                      {\n                        \"$ref\": \"#/components/schemas/chatCompletionChoiceCommon\"\n                      },\n                      {\n                        \"properties\": {\n                          \"message\": {\n                            \"$ref\": \"#/components/schemas/chatCompletionResponseMessage\"\n                          },\n                          \"content_filter_results\": {\n                            \"$ref\": \"#/components/schemas/contentFilterChoiceResults\"\n                          }\n                        }\n                      }\n                    ]\n                  }\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"id\",\n            \"object\",\n            \"created\",\n            \"model\",\n            \"choices\"\n          ]\n        },\n        \"chatCompletionResponseMessage\": {\n          \"type\": \"object\",\n          \"description\": \"A chat completion message generated by the model.\",\n          \"properties\": {\n            \"role\": {\n              \"$ref\": \"#/components/schemas/chatCompletionResponseMessageRole\"\n            },\n            \"content\": {\n              \"type\": \"string\",\n              \"description\": \"The contents of the message.\",\n              \"nullable\": true\n            },\n            \"tool_calls\": {\n              \"type\": \"array\",\n              \"description\": \"The tool calls generated by the model, such as function calls.\",\n              \"items\": {\n                \"$ref\": \"#/components/schemas/chatCompletionMessageToolCall\"\n              }\n            },\n            \"function_call\": {\n              \"$ref\": \"#/components/schemas/chatCompletionFunctionCall\"\n            },\n            \"context\": {\n              \"$ref\": \"#/components/schemas/azureChatExtensionsMessageContext\"\n            }\n          }\n        },\n        \"chatCompletionResponseMessageRole\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"assistant\"\n          ],\n          \"description\": \"The role of the author of the response message.\"\n        },\n        \"chatCompletionToolChoiceOption\": {\n          \"description\": \"Controls which (if any) function is called by the model. `none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function. Specifying a particular function via `{\\\"type\\\": \\\"function\\\", \\\"function\\\": {\\\"name\\\": \\\"my_function\\\"}}` forces the model to call that function.\",\n          \"oneOf\": [\n            {\n              \"type\": \"string\",\n              \"description\": \"`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.\",\n              \"enum\": [\n                \"none\",\n                \"auto\"\n              ]\n            },\n            {\n              \"$ref\": \"#/components/schemas/chatCompletionNamedToolChoice\"\n            }\n          ]\n        },\n        \"chatCompletionNamedToolChoice\": {\n          \"type\": \"object\",\n          \"description\": \"Specifies a tool the model should use. Use to force the model to call a specific function.\",\n          \"properties\": {\n            \"type\": {\n              \"type\": \"string\",\n              \"enum\": [\n                \"function\"\n              ],\n              \"description\": \"The type of the tool. Currently, only `function` is supported.\"\n            },\n            \"function\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"name\": {\n                  \"type\": \"string\",\n                  \"description\": \"The name of the function to call.\"\n                }\n              },\n              \"required\": [\n                \"name\"\n              ]\n            }\n          }\n        },\n        \"chatCompletionFunctionCall\": {\n          \"type\": \"object\",\n          \"description\": \"Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.\",\n          \"properties\": {\n            \"name\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the function to call.\"\n            },\n            \"arguments\": {\n              \"type\": \"string\",\n              \"description\": \"The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.\"\n            }\n          },\n          \"required\": [\n            \"name\",\n            \"arguments\"\n          ]\n        },\n        \"chatCompletionsResponseCommon\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"id\": {\n              \"type\": \"string\",\n              \"description\": \"A unique identifier for the chat completion.\"\n            },\n            \"object\": {\n              \"$ref\": \"#/components/schemas/chatCompletionResponseObject\"\n            },\n            \"created\": {\n              \"type\": \"integer\",\n              \"format\": \"unixtime\",\n              \"description\": \"The Unix timestamp (in seconds) of when the chat completion was created.\"\n            },\n            \"model\": {\n              \"type\": \"string\",\n              \"description\": \"The model used for the chat completion.\"\n            },\n            \"usage\": {\n              \"$ref\": \"#/components/schemas/completionUsage\"\n            },\n            \"system_fingerprint\": {\n              \"type\": \"string\",\n              \"description\": \"Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\"\n            }\n          },\n          \"required\": [\n            \"id\",\n            \"object\",\n            \"created\",\n            \"model\"\n          ]\n        },\n        \"chatCompletionResponseObject\": {\n          \"type\": \"string\",\n          \"description\": \"The object type.\",\n          \"enum\": [\n            \"chat.completion\"\n          ],\n          \"x-ms-enum\": {\n            \"name\": \"ChatCompletionResponseObject\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"chat.completion\",\n                \"description\": \"The object type is chat completion.\"\n              }\n            ]\n          }\n        },\n        \"completionUsage\": {\n          \"type\": \"object\",\n          \"description\": \"Usage statistics for the completion request.\",\n          \"properties\": {\n            \"prompt_tokens\": {\n              \"type\": \"integer\",\n              \"description\": \"Number of tokens in the prompt.\"\n            },\n            \"completion_tokens\": {\n              \"type\": \"integer\",\n              \"description\": \"Number of tokens in the generated completion.\"\n            },\n            \"total_tokens\": {\n              \"type\": \"integer\",\n              \"description\": \"Total number of tokens used in the request (prompt + completion).\"\n            }\n          },\n          \"required\": [\n            \"prompt_tokens\",\n            \"completion_tokens\",\n            \"total_tokens\"\n          ]\n        },\n        \"chatCompletionTool\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"type\": {\n              \"$ref\": \"#/components/schemas/chatCompletionToolType\"\n            },\n            \"function\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"description\": {\n                  \"type\": \"string\",\n                  \"description\": \"A description of what the function does, used by the model to choose when and how to call the function.\"\n                },\n                \"name\": {\n                  \"type\": \"string\",\n                  \"description\": \"The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.\"\n                },\n                \"parameters\": {\n                  \"$ref\": \"#/components/schemas/chatCompletionFunctionParameters\"\n                }\n              },\n              \"required\": [\n                \"name\",\n                \"parameters\"\n              ]\n            }\n          },\n          \"required\": [\n            \"type\",\n            \"function\"\n          ]\n        },\n        \"chatCompletionToolType\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"function\"\n          ],\n          \"description\": \"The type of the tool. Currently, only `function` is supported.\",\n          \"x-ms-enum\": {\n            \"name\": \"ChatCompletionToolType\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"function\",\n                \"description\": \"The tool type is function.\"\n              }\n            ]\n          }\n        },\n        \"chatCompletionChoiceCommon\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"index\": {\n              \"type\": \"integer\"\n            },\n            \"finish_reason\": {\n              \"type\": \"string\"\n            }\n          }\n        },\n        \"createTranslationRequest\": {\n          \"type\": \"object\",\n          \"description\": \"Translation request.\",\n          \"properties\": {\n            \"file\": {\n              \"type\": \"string\",\n              \"description\": \"The audio file to translate.\",\n              \"format\": \"binary\"\n            },\n            \"prompt\": {\n              \"type\": \"string\",\n              \"description\": \"An optional text to guide the model's style or continue a previous audio segment. The prompt should be in English.\"\n            },\n            \"response_format\": {\n              \"$ref\": \"#/components/schemas/audioResponseFormat\"\n            },\n            \"temperature\": {\n              \"type\": \"number\",\n              \"default\": 0,\n              \"description\": \"The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\"\n            }\n          },\n          \"required\": [\n            \"file\"\n          ]\n        },\n        \"audioResponse\": {\n          \"description\": \"Translation or transcription response when response_format was json\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"text\": {\n              \"type\": \"string\",\n              \"description\": \"Translated or transcribed text.\"\n            }\n          },\n          \"required\": [\n            \"text\"\n          ]\n        },\n        \"audioVerboseResponse\": {\n          \"description\": \"Translation or transcription response when response_format was verbose_json\",\n          \"type\": \"object\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/components/schemas/audioResponse\"\n            },\n            {\n              \"properties\": {\n                \"task\": {\n                  \"type\": \"string\",\n                  \"description\": \"Type of audio task.\",\n                  \"enum\": [\n                    \"transcribe\",\n                    \"translate\"\n                  ],\n                  \"x-ms-enum\": {\n                    \"modelAsString\": true\n                  }\n                },\n                \"language\": {\n                  \"type\": \"string\",\n                  \"description\": \"Language.\"\n                },\n                \"duration\": {\n                  \"type\": \"number\",\n                  \"description\": \"Duration.\"\n                },\n                \"segments\": {\n                  \"type\": \"array\",\n                  \"items\": {\n                    \"$ref\": \"#/components/schemas/audioSegment\"\n                  }\n                }\n              }\n            }\n          ],\n          \"required\": [\n            \"text\"\n          ]\n        },\n        \"audioResponseFormat\": {\n          \"title\": \"AudioResponseFormat\",\n          \"description\": \"Defines the format of the output.\",\n          \"enum\": [\n            \"json\",\n            \"text\",\n            \"srt\",\n            \"verbose_json\",\n            \"vtt\"\n          ],\n          \"type\": \"string\",\n          \"x-ms-enum\": {\n            \"modelAsString\": true\n          }\n        },\n        \"createTranscriptionRequest\": {\n          \"type\": \"object\",\n          \"description\": \"Transcription request.\",\n          \"properties\": {\n            \"file\": {\n              \"type\": \"string\",\n              \"description\": \"The audio file object to transcribe.\",\n              \"format\": \"binary\"\n            },\n            \"prompt\": {\n              \"type\": \"string\",\n              \"description\": \"An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.\"\n            },\n            \"response_format\": {\n              \"$ref\": \"#/components/schemas/audioResponseFormat\"\n            },\n            \"temperature\": {\n              \"type\": \"number\",\n              \"default\": 0,\n              \"description\": \"The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\"\n            },\n            \"language\": {\n              \"type\": \"string\",\n              \"description\": \"The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency.\"\n            }\n          },\n          \"required\": [\n            \"file\"\n          ]\n        },\n        \"audioSegment\": {\n          \"type\": \"object\",\n          \"description\": \"Transcription or translation segment.\",\n          \"properties\": {\n            \"id\": {\n              \"type\": \"integer\",\n              \"description\": \"Segment identifier.\"\n            },\n            \"seek\": {\n              \"type\": \"number\",\n              \"description\": \"Offset of the segment.\"\n            },\n            \"start\": {\n              \"type\": \"number\",\n              \"description\": \"Segment start offset.\"\n            },\n            \"end\": {\n              \"type\": \"number\",\n              \"description\": \"Segment end offset.\"\n            },\n            \"text\": {\n              \"type\": \"string\",\n              \"description\": \"Segment text.\"\n            },\n            \"tokens\": {\n              \"type\": \"array\",\n              \"items\": {\n                \"type\": \"number\",\n                \"nullable\": false\n              },\n              \"description\": \"Tokens of the text.\"\n            },\n            \"temperature\": {\n              \"type\": \"number\",\n              \"description\": \"Temperature.\"\n            },\n            \"avg_logprob\": {\n              \"type\": \"number\",\n              \"description\": \"Average log probability.\"\n            },\n            \"compression_ratio\": {\n              \"type\": \"number\",\n              \"description\": \"Compression ratio.\"\n            },\n            \"no_speech_prob\": {\n              \"type\": \"number\",\n              \"description\": \"Probability of 'no speech'.\"\n            }\n          }\n        },\n        \"imageQuality\": {\n          \"description\": \"The quality of the image that will be generated.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"standard\",\n            \"hd\"\n          ],\n          \"default\": \"standard\",\n          \"x-ms-enum\": {\n            \"name\": \"Quality\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"standard\",\n                \"description\": \"Standard quality creates images with standard quality.\",\n                \"name\": \"Standard\"\n              },\n              {\n                \"value\": \"hd\",\n                \"description\": \"HD quality creates images with finer details and greater consistency across the image.\",\n                \"name\": \"HD\"\n              }\n            ]\n          }\n        },\n        \"imagesResponseFormat\": {\n          \"description\": \"The format in which the generated images are returned.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"url\",\n            \"b64_json\"\n          ],\n          \"default\": \"url\",\n          \"x-ms-enum\": {\n            \"name\": \"ImagesResponseFormat\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"url\",\n                \"description\": \"The URL that provides temporary access to download the generated images.\",\n                \"name\": \"Url\"\n              },\n              {\n                \"value\": \"b64_json\",\n                \"description\": \"The generated images are returned as base64 encoded string.\",\n                \"name\": \"Base64Json\"\n              }\n            ]\n          }\n        },\n        \"imageSize\": {\n          \"description\": \"The size of the generated images.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"1792x1024\",\n            \"1024x1792\",\n            \"1024x1024\"\n          ],\n          \"default\": \"1024x1024\",\n          \"x-ms-enum\": {\n            \"name\": \"Size\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"1792x1024\",\n                \"description\": \"The desired size of the generated image is 1792x1024 pixels.\",\n                \"name\": \"Size1792x1024\"\n              },\n              {\n                \"value\": \"1024x1792\",\n                \"description\": \"The desired size of the generated image is 1024x1792 pixels.\",\n                \"name\": \"Size1024x1792\"\n              },\n              {\n                \"value\": \"1024x1024\",\n                \"description\": \"The desired size of the generated image is 1024x1024 pixels.\",\n                \"name\": \"Size1024x1024\"\n              }\n            ]\n          }\n        },\n        \"imageStyle\": {\n          \"description\": \"The style of the generated images.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"vivid\",\n            \"natural\"\n          ],\n          \"default\": \"vivid\",\n          \"x-ms-enum\": {\n            \"name\": \"Style\",\n            \"modelAsString\": true,\n            \"values\": [\n              {\n                \"value\": \"vivid\",\n                \"description\": \"Vivid creates images that are hyper-realistic and dramatic.\",\n                \"name\": \"Vivid\"\n              },\n              {\n                \"value\": \"natural\",\n                \"description\": \"Natural creates images that are more natural and less hyper-realistic.\",\n                \"name\": \"Natural\"\n              }\n            ]\n          }\n        },\n        \"imageGenerationsRequest\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"prompt\": {\n              \"description\": \"A text description of the desired image(s). The maximum length is 4000 characters.\",\n              \"type\": \"string\",\n              \"format\": \"string\",\n              \"example\": \"a corgi in a field\",\n              \"minLength\": 1\n            },\n            \"n\": {\n              \"description\": \"The number of images to generate.\",\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"maximum\": 1,\n              \"default\": 1\n            },\n            \"size\": {\n              \"$ref\": \"#/components/schemas/imageSize\"\n            },\n            \"response_format\": {\n              \"$ref\": \"#/components/schemas/imagesResponseFormat\"\n            },\n            \"user\": {\n              \"description\": \"A unique identifier representing your end-user, which can help to monitor and detect abuse.\",\n              \"type\": \"string\",\n              \"format\": \"string\",\n              \"example\": \"user123456\"\n            },\n            \"quality\": {\n              \"$ref\": \"#/components/schemas/imageQuality\"\n            },\n            \"style\": {\n              \"$ref\": \"#/components/schemas/imageStyle\"\n            }\n          },\n          \"required\": [\n            \"prompt\"\n          ]\n        },\n        \"generateImagesResponse\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"created\": {\n              \"type\": \"integer\",\n              \"format\": \"unixtime\",\n              \"description\": \"The unix timestamp when the operation was created.\",\n              \"example\": \"1676540381\"\n            },\n            \"data\": {\n              \"type\": \"array\",\n              \"description\": \"The result data of the operation, if successful\",\n              \"items\": {\n                \"$ref\": \"#/components/schemas/imageResult\"\n              }\n            }\n          },\n          \"required\": [\n            \"created\",\n            \"data\"\n          ]\n        },\n        \"imageResult\": {\n          \"type\": \"object\",\n          \"description\": \"The image url or encoded image if successful, and an error otherwise.\",\n          \"properties\": {\n            \"url\": {\n              \"type\": \"string\",\n              \"description\": \"The image url.\",\n              \"example\": \"https://www.contoso.com\"\n            },\n            \"b64_json\": {\n              \"type\": \"string\",\n              \"description\": \"The base64 encoded image\"\n            },\n            \"content_filter_results\": {\n              \"$ref\": \"#/components/schemas/dalleContentFilterResults\"\n            },\n            \"revised_prompt\": {\n              \"type\": \"string\",\n              \"description\": \"The prompt that was used to generate the image, if there was any revision to the prompt.\"\n            },\n            \"prompt_filter_results\": {\n              \"$ref\": \"#/components/schemas/dalleFilterResults\"\n            }\n          }\n        }\n      },\n      \"securitySchemes\": {\n        \"bearer\": {\n          \"type\": \"oauth2\",\n          \"flows\": {\n            \"implicit\": {\n              \"authorizationUrl\": \"https://login.microsoftonline.com/common/oauth2/v2.0/authorize\",\n              \"scopes\": {}\n            }\n          },\n          \"x-tokenInfoFunc\": \"api.middleware.auth.bearer_auth\",\n          \"x-scopeValidateFunc\": \"api.middleware.auth.validate_scopes\"\n        },\n        \"apiKey\": {\n          \"type\": \"apiKey\",\n          \"name\": \"api-key\",\n          \"in\": \"header\"\n        }\n      }\n    }\n  }"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/apim_policies/apimanagement.tf",
    "content": "locals {\n  azureOpenAIAPINames = [azurerm_api_management_api.azureOpenAIApi.name]\n}\n\ndata \"azurerm_api_management\" \"apiManagementService\" {\n  name                = var.apiManagementServiceName\n  resource_group_name = var.resourceGroupName\n}\n\ndata \"azurerm_user_assigned_identity\" \"apimIdentity\" {\n  name                = var.apimIdentityName\n  resource_group_name = var.resourceGroupName\n}\n\ndata \"azurerm_eventhub_namespace\" \"eventHubNamespace\" {\n  name                = var.eventHubNamespaceName\n  resource_group_name = var.openaiResourceGroupName\n}\n\nresource \"azurerm_api_management_api\" \"azureOpenAIApi\" {\n  name                = \"azure-openai-api\"\n  resource_group_name = var.resourceGroupName\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  revision            = \"1\"\n  display_name        = \"AzureOpenAI\"\n  path                = \"openai\"\n  protocols           = [\"https\"]\n\n  import {\n    content_format = \"openapi+json\"\n    content_value  = file(\"modules/apim_policies/api-specs/openapi-spec.json\")\n  }\n}\n\nresource \"azurerm_api_management_product\" \"azureOpenAIProduct\" {\n  product_id            = \"aoai-product\"\n  resource_group_name   = var.resourceGroupName\n  api_management_name   = data.azurerm_api_management.apiManagementService.name\n  display_name          = \"aoai-product\"\n  subscription_required = true\n  published             = true\n}\n\nresource \"azurerm_api_management_product\" \"multiTenantProduct1\" {\n  product_id            = \"multi-tenant-product1\"\n  resource_group_name   = var.resourceGroupName\n  api_management_name   = data.azurerm_api_management.apiManagementService.name\n  display_name          = \"multi-tenant-product1\"\n  subscription_required = true\n  published             = true\n}\n\nresource \"azurerm_api_management_product\" \"multiTenantProduct2\" {\n  product_id            = \"multi-tenant-product2\"\n  resource_group_name   = var.resourceGroupName\n  api_management_name   = data.azurerm_api_management.apiManagementService.name\n  display_name          = \"multi-tenant-product2\"\n  subscription_required = true\n  published             = true\n}\n\nresource \"azurerm_api_management_product_api\" \"azureOpenAIProductAPI\" {\n  product_id          = azurerm_api_management_product.azureOpenAIProduct.product_id\n  api_name            = azurerm_api_management_api.azureOpenAIApi.name\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  resource_group_name = var.resourceGroupName\n  depends_on = [\n    azurerm_api_management_api.azureOpenAIApi,\n    azurerm_api_management_policy_fragment.simpleRoundRobinPolicyFragment\n  ]\n}\n\nresource \"azurerm_api_management_product_api\" \"multiTenantProduct1API\" {\n  product_id          = azurerm_api_management_product.multiTenantProduct1.product_id\n  api_name            = azurerm_api_management_api.azureOpenAIApi.name\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  resource_group_name = var.resourceGroupName\n  depends_on = [\n    azurerm_api_management_api.azureOpenAIApi,\n    azurerm_api_management_policy_fragment.simpleRoundRobinPolicyFragment\n  ]\n}\n\nresource \"azurerm_api_management_product_api\" \"multiTenantProduct2API\" {\n  product_id          = azurerm_api_management_product.multiTenantProduct2.product_id\n  api_name            = azurerm_api_management_api.azureOpenAIApi.name\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  resource_group_name = var.resourceGroupName\n  depends_on = [\n    azurerm_api_management_api.azureOpenAIApi,\n    azurerm_api_management_policy_fragment.simpleRoundRobinPolicyFragment\n  ]\n}\n\nresource \"azurerm_api_management_backend\" \"ptuBackendOne\" {\n  name                = \"ptu-backend-1\"\n  resource_group_name = var.resourceGroupName\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  protocol            = \"http\"\n  url                 = var.ptuDeploymentOneBaseUrl\n}\n\nresource \"azurerm_api_management_backend\" \"payAsYouGoBackendOne\" {\n  name                = \"payg-backend-1\"\n  resource_group_name = var.resourceGroupName\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  protocol            = \"http\"\n  url                 = var.payAsYouGoDeploymentOneBaseUrl\n}\n\nresource \"azurerm_api_management_backend\" \"payAsYouGoBackendTwo\" {\n  name                = \"payg-backend-2\"\n  resource_group_name = var.resourceGroupName\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  protocol            = \"http\"\n  url                 = var.payAsYouGoDeploymentTwoBaseUrl\n}\n\nresource \"azurerm_api_management_subscription\" \"azureOpenAIProductSubscription\" {\n  subscription_id     = \"aoai-product-subscription\"\n  resource_group_name = var.resourceGroupName\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  display_name        = \"aoai-product-subscription\"\n  state               = \"active\"\n  product_id          = azurerm_api_management_product.azureOpenAIProduct.id\n}\n\nresource \"azurerm_api_management_subscription\" \"multiTenantProduct1Subscription\" {\n  subscription_id     = \"multi-tenant-product1-subscription\"\n  resource_group_name = var.resourceGroupName\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  display_name        = \"multi-tenant-product1-subscription\"\n  state               = \"active\"\n  product_id          = azurerm_api_management_product.multiTenantProduct1.id\n}\n\nresource \"azurerm_api_management_subscription\" \"multiTenantProduct2Subscription\" {\n  subscription_id     = \"multi-tenant-product2-subscription\"\n  resource_group_name = var.resourceGroupName\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  display_name        = \"multi-tenant-product2-subscription\"\n  state               = \"active\"\n  product_id          = azurerm_api_management_product.multiTenantProduct2.id\n}\n\nresource \"azurerm_api_management_policy_fragment\" \"simpleRoundRobinPolicyFragment\" {\n  api_management_id = data.azurerm_api_management.apiManagementService.id\n  name              = \"simple-priority-weighted\"\n  format            = \"rawxml\"\n  value             = file(\"../policies/fragments/load-balancing/simple-priority-weighted.xml\")\n  depends_on = [\n    azurerm_api_management_backend.payAsYouGoBackendOne,\n    azurerm_api_management_backend.payAsYouGoBackendTwo,\n    azurerm_api_management_named_value.apimOpenaiApiUamiNamedValue,\n    module.api_lb_pool\n  ]\n}\n\nresource \"azurerm_api_management_policy_fragment\" \"simpleRateLimitingPolicyFragment\" {\n  api_management_id = data.azurerm_api_management.apiManagementService.id\n  name              = \"rate-limiting-by-tokens\"\n  format            = \"rawxml\"\n  value             = file(\"../policies/fragments/rate-limiting/rate-limiting-by-tokens.xml\")\n  depends_on = [\n    azurerm_api_management_backend.payAsYouGoBackendOne,\n    azurerm_api_management_backend.payAsYouGoBackendTwo\n  ]\n}\n\nresource \"azurerm_api_management_policy_fragment\" \"adaptiveRateLimitingPolicyFragment\" {\n  api_management_id = data.azurerm_api_management.apiManagementService.id\n  name              = \"adaptive-rate-limiting\"\n  format            = \"rawxml\"\n  value             = file(\"../policies/fragments/rate-limiting/adaptive-rate-limiting.xml\")\n  depends_on = [\n    azurerm_api_management_backend.payAsYouGoBackendOne,\n    azurerm_api_management_backend.payAsYouGoBackendTwo\n  ]\n}\n\nresource \"azurerm_api_management_policy_fragment\" \"adaptiveRateLimitingWorkAroundPolicyFragment\" {\n  api_management_id = data.azurerm_api_management.apiManagementService.id\n  name              = \"rate-limiting-workaround\"\n  format            = \"rawxml\"\n  value             = file(\"../policies/fragments/rate-limiting/rate-limiting-workaround.xml\")\n  depends_on = [\n    azurerm_api_management_backend.payAsYouGoBackendOne,\n    azurerm_api_management_backend.payAsYouGoBackendTwo\n  ]\n}\n\n\nresource \"azurerm_api_management_policy_fragment\" \"usageTrackingEHPolicyFragment\" {\n  api_management_id = data.azurerm_api_management.apiManagementService.id\n  name              = \"usage-tracking-with-eventhub\"\n  format            = \"rawxml\"\n  value             = file(\"../policies/fragments/usage-tracking/usage-tracking-with-eventhub.xml\")\n  depends_on = [\n    azurerm_api_management_logger.event_hub_logger\n  ]\n}\n\nresource \"azurerm_api_management_policy_fragment\" \"usageTrackingWithAppInsightsPolicyFragment\" {\n  api_management_id = data.azurerm_api_management.apiManagementService.id\n  name              = \"usage-tracking-with-appinsights\"\n  format            = \"rawxml\"\n  value             = file(\"../policies/fragments/usage-tracking/usage-tracking-with-appinsights.xml\")\n  depends_on = [\n    azurerm_api_management_logger.event_hub_logger\n  ]\n}\n\n//Load-balancing with Circuit Breaker policy\nmodule \"api_backend\" {\n  source                      = \"./backends\"\n  api_management_service_name = data.azurerm_api_management.apiManagementService.name\n  backend_uris = [\n    \"${var.ptuDeploymentOneBaseUrl}/\",\n    \"${var.payAsYouGoDeploymentOneBaseUrl}/\",\n    \"${var.payAsYouGoDeploymentTwoBaseUrl}/\"\n  ]\n  resource_group_name = var.resourceGroupName\n  depends_on = [\n    data.azurerm_api_management.apiManagementService\n  ]\n}\n\nmodule \"api_lb_pool\" {\n  source                      = \"./lb_pool\"\n  api_management_service_name = data.azurerm_api_management.apiManagementService.name\n  backends                    = module.api_backend.backend_names\n  resource_group_name         = var.resourceGroupName\n  depends_on = [\n    module.api_backend\n  ]\n}\n\nresource \"azurerm_api_management_api_policy\" \"azureOpenAIApiPolicy\" {\n  api_name            = azurerm_api_management_api.azureOpenAIApi.name\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  resource_group_name = data.azurerm_api_management.apiManagementService.resource_group_name\n  xml_content         = file(\"../policies/genai-policy.xml\")\n  depends_on = [\n    azurerm_api_management_policy_fragment.simpleRoundRobinPolicyFragment,\n    azurerm_api_management_policy_fragment.adaptiveRateLimitingPolicyFragment,\n    azurerm_api_management_policy_fragment.usageTrackingWithAppInsightsPolicyFragment\n  ]\n}\n\nresource \"azurerm_api_management_product_policy\" \"multiTenantProduct1Policy\" {\n  product_id          = azurerm_api_management_product.multiTenantProduct1.product_id\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  resource_group_name = data.azurerm_api_management.apiManagementService.resource_group_name\n  xml_content         = file(\"../policies/multi-tenancy/multi-tenant-product1-policy.xml\")\n}\n\nresource \"azurerm_api_management_product_policy\" \"multiTenantProduct2Policy\" {\n  product_id          = azurerm_api_management_product.multiTenantProduct2.product_id\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  resource_group_name = data.azurerm_api_management.apiManagementService.resource_group_name\n  xml_content         = file(\"../policies/multi-tenancy/multi-tenant-product2-policy.xml\")\n}\n\nresource \"azurerm_api_management_named_value\" \"apimOpenaiApiUamiNamedValue\" {\n  name                = \"apim-identity\"\n  resource_group_name = var.resourceGroupName\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  display_name        = \"apim-identity\"\n  value               = data.azurerm_user_assigned_identity.apimIdentity.client_id\n  secret              = true\n}\n\nresource \"azurerm_api_management_logger\" \"event_hub_logger\" {\n  name                = \"eventhub-logger\"\n  resource_group_name = var.resourceGroupName\n  api_management_name = data.azurerm_api_management.apiManagementService.name\n  eventhub {\n    name                            = var.eventHubName\n    endpoint_uri                    = \"${data.azurerm_eventhub_namespace.eventHubNamespace.name}.servicebus.windows.net\"\n    user_assigned_identity_client_id = data.azurerm_user_assigned_identity.apimIdentity.client_id\n  }\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/apim_policies/backends/backends.tf",
    "content": "variable \"api_management_service_name\" {\n  type = string\n}\n\nvariable \"backend_uris\" {\n  type = list(string)\n}\n\nvariable \"resource_group_name\" {\n  type = string\n}\n\ndata \"azurerm_api_management\" \"apiManagementService\" {\n  name                = var.api_management_service_name\n  resource_group_name = var.resource_group_name\n}\n\nresource \"azapi_resource\" \"backend\" {\n  count = length(var.backend_uris)\n\n  type      = \"Microsoft.ApiManagement/service/backends@2023-09-01-preview\"\n  name      = \"aoai-${count.index}\"\n  parent_id = data.azurerm_api_management.apiManagementService.id\n\n  body = jsonencode({\n    properties = {\n      url      = var.backend_uris[count.index]\n      protocol = \"http\"\n      circuitBreaker = {\n        rules = [\n          {\n            name = \"breakerRule\"\n            failureCondition = {\n              count    = 1\n              interval = \"PT1M\"\n              statusCodeRanges = [\n                {\n                  min = 429\n                  max = 429\n                }\n              ]\n              errorReasons = [\"timeout\"]\n            }\n            tripDuration     = \"PT1M\"\n            acceptRetryAfter = true\n          }\n        ]\n      }\n    }\n  })\n  response_export_values = [\"*\"]\n}\n\n\noutput \"backend_names\" {\n  value = [for i in range(0, length(var.backend_uris)) : azapi_resource.backend[i].name]\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/apim_policies/backends/providers.tf",
    "content": "terraform {\n  required_providers {\n    azapi = {\n      source  = \"azure/azapi\"\n      version = \"~> 1.0\"\n    }\n  }\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/apim_policies/lb_pool/lb-pool.tf",
    "content": "variable \"api_management_service_name\" {\n  type = string\n}\n\nvariable \"backends\" {\n  type = list(string)\n}\n\n# variable \"backendUris\" {\n#   type = list(string)\n# }\n\nvariable \"resource_group_name\" {\n  type = string\n}\n\ndata \"azurerm_api_management\" \"apiManagementService\" {\n  name                = var.api_management_service_name\n  resource_group_name = var.resource_group_name\n}\nresource \"azapi_resource\" \"aoai_lb_pool\" {\n  type      = \"Microsoft.ApiManagement/service/backends@2023-09-01-preview\"\n  name      = \"aoai-lb-pool\"\n  parent_id = data.azurerm_api_management.apiManagementService.id\n\n  schema_validation_enabled = false\n  body = jsonencode({\n    properties = {\n      title = \"aoai-lb-pool\"\n      type  = \"Pool\"\n      pool = {\n        services = [\n          {\n            id       = \"/backends/${var.backends[0]}\"\n            priority = 1\n            weight   = 1\n          },\n          {\n            id       = \"/backends/${var.backends[1]}\"\n            priority = 2\n            weight   = 2\n          },\n          {\n            id       = \"/backends/${var.backends[2]}\"\n            priority = 1\n            weight   = 3\n          }\n        ]\n      }\n    }\n  })\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/apim_policies/lb_pool/providers.tf",
    "content": "terraform {\n  required_providers {\n    azapi = {\n      source  = \"azure/azapi\"\n      version = \"~> 1.0\"\n    }\n  }\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/apim_policies/outputs.tf",
    "content": "output \"apiManagementServiceName\" {\n  description = \"The name of the API Management service instance\"\n  value       = var.apiManagementServiceName\n}\n\n# This is in bicep - but we are using AZ CLI in the deployment script\n# to get the key instead of exposing it in the output\n# output \"apiManagementAzureOpenAIProductSubscriptionKey\" {\n#   value = azurerm_api_management_subscription.example.primary_key\n#   description = \"The primary key of the Azure OpenAI product subscription.\"\n# }\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/apim_policies/variables.tf",
    "content": "variable \"resourceGroupName\" {\n  type        = string\n  description = \"The name of the resource group\"\n}\n\nvariable \"apiManagementServiceName\" {\n  description = \"The name of the API Management service instance\"\n  type        = string\n}\n\nvariable \"ptuDeploymentOneBaseUrl\" {\n  description = \"The base url of the first Azure Open AI Service PTU deployment\"\n  type        = string\n}\n\nvariable \"payAsYouGoDeploymentOneBaseUrl\" {\n  description = \"The base url of the first Azure Open AI Service Pay-As-You-Go deployment\"\n  type        = string\n}\n\nvariable \"payAsYouGoDeploymentTwoBaseUrl\" {\n  description = \"The base url of the second Azure Open AI Service Pay-As-You-Go deployment\"\n  type        = string\n}\n\nvariable \"eventHubNamespaceName\" {\n  description = \"The name of the Event Hub Namespace to log to\"\n  type        = string\n}\n\nvariable \"eventHubName\" {\n  description = \"The name of the Event Hub to log utilization data to\"\n  type        = string\n}\n\nvariable \"apimIdentityName\" {\n  description = \"The name of the API Management Identity\"\n  type        = string\n}\n\nvariable \"location\" {\n  description = \"The location of the resource group\"\n  type        = string\n}\n\nvariable \"openaiResourceGroupName\" {\n  description = \"The name of the resource group for the OpenAI service\"\n  type        = string\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/eventhub/eventhub.tf",
    "content": "resource \"azurerm_eventhub_namespace\" \"eventHubNamespace\" {\n  name                 = var.eventHubNamespaceName\n  location             = var.location\n  resource_group_name  = var.openaiResourceGroupName\n  sku                  = var.eventHubSku\n  capacity             = 1\n  auto_inflate_enabled = false\n}\n\nresource \"azurerm_eventhub\" \"eventHub\" {\n  name                = var.eventHubName\n  namespace_name      = azurerm_eventhub_namespace.eventHubNamespace.name\n  resource_group_name = var.openaiResourceGroupName\n  partition_count     = 1\n  message_retention   = 7\n}\n\ndata \"azurerm_user_assigned_identity\" \"apimIdentity\" {\n  name                = var.apimIdentityName\n  resource_group_name = var.apimResourceGroupName\n}\n\ndata \"azurerm_role_definition\" \"eventHubsDataSenderRoleDefinition\" {\n  name = \"Azure Event Hubs Data Sender\"\n}\n\nresource \"azurerm_role_assignment\" \"assignEventHubsDataSenderToApiManagement\" {\n  scope                = azurerm_eventhub_namespace.eventHubNamespace.id\n  role_definition_name = data.azurerm_role_definition.eventHubsDataSenderRoleDefinition.name\n  principal_id         = data.azurerm_user_assigned_identity.apimIdentity.principal_id\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/eventhub/outputs.tf",
    "content": "output \"eventHubNamespaceName\" {\n  description = \"The name of the Event Hub Namespace.\"\n  value       = azurerm_eventhub_namespace.eventHubNamespace.name\n}\n\noutput \"eventHubName\" {\n  description = \"The name of the Event Hub.\"\n  value       = azurerm_eventhub.eventHub.name\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/eventhub/variables.tf",
    "content": "variable \"eventHubNamespaceName\" {\n  description = \"The name of the Event Hub Namespace\"\n  type        = string\n}\n\nvariable \"eventHubName\" {\n  description = \"The name of the Event Hub\"\n  type        = string\n}\n\nvariable \"eventHubSku\" {\n  description = \"The messaging tier for Event Hub Namespace.\"\n  type        = string\n  default     = \"Standard\"\n}\n\nvariable \"apimIdentityName\" {\n  type = string\n}\n\nvariable \"apimResourceGroupName\" {\n  type = string\n}\n\nvariable \"openaiResourceGroupName\" {\n  type = string\n}\n\nvariable \"location\" {\n  description = \"Location for all resources.\"\n  type        = string\n  default     = \"eastus\"\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/openai/openai.tf",
    "content": "data \"azurerm_user_assigned_identity\" \"apimIdentity\" {\n  name                = var.apimIdentityName\n  resource_group_name = var.apimResourceGroupName\n}\n\ndata \"azurerm_subscription\" \"primary\" {\n}\n\nresource \"azurerm_cognitive_account\" \"openai\" {\n  name                          = var.name\n  location                      = var.location\n  resource_group_name           = var.resource_group_name\n  kind                          = \"OpenAI\"\n  custom_subdomain_name         = var.custom_subdomain_name\n  sku_name                      = var.sku_name\n  public_network_access_enabled = var.public_network_access_enabled\n  tags                          = var.tags\n\n  identity {\n    type = \"SystemAssigned\"\n  }\n\n  lifecycle {\n    ignore_changes = [\n      tags\n    ]\n  }\n}\n\nresource \"azurerm_cognitive_deployment\" \"deployment\" {\n  for_each = { for deployment in var.deployments : deployment.name => deployment }\n\n  name                 = each.key\n  cognitive_account_id = azurerm_cognitive_account.openai.id\n\n  model {\n    format  = \"OpenAI\"\n    name    = each.value.model.name\n    version = each.value.model.version\n  }\n\n  scale {\n    type = \"Standard\"\n  }\n}\n\ndata \"azurerm_role_definition\" \"cognitiveServicesOpenAIUser\" {\n  name  = \"Cognitive Services OpenAI User\"\n  scope = data.azurerm_subscription.primary.id\n}\n\nresource \"azurerm_role_assignment\" \"roleAssignment\" {\n  scope              = azurerm_cognitive_account.openai.id\n  role_definition_id = data.azurerm_role_definition.cognitiveServicesOpenAIUser.id\n  principal_id       = data.azurerm_user_assigned_identity.apimIdentity.principal_id\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/openai/outputs.tf",
    "content": "output \"id\" {\n  value = azurerm_cognitive_account.openai.id\n  description = \"Specifies the resource id of the log analytics workspace\"\n}\n\noutput \"location\" {\n  value = azurerm_cognitive_account.openai.location\n  description = \"Specifies the location of the log analytics workspace\"\n}\n\noutput \"name\" {\n  value = azurerm_cognitive_account.openai.name\n  description = \"Specifies the name of the log analytics workspace\"\n}\n\noutput \"resource_group_name\" {\n  value = azurerm_cognitive_account.openai.resource_group_name\n  description = \"Specifies the name of the resource group that contains the log analytics workspace\"\n}\n\noutput \"endpoint\" {\n  value = azurerm_cognitive_account.openai.endpoint\n  description = \"Specifies the endpoint of the Azure OpenAI Service.\"\n}\n\n# This is in bicep - but we are using AZ CLI in the deployment script\n# to get the key instead of exposing it in the output\n# output \"primary_access_key\" {\n#   value = azurerm_cognitive_account.openai.primary_access_key\n#   sensitive = true\n#   description = \"Specifies the primary access key of the Azure OpenAI Service.\"\n# }\n\n# This is in bicep - but we are using AZ CLI in the deployment script\n# to get the key instead of exposing it in the output\n# output \"secondary_access_key\" {\n#   value = azurerm_cognitive_account.openai.secondary_access_key\n#   sensitive = true\n#   description = \"Specifies the secondary access key of the Azure OpenAI Service.\"\n# }\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/openai/variables.tf",
    "content": "variable \"resource_group_name\" {\n  description = \"(Required) Specifies the resource group name\"\n  type        = string\n}\n\nvariable \"location\" {\n  description = \"(Required) Specifies the location of the Azure OpenAI Service\"\n  type        = string\n}\n\nvariable \"name\" {\n  description = \"(Required) Specifies the name of the Azure OpenAI Service\"\n  type        = string\n}\n\nvariable \"sku_name\" {\n  description = \"(Optional) Specifies the sku name for the Azure OpenAI Service\"\n  type        = string\n  default     = \"S0\"\n}\n\nvariable \"tags\" {\n  description = \"(Optional) Specifies the tags of the Azure OpenAI Service\"\n  type        = map(any)\n  default     = {}\n}\n\nvariable \"custom_subdomain_name\" {\n  description = \"(Optional) Specifies the custom subdomain name of the Azure OpenAI Service\"\n  type        = string\n}\n\nvariable \"public_network_access_enabled\" {\n  description = \"(Optional) Specifies whether public network access is allowed for the Azure OpenAI Service\"\n  type        = bool\n  default     = false\n}\n\nvariable \"deployments\" {\n  description = \"(Optional) Specifies the deployments of the Azure OpenAI Service\"\n  type = list(object({\n    name = string\n    model = object({\n      name    = string\n      version = string\n    })\n    rai_policy_name = string\n  }))\n  default = [\n    {\n      name = \"gpt-35-turbo\"\n      model = {\n        name    = \"gpt-35-turbo\"\n        version = \"0301\"\n      }\n      rai_policy_name = \"\"\n    }\n  ]\n}\n\nvariable \"apimIdentityName\" {\n  description = \"The name of the API Management Identity\"\n  type        = string\n}\n\nvariable \"apimResourceGroupName\" {\n  description = \"The name of the resource group for the API Management Identity\"\n  type        = string\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/private_dns_zone/outputs.tf",
    "content": "output \"id\" {\n  description = \"Specifies the resource id of the private dns zone\"\n  value       = azurerm_private_dns_zone.private_dns_zone.id\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/private_dns_zone/privatednszone.tf",
    "content": "resource \"azurerm_private_dns_zone\" \"private_dns_zone\" {\n  name                = var.name\n  resource_group_name = var.resource_group_name\n  tags                = var.tags\n\n  lifecycle {\n    ignore_changes = [\n      tags\n    ]\n  }\n}\n\nresource \"azurerm_private_dns_zone_virtual_network_link\" \"link\" {\n  name                  = \"link_to_${lower(basename(var.virtual_networks_to_link_id))}\"\n  resource_group_name   = var.resource_group_name\n  private_dns_zone_name = azurerm_private_dns_zone.private_dns_zone.name\n  virtual_network_id    = var.virtual_networks_to_link_id\n\n  lifecycle {\n    ignore_changes = [\n      tags\n    ]\n  }\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/private_dns_zone/variables.tf",
    "content": "variable \"name\" {\n  description = \"(Required) Specifies the name of the private dns zone\"\n  type        = string\n}\n\nvariable \"resource_group_name\" {\n  description = \"(Required) Specifies the resource group name of the private dns zone\"\n  type        = string\n}\n\nvariable \"tags\" {\n  description = \"(Optional) Specifies the tags of the private dns zone\"\n  default     = {}\n}\n\nvariable \"virtual_networks_to_link_id\" {\n  description = \"(Optional) Specifies the virtual networks id to which create a virtual network link\"\n  type        = string\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/private_endpoint/outputs.tf",
    "content": "output \"id\" {\n  description = \"Specifies the resource id of the private endpoint.\"\n  value       = azurerm_private_endpoint.private_endpoint.id\n}\n\noutput \"private_dns_zone_group\" {\n  description = \"Specifies the private dns zone group of the private endpoint.\"\n  value = azurerm_private_endpoint.private_endpoint.private_dns_zone_group\n}\n\noutput \"private_dns_zone_configs\" {\n  description = \"Specifies the private dns zone(s) configuration\"\n  value = azurerm_private_endpoint.private_endpoint.private_dns_zone_configs\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/private_endpoint/privateendpoint.tf",
    "content": "resource \"azurerm_private_endpoint\" \"private_endpoint\" {\n  name                = var.name\n  location            = var.location\n  resource_group_name = var.resource_group_name\n  subnet_id           = var.subnet_id\n  tags                = var.tags\n\n  private_service_connection {\n    name                           = \"${var.name}Connection\"\n    private_connection_resource_id = var.private_connection_resource_id\n    is_manual_connection           = var.is_manual_connection\n    subresource_names              = try([var.subresource_name], null)\n    request_message                = try(var.request_message, null)\n  }\n\n  private_dns_zone_group {\n    name                 = var.private_dns_zone_group_name\n    private_dns_zone_ids = var.private_dns_zone_group_ids\n  }\n\n  lifecycle {\n    ignore_changes = [\n      tags\n    ]\n  }\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/modules/private_endpoint/variables.tf",
    "content": "variable \"name\" {\n  description = \"(Required) Specifies the name of the private endpoint. Changing this forces a new resource to be created.\"\n  type        = string\n}\n\nvariable \"resource_group_name\" {\n  description = \"(Required) The name of the resource group. Changing this forces a new resource to be created.\"\n  type        = string\n}\n\nvariable \"private_connection_resource_id\" {\n  description = \"(Required) Specifies the resource id of the private link service\"\n  type        = string \n}\n\nvariable \"location\" {\n  description = \"(Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created.\"\n  type        = string\n}\n\nvariable \"subnet_id\" {\n  description = \"(Required) Specifies the resource id of the subnet\"\n  type        = string\n}\n\nvariable \"is_manual_connection\" {\n  description = \"(Optional) Specifies whether the private endpoint connection requires manual approval from the remote resource owner.\"\n  type        = string\n  default     = false  \n}\n\nvariable \"subresource_name\" {\n  description = \"(Optional) Specifies a subresource name which the Private Endpoint is able to connect to.\"\n  type        = string\n  default     = null\n}\n\nvariable \"request_message\" {\n  description = \"(Optional) Specifies a message passed to the owner of the remote resource when the private endpoint attempts to establish the connection to the remote resource.\"\n  type        = string\n  default     = null \n}\n\nvariable \"private_dns_zone_group_name\" {\n  description = \"(Required) Specifies the Name of the Private DNS Zone Group. Changing this forces a new private_dns_zone_group resource to be created.\"\n  type        = string\n}\n\nvariable \"private_dns_zone_group_ids\" {\n  description = \"(Required) Specifies the list of Private DNS Zones to include within the private_dns_zone_group.\"\n  type        = list(string)\n}\n\nvariable \"tags\" {\n  description = \"(Optional) Specifies the tags of the network security group\"\n  default     = {}\n}\n\nvariable \"private_dns\" {\n  default = {}\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/provider.tf",
    "content": "terraform {\n\n  # for storage backends, see backend.tf.sample\n  \n  required_providers {\n    azurerm = {\n      source  = \"hashicorp/azurerm\"\n      version = \"~> 3.1\"\n    }\n    random = {\n      source  = \"hashicorp/random\"\n      version = \"~> 3.6.0\"\n    }\n    azapi = {\n      source = \"azure/azapi\"\n    }\n  }\n}\n\n# Configure the Microsft Azure provider\nprovider \"azurerm\" {\n  features {\n    resource_group {\n      prevent_deletion_if_contains_resources = false\n    }\n  }\n  use_oidc = true\n}\n\nprovider \"azapi\" {\n}\n"
  },
  {
    "path": "scenarios/workload-genai/terraform/variables.tf",
    "content": "variable \"location\" {\n  type        = string\n  description = \"The Azure location in which the deployment is happening\"\n  default     = \"eastus2\"\n}\n\nvariable \"workloadName\" {\n  type        = string\n  description = \"A suffix for naming\"\n  default     = \"apimdemo\"\n}\n\nvariable \"environment\" {\n  type        = string\n  description = \"Environment\"\n  default     = \"dev\"\n}\n\nvariable \"identifier\" {\n  description = \"The identifier for the resource deployments\"\n  type        = string\n}\n\nvariable \"tags\" {\n  description = \"(Optional) Specifies tags for all the resources\"\n  default     = {}\n}\n\nvariable \"log_analytics_workspace_name\" {\n  description = \"Specifies the name of the log analytics workspace\"\n  default     = \"Workspace\"\n  type        = string\n}\n\nvariable \"vnet_name\" {\n  description = \"Specifies the name of the virtual network\"\n  default     = \"VNet\"\n  type        = string\n}\n\nvariable \"vnet_address_space\" {\n  description = \"Specifies the address prefix of the virtual network\"\n  default     = [\"10.0.0.0/16\"]\n  type        = list(string)\n}\n\nvariable \"privateEndpointAddressPrefix\" {\n  description = \"Private Endpoint Address Prefix\"\n  type        = string\n  default     = \"10.2.5.0/24\"\n}\n\nvariable \"internal_load_balancer_enabled\" {\n  description = \"(Optional) specifies whether the Azure Container Apps Environment operate in Internal Load Balancing Mode? Defaults to false. Changing this forces a new resource to be created.\"\n  type        = bool\n  default     = false\n}\n\nvariable \"openai_name\" {\n  description = \"(Required) Specifies the name of the Azure OpenAI Service\"\n  type        = string\n  default     = \"OpenAI\"\n}\n\nvariable \"openai_sku_name\" {\n  description = \"(Optional) Specifies the sku name for the Azure OpenAI Service\"\n  type        = string\n  default     = \"S0\"\n}\n\nvariable \"openai_custom_subdomain_name\" {\n  description = \"(Optional) Specifies the custom subdomain name of the Azure OpenAI Service\"\n  type        = string\n  nullable    = true\n  default     = \"\"\n}\n\nvariable \"openai_public_network_access_enabled\" {\n  description = \"(Optional) Specifies whether public network access is allowed for the Azure OpenAI Service\"\n  type        = bool\n  default     = false\n}\n\nvariable \"openai_deployments\" {\n  description = \"(Optional) Specifies the deployments of the Azure OpenAI Service\"\n  type = list(object({\n    name = string\n    model = object({\n      name    = string\n      version = string\n    })\n    rai_policy_name = string\n  }))\n  default = [\n    {\n      name = \"gpt-4o-mini\"\n      model = {\n        name    = \"gpt-4o-mini\"\n        version = \"2024-07-18\"\n      }\n      rai_policy_name = \"\"\n    },\n    {\n      name = \"text-embedding-ada-002\"\n      model = {\n        name    = \"text-embedding-ada-002\"\n        version = \"2\"\n      }\n      rai_policy_name = \"\"\n    }\n  ]\n}\n\nvariable \"workload_managed_identity_name\" {\n  description = \"(Required) Specifies the name of the workload user-defined managed identity\"\n  type        = string\n  default     = \"WorkloadIdentity\"\n}\n\nvariable \"eventHubName\" {\n  description = \"The name of the Event Hub to log utilization data to\"\n  type        = string\n  default     = \"apim-utilization-reporting\"\n}\n\nvariable \"apimIdentityName\" {\n  description = \"The name of the API Management Identity\"\n  type        = string\n  default     = \"apimIdentity\"\n}\n"
  }
]