Showing preview only (627K chars total). Download the full file or copy to clipboard to get everything.
Repository: Azure/apim-landing-zone-accelerator
Branch: main
Commit: 0180a563a013
Files: 157
Total size: 577.2 KB
Directory structure:
gitextract_6xk9lkjw/
├── .devcontainer/
│ └── devcontainer.json
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── lza-validate-multiregion-apim.yml
│ └── lza-validate-singleregion-apim.yml
├── .gitignore
├── .pre-commit-config.yaml
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── SECURITY.md
├── SUPPORT.md
├── docs/
│ ├── assets/
│ │ └── genai-capabilities.pptx
│ └── images/
│ └── APIM.vsdx
└── scenarios/
├── apim-baseline/
│ ├── README.md
│ ├── bicep/
│ │ ├── README.md
│ │ ├── apim/
│ │ │ ├── apim.bicep
│ │ │ └── modules/
│ │ │ ├── dnsrecords.bicep
│ │ │ └── kvaccess.bicep
│ │ ├── gateway/
│ │ │ ├── appgw.bicep
│ │ │ └── modules/
│ │ │ ├── certificate.bicep
│ │ │ └── certificateSecret.bicep
│ │ ├── main.bicep
│ │ ├── networking/
│ │ │ └── networking.bicep
│ │ └── shared/
│ │ ├── modules/
│ │ │ ├── azmon.bicep
│ │ │ ├── dnszone.bicep
│ │ │ ├── privatedeploy.bicep
│ │ │ └── privateendpoint.bicep
│ │ └── shared.bicep
│ └── terraform/
│ ├── README.md
│ ├── backend.tf.sample
│ ├── main.tf
│ ├── modules/
│ │ ├── apim/
│ │ │ ├── apim.tf
│ │ │ ├── outputs.tf
│ │ │ └── variables.tf
│ │ ├── dns/
│ │ │ ├── dnszone.tf
│ │ │ └── variables.tf
│ │ ├── gateway/
│ │ │ ├── certificate/
│ │ │ │ ├── certificate.tf
│ │ │ │ ├── providers.tf
│ │ │ │ └── variables.tf
│ │ │ ├── gateway.tf
│ │ │ ├── outputs.tf
│ │ │ └── variables.tf
│ │ ├── multi_apim/
│ │ │ ├── apim.tf
│ │ │ ├── outputs.tf
│ │ │ └── variables.tf
│ │ ├── multi_apim-dns-regional/
│ │ │ ├── dnszone.tf
│ │ │ └── variables.tf
│ │ ├── multi_gateway/
│ │ │ ├── certificate/
│ │ │ │ ├── certificate.tf
│ │ │ │ ├── providers.tf
│ │ │ │ └── variables.tf
│ │ │ ├── gateway.tf
│ │ │ ├── outputs.tf
│ │ │ └── variables.tf
│ │ ├── multi_private_dns_zone/
│ │ │ ├── outputs.tf
│ │ │ ├── privatednszone.tf
│ │ │ └── variables.tf
│ │ ├── multi_shared/
│ │ │ ├── azmon.tf
│ │ │ ├── outputs.tf
│ │ │ ├── private_endpoint/
│ │ │ │ ├── outputs.tf
│ │ │ │ ├── privateendpoint.tf
│ │ │ │ └── variables.tf
│ │ │ ├── privatedeploy.tf
│ │ │ ├── shared.tf
│ │ │ └── variables.tf
│ │ ├── multi_traffic_manager/
│ │ │ ├── outputs.tf
│ │ │ ├── trafficmanager.tf
│ │ │ └── variables.tf
│ │ ├── networking/
│ │ │ ├── networking.tf
│ │ │ ├── outputs.tf
│ │ │ └── variables.tf
│ │ └── shared/
│ │ ├── azmon.tf
│ │ ├── outputs.tf
│ │ ├── private_dns_zone/
│ │ │ ├── outputs.tf
│ │ │ ├── privatednszone.tf
│ │ │ └── variables.tf
│ │ ├── private_endpoint/
│ │ │ ├── outputs.tf
│ │ │ ├── privateendpoint.tf
│ │ │ └── variables.tf
│ │ ├── privatedeploy.tf
│ │ ├── shared.tf
│ │ └── variables.tf
│ ├── multi-region/
│ │ ├── multi-region-main.tf
│ │ └── variables.tf
│ ├── provider.tf
│ ├── single-region/
│ │ ├── single-region-main.tf
│ │ └── variables.tf
│ └── variables.tf
├── certs/
│ └── place-custom-cert-here
├── scripts/
│ ├── bicep/
│ │ ├── deploy-apim-baseline.sh
│ │ ├── deploy-workload-function.sh
│ │ └── deploy-workload-genai.sh
│ └── terraform/
│ ├── __destroy-apim-baseline.sh
│ ├── azure-backend-sample.sh
│ ├── deploy-apim-baseline.sh
│ ├── deploy-workload-genai-new.sh
│ ├── deploy-workload-genai.sh
│ ├── sample-multi-region.env
│ ├── sample.backend.hcl
│ ├── sample.env
│ └── test-apim-baseline-deployment.sh
├── workload-functions/
│ ├── README.md
│ └── bicep/
│ ├── README.md
│ ├── apim/
│ │ └── config.bicep
│ ├── backend/
│ │ ├── backend.bicep
│ │ └── modules/
│ │ └── networking.bicep
│ ├── deploy/
│ │ ├── deploy.bicep
│ │ └── modules/
│ │ └── networking.bicep
│ └── main.bicep
└── workload-genai/
├── README.md
├── bicep/
│ ├── README.md
│ ├── apim-policies/
│ │ ├── api-specs/
│ │ │ └── openapi-spec.json
│ │ ├── apiManagement.bicep
│ │ └── load-balancing/
│ │ ├── backends.bicep
│ │ └── lb-pool.bicep
│ ├── eventhub/
│ │ └── eventHub.bicep
│ ├── main.bicep
│ └── openai/
│ └── openai.bicep
├── policies/
│ ├── fragments/
│ │ ├── load-balancing/
│ │ │ ├── README.md
│ │ │ └── simple-priority-weighted.xml
│ │ ├── manage-spikes-with-payg/
│ │ │ ├── README.md
│ │ │ └── retry-with-payg.xml
│ │ ├── rate-limiting/
│ │ │ ├── README.md
│ │ │ ├── adaptive-rate-limiting.xml
│ │ │ ├── rate-limiting-by-tokens.xml
│ │ │ └── rate-limiting-workaround.xml
│ │ └── usage-tracking/
│ │ ├── README.md
│ │ ├── usage-tracking-with-appinsights.xml
│ │ └── usage-tracking-with-eventhub.xml
│ ├── genai-policy.xml
│ └── multi-tenancy/
│ ├── README.md
│ ├── multi-tenant-product1-policy.xml
│ └── multi-tenant-product2-policy.xml
└── terraform/
├── README.md
├── backend.tf.sample
├── main.tf
├── modules/
│ ├── apim_policies/
│ │ ├── api-specs/
│ │ │ └── openapi-spec.json
│ │ ├── apimanagement.tf
│ │ ├── backends/
│ │ │ ├── backends.tf
│ │ │ └── providers.tf
│ │ ├── lb_pool/
│ │ │ ├── lb-pool.tf
│ │ │ └── providers.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── eventhub/
│ │ ├── eventhub.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── openai/
│ │ ├── openai.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── private_dns_zone/
│ │ ├── outputs.tf
│ │ ├── privatednszone.tf
│ │ └── variables.tf
│ └── private_endpoint/
│ ├── outputs.tf
│ ├── privateendpoint.tf
│ └── variables.tf
├── provider.tf
└── variables.tf
================================================
FILE CONTENTS
================================================
================================================
FILE: .devcontainer/devcontainer.json
================================================
{
"name": "Getting Started",
"image": "mcr.microsoft.com/devcontainers/universal:2",
"hostRequirements": {
"cpus": 4
},
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {
"configureZshAsDefaultShell": true,
"installOhMyZsh": true,
"installOhMyZshConfig": true
},
"ghcr.io/stuartleeks/dev-container-features/shell-history:0": {},
"ghcr.io/devcontainers/features/azure-cli:1": {
"installBicep": true,
"version": "latest"
},
"ghcr.io/stuartleeks/dev-container-features/azure-cli-persistence:0": {},
"ghcr.io/devcontainers/features/terraform:1": {}
},
"waitFor": "onCreateCommand",
"customizations": {
"vscode": {
"extensions": [
"eamodio.gitlens",
"GitHub.copilot",
"Gruntfuggly.todo-tree",
"ionutvmi.path-autocomplete",
"mechatroner.rainbow-csv",
"ms-vsliveshare.vsliveshare",
"redhat.vscode-yaml",
"timonwong.shellcheck",
"GitHub.vscode-pull-request-github",
"humao.rest-client",
"ms-azuretools.vscode-bicep",
"ms-azuretools.vscode-azureterraform",
"azapi-vscode.azapi"
],
"settings": {
"files.insertFinalNewline": true,
"github.copilot.enable": {
"markdown": true
}
}
}
},
"remoteEnv": {
"HOST_PROJECT_PATH": "${localWorkspaceFolder}"
},
"mounts": [
// map host ssh to container
"source=${env:HOME}${env:USERPROFILE}/.ssh,target=/home/codespace/.ssh,type=bind,consistency=cached"
]
}
================================================
FILE: .gitattributes
================================================
# Set bash scripts to use LF line endings regardless of the platform
# used to clone the code
*.sh text eol=lf
================================================
FILE: .github/workflows/lza-validate-multiregion-apim.yml
================================================
# This is a basic workflow to help you get started with Actions
name: LZA-Validation-Multi-Region-APIM
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the "main" branch
push:
pull_request:
branches: [ "main" ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
# Runs a set of commands using the runners shell
- name: Run deploy-apim-baseline script
run: |
echo Performing Validation....
ls -la
cd scenarios/scripts/terraform
cp sample-multi-region.env .env
./deploy-apim-baseline.sh --validate-commit
================================================
FILE: .github/workflows/lza-validate-singleregion-apim.yml
================================================
# This is a basic workflow to help you get started with Actions
name: LZA-Validation-Single-Region-APIM
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the "main" branch
push:
pull_request:
branches: [ "main" ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
# Runs a set of commands using the runners shell
- name: Run deploy-apim-baseline script
run: |
echo Performing Validation....
ls -la
cd scenarios/scripts/terraform
cp sample.env .env
./deploy-apim-baseline.sh --validate-commit
================================================
FILE: .gitignore
================================================
/.vs/ProjectSettings.json
/.vs/slnx.sqlite
deployment/bicep/parameters.local.json
deployment/bicep/localparam*.json
deployment/bicep/localmain.bicep
deployment/bicep/localtestscript.ps1
# Terraform section
# Local .terraform directories
**/.terraform/*
output.json
parameters.json
# .tfstate files
*.tfstate
*.tfstate.*
*.tfplan
# Crash log files
crash.log
crash.*.log
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# Ignore CLI configuration files
.terraformrc
terraform.rc
# Don't copy local lock
*.terraform.lock.hcl
# Azure Functions localsettings file
local.settings.json
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
#Local .terraform directories
**/.terraform/*
.terraform/
# .tfstate files
*.tfstate
*.tfstate.*
# tf plan files
*.plan
# Crash log files
crash.log
# Ignore override files as they are usually used to override resources locally
override.tf
override.tf.json
*_override.tf
*_override.tf.json
**/azuredeploy.parameters.json
scenarios/.env
scenarios/apim-baseline/bicep/parameters.json
# Rest client test files
*.http
*-backend.hcl
scenarios/scripts/terraform/apim-self-signed.crt
scenarios/scripts/terraform/apim-self-signed.key
scenarios/scripts/terraform/tmp-self-signed-cert.conf
scenarios/scripts/terraform/.env
================================================
FILE: .pre-commit-config.yaml
================================================
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.76.0
hooks:
- id: terraform_fmt
- id: terraform_docs
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Microsoft Open Source Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
Resources:
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
================================================
FILE: README.md
================================================
# Azure API Management Landing Zone Accelerator
Azure 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.
## Reference Architecture

## :mag: Design areas
The enterprise architecture is broken down into six different design areas, where you can find the links to each at:
| Design Area|Considerations|Recommendations|
|:--------------:|:--------------:|:--------------:|
| 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)|
| 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)|
| 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)|
| 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)|
| 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)|
| 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)|
## :rocket: Deployment scenarios
This repo contains the Azure landing zone accelerator's reference implementations, all with supporting *Infrastructure as Code* artifacts. The scenarios covered are:
### :arrow_forward: [Scenario 1: Azure API Management - Secure Baseline](scenarios/apim-baseline/README.md)
Deploys APIM with a secure baseline configuration with no backends and a sample API.
### :arrow_forward: [Scenario 2: Azure API Management - Function Backend](scenarios/workload-functions/README.md)
On top of the secure baseline, deploys a private Azure function as a backend and provision APIs in APIM to access the function.
### :arrow_forward: [Scenario 3: Azure API Management - Gen AI Backend](scenarios/workload-genai/README.md)
On 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)
*More reference implementation scenarios will be added as they become available.*
### Supported Regions
Some 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.
```shell
australiacentral, australiaeast, australiasoutheast, brazilsouth, eastasia, francecentral, germanywestcentral, koreacentral, northeurope, southeastasia, southcentralus, uksouth, ukwest, westeurope, westus2, westus3
```
## Got a feedback
Please leverage issues if you have any feedback or request on how we can improve on this repository.
---
## Data Collection
The 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.
### Telemetry Configuration
Telemetry collection is on by default.
To opt-out, set the variable ENABLE_TELEMETRY to `false` in [.env](./scenarios/.env) file.
---
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Trademarks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
trademarks or logos is subject to and must follow
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
Any use of third-party trademarks or logos are subject to those third-party's policies.
================================================
FILE: SECURITY.md
================================================
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->
# Security
Microsoft 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/).
If 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.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
If 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).
Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
Please 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:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
If 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.
## Preferred Languages
We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->
================================================
FILE: SUPPORT.md
================================================
# Support
## How to file issues and get help
This project uses GitHub Issues to track bugs and feature requests. Please search the existing
issues before filing new issues to avoid duplicates. For new issues, file your bug or
feature request as a new Issue.
## Microsoft Support Policy
Support for this **PROJECT or PRODUCT** is limited to the resources listed above.
================================================
FILE: scenarios/apim-baseline/README.md
================================================
# Scenario 1: Azure API Management - Secure Baseline
This 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.
By the end of this deployment guide, you would have deployed an "internal mode" Azure API Management premium instance.

