From 1e7201f51e291522788f0708debcd3409bf2e466 Mon Sep 17 00:00:00 2001 From: Chad Kittel Date: Wed, 10 May 2023 02:36:34 -0500 Subject: [PATCH] QoL: Bicep formatting & commenting, learn links, and typos (#59) --- .ado/README.md | 22 ++-- CONTRIBUTING.md | 101 +++++++------- docs/design-areas/identity.md | 4 +- docs/design-areas/security.md | 12 +- scenarios/aca-internal/bicep/main.bicep | 14 +- .../aca-internal/bicep/main.parameters.jsonc | 6 +- .../bicep/modules/01-hub/deploy.hub.bicep | 63 ++++----- .../01-hub/deploy.hub.parameters.jsonc | 2 +- .../bicep/modules/02-spoke/deploy.spoke.bicep | 114 ++++++++-------- .../02-spoke/deploy.spoke.parameters.jsonc | 2 +- .../nsgContainerAppsEnvironment.jsonc | 2 +- .../deploy.supporting-services.bicep | 35 +++-- ...eploy.supporting-services.parameters.jsonc | 2 +- .../deploy.aca-environment.bicep | 124 ++++++++++-------- .../deploy.aca-environment.parameters.jsonc | 2 +- .../deploy.hello-world.bicep | 17 ++- .../modules/06-application-gateway/README.md | 4 +- .../configuration/Readme.md | 35 ++--- .../deploy.app-gateway.bicep | 45 ++++--- .../deploy.app-gateway.parameters.jsonc | 12 +- .../modules/app-gateway-config.bicep | 14 +- .../06-front-door/deploy.front-door.bicep | 4 +- .../deploy.front-door.parameters.jsonc | 2 +- .../dotnet-task-tracker-service/main.bicep | 4 +- .../java-fine-collection-service/main.bicep | 10 +- .../main.parameters.jsonc | 6 +- .../modules/container-apps.bicep | 4 +- .../modules/container-apps/simulation.bicep | 4 +- .../traffic-control-service.bicep | 4 +- .../shared/bicep/naming/naming-rules.jsonc | 2 +- .../shared/bicep/naming/naming.module.bicep | 2 +- scenarios/shared/bicep/nsg.bicep | 2 +- scenarios/shared/bicep/private-dns-zone.bicep | 2 +- scenarios/shared/bicep/private-endpoint.bicep | 2 +- scenarios/shared/bicep/vnet.bicep | 2 +- 35 files changed, 378 insertions(+), 304 deletions(-) diff --git a/.ado/README.md b/.ado/README.md index 13b9bb16..93d347f8 100644 --- a/.ado/README.md +++ b/.ado/README.md @@ -1,21 +1,25 @@ # Azure Pipeline Deployment + If you'd like to use an Azure Pipeline to deploy the ACA Landing Zone Accelerator, you will need: + - A fork of the ACA Landing Zone repository - An Azure DevOps project -- A [service connection](https://learn.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml) available for your pipeline that connects to your Azure subscription +- A [service connection](https://learn.microsoft.com/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml) available for your pipeline that connects to your Azure subscription - A variable group called "ACA-LZA" that contains the following variables: - location: The location of where you want the Azure resources deployed - azureServiceConnection: the name of the service connection you created in the previous step -# Create your pipeline +## Create your pipeline + After you've created the items in the previous step, follow these instructions for creating your pipeline. -1. Navigate into your Azure DevOps projects and click on Pipelines on the left sidebar. -2. Click *New Pipeline* in the upper right hand corner of the window or the *create pipeline* button in the middle if this is your first pipeline. -3. Select *GitHub* as the source for your YAML. -4. Select your repository in GitHub. If you don't already have the Azure Pipeline app installed in your GitHub repository, it will prompt you to enable that and redirect you back to this creation screen. -5. Select *Existing Azure Pipelines YAML file*, select the main branch and the file *lza-deployment-bicep.yaml*. -6. Once you select the file, hit next and then click *Run* in the upper right hand corner of the *Review* tab. If you don't want to run it immediately, you can click the dropdown on the *Run* button and choose to save it. + +1. Navigate into your Azure DevOps projects and click on Pipelines on the left sidebar. +1. Click *New Pipeline* in the upper right hand corner of the window or the *create pipeline* button in the middle if this is your first pipeline. +1. Select *GitHub* as the source for your YAML. +1. Select your repository in GitHub. If you don't already have the Azure Pipeline app installed in your GitHub repository, it will prompt you to enable that and redirect you back to this creation screen. +1. Select *Existing Azure Pipelines YAML file*, select the main branch and the file *lza-deployment-bicep.yaml*. +1. Once you select the file, hit next and then click *Run* in the upper right hand corner of the *Review* tab. If you don't want to run it immediately, you can click the dropdown on the *Run* button and choose to save it. ### Note -When you first run your pipeline, you may need to give the pipeline permission to access the service connection and the variable group. This will only occur the first time you run the pipeline. +When you first run your pipeline, you may need to give the pipeline permission to access the service connection and the variable group. This will only occur the first time you run the pipeline. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e0b49bf9..cf44a7d2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,33 +19,36 @@ --- ## 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. + +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 . 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. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments. +This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact with any additional questions or comments. The rest of this document outlines a few important notes/guidelines to help us to start contributing to this *Landing Zone Accelerator* project effectively. ## GitHub Operations, Conventions and other Standards ### GitHub Basics + The following guides provide basic knowledge for understanding Git command usage and the workflow of GitHub. -- [Introduction to version control with Git](https://docs.microsoft.com/learn/paths/intro-to-vc-git/) +- [Introduction to version control with Git](https://learn.microsoft.com/learn/paths/intro-to-vc-git/) - [Git Cheat Sheet](https://education.github.com/git-cheat-sheet-education.pdf) - [GitHub Flow](https://guides.github.com/introduction/flow/) ### Folder Structure and Naming Conventions -- Github uses ASCII for ordering files and folders. For consistent ordering create all files and folders in **lowercase**. The only exception to this guideline is the *common supporting files* such as README.md, CONTRIBUTING.md etc files, that should be in the format **UPPERCASE.lowercase**. Remember that there are operating systems that handle files with different casing as distinct files, so we need to avoid these kind of conflicts. -- Avoid **camelCase** for files and folders consisting of two or more words. Use only lowercase and append words with dashes, i.e. **`folder-name-one`** and **not** `folderNameOne` + +- Github uses ASCII for ordering files and folders. For consistent ordering create all files and folders in **lowercase**. The only exception to this guideline is the *common supporting files* such as README.md, CONTRIBUTING.md etc files, that should be in the format **UPPERCASE.lowercase**. Remember that there are operating systems that handle files with different casing as distinct files, so we need to avoid these kind of conflicts. +- Avoid **camelCase** for files and folders consisting of two or more words. Use only lowercase and append words with dashes, i.e. **`folder-name-one`** and **not** `folderNameOne` > NOTE: the aforementioned rules can be overridden, if any Language Coding Styles or Guidelines instruct the usage of different conventions -Below you can see the selected folder structure for the project. The main folders and a brief description of their purpose is as follows: +Below you can see the selected folder structure for the project. The main folders and a brief description of their purpose is as follows: - **docs** The *docs* folder contains two subfolders; **design-areas** and **media**. - - The **design-areas** subfolder contains the relevant documentation. + - The **design-areas** subfolder contains the relevant documentation. - The **media** subfolder will contain images or other media file types used in the README.md files. Folder structure inside that subfolder is optional and free to the grouping desires of every author. For instance if you create the README.md file describing the architecture of the *scenario1* scenario, and *scenario1* sub-folder may be created to group all supporting media files together. In the same context, if the *Design Area* documents (as described above) need some supporting media material, we can add them in this subfolder or create a new subfolder, named *design-areas* and add them all there, for grouping purposes - **scenarios** @@ -57,8 +60,8 @@ Below you can see the selected folder structure for the project. The main folder - (scenario1)\README.md Outlines the details of the specific scenario (architecture, resources to be deployed, business case scenarios etc) for the given scenario - (scenario1)\sample-apps - This folder may contain one or more subfolders, depending on the selected sample applications that will be created to serve as smoke tests or best-practices examples using the specific Landing Zone Accelerator artifacts. Folder structure inside each sample application sub-folder is free. - - *shared* + This folder may contain one or more subfolders, depending on the selected sample applications that will be created to serve as smoke tests or best-practices examples using the specific Landing Zone Accelerator artifacts. Folder structure inside each sample application sub-folder is free. + - *shared* To avoid duplication of code modules/artifacts, we store all scripts, modules or coding artifacts in general, in this subfolder. This folder can have more depth, i.e. one folder for every deployment method (i.e. bicep, terraform etc) as shown below in the sample folder structure. Contains also a README.md file that gives details of the shared modules/scripts to help end-users understand their functionality. ``` bash @@ -115,8 +118,9 @@ Unless you are working with multiple contributors on the same file, we ask that - [Contributing to GitHub projects](https://guides.github.com/activities/forking/). ### Branch Naming Standards + For branches, use the following prefixes depending on which is most applicable to the work being done: -| Prefix | Purpose | +| Prefix | Purpose | |-------------|-----------| |fix/|Any task related to a bug or minor fix| |feat/|Any task related to a new feature of the codebase| @@ -126,10 +130,11 @@ For branches, use the following prefixes depending on which is most applicable t |test/|Any task pertaining to testing updates | ### Commit Standards (optional) + Prefixing the commits as described below, is **optional**, however is **highly encouraged**. For commits, use the following prefixes depending on which is most applicable to the changes: -| Prefix | Purpose | -|-------------|-----------| +| Prefix | Purpose | +|-------------|-----------| |fix:|Update to code base or bug| |feat:|New feature added to code base| |chore:|Basic task to update formatting or versions, etc.| @@ -137,23 +142,25 @@ For commits, use the following prefixes depending on which is most applicable to |ci:|New workflow or updates to workflow(s) | |test:|New tests or updates to testing framework(s) | -## Style Guide and coding conventions -A guide outlining the coding conventions and style guidelines that should be followed when contributing code to the repository is outlined below: +## Style Guide and coding conventions + +A guide outlining the coding conventions and style guidelines that should be followed when contributing code to the repository is outlined below: ### Bicep Best Practices and Conventions -- The starting point for any deployment should be named **main.bicep**. This (usually) should be the main deployment file, scoped at *subscription* level, and it would call several sub-deployments, usually at the resource group scope. - - The **main.bicep** file should be accompanied with a parameter file named **main.parameters.jsonc**. The benefit of the `*.jsonc` file extension is that you can use inline comments (either `//` or `/* ... */`) in Visual Studio Code (otherwise you will get an error message saying "*Comments not permitted in JSON*"). [Bicep Parameter Files](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/parameter-files) + +- The starting point for any deployment should be named **main.bicep**. This (usually) should be the main deployment file, scoped at *subscription* level, and it would call several sub-deployments, usually at the resource group scope. + - The **main.bicep** file should be accompanied with a parameter file named **main.parameters.jsonc**. The benefit of the `*.jsonc` file extension is that you can use inline comments (either `//` or `/* ... */`) in Visual Studio Code (otherwise you will get an error message saying "*Comments not permitted in JSON*"). [Bicep Parameter Files](https://learn.microsoft.com/azure/azure-resource-manager/bicep/parameter-files) - Details of using the deployment should be given in the README.md file. However if we need extra scripts to either deploy the bicep files or other functionality use a naming convention as the following - deploy.main.sh: for bash-based script deploying the main.bicep - deploy.main.ps1: for PowerShell-based script deploying the main.bicep -- Do not include compiled versions of the bicep files (i.e. no `main.json ` files) +- Do not include compiled versions of the bicep files (i.e. no `main.json` files) -- Follow the best practices as defined in [Best practices for Bicep](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/best-practices). +- Follow the best practices as defined in [Best practices for Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/best-practices). - Use strictly `camelCasing` for all elements like parameters, variables, resources, modules and outputs, and avoid prepending those elements in a [Hungarian Notation](https://en.wikipedia.org/wiki/Hungarian_notation) style. - - Put always the resource type identifier in front accompanied, where applicable, from some conext, like hub or spoke. For example, `vnetHubName` is better than `vnetName` or `hubVNetName`. - - Use only well known abreviations like *Url, Fqdn, Vnet*, adhering to the CamelCasing rule. + - Put always the resource type identifier in front accompanied, where applicable, from some context, like hub or spoke. For example, `vnetHubName` is better than `vnetName` or `hubVNetName`. + - Use only well known abbreviations like *Url, Fqdn, Vnet*, adhering to the CamelCasing rule. - Each parameter, variable or resource identifier should be descriptive and pronounceable. For example, `vmName` is better than `vmnm`. - Outputs should also be descriptive. Do not output just `name` or `id` but prefer to output as `Name` and `Id` to avoid confusion, especially in the case where a module outputs many different resources. For example, use `storageAccountName` and `storageAccountId`, which is better than just `name` and `id`. - All parameters and outputs should be documented using the `@description` decorator. @@ -168,12 +175,13 @@ A guide outlining the coding conventions and style guidelines that should be fol ``` - If naming of Azure resources is chosen to be done manually, follow some best practices as defined in: - - [Define your naming convention](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) - - Use abbreviations of resource type as proposed in [Abbreviation examples for Azure resources](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) document - - Always take into consideration [naming rules and restrictions for Azure resources](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/resource-name-rules) + - [Define your naming convention](https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) + - Use abbreviations of resource type as proposed in [Abbreviation examples for Azure resources](https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) document + - Always take into consideration [naming rules and restrictions for Azure resources](https://learn.microsoft.com/azure/azure-resource-manager/management/resource-name-rules) **Bad practice examples that should be avoided:** - ``` bicep + + ```bicep \\ BAD PRACTICE param EXAMPLES param parVmSubnetAddressPrefix string param strVmSubnetAddressPrefix string @@ -202,8 +210,9 @@ A guide outlining the coding conventions and style guidelines that should be fol output containerRegistryId string = containerRegistry.id ``` -- Bicep is a declarative language, which means the elements can appear in any order. In reality you can put parameter declarations anywhere in the template file, and the same you can do for resources, variables and outputs. However it is highly recommended that any bicep template file to adhere to the following order `Parameters > Variables > Resources/Modules > Outputs` as shown in the code snippet. - ``` bicep +- Bicep is a declarative language, which means the elements can appear in any order. In reality you can put parameter declarations anywhere in the template file, and the same you can do for resources, variables and outputs. However it is highly recommended that any bicep template file to adhere to the following order `Parameters > Variables > Resources/Modules > Outputs` as shown in the code snippet. + + ```bicep targetScope = 'subscription' // ------------------ @@ -253,12 +262,12 @@ A guide outlining the coding conventions and style guidelines that should be fol ``` -- Use [parameter decorators](https://docs.microsoft.com/azure/azure-resource-manager/bicep/parameters#decorators) to ensure integrity of user inputs are complete and therefore enable successful deployment - - Use the [`@secure()` parameter decorator](https://docs.microsoft.com/azure/azure-resource-manager/bicep/parameters#secure-parameters) **ONLY** for inputs. Never for outputs as this is not stored securely and will be stored/shown as plain-text! +- Use [parameter decorators](https://learn.microsoft.com/azure/azure-resource-manager/bicep/parameters#decorators) to ensure integrity of user inputs are complete and therefore enable successful deployment + - Use the [`@secure()` parameter decorator](https://learn.microsoft.com/azure/azure-resource-manager/bicep/parameters#secure-parameters) **ONLY** for inputs. Never for outputs as this is not stored securely and will be stored/shown as plain-text! - All parameters should have a meaningful `@description` decorator - Use constraints where possible, allowed values, min/max, but Use the `@allowed` decorator sparingly, as it can mistakenly result in blocking valid deployments - - If more than one parameter decorators are present, the `@description` decorator should always come first. - - Avoid prompting for parameter value at runtime. Parameters should either be initialized in the bicep template file and/or in the accompanying paramater file. + - If more than one parameter decorators are present, the `@description` decorator should always come first. + - Avoid prompting for parameter value at runtime. Parameters should either be initialized in the bicep template file and/or in the accompanying parameter file. - `targetScope` should always be indicated at the beginning of the bicep template file @@ -267,14 +276,16 @@ A guide outlining the coding conventions and style guidelines that should be fol - Parameters and variables should be named according to their use on specific properties where applicable. For example a parameter used for the name property on a Storage Account would be named `storageAccountName` rather than simple `name` or `storageAccount`. A parameter used for the size of a VM should be `vmSize` rather than `size`. As well, parameters, variables and outputs that related to a specific resource should use the resource's symbolic name as a prefix. -- Consider sanitizing names of resources to avoid deployment errors. For example consider the name limitations for a storage account (all lowercase, less than 24 characters long, no dashes etc). - ``` bicep +- Consider sanitizing names of resources to avoid deployment errors. For example consider the name limitations for a storage account (all lowercase, less than 24 characters long, no dashes etc). + + ```bicep var maxStorageNameLength = 24 var storageName = take( toLower(substring(replace(name, '-', ''), 0, maxStorageNameLength)), maxStorageNameLength) - ``` -- Use bicep **parameter** files for giving the end user the ability to parametrize the deployed resources. (i.e. to select CIDR network spaces, to select SKUs for given resources etc). As a rule of thumb, avoid using the parameter file for *naming resources*, unless there is a really good reason for that. Naming resources should be handled centrally (preferably with variables), following specific rules (as already described). Try not to overuse parameters in the template, because this creates a burden on your template users, since they need to understand the values to use for each resource, and the impact of setting each parameter. Consider using the [t-shirt sizing pattern](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/patterns-configuration-set#solution) + ``` -- Avoid using `dependsOn` in the bicep template files. Bicep is building [implicit depedencies](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/resource-dependencies#implicit-dependency) for us, as long as we follow some good practices rules. For instance a resource A depends on a Resource B (i.e. a storage Account) chances are that in resource A you need somehow to pass data of the Resource B(i.e. name, ID etc.). In that case, avoid passing the resource name as string, but pass the property Name of the resource instead (i.e. `myStorage.Name`) +- Use bicep **parameter** files for giving the end user the ability to parametrize the deployed resources. (i.e. to select CIDR network spaces, to select SKUs for given resources etc). As a rule of thumb, avoid using the parameter file for *naming resources*, unless there is a really good reason for that. Naming resources should be handled centrally (preferably with variables), following specific rules (as already described). Try not to overuse parameters in the template, because this creates a burden on your template users, since they need to understand the values to use for each resource, and the impact of setting each parameter. Consider using the [t-shirt sizing pattern](https://learn.microsoft.com/azure/azure-resource-manager/bicep/patterns-configuration-set#solution) + +- Avoid using `dependsOn` in the bicep template files. Bicep is building [implicit dependencies](https://learn.microsoft.com/azure/azure-resource-manager/bicep/resource-dependencies#implicit-dependency) for us, as long as we follow some good practices rules. For instance a resource A depends on a Resource B (i.e. a storage Account) chances are that in resource A you need somehow to pass data of the Resource B(i.e. name, ID etc.). In that case, avoid passing the resource name as string, but pass the property Name of the resource instead (i.e. `myStorage.Name`) ``` bicep var storageName='ttst20230301' @@ -282,10 +293,10 @@ var storageName='ttst20230301' resource resourceModuleA 'module/someResource' = { name: 'myResource' - //This is wrong, does NOT build implicit depedency + //This is wrong, does NOT build implicit dependency //storageAccountName: storageName - //This is OK, does build implicit depedency + //This is OK, does build implicit dependency storageAccountName: storage.name } @@ -307,15 +318,17 @@ resource storage 'Microsoft.Storage/storageAccounts@2019-06-01' = { ``` -**More details for the afforementioned guidelines you may find at:** -- [Bicep Best Practices](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/best-practices) -- [Deployment Scripts in Bicep](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/deployment-script-bicep) -- [Configuration Map Pattern and t-shirt sizing](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/patterns-configuration-set): Can be used to provide a smaller set of parameters -- [Logical Parameters](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/patterns-logical-parameter) -- [Azure Resource Naming Convention](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) -- [Azure Resource Abbreviations](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +**More details for the aforementioned guidelines you may find at:** + +- [Bicep Best Practices](https://learn.microsoft.com/azure/azure-resource-manager/bicep/best-practices) +- [Deployment Scripts in Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/deployment-script-bicep) +- [Configuration Map Pattern and t-shirt sizing](https://learn.microsoft.com/azure/azure-resource-manager/bicep/patterns-configuration-set): Can be used to provide a smaller set of parameters +- [Logical Parameters](https://learn.microsoft.com/azure/azure-resource-manager/bicep/patterns-logical-parameter) +- [Azure Resource Naming Convention](https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) +- [Azure Resource Abbreviations](https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) ### Terraform Best Practices and Conventions + - Use modules: Terraform modules allow you to reuse and share code across different projects and teams. This helps to reduce duplication of effort and increases consistency - Use Terraform variables: Use Terraform variables to provide input values and make your code more flexible and easier to maintain. - Use Terraform outputs: Use Terraform outputs to extract information about your infrastructure for later use. diff --git a/docs/design-areas/identity.md b/docs/design-areas/identity.md index fba87493..6bc1afac 100644 --- a/docs/design-areas/identity.md +++ b/docs/design-areas/identity.md @@ -18,6 +18,6 @@ - Use system-assigned managed identities unless there is a strong requirement for using user-managed identities -- Use Azure [built-in roles](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#web-plan-contributor) to assign least privilege permissions to resources and users. +- Use Azure [built-in roles](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#web-plan-contributor) to assign least privilege permissions to resources and users. -- Ensure that access to production environments is limited. Ideally, no one has standing access to production environment, instead relying on automation to handle deployments and [Privileged Identity Management](https://learn.microsoft.com/azure/active-directory/privileged-identity-management/pim-configure) for emergency access. \ No newline at end of file +- Ensure that access to production environments is limited. Ideally, no one has standing access to production environment, instead relying on automation to handle deployments and [Privileged Identity Management](https://learn.microsoft.com/azure/active-directory/privileged-identity-management/pim-configure) for emergency access. \ No newline at end of file diff --git a/docs/design-areas/security.md b/docs/design-areas/security.md index a865a1b4..c6c69439 100644 --- a/docs/design-areas/security.md +++ b/docs/design-areas/security.md @@ -5,9 +5,9 @@ Security is a one of the foundational design principles and also a key design ar --- ### Design Area Considerations -- Cloud application security in enterprise environments is built in a layered approach. Microsoft provides recommendations for securing your application, as does the enterprise security organization governing cloud solutions. Finally, the solution developer has a responsibility to implement the appropriate security measures for the solution. - - The [Container Apps security profile](https://learn.microsoft.com/security/benchmark/azure/baselines/azure-container-apps-security-baseline) from the Microsoft [Cloud Security Benchmark](https://learn.microsoft.com/en-us/security/benchmark/azure/overview) provides a good starting point - - The [Azure Landing Zone security documentation](https://learn.microsoft.com/azure/cloud-adoption-framework/secure/), part of the Cloud Adoption Framework, provides an additional security view on Landing Zones. +- Cloud application security in enterprise environments is built in a layered approach. Microsoft provides recommendations for securing your application, as does the enterprise security organization governing cloud solutions. Finally, the solution developer has a responsibility to implement the appropriate security measures for the solution. + - The [Container Apps security profile](https://learn.microsoft.com/security/benchmark/azure/baselines/azure-container-apps-security-baseline) from the Microsoft [Cloud Security Benchmark](https://learn.microsoft.com/security/benchmark/azure/overview) provides a good starting point + - The [Azure Landing Zone security documentation](https://learn.microsoft.com/azure/cloud-adoption-framework/secure/), part of the Cloud Adoption Framework, provides an additional security view on Landing Zones. - Azure Container Apps allows for _internal_ and _external_ connectivity models. This allows for connecting the Container Apps to the outside world directly or not at all. In this Landing Zone Accelerator, we recommend the _internal_ model and assume that the application will be either fully internal facing or will be published externally through a service that adds the appropriate security controls, such as Azure Application Gateway. The security architecture would then look as follows (the box with Application Gateway being optional in internal-facing scenarios): ![ACA Hub and Spoke architecture](../media/acaInternal/aca-internal.png) @@ -26,8 +26,8 @@ Security is a one of the foundational design principles and also a key design ar ## References -- [Container Apps security profile](https://learn.microsoft.com/security/benchmark/azure/baselines/azure-container-apps-security-baseline) -- [Cloud Security Benchmark](https://learn.microsoft.com/en-us/security/benchmark/azure/overview) -- [Securing a custom VNET in Azure Container Apps](https://learn.microsoft.com/en-us/azure/container-apps/firewall-integration) +- [Container Apps security profile](https://learn.microsoft.com/security/benchmark/azure/baselines/azure-container-apps-security-baseline) +- [Cloud Security Benchmark](https://learn.microsoft.com/security/benchmark/azure/overview) +- [Securing a custom VNET in Azure Container Apps](https://learn.microsoft.com/azure/container-apps/firewall-integration) - [Azure Landing Zone security documentation](https://learn.microsoft.com/azure/cloud-adoption-framework/secure/) \ No newline at end of file diff --git a/scenarios/aca-internal/bicep/main.bicep b/scenarios/aca-internal/bicep/main.bicep index 00470c48..53ec09e2 100644 --- a/scenarios/aca-internal/bicep/main.bicep +++ b/scenarios/aca-internal/bicep/main.bicep @@ -5,10 +5,10 @@ targetScope = 'subscription' // ------------------ @minLength(2) @maxLength(10) -@description('The name of the workloard that is being deployed. Up to 10 characters long.') +@description('The name of the workload that is being deployed. Up to 10 characters long.') param workloadName string = 'aca-lza' -@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa") Up to 8 characters long.') +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') @maxLength(8) param environment string = 'test' @@ -33,7 +33,7 @@ param enableBastion bool param bastionSubnetAddressPrefix string // Hub Virtual Machine -@description('The size of the virtual machine to create. See https://docs.microsoft.com/en-us/azure/virtual-machines/sizes for more information.') +@description('The size of the virtual machine to create. See https://learn.microsoft.com/azure/virtual-machines/sizes for more information.') param vmSize string @description('The username to use for the virtual machine.') @@ -78,8 +78,8 @@ param enableDaprInstrumentation bool @description('Enable or disable the deployment of the Hello World Sample App. If disabled, the Application Gateway will not be deployed.') param deployHelloWorldSample bool -@description('The FQDN of the Application Gateawy. Must match the TLS Certificate.') -param applicationGatewayFQDN string +@description('The FQDN of the Application Gateway. Must match the TLS Certificate.') +param applicationGatewayFqdn string @description('Enable or disable Application Gateway Certificate (PFX).') param enableApplicationGatewayCertificate bool @@ -197,8 +197,8 @@ module applicationGateway 'modules/06-application-gateway/deploy.app-gateway.bic environment: environment workloadName: workloadName applicationGatewayCertificateKeyName: applicationGatewayCertificateKeyName - applicationGatewayFQDN: applicationGatewayFQDN - applicationGatewayPrimaryBackendEndFQDN: (deployHelloWorldSample) ? helloWorlSampleApp.outputs.helloWorldAppFQDN : '' // To fix issue when hello world is not deployed + applicationGatewayFqdn: applicationGatewayFqdn + applicationGatewayPrimaryBackendEndFqdn: (deployHelloWorldSample) ? helloWorlSampleApp.outputs.helloWorldAppFqdn : '' // To fix issue when hello world is not deployed applicationGatewaySubnetId: spoke.outputs.spokeApplicationGatewaySubnetId enableApplicationGatewayCertificate: enableApplicationGatewayCertificate keyVaultId: supportingServices.outputs.keyVaultId diff --git a/scenarios/aca-internal/bicep/main.parameters.jsonc b/scenarios/aca-internal/bicep/main.parameters.jsonc index b24e7458..b6387a27 100644 --- a/scenarios/aca-internal/bicep/main.parameters.jsonc +++ b/scenarios/aca-internal/bicep/main.parameters.jsonc @@ -2,7 +2,7 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - // The name of the workloard that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) + // The name of the workload that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) "workloadName": { "value": "lzaaca" }, @@ -75,8 +75,8 @@ "deployHelloWorldSample": { "value": true }, - // The FQDN of the Application Gateawy. Must match the TLS Certificate. - "applicationGatewayFQDN": { + // The FQDN of the Application Gateway. Must match the TLS Certificate. + "applicationGatewayFqdn": { "value": "acahello.demoapp.com" }, "enableApplicationGatewayCertificate": { diff --git a/scenarios/aca-internal/bicep/modules/01-hub/deploy.hub.bicep b/scenarios/aca-internal/bicep/modules/01-hub/deploy.hub.bicep index c6bf0891..d881ee03 100644 --- a/scenarios/aca-internal/bicep/modules/01-hub/deploy.hub.bicep +++ b/scenarios/aca-internal/bicep/modules/01-hub/deploy.hub.bicep @@ -6,10 +6,10 @@ targetScope = 'subscription' @minLength(2) @maxLength(10) -@description('The name of the workloard that is being deployed. Up to 10 characters long.') +@description('The name of the workload that is being deployed. Up to 10 characters long.') param workloadName string -@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa") Up to 8 characters long.') +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') @maxLength(8) param environment string @@ -22,38 +22,37 @@ param hubResourceGroupName string = '' @description('Optional. The tags to be assigned to the created resources.') param tags object = {} -@description('CIDR of the Hub Virtual Network.') +@description('CIDR of the hub virtual network.') param vnetAddressPrefixes array -// Bastion @description('Enable or disable the creation of the Azure Bastion.') param enableBastion bool @description('CIDR to use for the Azure Bastion subnet.') param bastionSubnetAddressPrefix string -@description('The size of the virtual machine to create. See https://docs.microsoft.com/en-us/azure/virtual-machines/sizes for more information.') +@description('The size of the jump box virtual machine to create. See https://learn.microsoft.com/azure/virtual-machines/sizes for more information.') param vmSize string -@description('The username to use for the virtual machine.') +@description('The username to use for the jump box.') param vmAdminUsername string -@description('The password to use for the virtual machine.') +@description('The password to use for the jump box.') @secure() param vmAdminPassword string -@description('The SSH public key to use for the virtual machine.') +@description('The SSH public key to use for the jump box. Only relevant for Linux.') @secure() param vmLinuxSshAuthorizedKeys string -@description('The type of the virtual machine OS to create. If set to "none", no virtual machine will be created.') -@allowed(['linux', 'windows', 'none']) +@description('The OS of the jump box virtual machine to create. If set to "none", no jump box will be created.') +@allowed([ 'linux', 'windows', 'none' ]) param vmJumpboxOSType string = 'none' -@description('Optional. The name of the subnet to create for the virtual machine. If set, it overrides the name generated by the template.') +@description('Optional. The name of the subnet to create for the jump box. If set, it overrides the name generated by the template.') param vmSubnetName string = 'snet-jumpbox' -@description('CIDR to use for the virtual machine subnet.') +@description('CIDR to use for the jump box subnet.') param vmJumpBoxSubnetAddressPrefix string // ------------------ @@ -68,39 +67,40 @@ var bastionSubnetName = 'AzureBastionSubnet' // Append optional bastion subnet, if required var subnets = enableBastion ? concat(defaultSubnets, [ - { - name: bastionSubnetName - properties: { - addressPrefix: bastionSubnetAddressPrefix + { + name: bastionSubnetName + properties: { + addressPrefix: bastionSubnetAddressPrefix + } } - } -]) : defaultSubnets + ]) : defaultSubnets //Append optional jumpbox subnet, if required var vnetSubnets = vmJumpboxOSType != 'none' ? concat(subnets, [ - { - name: vmSubnetName - properties: { - addressPrefix: vmJumpBoxSubnetAddressPrefix - } - } -]) : subnets + { + name: vmSubnetName + properties: { + addressPrefix: vmJumpBoxSubnetAddressPrefix + } + } + ]) : subnets //used only to override the RG name - because it is created at the subscription level, the naming module cannot be loaded/used var namingRules = json(loadTextContent('../../../../shared/bicep/naming/naming-rules.jsonc')) var rgHubName = !empty(hubResourceGroupName) ? hubResourceGroupName : '${namingRules.resourceTypeAbbreviations.resourceGroup}-${workloadName}-hub-${environment}-${namingRules.regionAbbreviations[toLower(location)]}' - // ------------------ -// RESOURCES"TCP" +// RESOURCES // ------------------ +@description('The hub resource group. This would normally be already provisioned by your platform team.') resource hubResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: rgHubName location: location tags: tags } +@description('User-configured naming rules') module naming '../../../../shared/bicep/naming/naming.module.bicep' = { scope: hubResourceGroup name: take('01-sharedNamingDeployment-${deployment().name}', 64) @@ -112,6 +112,7 @@ module naming '../../../../shared/bicep/naming/naming.module.bicep' = { } } +@description('The virtual network used as the stand-in for the regional hub. This would normally be already provisioned by your platform team.') module vnetHub '../../../../shared/bicep/vnet.bicep' = { name: take('vnetHub-${deployment().name}', 64) scope: hubResourceGroup @@ -124,6 +125,7 @@ module vnetHub '../../../../shared/bicep/vnet.bicep' = { } } +@description('An optional Azure Bastion deployment for jump box access. This would normally be already provisioned by your platform team.') module bastion './modules/bastion.bicep' = if (enableBastion) { name: take('bastion-${deployment().name}', 64) scope: hubResourceGroup @@ -137,8 +139,9 @@ module bastion './modules/bastion.bicep' = if (enableBastion) { bastionSubnetAddressPrefix: bastionSubnetAddressPrefix bastionVNetName: vnetHub.outputs.vnetName } - } +} +@description('An optional Linux virtual machine deployment to act as a jump box.') module jumpboxLinuxVM './modules/vm/linux-vm.bicep' = if (vmJumpboxOSType == 'linux') { name: take('vm-linux-${deployment().name}', 64) scope: hubResourceGroup @@ -158,6 +161,7 @@ module jumpboxLinuxVM './modules/vm/linux-vm.bicep' = if (vmJumpboxOSType == 'li } } +@description('An optional Windows virtual machine deployment to act as a jump box.') module jumpboxWindowsVM './modules/vm/windows-vm.bicep' = if (vmJumpboxOSType == 'windows') { name: take('vm-windows-${deployment().name}', 64) scope: hubResourceGroup @@ -176,7 +180,6 @@ module jumpboxWindowsVM './modules/vm/windows-vm.bicep' = if (vmJumpboxOSType == } } - // ------------------ // OUTPUTS // ------------------ @@ -184,5 +187,5 @@ module jumpboxWindowsVM './modules/vm/windows-vm.bicep' = if (vmJumpboxOSType == @description('The resource ID of hub virtual network.') output hubVNetId string = vnetHub.outputs.vnetId -@description('The name of the Hub resource group.') +@description('The name of the hub resource group.') output resourceGroupName string = hubResourceGroup.name diff --git a/scenarios/aca-internal/bicep/modules/01-hub/deploy.hub.parameters.jsonc b/scenarios/aca-internal/bicep/modules/01-hub/deploy.hub.parameters.jsonc index 3633c7b0..f4081a4e 100644 --- a/scenarios/aca-internal/bicep/modules/01-hub/deploy.hub.parameters.jsonc +++ b/scenarios/aca-internal/bicep/modules/01-hub/deploy.hub.parameters.jsonc @@ -2,7 +2,7 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - // The name of the workloard that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) + // The name of the workload that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) "workloadName": { "value": "lzaaca" }, diff --git a/scenarios/aca-internal/bicep/modules/02-spoke/deploy.spoke.bicep b/scenarios/aca-internal/bicep/modules/02-spoke/deploy.spoke.bicep index 169b6ab7..17082371 100644 --- a/scenarios/aca-internal/bicep/modules/02-spoke/deploy.spoke.bicep +++ b/scenarios/aca-internal/bicep/modules/02-spoke/deploy.spoke.bicep @@ -6,14 +6,14 @@ targetScope = 'subscription' @minLength(2) @maxLength(10) -@description('The name of the workloard that is being deployed. Up to 10 characters long.') +@description('The name of the workload that is being deployed. Up to 10 characters long.') param workloadName string -@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa") Up to 8 characters long.') +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') @maxLength(8) param environment string -@description('The location where the resources will be created.') +@description('The location where the resources will be created. This should be the same region as the hub.') param location string = deployment().location @description('Optional. The name of the resource group to create the resources in. If set, it overrides the name generated by the template.') @@ -23,30 +23,31 @@ param spokeResourceGroupName string param tags object = {} // Hub -@description('The resource ID of the Hub Virtual Network.') + +@description('The resource ID of the existing hub virtual network.') param hubVNetId string // Spoke -@description('CIDR of the Spoke Virtual Network.') +@description('CIDR of the spoke virtual network. For most landing zone implementations, the spoke network would have been created by your platform team.') param spokeVNetAddressPrefixes array @description('Optional. The name of the subnet to create for the spoke infrastructure. If set, it overrides the name generated by the template.') param spokeInfraSubnetName string = 'snet-infra' -@description('CIDR of the Spoke Infrastructure Subnet.') +@description('CIDR of the spoke infrastructure subnet.') param spokeInfraSubnetAddressPrefix string @description('Optional. The name of the subnet to create for the spoke private endpoints. If set, it overrides the name generated by the template.') param spokePrivateEndpointsSubnetName string = 'snet-pep' -@description('CIDR of the Spoke Private Endpoints Subnet.') +@description('CIDR of the spoke private endpoints subnet.') param spokePrivateEndpointsSubnetAddressPrefix string @description('Optional. The name of the subnet to create for the spoke application gateway. If set, it overrides the name generated by the template.') param spokeApplicationGatewaySubnetName string = 'snet-agw' -@description('CIDR of the Spoke Application Gateway Subnet. If the value is emnpty, the subnet will not be created.') +@description('CIDR of the spoke Application Gateway subnet. If the value is empty, this subnet will not be created.') param spokeApplicationGatewaySubnetAddressPrefix string // ------------------ @@ -54,14 +55,20 @@ param spokeApplicationGatewaySubnetAddressPrefix string // ------------------ // load as text (and not as Json) to replace placeholder in the nsg rules -var nsgRules = json( replace( loadTextContent('./nsgContainerAppsEnvironment.jsonc') , '', location) ) +var nsgRules = json(replace(loadTextContent('./nsgContainerAppsEnvironment.jsonc'), '', location)) var namingRules = json(loadTextContent('../../../../shared/bicep/naming/naming-rules.jsonc')) var rgSpokeName = !empty(spokeResourceGroupName) ? spokeResourceGroupName : '${namingRules.resourceTypeAbbreviations.resourceGroup}-${workloadName}-spoke-${environment}-${namingRules.regionAbbreviations[toLower(location)]}' -var hubVNetResourIdTokens = !empty(hubVNetId) ? split(hubVNetId, '/') : array('') -var hubSubscriptionId = hubVNetResourIdTokens[2] -var hubResourceGroupName = hubVNetResourIdTokens[4] -var hubVNetName = hubVNetResourIdTokens[8] +var hubVNetResourceIdTokens = !empty(hubVNetId) ? split(hubVNetId, '/') : array('') + +@description('The ID of the subscription containing the hub virtual network.') +var hubSubscriptionId = hubVNetResourceIdTokens[2] + +@description('The name of the resource group containing the hub virtual network.') +var hubResourceGroupName = hubVNetResourceIdTokens[4] + +@description('The name of the hub virtual network.') +var hubVNetName = hubVNetResourceIdTokens[8] // Subnet definition taking in consideration feature flags var defaultSubnets = [ @@ -92,17 +99,18 @@ var spokeSubnets = !empty(spokeApplicationGatewaySubnetAddressPrefix) ? concat(d } ]) : defaultSubnets - // ------------------ // RESOURCES // ------------------ +@description('The spoke resource group. This would normally be already provisioned by your subscription vending process.') resource spokeResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: rgSpokeName location: location tags: tags } +@description('User-configured naming rules') module naming '../../../../shared/bicep/naming/naming.module.bicep' = { scope: spokeResourceGroup name: take('02-sharedNamingDeployment-${deployment().name}', 64) @@ -114,6 +122,7 @@ module naming '../../../../shared/bicep/naming/naming.module.bicep' = { } } +@description('The spoke virtual network in which the workload will run from. This virtual network would normally already be provisioned by your subscription vending process, and only the subnets would need to be configured.') module vnetSpoke '../../../../shared/bicep/vnet.bicep' = { name: take('vnetSpoke-${deployment().name}', 64) scope: spokeResourceGroup @@ -126,6 +135,7 @@ module vnetSpoke '../../../../shared/bicep/vnet.bicep' = { } } +@description('Network security group rules for the Container Apps cluster.') module nsgContainerAppsEnvironment '../../../../shared/bicep/nsg.bicep' = { name: take('nsgContainerAppsEnvironment-${deployment().name}', 64) scope: spokeResourceGroup @@ -136,7 +146,9 @@ module nsgContainerAppsEnvironment '../../../../shared/bicep/nsg.bicep' = { securityRules: nsgRules.securityRules } } -module peerSpokeToHub '../../../../shared/bicep/peering.bicep' = if (!empty(hubVNetId)) { + +@description('Spoke peering to regional hub network. This peering would normally already be provisioned by your subscription vending process.') +module peerSpokeToHub '../../../../shared/bicep/peering.bicep' = if (!empty(hubVNetId)) { name: take('${deployment().name}-peerSpokeToHubDeployment', 64) scope: spokeResourceGroup params: { @@ -147,64 +159,62 @@ module peerSpokeToHub '../../../../shared/bicep/peering.bicep' = if (!empty(hubV } } -module peerHubToSpoke '../../../../shared/bicep/peering.bicep' = if (!empty(hubVNetId) ) { +@description('Regional hub peering to this spoke network. This peering would normally already be provisioned by your subscription vending process.') +module peerHubToSpoke '../../../../shared/bicep/peering.bicep' = if (!empty(hubVNetId)) { name: take('${deployment().name}-peerHubToSpokeDeployment', 64) scope: resourceGroup(hubSubscriptionId, hubResourceGroupName) - params: { - localVnetName: hubVNetName - remoteSubscriptionId: last(split(subscription().id, '/'))! - remoteRgName: spokeResourceGroup.name - remoteVnetName: vnetSpoke.outputs.vnetName + params: { + localVnetName: hubVNetName + remoteSubscriptionId: last(split(subscription().id, '/'))! + remoteRgName: spokeResourceGroup.name + remoteVnetName: vnetSpoke.outputs.vnetName } } +// ------------------ +// OUTPUTS +// ------------------ + resource vnetSpokeCreated 'Microsoft.Network/virtualNetworks@2022-07-01' existing = { name: vnetSpoke.outputs.vnetName scope: spokeResourceGroup -} -resource spokeInfraSubnet 'Microsoft.Network/virtualNetworks/subnets@2022-07-01' existing = { - parent: vnetSpokeCreated - name: spokeInfraSubnetName -} + resource spokeInfraSubnet 'subnets' existing = { + name: spokeInfraSubnetName + } -resource spokePrivateEndpointsSubnet 'Microsoft.Network/virtualNetworks/subnets@2022-07-01' existing = { - parent: vnetSpokeCreated - name: spokePrivateEndpointsSubnetName -} + resource spokePrivateEndpointsSubnet 'subnets' existing = { + name: spokePrivateEndpointsSubnetName + } -resource spokeApplicationGatewaySubnet 'Microsoft.Network/virtualNetworks/subnets@2022-07-01' existing = if (!empty(spokeApplicationGatewaySubnetAddressPrefix)) { - parent: vnetSpokeCreated - name: spokeApplicationGatewaySubnetName + resource spokeApplicationGatewaySubnet 'subnets' existing = if (!empty(spokeApplicationGatewaySubnetAddressPrefix)) { + name: spokeApplicationGatewaySubnetName + } } -// ------------------ -// OUTPUTS -// ------------------ - -@description('The name of the Hub resource group.') +@description('The name of the spoke resource group.') output spokeResourceGroupName string = spokeResourceGroup.name -@description('The resource ID of the Spoke Virtual Network.') +@description('The resource ID of the spoke virtual network.') output spokeVNetId string = vnetSpokeCreated.id -@description('The name of the Spoke Virtual Network.') +@description('The name of the spoke virtual network.') output spokeVNetName string = vnetSpokeCreated.name -@description('The resource ID of the Spoke Infrastructure Subnet.') -output spokeInfraSubnetId string = spokeInfraSubnet.id +@description('The resource ID of the spoke infrastructure subnet.') +output spokeInfraSubnetId string = vnetSpokeCreated::spokeInfraSubnet.id -@description('The name of the Spoke Infrastructure Subnet.') -output spokeInfraSubnetName string = spokeInfraSubnet.name +@description('The name of the spoke infrastructure subnet.') +output spokeInfraSubnetName string = vnetSpokeCreated::spokeInfraSubnet.name -@description('The resource ID of the Spoke Private Endpoints Subnet.') -output spokePrivateEndpointsSubnetId string = spokePrivateEndpointsSubnet.id +@description('The resource ID of the spoke private endpoints subnet.') +output spokePrivateEndpointsSubnetId string = vnetSpokeCreated::spokePrivateEndpointsSubnet.id -@description('The name of the Spoke Private Endpoints Subnet.') -output spokePrivateEndpointsSubnetName string = spokePrivateEndpointsSubnet.name +@description('The name of the spoke private endpoints subnet.') +output spokePrivateEndpointsSubnetName string = vnetSpokeCreated::spokePrivateEndpointsSubnet.name -@description('The resource ID of the Spoke Application Gateway Subnet. If "spokeApplicationGatewaySubnetAddressPrefix" is empty, the subnet will not be created and the value returned is empty.') -output spokeApplicationGatewaySubnetId string = (!empty(spokeApplicationGatewaySubnetAddressPrefix)) ? spokeApplicationGatewaySubnet.id : '' +@description('The resource ID of the spoke Application Gateway subnet. This is \'\' if the subnet was not created.') +output spokeApplicationGatewaySubnetId string = (!empty(spokeApplicationGatewaySubnetAddressPrefix)) ? vnetSpokeCreated::spokeApplicationGatewaySubnet.id : '' -@description('The name of the Spoke Application Gateway Subnet. If "spokeApplicationGatewaySubnetAddressPrefix" is empty, the subnet will not be created and the value returned is empty.') -output spokeApplicationGatewaySubnetName string = (!empty(spokeApplicationGatewaySubnetAddressPrefix)) ? spokeApplicationGatewaySubnet.name : '' +@description('The name of the spoke Application Gateway subnet. This is \'\' if the subnet was not created.') +output spokeApplicationGatewaySubnetName string = (!empty(spokeApplicationGatewaySubnetAddressPrefix)) ? vnetSpokeCreated::spokeApplicationGatewaySubnet.name : '' diff --git a/scenarios/aca-internal/bicep/modules/02-spoke/deploy.spoke.parameters.jsonc b/scenarios/aca-internal/bicep/modules/02-spoke/deploy.spoke.parameters.jsonc index a38385bf..f89fea28 100644 --- a/scenarios/aca-internal/bicep/modules/02-spoke/deploy.spoke.parameters.jsonc +++ b/scenarios/aca-internal/bicep/modules/02-spoke/deploy.spoke.parameters.jsonc @@ -2,7 +2,7 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - // The name of the workloard that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) + // The name of the workload that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) "workloadName": { "value": "lzaaca" }, diff --git a/scenarios/aca-internal/bicep/modules/02-spoke/nsgContainerAppsEnvironment.jsonc b/scenarios/aca-internal/bicep/modules/02-spoke/nsgContainerAppsEnvironment.jsonc index beb16ea8..9b800f2c 100644 --- a/scenarios/aca-internal/bicep/modules/02-spoke/nsgContainerAppsEnvironment.jsonc +++ b/scenarios/aca-internal/bicep/modules/02-spoke/nsgContainerAppsEnvironment.jsonc @@ -1,5 +1,5 @@ { - //security rules (as of 2023-march-10): https://learn.microsoft.com/en-us/azure/container-apps/firewall-integration#nsg-allow-rules + //security rules (as of 2023-march-10): https://learn.microsoft.com/azure/container-apps/firewall-integration#nsg-allow-rules //Inbound rules are not required, if we leave the default inbound rules 65000 and 65001 "securityRules": [ // { diff --git a/scenarios/aca-internal/bicep/modules/03-supporting-services/deploy.supporting-services.bicep b/scenarios/aca-internal/bicep/modules/03-supporting-services/deploy.supporting-services.bicep index a64e4007..60f4ca3c 100644 --- a/scenarios/aca-internal/bicep/modules/03-supporting-services/deploy.supporting-services.bicep +++ b/scenarios/aca-internal/bicep/modules/03-supporting-services/deploy.supporting-services.bicep @@ -6,32 +6,37 @@ targetScope = 'resourceGroup' @minLength(2) @maxLength(10) -@description('The name of the workloard that is being deployed. Up to 10 characters long.') +@description('The name of the workload that is being deployed. Up to 10 characters long.') param workloadName string -@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa") Up to 8 characters long.') +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') @maxLength(8) param environment string -@description('The location where the resources will be created.') +@description('The location where the resources will be created. This needs to be the same region as the spoke.') param location string = resourceGroup().location @description('Optional. The tags to be assigned to the created resources.') param tags object = {} -@description('The resource ID of the VNet to which the private endpoint will be connected.') -param spokeVNetId string +// Hub -@description('The resource ID of the Hub Virtual Network.') +@description('The resource ID of the existing hub virtual network.') param hubVNetId string -@description('The name of the subnet in the VNet to which the private endpoint will be connected.') +// Spoke + +@description('The resource ID of the existing spoke virtual network to which the private endpoint will be connected.') +param spokeVNetId string + +@description('The name of the existing subnet in the spoke virtual to which the private endpoint will be connected.') param spokePrivateEndpointSubnetName string // ------------------ // RESOURCES // ------------------ +@description('User-configured naming rules') module naming '../../../../shared/bicep/naming/naming.module.bicep' = { name: take('03-sharedNamingDeployment-${deployment().name}', 64) params: { @@ -42,6 +47,7 @@ module naming '../../../../shared/bicep/naming/naming.module.bicep' = { } } +@description('Azure Container Registry, where all workload images should be pulled from.') module containerRegistry 'modules/container-registry.bicep' = { name: 'containerRegistry-${uniqueString(resourceGroup().id)}' params: { @@ -52,10 +58,11 @@ module containerRegistry 'modules/container-registry.bicep' = { hubVNetId: hubVNetId spokePrivateEndpointSubnetName: spokePrivateEndpointSubnetName containerRegistryPrivateEndpointName: naming.outputs.resourcesNames.containerRegistryPep - containerRegistryUserAssignedIdentityName: naming.outputs.resourcesNames.containerRegistryUserAssignedIdentity + containerRegistryUserAssignedIdentityName: naming.outputs.resourcesNames.containerRegistryUserAssignedIdentity } } +@description('Azure Key Vault used to hold items like TLS certs and application secrets that your workload will need.') module keyVault 'modules/key-vault.bicep' = { name: 'keyVault-${uniqueString(resourceGroup().id)}' params: { @@ -74,20 +81,20 @@ module keyVault 'modules/key-vault.bicep' = { // OUTPUTS // ------------------ -@description('The resource ID of the container registry.') +@description('The resource ID of the Azure Container Registry.') output containerRegistryId string = containerRegistry.outputs.containerRegistryId -@description('The name of the container registry.') +@description('The name of the Azure Container Registry.') output containerRegistryName string = containerRegistry.outputs.containerRegistryName -@description('The resource ID of the user assigned managed identity for the container registry to be able to pull images from it.') +@description('The resource ID of the user-assigned managed identity for the Azure Container Registry to be able to pull images from it.') output containerRegistryUserAssignedIdentityId string = containerRegistry.outputs.containerRegistryUserAssignedIdentityId -@description('The resource ID of the key vault.') +@description('The resource ID of the Azure Key Vault.') output keyVaultId string = keyVault.outputs.keyVaultId -@description('The name of the key vault.') +@description('The name of the Azure Key Vault.') output keyVaultName string = keyVault.outputs.keyVaultName -@description('The resource ID of the user assigned managed identity to access the key vault.') +@description('The resource ID of the user-assigned managed identity to read from Azure Key Vault.') output keyVaultUserAssignedIdentityId string = keyVault.outputs.keyVaultUserAssignedIdentityId diff --git a/scenarios/aca-internal/bicep/modules/03-supporting-services/deploy.supporting-services.parameters.jsonc b/scenarios/aca-internal/bicep/modules/03-supporting-services/deploy.supporting-services.parameters.jsonc index 58a98ce0..295bfb51 100644 --- a/scenarios/aca-internal/bicep/modules/03-supporting-services/deploy.supporting-services.parameters.jsonc +++ b/scenarios/aca-internal/bicep/modules/03-supporting-services/deploy.supporting-services.parameters.jsonc @@ -2,7 +2,7 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - // The name of the workloard that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) + // The name of the workload that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) "workloadName": { "value": "lzaaca" }, diff --git a/scenarios/aca-internal/bicep/modules/04-container-apps-environment/deploy.aca-environment.bicep b/scenarios/aca-internal/bicep/modules/04-container-apps-environment/deploy.aca-environment.bicep index 7342a9e3..a7d903a0 100644 --- a/scenarios/aca-internal/bicep/modules/04-container-apps-environment/deploy.aca-environment.bicep +++ b/scenarios/aca-internal/bicep/modules/04-container-apps-environment/deploy.aca-environment.bicep @@ -5,65 +5,84 @@ targetScope = 'resourceGroup' // ------------------ @minLength(2) @maxLength(10) -@description('The name of the workloard that is being deployed. Up to 10 characters long.') +@description('The name of the workload that is being deployed. Up to 10 characters long.') param workloadName string -@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa") Up to 8 characters long.') +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') @maxLength(8) param environment string -@description('The location where the resources will be created.') +@description('The location where the resources will be created. This needs to be the same region as the spoke.') param location string = resourceGroup().location @description('Optional. The tags to be assigned to the created resources.') param tags object = {} -@description('Enable or disable the createion of Application Insights.') -param enableApplicationInsights bool +// Hub -@description('Enable or disable Dapr Application Instrumentation Key used for Dapr telemetry. If Application Insights is not enabled, this parameter is ignored.') -param enableDaprInstrumentation bool +@description('The resource ID of the existing hub virtual network.') +param hubVNetId string + +// Spoke -@description('The name of the spoke VNet.') +@description('The name of the existing spoke virtual network.') param spokeVNetName string -@description('The name of the spoke infrastructure subnet.') +@description('The name of the existing spoke infrastructure subnet.') param spokeInfraSubnetName string -@description('The resource ID of the Hub Virtual Network.') -param hubVNetId string +// Telemetry + +@description('Enable or disable the createion of Application Insights.') +param enableApplicationInsights bool + +@description('Enable or disable Dapr application instrumentation using Application Insights. If enableApplicationInsights is false, this parameter is ignored.') +param enableDaprInstrumentation bool -@description('Enable usage and telemetry feedback to Microsoft.') +@description('Enable sending usage and telemetry feedback to Microsoft.') param enableTelemetry bool = true // ------------------ // VARIABLES // ------------------ -var hubVNetResourIdTokens = !empty(hubVNetId) ? split(hubVNetId, '/') : array('') -var hubSubscriptionId = hubVNetResourIdTokens[2] -var hubResourceGroupName = hubVNetResourIdTokens[4] -var hubVNetName = hubVNetResourIdTokens[8] +var hubVNetResourceIdTokens = !empty(hubVNetId) ? split(hubVNetId, '/') : array('') -var spokeVNetLinks = [ - { - vnetName: spokeVNetName - vnetId: spokeVNet.id - registrationEnabled: false - } - { - vnetName: vnetHub.name - vnetId: vnetHub.id - registrationEnabled: false - } -] +@description('The ID of the subscription containing the hub virtual network.') +var hubSubscriptionId = hubVNetResourceIdTokens[2] + +@description('The name of the resource group containing the hub virtual network.') +var hubResourceGroupName = hubVNetResourceIdTokens[4] + +@description('The name of the hub virtual network.') +var hubVNetName = hubVNetResourceIdTokens[8] var telemetryId = '9b4433d6-924a-4c07-b47c-7478619759c7-${location}-acasb' +// ------------------ +// EXISTING RESOURCES +// ------------------ + +@description('The existing hub virtual network.') +resource vnetHub 'Microsoft.Network/virtualNetworks@2022-07-01' existing = { + scope: resourceGroup(hubSubscriptionId, hubResourceGroupName) + name: hubVNetName +} + +@description('The existing spoke virtual network.') +resource spokeVNet 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { + name: spokeVNetName + + resource infraSubnet 'subnets' existing = { + name: spokeInfraSubnetName + } +} + // ------------------ // RESOURCES // ------------------ +@description('User-configured naming rules') module naming '../../../../shared/bicep/naming/naming.module.bicep' = { name: take('04-sharedNamingDeployment-${deployment().name}', 64) params: { @@ -74,20 +93,7 @@ module naming '../../../../shared/bicep/naming/naming.module.bicep' = { } } -resource vnetHub 'Microsoft.Network/virtualNetworks@2022-07-01' existing = { - scope: resourceGroup(hubSubscriptionId, hubResourceGroupName) - name: hubVNetName -} - -resource spokeVNet 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { - name: spokeVNetName -} - -resource spokeInfraSubnet 'Microsoft.Network/virtualNetworks/subnets@2022-07-01' existing = { - parent: spokeVNet - name: spokeInfraSubnetName -} - +@description('The log sink for Azure Diagnostics for workload resources and also for Application Insights.') module logAnalyticsWorkspace '../../../../shared/bicep/log-analytics-ws.bicep' = { name: take('logAnalyticsWs-${uniqueString(resourceGroup().id)}', 64) params: { @@ -96,34 +102,48 @@ module logAnalyticsWorkspace '../../../../shared/bicep/log-analytics-ws.bicep' = } } +@description('Azure Application Insights, the workload\' log & metric sink and APM tool') module applicationInsights '../../../../shared/bicep/app-insights.bicep' = if (enableApplicationInsights) { name: take('applicationInsights-${uniqueString(resourceGroup().id)}', 64) params: { name: naming.outputs.resourcesNames.applicationInsights location: location - tags: tags + tags: tags workspaceResourceId: logAnalyticsWorkspace.outputs.logAnalyticsWsId } } +@description('The Azure Container Apps (ACA) cluster.') module containerAppsEnvironment '../../../../shared/bicep/aca-environment.bicep' = { name: take('containerAppsEnvironment-${uniqueString(resourceGroup().id)}', 64) params: { name: naming.outputs.resourcesNames.containerAppsEnvironment location: location tags: tags - logAnalyticsWsResourceId: logAnalyticsWorkspace.outputs.logAnalyticsWsId - subnetId: spokeInfraSubnet.id + logAnalyticsWsResourceId: logAnalyticsWorkspace.outputs.logAnalyticsWsId + subnetId: spokeVNet::infraSubnet.id vnetEndpointInternal: true - appInsightsInstrumentationKey: (enableApplicationInsights && enableDaprInstrumentation) ? applicationInsights.outputs.appInsInstrumentationKey : '' + appInsightsInstrumentationKey: (enableApplicationInsights && enableDaprInstrumentation) ? applicationInsights.outputs.appInsInstrumentationKey : '' } } -module containerAppsEnvironmentPrivateDnsZone '../../../../shared/bicep/private-dns-zone.bicep' = { +@description('The Private DNS zone containing the ACA load balancer IP') +module containerAppsEnvironmentPrivateDnsZone '../../../../shared/bicep/private-dns-zone.bicep' = { name: 'containerAppsEnvironmentPrivateDnsZone-${uniqueString(resourceGroup().id)}' params: { name: containerAppsEnvironment.outputs.containerAppsEnvironmentDefaultDomain - virtualNetworkLinks: spokeVNetLinks + virtualNetworkLinks: [ + { + vnetName: spokeVNet.name /* Link to spoke */ + vnetId: spokeVNet.id + registrationEnabled: false + } + { + vnetName: vnetHub.name /* Link to hub */ + vnetId: vnetHub.id + registrationEnabled: false + } + ] tags: tags aRecords: [ { @@ -134,7 +154,7 @@ module containerAppsEnvironmentPrivateDnsZone '../../../../shared/bicep/private } } -// Telemetry Deployment +@description('Microsoft telemetry deployment.') resource telemetrydeployment 'Microsoft.Resources/deployments@2021-04-01' = if (enableTelemetry) { name: telemetryId properties: { @@ -151,11 +171,11 @@ resource telemetrydeployment 'Microsoft.Resources/deployments@2021-04-01' = if ( // OUTPUTS // ------------------ -@description('The resource ID of the container apps environment.') +@description('The resource ID of the Container Apps environment.') output containerAppsEnvironmentId string = containerAppsEnvironment.outputs.containerAppsEnvironmentNameId -@description('The name of the container apps environment.') +@description('The name of the Container Apps environment.') output containerAppsEnvironmentName string = containerAppsEnvironment.outputs.containerAppsEnvironmentName -@description('The customer id of the log analytics workspace.') +@description('The customer ID of the Azure Log Analytics Workspace.') output logAnalyticsWorkspaceCustomerId string = logAnalyticsWorkspace.outputs.customerId diff --git a/scenarios/aca-internal/bicep/modules/04-container-apps-environment/deploy.aca-environment.parameters.jsonc b/scenarios/aca-internal/bicep/modules/04-container-apps-environment/deploy.aca-environment.parameters.jsonc index 3d6fc4b7..a5bcbb84 100644 --- a/scenarios/aca-internal/bicep/modules/04-container-apps-environment/deploy.aca-environment.parameters.jsonc +++ b/scenarios/aca-internal/bicep/modules/04-container-apps-environment/deploy.aca-environment.parameters.jsonc @@ -2,7 +2,7 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - // The name of the workloard that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) + // The name of the workload that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) "workloadName": { "value": "lzaaca" }, diff --git a/scenarios/aca-internal/bicep/modules/05-hello-world-sample-app/deploy.hello-world.bicep b/scenarios/aca-internal/bicep/modules/05-hello-world-sample-app/deploy.hello-world.bicep index 97cced34..a05ceafe 100644 --- a/scenarios/aca-internal/bicep/modules/05-hello-world-sample-app/deploy.hello-world.bicep +++ b/scenarios/aca-internal/bicep/modules/05-hello-world-sample-app/deploy.hello-world.bicep @@ -4,27 +4,28 @@ targetScope = 'resourceGroup' // PARAMETERS // ------------------ -@description('The location where the resources will be created.') +@description('The location where the resources will be created. This needs to be the same region as the Azure Container Apps instances.') param location string = resourceGroup().location @description('Optional. The tags to be assigned to the created resources.') param tags object = {} -@description('Optional. The name of the container app. If set, it overrides the name generated by the template.') +@description('Optional. The name of the Container App. If set, it overrides the name generated by the template.') @minLength(2) @maxLength(32) param helloWorldContainerAppName string = 'ca-simple-hello' -@description('The resource ID of the user-assigned identity to be assigned to the container app to be able to pull images for the container registry.') +@description('The resource ID of the existing user-assigned managed identity to be assigned to the Container App to be able to pull images from the container registry.') param containerRegistryUserAssignedIdentityId string -@description('The resource ID of the container apps environment in which the container app will be deployed.') +@description('The resource ID of the existing Container Apps environment in which the Container App will be deployed.') param containerAppsEnvironmentId string // ------------------ // RESOURCES // ------------------ +@description('The "Hello World" Container App.') resource containerApp 'Microsoft.App/containerApps@2022-10-01' = { name: helloWorldContainerAppName location: location @@ -32,7 +33,7 @@ resource containerApp 'Microsoft.App/containerApps@2022-10-01' = { identity: { type: 'UserAssigned' userAssignedIdentities: { - '${containerRegistryUserAssignedIdentityId}': {} + '${containerRegistryUserAssignedIdentityId}': {} } } properties: { @@ -52,6 +53,8 @@ resource containerApp 'Microsoft.App/containerApps@2022-10-01' = { containers: [ { name: 'simple-hello' + // Production readiness change + // All workloads should be pulled from your private container registry and not public registries. image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' resources: { cpu: json('0.25') @@ -72,5 +75,5 @@ resource containerApp 'Microsoft.App/containerApps@2022-10-01' = { // OUTPUTS // ------------------ -@description('The FQDN of the Hello World container app.') -output helloWorldAppFQDN string = containerApp.properties.configuration.ingress.fqdn +@description('The FQDN of the "Hello World" Container App.') +output helloWorldAppFqdn string = containerApp.properties.configuration.ingress.fqdn diff --git a/scenarios/aca-internal/bicep/modules/06-application-gateway/README.md b/scenarios/aca-internal/bicep/modules/06-application-gateway/README.md index c7c48964..fd1ae18f 100644 --- a/scenarios/aca-internal/bicep/modules/06-application-gateway/README.md +++ b/scenarios/aca-internal/bicep/modules/06-application-gateway/README.md @@ -21,7 +21,7 @@ The "Hello World" container app is exposed through Application Gateway, includin ```bash RESOURCENAME_RESOURCEGROUP_SPOKE=$(az deployment sub show -n acalza01-spokenetwork --query properties.outputs.spokeResourceGroupName.value -o tsv) RESOURCEID_SUBNET_APPGW=$(az deployment sub show -n acalza01-spokenetwork --query properties.outputs.spokeApplicationGatewaySubnetId.value -o tsv) - FQDN_HELLOWORLD_ACA=$(az deployment group show -g rg-lzaaca-spoke-dev-eus2 -n acalza01-helloworld --query properties.outputs.helloWorldAppFQDN.value -o tsv) + FQDN_HELLOWORLD_ACA=$(az deployment group show -g rg-lzaaca-spoke-dev-eus2 -n acalza01-helloworld --query properties.outputs.helloWorldAppFqdn.value -o tsv) RESOURCEID_KEYVAULT=$(az deployment group show -g rg-lzaaca-spoke-dev-eus2 -n acalza01-dependencies --query properties.outputs.keyVaultId.value -o tsv) echo RESOURCENAME_RESOURCEGROUP_SPOKE: $RESOURCENAME_RESOURCEGROUP_SPOKE && \ echo RESOURCEID_SUBNET_APPGW: $RESOURCEID_SUBNET_APPGW && \ @@ -34,7 +34,7 @@ The "Hello World" container app is exposed through Application Gateway, includin -g $RESOURCENAME_RESOURCEGROUP_SPOKE \ -f 06-application-gateway/deploy.app-gateway.bicep \ -p 06-application-gateway/deploy.app-gateway.parameters.jsonc \ - -p applicationGatewaySubnetId=${RESOURCEID_SUBNET_APPGW} applicationGatewayPrimaryBackendEndFQDN=${FQDN_HELLOWORLD_ACA} keyVaultId=${RESOURCEID_KEYVAULT} + -p applicationGatewaySubnetId=${RESOURCEID_SUBNET_APPGW} applicationGatewayPrimaryBackendEndFqdn=${FQDN_HELLOWORLD_ACA} keyVaultId=${RESOURCEID_KEYVAULT} ``` 1. Get the public IP of Application Gateway. diff --git a/scenarios/aca-internal/bicep/modules/06-application-gateway/configuration/Readme.md b/scenarios/aca-internal/bicep/modules/06-application-gateway/configuration/Readme.md index 5bcf066c..0193cff1 100644 --- a/scenarios/aca-internal/bicep/modules/06-application-gateway/configuration/Readme.md +++ b/scenarios/aca-internal/bicep/modules/06-application-gateway/configuration/Readme.md @@ -1,22 +1,25 @@ -# Create a self signed pfx certificate +# Create a self signed PFX certificate -- If we want our certificate signed, we need a certificate signing request (CSR). The CSR includes the public key and some additional information (such as organization and country). Pay attention to the **Common Name (e.g. server FQDN or YOUR name)**, this must much your domain name, i.e. www.microsoft.com -``` - openssl \ - req \ - -nodes \ - -newkey rsa:2048 \ - -keyout acahello.demoapp.com.key \ - -out acahello.demoapp.com.csr \ - -subj "/C=US/ST=WA/L=Redmond/O=LZA/OU=LZA/CN=acahello.demoapp.com/emailAddress=lza@microsoft.com" -``` +- If we want our certificate signed, we need a certificate signing request (CSR). The CSR includes the public key and some additional information (such as organization and country). Pay attention to the **Common Name (e.g. server FQDN or YOUR name)**, this must much your domain name, i.e. www.microsoft.com. -- Creating a Self-Signed Certificate. A self-signed certificate is a certificate that's signed with its own private key. It can be used to encrypt data just as well as CA-signed certificates, but our users will be shown a warning that says the certificate isn't trusted. Let's create a self-signed certificate (domain.crt) with our existing private key and CSR -``` - openssl x509 -signkey acahello.demoapp.com.key -in acahello.demoapp.com.csr -req -days 365 -out acahello.demoapp.com.crt +```bash +openssl \ + req \ + -nodes \ + -newkey rsa:2048 \ + -keyout acahello.demoapp.com.key \ + -out acahello.demoapp.com.csr \ + -subj "/C=US/ST=WA/L=Redmond/O=LZA/OU=LZA/CN=acahello.demoapp.com/emailAddress=lza@microsoft.com" ``` -- Convert PEM to PKCS12. PKCS12 files, also known as PFX files, are usually used for importing and exporting certificate chains in Microsoft IIS. We'll use the following command to take our private key and certificate, and then combine them into a PKCS12 file +- Creating a Self-Signed Certificate. A self-signed certificate is a certificate that's signed with its own private key. It can be used to encrypt data just as well as CA-signed certificates, but our users will be shown a warning that says the certificate isn't trusted. Let's create a self-signed certificate (domain.crt) with our existing private key and CSR. + +```bash +openssl x509 -signkey acahello.demoapp.com.key -in acahello.demoapp.com.csr -req -days 365 -out acahello.demoapp.com.crt ``` - openssl pkcs12 -inkey acahello.demoapp.com.key -in acahello.demoapp.com.crt -export -out acahello.demoapp.com.pfx + +- Convert PEM to PKCS12. PKCS12 files, also known as PFX files, are usually used for importing and exporting certificate chains in Microsoft IIS. We'll use the following command to take our private key and certificate, and then combine them into a PKCS12 file. + +```bash +openssl pkcs12 -inkey acahello.demoapp.com.key -in acahello.demoapp.com.crt -export -out acahello.demoapp.com.pfx ``` diff --git a/scenarios/aca-internal/bicep/modules/06-application-gateway/deploy.app-gateway.bicep b/scenarios/aca-internal/bicep/modules/06-application-gateway/deploy.app-gateway.bicep index db112401..2080b97f 100644 --- a/scenarios/aca-internal/bicep/modules/06-application-gateway/deploy.app-gateway.bicep +++ b/scenarios/aca-internal/bicep/modules/06-application-gateway/deploy.app-gateway.bicep @@ -6,41 +6,41 @@ targetScope = 'resourceGroup' @minLength(2) @maxLength(10) -@description('The name of the workloard that is being deployed. Up to 10 characters long.') +@description('The name of the workload that is being deployed. Up to 10 characters long.') param workloadName string -@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa") Up to 8 characters long.') +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') @maxLength(8) param environment string -@description('The location where the resources will be created.') +@description('The location where the resources will be created. This needs to be the same region as the spoke.') param location string = resourceGroup().location @description('Optional. The tags to be assigned to the created resources.') param tags object = {} -@description('The FQDN of the Application Gateawy.Must match the TLS Certificate.') -param applicationGatewayFQDN string +@description('The FQDN of the Application Gateawy. Must match the TLS certificate.') +param applicationGatewayFqdn string -@description('The subnet resource id to use for Application Gateway.') +@description('The existing subnet resource ID to use for Application Gateway.') param applicationGatewaySubnetId string @description('The FQDN of the primary backend endpoint.') -param applicationGatewayPrimaryBackendEndFQDN string +param applicationGatewayPrimaryBackendEndFqdn string -@description('The path to use for Application Gateway backend health probe.') +@description('The path to use for Application Gateway\'s backend health probe.') param appGatewayBackendHealthProbePath string = '/' -@description('Enable or disable Application Gateway Certificate (PFX).') +@description('Enable or disable Application Gateway certificate (PFX).') param enableApplicationGatewayCertificate bool @description('The name of the certificate key to use for Application Gateway certificate.') param applicationGatewayCertificateKeyName string -@description('Provide a resource ID of the Web Analytics WS if you need diagnostic settngs, or nothing if you don t need any.') +@description('The resource ID of the exsiting Log Analytics workload for diagnostic settngs, or nothing if you don\'t need any.') param applicationGatewayLogAnalyticsId string = '' -@description('The resource ID of the Key Vault.') +@description('The resource ID of the existing Key Vault which contains Application Gateway\'s cert.') param keyVaultId string // ------------------ @@ -48,16 +48,24 @@ param keyVaultId string // ------------------ var keyVaultIdTokens = split(keyVaultId, '/') + +@description('The subscription ID of the existing Key Vault.') var keyVaultSubscriptionId = keyVaultIdTokens[2] + +@description('The name of the resource group containing the existing Key Vault.') var keyVaultResourceGroupName = keyVaultIdTokens[4] + +@description('The name of the existing Key Vault.') var keyVaultName = keyVaultIdTokens[8] +@description('The existing PFX for Azure Application Gateway to use on its frontend.') var applicationGatewayCertificatePath = 'configuration/acahello.demoapp.com.pfx' // ------------------ // RESOURCES // ------------------ +@description('User-configured naming rules') module naming '../../../../shared/bicep/naming/naming.module.bicep' = { name: take('06-sharedNamingDeployment-${deployment().name}', 64) params: { @@ -69,6 +77,7 @@ module naming '../../../../shared/bicep/naming/naming.module.bicep' = { } // TODO: Check if this is required if enableApplicationCertificate is false +@description('A user-assigned managed identity that enables Application Gateway to access Key Vault for its TLS certs.') module userAssignedIdentity '../../../../shared/bicep/managed-identity.bicep' = { name: take('appGwUserAssignedIdentity-Deployment-${uniqueString(resourceGroup().id)}', 64) params: { @@ -80,10 +89,11 @@ module userAssignedIdentity '../../../../shared/bicep/managed-identity.bicep' = // => Key Vault User Assigned Identity, Secret & Role Assignement for certificate // As of today, App Gateway does not supports "System Managed Identity" for Key Vault -// https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#key-vault-secrets-user +// https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#key-vault-secrets-user // => Certificates (supports only 1 for now) +@description('Adds the PFX file into Azure Key Vault for consumption by Application Gateway.') module appGatewayAddCertificates './modules/app-gateway-cert.bicep' = if (enableApplicationGatewayCertificate) { name: take('appGatewayAddCertificates-Deployment-${uniqueString(resourceGroup().id)}', 64) scope: resourceGroup(keyVaultSubscriptionId, keyVaultResourceGroupName) @@ -95,14 +105,15 @@ module appGatewayAddCertificates './modules/app-gateway-cert.bicep' = if (enable } } -module appGatewayConfiguration './modules/app-gateway-config.bicep'= { +@description('Azure Application Gateway, which acts as the public Internet gateway and WAF for the workload.') +module appGatewayConfiguration './modules/app-gateway-config.bicep' = { name: take('appGatewayConfiguration-Deployment-${uniqueString(resourceGroup().id)}', 64) params: { appGatewayName: naming.outputs.resourcesNames.applicationGateway location: location tags: tags - appGatewayFQDN: applicationGatewayFQDN - appGatewayPrimaryBackendEndFQDN: applicationGatewayPrimaryBackendEndFQDN + appGatewayFqdn: applicationGatewayFqdn + appGatewayPrimaryBackendEndFqdn: applicationGatewayPrimaryBackendEndFqdn appGatewayBackendHealthProbePath: appGatewayBackendHealthProbePath appGatewayPublicIpName: naming.outputs.resourcesNames.applicationGatewayPip appGatewaySubnetId: applicationGatewaySubnetId @@ -116,8 +127,8 @@ module appGatewayConfiguration './modules/app-gateway-config.bicep'= { // OUTPUTS // ------------------ -@description('The FQDN of the application gateway.') +@description('The FQDN of the Azure Application Gateway.') output applicationGatewayFqdn string = appGatewayConfiguration.outputs.applicationGatewayFqdn -@description('The public IP address of the application gateway.') +@description('The public IP address of the Azure Application Gateway.') output applicationGatewayPublicIp string = appGatewayConfiguration.outputs.applicationGatewayPublicIp diff --git a/scenarios/aca-internal/bicep/modules/06-application-gateway/deploy.app-gateway.parameters.jsonc b/scenarios/aca-internal/bicep/modules/06-application-gateway/deploy.app-gateway.parameters.jsonc index 6bdfc7c3..dda73cd0 100644 --- a/scenarios/aca-internal/bicep/modules/06-application-gateway/deploy.app-gateway.parameters.jsonc +++ b/scenarios/aca-internal/bicep/modules/06-application-gateway/deploy.app-gateway.parameters.jsonc @@ -2,7 +2,7 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - // The name of the workloard that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) + // The name of the workload that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) "workloadName": { "value": "lzaaca" }, @@ -13,16 +13,16 @@ "tags": { "value": {} }, - // The FQDN of the Application Gateawy. Must match the TLS Certificate. - "applicationGatewayFQDN": { + // The FQDN of the Application Gateway. Must match the TLS Certificate. + "applicationGatewayFqdn": { "value": "acahello.demoapp.com" }, - // Replace with the resource ID of the application gateway subenet of the spoke virtual network + // Replace with the resource ID of the application gateway subnet of the spoke virtual network "applicationGatewaySubnetId": { "value": "" }, // Replace with the FQDN of the hello world sample app - "applicationGatewayPrimaryBackendEndFQDN": { + "applicationGatewayPrimaryBackendEndFqdn": { "value": "" }, "enableApplicationGatewayCertificate": { @@ -31,7 +31,7 @@ "applicationGatewayCertificateKeyName": { "value": "agwcert" }, - // Replace with the resource ID of the key vault + // Replace with the resource ID of the Key Vault "keyVaultId": { "value": "" } diff --git a/scenarios/aca-internal/bicep/modules/06-application-gateway/modules/app-gateway-config.bicep b/scenarios/aca-internal/bicep/modules/06-application-gateway/modules/app-gateway-config.bicep index 5bfd1760..680f691e 100644 --- a/scenarios/aca-internal/bicep/modules/06-application-gateway/modules/app-gateway-config.bicep +++ b/scenarios/aca-internal/bicep/modules/06-application-gateway/modules/app-gateway-config.bicep @@ -6,10 +6,10 @@ targetScope = 'resourceGroup' param appGatewayName string @description('The FQDN of the Application Gateawy.Must match the TLS Certificate.') -param appGatewayFQDN string +param appGatewayFqdn string @description('The subnet resource id to use for Application Gateway.') param appGatewaySubnetId string -param appGatewayPrimaryBackendEndFQDN string +param appGatewayPrimaryBackendEndFqdn string param appGatewayBackendHealthProbePath string param appGatewayUserAssignedIdentityId string @@ -75,7 +75,7 @@ resource appGateway 'Microsoft.Network/applicationGateways@2019-09-01' = { ] sslCertificates: (!empty(keyVaultSecretId)) ? [ { - name: appGatewayFQDN + name: appGatewayFqdn properties: { keyVaultSecretId: keyVaultSecretId } @@ -120,7 +120,7 @@ resource appGateway 'Microsoft.Network/applicationGateways@2019-09-01' = { properties: { backendAddresses: [ { - fqdn: appGatewayPrimaryBackendEndFQDN + fqdn: appGatewayPrimaryBackendEndFqdn } ] } @@ -187,7 +187,7 @@ resource appGateway 'Microsoft.Network/applicationGateways@2019-09-01' = { protocol: 'Https' sslCertificate: { #disable-next-line use-resource-id-functions - id: '${resourceId('Microsoft.Network/applicationGateways', appGatewayName)}/sslCertificates/${appGatewayFQDN}' + id: '${resourceId('Microsoft.Network/applicationGateways', appGatewayName)}/sslCertificates/${appGatewayFqdn}' } hostnames: [] requireServerNameIndication: false @@ -220,7 +220,7 @@ resource appGateway 'Microsoft.Network/applicationGateways@2019-09-01' = { name: 'webProbe' properties: { protocol: 'Https' - host: appGatewayPrimaryBackendEndFQDN + host: appGatewayPrimaryBackendEndFqdn path: appGatewayBackendHealthProbePath interval: 30 timeout: 30 @@ -288,7 +288,7 @@ resource agwDiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-previe // ------------------ @description('The FQDN of the application gateway.') -output applicationGatewayFqdn string = appGatewayFQDN +output applicationGatewayFqdn string = appGatewayFqdn @description('The public IP address of the application gateway.') output applicationGatewayPublicIp string = appGatewayPip.properties.ipAddress diff --git a/scenarios/aca-internal/bicep/modules/06-front-door/deploy.front-door.bicep b/scenarios/aca-internal/bicep/modules/06-front-door/deploy.front-door.bicep index 3729bf54..74e64bbd 100644 --- a/scenarios/aca-internal/bicep/modules/06-front-door/deploy.front-door.bicep +++ b/scenarios/aca-internal/bicep/modules/06-front-door/deploy.front-door.bicep @@ -6,10 +6,10 @@ targetScope = 'resourceGroup' @minLength(2) @maxLength(10) -@description('The name of the workloard that is being deployed. Up to 10 characters long.') +@description('The name of the workload that is being deployed. Up to 10 characters long.') param workloadName string -@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa") Up to 8 characters long.') +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') @maxLength(8) param environment string diff --git a/scenarios/aca-internal/bicep/modules/06-front-door/deploy.front-door.parameters.jsonc b/scenarios/aca-internal/bicep/modules/06-front-door/deploy.front-door.parameters.jsonc index a3d7fdc3..b9d56597 100644 --- a/scenarios/aca-internal/bicep/modules/06-front-door/deploy.front-door.parameters.jsonc +++ b/scenarios/aca-internal/bicep/modules/06-front-door/deploy.front-door.parameters.jsonc @@ -2,7 +2,7 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - // The name of the workloard that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) + // The name of the workload that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) "workloadName": { "value": "lzaaca" }, diff --git a/scenarios/aca-internal/bicep/sample-apps/dotnet-task-tracker-service/main.bicep b/scenarios/aca-internal/bicep/sample-apps/dotnet-task-tracker-service/main.bicep index 422231ed..52492338 100644 --- a/scenarios/aca-internal/bicep/sample-apps/dotnet-task-tracker-service/main.bicep +++ b/scenarios/aca-internal/bicep/sample-apps/dotnet-task-tracker-service/main.bicep @@ -4,12 +4,12 @@ targetScope = 'resourceGroup' // PARAMETERS // ------------------ -@description('The name of the workloard that is being deployed. Up to 10 characters long.') +@description('The name of the workload that is being deployed. Up to 10 characters long.') @minLength(2) @maxLength(10) param workloadName string -@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa") Up to 8 characters long.') +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') @maxLength(8) param environment string diff --git a/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/main.bicep b/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/main.bicep index 361b6983..d686efee 100644 --- a/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/main.bicep +++ b/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/main.bicep @@ -6,10 +6,10 @@ targetScope = 'resourceGroup' @minLength(2) @maxLength(10) -@description('The name of the workloard that is being deployed. Up to 10 characters long.') +@description('The name of the workload that is being deployed. Up to 10 characters long.') param workloadName string -@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa") Up to 8 characters long.') +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') @maxLength(8) param environment string @@ -107,7 +107,7 @@ param simulationImage string = '' // Application Gateway @description('The FQDN of the Application Gateawy.Must match the TLS Certificate.') -param applicationGatewayFQDN string +param applicationGatewayFqdn string @description('The subnet name to use for Application Gateway.') param spokeApplicationGatewaySubnetName string @@ -238,9 +238,9 @@ module applicationGateway '../../modules/06-application-gateway/deploy.app-gatew tags: tags environment: environment workloadName: workloadName - applicationGatewayFQDN: applicationGatewayFQDN + applicationGatewayFqdn: applicationGatewayFqdn applicationGatewaySubnetId: spokeApplicationGatewaySubnet.id - applicationGatewayPrimaryBackendEndFQDN: containerApps.outputs.trafficControlServiceFQDN + applicationGatewayPrimaryBackendEndFqdn: containerApps.outputs.trafficControlServiceFqdn appGatewayBackendHealthProbePath: appGatewayBackendHealthProbePath enableApplicationGatewayCertificate: enableApplicationGatewayCertificate applicationGatewayCertificateKeyName: applicationGatewayCertificateKeyName diff --git a/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/main.parameters.jsonc b/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/main.parameters.jsonc index e5354cd0..e580e577 100644 --- a/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/main.parameters.jsonc +++ b/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/main.parameters.jsonc @@ -2,7 +2,7 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - // The name of the workloard that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) + // The name of the workload that is being deployed. Up to 10 characters long. This wil be used as part of the naming convention (i.e. as defined here: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) "workloadName": { "value": "lzaaca" }, @@ -117,8 +117,8 @@ }, /* APPLICATION GATEWAY */ - // The FQDN of the Application Gateawy. Must match the TLS Certificate. - "applicationGatewayFQDN": { + // The FQDN of the Application Gateway. Must match the TLS certificate. + "applicationGatewayFqdn": { "value": "acahello.demoapp.com" }, // Replace with the name of the spoke application gateway subnet name diff --git a/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/modules/container-apps.bicep b/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/modules/container-apps.bicep index 7cbdfe9a..8614f663 100644 --- a/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/modules/container-apps.bicep +++ b/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/modules/container-apps.bicep @@ -150,7 +150,7 @@ module simulation 'container-apps/simulation.bicep' = if (deploySimulationInAcaE location: location tags: tags containerAppsEnvironmentId: containerAppsEnvironment.id - trafficControlServiceFQDN: trafficControlService.outputs.trafficControlServiceFQDN + trafficControlServiceFqdn: trafficControlService.outputs.trafficControlServiceFqdn containerRegistryName: containerRegistryName containerRegistryUserAssignedIdentityId: containerRegistryUserAssignedIdentityId simulationImage: simulationImage @@ -174,4 +174,4 @@ output trafficControlServiceContainerAppName string = trafficControlService.outp output simulationContainerAppName string = (deploySimulationInAcaEnvironment) ? simulation.outputs.simulationContainerAppName : '' @description('The FQDN of the traffic control service.') -output trafficControlServiceFQDN string = trafficControlService.outputs.trafficControlServiceFQDN +output trafficControlServiceFqdn string = trafficControlService.outputs.trafficControlServiceFqdn diff --git a/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/modules/container-apps/simulation.bicep b/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/modules/container-apps/simulation.bicep index 39854844..3ad6ecb6 100644 --- a/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/modules/container-apps/simulation.bicep +++ b/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/modules/container-apps/simulation.bicep @@ -14,7 +14,7 @@ param tags object = {} param containerAppsEnvironmentId string @description('The FQDN of the traffic control service.') -param trafficControlServiceFQDN string +param trafficControlServiceFqdn string @description('The name of the the simulation.') param simulationName string @@ -74,7 +74,7 @@ resource simulationService 'Microsoft.App/containerApps@2022-06-01-preview' = { env: [ { name: 'TRAFFIC_CONTROL_SERVICE_BASE_URL' - value: 'https://${trafficControlServiceFQDN}' + value: 'https://${trafficControlServiceFqdn}' } ] } diff --git a/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/modules/container-apps/traffic-control-service.bicep b/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/modules/container-apps/traffic-control-service.bicep index 1afeacd9..3c9e5519 100644 --- a/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/modules/container-apps/traffic-control-service.bicep +++ b/scenarios/aca-internal/bicep/sample-apps/java-fine-collection-service/modules/container-apps/traffic-control-service.bicep @@ -144,7 +144,7 @@ resource trafficControlServiceSbRoleAssignment 'Microsoft.Authorization/roleAssi } // Assign cosmosdb account read/write access to aca user assigned identity -// To know more: https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-setup-rbac +// To know more: https://learn.microsoft.com/azure/cosmos-db/how-to-setup-rbac resource cosmosDbCollectionDataContributorRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-08-15' = { name: guid(subscription().id, trafficControlService.name, '00000000-0000-0000-0000-000000000002') parent: cosmosDbAccount @@ -163,4 +163,4 @@ resource cosmosDbCollectionDataContributorRoleAssignment 'Microsoft.DocumentDB/ output trafficControlServiceContainerAppName string = trafficControlService.name @description('The FQDN of the traffic control service.') -output trafficControlServiceFQDN string = trafficControlService.properties.configuration.ingress.fqdn +output trafficControlServiceFqdn string = trafficControlService.properties.configuration.ingress.fqdn diff --git a/scenarios/shared/bicep/naming/naming-rules.jsonc b/scenarios/shared/bicep/naming/naming-rules.jsonc index bffb6ea0..6fde4190 100644 --- a/scenarios/shared/bicep/naming/naming-rules.jsonc +++ b/scenarios/shared/bicep/naming/naming-rules.jsonc @@ -1,5 +1,5 @@ { - // Recommended abreviations: https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations + // Recommended abreviations: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations "resourceTypeAbbreviations" : { "applicationGateway": "agw", "applicationInsights": "appi", diff --git a/scenarios/shared/bicep/naming/naming.module.bicep b/scenarios/shared/bicep/naming/naming.module.bicep index 68eac6e4..b0ca1b50 100644 --- a/scenarios/shared/bicep/naming/naming.module.bicep +++ b/scenarios/shared/bicep/naming/naming.module.bicep @@ -26,7 +26,7 @@ var naming = json(loadTextContent('./naming-rules.jsonc')) var uniqueIdShort = substring(uniqueId, 0, 5) var resourceTypeToken = 'RES_TYPE' -// Define and adhere to a naming convention, such as: https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming +// Define and adhere to a naming convention, such as: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming var namingBase = '${resourceTypeToken}-${workloadName}-${environment}-${naming.regionAbbreviations[toLower(location)]}' var namingBaseUnique = '${resourceTypeToken}-${workloadName}-${uniqueIdShort}-${environment}-${naming.regionAbbreviations[toLower(location)]}' diff --git a/scenarios/shared/bicep/nsg.bicep b/scenarios/shared/bicep/nsg.bicep index ca292585..7b9565f2 100644 --- a/scenarios/shared/bicep/nsg.bicep +++ b/scenarios/shared/bicep/nsg.bicep @@ -25,7 +25,7 @@ param securityRules array // RESOURCES // ------------------ -// TODO: do we need flowlogs? https://learn.microsoft.com/en-us/azure/network-watcher/quickstart-configure-network-security-group-flow-logs-from-bicep?tabs=CLI +// TODO: do we need flowlogs? https://learn.microsoft.com/azure/network-watcher/quickstart-configure-network-security-group-flow-logs-from-bicep?tabs=CLI resource nsg 'Microsoft.Network/networkSecurityGroups@2022-09-01' = { name: name diff --git a/scenarios/shared/bicep/private-dns-zone.bicep b/scenarios/shared/bicep/private-dns-zone.bicep index a2358846..33a31b0a 100644 --- a/scenarios/shared/bicep/private-dns-zone.bicep +++ b/scenarios/shared/bicep/private-dns-zone.bicep @@ -1,4 +1,4 @@ -@description('Required. Name of the Private DNS Zone Service. For az private endpoints you might find info here: https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns#azure-services-dns-zone-configuration') +@description('Required. Name of the Private DNS Zone Service. For az private endpoints you might find info here: https://learn.microsoft.com/azure/private-link/private-endpoint-dns#azure-services-dns-zone-configuration') param name string @description('Optional. Tags of the resource.') diff --git a/scenarios/shared/bicep/private-endpoint.bicep b/scenarios/shared/bicep/private-endpoint.bicep index e78bdb29..c45da642 100644 --- a/scenarios/shared/bicep/private-endpoint.bicep +++ b/scenarios/shared/bicep/private-endpoint.bicep @@ -15,7 +15,7 @@ param snetId string @description('The resource id of private link service. The resource ID of the Az Resource that we need to attach the pe to.') param privateLinkServiceId string -@description('The resource that the private endpoint will be attached to, as shown in https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview#private-link-resource') +@description('The resource that the private endpoint will be attached to, as shown in https://learn.microsoft.com/azure/private-link/private-endpoint-overview#private-link-resource') param subresource string @description('Id of the relevant private DNS Zone, so that the PE can create an A record for the implicitly created nic') diff --git a/scenarios/shared/bicep/vnet.bicep b/scenarios/shared/bicep/vnet.bicep index 8672ae9d..908a9ffa 100644 --- a/scenarios/shared/bicep/vnet.bicep +++ b/scenarios/shared/bicep/vnet.bicep @@ -63,7 +63,7 @@ output vnetId string = vnet.id output vnetName string = vnet.name -// INFO: based on second example of https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/loops#array-and-index +// INFO: based on second example of https://learn.microsoft.com/azure/azure-resource-manager/bicep/loops#array-and-index @description('Outputs the array of the subnets, printing: index, subnetResourceId, subnerName. ') output vnetSubnets array = [ for (item, i) in subnets: { subnetIndex: i