## Core architecture components
- Azure API Management (Developer SKU)
- Azure Virtual Networks
- Azure Application Gateway (with Web Application Firewall)
- Azure Standard Public IP
- Azure Key Vault
- Azure Private Endpoint
- Azure Private DNS Zones
- Log Analytics Workspace
- Azure Application Insights
## Deploy the reference implementation
This 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.
:arrow_forward: [Bicep-based deployment guide](./bicep/README.md)
:arrow_forward: [Terraform-based deployment guide](./terraform/README.md)
================================================
FILE: scenarios/apim-baseline/bicep/README.md
================================================
# Azure API Management - Secure Baseline [Bicep]
**Note:**
This 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.
This is the Bicep-based deployment guide for [Scenario 1: Azure API Management - Secure Baseline](../README.md).
## Prerequisites
This 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.
- An Azure subscription
- The following resource providers [registered](https://learn.microsoft.com/azure/azure-resource-manager/management/resource-providers-and-types#register-resource-provider):
- `Microsoft.ApiManagement`
- `Microsoft.Network`
- `Microsoft.KeyVault`
- 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).
- Access to Bash command line to run the deployment script.
- 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.
[](https://shell.azure.com)
- JQ command line JSON processor installed
```bash
sudo apt-get install jq
```
## Steps
1. Clone/download this repo locally, or even better fork this repository.
```bash
git clone https://github.com/Azure/apim-landing-zone-accelerator.git
cd apim-landing-zone-accelerator/scenarios/scripts
```
1. Log into Azure from the AZ CLI and select your subscription.
```bash
az login
```
1. Review and update deployment parameters.
Copy the `sample.env` into a new file called `.env` in the same directory.
```bash
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.
**Deployment parameters**
| Name | Description | Default | Example(s) |
| :---- | :---------- | :------ | :--------- |
| `AZURE_LOCATION` | The Azure location to deploy to. | **eastus** | **westus** |
| `RESOURCE_NAME_PREFIX` | A suffix for naming. | **apimdemo** | **appname** |
| `ENVIRONMENT_TAG` | A tag that will be included in the naming. | **dev** | **stage** |
| `APPGATEWAY_FQDN` | The Azure location to deploy to. | **apim.example.com** | **my.org.com** |
| `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** |
| `CERT_PWD` | The password for the pfx certificate. Only required if CERT_TYPE is custom. | **N/A** | **password123** |
| `RANDOM_IDENTIFIER` | Optional 3 character random string to ensure deployments are unique. Automatically assigned if not provided | **abc** | **pqr** |
1. Deploy the reference implementation.
Run the following command to deploy the APIM baseline
```bash
./scripts/bicep/deploy-apim-baseline.sh
```
Test the echo api using the generated command from the output
## Troubleshooting
If 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:
```bash
sed -i -e 's/\r$//' deploy-apim-baseline.sh
```
================================================
FILE: scenarios/apim-baseline/bicep/apim/apim.bicep
================================================
targetScope='resourceGroup'
/*
* Input parameters
*/
@description('The name of the API Management resource to be created.')
param apimName string
@description('The subnet resource id to use for APIM.')
@minLength(1)
param apimSubnetId string
@description('The email address of the publisher of the APIM resource.')
@minLength(1)
param publisherEmail string = 'apim@contoso.com'
@description('Company name of the publisher of the APIM resource.')
@minLength(1)
param publisherName string = 'Contoso'
@description('The pricing tier of the APIM resource.')
param skuName string = 'Developer'
@description('The instance size of the APIM resource.')
param capacity int = 1
@description('Location for Azure resources.')
param location string = resourceGroup().location
param appInsightsName string
param appInsightsId string
param appInsightsInstrumentationKey string
param keyVaultName string
param keyVaultResourceGroupName string
param vnetName string
param networkingResourceGroupName string
param apimRG string
var echoSubscriptionKey = guid('echoPrimaryKey')
/*
* Resources
*/
var apimIdentityName = 'identity-${apimName}'
resource apimIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
name: apimIdentityName
location: location
}
resource apimName_resource 'Microsoft.ApiManagement/service@2020-12-01' = {
name: apimName
location: location
sku:{
capacity: capacity
name: skuName
}
identity: {
type:'UserAssigned'
userAssignedIdentities: {
'${apimIdentity.id}': {}
}
}
properties:{
virtualNetworkType: 'Internal'
publisherEmail: publisherEmail
publisherName: publisherName
virtualNetworkConfiguration: {
subnetResourceId: apimSubnetId
}
apiVersionConstraint: {
minApiVersion: '2019-12-01'
}
}
}
resource echoSubscription 'Microsoft.ApiManagement/service/subscriptions@2020-12-01' = {
parent: apimName_resource
name: 'Echo'
properties: {
displayName: 'Echo'
scope: '/products/starter'
primaryKey: echoSubscriptionKey
}
}
resource apimName_appInsightsLogger_resource 'Microsoft.ApiManagement/service/loggers@2021-08-01' = {
parent: apimName_resource
name: appInsightsName
properties: {
loggerType: 'applicationInsights'
resourceId: appInsightsId
credentials: {
instrumentationKey: appInsightsInstrumentationKey
}
}
}
resource apimName_applicationinsights 'Microsoft.ApiManagement/service/diagnostics@2021-08-01' = {
parent: apimName_resource
name: 'applicationinsights'
properties: {
loggerId: apimName_appInsightsLogger_resource.id
alwaysLog: 'allErrors'
sampling: {
percentage: 100
samplingType: 'fixed'
}
metrics: true
}
}
module kvaccess './modules/kvaccess.bicep' = {
name: 'kvaccess'
scope: resourceGroup(keyVaultResourceGroupName)
params: {
managedIdentity: apimIdentity
keyVaultName: keyVaultName
}
}
//Creation of private DNS zones
module dnsZoneModule './modules/dnsrecords.bicep' = {
name: 'apimDnsRecordsDeploy'
scope: resourceGroup(networkingResourceGroupName)
dependsOn: [
apimName_resource
]
params: {
vnetName: vnetName
apimName: apimName
apimRG: apimRG
networkingResourceGroupName: networkingResourceGroupName
}
}
output apimStarterSubscriptionKey string = echoSubscriptionKey
output apimIdentityName string = apimIdentityName
================================================
FILE: scenarios/apim-baseline/bicep/apim/modules/dnsrecords.bicep
================================================
param vnetName string
param networkingResourceGroupName string
param apimName string
param apimRG string
resource apim 'Microsoft.ApiManagement/service@2020-12-01' existing = {
name: apimName
scope: resourceGroup(apimRG)
}
module dnsZone '../../shared/modules/dnszone.bicep' = {
name: 'apimDnsZoneDeploy'
params: {
vnetName: vnetName
networkingResourceGroupName: networkingResourceGroupName
domain: '${apimName}.azure-api.net'
}
}
resource apimDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = {
name: '${apimName}.azure-api.net'
}
resource gatewayRecord 'Microsoft.Network/privateDnsZones/A@2020-06-01' = {
parent: apimDnsZone
name: '@'
dependsOn: [
apim
dnsZone
]
properties: {
aRecords: [
{
ipv4Address: apim.properties.privateIPAddresses[0]
}
]
ttl: 36000
}
}
resource developerRecord 'Microsoft.Network/privateDnsZones/A@2020-06-01' = {
parent: apimDnsZone
name: 'developer'
dependsOn: [
apim
dnsZone
]
properties: {
aRecords: [
{
ipv4Address: apim.properties.privateIPAddresses[0]
}
]
ttl: 36000
}
}
================================================
FILE: scenarios/apim-baseline/bicep/apim/modules/kvaccess.bicep
================================================
param keyVaultName string
param managedIdentity object
resource accessPolicyGrant 'Microsoft.KeyVault/vaults/accessPolicies@2019-09-01' = {
name: '${keyVaultName}/add'
properties: {
accessPolicies: [
{
objectId: managedIdentity.properties.principalId
tenantId: managedIdentity.properties.tenantId
permissions: {
secrets: [
'get'
'list'
]
certificates: [
'get'
'list'
]
}
}
]
}
}
================================================
FILE: scenarios/apim-baseline/bicep/gateway/appgw.bicep
================================================
/*
* Input parameters
*/
@description('The name of the Application Gateawy to be created.')
param appGatewayName string
@description('The FQDN of the Application Gateawy.Must match the TLS Certificate.')
param appGatewayFQDN string
@description('The location of the Application Gateawy to be created')
param location string = resourceGroup().location
@description('The subnet resource id to use for Application Gateway.')
param appGatewaySubnetId string
@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.')
param appGatewayCertType string
@description('The backend URL of the APIM.')
param primaryBackendEndFQDN string
@description('The Url for the APIM Health Probe.')
param probeUrl string = '/status-0123456789abcdef'
param appGatewayPublicIpName string
param keyVaultName string
param keyVaultResourceGroupName string
param deploymentIdentityName string
param deploymentSubnetId string
param deploymentStorageName string
param certKey string
param certData string
var appGatewayIdentityId = 'identity-${appGatewayName}'
var appGatewayFirewallPolicy = 'waf-${appGatewayName}'
resource appGatewayIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
name: appGatewayIdentityId
location: location
}
module certificate './modules/certificate.bicep' = {
name: 'certificate'
scope: resourceGroup(keyVaultResourceGroupName)
params: {
managedIdentity: appGatewayIdentity
deploymentIdentityName: deploymentIdentityName
deploymentSubnetId: deploymentSubnetId
deploymentStorageName: deploymentStorageName
keyVaultName: keyVaultName
location: location
appGatewayFQDN: appGatewayFQDN
appGatewayCertType: appGatewayCertType
certKey: certKey
certData: certData
}
}
resource appGatewayPublicIPAddress 'Microsoft.Network/publicIPAddresses@2019-09-01' existing = {
name: appGatewayPublicIpName
}
resource appgw_waf_Pol 'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies@2021-08-01' = {
name: appGatewayFirewallPolicy
location: location
properties: {
policySettings: {
requestBodyCheck: true
maxRequestBodySizeInKb: 128
fileUploadLimitInMb: 100
state: 'Enabled'
mode: 'detection'
}
managedRules: {
managedRuleSets: [
{
ruleSetType: 'OWASP'
ruleSetVersion: '3.2'
}
]
}
}
}
resource appGatewayName_resource 'Microsoft.Network/applicationGateways@2019-09-01' = {
name: appGatewayName
location: location
dependsOn: [
certificate
]
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${appGatewayIdentity.id}': {}
}
}
properties: {
sku: {
name: 'WAF_v2'
tier: 'WAF_v2'
}
gatewayIPConfigurations: [
{
name: 'appGatewayIpConfig'
properties: {
subnet: {
id: appGatewaySubnetId
}
}
}
]
sslCertificates: [
{
name: appGatewayFQDN
properties: {
keyVaultSecretId: certificate.outputs.secretUri
}
}
]
sslPolicy: {
minProtocolVersion: 'TLSv1_2'
policyType: 'Custom'
cipherSuites: [
'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256'
'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384'
'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'
'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256'
'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384'
'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256'
'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384'
]
}
trustedRootCertificates: []
frontendIPConfigurations: [
{
name: 'appGwPublicFrontendIp'
properties: {
privateIPAllocationMethod: 'Dynamic'
publicIPAddress: {
id: appGatewayPublicIPAddress.id
}
}
}
]
frontendPorts: [
{
name: 'port_443'
properties: {
port: 443
}
}
]
backendAddressPools: [
{
name: 'apim'
properties: {
backendAddresses: [
{
fqdn: primaryBackendEndFQDN
}
]
}
}
{
name: 'sink-hole'
properties: {
backendAddresses: []
}
}
]
backendHttpSettingsCollection: [
{
name: 'apim-demo-apis-https'
properties: {
port: 443
protocol: 'Https'
cookieBasedAffinity: 'Disabled'
hostName: primaryBackendEndFQDN
pickHostNameFromBackendAddress: false
requestTimeout: 20
probe: {
id: resourceId('Microsoft.Network/applicationGateways/probes', appGatewayName, 'apim-demo-apis-https')
}
}
}
]
httpListeners: [
{
name: 'apim-demo-apis-https'
properties: {
frontendIPConfiguration: {
id: resourceId(
'Microsoft.Network/applicationGateways/frontendIPConfigurations',
appGatewayName,
'appGwPublicFrontendIp'
)
}
frontendPort: {
id: resourceId('Microsoft.Network/applicationGateways/frontendPorts', appGatewayName, 'port_443')
}
protocol: 'Https'
sslCertificate: {
id: resourceId('Microsoft.Network/applicationGateways/sslCertificates', appGatewayName, appGatewayFQDN)
}
hostnames: [
appGatewayFQDN
]
requireServerNameIndication: false
}
}
]
urlPathMaps: [
{
name: 'urlPathMapApim'
properties: {
defaultBackendAddressPool: {
id: resourceId(
'Microsoft.Network/applicationGateways/backendAddressPools',
appGatewayName,
'apim'
)
}
defaultBackendHttpSettings: {
id: resourceId(
'Microsoft.Network/applicationGateways/backendHttpSettingsCollection',
appGatewayName,
'apim-demo-apis-https'
)
}
pathRules: [
{
name: 'echo-api'
properties: {
paths: [
'/echo/*'
]
backendAddressPool: {
id: resourceId(
'Microsoft.Network/applicationGateways/backendAddressPools',
appGatewayName,
'apim'
)
}
backendHttpSettings: {
id: resourceId(
'Microsoft.Network/applicationGateways/backendHttpSettingsCollection',
appGatewayName,
'apim-demo-apis-https'
)
}
}
}
{
name: 'hello-api'
properties: {
paths: [
'/hello*'
]
backendAddressPool: {
id: resourceId(
'Microsoft.Network/applicationGateways/backendAddressPools',
appGatewayName,
'apim'
)
}
backendHttpSettings: {
id: resourceId(
'Microsoft.Network/applicationGateways/backendHttpSettingsCollection',
appGatewayName,
'apim-demo-apis-https'
)
}
}
}
{
name: 'openai-api'
properties: {
paths: [
'/openai/*'
]
backendAddressPool: {
id: resourceId(
'Microsoft.Network/applicationGateways/backendAddressPools',
appGatewayName,
'apim'
)
}
backendHttpSettings: {
id: resourceId(
'Microsoft.Network/applicationGateways/backendHttpSettingsCollection',
appGatewayName,
'apim-demo-apis-https'
)
}
}
}
{
name: 'default'
properties: {
paths: [
'/*'
]
backendAddressPool: {
id: resourceId(
'Microsoft.Network/applicationGateways/backendAddressPools',
appGatewayName,
'sink-hole'
)
}
backendHttpSettings: {
id: resourceId(
'Microsoft.Network/applicationGateways/backendHttpSettingsCollection',
appGatewayName,
'apim-demo-apis-https'
)
}
}
}
]
}
}
]
requestRoutingRules: [
{
name: 'apim-demo-apis'
properties: {
ruleType: 'PathBasedRouting'
priority: 100
urlPathMap: {
id: resourceId('Microsoft.Network/applicationGateways/urlPathMaps', appGatewayName, 'urlPathMapApim')
}
httpListener: {
id: resourceId(
'Microsoft.Network/applicationGateways/httpListeners',
appGatewayName,
'apim-demo-apis-https'
)
}
}
}
]
probes: [
{
name: 'apim-demo-apis-https'
properties: {
protocol: 'Https'
host: primaryBackendEndFQDN
path: probeUrl
interval: 30
timeout: 30
unhealthyThreshold: 3
pickHostNameFromBackendHttpSettings: false
minServers: 0
match: {
statusCodes: [
'200-399'
]
}
}
}
]
rewriteRuleSets: []
redirectConfigurations: []
firewallPolicy: {
id: appgw_waf_Pol.id
}
enableHttp2: true
autoscaleConfiguration: {
minCapacity: 2
maxCapacity: 3
}
}
}
output appGatewayPublicIpAddress string = appGatewayPublicIPAddress.properties.ipAddress
================================================
FILE: scenarios/apim-baseline/bicep/gateway/modules/certificate.bicep
================================================
param keyVaultName string
param managedIdentity object
param location string
param appGatewayFQDN string
param certKey string
param certData string
param appGatewayCertType string
param deploymentIdentityName string
param deploymentSubnetId string
param deploymentStorageName string
var secretName = replace(appGatewayFQDN,'.', '-')
var subjectName='CN=${appGatewayFQDN}'
var certPwd = appGatewayCertType == 'selfsigned' ? 'null' : certKey
var certDataString = appGatewayCertType == 'selfsigned' ? 'null' : certData
resource deploymentIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' existing = {
name: deploymentIdentityName
}
resource accessPolicyGrantForCertificate 'Microsoft.KeyVault/vaults/accessPolicies@2019-09-01' = {
name: '${keyVaultName}/add'
properties: {
accessPolicies: [
{
objectId: managedIdentity.properties.principalId
tenantId: managedIdentity.properties.tenantId
permissions: {
secrets: [
'get'
'list'
]
certificates: [
'import'
'get'
'list'
'update'
'create'
]
}
}
{
objectId: deploymentIdentity.properties.principalId
tenantId: deploymentIdentity.properties.tenantId
permissions: {
secrets: [
'get'
'list'
]
certificates: [
'import'
'get'
'list'
'update'
'create'
]
}
}
]
}
}
resource appGatewayCertificate 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
name: '${secretName}-certificate'
dependsOn: [
accessPolicyGrantForCertificate
]
location: location
identity: {
type: 'userAssigned'
userAssignedIdentities: {
'${deploymentIdentity.id}': {}
}
}
kind: 'AzurePowerShell'
properties: {
azPowerShellVersion: '6.6'
storageAccountSettings: {
storageAccountName: deploymentStorageName
}
containerSettings: {
subnetIds: [
{
id: deploymentSubnetId
}
]
}
arguments: ' -vaultName ${keyVaultName} -certificateName ${secretName} -subjectName ${subjectName} -certPwd ${certPwd} -certDataString ${certDataString} -certType ${appGatewayCertType}'
scriptContent: '''
param(
[string] [Parameter(Mandatory=$true)] $vaultName,
[string] [Parameter(Mandatory=$true)] $certificateName,
[string] [Parameter(Mandatory=$true)] $subjectName,
[string] [Parameter(Mandatory=$true)] $certPwd,
[string] [Parameter(Mandatory=$true)] $certDataString,
[string] [Parameter(Mandatory=$true)] $certType
)
$ErrorActionPreference = 'Stop'
$DeploymentScriptOutputs = @{}
if ($certType -eq 'selfsigned') {
$policy = New-AzKeyVaultCertificatePolicy -SubjectName $subjectName -IssuerName Self -ValidityInMonths 12 -Verbose
# private key is added as a secret that can be retrieved in the ARM template
Add-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName -CertificatePolicy $policy -Verbose
$newCert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName
# it takes a few seconds for KeyVault to finish
$tries = 0
do {
Write-Host 'Waiting for certificate creation completion...'
Start-Sleep -Seconds 10
$operation = Get-AzKeyVaultCertificateOperation -VaultName $vaultName -Name $certificateName
$tries++
if ($operation.Status -eq 'failed')
{
throw 'Creating certificate $certificateName in vault $vaultName failed with error $($operation.ErrorMessage)'
}
if ($tries -gt 120)
{
throw 'Timed out waiting for creation of certificate $certificateName in vault $vaultName'
}
} while ($operation.Status -ne 'completed')
}
else {
$ss = Convertto-SecureString -String $certPwd -AsPlainText -Force;
Import-AzKeyVaultCertificate -Name $certificateName -VaultName $vaultName -CertificateString $certDataString -Password $ss
}
'''
retentionInterval: 'P1D'
}
}
module appGatewaySecretsUri 'certificateSecret.bicep' = {
name: '${secretName}-certificate'
dependsOn: [
appGatewayCertificate
]
params: {
keyVaultName: keyVaultName
secretName: secretName
}
}
output secretUri string = appGatewaySecretsUri.outputs.secretUri
================================================
FILE: scenarios/apim-baseline/bicep/gateway/modules/certificateSecret.bicep
================================================
param keyVaultName string
param secretName string
resource keyVaultCertificate 'Microsoft.KeyVault/vaults/secrets@2021-06-01-preview' existing = {
name: '${keyVaultName}/${secretName}'
}
output secretUri string = keyVaultCertificate.properties.secretUriWithVersion
================================================
FILE: scenarios/apim-baseline/bicep/main.bicep
================================================
targetScope = 'subscription'
// Parameters
@description('A short name for the workload being deployed alphanumberic only')
@maxLength(8)
param workloadName string
@description('The environment for which the deployment is being executed')
@allowed([
'dev'
'uat'
'prod'
'dr'
])
param environment string
param identifier string
@description('The FQDN for the Application Gateway. Example - api.contoso.com.')
param appGatewayFqdn string
@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')
param certKey string = 'placeholder'
param certData string = 'placeholder'
@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')
param appGatewayCertType string
param location string = deployment().location
@description('Enable sending usage and telemetry feedback to Microsoft.')
param enableTelemetry bool = true
var telemetryId = 'ab1e5729-7452-41b2-9fbb-945cc51d9cd0-${location}-apimsb-main'
// Variables
var resourceSuffix = '${workloadName}-${environment}-${location}-${identifier}'
var networkingResourceGroupName = 'rg-networking-${resourceSuffix}'
var sharedResourceGroupName = 'rg-shared-${resourceSuffix}'
var apimResourceGroupName = 'rg-apim-${resourceSuffix}'
// Resource Names
var apimName = 'apim-${resourceSuffix}'
var appGatewayName = 'appgw-${resourceSuffix}'
resource networkingRG 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: networkingResourceGroupName
location: location
}
resource sharedRG 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: sharedResourceGroupName
location: location
}
resource apimRG 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: apimResourceGroupName
location: location
}
module networking './networking/networking.bicep' = {
name: 'networkingresources'
scope: resourceGroup(networkingRG.name)
params: {
location: location
resourceSuffix: resourceSuffix
}
}
module shared './shared/shared.bicep' = {
dependsOn: [
networking
]
name: 'sharedresources'
scope: resourceGroup(sharedRG.name)
params: {
workloadName: workloadName
environment: environment
identifier: identifier
location: location
resourceGroupName: sharedRG.name
resourceSuffix: resourceSuffix
vnetName: networking.outputs.apimCSVNetName
privateEndpointSubnetid: networking.outputs.privateEndpointSubnetid
networkingResourceGroupName: networkingRG.name
deploymentSubnetId: networking.outputs.deploymentSubnetId
}
}
module apimModule 'apim/apim.bicep' = {
name: 'apimDeploy'
scope: resourceGroup(apimRG.name)
params: {
apimName: apimName
apimSubnetId: networking.outputs.apimSubnetid
location: location
appInsightsName: shared.outputs.appInsightsName
appInsightsId: shared.outputs.appInsightsId
appInsightsInstrumentationKey: shared.outputs.appInsightsInstrumentationKey
keyVaultName: shared.outputs.keyVaultName
keyVaultResourceGroupName: sharedRG.name
networkingResourceGroupName: networkingRG.name
apimRG: apimRG.name
vnetName: networking.outputs.apimCSVNetName
}
}
module appgwModule 'gateway/appgw.bicep' = {
name: 'appgwDeploy'
scope: resourceGroup(networkingRG.name)
dependsOn: [
apimModule
]
params: {
appGatewayName: appGatewayName
appGatewayFQDN: appGatewayFqdn
location: location
appGatewaySubnetId: networking.outputs.appGatewaySubnetid
primaryBackendEndFQDN: '${apimName}.azure-api.net'
keyVaultName: shared.outputs.keyVaultName
keyVaultResourceGroupName: sharedRG.name
appGatewayCertType: appGatewayCertType
certKey: certKey
certData: certData
appGatewayPublicIpName: networking.outputs.appGatewayPublicIpName
deploymentIdentityName: shared.outputs.deploymentIdentityName
deploymentSubnetId: networking.outputs.deploymentSubnetId
deploymentStorageName: shared.outputs.deploymentStorageName
}
}
@description('Microsoft telemetry deployment.')
#disable-next-line no-deployments-resources
resource telemetrydeployment 'Microsoft.Resources/deployments@2021-04-01' = if (enableTelemetry) {
location: location
name: telemetryId
properties: {
mode: 'Incremental'
template: {
'$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#'
contentVersion: '1.0.0.0'
resources: {}
}
}
}
output resourceSuffix string = resourceSuffix
output networkingResourceGroupName string = networkingResourceGroupName
output sharedResourceGroupName string = sharedResourceGroupName
output apimResourceGroupName string = apimResourceGroupName
output apimName string = apimName
output apimIdentityName string = apimModule.outputs.apimIdentityName
output vnetId string = networking.outputs.apimCSVNetId
output vnetName string = networking.outputs.apimCSVNetName
output privateEndpointSubnetid string = networking.outputs.privateEndpointSubnetid
output deploymentIdentityName string = shared.outputs.deploymentIdentityName
output deploymentSubnetId string = networking.outputs.deploymentSubnetId
output deploymentStorageName string = shared.outputs.deploymentStorageName
output keyVaultName string = shared.outputs.keyVaultName
output appGatewayName string = appGatewayName
output appGatewayPublicIpAddress string = appgwModule.outputs.appGatewayPublicIpAddress
output apimStarterSubscriptionKey string = apimModule.outputs.apimStarterSubscriptionKey
================================================
FILE: scenarios/apim-baseline/bicep/networking/networking.bicep
================================================
param apimCSVNetNameAddressPrefix string = '10.2.0.0/16'
param appGatewayAddressPrefix string = '10.2.4.0/24'
param apimAddressPrefix string = '10.2.7.0/24'
param privateEndpointAddressPrefix string = '10.2.5.0/24'
param deploymentAddressPrefix string = '10.2.8.0/24'
param location string
@description('Standardized suffix text to be added to resource names')
param resourceSuffix string
// Variables
var owner = 'APIM Const Set'
var apimCSVNetName = 'vnet-apim-cs-${resourceSuffix}'
var appGatewaySubnetName = 'snet-apgw-${resourceSuffix}'
var apimSubnetName = 'snet-apim-${resourceSuffix}'
var appGatewaySNNSG = 'nsg-apgw-${resourceSuffix}'
var apimSNNSG = 'nsg-apim-${resourceSuffix}'
var privateEndpointSubnetName = 'snet-prep-${resourceSuffix}'
var privateEndpointSNNSG = 'nsg-prep-${resourceSuffix}'
var deploymentSubnetName = 'snet-deploy-${resourceSuffix}'
var appGatewayPublicIpName = 'pip-appgw-${resourceSuffix}'
// Resources - VNet - SubNets
resource vnetApimCs 'Microsoft.Network/virtualNetworks@2021-02-01' = {
name: apimCSVNetName
location: location
tags: {
Owner: owner
}
properties: {
addressSpace: {
addressPrefixes: [
apimCSVNetNameAddressPrefix
]
}
enableVmProtection: false
enableDdosProtection: false
subnets: [
{
name: appGatewaySubnetName
properties: {
addressPrefix: appGatewayAddressPrefix
networkSecurityGroup: {
id: appGatewayNSG.id
}
}
}
{
name: apimSubnetName
properties: {
addressPrefix: apimAddressPrefix
networkSecurityGroup: {
id: apimNSG.id
}
}
}
{
name: privateEndpointSubnetName
properties: {
addressPrefix: privateEndpointAddressPrefix
networkSecurityGroup: {
id: privateEndpointNSG.id
}
privateEndpointNetworkPolicies: 'Disabled'
}
}
{
name: deploymentSubnetName
properties: {
addressPrefix: deploymentAddressPrefix
serviceEndpoints: [
{
service: 'Microsoft.Storage'
}
]
delegations: [
{
name: 'Microsoft.ContainerInstance.containerGroups'
properties: {
serviceName: 'Microsoft.ContainerInstance/containerGroups'
}
}
]
}
}
]
}
}
// Network Security Groups (NSG)
resource appGatewayNSG 'Microsoft.Network/networkSecurityGroups@2020-06-01' = {
name: appGatewaySNNSG
location: location
properties: {
securityRules: [
{
name: 'AllowHealthProbes'
properties: {
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '65200-65535'
sourceAddressPrefix: 'GatewayManager'
destinationAddressPrefix: '*'
access: 'Allow'
priority: 100
direction: 'Inbound'
}
}
{
name: 'AllowClientTrafficToSubnet'
properties: {
protocol: 'Tcp'
sourcePortRange: '*'
destinationPortRanges: ['80', '443']
sourceAddressPrefix: '*'
destinationAddressPrefix: appGatewayAddressPrefix
access: 'Allow'
priority: 110
direction: 'Inbound'
}
}
{
name: 'AllowClientTrafficToFrontendIP'
properties: {
protocol: 'Tcp'
sourcePortRange: '*'
destinationPortRanges: ['80', '443']
sourceAddressPrefix: '*'
destinationAddressPrefix: '${pipAppGw.properties.ipAddress}/32'
access: 'Allow'
priority: 111
direction: 'Inbound'
}
}
{
name: 'AllowAzureLoadBalancer'
properties: {
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '*'
sourceAddressPrefix: 'AzureLoadBalancer'
destinationAddressPrefix: '*'
access: 'Allow'
priority: 120
direction: 'Inbound'
}
}
]
}
}
resource apimNSG 'Microsoft.Network/networkSecurityGroups@2020-06-01' = {
name: apimSNNSG
location: location
properties: {
securityRules: [
{
name: 'AllowApimManagement'
properties: {
priority: 2000
sourceAddressPrefix: 'ApiManagement'
protocol: 'Tcp'
destinationPortRange: '3443'
access: 'Allow'
direction: 'Inbound'
sourcePortRange: '*'
destinationAddressPrefix: 'VirtualNetwork'
}
}
{
name: 'AllowAzureLoadBalancer'
properties: {
priority: 2010
sourceAddressPrefix: 'AzureLoadBalancer'
protocol: 'Tcp'
destinationPortRange: '6390'
access: 'Allow'
direction: 'Inbound'
sourcePortRange: '*'
destinationAddressPrefix: 'VirtualNetwork'
}
}
{
name: 'AllowAzureTrafficManager'
properties: {
priority: 2020
sourceAddressPrefix: 'AzureTrafficManager'
protocol: 'Tcp'
destinationPortRange: '443'
access: 'Allow'
direction: 'Inbound'
sourcePortRange: '*'
destinationAddressPrefix: 'VirtualNetwork'
}
}
{
name: 'AllowStorage'
properties: {
priority: 2000
sourceAddressPrefix: 'VirtualNetwork'
protocol: 'Tcp'
destinationPortRange: '443'
access: 'Allow'
direction: 'Outbound'
sourcePortRange: '*'
destinationAddressPrefix: 'Storage'
}
}
{
name: 'AllowSql'
properties: {
priority: 2010
sourceAddressPrefix: 'VirtualNetwork'
protocol: 'Tcp'
destinationPortRange: '1433'
access: 'Allow'
direction: 'Outbound'
sourcePortRange: '*'
destinationAddressPrefix: 'SQL'
}
}
{
name: 'AllowKeyVault'
properties: {
priority: 2020
sourceAddressPrefix: 'VirtualNetwork'
protocol: 'Tcp'
destinationPortRange: '443'
access: 'Allow'
direction: 'Outbound'
sourcePortRange: '*'
destinationAddressPrefix: 'AzureKeyVault'
}
}
{
name: 'AllowMonitor'
properties: {
priority: 2030
sourceAddressPrefix: 'VirtualNetwork'
protocol: 'Tcp'
destinationPortRanges: ['1886', '443']
access: 'Allow'
direction: 'Outbound'
sourcePortRange: '*'
destinationAddressPrefix: 'AzureMonitor'
}
}
]
}
}
resource privateEndpointNSG 'Microsoft.Network/networkSecurityGroups@2020-06-01' = {
name: privateEndpointSNNSG
location: location
properties: {
securityRules: []
}
}
// Public IP
resource pipAppGw 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
name: appGatewayPublicIpName
location: location
sku: {
name: 'Standard'
}
zones: ['1', '2', '3']
properties: {
publicIPAddressVersion: 'IPv4'
publicIPAllocationMethod: 'Static'
}
}
// Output section
output apimCSVNetName string = apimCSVNetName
output apimCSVNetId string = vnetApimCs.id
output appGatewaySubnetName string = appGatewaySubnetName
output apimSubnetName string = apimSubnetName
output privateEndpointSubnetName string = privateEndpointSubnetName
output appGatewaySubnetid string = '${vnetApimCs.id}/subnets/${appGatewaySubnetName}'
output apimSubnetid string = '${vnetApimCs.id}/subnets/${apimSubnetName}'
output privateEndpointSubnetid string = '${vnetApimCs.id}/subnets/${privateEndpointSubnetName}'
output deploymentSubnetId string = '${vnetApimCs.id}/subnets/${deploymentSubnetName}'
output deploymentSubnetName string = deploymentSubnetName
output publicIpAppGw string = pipAppGw.id
output appGatewayPublicIpName string = appGatewayPublicIpName
================================================
FILE: scenarios/apim-baseline/bicep/shared/modules/azmon.bicep
================================================
targetScope='resourceGroup'
// Parameters
@description('Azure location to which the resources are to be deployed')
param location string
@description('Standardized suffix text to be added to resource names')
param resourceSuffix string
// Variables
var appInsightsName = 'appi-${resourceSuffix}'
var logAnalyticsWorkspaceName = 'log-${resourceSuffix}'
// Resources
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
name: logAnalyticsWorkspaceName
location: location
properties: any({
retentionInDays: 30
features: {
searchVersion: 1
}
sku: {
name: 'PerGB2018'
}
})
}
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: appInsightsName
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalyticsWorkspace.id
}
}
output appInsightsConnectionString string = appInsights.properties.ConnectionString
output appInsightsName string = appInsights.name
output appInsightsId string = appInsights.id
output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
================================================
FILE: scenarios/apim-baseline/bicep/shared/modules/dnszone.bicep
================================================
param vnetName string
param networkingResourceGroupName string
param domain string
resource vnet 'Microsoft.Network/virtualNetworks@2021-02-01' existing = {
name: vnetName
scope: resourceGroup(networkingResourceGroupName)
}
resource dnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: domain
location: 'global'
}
resource vnetLinks 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
name: vnetName
parent: dnsZone
location: 'global'
properties: {
virtualNetwork: {
id: vnet.id
}
registrationEnabled: false
}
}
output dnsZoneName string = dnsZone.name
output dnsZoneId string = dnsZone.id
output vnetLinksLink string = vnetLinks.id
================================================
FILE: scenarios/apim-baseline/bicep/shared/modules/privatedeploy.bicep
================================================
param resourceSuffix string
param location string
param deploymentSubnetId string
var userAssignedIdentityName = 'mi-deploy-${resourceSuffix}'
param storageAccountName string = toLower(take(replace('stdep${resourceSuffix}', '-',''), 24))
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
networkAcls: {
bypass: 'AzureServices'
virtualNetworkRules: [
{
id: deploymentSubnetId
action: 'Allow'
state: 'Succeeded'
}
]
defaultAction: 'Deny'
}
}
}
resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: userAssignedIdentityName
location: location
}
resource storageFileDataPrivilegedContributor 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
name: '69566ab7-960f-475b-8e7c-b3118f30c6bd' // Storage File Data Privileged Contributor
scope: tenant()
}
resource roleAssignmentStorage 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: storageAccount
name: guid(storageFileDataPrivilegedContributor.id, userAssignedIdentity.id, storageAccount.id)
properties: {
principalId: userAssignedIdentity.properties.principalId
roleDefinitionId: storageFileDataPrivilegedContributor.id
principalType: 'ServicePrincipal'
}
}
output deploymentIdentityName string = userAssignedIdentityName
output deploymentStorageName string = storageAccountName
================================================
FILE: scenarios/apim-baseline/bicep/shared/modules/privateendpoint.bicep
================================================
param privateEndpointName string
param groupId string
param location string
param vnetName string
param networkingResourceGroupName string
param subnetId string
param serviceResourceId string
param createDnsZone bool = true
param domain string
resource privateEndpoint 'Microsoft.Network/privateEndpoints@2021-03-01' = {
name: privateEndpointName
location: location
properties: {
subnet: {
id: subnetId
}
privateLinkServiceConnections: [
{
name: privateEndpointName
properties: {
privateLinkServiceId: serviceResourceId
groupIds: [
groupId
]
}
}
]
}
}
module dnsZoneNew './dnszone.bicep' = if (createDnsZone == true) {
name: take('${replace(domain, '.', '-')}-deploy', 64)
params: {
vnetName: vnetName
networkingResourceGroupName: networkingResourceGroupName
domain: domain
}
dependsOn: [
privateEndpoint
]
}
resource dnsZone 'Microsoft.Network/privateDnsZones@2018-09-01' existing = if (createDnsZone == false) {
name: domain
}
var dnsZoneName = (createDnsZone == true) ? dnsZoneNew.outputs.dnsZoneName : dnsZone.name
var dnsZoneId = (createDnsZone == true) ? dnsZoneNew.outputs.dnsZoneId : dnsZone.id
resource dnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2020-03-01' = {
name: 'default'
parent: privateEndpoint
properties: {
privateDnsZoneConfigs: [
{
name: dnsZoneName
properties: {
privateDnsZoneId: dnsZoneId
}
}
]
}
}
output privateEndpointId string = privateEndpoint.id
output dnsZoneGroupId string = dnsZoneGroup.id
================================================
FILE: scenarios/apim-baseline/bicep/shared/shared.bicep
================================================
targetScope='resourceGroup'
// Parameters
@description('A short name for the workload being deployed')
param workloadName string
@description('The environment for which the deployment is being executed')
@allowed([
'dev'
'uat'
'prod'
'dr'
])
param environment string
param identifier string
@description('Azure location to which the resources are to be deployed')
param location string
param vnetName string
param privateEndpointSubnetid string
param deploymentSubnetId string
param networkingResourceGroupName string
@description('The name of the shared resource group')
param resourceGroupName string
@description('Standardized suffix text to be added to resource names')
param resourceSuffix string
// Variables - ensure key vault name does not end with '-'
var tempKeyVaultName = take('kv-${workloadName}-${environment}-${location}', 20) // Must be between 3-24 alphanumeric characters
var uniqueKeyVaultName = take('${tempKeyVaultName}-${identifier}', 24)
var keyVaultName = endsWith(uniqueKeyVaultName, '-') ? substring(uniqueKeyVaultName, 0, length(uniqueKeyVaultName) - 1) : uniqueKeyVaultName
var privateEndpoint_keyvault_Name = 'pep-kv-${resourceSuffix}'
// Resources
module appInsights './modules/azmon.bicep' = {
name: 'azmon'
scope: resourceGroup(resourceGroupName)
params: {
location: location
resourceSuffix: resourceSuffix
}
}
resource key_vault 'Microsoft.KeyVault/vaults@2023-07-01' = {
name: keyVaultName
location: location
properties: {
tenantId: subscription().tenantId
sku: {
family: 'A'
name: 'standard'
}
publicNetworkAccess: 'Disabled'
networkAcls: {
bypass: 'AzureServices'
defaultAction: 'Deny'
ipRules: []
virtualNetworkRules: []
}
accessPolicies: [
]
}
}
module keyvaultPrivateEndpoint './modules/privateendpoint.bicep' = {
name: privateEndpoint_keyvault_Name
scope: resourceGroup(networkingResourceGroupName)
params: {
location: location
privateEndpointName: privateEndpoint_keyvault_Name
groupId: 'vault'
serviceResourceId: key_vault.id
vnetName: vnetName
networkingResourceGroupName: networkingResourceGroupName
subnetId: privateEndpointSubnetid
domain:'privatelink.vaultcore.azure.net'
}
}
module deploy './modules/privatedeploy.bicep' = {
name: 'deploymenEssentials'
params: {
location: location
resourceSuffix: resourceSuffix
deploymentSubnetId: deploymentSubnetId
}
}
// Outputs
output appInsightsConnectionString string = appInsights.outputs.appInsightsConnectionString
output appInsightsName string=appInsights.outputs.appInsightsName
output appInsightsId string=appInsights.outputs.appInsightsId
output appInsightsInstrumentationKey string=appInsights.outputs.appInsightsInstrumentationKey
output keyVaultName string = key_vault.name
output deploymentIdentityName string = deploy.outputs.deploymentIdentityName
output deploymentStorageName string = deploy.outputs.deploymentStorageName
================================================
FILE: scenarios/apim-baseline/terraform/README.md
================================================
# Azure API Management - Secure Baseline [Terraform]
- Single Region Deployment
- Optional Multi-Region High Availability
- Optional Zone Redundancy
This is the Terraform-based deployment guide for [Scenario 1: Azure API Management - Secure Baseline](../README.md).
## Sections
- [Prerequisites](#prerequisites)
- [Quick Start](#quick-start)
- [Deployment Customization](#deployment-customization)
- [Deployment Steps](#deployment-steps)
- [Using a Terraform AzureRM backend](#using-a-terraform-azurerm-backend)
- [Troubleshooting](#troubleshooting)
## Prerequisites
This 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.
- An Azure subscription
- The following resource providers [registered](https://learn.microsoft.com/azure/azure-resource-manager/management/resource-providers-and-types#register-resource-provider):
- `Microsoft.ApiManagement`
- `Microsoft.Network`
- `Microsoft.KeyVault`
- 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:
| Role | Level | Why |
| :---- | :---------- | :------ |
| Contributor | Subscription | The plan needs the ability to create resource groups |
| 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. |
- Access to Bash command line to run the deployment script.
- 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.
[](https://shell.azure.com)
- JQ command line JSON processor installed
```bash
sudo apt-get install jq
```
- 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.
## Quick Start
This will yield a working deployment for testing/poc, with no customizations.
```bash
# Login to Azure
az login
# Clone the repo
git clone https://github.com/Azure/apim-landing-zone-accelerator.git
# Change into the scripts/terraform directory
cd apim-landing-zone-accelerator/scenarios/scripts/terraform
# Use the provided sample environment file
cp sample.env .env to create one and edit as needed
# Deploy the APIM baseline
./deploy-apim-baseline.sh
# Follow on screen prompts
```
## Deployment Customization
Review and update deployment parameters.
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.
Copy the [`sample.env`](../../scripts/terraform/sample.env) into a new file called `.env` in the same directory.
```bash
cp sample.env .env
```
**Deployment parameters**
| Name | Description | Default | Example(s) |
| :---- | :---------- | :------ | :--------- |
| `AZURE_LOCATION` | The Azure location to deploy to. | **eastus2** | **eastus2** |
| `MULT_REGION`| Should this deployment extend to a secondary location? | **false** | **true** |
| `AZURE_LOCATION2`| The Azure secondary location to deploy to? | **centralus** | **centralus** |
| `ZONE_REDUNDANT` | Should the deployment be zone redundant. | **false** | **true** |
| `RESOURCE_NAME_PREFIX` | A suffix for naming. | **apimdemo** | **appname** |
| `ENVIRONMENT_TAG` | A tag that will be included in the naming. | **dev** | **stage** |
| `APPGATEWAY_FQDN` | The Azure location to deploy to. | **apim.example.com** | **my.org.com** |
| `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** |
| `CERT_PWD` | The password for the pfx certificate. Only required if CERT_TYPE is custom. | **N/A** | **password123** |
| `RANDOM_IDENTIFIER` | Optional 3 character random string to ensure deployments are unique. Automatically assigned if not provided | **abc** | **pqr** |
### examples `.env` file
- Single region, Single Zone deployment with Developer SKU
```bash
AZURE_LOCATION='eastus2'
RESOURCE_NAME_PREFIX='lzv01'
ENVIRONMENT_TAG='dev'
APPGATEWAY_FQDN='apim.example.com'
CERT_TYPE='selfsigned'
ZONE_REDUNDANT='false'
MULTI_REGION='false'
AZURE_LOCATION2=''
```
- Single region and Zone redundant deployment with Premium SKU
```bash
AZURE_LOCATION='eastus2'
RESOURCE_NAME_PREFIX='lzv01'
ENVIRONMENT_TAG='dev'
APPGATEWAY_FQDN='apim.example.com'
CERT_TYPE='selfsigned'
ZONE_REDUNDANT='true'
MULTI_REGION='false'
```
- Multi-region and Zone Redundant deployment with Premium SKU
```bash
AZURE_LOCATION='eastus2'
RESOURCE_NAME_PREFIX='lzv01'
ENVIRONMENT_TAG='dev'
APPGATEWAY_FQDN='apim.example.com'
CERT_TYPE='selfsigned'
ZONE_REDUNDANT='true'
MULTI_REGION='true'
AZURE_LOCATION2='centralus'
```
## Deployment Steps
1. Clone/download this repo locally, or even better fork this repository.
```bash
git clone https://github.com/Azure/apim-landing-zone-accelerator.git --branch wip/apim-lza
cd apim-landing-zone-accelerator/scenarios/scripts/terraform
```
2. Log into Azure from the AZ CLI and select your subscription.
```bash
az login
```
3. Deploy the reference implementation.
Run the following command to deploy the APIM baseline
```bash
# Note, you haven't created an .env file as explained above
# use cp sample.env .env to create one and edit as needed
./deploy-apim-baseline.sh
```
Notes:
- During script execution, you will encounter prompts and will need to respond with a 'y' to continue.
- 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.
Test the echo api using the generated command from the output.
## Using a Terraform AzureRM backend
For 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:
```bash
./azure-backend-sample.sh \
--resource-group my-resource-group \
--storage-account mystorageaccount \
--container my-container
```
An `${ENVIRONMENT_TAG}-backend.hcl` file will be created automatically in the same directory as your `.env`. The file looks like this:
```hcl
resource_group_name = "my-resource-group"
storage_account_name = "mystorageaccount"
container_name = "my-container"
```
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:
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/service_principal_client_secret#configuring-the-service-principal-in-terraform
## Troubleshooting
If 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:
```bash
sed -i -e 's/\r$//' deploy-apim-baseline.sh
```
================================================
FILE: scenarios/apim-baseline/terraform/backend.tf.sample
================================================
# terraform {
# backend "azurerm" {
# # ----------------------
# # Will be passing in these arguments via CLI as the state file \
# # is now being overwritten via local testing environments
# # > https://developer.hashicorp.com/terraform/language/settings/backends/configuration#command-line-key-value-pairs
# # ----------------------
# # e.g: terraform init \
# # -backend-config="resource_group_name=rg-tfstate-auseast" \
# # -backend-config="storage_account_name=tfstateauseaststorage" \
# # -backend-config="container_name=apimlza" \
# # -backend-config="key=terraform-apimlza-dev-v2.tfstate"
# # ----------------------
# # resource_group_name = "rg-tfstate-auseast"
# # storage_account_name = "tfstateauseaststorage"
# # container_name = "apimlza"
# # key = "terraform-apimlza-dev-v6.tfstate"
# }
# }
================================================
FILE: scenarios/apim-baseline/terraform/main.tf
================================================
# This module deploys an Azure API Management (APIM) service in a single region.
module "apim_baseline_single_region" {
count = var.multiRegionEnabled ? 0 : 1
source = "./single-region"
location = var.location
workloadName = var.workloadName
appGatewayFqdn = var.appGatewayFqdn
appGatewayCertType = var.appGatewayCertType
environment = var.environment
apimCSVNetNameAddressPrefix = var.apimCSVNetNameAddressPrefix
appGatewayAddressPrefix = var.appGatewayAddressPrefix
apimAddressPrefix = var.apimAddressPrefix
privateEndpointAddressPrefix = var.privateEndpointAddressPrefix
deploymentAddressPrefix = var.deploymentAddressPrefix
additionalClientIds = var.additionalClientIds
certificatePassword = var.certificatePassword
certificatePath = var.certificatePath
identifier = var.identifier
zoneRedundantEnabled = var.zoneRedundantEnabled
}
module "apim_baseline_multi_region" {
count = var.multiRegionEnabled ? 1 : 0
source = "./multi-region"
location = var.location
workloadName = var.workloadName
appGatewayFqdn = var.appGatewayFqdn
appGatewayCertType = var.appGatewayCertType
environment = var.environment
apimCSVNetNameAddressPrefix = var.apimCSVNetNameAddressPrefix
appGatewayAddressPrefix = var.appGatewayAddressPrefix
apimAddressPrefix = var.apimAddressPrefix
privateEndpointAddressPrefix = var.privateEndpointAddressPrefix
deploymentAddressPrefix = var.deploymentAddressPrefix
additionalClientIds = var.additionalClientIds
certificatePassword = var.certificatePassword
certificatePath = var.certificatePath
identifier = var.identifier
apimCSVNetNameSecondAddressPrefix = var.apimCSVNetNameSecondAddressPrefix
appGatewaySecondAddressPrefix = var.appGatewaySecondAddressPrefix
apimSecondAddressPrefix = var.apimSecondAddressPrefix
privateEndpointSecondAddressPrefix = var.privateEndpointSecondAddressPrefix
deploymentSecondAddressPrefix = var.deploymentSecondAddressPrefix
locationSecond = var.locationSecond
zoneRedundantEnabled = var.zoneRedundantEnabled
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/apim/apim.tf
================================================
locals {
apimName = "apim-${var.resourceSuffix}"
apimPipPrimaryPip = "pip-apim-${var.resourceSuffix}"
apimIdentityName = "identity-${local.apimName}"
skuCount = var.zoneRedundantEnabled ? 3 : 1
skuNameAuto = var.zoneRedundantEnabled ? "Premium_${local.skuCount}" : var.skuName
zones = var.zoneRedundantEnabled ? ["1", "2", "3"] : null
}
resource "azurerm_user_assigned_identity" "apimIdentity" {
name = local.apimIdentityName
location = var.location
resource_group_name = var.resourceGroupName
}
data "azurerm_key_vault" "keyVault" {
name = var.keyVaultName
resource_group_name = var.sharedResourceGroupName
}
#-------------------------------
# Creation of an internal APIM instance
#-------------------------------
resource "azurerm_api_management" "apim_internal" {
name = local.apimName
location = var.location
resource_group_name = var.resourceGroupName
publisher_name = var.publisherName
publisher_email = var.publisherEmail
virtual_network_type = "Internal"
sku_name = local.skuNameAuto
zones = local.zones
min_api_version = "2019-12-01"
virtual_network_configuration {
subnet_id = var.apimSubnetId
}
identity {
type = "UserAssigned"
identity_ids = ["${azurerm_user_assigned_identity.apimIdentity.id}"]
}
lifecycle {
#prevent_destroy = true
}
}
#-------------------------------
# Creation of the apim logger entity
#-------------------------------
resource "azurerm_api_management_logger" "apim_logger" {
name = "apim-logger"
api_management_name = azurerm_api_management.apim_internal.name
resource_group_name = var.resourceGroupName
resource_id = var.workspaceId
application_insights {
instrumentation_key = var.instrumentationKey
}
lifecycle {
#prevent_destroy = true
}
}
#-------------------------------
# API management service diagnostic
#-------------------------------
resource "azurerm_api_management_diagnostic" "apim_diagnostic" {
identifier = "applicationinsights"
resource_group_name = var.resourceGroupName
api_management_name = azurerm_api_management.apim_internal.name
api_management_logger_id = azurerm_api_management_logger.apim_logger.id
sampling_percentage = 100.0
always_log_errors = true
verbosity = "verbose" #possible value are verbose, error, information
frontend_request {
body_bytes = 32
headers_to_log = [
"content-type",
"accept",
"origin",
]
}
frontend_response {
body_bytes = 32
headers_to_log = [
"content-type",
"content-length",
"origin",
]
}
backend_request {
body_bytes = 32
headers_to_log = [
"content-type",
"accept",
"origin",
]
}
backend_response {
body_bytes = 32
headers_to_log = [
"content-type",
"content-length",
"origin",
]
}
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_api_management_product" "starter" {
display_name = "Starter"
product_id = "starter"
api_management_name = azurerm_api_management.apim_internal.name
resource_group_name = azurerm_api_management.apim_internal.resource_group_name
published = true
lifecycle {
#prevent_destroy = true
}
}
resource "random_uuid" "starter_key" {
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_api_management_subscription" "echo" {
api_management_name = azurerm_api_management.apim_internal.name
resource_group_name = azurerm_api_management.apim_internal.resource_group_name
product_id = azurerm_api_management_product.starter.id
display_name = "Echo API"
primary_key = random_uuid.starter_key.result
allow_tracing = false
state = "active"
lifecycle {
#prevent_destroy = true
}
}
#-------------------------------
# Importing the Echo API into API Management
#-------------------------------
resource "azurerm_api_management_api" "echo_api" {
name = "echo-api"
api_management_name = azurerm_api_management.apim_internal.name
resource_group_name = azurerm_api_management.apim_internal.resource_group_name
revision = "1"
display_name = "Echo API"
path = "echo"
protocols = ["https"]
service_url = "https://httpbin.io/anything"
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_api_management_api_operation" "echo_api_operation" {
api_name = azurerm_api_management_api.echo_api.name
api_management_name = azurerm_api_management.apim_internal.name
resource_group_name = azurerm_api_management.apim_internal.resource_group_name
display_name = "Retrieve resource"
method = "GET"
url_template = "/resource"
request {
query_parameter {
type = "string"
name = "param1"
default_value = "sample"
required = true
}
query_parameter {
type = "number"
name = "param2"
required = false
}
}
response {
status_code = 200
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)."
}
operation_id = "retrieve-resource"
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_api_management_product_api" "echo" {
api_name = azurerm_api_management_api.echo_api.name
product_id = azurerm_api_management_product.starter.product_id
api_management_name = azurerm_api_management.apim_internal.name
resource_group_name = azurerm_api_management.apim_internal.resource_group_name
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_key_vault_access_policy" "apim_access_policy" {
key_vault_id = data.azurerm_key_vault.keyVault.id
tenant_id = azurerm_user_assigned_identity.apimIdentity.tenant_id
object_id = azurerm_user_assigned_identity.apimIdentity.principal_id
secret_permissions = [
"Get",
"List"
]
certificate_permissions = [
"Get",
"List"
]
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/apim/outputs.tf
================================================
output "primaryBackendendFqdn" {
value = azurerm_api_management.apim_internal.gateway_url
}
output "bakendUrl" {
value = "${azurerm_api_management.apim_internal.name}.azure-api.net"
}
output "subscriptionKey" {
value = random_uuid.starter_key.result
}
output "apimPrivateIp" {
value = azurerm_api_management.apim_internal.private_ip_addresses[0]
}
output "apimName" {
value = azurerm_api_management.apim_internal.name
}
output "apimIdentityName" {
value = azurerm_user_assigned_identity.apimIdentity.name
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/apim/variables.tf
================================================
variable "location" {
type = string
description = "The Azure location in which the deployment is happening"
default = "eastus"
}
variable "resourceSuffix" {
type = string
description = "A suffix for naming"
}
variable "environment" {
type = string
description = "Environment"
default = "dev"
}
variable "resourceGroupName" {
type = string
description = "The name of the resource group"
}
#-------------------------------
# APIM specific variables
#-------------------------------
variable "keyVaultName" {
description = "The name of the Key Vault"
type = string
}
variable "publisherName" {
description = "The name of the publisher/company"
type = string
default = "Contoso"
}
variable "publisherEmail" {
description = "The email of the publisher/company; shows as administrator email in APIM"
type = string
default = "apim@contoso.com"
}
variable "skuName" {
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)"
type = string
default = "Developer_1"
}
variable "apimSubnetId" {
description = "The subnet id of the apim instance"
type = string
}
variable "workspaceId" {
type = string
description = "The workspace id of the log analytics workspace"
}
variable "instrumentationKey" {
type = string
description = "App insights instrumentation key"
}
variable "sharedResourceGroupName" {
type = string
description = "The name of the shared resource group"
}
variable "zoneRedundantEnabled" {
description = "Boolean to indicate if the deployment is zone redundant"
type = bool
default = false
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/dns/dnszone.tf
================================================
/* Creates a Private DNS ZOne, A Records and Vnet Link for each of the below endpoints
API Gateway contosointernalvnet.azure-api.net
Developer portal contosointernalvnet.portal.azure-api.net
The new developer portal contosointernalvnet.developer.azure-api.net
Direct management endpoint contosointernalvnet.management.azure-api.net
Git contosointernalvnet.scm.azure-api.net */
#-------------------------------
# DNS zones
#-------------------------------
resource "azurerm_private_dns_zone" "gateway" {
name = "azure-api.net"
resource_group_name = var.resourceGroupName
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_private_dns_zone" "dev_portal" {
name = "portal.azure-api.net"
resource_group_name = var.resourceGroupName
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_private_dns_zone" "new_dev_portal" {
name = "developer.azure-api.net"
resource_group_name = var.resourceGroupName
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_private_dns_zone" "mgmt_portal" {
name = "management.azure-api.net"
resource_group_name = var.resourceGroupName
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_private_dns_zone" "scm" {
name = "scm.azure-api.net"
resource_group_name = var.resourceGroupName
lifecycle {
#prevent_destroy = true
}
}
#-------------------------------
# A records for the DNS zones
#-------------------------------
resource "azurerm_private_dns_a_record" "gateway_record" {
name = lower(var.apimName)
zone_name = azurerm_private_dns_zone.gateway.name
resource_group_name = var.resourceGroupName
ttl = 36000
records = [var.apimPrivateIp]
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_private_dns_a_record" "dev_portal_record" {
name = "portal"
zone_name = azurerm_private_dns_zone.dev_portal.name
resource_group_name = var.resourceGroupName
ttl = 300
records = [var.apimPrivateIp]
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_private_dns_a_record" "new_dev_portal_record" {
name = "developer"
zone_name = azurerm_private_dns_zone.new_dev_portal.name
resource_group_name = var.resourceGroupName
ttl = 300
records = [var.apimPrivateIp]
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_private_dns_a_record" "mgmt_portal_record" {
name = "management"
zone_name = azurerm_private_dns_zone.mgmt_portal.name
resource_group_name = var.resourceGroupName
ttl = 300
records = [var.apimPrivateIp]
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_private_dns_a_record" "scm_record" {
name = "scm"
zone_name = azurerm_private_dns_zone.scm.name
resource_group_name = var.resourceGroupName
ttl = 300
records = [var.apimPrivateIp]
lifecycle {
#prevent_destroy = true
}
}
#-------------------------------
# Vnet links
#-------------------------------
resource "azurerm_private_dns_zone_virtual_network_link" "gateway_vnetlink" {
name = "gateway-vnet-link"
resource_group_name = var.resourceGroupName
private_dns_zone_name = azurerm_private_dns_zone.gateway.name
virtual_network_id = var.apimVnetId
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "dev_portal_vnetlink" {
name = "portal-vnet-link"
resource_group_name = var.resourceGroupName
private_dns_zone_name = azurerm_private_dns_zone.dev_portal.name
virtual_network_id = var.apimVnetId
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "new_dev_portal_vnetlink" {
name = "dev-portal-vnet-link"
resource_group_name = var.resourceGroupName
private_dns_zone_name = azurerm_private_dns_zone.new_dev_portal.name
virtual_network_id = var.apimVnetId
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "mgmt_vnetlink" {
name = "mgmt-vnet-link"
resource_group_name = var.resourceGroupName
private_dns_zone_name = azurerm_private_dns_zone.mgmt_portal.name
virtual_network_id = var.apimVnetId
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "scm_vnetlink" {
name = "scm-vnet-link"
resource_group_name = var.resourceGroupName
private_dns_zone_name = azurerm_private_dns_zone.scm.name
virtual_network_id = var.apimVnetId
lifecycle {
#prevent_destroy = true
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/dns/variables.tf
================================================
variable "location" {
type = string
description = "The Azure location in which the deployment is happening"
default = "eastus"
}
variable "resourceSuffix" {
type = string
description = "A suffix for naming"
}
variable "environment" {
type = string
description = "Environment"
default = "dev"
}
variable "resourceGroupName" {
type = string
description = "The name of the resource group"
}
variable "apimName" {
type = string
description = "The name of the API Management instance"
}
variable "apimPrivateIp" {
type = string
description = "The private IP address of the API Management instance"
}
variable "apimVnetId" {
type = string
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/gateway/certificate/certificate.tf
================================================
##################################################
# Tried creating the certificate using the #
# azurerm_key_vault_certificate resource, but it #
# doesn't work due to keyvault being private #
##################################################
# resource "azurerm_key_vault_certificate" "kv_domain_certs" {
# count = local.isLocalCertificate ? 1 : 0
# name = local.secretName
# key_vault_id = var.keyvaultId
# certificate {
# contents = filebase64(var.certificate_path)
# password = var.certificate_password
# }
# certificate_policy {
# issuer_parameters {
# name = "Self"
# }
# key_properties {
# exportable = true
# key_size = 256
# key_type = "EC"
# reuse_key = false
# curve = "P-256"
# }
# secret_properties {
# content_type = "application/x-pkcs12"
# }
# }
# lifecycle {
# #prevent_destroy = true
# }
# }
# resource "azurerm_key_vault_certificate" "local_domain_certs" {
# count = !local.isLocalCertificate ? 1 : 0
# name = "generated-cert"
# key_vault_id = var.keyvaultId
# certificate_policy {
# issuer_parameters {
# name = "Self"
# }
# key_properties {
# exportable = true
# key_size = 2048
# key_type = "RSA"
# reuse_key = true
# }
# lifetime_action {
# action {
# action_type = "AutoRenew"
# }
# trigger {
# days_before_expiry = 30
# }
# }
# secret_properties {
# content_type = "application/x-pkcs12"
# }
# x509_certificate_properties {
# extended_key_usage = ["1.3.6.1.5.5.7.3.1"]
# key_usage = [
# "digitalSignature",
# "keyEncipherment"
# ]
# subject = "CN=${var.appGatewayFqdn}"
# validity_in_months = 12
# }
# }
# lifecycle {
# #prevent_destroy = true
# }
# }
#########################################################
# Tried creating the certificate using the #
# azurerm_resource_deployment_script_azure_power_shell #
# resource, but it doesn't work due to keyvault being #
# private. Main issue compared to bicep is the resource #
# doesn't have the option to run from a subnet #
#########################################################
# resource "azurerm_resource_deployment_script_azure_power_shell" "appGatewayCertificate" {
# name = "${local.secretName}-certificate"
# resource_group_name = var.sharedResourceGroupName
# location = var.location
# version = "6.6"
# retention_interval = "P1D"
# command_line = " -vaultName ${var.keyVaultName} -certificateName ${local.secretName} -subjectName ${local.subjectName} -certPwd ${local.certPwd} -certDataString ${local.certDataString} -certType ${var.appGatewayCertType}"
# cleanup_preference = "OnSuccess"
# force_update_tag = "1"
# timeout = "PT30M"
# # container -> doesn't have the property to tell it from which subnet to run
# script_content = <<EOF
# param(
# [string] [Parameter(Mandatory=$true)] $vaultName,
# [string] [Parameter(Mandatory=$true)] $certificateName,
# [string] [Parameter(Mandatory=$true)] $subjectName,
# [string] [Parameter(Mandatory=$true)] $certPwd,
# [string] [Parameter(Mandatory=$true)] $certDataString,
# [string] [Parameter(Mandatory=$true)] $certType
# )
# $ErrorActionPreference = 'Stop'
# $DeploymentScriptOutputs = @{}
# if ($certType -eq 'selfsigned') {
# $policy = New-AzKeyVaultCertificatePolicy -SubjectName $subjectName -IssuerName Self -ValidityInMonths 12 -Verbose
# # private key is added as a secret that can be retrieved in the ARM template
# Add-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName -CertificatePolicy $policy -Verbose
# $newCert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName
# # it takes a few seconds for KeyVault to finish
# $tries = 0
# do {
# Write-Host 'Waiting for certificate creation completion...'
# Start-Sleep -Seconds 10
# $operation = Get-AzKeyVaultCertificateOperation -VaultName $vaultName -Name $certificateName
# $tries++
# if ($operation.Status -eq 'failed')
# {
# throw 'Creating certificate $certificateName in vault $vaultName failed with error $($operation.ErrorMessage)'
# }
# if ($tries -gt 120)
# {
# throw 'Timed out waiting for creation of certificate $certificateName in vault $vaultName'
# }
# } while ($operation.Status -ne 'completed')
# }
# else {
# $ss = Convertto-SecureString -String $certPwd -AsPlainText -Force;
# Import-AzKeyVaultCertificate -Name $certificateName -VaultName $vaultName -CertificateString $certDataString -Password $ss
# }
# EOF
# identity {
# type = "UserAssigned"
# identity_ids = [
# data.azurerm_user_assigned_identity.deploymentIdentity.id
# ]
# }
# depends_on = [
# azurerm_key_vault_access_policy.user_assigned_deployment_keyvault_permissions
# ]
# }
#########################
# Trying azapi approach #
#########################
# locals
locals {
secretName = replace(var.appGatewayFqdn, ".", "-")
subjectName = "CN=${var.appGatewayFqdn}"
certPwd = var.appGatewayCertType == "selfsigned" ? "null" : var.certificate_password
certDataString = var.appGatewayCertType == "selfsigned" ? "null" : var.certificate_path
}
# data userasignedidentity for deployment
data "azurerm_user_assigned_identity" "deploymentIdentity" {
resource_group_name = var.sharedResourceGroupName
name = var.deploymentIdentityName
}
resource "azurerm_key_vault_access_policy" "user_assigned_deployment_keyvault_permissions" {
key_vault_id = var.keyvaultId
tenant_id = data.azurerm_user_assigned_identity.deploymentIdentity.tenant_id
object_id = data.azurerm_user_assigned_identity.deploymentIdentity.principal_id
certificate_permissions = [
"Import",
"Get",
"List",
"Update",
"Create"
]
secret_permissions = [
"Get",
"List",
]
lifecycle {
#prevent_destroy = true
}
}
# get the ide of the resource group
data "azurerm_resource_group" "sharedResourceGroup" {
name = var.sharedResourceGroupName
}
resource "azapi_resource" "appGatewayCertificate" {
type = "Microsoft.Resources/deploymentScripts@2023-08-01"
name = "${local.secretName}-certificate"
depends_on = [azurerm_key_vault_access_policy.user_assigned_deployment_keyvault_permissions]
parent_id = data.azurerm_resource_group.sharedResourceGroup.id
identity {
type = "UserAssigned"
identity_ids = [data.azurerm_user_assigned_identity.deploymentIdentity.id]
}
body = jsonencode({
kind = "AzurePowerShell"
location = var.location
properties = {
storageAccountSettings = {
storageAccountName = var.deploymentStorageName
}
azPowerShellVersion = "14.3"
containerSettings = {
subnetIds = [
{
id = var.deploymentSubnetId
}
]
}
arguments = " -vaultName ${var.keyVaultName} -certificateName ${local.secretName} -subjectName ${local.subjectName} -certPwd ${local.certPwd} -certDataString ${local.certDataString} -certType ${var.appGatewayCertType}"
scriptContent = <<-EOT
param(
[string] [Parameter(Mandatory=$true)] $vaultName,
[string] [Parameter(Mandatory=$true)] $certificateName,
[string] [Parameter(Mandatory=$true)] $subjectName,
[string] [Parameter(Mandatory=$true)] $certPwd,
[string] [Parameter(Mandatory=$true)] $certDataString,
[string] [Parameter(Mandatory=$true)] $certType
)
$ErrorActionPreference = 'Stop'
$DeploymentScriptOutputs = @{}
if ($certType -eq 'selfsigned') {
$policy = New-AzKeyVaultCertificatePolicy -SubjectName $subjectName -IssuerName Self -ValidityInMonths 12 -Verbose
# private key is added as a secret that can be retrieved in the ARM template
Add-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName -CertificatePolicy $policy -Verbose
$newCert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName
# it takes a few seconds for KeyVault to finish
$tries = 0
do {
Write-Host 'Waiting for certificate creation completion...'
Start-Sleep -Seconds 10
$operation = Get-AzKeyVaultCertificateOperation -VaultName $vaultName -Name $certificateName
$tries++
if ($operation.Status -eq 'failed')
{
throw 'Creating certificate $certificateName in vault $vaultName failed with error $($operation.ErrorMessage)'
}
if ($tries -gt 120)
{
throw 'Timed out waiting for creation of certificate $certificateName in vault $vaultName'
}
} while ($operation.Status -ne 'completed')
}
else {
$ss = Convertto-SecureString -String $certPwd -AsPlainText -Force;
Import-AzKeyVaultCertificate -Name $certificateName -VaultName $vaultName -CertificateString $certDataString -Password $ss
}
$certificateIdOutput = $(Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName).id
Write-Output "certificateId: $certificateIdOutput"
$DeploymentScriptOutputs = @{}
$DeploymentScriptOutputs['certificateId'] = $certificateIdOutput
EOT
retentionInterval = "P1D"
}
})
response_export_values = ["*"]
}
output "secret_id" {
value = jsondecode(azapi_resource.appGatewayCertificate.output).properties.outputs.certificateId
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/gateway/certificate/providers.tf
================================================
terraform {
required_providers {
azapi = {
source = "azure/azapi"
version = "~> 1.0"
}
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/gateway/certificate/variables.tf
================================================
variable "location" {
type = string
description = "The Azure location in which the deployment is happening"
default = "eastus"
}
variable "sharedResourceGroupName" {
type = string
description = "Resource group with deploymnent Identity"
}
variable "keyVaultName" {
type = string
description = ""
}
variable "deploymentIdentityName" {
type = string
description = "deployment identity name"
}
variable "keyvaultId" {
type = string
description = "for giving permission to deployment identity"
}
variable "appGatewayFqdn" {
type = string
description = "The Azure location to deploy to"
default = "apim.example.com"
}
variable "certificate_path" {
type = string
description = ""
default = null
}
variable "certificate_password" {
type = string
description = ""
}
variable "appGatewayCertType" {
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 "
default = "selfsigned"
}
variable "deploymentSubnetId" {
type = string
description = "The subnet id where the deployment will run"
}
variable "deploymentStorageName" {
type = string
description = "The name of the storage account to use for deployment"
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/gateway/gateway.tf
================================================
locals {
appGatewayName = "appgw-${var.resourceSuffix}"
appGatewayPrimaryPip = "pip-appgw-${var.resourceSuffix}"
appGatewayIdentityId = "identity-${local.appGatewayName}"
httpsBackendProbeName = "APIM"
isLocalCertificate = var.appGatewayCertType == "custom"
# certificateSecretId = local.isLocalCertificate ? azurerm_key_vault_certificate.kv_domain_certs[0].secret_id : azurerm_key_vault_certificate.local_domain_certs[0].secret_id
secretName = replace(var.appGatewayFqdn, ".", "-")
subjectName = "CN=${var.appGatewayFqdn}"
certPwd = var.certificate_password
certDataString = var.certificate_path
}
resource "azurerm_user_assigned_identity" "user_assigned_identity" {
resource_group_name = var.resourceGroupName
location = var.location
name = local.appGatewayIdentityId
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_key_vault_access_policy" "user_assigned_identity_keyvault_permissions" {
key_vault_id = var.keyvaultId
tenant_id = azurerm_user_assigned_identity.user_assigned_identity.tenant_id
object_id = azurerm_user_assigned_identity.user_assigned_identity.principal_id
certificate_permissions = [
"Import",
"Get",
"List",
"Update",
"Create"
]
secret_permissions = [
"Get",
"List",
]
lifecycle {
#prevent_destroy = true
}
}
# module "certificate" {
# source = "./certificate"
# location = var.location
# sharedResourceGroupName = var.sharedResourceGroupName
# keyVaultName = var.keyVaultName
# deploymentIdentityName = var.deploymentIdentityName
# keyvaultId = var.keyvaultId
# appGatewayFqdn = var.appGatewayFqdn
# certificate_path = var.certificate_path
# certificate_password = var.certificate_password
# appGatewayCertType = var.appGatewayCertType
# deploymentSubnetId = var.deploymentSubnetId
# deploymentStorageName = var.deploymentStorageName
# }
//Public IP
resource "azurerm_public_ip" "public_ip" {
name = local.appGatewayPrimaryPip
resource_group_name = var.resourceGroupName
location = var.location
sku = "Standard"
sku_tier = "Regional"
allocation_method = "Static"
ip_version = "IPv4"
zones = ["1", "2", "3"]
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_application_gateway" "network" {
name = local.appGatewayName
resource_group_name = var.resourceGroupName
location = var.location
depends_on = [
azurerm_key_vault_access_policy.user_assigned_identity_keyvault_permissions
#,module.certificate
]
identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.user_assigned_identity.id]
}
sku {
name = "WAF_v2"
tier = "WAF_v2"
}
ssl_certificate {
name = var.appGatewayFqdn
#key_vault_secret_id = "https://${var.keyVaultName}.vault.azure.net:443/secrets/${local.secretName}"
data = filebase64(local.certDataString)
password = local.certPwd
}
gateway_ip_configuration {
name = "appGatewayIpConfig"
subnet_id = var.subnetId
}
frontend_ip_configuration {
name = "appGwPublicFrontendIp"
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.public_ip.id
}
frontend_port {
name = "port_80"
port = 80
}
frontend_port {
name = "port_443"
port = 443
}
backend_address_pool {
name = "apim"
fqdns = [var.primaryBackendendFqdn]
}
backend_http_settings {
name = "default"
port = 80
protocol = "Http"
cookie_based_affinity = "Disabled"
pick_host_name_from_backend_address = false
affinity_cookie_name = "ApplicationGatewayAffinity"
request_timeout = 20
}
backend_http_settings {
name = "https"
port = 443
protocol = "Https"
cookie_based_affinity = "Disabled"
host_name = var.primaryBackendendFqdn
pick_host_name_from_backend_address = false
request_timeout = 20
probe_name = local.httpsBackendProbeName
}
http_listener {
name = "default"
frontend_ip_configuration_name = "appGwPublicFrontendIp"
frontend_port_name = "port_80"
protocol = "Http"
require_sni = false
}
http_listener {
name = "https"
frontend_ip_configuration_name = "appGwPublicFrontendIp"
frontend_port_name = "port_443"
protocol = "Https"
require_sni = false
ssl_certificate_name = var.appGatewayFqdn
}
request_routing_rule {
name = "apim"
rule_type = "Basic"
http_listener_name = "https"
backend_address_pool_name = "apim"
backend_http_settings_name = "https"
priority = 100
}
probe {
name = "APIM"
protocol = "Https"
host = var.primaryBackendendFqdn
path = var.probe_url
interval = 30
timeout = 30
unhealthy_threshold = 3
pick_host_name_from_backend_http_settings = false
minimum_servers = 0
match {
status_code = ["200-399"]
}
}
waf_configuration {
enabled = true
firewall_mode = "Detection"
rule_set_type = "OWASP"
rule_set_version = "3.0"
request_body_check = true
max_request_body_size_kb = 128
file_upload_limit_mb = 100
}
enable_http2 = true
autoscale_configuration {
min_capacity = 2
max_capacity = 3
}
lifecycle {
#prevent_destroy = true
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/gateway/outputs.tf
================================================
================================================
FILE: scenarios/apim-baseline/terraform/modules/gateway/variables.tf
================================================
variable "location" {
type = string
description = "The Azure location in which the deployment is happening"
default = "eastus"
}
variable "resourceSuffix" {
type = string
description = "A suffix for naming"
}
variable "environment" {
type = string
description = "Environment"
default = "dev"
}
variable "resourceGroupName" {
type = string
description = "The name of the resource group"
}
variable "appGatewayCertType" {
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 "
default = "selfsigned"
}
variable "keyvaultId" {
type = string
description = ""
default = null
}
variable "keyVaultName" {
type = string
description = ""
}
variable "deploymentIdentityName" {
type = string
description = "deployment identity name"
}
variable "appGatewayFqdn" {
type = string
description = "The Azure location to deploy to"
default = "apim.example.com"
}
variable "certificate_path" {
type = string
description = ""
default = null
}
variable "certificate_password" {
type = string
sensitive = true
description = ""
}
variable "subnetId" {
type = string
description = ""
}
variable "primaryBackendendFqdn" {
type = string
description = ""
}
variable "probe_url" {
type = string
description = ""
default = "/status-0123456789abcdef"
}
variable "sharedResourceGroupName" {
type = string
description = "Resource group with deploymnent Identity"
}
variable "deploymentSubnetId" {
type = string
description = "The subnet id where the deployment will run"
}
variable "deploymentStorageName" {
type = string
description = "The name of the storage account to use for deployment"
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_apim/apim.tf
================================================
locals {
apimName = "apim-${var.resourceSuffix}"
apimPipPrimaryPip = "pip-apim-${var.resourceSuffix}"
apimIdentityName = "identity-${local.apimName}"
skuCount = var.zoneRedundantEnabled ? 3 : 1
skuName = "Premium_${local.skuCount}"
zones = var.zoneRedundantEnabled ? ["1", "2", "3"] : ["1"]
}
resource "azurerm_user_assigned_identity" "apimIdentity" {
name = local.apimIdentityName
location = var.location
resource_group_name = var.resourceGroupName
}
data "azurerm_key_vault" "keyVault" {
name = var.keyVaultName
resource_group_name = var.sharedResourceGroupName
}
#-------------------------------
# Creation of an internal APIM instance
#-------------------------------
resource "azurerm_api_management" "apim_internal" {
name = local.apimName
location = var.location
resource_group_name = var.resourceGroupName
publisher_name = var.publisherName
publisher_email = var.publisherEmail
virtual_network_type = "Internal"
sku_name = local.skuName
zones = local.zones
min_api_version = "2019-12-01"
virtual_network_configuration {
subnet_id = var.apimSubnetId
}
identity {
type = "UserAssigned"
identity_ids = ["${azurerm_user_assigned_identity.apimIdentity.id}"]
}
additional_location {
location = var.locationSecond
zones = local.zones
virtual_network_configuration {
subnet_id = var.apimSecondSubnetId
}
}
lifecycle {
##prevent_destroy = true
}
}
#-------------------------------
#Creation of the apim logger entity
#-------------------------------
resource "azurerm_api_management_logger" "apim_logger" {
name = "apim-logger"
api_management_name = azurerm_api_management.apim_internal.name
resource_group_name = var.resourceGroupName
resource_id = var.workspaceId
application_insights {
instrumentation_key = var.instrumentationKey
}
lifecycle {
##prevent_destroy = true
}
}
#-------------------------------
#API management service diagnostic
#-------------------------------
resource "azurerm_api_management_diagnostic" "apim_diagnostic" {
identifier = "applicationinsights"
resource_group_name = var.resourceGroupName
api_management_name = azurerm_api_management.apim_internal.name
api_management_logger_id = azurerm_api_management_logger.apim_logger.id
sampling_percentage = 100.0
always_log_errors = true
verbosity = "verbose" #possible value are verbose, error, information
frontend_request {
body_bytes = 32
headers_to_log = [
"content-type",
"accept",
"origin",
]
}
frontend_response {
body_bytes = 32
headers_to_log = [
"content-type",
"content-length",
"origin",
]
}
backend_request {
body_bytes = 32
headers_to_log = [
"content-type",
"accept",
"origin",
]
}
backend_response {
body_bytes = 32
headers_to_log = [
"content-type",
"content-length",
"origin",
]
}
lifecycle {
##prevent_destroy = true
}
}
resource "azurerm_api_management_product" "starter" {
display_name = "Starter"
product_id = "starter"
api_management_name = azurerm_api_management.apim_internal.name
resource_group_name = azurerm_api_management.apim_internal.resource_group_name
published = true
lifecycle {
##prevent_destroy = true
}
}
resource "random_uuid" "starter_key" {
lifecycle {
##prevent_destroy = true
}
}
resource "azurerm_api_management_subscription" "echo" {
api_management_name = azurerm_api_management.apim_internal.name
resource_group_name = azurerm_api_management.apim_internal.resource_group_name
product_id = azurerm_api_management_product.starter.id
display_name = "Echo API"
primary_key = random_uuid.starter_key.result
allow_tracing = false
state = "active"
lifecycle {
##prevent_destroy = true
}
}
#-------------------------------
# Importing the Echo API into API Management
#-------------------------------
resource "azurerm_api_management_api" "echo_api" {
name = "echo-api"
api_management_name = azurerm_api_management.apim_internal.name
resource_group_name = azurerm_api_management.apim_internal.resource_group_name
revision = "1"
display_name = "Echo API"
path = "echo"
protocols = ["https"]
service_url = "https://httpbin.io/anything"
lifecycle {
##prevent_destroy = true
}
}
resource "azurerm_api_management_api_operation" "echo_api_operation" {
api_name = azurerm_api_management_api.echo_api.name
api_management_name = azurerm_api_management.apim_internal.name
resource_group_name = azurerm_api_management.apim_internal.resource_group_name
display_name = "Retrieve resource"
method = "GET"
url_template = "/resource"
request {
query_parameter {
type = "string"
name = "param1"
default_value = "sample"
required = true
}
query_parameter {
type = "number"
name = "param2"
required = false
}
}
response {
status_code = 200
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)."
}
operation_id = "retrieve-resource"
lifecycle {
##prevent_destroy = true
}
}
resource "azurerm_api_management_product_api" "echo" {
api_name = azurerm_api_management_api.echo_api.name
product_id = azurerm_api_management_product.starter.product_id
api_management_name = azurerm_api_management.apim_internal.name
resource_group_name = azurerm_api_management.apim_internal.resource_group_name
lifecycle {
##prevent_destroy = true
}
}
resource "azurerm_key_vault_access_policy" "apim_access_policy" {
key_vault_id = data.azurerm_key_vault.keyVault.id
tenant_id = azurerm_user_assigned_identity.apimIdentity.tenant_id
object_id = azurerm_user_assigned_identity.apimIdentity.principal_id
secret_permissions = [
"Get",
"List"
]
certificate_permissions = [
"Get",
"List"
]
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_apim/outputs.tf
================================================
output "primaryBackendendFqdn" {
value = azurerm_api_management.apim_internal.gateway_url
}
output "bakendUrl" {
value = "${azurerm_api_management.apim_internal.name}.azure-api.net"
}
output "subscriptionKey" {
value = random_uuid.starter_key.result
}
output "apimPrivateIp" {
value = azurerm_api_management.apim_internal.private_ip_addresses[0]
}
output "apimName" {
value = azurerm_api_management.apim_internal.name
}
output "apimIdentityName" {
value = azurerm_user_assigned_identity.apimIdentity.name
}
output "apim_regional_url_1" {
value = replace(azurerm_api_management.apim_internal.gateway_regional_url,"https://","")
}
output "apim_regional_url_2" {
value = replace(azurerm_api_management.apim_internal.additional_location[0].gateway_regional_url,"https://","")
}
output "apim_regional_IP_1" {
value = azurerm_api_management.apim_internal.private_ip_addresses[0]
}
output "apim_regional_IP_2" {
value = azurerm_api_management.apim_internal.additional_location[0].private_ip_addresses[0]
}
output "apim_regional_name_1" {
value = split(".",replace(azurerm_api_management.apim_internal.gateway_regional_url,"https://",""))[0]
}
output "apim_regional_name_2" {
value = split(".",replace(azurerm_api_management.apim_internal.additional_location[0].gateway_regional_url,"https://",""))[0]
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_apim/variables.tf
================================================
variable "location" {
type = string
description = "The Azure location in which the deployment is happening"
default = "eastus"
}
variable "resourceSuffix" {
type = string
description = "A suffix for naming"
}
variable "environment" {
type = string
description = "Environment"
default = "dev"
}
variable "resourceGroupName" {
type = string
description = "The name of the resource group"
}
#-------------------------------
# APIM specific variables
#-------------------------------
variable "keyVaultName" {
description = "The name of the Key Vault"
type = string
}
variable "publisherName" {
description = "The name of the publisher/company"
type = string
default = "Contoso"
}
variable "publisherEmail" {
description = "The email of the publisher/company; shows as administrator email in APIM"
type = string
default = "apim@contoso.com"
}
variable "skuName" {
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)"
type = string
default = "Developer_1"
}
variable "apimSubnetId" {
description = "The subnet id of the apim instance"
type = string
}
variable "workspaceId" {
type = string
description = "The workspace id of the log analytics workspace"
}
variable "instrumentationKey" {
type = string
description = "App insights instrumentation key"
}
variable "sharedResourceGroupName" {
type = string
description = "The name of the shared resource group"
}
# Multi-region deployment variables
variable "apimSecondSubnetId" {
type = string
description = "The subnet id of the second apim instance"
}
variable "zoneRedundantEnabled" {
type = bool
description = "Flag to enable zone redundancy"
default = false
}
variable "locationSecond" {
type = string
description = "The Azure location for the second region in a multi-region deployment"
default = null
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_apim-dns-regional/dnszone.tf
================================================
/* Creates a Private DNS ZOne, A Records and Vnet Link for each of the below endpoints
API Gateway contosointernalvnet.azure-api.net
Developer portal contosointernalvnet.portal.azure-api.net
The new developer portal contosointernalvnet.developer.azure-api.net
Direct management endpoint contosointernalvnet.management.azure-api.net
Git contosointernalvnet.scm.azure-api.net */
#-------------------------------
# DNS zones
#-------------------------------
resource "azurerm_private_dns_zone" "gateway" {
name = "regional.azure-api.net"
resource_group_name = var.resourceGroupName
lifecycle {
##prevent_destroy = true
}
}
#-------------------------------
# A records for the DNS zones
#-------------------------------
resource "azurerm_private_dns_a_record" "gateway_record" {
name = lower(var.apimRegionalName)
zone_name = azurerm_private_dns_zone.gateway.name
resource_group_name = var.resourceGroupName
ttl = 36000
records = [var.apimPrivateIp]
lifecycle {
##prevent_destroy = true
}
}
resource "azurerm_private_dns_a_record" "gateway_second_record" {
name = lower(var.apimSecondRegionalName)
zone_name = azurerm_private_dns_zone.gateway.name
resource_group_name = var.resourceGroupName
ttl = 36000
records = [var.apimSecondPrivateIp]
lifecycle {
##prevent_destroy = true
}
}
#-------------------------------
# Vnet links
#-------------------------------
resource "azurerm_private_dns_zone_virtual_network_link" "gateway_vnetlink" {
name = "gateway-vnet-link"
resource_group_name = var.resourceGroupName
private_dns_zone_name = azurerm_private_dns_zone.gateway.name
virtual_network_id = var.apimVnetId
lifecycle {
##prevent_destroy = true
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "gateway_second_vnetlink" {
name = "gateway-second-vnet-link"
resource_group_name = var.resourceGroupName
private_dns_zone_name = azurerm_private_dns_zone.gateway.name
virtual_network_id = var.apimSecondVnetId
lifecycle {
##prevent_destroy = true
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_apim-dns-regional/variables.tf
================================================
variable "location" {
type = string
description = "The Azure location in which the deployment is happening"
default = "eastus"
}
variable "resourceSuffix" {
type = string
description = "A suffix for naming"
}
variable "environment" {
type = string
description = "Environment"
default = "dev"
}
variable "resourceGroupName" {
type = string
description = "The name of the resource group"
}
variable "apimRegionalName" {
type = string
description = "The name of the API Management instance"
}
variable "apimSecondRegionalName" {
type = string
description = "The name of the second API Management instance (gateway)"
}
variable "apimPrivateIp" {
type = string
description = "The private IP address of the API Management instance"
}
variable "apimSecondPrivateIp" {
type = string
description = "The private IP address of the second API Management instance"
}
variable "apimVnetId" {
type = string
}
variable "apimSecondVnetId" {
type = string
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_gateway/certificate/certificate.tf
================================================
##################################################
# Tried creating the certificate using the #
# azurerm_key_vault_certificate resource, but it #
# doesn't work due to keyvault being private #
##################################################
# resource "azurerm_key_vault_certificate" "kv_domain_certs" {
# count = local.isLocalCertificate ? 1 : 0
# name = local.secretName
# key_vault_id = var.keyvaultId
# certificate {
# contents = filebase64(var.certificate_path)
# password = var.certificate_password
# }
# certificate_policy {
# issuer_parameters {
# name = "Self"
# }
# key_properties {
# exportable = true
# key_size = 256
# key_type = "EC"
# reuse_key = false
# curve = "P-256"
# }
# secret_properties {
# content_type = "application/x-pkcs12"
# }
# }
# lifecycle {
# ##prevent_destroy = true
# }
# }
# resource "azurerm_key_vault_certificate" "local_domain_certs" {
# count = !local.isLocalCertificate ? 1 : 0
# name = "generated-cert"
# key_vault_id = var.keyvaultId
# certificate_policy {
# issuer_parameters {
# name = "Self"
# }
# key_properties {
# exportable = true
# key_size = 2048
# key_type = "RSA"
# reuse_key = true
# }
# lifetime_action {
# action {
# action_type = "AutoRenew"
# }
# trigger {
# days_before_expiry = 30
# }
# }
# secret_properties {
# content_type = "application/x-pkcs12"
# }
# x509_certificate_properties {
# extended_key_usage = ["1.3.6.1.5.5.7.3.1"]
# key_usage = [
# "digitalSignature",
# "keyEncipherment"
# ]
# subject = "CN=${var.appGatewayFqdn}"
# validity_in_months = 12
# }
# }
# lifecycle {
# ##prevent_destroy = true
# }
# }
#########################################################
# Tried creating the certificate using the #
# azurerm_resource_deployment_script_azure_power_shell #
# resource, but it doesn't work due to keyvault being #
# private. Main issue compared to bicep is the resource #
# doesn't have the option to run from a subnet #
#########################################################
# resource "azurerm_resource_deployment_script_azure_power_shell" "appGatewayCertificate" {
# name = "${local.secretName}-certificate"
# resource_group_name = var.sharedResourceGroupName
# location = var.location
# version = "6.6"
# retention_interval = "P1D"
# command_line = " -vaultName ${var.keyVaultName} -certificateName ${local.secretName} -subjectName ${local.subjectName} -certPwd ${local.certPwd} -certDataString ${local.certDataString} -certType ${var.appGatewayCertType}"
# cleanup_preference = "OnSuccess"
# force_update_tag = "1"
# timeout = "PT30M"
# # container -> doesn't have the property to tell it from which subnet to run
# script_content = <<EOF
# param(
# [string] [Parameter(Mandatory=$true)] $vaultName,
# [string] [Parameter(Mandatory=$true)] $certificateName,
# [string] [Parameter(Mandatory=$true)] $subjectName,
# [string] [Parameter(Mandatory=$true)] $certPwd,
# [string] [Parameter(Mandatory=$true)] $certDataString,
# [string] [Parameter(Mandatory=$true)] $certType
# )
# $ErrorActionPreference = 'Stop'
# $DeploymentScriptOutputs = @{}
# if ($certType -eq 'selfsigned') {
# $policy = New-AzKeyVaultCertificatePolicy -SubjectName $subjectName -IssuerName Self -ValidityInMonths 12 -Verbose
# # private key is added as a secret that can be retrieved in the ARM template
# Add-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName -CertificatePolicy $policy -Verbose
# $newCert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName
# # it takes a few seconds for KeyVault to finish
# $tries = 0
# do {
# Write-Host 'Waiting for certificate creation completion...'
# Start-Sleep -Seconds 10
# $operation = Get-AzKeyVaultCertificateOperation -VaultName $vaultName -Name $certificateName
# $tries++
# if ($operation.Status -eq 'failed')
# {
# throw 'Creating certificate $certificateName in vault $vaultName failed with error $($operation.ErrorMessage)'
# }
# if ($tries -gt 120)
# {
# throw 'Timed out waiting for creation of certificate $certificateName in vault $vaultName'
# }
# } while ($operation.Status -ne 'completed')
# }
# else {
# $ss = Convertto-SecureString -String $certPwd -AsPlainText -Force;
# Import-AzKeyVaultCertificate -Name $certificateName -VaultName $vaultName -CertificateString $certDataString -Password $ss
# }
# EOF
# identity {
# type = "UserAssigned"
# identity_ids = [
# data.azurerm_user_assigned_identity.deploymentIdentity.id
# ]
# }
# depends_on = [
# azurerm_key_vault_access_policy.user_assigned_deployment_keyvault_permissions
# ]
# }
#########################
# Trying azapi approach #
#########################
# locals
locals {
secretName = replace(var.appGatewayFqdn, ".", "-")
subjectName = "CN=${var.appGatewayFqdn}"
certPwd = var.appGatewayCertType == "selfsigned" ? "null" : var.certificate_password
certDataString = var.appGatewayCertType == "selfsigned" ? "null" : var.certificate_path
}
# data userasignedidentity for deployment
data "azurerm_user_assigned_identity" "deploymentIdentity" {
resource_group_name = var.sharedResourceGroupName
name = var.deploymentIdentityName
}
resource "azurerm_key_vault_access_policy" "user_assigned_deployment_keyvault_permissions" {
key_vault_id = var.keyvaultId
tenant_id = data.azurerm_user_assigned_identity.deploymentIdentity.tenant_id
object_id = data.azurerm_user_assigned_identity.deploymentIdentity.principal_id
certificate_permissions = [
"Import",
"Get",
"List",
"Update",
"Create"
]
secret_permissions = [
"Get",
"List",
]
lifecycle {
###prevent_destroy = true
}
}
# get the ide of the resource group
data "azurerm_resource_group" "sharedResourceGroup" {
name = var.sharedResourceGroupName
}
resource "azapi_resource" "appGatewayCertificate" {
type = "Microsoft.Resources/deploymentScripts@2023-08-01"
name = "${local.secretName}-certificate"
depends_on = [azurerm_key_vault_access_policy.user_assigned_deployment_keyvault_permissions]
parent_id = data.azurerm_resource_group.sharedResourceGroup.id
identity {
type = "UserAssigned"
identity_ids = [data.azurerm_user_assigned_identity.deploymentIdentity.id]
}
body = jsonencode({
kind = "AzurePowerShell"
location = var.location
properties = {
storageAccountSettings = {
storageAccountName = var.deploymentStorageName
}
azPowerShellVersion = "14.3"
containerSettings = {
subnetIds = [
{
id = var.deploymentSubnetId
}
]
}
arguments = " -vaultName ${var.keyVaultName} -certificateName ${local.secretName} -subjectName ${local.subjectName} -certPwd ${local.certPwd} -certDataString ${local.certDataString} -certType ${var.appGatewayCertType}"
scriptContent = <<-EOT
param(
[string] [Parameter(Mandatory=$true)] $vaultName,
[string] [Parameter(Mandatory=$true)] $certificateName,
[string] [Parameter(Mandatory=$true)] $subjectName,
[string] [Parameter(Mandatory=$true)] $certPwd,
[string] [Parameter(Mandatory=$true)] $certDataString,
[string] [Parameter(Mandatory=$true)] $certType
)
$ErrorActionPreference = 'Stop'
$DeploymentScriptOutputs = @{}
if ($certType -eq 'selfsigned') {
$policy = New-AzKeyVaultCertificatePolicy -SubjectName $subjectName -IssuerName Self -ValidityInMonths 12 -Verbose
# private key is added as a secret that can be retrieved in the ARM template
Add-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName -CertificatePolicy $policy -Verbose
$newCert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName
# it takes a few seconds for KeyVault to finish
$tries = 0
do {
Write-Host 'Waiting for certificate creation completion...'
Start-Sleep -Seconds 10
$operation = Get-AzKeyVaultCertificateOperation -VaultName $vaultName -Name $certificateName
$tries++
if ($operation.Status -eq 'failed')
{
throw 'Creating certificate $certificateName in vault $vaultName failed with error $($operation.ErrorMessage)'
}
if ($tries -gt 120)
{
throw 'Timed out waiting for creation of certificate $certificateName in vault $vaultName'
}
} while ($operation.Status -ne 'completed')
}
else {
$ss = Convertto-SecureString -String $certPwd -AsPlainText -Force;
Import-AzKeyVaultCertificate -Name $certificateName -VaultName $vaultName -CertificateString $certDataString -Password $ss
}
$certificateIdOutput = $(Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName).id
Write-Output "certificateId: $certificateIdOutput"
$DeploymentScriptOutputs = @{}
$DeploymentScriptOutputs['certificateId'] = $certificateIdOutput
EOT
retentionInterval = "P1D"
}
})
response_export_values = ["*"]
}
output "secret_id" {
value = jsondecode(azapi_resource.appGatewayCertificate.output).properties.outputs.certificateId
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_gateway/certificate/providers.tf
================================================
terraform {
required_providers {
azapi = {
source = "azure/azapi"
version = "~> 1.0"
}
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_gateway/certificate/variables.tf
================================================
variable "location" {
type = string
description = "The Azure location in which the deployment is happening"
default = "eastus"
}
variable "sharedResourceGroupName" {
type = string
description = "Resource group with deploymnent Identity"
}
variable "keyVaultName" {
type = string
description = ""
}
variable "deploymentIdentityName" {
type = string
description = "deployment identity name"
}
variable "keyvaultId" {
type = string
description = "for giving permission to deployment identity"
}
variable "appGatewayFqdn" {
type = string
description = "The Azure location to deploy to"
default = "apim.example.com"
}
variable "certificate_path" {
type = string
description = ""
default = null
}
variable "certificate_password" {
type = string
description = ""
}
variable "appGatewayCertType" {
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 "
default = "selfsigned"
}
variable "deploymentSubnetId" {
type = string
description = "The subnet id where the deployment will run"
}
variable "deploymentStorageName" {
type = string
description = "The name of the storage account to use for deployment"
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_gateway/gateway.tf
================================================
locals {
appGatewayName = "appgw-${var.resourceSuffix}"
appGatewayPrimaryPip = "pip-appgw-${var.resourceSuffix}"
appGatewayIdentityId = "identity-${local.appGatewayName}"
httpsBackendProbeName = "APIM"
isLocalCertificate = var.appGatewayCertType == "custom"
# certificateSecretId = local.isLocalCertificate ? azurerm_key_vault_certificate.kv_domain_certs[0].secret_id : azurerm_key_vault_certificate.local_domain_certs[0].secret_id
secretName = replace(var.appGatewayFqdn, ".", "-")
subjectName = "CN=${var.appGatewayFqdn}"
certPwd = var.certificate_password
certDataString = var.certificate_path
trafficManagerName = "${replace(var.appGatewayFqdn,".","-")}.trafficmanager.net"
}
resource "azurerm_user_assigned_identity" "user_assigned_identity" {
resource_group_name = var.resourceGroupName
location = var.location
name = local.appGatewayIdentityId
lifecycle {
##prevent_destroy = true
}
}
resource "azurerm_key_vault_access_policy" "user_assigned_identity_keyvault_permissions" {
key_vault_id = var.keyvaultId
tenant_id = azurerm_user_assigned_identity.user_assigned_identity.tenant_id
object_id = azurerm_user_assigned_identity.user_assigned_identity.principal_id
certificate_permissions = [
"Import",
"Get",
"List",
"Update",
"Create"
]
secret_permissions = [
"Get",
"List",
]
lifecycle {
##prevent_destroy = true
}
}
# module "certificate" {
# source = "./certificate"
# location = var.location
# sharedResourceGroupName = var.sharedResourceGroupName
# keyVaultName = var.keyVaultName
# deploymentIdentityName = var.deploymentIdentityName
# keyvaultId = var.keyvaultId
# appGatewayFqdn = var.appGatewayFqdn
# certificate_path = var.certificate_path
# certificate_password = var.certificate_password
# appGatewayCertType = var.appGatewayCertType
# deploymentSubnetId = var.deploymentSubnetId
# deploymentStorageName = var.deploymentStorageName
# }
//Public IP
resource "azurerm_public_ip" "public_ip" {
name = local.appGatewayPrimaryPip
resource_group_name = var.resourceGroupName
location = var.location
sku = "Standard"
sku_tier = "Regional"
allocation_method = "Static"
ip_version = "IPv4"
zones = ["1", "2", "3"]
domain_name_label = local.appGatewayName
lifecycle {
##prevent_destroy = true
}
}
resource "azurerm_application_gateway" "network" {
name = local.appGatewayName
resource_group_name = var.resourceGroupName
location = var.location
depends_on = [
azurerm_key_vault_access_policy.user_assigned_identity_keyvault_permissions
#, module.certificate
]
identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.user_assigned_identity.id]
}
sku {
name = "WAF_v2"
tier = "WAF_v2"
}
ssl_certificate {
name = var.appGatewayFqdn
#key_vault_secret_id = "https://${var.keyVaultName}.vault.azure.net:443/secrets/${local.secretName}"
data = filebase64(local.certDataString)
password = local.certPwd
}
gateway_ip_configuration {
name = "appGatewayIpConfig"
subnet_id = var.subnetId
}
frontend_ip_configuration {
name = "appGwPublicFrontendIp"
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.public_ip.id
}
frontend_port {
name = "port_80"
port = 80
}
frontend_port {
name = "port_443"
port = 443
}
backend_address_pool {
name = "apim"
fqdns = [var.primaryBackendendFqdn]
}
backend_address_pool {
name = "sink-hole"
}
backend_http_settings {
name = "default"
port = 80
protocol = "Http"
cookie_based_affinity = "Disabled"
pick_host_name_from_backend_address = false
affinity_cookie_name = "ApplicationGatewayAffinity"
request_timeout = 20
}
backend_http_settings {
name = "https"
port = 443
protocol = "Https"
cookie_based_affinity = "Disabled"
#host_name = var.primaryBackendendFqdn
pick_host_name_from_backend_address = true
request_timeout = 20
probe_name = local.httpsBackendProbeName
}
http_listener {
name = "default"
frontend_ip_configuration_name = "appGwPublicFrontendIp"
frontend_port_name = "port_80"
protocol = "Http"
require_sni = false
}
http_listener {
name = "https"
frontend_ip_configuration_name = "appGwPublicFrontendIp"
frontend_port_name = "port_443"
protocol = "Https"
#host_name = var.appGatewayFqdn
require_sni = false
ssl_certificate_name = var.appGatewayFqdn
host_names = [
var.appGatewayFqdn,
azurerm_public_ip.public_ip.fqdn,
local.trafficManagerName
]
}
request_routing_rule {
name = "apim"
rule_type = "PathBasedRouting"
http_listener_name = "https"
url_path_map_name = "urlPathMapApim"
#backend_address_pool_name = "apim"
#backend_http_settings_name = "https"
priority = 100
}
url_path_map {
default_backend_address_pool_name = "apim"
default_backend_http_settings_name = "https"
name = "urlPathMapApim"
path_rule {
backend_address_pool_name = "apim"
backend_http_settings_name = "https"
name = "echo-api"
paths = ["/echo/*"]
}
path_rule {
backend_address_pool_name = "apim"
backend_http_settings_name = "https"
name = "hello-api"
paths = ["/hello*"]
}
path_rule {
backend_address_pool_name = "apim"
backend_http_settings_name = "https"
name = "openai-api"
paths = ["/openai/*"]
}
path_rule {
backend_address_pool_name = "sink-hole"
backend_http_settings_name = "https"
name = "default"
paths = ["/*"]
}
path_rule {
backend_address_pool_name = "apim"
backend_http_settings_name = "https"
name = "probe-for-traffic-manager"
paths = ["/status-0123456789abcdef"]
}
}
probe {
name = "APIM"
protocol = "Https"
host = var.primaryBackendendFqdn
path = var.probe_url
interval = 30
timeout = 30
unhealthy_threshold = 3
pick_host_name_from_backend_http_settings = false
minimum_servers = 0
match {
status_code = ["200-399"]
}
}
waf_configuration {
enabled = true
firewall_mode = "Detection"
rule_set_type = "OWASP"
rule_set_version = "3.0"
request_body_check = true
max_request_body_size_kb = 128
file_upload_limit_mb = 100
}
enable_http2 = true
autoscale_configuration {
min_capacity = 2
max_capacity = 3
}
lifecycle {
##prevent_destroy = true
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_gateway/outputs.tf
================================================
output "gw_pip_id" {
value = azurerm_public_ip.public_ip.id
}
output "gw_pip_fqdn" {
value = azurerm_public_ip.public_ip.fqdn
}
output "app_gateway_name" {
value = azurerm_application_gateway.network.name
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_gateway/variables.tf
================================================
variable "location" {
type = string
description = "The Azure location in which the deployment is happening"
default = "eastus"
}
variable "resourceSuffix" {
type = string
description = "A suffix for naming"
}
variable "environment" {
type = string
description = "Environment"
default = "dev"
}
variable "resourceGroupName" {
type = string
description = "The name of the resource group"
}
variable "appGatewayCertType" {
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 "
default = "selfsigned"
}
variable "keyvaultId" {
type = string
description = ""
default = null
}
variable "keyVaultName" {
type = string
description = ""
}
variable "deploymentIdentityName" {
type = string
description = "deployment identity name"
}
variable "appGatewayFqdn" {
type = string
description = "The Azure location to deploy to"
default = "apim.example.com"
}
variable "certificate_path" {
type = string
description = ""
default = null
}
variable "certificate_password" {
type = string
description = ""
}
variable "subnetId" {
type = string
description = ""
}
variable "primaryBackendendFqdn" {
type = string
description = ""
}
variable "probe_url" {
type = string
description = ""
default = "/status-0123456789abcdef"
}
variable "sharedResourceGroupName" {
type = string
description = "Resource group with deploymnent Identity"
}
variable "deploymentSubnetId" {
type = string
description = "The subnet id where the deployment will run"
}
variable "deploymentStorageName" {
type = string
description = "The name of the storage account to use for deployment"
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_private_dns_zone/outputs.tf
================================================
output "id" {
description = "Specifies the resource id of the private dns zone"
value = azurerm_private_dns_zone.private_dns_zone.id
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_private_dns_zone/privatednszone.tf
================================================
resource "azurerm_private_dns_zone" "private_dns_zone" {
name = var.name
resource_group_name = var.resource_group_name
tags = var.tags
lifecycle {
ignore_changes = [
tags
]
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "link" {
name = "link_to_${lower(basename(var.virtual_networks_to_link_id))}"
resource_group_name = var.resource_group_name
private_dns_zone_name = azurerm_private_dns_zone.private_dns_zone.name
virtual_network_id = var.virtual_networks_to_link_id
lifecycle {
ignore_changes = [
tags
]
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "second_link" {
count = var.multiRegionEnabled ? 1 : 0
name = "link_to_${lower(basename(var.second_virtual_networks_to_link_id))}"
resource_group_name = var.resource_group_name
private_dns_zone_name = azurerm_private_dns_zone.private_dns_zone.name
virtual_network_id = var.second_virtual_networks_to_link_id
lifecycle {
ignore_changes = [
tags
]
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_private_dns_zone/variables.tf
================================================
variable "name" {
description = "(Required) Specifies the name of the private dns zone"
type = string
}
variable "resource_group_name" {
description = "(Required) Specifies the resource group name of the private dns zone"
type = string
}
variable "tags" {
description = "(Optional) Specifies the tags of the private dns zone"
default = {}
}
variable "virtual_networks_to_link_id" {
description = "(Optional) Specifies the virtual networks id to which create a virtual network link"
type = string
}
variable "second_virtual_networks_to_link_id" {
description = "(Optional) Specifies the virtual networks id to which create a virtual network link"
type = string
default = null
}
variable "multiRegionEnabled" {
description = "(Optional) Specifies if the multi-region is enabled"
type = bool
default = false
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_shared/azmon.tf
================================================
#-------------------------------
# Creation of log analytics workspace instance
#-------------------------------
resource "azurerm_log_analytics_workspace" "log_analytics_workspace" {
name = "log-${var.resourceSuffix}"
location = var.location
resource_group_name = var.resourceGroupName
sku = "PerGB2018"
retention_in_days = 30
lifecycle {
##prevent_destroy = true
}
}
#-------------------------------
# Creation of an application insight instance
#-------------------------------
resource "azurerm_application_insights" "shared_apim_insight" {
name = "appi-${var.resourceSuffix}"
location = var.location
resource_group_name = var.resourceGroupName
application_type = "web"
workspace_id = azurerm_log_analytics_workspace.log_analytics_workspace.id
lifecycle {
##prevent_destroy = true
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_shared/outputs.tf
================================================
output "workspaceId" {
description = "The id of the workspace"
value = azurerm_log_analytics_workspace.log_analytics_workspace.id
}
output "instrumentationKey" {
description = "The instrumentation key of the workspace"
value = azurerm_application_insights.shared_apim_insight.instrumentation_key
}
output "keyVaultId" {
value = azurerm_key_vault.key_vault.id
}
output "keyVaultName" {
value = azurerm_key_vault.key_vault.name
}
output "deploymentIdentityName" {
value = azurerm_user_assigned_identity.privatedeploymanagedidentity.name
}
output "deploymentStorageName" {
value = azurerm_storage_account.privatedeploystorage.name
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_shared/private_endpoint/outputs.tf
================================================
output "id" {
description = "Specifies the resource id of the private endpoint."
value = azurerm_private_endpoint.private_endpoint.id
}
output "private_dns_zone_group" {
description = "Specifies the private dns zone group of the private endpoint."
value = azurerm_private_endpoint.private_endpoint.private_dns_zone_group
}
output "private_dns_zone_configs" {
description = "Specifies the private dns zone(s) configuration"
value = azurerm_private_endpoint.private_endpoint.private_dns_zone_configs
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_shared/private_endpoint/privateendpoint.tf
================================================
resource "azurerm_private_endpoint" "private_endpoint" {
name = var.name
location = var.location
resource_group_name = var.resource_group_name
subnet_id = var.subnet_id
tags = var.tags
private_service_connection {
name = "${var.name}Connection"
private_connection_resource_id = var.private_connection_resource_id
is_manual_connection = var.is_manual_connection
subresource_names = try([var.subresource_name], null)
request_message = try(var.request_message, null)
}
private_dns_zone_group {
name = var.private_dns_zone_group_name
private_dns_zone_ids = var.private_dns_zone_group_ids
}
lifecycle {
ignore_changes = [
tags
]
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_shared/private_endpoint/variables.tf
================================================
variable "name" {
description = "(Required) Specifies the name of the private endpoint. Changing this forces a new resource to be created."
type = string
}
variable "resource_group_name" {
description = "(Required) The name of the resource group. Changing this forces a new resource to be created."
type = string
}
variable "private_connection_resource_id" {
description = "(Required) Specifies the resource id of the private link service"
type = string
}
variable "location" {
description = "(Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created."
type = string
}
variable "subnet_id" {
description = "(Required) Specifies the resource id of the subnet"
type = string
}
variable "is_manual_connection" {
description = "(Optional) Specifies whether the private endpoint connection requires manual approval from the remote resource owner."
type = string
default = false
}
variable "subresource_name" {
description = "(Optional) Specifies a subresource name which the Private Endpoint is able to connect to."
type = string
default = null
}
variable "request_message" {
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."
type = string
default = null
}
variable "private_dns_zone_group_name" {
description = "(Required) Specifies the Name of the Private DNS Zone Group. Changing this forces a new private_dns_zone_group resource to be created."
type = string
}
variable "private_dns_zone_group_ids" {
description = "(Required) Specifies the list of Private DNS Zones to include within the private_dns_zone_group."
type = list(string)
}
variable "tags" {
description = "(Optional) Specifies the tags of the network security group"
default = {}
}
variable "private_dns" {
default = {}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_shared/privatedeploy.tf
================================================
resource "azurerm_storage_account" "privatedeploystorage" {
name = var.storage_account_name
location = var.location
resource_group_name = var.resourceGroupName
account_tier = "Standard"
account_replication_type = "LRS"
allow_nested_items_to_be_public = false
shared_access_key_enabled = false
network_rules {
bypass = ["AzureServices"]
default_action = "Deny"
virtual_network_subnet_ids = [
var.deploymentSubnetId
]
}
}
# Resource: User Assigned Identity
resource "azurerm_user_assigned_identity" "privatedeploymanagedidentity" {
name = "mi-deploy-${var.resourceSuffix}"
location = var.location
resource_group_name = var.resourceGroupName
}
# Resource: Role Assignment
resource "azurerm_role_assignment" "privatedeploystorageroleassignment" {
scope = azurerm_storage_account.privatedeploystorage.id
role_definition_name = "Storage File Data Privileged Contributor"
principal_id = azurerm_user_assigned_identity.privatedeploymanagedidentity.principal_id
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_shared/shared.tf
================================================
data "azurerm_client_config" "current" {}
#-------------------------------
# Creation of a key vault instance
#-------------------------------
resource "azurerm_key_vault" "key_vault" {
name = var.keyVaultName
location = var.location
resource_group_name = var.resourceGroupName
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = var.keyVaultSku
# -> Bicep has keyvault as private, should we change this?
# -> This will need the certificate to be created through a azurerm_template_deployment resource
public_network_access_enabled = false
network_acls {
bypass = "AzureServices"
default_action = "Deny"
}
}
locals {
# deployment_client_ids = toset(
# concat(
# [data.azurerm_client_config.current.object_id],
# var.additionalClientIds
# )
# )
privateEndpoint_keyvault_Name = "pep-kv-${var.resourceSuffix}"
apim_cs_vnet_name = "vnet-apim-cs-${var.resourceSuffix}"
networkingResourceGroupName = "rg-networking-${var.resourceSuffix}"
private_endpoint_subnet_name = "snet-prep-${var.resourceSuffix}"
}
# 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
resource "azurerm_key_vault_access_policy" "deployment_spn_access_policy" {
key_vault_id = azurerm_key_vault.key_vault.id
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id
key_permissions = [
"Get",
]
secret_permissions = [
"Get",
]
storage_permissions = [
"Get",
]
certificate_permissions = [
"Import",
"Get",
"List",
"Update",
"Create"
]
}
data "azurerm_virtual_network" "apim_cs_vnet" {
name = local.apim_cs_vnet_name
resource_group_name = local.networkingResourceGroupName
}
data "azurerm_subnet" "private_endpoint_subnet" {
name = local.private_endpoint_subnet_name
resource_group_name = local.networkingResourceGroupName
virtual_network_name = local.apim_cs_vnet_name
}
module "keyvault_private_endpoint" {
source = "./private_endpoint"
name = local.privateEndpoint_keyvault_Name
location = var.location
resource_group_name = local.networkingResourceGroupName
subnet_id = data.azurerm_subnet.private_endpoint_subnet.id
private_connection_resource_id = azurerm_key_vault.key_vault.id
is_manual_connection = false
subresource_name = "vault"
private_dns_zone_group_name = "KeyVaultPrivateDnsZoneGroup"
private_dns_zone_group_ids = [var.keyVaultPrivateDnsZoneId]
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_shared/variables.tf
================================================
variable "location" {
type = string
description = "The Azure location in which the deployment is happening"
default = "eastus"
}
variable "resourceSuffix" {
type = string
description = "A suffix for naming"
}
variable "environment" {
type = string
description = "Environment"
default = "dev"
}
variable "resourceGroupName" {
type = string
description = "The name of the resource group"
}
variable "keyVaultName" {
type = string
description = "The name of the Key Vault"
}
variable "keyVaultSku" {
type = string
description = "The Name of the SKU used for this Key Vault. Possible values are standard and premium"
default = "standard"
}
variable "keyVaultPrivateDnsZoneId" {
type = string
description = "The ID of the private DNS zone for the Key Vault"
}
variable "additionalClientIds" {
description = "List of additional clients to add to the Key Vault access policy."
type = list(string)
default = []
}
variable "deploymentSubnetId" {
type = string
}
variable "storage_account_name" {
type = string
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_traffic_manager/outputs.tf
================================================
output "fqdn" {
value = azurerm_traffic_manager_profile.tm1.fqdn
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_traffic_manager/trafficmanager.tf
================================================
resource "azurerm_traffic_manager_profile" "tm1" {
name = var.name
resource_group_name = var.resourceGroupName
traffic_routing_method = "Performance"
dns_config {
relative_name = var.name
ttl = 60
}
monitor_config {
expected_status_code_ranges = ["200-299"]
path = var.probe_url
port = 443
protocol = "HTTPS"
}
}
resource "azurerm_traffic_manager_azure_endpoint" "primaryEndpoint" {
name = var.primaryName
profile_id = azurerm_traffic_manager_profile.tm1.id
always_serve_enabled = false
weight = 100
target_resource_id = var.primaryPublicIpId
}
resource "azurerm_traffic_manager_azure_endpoint" "secondaryEndpoint" {
name = var.secondaryName
profile_id = azurerm_traffic_manager_profile.tm1.id
always_serve_enabled = false
weight = 100
target_resource_id = var.secondaryPublicIpId
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/multi_traffic_manager/variables.tf
================================================
variable "name" {
type = string
description = "The traffic manager profile name"
}
variable "environment" {
type = string
description = "Environment"
default = "dev"
}
variable "resourceGroupName" {
type = string
description = "The name of the resource group"
}
variable "primaryName" {
type = string
description = ""
}
variable "primaryPublicIpId" {
type = string
description = ""
}
variable "secondaryName" {
type = string
description = ""
}
variable "secondaryPublicIpId" {
type = string
description = ""
}
variable "probe_url" {
type = string
description = ""
default = "/status-0123456789abcdef"
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/networking/networking.tf
================================================
locals {
apim_cs_vnet_name = "vnet-apim-cs-${var.resourceSuffix}"
appgateway_subnet_name = "snet-apgw-${var.resourceSuffix}"
deploy_subnet_name = "snet-deploy-${var.resourceSuffix}"
appgateway_snnsg = "nsg-apgw-${var.resourceSuffix}"
private_endpoint_subnet_name = "snet-prep-${var.resourceSuffix}"
private_endpoint_snnsg = "nsg-prep-${var.resourceSuffix}"
apim_subnet_name = "snet-apim-${var.resourceSuffix}"
owner = "APIM Const Set"
appgateway_public_ipname = "pip-appgw-${var.resourceSuffix}"
apim_snnsg = "nsg-apim-${var.resourceSuffix}"
}
resource "azurerm_network_security_group" "appgateway_nsg" {
name = local.appgateway_snnsg
location = var.location
resource_group_name = var.resourceGroupName
security_rule {
name = "AllowHealthProbesInbound"
priority = 100
protocol = "*"
destination_port_range = "65200-65535"
access = "Allow"
direction = "Inbound"
source_port_range = "*"
source_address_prefix = "GatewayManager"
destination_address_prefix = "*"
}
security_rule {
name = "AllowTLSInbound"
priority = 110
protocol = "Tcp"
destination_port_range = "443"
access = "Allow"
direction = "Inbound"
source_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "AllowHTTPInbound"
priority = 111
protocol = "Tcp"
destination_port_range = "80"
access = "Allow"
direction = "Inbound"
source_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "AllowAzureLoadBalancerInbound"
priority = 121
protocol = "Tcp"
destination_port_range = "*"
access = "Allow"
direction = "Inbound"
source_port_range = "*"
source_address_prefix = "AzureLoadBalancer"
destination_address_prefix = "*"
}
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_network_security_group" "apim_snnsg_nsg" {
name = local.apim_snnsg
location = var.location
resource_group_name = var.resourceGroupName
security_rule {
name = "AllowApimVnetInbound"
priority = 2000
protocol = "Tcp"
destination_port_range = "3443"
access = "Allow"
direction = "Inbound"
source_port_range = "*"
source_address_prefix = "ApiManagement"
destination_address_prefix = "VirtualNetwork"
}
security_rule {
name = "apim-azure-infra-lb"
priority = 2010
protocol = "Tcp"
destination_port_range = "6390"
access = "Allow"
direction = "Inbound"
source_port_range = "*"
source_address_prefix = "AzureLoadBalancer"
destination_address_prefix = "VirtualNetwork"
}
security_rule {
name = "apim-azure-storage"
priority = 2000
protocol = "Tcp"
destination_port_range = "443"
access = "Allow"
direction = "Outbound"
source_port_range = "*"
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "Storage"
}
security_rule {
name = "apim-azure-sql"
priority = 2010
protocol = "Tcp"
destination_port_range = "1443"
access = "Allow"
direction = "Outbound"
source_port_range = "*"
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "SQL"
}
security_rule {
name = "apim-azure-kv"
priority = 2020
protocol = "Tcp"
destination_port_range = "443"
access = "Allow"
direction = "Outbound"
source_port_range = "*"
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "AzureKeyVault"
}
security_rule {
name = "apim-azure-monitor"
priority = 2030
protocol = "Tcp"
destination_port_range = "443"
access = "Allow"
direction = "Outbound"
source_port_range = "*"
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "AzureMonitor"
}
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_network_security_group" "private_endpoint_snnsg_nsg" {
name = local.private_endpoint_snnsg
location = var.location
resource_group_name = var.resourceGroupName
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_virtual_network" "apim_cs_vnet" {
name = local.apim_cs_vnet_name
location = var.location
resource_group_name = var.resourceGroupName
address_space = [var.apimCSVNetNameAddressPrefix]
tags = {
Owner = local.owner
}
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_subnet" "appgateway_subnet" {
name = local.appgateway_subnet_name
resource_group_name = var.resourceGroupName
virtual_network_name = azurerm_virtual_network.apim_cs_vnet.name
address_prefixes = [var.appGatewayAddressPrefix]
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_subnet_network_security_group_association" "appgateway_subnet" {
subnet_id = azurerm_subnet.appgateway_subnet.id
network_security_group_id = azurerm_network_security_group.appgateway_nsg.id
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_subnet" "private_endpoint_subnet" {
name = local.private_endpoint_subnet_name
resource_group_name = var.resourceGroupName
virtual_network_name = azurerm_virtual_network.apim_cs_vnet.name
address_prefixes = [var.privateEndpointAddressPrefix]
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_subnet_network_security_group_association" "private_endpoint_subnet" {
subnet_id = azurerm_subnet.private_endpoint_subnet.id
network_security_group_id = azurerm_network_security_group.private_endpoint_snnsg_nsg.id
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_subnet" "deploy_subnet" {
name = local.deploy_subnet_name
resource_group_name = var.resourceGroupName
virtual_network_name = azurerm_virtual_network.apim_cs_vnet.name
address_prefixes = [var.deploymentAddressPrefix]
service_endpoints = ["Microsoft.Storage"]
delegation {
name = "Microsoft.ContainerInstance.containerGroups"
service_delegation {
name = "Microsoft.ContainerInstance/containerGroups"
actions = ["Microsoft.Network/virtualNetworks/subnets/action"]
}
}
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_subnet" "apim_subnet" {
name = local.apim_subnet_name
resource_group_name = var.resourceGroupName
virtual_network_name = azurerm_virtual_network.apim_cs_vnet.name
address_prefixes = [var.apimAddressPrefix]
lifecycle {
#prevent_destroy = true
}
}
resource "azurerm_subnet_network_security_group_association" "apim_subnet" {
subnet_id = azurerm_subnet.apim_subnet.id
network_security_group_id = azurerm_network_security_group.apim_snnsg_nsg.id
lifecycle {
#prevent_destroy = true
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/networking/outputs.tf
================================================
output "apimSubnetId" {
value = azurerm_subnet.apim_subnet.id
}
output "appGatewaySubnetId" {
value = azurerm_subnet.appgateway_subnet.id
}
output "apimVnetId" {
value = azurerm_virtual_network.apim_cs_vnet.id
}
output "deploymentSubnetId" {
value = azurerm_subnet.deploy_subnet.id
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/networking/variables.tf
================================================
variable "location" {
type = string
description = "The Azure location in which the deployment is happening"
default = "eastus"
}
variable "resourceSuffix" {
type = string
description = "A suffix for naming"
}
variable "environment" {
type = string
description = "Environment"
default = "dev"
}
variable "resourceGroupName" {
type = string
description = "The name of the resource group"
}
variable "apimCSVNetNameAddressPrefix" {
description = "APIM CSV Net Name Address Prefix"
type = string
}
variable "appGatewayAddressPrefix" {
description = "App Gateway Address Prefix"
type = string
}
variable "apimAddressPrefix" {
description = "APIM Address Prefix"
type = string
}
variable "privateEndpointAddressPrefix" {
description = "Private Endpoint Address Prefix"
type = string
}
variable "deploymentAddressPrefix" {
description = "Deployment Address Prefix"
type = string
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/shared/azmon.tf
================================================
#-------------------------------
# Creation of log analytics workspace instance
#-------------------------------
resource "azurerm_log_analytics_workspace" "log_analytics_workspace" {
name = "log-${var.resourceSuffix}"
location = var.location
resource_group_name = var.resourceGroupName
sku = "PerGB2018"
retention_in_days = 30
lifecycle {
#prevent_destroy = true
}
}
#-------------------------------
# Creation of an application insight instance
#-------------------------------
resource "azurerm_application_insights" "shared_apim_insight" {
name = "appi-${var.resourceSuffix}"
location = var.location
resource_group_name = var.resourceGroupName
application_type = "web"
workspace_id = azurerm_log_analytics_workspace.log_analytics_workspace.id
lifecycle {
#prevent_destroy = true
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/shared/outputs.tf
================================================
output "workspaceId" {
description = "The id of the workspace"
value = azurerm_log_analytics_workspace.log_analytics_workspace.id
}
output "instrumentationKey" {
description = "The instrumentation key of the workspace"
value = azurerm_application_insights.shared_apim_insight.instrumentation_key
}
output "keyVaultId" {
value = azurerm_key_vault.key_vault.id
}
output "keyVaultName" {
value = azurerm_key_vault.key_vault.name
}
output "deploymentIdentityName" {
value = azurerm_user_assigned_identity.privatedeploymanagedidentity.name
}
output "deploymentStorageName" {
value = azurerm_storage_account.privatedeploystorage.name
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/shared/private_dns_zone/outputs.tf
================================================
output "id" {
description = "Specifies the resource id of the private dns zone"
value = azurerm_private_dns_zone.private_dns_zone.id
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/shared/private_dns_zone/privatednszone.tf
================================================
resource "azurerm_private_dns_zone" "private_dns_zone" {
name = var.name
resource_group_name = var.resource_group_name
tags = var.tags
lifecycle {
ignore_changes = [
tags
]
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "link" {
name = "link_to_${lower(basename(var.virtual_networks_to_link_id))}"
resource_group_name = var.resource_group_name
private_dns_zone_name = azurerm_private_dns_zone.private_dns_zone.name
virtual_network_id = var.virtual_networks_to_link_id
lifecycle {
ignore_changes = [
tags
]
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/shared/private_dns_zone/variables.tf
================================================
variable "name" {
description = "(Required) Specifies the name of the private dns zone"
type = string
}
variable "resource_group_name" {
description = "(Required) Specifies the resource group name of the private dns zone"
type = string
}
variable "tags" {
description = "(Optional) Specifies the tags of the private dns zone"
default = {}
}
variable "virtual_networks_to_link_id" {
description = "(Optional) Specifies the virtual networks id to which create a virtual network link"
type = string
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/shared/private_endpoint/outputs.tf
================================================
output "id" {
description = "Specifies the resource id of the private endpoint."
value = azurerm_private_endpoint.private_endpoint.id
}
output "private_dns_zone_group" {
description = "Specifies the private dns zone group of the private endpoint."
value = azurerm_private_endpoint.private_endpoint.private_dns_zone_group
}
output "private_dns_zone_configs" {
description = "Specifies the private dns zone(s) configuration"
value = azurerm_private_endpoint.private_endpoint.private_dns_zone_configs
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/shared/private_endpoint/privateendpoint.tf
================================================
resource "azurerm_private_endpoint" "private_endpoint" {
name = var.name
location = var.location
resource_group_name = var.resource_group_name
subnet_id = var.subnet_id
tags = var.tags
private_service_connection {
name = "${var.name}Connection"
private_connection_resource_id = var.private_connection_resource_id
is_manual_connection = var.is_manual_connection
subresource_names = try([var.subresource_name], null)
request_message = try(var.request_message, null)
}
private_dns_zone_group {
name = var.private_dns_zone_group_name
private_dns_zone_ids = var.private_dns_zone_group_ids
}
lifecycle {
ignore_changes = [
tags
]
}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/shared/private_endpoint/variables.tf
================================================
variable "name" {
description = "(Required) Specifies the name of the private endpoint. Changing this forces a new resource to be created."
type = string
}
variable "resource_group_name" {
description = "(Required) The name of the resource group. Changing this forces a new resource to be created."
type = string
}
variable "private_connection_resource_id" {
description = "(Required) Specifies the resource id of the private link service"
type = string
}
variable "location" {
description = "(Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created."
type = string
}
variable "subnet_id" {
description = "(Required) Specifies the resource id of the subnet"
type = string
}
variable "is_manual_connection" {
description = "(Optional) Specifies whether the private endpoint connection requires manual approval from the remote resource owner."
type = string
default = false
}
variable "subresource_name" {
description = "(Optional) Specifies a subresource name which the Private Endpoint is able to connect to."
type = string
default = null
}
variable "request_message" {
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."
type = string
default = null
}
variable "private_dns_zone_group_name" {
description = "(Required) Specifies the Name of the Private DNS Zone Group. Changing this forces a new private_dns_zone_group resource to be created."
type = string
}
variable "private_dns_zone_group_ids" {
description = "(Required) Specifies the list of Private DNS Zones to include within the private_dns_zone_group."
type = list(string)
}
variable "tags" {
description = "(Optional) Specifies the tags of the network security group"
default = {}
}
variable "private_dns" {
default = {}
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/shared/privatedeploy.tf
================================================
resource "azurerm_storage_account" "privatedeploystorage" {
name = var.storage_account_name
location = var.location
resource_group_name = var.resourceGroupName
account_tier = "Standard"
account_replication_type = "LRS"
allow_nested_items_to_be_public = false
shared_access_key_enabled = false
network_rules {
bypass = ["AzureServices"]
default_action = "Deny"
virtual_network_subnet_ids = [
var.deploymentSubnetId
]
}
}
# Resource: User Assigned Identity
resource "azurerm_user_assigned_identity" "privatedeploymanagedidentity" {
name = "mi-deploy-${var.resourceSuffix}"
location = var.location
resource_group_name = var.resourceGroupName
}
# Resource: Role Assignment
resource "azurerm_role_assignment" "privatedeploystorageroleassignment" {
scope = azurerm_storage_account.privatedeploystorage.id
role_definition_name = "Storage File Data Privileged Contributor"
principal_id = azurerm_user_assigned_identity.privatedeploymanagedidentity.principal_id
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/shared/shared.tf
================================================
data "azurerm_client_config" "current" {}
#-------------------------------
# Creation of a key vault instance
#-------------------------------
resource "azurerm_key_vault" "key_vault" {
name = var.keyVaultName
location = var.location
resource_group_name = var.resourceGroupName
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = var.keyVaultSku
# -> Bicep has keyvault as private, should we change this?
# -> This will need the certificate to be created through a azurerm_template_deployment resource
public_network_access_enabled = false
network_acls {
bypass = "AzureServices"
default_action = "Deny"
}
}
locals {
# deployment_client_ids = toset(
# concat(
# [data.azurerm_client_config.current.object_id],
# var.additionalClientIds
# )
# )
privateEndpoint_keyvault_Name = "pep-kv-${var.resourceSuffix}"
apim_cs_vnet_name = "vnet-apim-cs-${var.resourceSuffix}"
networkingResourceGroupName = "rg-networking-${var.resourceSuffix}"
private_endpoint_subnet_name = "snet-prep-${var.resourceSuffix}"
}
# 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
resource "azurerm_key_vault_access_policy" "deployment_spn_access_policy" {
key_vault_id = azurerm_key_vault.key_vault.id
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id
key_permissions = [
"Get",
]
secret_permissions = [
"Get",
]
storage_permissions = [
"Get",
]
certificate_permissions = [
"Import",
"Get",
"List",
"Update",
"Create"
]
}
data "azurerm_virtual_network" "apim_cs_vnet" {
name = local.apim_cs_vnet_name
resource_group_name = local.networkingResourceGroupName
}
data "azurerm_subnet" "private_endpoint_subnet" {
name = local.private_endpoint_subnet_name
resource_group_name = local.networkingResourceGroupName
virtual_network_name = local.apim_cs_vnet_name
}
module "keyvault_dns_zone" {
source = "./private_dns_zone"
name = "privatelink.vaultcore.azure.net"
resource_group_name = local.networkingResourceGroupName
virtual_networks_to_link_id = data.azurerm_virtual_network.apim_cs_vnet.id
}
module "keyvault_private_endpoint" {
source = "./private_endpoint"
name = local.privateEndpoint_keyvault_Name
location = var.location
resource_group_name = local.networkingResourceGroupName
subnet_id = data.azurerm_subnet.private_endpoint_subnet.id
private_connection_resource_id = azurerm_key_vault.key_vault.id
is_manual_connection = false
subresource_name = "vault"
private_dns_zone_group_name = "KeyVaultPrivateDnsZoneGroup"
private_dns_zone_group_ids = [module.keyvault_dns_zone.id]
}
================================================
FILE: scenarios/apim-baseline/terraform/modules/shared/variables.tf
================================================
variable "location" {
type = string
description = "The Azure location in which the deployment is happening"
default = "eastus"
}
variable "resourceSuffix" {
type = string
description = "A suffix for naming"
}
variable "environment" {
type = string
description = "Environment"
default = "dev"
}
variable "resourceGroupName" {
type = string
description = "The name of the resource group"
}
variable "keyVaultName" {
type = string
description = "The name of the Key Vault"
}
variable "keyVaultSku" {
type = string
description = "The Name of the SKU used for this Key Vault. Possible values are standard and premium"
default = "standard"
}
variable "additionalClientIds" {
description = "List of additional clients to add to the Key Vault access policy."
type = list(string)
default = []
}
variable "deploymentSubnetId" {
type = string
}
variable "storage_account_name" {
type = string
}
================================================
FILE: scenarios/apim-baseline/terraform/multi-region/multi-region-main.tf
================================================
resource "random_string" "suffix" {
length = 5
upper = false
special = false
}
locals {
resourceSuffix = "${var.workloadName}-${var.environment}-${var.location}-${var.identifier}"
networkingResourceGroupName = "rg-networking-${local.resourceSuffix}"
sharedResourceGroupName = "rg-shared-${local.resourceSuffix}"
apimResourceGroupName = "rg-apim-${local.resourceSuffix}"
keyVaultName = substr(lower(replace("kv-${var.workloadName}${random_string.suffix.result}", "-", "")), 0, 23)
storageAccountName = substr(lower(replace("sadep${var.workloadName}${random_string.suffix.result}", "-", "")), 0, 21)
# to support multi-region
resourceSuffix2nd
gitextract_6xk9lkjw/
├── .devcontainer/
│ └── devcontainer.json
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── lza-validate-multiregion-apim.yml
│ └── lza-validate-singleregion-apim.yml
├── .gitignore
├── .pre-commit-config.yaml
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── SECURITY.md
├── SUPPORT.md
├── docs/
│ ├── assets/
│ │ └── genai-capabilities.pptx
│ └── images/
│ └── APIM.vsdx
└── scenarios/
├── apim-baseline/
│ ├── README.md
│ ├── bicep/
│ │ ├── README.md
│ │ ├── apim/
│ │ │ ├── apim.bicep
│ │ │ └── modules/
│ │ │ ├── dnsrecords.bicep
│ │ │ └── kvaccess.bicep
│ │ ├── gateway/
│ │ │ ├── appgw.bicep
│ │ │ └── modules/
│ │ │ ├── certificate.bicep
│ │ │ └── certificateSecret.bicep
│ │ ├── main.bicep
│ │ ├── networking/
│ │ │ └── networking.bicep
│ │ └── shared/
│ │ ├── modules/
│ │ │ ├── azmon.bicep
│ │ │ ├── dnszone.bicep
│ │ │ ├── privatedeploy.bicep
│ │ │ └── privateendpoint.bicep
│ │ └── shared.bicep
│ └── terraform/
│ ├── README.md
│ ├── backend.tf.sample
│ ├── main.tf
│ ├── modules/
│ │ ├── apim/
│ │ │ ├── apim.tf
│ │ │ ├── outputs.tf
│ │ │ └── variables.tf
│ │ ├── dns/
│ │ │ ├── dnszone.tf
│ │ │ └── variables.tf
│ │ ├── gateway/
│ │ │ ├── certificate/
│ │ │ │ ├── certificate.tf
│ │ │ │ ├── providers.tf
│ │ │ │ └── variables.tf
│ │ │ ├── gateway.tf
│ │ │ ├── outputs.tf
│ │ │ └── variables.tf
│ │ ├── multi_apim/
│ │ │ ├── apim.tf
│ │ │ ├── outputs.tf
│ │ │ └── variables.tf
│ │ ├── multi_apim-dns-regional/
│ │ │ ├── dnszone.tf
│ │ │ └── variables.tf
│ │ ├── multi_gateway/
│ │ │ ├── certificate/
│ │ │ │ ├── certificate.tf
│ │ │ │ ├── providers.tf
│ │ │ │ └── variables.tf
│ │ │ ├── gateway.tf
│ │ │ ├── outputs.tf
│ │ │ └── variables.tf
│ │ ├── multi_private_dns_zone/
│ │ │ ├── outputs.tf
│ │ │ ├── privatednszone.tf
│ │ │ └── variables.tf
│ │ ├── multi_shared/
│ │ │ ├── azmon.tf
│ │ │ ├── outputs.tf
│ │ │ ├── private_endpoint/
│ │ │ │ ├── outputs.tf
│ │ │ │ ├── privateendpoint.tf
│ │ │ │ └── variables.tf
│ │ │ ├── privatedeploy.tf
│ │ │ ├── shared.tf
│ │ │ └── variables.tf
│ │ ├── multi_traffic_manager/
│ │ │ ├── outputs.tf
│ │ │ ├── trafficmanager.tf
│ │ │ └── variables.tf
│ │ ├── networking/
│ │ │ ├── networking.tf
│ │ │ ├── outputs.tf
│ │ │ └── variables.tf
│ │ └── shared/
│ │ ├── azmon.tf
│ │ ├── outputs.tf
│ │ ├── private_dns_zone/
│ │ │ ├── outputs.tf
│ │ │ ├── privatednszone.tf
│ │ │ └── variables.tf
│ │ ├── private_endpoint/
│ │ │ ├── outputs.tf
│ │ │ ├── privateendpoint.tf
│ │ │ └── variables.tf
│ │ ├── privatedeploy.tf
│ │ ├── shared.tf
│ │ └── variables.tf
│ ├── multi-region/
│ │ ├── multi-region-main.tf
│ │ └── variables.tf
│ ├── provider.tf
│ ├── single-region/
│ │ ├── single-region-main.tf
│ │ └── variables.tf
│ └── variables.tf
├── certs/
│ └── place-custom-cert-here
├── scripts/
│ ├── bicep/
│ │ ├── deploy-apim-baseline.sh
│ │ ├── deploy-workload-function.sh
│ │ └── deploy-workload-genai.sh
│ └── terraform/
│ ├── __destroy-apim-baseline.sh
│ ├── azure-backend-sample.sh
│ ├── deploy-apim-baseline.sh
│ ├── deploy-workload-genai-new.sh
│ ├── deploy-workload-genai.sh
│ ├── sample-multi-region.env
│ ├── sample.backend.hcl
│ ├── sample.env
│ └── test-apim-baseline-deployment.sh
├── workload-functions/
│ ├── README.md
│ └── bicep/
│ ├── README.md
│ ├── apim/
│ │ └── config.bicep
│ ├── backend/
│ │ ├── backend.bicep
│ │ └── modules/
│ │ └── networking.bicep
│ ├── deploy/
│ │ ├── deploy.bicep
│ │ └── modules/
│ │ └── networking.bicep
│ └── main.bicep
└── workload-genai/
├── README.md
├── bicep/
│ ├── README.md
│ ├── apim-policies/
│ │ ├── api-specs/
│ │ │ └── openapi-spec.json
│ │ ├── apiManagement.bicep
│ │ └── load-balancing/
│ │ ├── backends.bicep
│ │ └── lb-pool.bicep
│ ├── eventhub/
│ │ └── eventHub.bicep
│ ├── main.bicep
│ └── openai/
│ └── openai.bicep
├── policies/
│ ├── fragments/
│ │ ├── load-balancing/
│ │ │ ├── README.md
│ │ │ └── simple-priority-weighted.xml
│ │ ├── manage-spikes-with-payg/
│ │ │ ├── README.md
│ │ │ └── retry-with-payg.xml
│ │ ├── rate-limiting/
│ │ │ ├── README.md
│ │ │ ├── adaptive-rate-limiting.xml
│ │ │ ├── rate-limiting-by-tokens.xml
│ │ │ └── rate-limiting-workaround.xml
│ │ └── usage-tracking/
│ │ ├── README.md
│ │ ├── usage-tracking-with-appinsights.xml
│ │ └── usage-tracking-with-eventhub.xml
│ ├── genai-policy.xml
│ └── multi-tenancy/
│ ├── README.md
│ ├── multi-tenant-product1-policy.xml
│ └── multi-tenant-product2-policy.xml
└── terraform/
├── README.md
├── backend.tf.sample
├── main.tf
├── modules/
│ ├── apim_policies/
│ │ ├── api-specs/
│ │ │ └── openapi-spec.json
│ │ ├── apimanagement.tf
│ │ ├── backends/
│ │ │ ├── backends.tf
│ │ │ └── providers.tf
│ │ ├── lb_pool/
│ │ │ ├── lb-pool.tf
│ │ │ └── providers.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── eventhub/
│ │ ├── eventhub.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── openai/
│ │ ├── openai.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── private_dns_zone/
│ │ ├── outputs.tf
│ │ ├── privatednszone.tf
│ │ └── variables.tf
│ └── private_endpoint/
│ ├── outputs.tf
│ ├── privateendpoint.tf
│ └── variables.tf
├── provider.tf
└── variables.tf
Condensed preview — 157 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (639K chars).
[
{
"path": ".devcontainer/devcontainer.json",
"chars": 1443,
"preview": "{\n\t\"name\": \"Getting Started\",\n\t\"image\": \"mcr.microsoft.com/devcontainers/universal:2\",\n\t\"hostRequirements\": {\n\t \"cpus\":"
},
{
"path": ".gitattributes",
"chars": 111,
"preview": "# 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",
"chars": 1178,
"preview": "# This is a basic workflow to help you get started with Actions\n\nname: LZA-Validation-Multi-Region-APIM\n\n# Controls when"
},
{
"path": ".github/workflows/lza-validate-singleregion-apim.yml",
"chars": 1167,
"preview": "# This is a basic workflow to help you get started with Actions\n\nname: LZA-Validation-Single-Region-APIM\n\n# Controls whe"
},
{
"path": ".gitignore",
"chars": 6168,
"preview": "/.vs/ProjectSettings.json\n/.vs/slnx.sqlite\ndeployment/bicep/parameters.local.json\ndeployment/bicep/localparam*.json\ndepl"
},
{
"path": ".pre-commit-config.yaml",
"chars": 133,
"preview": "- repo: https://github.com/antonbabenko/pre-commit-terraform\n rev: v1.76.0\n hooks:\n - id: terraform_fmt\n - id: t"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 444,
"preview": "# Microsoft Open Source Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://op"
},
{
"path": "LICENSE",
"chars": 1141,
"preview": " MIT License\n\n Copyright (c) Microsoft Corporation.\n\n Permission is hereby granted, free of charge, to any pers"
},
{
"path": "README.md",
"chars": 7172,
"preview": "# Azure API Management Landing Zone Accelerator\n\nAzure API Management Landing Zone Accelerator provides packaged guidanc"
},
{
"path": "SECURITY.md",
"chars": 2643,
"preview": "<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->\n\n# Security\n\nMicrosoft takes the security of our software products and"
},
{
"path": "SUPPORT.md",
"chars": 373,
"preview": "# Support\n\n## How to file issues and get help\n\nThis project uses GitHub Issues to track bugs and feature requests. Pleas"
},
{
"path": "scenarios/apim-baseline/README.md",
"chars": 1540,
"preview": "# Scenario 1: Azure API Management - Secure Baseline\n\nThis reference implementation demonstrates a *secure baseline infr"
},
{
"path": "scenarios/apim-baseline/bicep/README.md",
"chars": 3830,
"preview": "# Azure API Management - Secure Baseline [Bicep]\n\n**Note:**\nThis template may be out of date temporarily. Please use th"
},
{
"path": "scenarios/apim-baseline/bicep/apim/apim.bicep",
"chars": 3456,
"preview": "targetScope='resourceGroup'\n\n/*\n * Input parameters\n*/\n\n@description('The name of the API Management resource to be crea"
},
{
"path": "scenarios/apim-baseline/bicep/apim/modules/dnsrecords.bicep",
"chars": 1211,
"preview": "\nparam vnetName string\nparam networkingResourceGroupName string\nparam apimName string\n"
},
{
"path": "scenarios/apim-baseline/bicep/apim/modules/kvaccess.bicep",
"chars": 568,
"preview": "param keyVaultName string\nparam managedIdentity object \n\nresource accessPolicyGrant 'Microsoft.KeyV"
},
{
"path": "scenarios/apim-baseline/bicep/gateway/appgw.bicep",
"chars": 10493,
"preview": "/*\n * Input parameters\n*/\n@description('The name of the Application Gateawy to be created.')\nparam appGatewayName string"
},
{
"path": "scenarios/apim-baseline/bicep/gateway/modules/certificate.bicep",
"chars": 4705,
"preview": "param keyVaultName string\nparam managedIdentity object \nparam location string\npar"
},
{
"path": "scenarios/apim-baseline/bicep/gateway/modules/certificateSecret.bicep",
"chars": 271,
"preview": "param keyVaultName string\nparam secretName string\n\nresource keyVaultCertificate 'Microsoft.KeyVault/vaults/secrets@202"
},
{
"path": "scenarios/apim-baseline/bicep/main.bicep",
"chars": 5664,
"preview": "targetScope = 'subscription'\n\n// Parameters\n@description('A short name for the workload being deployed alphanumberic onl"
},
{
"path": "scenarios/apim-baseline/bicep/networking/networking.bicep",
"chars": 8100,
"preview": "param apimCSVNetNameAddressPrefix string = '10.2.0.0/16'\n\nparam appGatewayAddressPrefix string = '10.2.4.0/24'\nparam api"
},
{
"path": "scenarios/apim-baseline/bicep/shared/modules/azmon.bicep",
"chars": 1145,
"preview": "targetScope='resourceGroup'\n// Parameters\n@description('Azure location to which the resources are to be deployed')\nparam"
},
{
"path": "scenarios/apim-baseline/bicep/shared/modules/dnszone.bicep",
"chars": 704,
"preview": "param vnetName string\nparam networkingResourceGroupName string\nparam domain string\n\nresource vnet 'Microsoft.Network/vir"
},
{
"path": "scenarios/apim-baseline/bicep/shared/modules/privatedeploy.bicep",
"chars": 1566,
"preview": "param resourceSuffix string\nparam location string\nparam deploymentSubnetId string\n\nvar userAssignedIdentityName = 'mi-de"
},
{
"path": "scenarios/apim-baseline/bicep/shared/modules/privateendpoint.bicep",
"chars": 1670,
"preview": "param privateEndpointName string\nparam groupId string\nparam location string\nparam vnetName string\nparam networkingResour"
},
{
"path": "scenarios/apim-baseline/bicep/shared/shared.bicep",
"chars": 2999,
"preview": "targetScope='resourceGroup'\n// Parameters\n@description('A short name for the workload being deployed')\nparam workloadNam"
},
{
"path": "scenarios/apim-baseline/terraform/README.md",
"chars": 8162,
"preview": "# Azure API Management - Secure Baseline [Terraform]\n- Single Region Deployment\n- Optional Multi-Region High Availabilit"
},
{
"path": "scenarios/apim-baseline/terraform/backend.tf.sample",
"chars": 935,
"preview": "# terraform {\n# backend \"azurerm\" {\n# # ----------------------\n# # Will be passing in these arguments via "
},
{
"path": "scenarios/apim-baseline/terraform/main.tf",
"chars": 2410,
"preview": "# This module deploys an Azure API Management (APIM) service in a single region.\nmodule \"apim_baseline_single_region\" {\n"
},
{
"path": "scenarios/apim-baseline/terraform/modules/apim/apim.tf",
"chars": 6332,
"preview": "locals {\n apimName = \"apim-${var.resourceSuffix}\"\n apimPipPrimaryPip = \"pip-apim-${var.resourceSuffix}\"\n api"
},
{
"path": "scenarios/apim-baseline/terraform/modules/apim/outputs.tf",
"chars": 523,
"preview": "output \"primaryBackendendFqdn\" {\n value = azurerm_api_management.apim_internal.gateway_url\n}\n\noutput \"bakendUrl\" {\n va"
},
{
"path": "scenarios/apim-baseline/terraform/modules/apim/variables.tf",
"chars": 1944,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/modules/dns/dnszone.tf",
"chars": 4931,
"preview": "/* Creates a Private DNS ZOne, A Records and Vnet Link for each of the below endpoints\nAPI Gateway\t conto"
},
{
"path": "scenarios/apim-baseline/terraform/modules/dns/variables.tf",
"chars": 733,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/modules/gateway/certificate/certificate.tf",
"chars": 10024,
"preview": "##################################################\n# Tried creating the certificate using the #\n# azurerm_key_vaul"
},
{
"path": "scenarios/apim-baseline/terraform/modules/gateway/certificate/providers.tf",
"chars": 116,
"preview": "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",
"chars": 1417,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/modules/gateway/gateway.tf",
"chars": 6407,
"preview": "locals {\n appGatewayName = \"appgw-${var.resourceSuffix}\"\n appGatewayPrimaryPip = \"pip-appgw-${var.resourceSuff"
},
{
"path": "scenarios/apim-baseline/terraform/modules/gateway/outputs.tf",
"chars": 0,
"preview": ""
},
{
"path": "scenarios/apim-baseline/terraform/modules/gateway/variables.tf",
"chars": 1974,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_apim/apim.tf",
"chars": 6485,
"preview": "locals {\n apimName = \"apim-${var.resourceSuffix}\"\n apimPipPrimaryPip = \"pip-apim-${var.resourceSuffix}\"\n api"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_apim/outputs.tf",
"chars": 1332,
"preview": "output \"primaryBackendendFqdn\" {\n value = azurerm_api_management.apim_internal.gateway_url\n}\n\noutput \"bakendUrl\" {\n va"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_apim/variables.tf",
"chars": 2235,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_apim-dns-regional/dnszone.tf",
"chars": 2258,
"preview": "/* Creates a Private DNS ZOne, A Records and Vnet Link for each of the below endpoints\nAPI Gateway\t conto"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_apim-dns-regional/variables.tf",
"chars": 1065,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_gateway/certificate/certificate.tf",
"chars": 10011,
"preview": "##################################################\n# Tried creating the certificate using the #\n# azurerm_key_vaul"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_gateway/certificate/providers.tf",
"chars": 116,
"preview": "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",
"chars": 1417,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_gateway/gateway.tf",
"chars": 8064,
"preview": "locals {\n appGatewayName = \"appgw-${var.resourceSuffix}\"\n appGatewayPrimaryPip = \"pip-appgw-${var.resourceSuff"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_gateway/outputs.tf",
"chars": 214,
"preview": "output \"gw_pip_id\" {\n value = azurerm_public_ip.public_ip.id\n}\n\noutput \"gw_pip_fqdn\" {\n value = azurerm_public_ip.publ"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_gateway/variables.tf",
"chars": 1953,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_private_dns_zone/outputs.tf",
"chars": 145,
"preview": "output \"id\" {\n description = \"Specifies the resource id of the private dns zone\"\n value = azurerm_private_dns_zo"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_private_dns_zone/privatednszone.tf",
"chars": 1092,
"preview": "resource \"azurerm_private_dns_zone\" \"private_dns_zone\" {\n name = var.name\n resource_group_name = var.re"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_private_dns_zone/variables.tf",
"chars": 888,
"preview": "variable \"name\" {\n description = \"(Required) Specifies the name of the private dns zone\"\n type = string\n}\n\nvari"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_shared/azmon.tf",
"chars": 910,
"preview": "#-------------------------------\n# Creation of log analytics workspace instance\n#-------------------------------\n\nresour"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_shared/outputs.tf",
"chars": 663,
"preview": "output \"workspaceId\" {\n description = \"The id of the workspace\"\n value = azurerm_log_analytics_workspace.log_ana"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_shared/private_endpoint/outputs.tf",
"chars": 520,
"preview": "output \"id\" {\n description = \"Specifies the resource id of the private endpoint.\"\n value = azurerm_private_endpo"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_shared/private_endpoint/privateendpoint.tf",
"chars": 826,
"preview": "resource \"azurerm_private_endpoint\" \"private_endpoint\" {\n name = var.name\n location = var.lo"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_shared/private_endpoint/variables.tf",
"chars": 2033,
"preview": "variable \"name\" {\n description = \"(Required) Specifies the name of the private endpoint. Changing this forces a new res"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_shared/privatedeploy.tf",
"chars": 1123,
"preview": "\nresource \"azurerm_storage_account\" \"privatedeploystorage\" {\n name = var.storage_account_name\n loc"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_shared/shared.tf",
"chars": 2864,
"preview": "data \"azurerm_client_config\" \"current\" {}\n\n#-------------------------------\n# Creation of a key vault instance\n#--------"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_shared/variables.tf",
"chars": 1128,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_traffic_manager/outputs.tf",
"chars": 68,
"preview": "output \"fqdn\" {\n value = azurerm_traffic_manager_profile.tm1.fqdn\n}"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_traffic_manager/trafficmanager.tf",
"chars": 1025,
"preview": "resource \"azurerm_traffic_manager_profile\" \"tm1\" {\n name = var.name\n resource_group_name = var.re"
},
{
"path": "scenarios/apim-baseline/terraform/modules/multi_traffic_manager/variables.tf",
"chars": 713,
"preview": "variable \"name\" {\n type = string\n description = \"The traffic manager profile name\"\n}\n\nvariable \"environment\" {\n"
},
{
"path": "scenarios/apim-baseline/terraform/modules/networking/networking.tf",
"chars": 8195,
"preview": "locals {\n apim_cs_vnet_name = \"vnet-apim-cs-${var.resourceSuffix}\"\n appgateway_subnet_name = \"snet-ap"
},
{
"path": "scenarios/apim-baseline/terraform/modules/networking/outputs.tf",
"chars": 295,
"preview": "output \"apimSubnetId\" {\n value = azurerm_subnet.apim_subnet.id\n}\n\noutput \"appGatewaySubnetId\" {\n value = azurerm_subne"
},
{
"path": "scenarios/apim-baseline/terraform/modules/networking/variables.tf",
"chars": 995,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/modules/shared/azmon.tf",
"chars": 908,
"preview": "#-------------------------------\n# Creation of log analytics workspace instance\n#-------------------------------\n\nresour"
},
{
"path": "scenarios/apim-baseline/terraform/modules/shared/outputs.tf",
"chars": 663,
"preview": "output \"workspaceId\" {\n description = \"The id of the workspace\"\n value = azurerm_log_analytics_workspace.log_ana"
},
{
"path": "scenarios/apim-baseline/terraform/modules/shared/private_dns_zone/outputs.tf",
"chars": 145,
"preview": "output \"id\" {\n description = \"Specifies the resource id of the private dns zone\"\n value = azurerm_private_dns_zo"
},
{
"path": "scenarios/apim-baseline/terraform/modules/shared/private_dns_zone/privatednszone.tf",
"chars": 629,
"preview": "resource \"azurerm_private_dns_zone\" \"private_dns_zone\" {\n name = var.name\n resource_group_name = var.re"
},
{
"path": "scenarios/apim-baseline/terraform/modules/shared/private_dns_zone/variables.tf",
"chars": 542,
"preview": "variable \"name\" {\n description = \"(Required) Specifies the name of the private dns zone\"\n type = string\n}\n\nvari"
},
{
"path": "scenarios/apim-baseline/terraform/modules/shared/private_endpoint/outputs.tf",
"chars": 520,
"preview": "output \"id\" {\n description = \"Specifies the resource id of the private endpoint.\"\n value = azurerm_private_endpo"
},
{
"path": "scenarios/apim-baseline/terraform/modules/shared/private_endpoint/privateendpoint.tf",
"chars": 826,
"preview": "resource \"azurerm_private_endpoint\" \"private_endpoint\" {\n name = var.name\n location = var.lo"
},
{
"path": "scenarios/apim-baseline/terraform/modules/shared/private_endpoint/variables.tf",
"chars": 2033,
"preview": "variable \"name\" {\n description = \"(Required) Specifies the name of the private endpoint. Changing this forces a new res"
},
{
"path": "scenarios/apim-baseline/terraform/modules/shared/privatedeploy.tf",
"chars": 1123,
"preview": "\nresource \"azurerm_storage_account\" \"privatedeploystorage\" {\n name = var.storage_account_name\n loc"
},
{
"path": "scenarios/apim-baseline/terraform/modules/shared/shared.tf",
"chars": 3156,
"preview": "data \"azurerm_client_config\" \"current\" {}\n\n#-------------------------------\n# Creation of a key vault instance\n#--------"
},
{
"path": "scenarios/apim-baseline/terraform/modules/shared/variables.tf",
"chars": 997,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/multi-region/multi-region-main.tf",
"chars": 9987,
"preview": "resource \"random_string\" \"suffix\" {\n length = 5\n upper = false\n special = false\n}\n\nlocals {\n resourceSuffix "
},
{
"path": "scenarios/apim-baseline/terraform/multi-region/variables.tf",
"chars": 3667,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/provider.tf",
"chars": 740,
"preview": "terraform {\n\n # for storage backends, see backend.tf.sample\n\n required_providers {\n azurerm = {\n source = \"ha"
},
{
"path": "scenarios/apim-baseline/terraform/single-region/single-region-main.tf",
"chars": 4198,
"preview": "locals {\n resourceSuffix = \"${var.workloadName}-${var.environment}-${var.location}-${var.identifier}\"\n ne"
},
{
"path": "scenarios/apim-baseline/terraform/single-region/variables.tf",
"chars": 2418,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/apim-baseline/terraform/variables.tf",
"chars": 3955,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
},
{
"path": "scenarios/certs/place-custom-cert-here",
"chars": 0,
"preview": ""
},
{
"path": "scenarios/scripts/bicep/deploy-apim-baseline.sh",
"chars": 3438,
"preview": "#!/bin/bash\n\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nif [[ -f \"$script_d"
},
{
"path": "scenarios/scripts/bicep/deploy-workload-function.sh",
"chars": 3180,
"preview": "#!/bin/bash\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nif [[ -f \"$script_di"
},
{
"path": "scenarios/scripts/bicep/deploy-workload-genai.sh",
"chars": 4074,
"preview": "#!/bin/bash\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\n\nif [[ -f \"$script_di"
},
{
"path": "scenarios/scripts/terraform/__destroy-apim-baseline.sh",
"chars": 373,
"preview": "# validate if wants to proceed\n\nsource \"./.env\"\necho \"Using TFVARS: ../../apim-baseline/terraform/${ENVIRONMENT_TAG}.tfv"
},
{
"path": "scenarios/scripts/terraform/azure-backend-sample.sh",
"chars": 5412,
"preview": "#!/bin/bash\n\nset -e\n\n# This script sets up the backend configuration for Terraform in Azure.\n# It requires the user to b"
},
{
"path": "scenarios/scripts/terraform/deploy-apim-baseline.sh",
"chars": 11486,
"preview": "#!/bin/bash\n\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\nenv_file=\"./.env\"\n\nw"
},
{
"path": "scenarios/scripts/terraform/deploy-workload-genai-new.sh",
"chars": 9663,
"preview": "#!/bin/bash\n\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\nenv_file=\"./.env\"\n\nw"
},
{
"path": "scenarios/scripts/terraform/deploy-workload-genai.sh",
"chars": 8841,
"preview": "#!/bin/bash\n\n#echo \"Not updated yet...\"\n#exit 0\n\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null"
},
{
"path": "scenarios/scripts/terraform/sample-multi-region.env",
"chars": 205,
"preview": "AZURE_LOCATION='eastus2'\nRESOURCE_NAME_PREFIX='lzv01'\nENVIRONMENT_TAG='dev'\nAPPGATEWAY_FQDN='apim.example.com'\nCERT_TYPE"
},
{
"path": "scenarios/scripts/terraform/sample.backend.hcl",
"chars": 123,
"preview": "resource_group_name = \"my-resource-group\"\nstorage_account_name = \"mystorageaccount\"\ncontainer_name = \"my-containe"
},
{
"path": "scenarios/scripts/terraform/sample.env",
"chars": 206,
"preview": "AZURE_LOCATION='eastus2'\nRESOURCE_NAME_PREFIX='lzv01'\nENVIRONMENT_TAG='dev'\nAPPGATEWAY_FQDN='apim.example.com'\nCERT_TYPE"
},
{
"path": "scenarios/scripts/terraform/test-apim-baseline-deployment.sh",
"chars": 6182,
"preview": "#!/bin/bash\n\nset -e\n\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null 2>&1 && pwd )\"\nenv_file=\"./.env\"\n\n\n"
},
{
"path": "scenarios/workload-functions/README.md",
"chars": 1231,
"preview": "# Scenario 2: Azure API Management - Azure Functions as backend\n\nThis reference implementation demonstrates how to provi"
},
{
"path": "scenarios/workload-functions/bicep/README.md",
"chars": 810,
"preview": "# Scenario 2: Azure API Management - Azure Functions as backend [Bicep]\n\nThis is the Bicep-based deployment guide for ["
},
{
"path": "scenarios/workload-functions/bicep/apim/config.bicep",
"chars": 4058,
"preview": "param apimName string\n\nparam backendHostName string\n\nvar backendUri = '${backendHostName}/api/HttpExample'\n\nresource api"
},
{
"path": "scenarios/workload-functions/bicep/backend/backend.bicep",
"chars": 11658,
"preview": "param resourceSuffix string\n\nparam vnetName string\nparam networkingResourceGroupName string\n\nparam privateEndpointSubnet"
},
{
"path": "scenarios/workload-functions/bicep/backend/modules/networking.bicep",
"chars": 1035,
"preview": "param resourceSuffix string\n\nparam location string\n\nparam vnetName string\n\nparam backEndAddressPrefix string = '10.2.6.0"
},
{
"path": "scenarios/workload-functions/bicep/deploy/deploy.bicep",
"chars": 2082,
"preview": "param resourceSuffix string\nparam location string\nparam funcAppName string\nparam deploymentIdentityName string\nparam dep"
},
{
"path": "scenarios/workload-functions/bicep/deploy/modules/networking.bicep",
"chars": 832,
"preview": "param vnetName string\nparam resourceSuffix string\nparam deploymentAddressPrefix string = '10.2.8.0/24'\n\nvar deploymentSu"
},
{
"path": "scenarios/workload-functions/bicep/main.bicep",
"chars": 2376,
"preview": "targetScope='subscription'\n\nparam resourceSuffix string\nparam networkingResourceGroupName string\nparam apimResourceGroup"
},
{
"path": "scenarios/workload-genai/README.md",
"chars": 4167,
"preview": "# Scenario 3: Azure API Management - Generative AI resources as backend\n\nThis reference implementation demonstrates how "
},
{
"path": "scenarios/workload-genai/bicep/README.md",
"chars": 780,
"preview": "# Scenario 3: Azure API Management - Gen AI Backend [Bicep]\n\nThis is the Bicep-based deployment guide for [Scenario 3: "
},
{
"path": "scenarios/workload-genai/bicep/apim-policies/api-specs/openapi-spec.json",
"chars": 105467,
"preview": "{\n \"openapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"Azure OpenAI Service API\",\n \"description\": \"Azure OpenAI AP"
},
{
"path": "scenarios/workload-genai/bicep/apim-policies/apiManagement.bicep",
"chars": 9911,
"preview": "@description('The name of the API Management service instance')\nparam apiManagementServiceName string\n\n@description('The"
},
{
"path": "scenarios/workload-genai/bicep/apim-policies/load-balancing/backends.bicep",
"chars": 868,
"preview": "param apiManagementServiceName string\nparam backendUris array\n\nresource apiManagementService 'Microsoft.ApiManagement/se"
},
{
"path": "scenarios/workload-genai/bicep/apim-policies/load-balancing/lb-pool.bicep",
"chars": 563,
"preview": "param apiManagementServiceName string\nparam backends array\n\nresource apiManagementService 'Microsoft.ApiManagement/servi"
},
{
"path": "scenarios/workload-genai/bicep/eventhub/eventHub.bicep",
"chars": 2010,
"preview": "@description('The name of the Event Hub Namespace')\nparam eventHubNamespaceName string\n\n@description('The name of the Ev"
},
{
"path": "scenarios/workload-genai/bicep/main.bicep",
"chars": 4856,
"preview": "targetScope = 'subscription'\n\n@description('The name of the API Management service instance')\nparam apiManagementService"
},
{
"path": "scenarios/workload-genai/bicep/openai/openai.bicep",
"chars": 3357,
"preview": "@description('Name of the resource.')\nparam name string\n@description('Location to deploy the resource. Defaults to the l"
},
{
"path": "scenarios/workload-genai/policies/fragments/load-balancing/README.md",
"chars": 966,
"preview": "# Load balancing across PAYG, PTU instances\n\n## Capability\n\nAPI Management supports the following load balancing options"
},
{
"path": "scenarios/workload-genai/policies/fragments/load-balancing/simple-priority-weighted.xml",
"chars": 654,
"preview": "<!-- \n Policy fragment to implement a load balancing algorithm.\n\n Expected named values\n - The Pool backed that"
},
{
"path": "scenarios/workload-genai/policies/fragments/manage-spikes-with-payg/README.md",
"chars": 711,
"preview": "# Managing spike across PTU instances using PAYG deployments.\n\n## Capability\n\nIn this capability, the traffic is routed "
},
{
"path": "scenarios/workload-genai/policies/fragments/manage-spikes-with-payg/retry-with-payg.xml",
"chars": 2233,
"preview": "<!-- Policy fragment to use Pay As You Go (PAYG) endpoints when the PTU endpoints are busy.\n\n should be used inside t"
},
{
"path": "scenarios/workload-genai/policies/fragments/rate-limiting/README.md",
"chars": 3601,
"preview": "# Rate limiting using Tokens consumed per request\n\nIn Azure OpenAI, the rate limiting policy is based on the number of t"
},
{
"path": "scenarios/workload-genai/policies/fragments/rate-limiting/adaptive-rate-limiting.xml",
"chars": 1900,
"preview": "<fragment>\n <azure-openai-token-limit counter-key=\"GlobalTokensLimit\"\n tokens-per-minute=\"500\"\n estimat"
},
{
"path": "scenarios/workload-genai/policies/fragments/rate-limiting/rate-limiting-by-tokens.xml",
"chars": 518,
"preview": "<fragment>\n <!-- Rate limit configuration at a subscription level. Works for Azure OpenAI endpoint, \n both st"
},
{
"path": "scenarios/workload-genai/policies/fragments/rate-limiting/rate-limiting-workaround.xml",
"chars": 1001,
"preview": "<fragment>\n <!-- This is an alternate approach for scenarios where the `azure-openai-token-limit` policy can't be use"
},
{
"path": "scenarios/workload-genai/policies/fragments/usage-tracking/README.md",
"chars": 1962,
"preview": "# Usage tracking of tokens\n\n## Capability\n\nIn this setup, you can track the usage of your APIs by sending token usage da"
},
{
"path": "scenarios/workload-genai/policies/fragments/usage-tracking/usage-tracking-with-appinsights.xml",
"chars": 432,
"preview": "<fragment>\n <azure-openai-emit-token-metric namespace=\"aoai.token.usage\">\n <dimension name=\"SubscriptionId\" va"
},
{
"path": "scenarios/workload-genai/policies/fragments/usage-tracking/usage-tracking-with-eventhub.xml",
"chars": 1462,
"preview": "<fragment>\n <choose>\n <!--Chargeback model using eventhub logging \n\t\t\t\tConditional Logging to EventHub:\n\t\t\t\tLo"
},
{
"path": "scenarios/workload-genai/policies/genai-policy.xml",
"chars": 1285,
"preview": "<policies>\n <inbound>\n <base />\n \n <!-- sets the backed to the load balanced pool-->\n <in"
},
{
"path": "scenarios/workload-genai/policies/multi-tenancy/README.md",
"chars": 5256,
"preview": "# Multi-Tenancy using Azure API Management\n\nCustomers may sometimes would also like to have a multi-tenancy model on top"
},
{
"path": "scenarios/workload-genai/policies/multi-tenancy/multi-tenant-product1-policy.xml",
"chars": 1068,
"preview": "<!--\n - Policies are applied in the order they appear.\n - Position <base/> inside a section to inherit policies fr"
},
{
"path": "scenarios/workload-genai/policies/multi-tenancy/multi-tenant-product2-policy.xml",
"chars": 1068,
"preview": "<!--\n - Policies are applied in the order they appear.\n - Position <base/> inside a section to inherit policies fr"
},
{
"path": "scenarios/workload-genai/terraform/README.md",
"chars": 874,
"preview": "# Scenario 3: Azure API Management - Gen AI Backend [Terraform]\n\nThis is the Terraform-based deployment guide for [Scen"
},
{
"path": "scenarios/workload-genai/terraform/backend.tf.sample",
"chars": 935,
"preview": "# terraform {\n# backend \"azurerm\" {\n# # ----------------------\n# # Will be passing in these arguments via "
},
{
"path": "scenarios/workload-genai/terraform/main.tf",
"chars": 7700,
"preview": "locals {\n resourceSuffix = \"${var.workloadName}-${var.environment}-${var.location}-${var.identifier}\"\n n"
},
{
"path": "scenarios/workload-genai/terraform/modules/apim_policies/api-specs/openapi-spec.json",
"chars": 105467,
"preview": "{\n \"openapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"Azure OpenAI Service API\",\n \"description\": \"Azure OpenAI AP"
},
{
"path": "scenarios/workload-genai/terraform/modules/apim_policies/apimanagement.tf",
"chars": 11919,
"preview": "locals {\n azureOpenAIAPINames = [azurerm_api_management_api.azureOpenAIApi.name]\n}\n\ndata \"azurerm_api_management\" \"apiM"
},
{
"path": "scenarios/workload-genai/terraform/modules/apim_policies/backends/backends.tf",
"chars": 1336,
"preview": "variable \"api_management_service_name\" {\n type = string\n}\n\nvariable \"backend_uris\" {\n type = list(string)\n}\n\nvariable "
},
{
"path": "scenarios/workload-genai/terraform/modules/apim_policies/backends/providers.tf",
"chars": 116,
"preview": "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",
"chars": 1171,
"preview": "variable \"api_management_service_name\" {\n type = string\n}\n\nvariable \"backends\" {\n type = list(string)\n}\n\n# variable \"b"
},
{
"path": "scenarios/workload-genai/terraform/modules/apim_policies/lb_pool/providers.tf",
"chars": 116,
"preview": "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",
"chars": 484,
"preview": "output \"apiManagementServiceName\" {\n description = \"The name of the API Management service instance\"\n value = va"
},
{
"path": "scenarios/workload-genai/terraform/modules/apim_policies/variables.tf",
"chars": 1300,
"preview": "variable \"resourceGroupName\" {\n type = string\n description = \"The name of the resource group\"\n}\n\nvariable \"apiM"
},
{
"path": "scenarios/workload-genai/terraform/modules/eventhub/eventhub.tf",
"chars": 1167,
"preview": "resource \"azurerm_eventhub_namespace\" \"eventHubNamespace\" {\n name = var.eventHubNamespaceName\n locatio"
},
{
"path": "scenarios/workload-genai/terraform/modules/eventhub/outputs.tf",
"chars": 275,
"preview": "output \"eventHubNamespaceName\" {\n description = \"The name of the Event Hub Namespace.\"\n value = azurerm_eventhub"
},
{
"path": "scenarios/workload-genai/terraform/modules/eventhub/variables.tf",
"chars": 628,
"preview": "variable \"eventHubNamespaceName\" {\n description = \"The name of the Event Hub Namespace\"\n type = string\n}\n\nvaria"
},
{
"path": "scenarios/workload-genai/terraform/modules/openai/openai.tf",
"chars": 1587,
"preview": "data \"azurerm_user_assigned_identity\" \"apimIdentity\" {\n name = var.apimIdentityName\n resource_group_nam"
},
{
"path": "scenarios/workload-genai/terraform/modules/openai/outputs.tf",
"chars": 1424,
"preview": "output \"id\" {\n value = azurerm_cognitive_account.openai.id\n description = \"Specifies the resource id of the log analyt"
},
{
"path": "scenarios/workload-genai/terraform/modules/openai/variables.tf",
"chars": 1696,
"preview": "variable \"resource_group_name\" {\n description = \"(Required) Specifies the resource group name\"\n type = string\n}"
},
{
"path": "scenarios/workload-genai/terraform/modules/private_dns_zone/outputs.tf",
"chars": 145,
"preview": "output \"id\" {\n description = \"Specifies the resource id of the private dns zone\"\n value = azurerm_private_dns_zo"
},
{
"path": "scenarios/workload-genai/terraform/modules/private_dns_zone/privatednszone.tf",
"chars": 629,
"preview": "resource \"azurerm_private_dns_zone\" \"private_dns_zone\" {\n name = var.name\n resource_group_name = var.re"
},
{
"path": "scenarios/workload-genai/terraform/modules/private_dns_zone/variables.tf",
"chars": 542,
"preview": "variable \"name\" {\n description = \"(Required) Specifies the name of the private dns zone\"\n type = string\n}\n\nvari"
},
{
"path": "scenarios/workload-genai/terraform/modules/private_endpoint/outputs.tf",
"chars": 520,
"preview": "output \"id\" {\n description = \"Specifies the resource id of the private endpoint.\"\n value = azurerm_private_endpo"
},
{
"path": "scenarios/workload-genai/terraform/modules/private_endpoint/privateendpoint.tf",
"chars": 826,
"preview": "resource \"azurerm_private_endpoint\" \"private_endpoint\" {\n name = var.name\n location = var.lo"
},
{
"path": "scenarios/workload-genai/terraform/modules/private_endpoint/variables.tf",
"chars": 2033,
"preview": "variable \"name\" {\n description = \"(Required) Specifies the name of the private endpoint. Changing this forces a new res"
},
{
"path": "scenarios/workload-genai/terraform/provider.tf",
"chars": 509,
"preview": "terraform {\n\n # for storage backends, see backend.tf.sample\n \n required_providers {\n azurerm = {\n source = \""
},
{
"path": "scenarios/workload-genai/terraform/variables.tf",
"chars": 3313,
"preview": "variable \"location\" {\n type = string\n description = \"The Azure location in which the deployment is happening\"\n "
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the Azure/apim-landing-zone-accelerator GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 157 files (577.2 KB), approximately 136.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.