.If no value is provided, a new temporary VNET and subnet will be created in the staging resource group and will be deleted along with the remaining temporary resources. |
| [`tags`](#parameter-tags) | object | Tags of the resource. |
+| [`validationProcess`](#parameter-validationprocess) | object | Configuration options and list of validations to be performed on the resulting image. |
| [`vmSize`](#parameter-vmsize) | string | Specifies the size for the VM. |
-| [`vmUserAssignedIdentities`](#parameter-vmuserassignedidentities) | array | List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts.Be aware, the user assigned identities specified in the \'managedIdentities\' parameter must have the \'Managed Identity Operator\' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM.
|
+| [`vmUserAssignedIdentities`](#parameter-vmuserassignedidentities) | array | List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts. Be aware, the user assigned identities specified in the 'managedIdentities' parameter must have the 'Managed Identity Operator' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM. |
**Generated parameters**
@@ -641,6 +675,20 @@ Specify the name of lock.
- Required: No
- Type: string
+### Parameter: `optimizeVmBoot`
+
+The optimize property can be enabled while creating a VM image and allows VM optimization to improve image creation time.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'Disabled'
+ 'Enabled'
+ ]
+ ```
+
### Parameter: `osDiskSizeGB`
Specifies the size of OS disk.
@@ -759,6 +807,140 @@ Tags of the resource.
- Required: No
- Type: object
+### Parameter: `validationProcess`
+
+Configuration options and list of validations to be performed on the resulting image.
+
+- Required: No
+- Type: object
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`continueDistributeOnFailure`](#parameter-validationprocesscontinuedistributeonfailure) | bool | If validation fails and this field is set to false, output image(s) will not be distributed. This is the default behavior. If validation fails and this field is set to true, output image(s) will still be distributed. Please use this option with caution as it may result in bad images being distributed for use. In either case (true or false), the end to end image run will be reported as having failed in case of a validation failure. [Note: This field has no effect if validation succeeds.]. |
+| [`inVMValidations`](#parameter-validationprocessinvmvalidations) | array | A list of validators that will be performed on the image. Azure Image Builder supports File, PowerShell and Shell validators. |
+| [`sourceValidationOnly`](#parameter-validationprocesssourcevalidationonly) | bool | If this field is set to true, the image specified in the 'source' section will directly be validated. No separate build will be run to generate and then validate a customized image. Not supported when performing customizations, validations or distributions on the image. |
+
+### Parameter: `validationProcess.continueDistributeOnFailure`
+
+If validation fails and this field is set to false, output image(s) will not be distributed. This is the default behavior. If validation fails and this field is set to true, output image(s) will still be distributed. Please use this option with caution as it may result in bad images being distributed for use. In either case (true or false), the end to end image run will be reported as having failed in case of a validation failure. [Note: This field has no effect if validation succeeds.].
+
+- Required: No
+- Type: bool
+
+### Parameter: `validationProcess.inVMValidations`
+
+A list of validators that will be performed on the image. Azure Image Builder supports File, PowerShell and Shell validators.
+
+- Required: No
+- Type: array
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`type`](#parameter-validationprocessinvmvalidationstype) | string | The type of validation. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`destination`](#parameter-validationprocessinvmvalidationsdestination) | string | Destination of the file. |
+| [`inline`](#parameter-validationprocessinvmvalidationsinline) | array | Array of commands to be run, separated by commas. |
+| [`name`](#parameter-validationprocessinvmvalidationsname) | string | Friendly Name to provide context on what this validation step does. |
+| [`runAsSystem`](#parameter-validationprocessinvmvalidationsrunassystem) | bool | If specified, the PowerShell script will be run with elevated privileges using the Local System user. Can only be true when the runElevated field above is set to true. |
+| [`runElevated`](#parameter-validationprocessinvmvalidationsrunelevated) | bool | If specified, the PowerShell script will be run with elevated privileges. |
+| [`scriptUri`](#parameter-validationprocessinvmvalidationsscripturi) | string | URI of the PowerShell script to be run for validation. It can be a github link, Azure Storage URI, etc. |
+| [`sha256Checksum`](#parameter-validationprocessinvmvalidationssha256checksum) | string | Value of sha256 checksum of the file, you generate this locally, and then Image Builder will checksum and validate. |
+| [`sourceUri`](#parameter-validationprocessinvmvalidationssourceuri) | string | The source URI of the file. |
+| [`validExitCodes`](#parameter-validationprocessinvmvalidationsvalidexitcodes) | array | Valid codes that can be returned from the script/inline command, this avoids reported failure of the script/inline command. |
+
+### Parameter: `validationProcess.inVMValidations.type`
+
+The type of validation.
+
+- Required: Yes
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'File'
+ 'PowerShell'
+ 'Shell'
+ ]
+ ```
+
+### Parameter: `validationProcess.inVMValidations.destination`
+
+Destination of the file.
+
+- Required: No
+- Type: string
+
+### Parameter: `validationProcess.inVMValidations.inline`
+
+Array of commands to be run, separated by commas.
+
+- Required: No
+- Type: array
+
+### Parameter: `validationProcess.inVMValidations.name`
+
+Friendly Name to provide context on what this validation step does.
+
+- Required: No
+- Type: string
+
+### Parameter: `validationProcess.inVMValidations.runAsSystem`
+
+If specified, the PowerShell script will be run with elevated privileges using the Local System user. Can only be true when the runElevated field above is set to true.
+
+- Required: No
+- Type: bool
+
+### Parameter: `validationProcess.inVMValidations.runElevated`
+
+If specified, the PowerShell script will be run with elevated privileges.
+
+- Required: No
+- Type: bool
+
+### Parameter: `validationProcess.inVMValidations.scriptUri`
+
+URI of the PowerShell script to be run for validation. It can be a github link, Azure Storage URI, etc.
+
+- Required: No
+- Type: string
+
+### Parameter: `validationProcess.inVMValidations.sha256Checksum`
+
+Value of sha256 checksum of the file, you generate this locally, and then Image Builder will checksum and validate.
+
+- Required: No
+- Type: string
+
+### Parameter: `validationProcess.inVMValidations.sourceUri`
+
+The source URI of the file.
+
+- Required: No
+- Type: string
+
+### Parameter: `validationProcess.inVMValidations.validExitCodes`
+
+Valid codes that can be returned from the script/inline command, this avoids reported failure of the script/inline command.
+
+- Required: No
+- Type: array
+
+### Parameter: `validationProcess.sourceValidationOnly`
+
+If this field is set to true, the image specified in the 'source' section will directly be validated. No separate build will be run to generate and then validate a customized image. Not supported when performing customizations, validations or distributions on the image.
+
+- Required: No
+- Type: bool
+
### Parameter: `vmSize`
Specifies the size for the VM.
@@ -769,7 +951,7 @@ Specifies the size for the VM.
### Parameter: `vmUserAssignedIdentities`
-List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts.
Be aware, the user assigned identities specified in the \'managedIdentities\' parameter must have the \'Managed Identity Operator\' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM.
+List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts. Be aware, the user assigned identities specified in the 'managedIdentities' parameter must have the 'Managed Identity Operator' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM.
- Required: No
- Type: array
diff --git a/avm/res/virtual-machine-images/image-template/main.bicep b/avm/res/virtual-machine-images/image-template/main.bicep
index 4c46be1680d..2a61c05386e 100644
--- a/avm/res/virtual-machine-images/image-template/main.bicep
+++ b/avm/res/virtual-machine-images/image-template/main.bicep
@@ -49,15 +49,22 @@ param roleAssignments roleAssignmentType
@description('Required. The distribution targets where the image output needs to go to.')
param distributions distributionType[]
-@description('''
-Optional. List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts.
-Be aware, the user assigned identities specified in the \'managedIdentities\' parameter must have the \'Managed Identity Operator\' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM.
-''')
+@description('Optional. List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts. Be aware, the user assigned identities specified in the \'managedIdentities\' parameter must have the \'Managed Identity Operator\' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM.')
param vmUserAssignedIdentities array = []
@description('Required. The managed identity definition for this resource.')
param managedIdentities managedIdentitiesType
+@description('Optional. Configuration options and list of validations to be performed on the resulting image.')
+param validationProcess validationProcessType
+
+@allowed([
+ 'Enabled'
+ 'Disabled'
+])
+@description('Optional. The optimize property can be enabled while creating a VM image and allows VM optimization to improve image creation time.')
+param optimizeVmBoot string?
+
var identity = {
type: 'UserAssigned'
userAssignedIdentities: reduce(
@@ -100,7 +107,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' =
}
}
-resource imageTemplate 'Microsoft.VirtualMachineImages/imageTemplates@2022-02-14' = {
+resource imageTemplate 'Microsoft.VirtualMachineImages/imageTemplates@2023-07-01' = {
#disable-next-line use-stable-resource-identifiers // Disabling as ImageTemplates are not idempotent and hence always must have new name
name: '${name}-${baseTime}'
location: location
@@ -164,6 +171,14 @@ resource imageTemplate 'Microsoft.VirtualMachineImages/imageTemplates@2022-02-14
: {})
)
]
+ validate: validationProcess
+ optimize: optimizeVmBoot != null
+ ? {
+ vmBoot: {
+ state: optimizeVmBoot
+ }
+ }
+ : null
}
}
@@ -316,6 +331,47 @@ type managedImageDistributionType = {
@description('Required. The resource ID of the managed image. Defaults to a compute image with name \'imageName-baseTime\' in the current resource group.')
imageResourceId: string?
- @description('Conditional. Name of the managed or unmanaged image that will be created..')
+ @description('Conditional. Name of the managed or unmanaged image that will be created.')
imageName: string
}
+
+type validationProcessType = {
+ @description('Optional. If validation fails and this field is set to false, output image(s) will not be distributed. This is the default behavior. If validation fails and this field is set to true, output image(s) will still be distributed. Please use this option with caution as it may result in bad images being distributed for use. In either case (true or false), the end to end image run will be reported as having failed in case of a validation failure. [Note: This field has no effect if validation succeeds.].')
+ continueDistributeOnFailure: bool?
+
+ @description('Optional. A list of validators that will be performed on the image. Azure Image Builder supports File, PowerShell and Shell validators.')
+ inVMValidations: {
+ @description('Required. The type of validation.')
+ type: ('PowerShell' | 'Shell' | 'File')
+
+ @description('Optional. Friendly Name to provide context on what this validation step does.')
+ name: string?
+
+ @description('Optional. URI of the PowerShell script to be run for validation. It can be a github link, Azure Storage URI, etc.')
+ scriptUri: string?
+
+ @description('Optional. Array of commands to be run, separated by commas.')
+ inline: string[]?
+
+ @description('Optional. Valid codes that can be returned from the script/inline command, this avoids reported failure of the script/inline command.')
+ validExitCodes: int[]?
+
+ @description('Optional. Value of sha256 checksum of the file, you generate this locally, and then Image Builder will checksum and validate.')
+ sha256Checksum: string?
+
+ @description('Optional. The source URI of the file.')
+ sourceUri: string?
+
+ @description('Optional. Destination of the file.')
+ destination: string?
+
+ @description('Optional. If specified, the PowerShell script will be run with elevated privileges using the Local System user. Can only be true when the runElevated field above is set to true.')
+ runAsSystem: bool?
+
+ @description('Optional. If specified, the PowerShell script will be run with elevated privileges.')
+ runElevated: bool?
+ }[]?
+
+ @description('Optional. If this field is set to true, the image specified in the \'source\' section will directly be validated. No separate build will be run to generate and then validate a customized image. Not supported when performing customizations, validations or distributions on the image.')
+ sourceValidationOnly: bool?
+}?
diff --git a/avm/res/virtual-machine-images/image-template/main.json b/avm/res/virtual-machine-images/image-template/main.json
index e2bef8c3df4..f3894b7f80d 100644
--- a/avm/res/virtual-machine-images/image-template/main.json
+++ b/avm/res/virtual-machine-images/image-template/main.json
@@ -5,8 +5,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "4620280027037163005"
+ "version": "0.26.170.59819",
+ "templateHash": "16618215702378872010"
},
"name": "Virtual Machine Image Templates",
"description": "This module deploys a Virtual Machine Image Template that can be consumed by Azure Image Builder (AIB).",
@@ -281,10 +281,122 @@
"imageName": {
"type": "string",
"metadata": {
- "description": "Conditional. Name of the managed or unmanaged image that will be created.."
+ "description": "Conditional. Name of the managed or unmanaged image that will be created."
}
}
}
+ },
+ "validationProcessType": {
+ "type": "object",
+ "properties": {
+ "continueDistributeOnFailure": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If validation fails and this field is set to false, output image(s) will not be distributed. This is the default behavior. If validation fails and this field is set to true, output image(s) will still be distributed. Please use this option with caution as it may result in bad images being distributed for use. In either case (true or false), the end to end image run will be reported as having failed in case of a validation failure. [Note: This field has no effect if validation succeeds.]."
+ }
+ },
+ "inVMValidations": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "allowedValues": [
+ "File",
+ "PowerShell",
+ "Shell"
+ ],
+ "metadata": {
+ "description": "Required. The type of validation."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Friendly Name to provide context on what this validation step does."
+ }
+ },
+ "scriptUri": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. URI of the PowerShell script to be run for validation. It can be a github link, Azure Storage URI, etc."
+ }
+ },
+ "inline": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of commands to be run, separated by commas."
+ }
+ },
+ "validExitCodes": {
+ "type": "array",
+ "items": {
+ "type": "int"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Valid codes that can be returned from the script/inline command, this avoids reported failure of the script/inline command."
+ }
+ },
+ "sha256Checksum": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Value of sha256 checksum of the file, you generate this locally, and then Image Builder will checksum and validate."
+ }
+ },
+ "sourceUri": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The source URI of the file."
+ }
+ },
+ "destination": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Destination of the file."
+ }
+ },
+ "runAsSystem": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If specified, the PowerShell script will be run with elevated privileges using the Local System user. Can only be true when the runElevated field above is set to true."
+ }
+ },
+ "runElevated": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If specified, the PowerShell script will be run with elevated privileges."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A list of validators that will be performed on the image. Azure Image Builder supports File, PowerShell and Shell validators."
+ }
+ },
+ "sourceValidationOnly": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If this field is set to true, the image specified in the 'source' section will directly be validated. No separate build will be run to generate and then validate a customized image. Not supported when performing customizations, validations or distributions on the image."
+ }
+ }
+ },
+ "nullable": true
}
},
"parameters": {
@@ -396,7 +508,7 @@
"type": "array",
"defaultValue": [],
"metadata": {
- "description": "Optional. List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts.\nBe aware, the user assigned identities specified in the \\'managedIdentities\\' parameter must have the \\'Managed Identity Operator\\' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM.\n"
+ "description": "Optional. List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts. Be aware, the user assigned identities specified in the 'managedIdentities' parameter must have the 'Managed Identity Operator' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM."
}
},
"managedIdentities": {
@@ -404,6 +516,23 @@
"metadata": {
"description": "Required. The managed identity definition for this resource."
}
+ },
+ "validationProcess": {
+ "$ref": "#/definitions/validationProcessType",
+ "metadata": {
+ "description": "Optional. Configuration options and list of validations to be performed on the resulting image."
+ }
+ },
+ "optimizeVmBoot": {
+ "type": "string",
+ "nullable": true,
+ "allowedValues": [
+ "Enabled",
+ "Disabled"
+ ],
+ "metadata": {
+ "description": "Optional. The optimize property can be enabled while creating a VM image and allows VM optimization to improve image creation time."
+ }
}
},
"variables": {
@@ -442,7 +571,7 @@
},
"imageTemplate": {
"type": "Microsoft.VirtualMachineImages/imageTemplates",
- "apiVersion": "2022-02-14",
+ "apiVersion": "2023-07-01",
"name": "[format('{0}-{1}', parameters('name'), parameters('baseTime'))]",
"location": "[parameters('location')]",
"tags": "[parameters('tags')]",
@@ -464,7 +593,9 @@
},
"source": "[parameters('imageSource')]",
"customize": "[parameters('customizationSteps')]",
- "stagingResourceGroup": "[parameters('stagingResourceGroup')]"
+ "stagingResourceGroup": "[parameters('stagingResourceGroup')]",
+ "validate": "[parameters('validationProcess')]",
+ "optimize": "[if(not(equals(parameters('optimizeVmBoot'), null())), createObject('vmBoot', createObject('state', parameters('optimizeVmBoot'))), null())]"
}
},
"imageTemplate_lock": {
@@ -545,7 +676,7 @@
"metadata": {
"description": "The location the resource was deployed into."
},
- "value": "[reference('imageTemplate', '2022-02-14', 'full').location]"
+ "value": "[reference('imageTemplate', '2023-07-01', 'full').location]"
}
}
}
\ No newline at end of file
diff --git a/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep b/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep
index 2bd4869dd5d..49eba449c66 100644
--- a/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep
+++ b/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep
@@ -81,6 +81,20 @@ module testDeployment '../../../main.bicep' = {
]
}
]
+ validationProcess: {
+ continueDistributeOnFailure: true
+ sourceValidationOnly: false
+ inVMValidations: [
+ {
+ type: 'Shell'
+ name: 'Validate-Software'
+ inline: [
+ 'echo "Software validation successful."'
+ ]
+ }
+ ]
+ }
+ optimizeVmBoot: 'Enabled'
imageSource: {
type: 'PlatformImage'
publisher: 'canonical'
diff --git a/avm/res/virtual-machine-images/image-template/version.json b/avm/res/virtual-machine-images/image-template/version.json
index 7fa401bdf78..9481fea58ee 100644
--- a/avm/res/virtual-machine-images/image-template/version.json
+++ b/avm/res/virtual-machine-images/image-template/version.json
@@ -1,6 +1,6 @@
{
"$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
- "version": "0.1",
+ "version": "0.2",
"pathFilters": [
"./main.json"
]
From a302384a779c9b32d15ba8378ac902d8df5b5778 Mon Sep 17 00:00:00 2001
From: Alexander Sehr
Date: Sat, 11 May 2024 18:43:33 +0200
Subject: [PATCH 33/52] feat: Migrated module App/Jobs from CARML -
`avm/res/app/job` (#1823)
## Description
Migrated `Microsoft.App/jobs` from CARML & updated to latest specs
cc: @MrRoundRobin fyi
## Pipeline Reference
| Pipeline |
| -------- |
|
[![avm.res.app.job](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.job.yml/badge.svg?branch=users%2Falsehr%2FappJobsModule&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.job.yml)
|
## Type of Change
- [ ] Update to CI Environment or utlities (Non-module effecting
changes)
- [x] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
---------
Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com>
---
.github/CODEOWNERS | 2 +-
.github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 +
.github/workflows/avm.res.app.job.yml | 90 ++
avm/res/app/job/ORPHANED.md | 4 +
avm/res/app/job/README.md | 812 ++++++++++++++++++
avm/res/app/job/main.bicep | 236 +++++
avm/res/app/job/main.json | 406 +++++++++
.../job/tests/e2e/defaults/dependencies.bicep | 21 +
.../job/tests/e2e/defaults/main.test.bicep | 74 ++
.../app/job/tests/e2e/max/dependencies.bicep | 40 +
avm/res/app/job/tests/e2e/max/main.test.bicep | 136 +++
.../tests/e2e/waf-aligned/dependencies.bicep | 40 +
.../job/tests/e2e/waf-aligned/main.test.bicep | 98 +++
avm/res/app/job/version.json | 7 +
14 files changed, 1966 insertions(+), 1 deletion(-)
create mode 100644 .github/workflows/avm.res.app.job.yml
create mode 100644 avm/res/app/job/ORPHANED.md
create mode 100644 avm/res/app/job/README.md
create mode 100644 avm/res/app/job/main.bicep
create mode 100644 avm/res/app/job/main.json
create mode 100644 avm/res/app/job/tests/e2e/defaults/dependencies.bicep
create mode 100644 avm/res/app/job/tests/e2e/defaults/main.test.bicep
create mode 100644 avm/res/app/job/tests/e2e/max/dependencies.bicep
create mode 100644 avm/res/app/job/tests/e2e/max/main.test.bicep
create mode 100644 avm/res/app/job/tests/e2e/waf-aligned/dependencies.bicep
create mode 100644 avm/res/app/job/tests/e2e/waf-aligned/main.test.bicep
create mode 100644 avm/res/app/job/version.json
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index a35c3158ea3..893e030b61b 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -7,10 +7,10 @@
/avm/ptn/authorization/role-assignment/ @Azure/avm-ptn-authorization-roleassignment-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/ptn/security/security-center/ @Azure/avm-ptn-security-securitycenter-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep @Azure/avm-core-team-technical-bicep
-#/avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep
/avm/res/analysis-services/server/ @Azure/avm-res-analysisservices-server-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/api-management/service/ @Azure/avm-res-apimanagement-service-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/app/container-app/ @Azure/avm-res-app-containerapp-module-owners-bicep @Azure/avm-core-team-technical-bicep
+/avm/res/app/job/ @Azure/avm-res-app-job-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/app/managed-environment/ @Azure/avm-res-app-managedenvironment-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/app-configuration/configuration-store/ @Azure/avm-res-appconfiguration-configurationstore-module-owners-bicep @Azure/avm-core-team-technical-bicep
#/avm/res/authorization/lock/ @Azure/avm-res-authorization-lock-module-owners-bicep @Azure/avm-core-team-technical-bicep
diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml
index 459c075404c..bd18fad337c 100644
--- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml
+++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml
@@ -51,6 +51,7 @@ body:
- "avm/res/api-management/service"
- "avm/res/app-configuration/configuration-store"
- "avm/res/app/container-app"
+ - "avm/res/app/job"
- "avm/res/app/managed-environment"
- "avm/res/automation/automation-account"
- "avm/res/batch/batch-account"
diff --git a/.github/workflows/avm.res.app.job.yml b/.github/workflows/avm.res.app.job.yml
new file mode 100644
index 00000000000..1d91926f99e
--- /dev/null
+++ b/.github/workflows/avm.res.app.job.yml
@@ -0,0 +1,90 @@
+name: "avm.res.app.job"
+
+on:
+ schedule:
+ - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month)
+ workflow_dispatch:
+ inputs:
+ staticValidation:
+ type: boolean
+ description: "Execute static validation"
+ required: false
+ default: true
+ deploymentValidation:
+ type: boolean
+ description: "Execute deployment validation"
+ required: false
+ default: true
+ removeDeployment:
+ type: boolean
+ description: "Remove deployed module"
+ required: false
+ default: true
+ customLocation:
+ type: string
+ description: "Default location overwrite (e.g., eastus)"
+ required: false
+ push:
+ branches:
+ - main
+ paths:
+ - ".github/actions/templates/avm-**"
+ - ".github/workflows/avm.template.module.yml"
+ - ".github/workflows/avm.res.app.job.yml"
+ - "avm/res/app/job/**"
+ - "avm/utilities/pipelines/**"
+ - "!avm/utilities/pipelines/platform/**"
+ - "!*/**/README.md"
+
+env:
+ modulePath: "avm/res/app/job"
+ workflowPath: ".github/workflows/avm.res.app.job.yml"
+
+concurrency:
+ group: ${{ github.workflow }}
+
+jobs:
+ ###########################
+ # Initialize pipeline #
+ ###########################
+ job_initialize_pipeline:
+ runs-on: ubuntu-latest
+ name: "Initialize pipeline"
+ steps:
+ - name: "Checkout"
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: "Set input parameters to output variables"
+ id: get-workflow-param
+ uses: ./.github/actions/templates/avm-getWorkflowInput
+ with:
+ workflowPath: "${{ env.workflowPath}}"
+ - name: "Get module test file paths"
+ id: get-module-test-file-paths
+ uses: ./.github/actions/templates/avm-getModuleTestFiles
+ with:
+ modulePath: "${{ env.modulePath }}"
+ outputs:
+ workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }}
+ moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }}
+ psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }}
+ modulePath: "${{ env.modulePath }}"
+
+ ##############################
+ # Call reusable workflow #
+ ##############################
+ call-workflow-passing-data:
+ name: "Run"
+ permissions:
+ id-token: write # For OIDC
+ contents: write # For release tags
+ needs:
+ - job_initialize_pipeline
+ uses: ./.github/workflows/avm.template.module.yml
+ with:
+ workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}"
+ moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}"
+ psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}"
+ modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}"
+ secrets: inherit
diff --git a/avm/res/app/job/ORPHANED.md b/avm/res/app/job/ORPHANED.md
new file mode 100644
index 00000000000..ef8fa911d2b
--- /dev/null
+++ b/avm/res/app/job/ORPHANED.md
@@ -0,0 +1,4 @@
+⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️
+
+- Only security and bug fixes are being handled by the AVM core team at present.
+- If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)!
\ No newline at end of file
diff --git a/avm/res/app/job/README.md b/avm/res/app/job/README.md
new file mode 100644
index 00000000000..93e9c6e55a0
--- /dev/null
+++ b/avm/res/app/job/README.md
@@ -0,0 +1,812 @@
+# Container App Jobs `[Microsoft.App/jobs]`
+
+> ⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️
+>
+> - Only security and bug fixes are being handled by the AVM core team at present.
+> - If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)!
+
+This module deploys a Container App Job.
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Usage examples](#Usage-examples)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+- [Data Collection](#Data-Collection)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+| `Microsoft.App/jobs` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2023-05-01/jobs) |
+| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) |
+| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) |
+
+## Usage examples
+
+The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository.
+
+>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order.
+
+>**Note**: To reference the module, please use the following syntax `br/public:avm/res/app/job:`.
+
+- [Using only defaults](#example-1-using-only-defaults)
+- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
+
+### Example 1: _Using only defaults_
+
+This instance deploys the module with the minimum set of required parameters.
+
+
+
+
+via Bicep module
+
+```bicep
+module job 'br/public:avm/res/app/job:' = {
+ name: 'jobDeployment'
+ params: {
+ // Required parameters
+ containers: [
+ {
+ image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
+ name: 'simple-hello-world-container'
+ resources: {
+ cpu: ''
+ memory: '0.5Gi'
+ }
+ }
+ ]
+ environmentResourceId: ''
+ name: 'ajmin001'
+ triggerType: 'Manual'
+ // Non-required parameters
+ location: ''
+ manualTriggerConfig: {
+ parallelism: 1
+ replicaCompletionCount: 1
+ }
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "containers": {
+ "value": [
+ {
+ "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest",
+ "name": "simple-hello-world-container",
+ "resources": {
+ "cpu": "",
+ "memory": "0.5Gi"
+ }
+ }
+ ]
+ },
+ "environmentResourceId": {
+ "value": ""
+ },
+ "name": {
+ "value": "ajmin001"
+ },
+ "triggerType": {
+ "value": "Manual"
+ },
+ // Non-required parameters
+ "location": {
+ "value": ""
+ },
+ "manualTriggerConfig": {
+ "value": {
+ "parallelism": 1,
+ "replicaCompletionCount": 1
+ }
+ }
+ }
+}
+```
+
+
+
+
+### Example 2: _Using large parameter set_
+
+This instance deploys the module with most of its features enabled.
+
+
+
+
+via Bicep module
+
+```bicep
+module job 'br/public:avm/res/app/job:' = {
+ name: 'jobDeployment'
+ params: {
+ // Required parameters
+ containers: [
+ {
+ image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
+ name: 'simple-hello-world-container'
+ probes: [
+ {
+ httpGet: {
+ httpHeaders: [
+ {
+ name: 'Custom-Header'
+ value: 'Awesome'
+ }
+ ]
+ path: '/health'
+ port: 8080
+ }
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ type: 'Liveness'
+ }
+ ]
+ resources: {
+ cpu: ''
+ memory: '0.5Gi'
+ }
+ }
+ ]
+ environmentResourceId: ''
+ name: 'ajmax001'
+ triggerType: 'Manual'
+ // Non-required parameters
+ location: ''
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourceIds: [
+ ''
+ ]
+ }
+ manualTriggerConfig: {
+ parallelism: 1
+ replicaCompletionCount: 1
+ }
+ roleAssignments: [
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: 'Owner'
+ }
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
+ }
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: ''
+ }
+ ]
+ secrets: {
+ secureList: [
+ {
+ name: 'customtest'
+ value: ''
+ }
+ ]
+ }
+ tags: {
+ Env: 'test'
+ 'hidden-title': 'This is visible in the resource name'
+ }
+ workloadProfileName: ''
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "containers": {
+ "value": [
+ {
+ "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest",
+ "name": "simple-hello-world-container",
+ "probes": [
+ {
+ "httpGet": {
+ "httpHeaders": [
+ {
+ "name": "Custom-Header",
+ "value": "Awesome"
+ }
+ ],
+ "path": "/health",
+ "port": 8080
+ },
+ "initialDelaySeconds": 3,
+ "periodSeconds": 3,
+ "type": "Liveness"
+ }
+ ],
+ "resources": {
+ "cpu": "",
+ "memory": "0.5Gi"
+ }
+ }
+ ]
+ },
+ "environmentResourceId": {
+ "value": ""
+ },
+ "name": {
+ "value": "ajmax001"
+ },
+ "triggerType": {
+ "value": "Manual"
+ },
+ // Non-required parameters
+ "location": {
+ "value": ""
+ },
+ "lock": {
+ "value": {
+ "kind": "CanNotDelete",
+ "name": "myCustomLockName"
+ }
+ },
+ "managedIdentities": {
+ "value": {
+ "systemAssigned": true,
+ "userAssignedResourceIds": [
+ ""
+ ]
+ }
+ },
+ "manualTriggerConfig": {
+ "value": {
+ "parallelism": 1,
+ "replicaCompletionCount": 1
+ }
+ },
+ "roleAssignments": {
+ "value": [
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": "Owner"
+ },
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c"
+ },
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": ""
+ }
+ ]
+ },
+ "secrets": {
+ "value": {
+ "secureList": [
+ {
+ "name": "customtest",
+ "value": ""
+ }
+ ]
+ }
+ },
+ "tags": {
+ "value": {
+ "Env": "test",
+ "hidden-title": "This is visible in the resource name"
+ }
+ },
+ "workloadProfileName": {
+ "value": ""
+ }
+ }
+}
+```
+
+
+
+
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+via Bicep module
+
+```bicep
+module job 'br/public:avm/res/app/job:' = {
+ name: 'jobDeployment'
+ params: {
+ // Required parameters
+ containers: [
+ {
+ image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
+ name: 'simple-hello-world-container'
+ probes: [
+ {
+ httpGet: {
+ httpHeaders: [
+ {
+ name: 'Custom-Header'
+ value: 'Awesome'
+ }
+ ]
+ path: '/health'
+ port: 8080
+ }
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ type: 'Liveness'
+ }
+ ]
+ resources: {
+ cpu: ''
+ memory: '0.5Gi'
+ }
+ }
+ ]
+ environmentResourceId: ''
+ name: 'ajwaf001'
+ triggerType: 'Manual'
+ // Non-required parameters
+ location: ''
+ manualTriggerConfig: {
+ parallelism: 1
+ replicaCompletionCount: 1
+ }
+ tags: {
+ Env: 'test'
+ 'hidden-title': 'This is visible in the resource name'
+ }
+ workloadProfileName: ''
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "containers": {
+ "value": [
+ {
+ "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest",
+ "name": "simple-hello-world-container",
+ "probes": [
+ {
+ "httpGet": {
+ "httpHeaders": [
+ {
+ "name": "Custom-Header",
+ "value": "Awesome"
+ }
+ ],
+ "path": "/health",
+ "port": 8080
+ },
+ "initialDelaySeconds": 3,
+ "periodSeconds": 3,
+ "type": "Liveness"
+ }
+ ],
+ "resources": {
+ "cpu": "",
+ "memory": "0.5Gi"
+ }
+ }
+ ]
+ },
+ "environmentResourceId": {
+ "value": ""
+ },
+ "name": {
+ "value": "ajwaf001"
+ },
+ "triggerType": {
+ "value": "Manual"
+ },
+ // Non-required parameters
+ "location": {
+ "value": ""
+ },
+ "manualTriggerConfig": {
+ "value": {
+ "parallelism": 1,
+ "replicaCompletionCount": 1
+ }
+ },
+ "tags": {
+ "value": {
+ "Env": "test",
+ "hidden-title": "This is visible in the resource name"
+ }
+ },
+ "workloadProfileName": {
+ "value": ""
+ }
+ }
+}
+```
+
+
+
+
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`containers`](#parameter-containers) | array | List of container definitions for the Container App. |
+| [`environmentResourceId`](#parameter-environmentresourceid) | string | Resource ID of environment. |
+| [`name`](#parameter-name) | string | Name of the Container App. |
+| [`triggerType`](#parameter-triggertype) | string | Trigger type of the job. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. |
+| [`eventTriggerConfig`](#parameter-eventtriggerconfig) | object | Required if TriggerType is Event. Configuration of an event driven job. |
+| [`initContainersTemplate`](#parameter-initcontainerstemplate) | array | List of specialized containers that run before app containers. |
+| [`location`](#parameter-location) | string | Location for all Resources. |
+| [`lock`](#parameter-lock) | object | The lock settings of the service. |
+| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. |
+| [`manualTriggerConfig`](#parameter-manualtriggerconfig) | object | Required if TriggerType is Manual. Configuration of a manual job. |
+| [`registries`](#parameter-registries) | array | Collection of private container registry credentials for containers used by the Container app. |
+| [`replicaRetryLimit`](#parameter-replicaretrylimit) | int | The maximum number of times a replica can be retried. |
+| [`replicaTimeout`](#parameter-replicatimeout) | int | Maximum number of seconds a replica is allowed to run. |
+| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. |
+| [`scheduleTriggerConfig`](#parameter-scheduletriggerconfig) | object | Required if TriggerType is Schedule. Configuration of a schedule based job. |
+| [`secrets`](#parameter-secrets) | secureObject | The secrets of the Container App. |
+| [`tags`](#parameter-tags) | object | Tags of the resource. |
+| [`volumes`](#parameter-volumes) | array | List of volume definitions for the Container App. |
+| [`workloadProfileName`](#parameter-workloadprofilename) | string | The name of the workload profile to use. |
+
+### Parameter: `containers`
+
+List of container definitions for the Container App.
+
+- Required: Yes
+- Type: array
+
+### Parameter: `environmentResourceId`
+
+Resource ID of environment.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `name`
+
+Name of the Container App.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `triggerType`
+
+Trigger type of the job.
+
+- Required: Yes
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'Event'
+ 'Manual'
+ 'Schedule'
+ ]
+ ```
+
+### Parameter: `enableTelemetry`
+
+Enable/Disable usage telemetry for module.
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `eventTriggerConfig`
+
+Required if TriggerType is Event. Configuration of an event driven job.
+
+- Required: No
+- Type: object
+
+### Parameter: `initContainersTemplate`
+
+List of specialized containers that run before app containers.
+
+- Required: No
+- Type: array
+
+### Parameter: `location`
+
+Location for all Resources.
+
+- Required: No
+- Type: string
+- Default: `[resourceGroup().location]`
+
+### Parameter: `lock`
+
+The lock settings of the service.
+
+- Required: No
+- Type: object
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`kind`](#parameter-lockkind) | string | Specify the type of lock. |
+| [`name`](#parameter-lockname) | string | Specify the name of lock. |
+
+### Parameter: `lock.kind`
+
+Specify the type of lock.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'CanNotDelete'
+ 'None'
+ 'ReadOnly'
+ ]
+ ```
+
+### Parameter: `lock.name`
+
+Specify the name of lock.
+
+- Required: No
+- Type: string
+
+### Parameter: `managedIdentities`
+
+The managed identity definition for this resource.
+
+- Required: No
+- Type: object
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. |
+| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. |
+
+### Parameter: `managedIdentities.systemAssigned`
+
+Enables system assigned managed identity on the resource.
+
+- Required: No
+- Type: bool
+
+### Parameter: `managedIdentities.userAssignedResourceIds`
+
+The resource ID(s) to assign to the resource.
+
+- Required: No
+- Type: array
+
+### Parameter: `manualTriggerConfig`
+
+Required if TriggerType is Manual. Configuration of a manual job.
+
+- Required: No
+- Type: object
+
+### Parameter: `registries`
+
+Collection of private container registry credentials for containers used by the Container app.
+
+- Required: No
+- Type: array
+
+### Parameter: `replicaRetryLimit`
+
+The maximum number of times a replica can be retried.
+
+- Required: No
+- Type: int
+- Default: `0`
+
+### Parameter: `replicaTimeout`
+
+Maximum number of seconds a replica is allowed to run.
+
+- Required: No
+- Type: int
+- Default: `1800`
+
+### Parameter: `roleAssignments`
+
+Array of role assignments to create.
+
+- Required: No
+- Type: array
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. |
+| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". |
+| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. |
+| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. |
+| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. |
+| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. |
+
+### Parameter: `roleAssignments.principalId`
+
+The principal ID of the principal (user/group/identity) to assign the role to.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `roleAssignments.roleDefinitionIdOrName`
+
+The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `roleAssignments.condition`
+
+The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.conditionVersion`
+
+Version of the condition.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ '2.0'
+ ]
+ ```
+
+### Parameter: `roleAssignments.delegatedManagedIdentityResourceId`
+
+The Resource Id of the delegated managed identity resource.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.description`
+
+The description of the role assignment.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.principalType`
+
+The principal type of the assigned principal ID.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'Device'
+ 'ForeignGroup'
+ 'Group'
+ 'ServicePrincipal'
+ 'User'
+ ]
+ ```
+
+### Parameter: `scheduleTriggerConfig`
+
+Required if TriggerType is Schedule. Configuration of a schedule based job.
+
+- Required: No
+- Type: object
+
+### Parameter: `secrets`
+
+The secrets of the Container App.
+
+- Required: No
+- Type: secureObject
+
+### Parameter: `tags`
+
+Tags of the resource.
+
+- Required: No
+- Type: object
+
+### Parameter: `volumes`
+
+List of volume definitions for the Container App.
+
+- Required: No
+- Type: array
+
+### Parameter: `workloadProfileName`
+
+The name of the workload profile to use.
+
+- Required: No
+- Type: string
+- Default: `'Consumption'`
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `location` | string | The location the resource was deployed into. |
+| `name` | string | The name of the Container App Job. |
+| `resourceGroupName` | string | The name of the resource group the Container App Job was deployed into. |
+| `resourceId` | string | The resource ID of the Container App Job. |
+| `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. |
+
+## Cross-referenced modules
+
+_None_
+
+## Data Collection
+
+The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.
diff --git a/avm/res/app/job/main.bicep b/avm/res/app/job/main.bicep
new file mode 100644
index 00000000000..2d9a0f6ba68
--- /dev/null
+++ b/avm/res/app/job/main.bicep
@@ -0,0 +1,236 @@
+metadata name = 'Container App Jobs'
+metadata description = 'This module deploys a Container App Job.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Required. Name of the Container App.')
+param name string
+
+@description('Optional. Location for all Resources.')
+param location string = resourceGroup().location
+
+@description('Required. Resource ID of environment.')
+param environmentResourceId string
+
+@description('Optional. The lock settings of the service.')
+param lock lockType
+
+@description('Optional. Tags of the resource.')
+param tags object?
+
+@description('Optional. Collection of private container registry credentials for containers used by the Container app.')
+param registries array?
+
+@description('Optional. The managed identity definition for this resource.')
+param managedIdentities managedIdentitiesType
+
+@description('Optional. Array of role assignments to create.')
+param roleAssignments roleAssignmentType
+
+@description('Optional. Enable/Disable usage telemetry for module.')
+param enableTelemetry bool = true
+
+@description('Required. List of container definitions for the Container App.')
+param containers array
+
+@description('Optional. List of specialized containers that run before app containers.')
+param initContainersTemplate array?
+
+@description('Optional. Required if TriggerType is Event. Configuration of an event driven job.')
+param eventTriggerConfig object?
+
+@description('Optional. Required if TriggerType is Schedule. Configuration of a schedule based job.')
+param scheduleTriggerConfig object?
+
+@description('Optional. Required if TriggerType is Manual. Configuration of a manual job.')
+param manualTriggerConfig object?
+
+@description('Optional. The maximum number of times a replica can be retried.')
+param replicaRetryLimit int = 0
+
+@description('Optional. The name of the workload profile to use.')
+param workloadProfileName string = 'Consumption'
+
+@description('Optional. The secrets of the Container App.')
+@secure()
+param secrets object?
+
+@description('Optional. List of volume definitions for the Container App.')
+param volumes array?
+
+@description('Optional. Maximum number of seconds a replica is allowed to run.')
+param replicaTimeout int = 1800
+
+@allowed([
+ 'Event'
+ 'Manual'
+ 'Schedule'
+])
+@description('Required. Trigger type of the job.')
+param triggerType string
+
+var secretList = secrets.?secureList
+
+var formattedUserAssignedIdentities = reduce(
+ map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }),
+ {},
+ (cur, next) => union(cur, next)
+) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} }
+
+var identity = !empty(managedIdentities)
+ ? {
+ type: (managedIdentities.?systemAssigned ?? false)
+ ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned')
+ : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None')
+ userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null
+ }
+ : null
+
+var builtInRoleNames = {
+ 'ContainerApp Reader': subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b'
+ )
+ Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
+ Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')
+ Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')
+ 'Role Based Access Control Administrator (Preview)': subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ 'f58310d9-a9f6-439a-9e8d-f62e7b41a168'
+ )
+ 'User Access Administrator': subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9'
+ )
+}
+
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: '46d3xbcp.res.app-job.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
+ properties: {
+ mode: 'Incremental'
+ template: {
+ '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
+ contentVersion: '1.0.0.0'
+ resources: []
+ outputs: {
+ telemetry: {
+ type: 'String'
+ value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
+ }
+ }
+ }
+ }
+}
+
+resource job 'Microsoft.App/jobs@2023-05-01' = {
+ name: name
+ tags: tags
+ location: location
+ identity: identity
+ properties: {
+ environmentId: environmentResourceId
+ configuration: {
+ eventTriggerConfig: triggerType == 'Event' ? eventTriggerConfig : null
+ manualTriggerConfig: triggerType == 'Manual' ? manualTriggerConfig : null
+ scheduleTriggerConfig: triggerType == 'Schedule' ? scheduleTriggerConfig : null
+ replicaRetryLimit: replicaRetryLimit
+ replicaTimeout: replicaTimeout
+ registries: registries
+ secrets: secretList
+ triggerType: triggerType
+ }
+ template: {
+ containers: containers
+ initContainers: initContainersTemplate
+ volumes: volumes
+ }
+ workloadProfileName: workloadProfileName
+ }
+}
+
+resource job_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
+ name: lock.?name ?? 'lock-${name}'
+ properties: {
+ level: lock.?kind ?? ''
+ notes: lock.?kind == 'CanNotDelete'
+ ? 'Cannot delete resource or child resources.'
+ : 'Cannot delete or modify the resource or child resources.'
+ }
+ scope: job
+}
+
+resource automationAccount_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [
+ for (roleAssignment, index) in (roleAssignments ?? []): {
+ name: guid(job.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName)
+ properties: {
+ roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName)
+ ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName]
+ : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/')
+ ? roleAssignment.roleDefinitionIdOrName
+ : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)
+ principalId: roleAssignment.principalId
+ description: roleAssignment.?description
+ principalType: roleAssignment.?principalType
+ condition: roleAssignment.?condition
+ conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set
+ delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId
+ }
+ scope: job
+ }
+]
+@description('The resource ID of the Container App Job.')
+output resourceId string = job.id
+
+@description('The name of the resource group the Container App Job was deployed into.')
+output resourceGroupName string = resourceGroup().name
+
+@description('The name of the Container App Job.')
+output name string = job.name
+
+@description('The location the resource was deployed into.')
+output location string = job.location
+
+@description('The principal ID of the system assigned identity.')
+output systemAssignedMIPrincipalId string = job.?identity.?principalId ?? ''
+
+// =============== //
+// Definitions //
+// =============== //
+
+type managedIdentitiesType = {
+ @description('Optional. Enables system assigned managed identity on the resource.')
+ systemAssigned: bool?
+
+ @description('Optional. The resource ID(s) to assign to the resource.')
+ userAssignedResourceIds: string[]?
+}?
+
+type lockType = {
+ @description('Optional. Specify the name of lock.')
+ name: string?
+
+ @description('Optional. Specify the type of lock.')
+ kind: ('CanNotDelete' | 'ReadOnly' | 'None')?
+}?
+
+type roleAssignmentType = {
+ @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.')
+ roleDefinitionIdOrName: string
+
+ @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.')
+ principalId: string
+
+ @description('Optional. The principal type of the assigned principal ID.')
+ principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')?
+
+ @description('Optional. The description of the role assignment.')
+ description: string?
+
+ @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".')
+ condition: string?
+
+ @description('Optional. Version of the condition.')
+ conditionVersion: '2.0'?
+
+ @description('Optional. The Resource Id of the delegated managed identity resource.')
+ delegatedManagedIdentityResourceId: string?
+}[]?
diff --git a/avm/res/app/job/main.json b/avm/res/app/job/main.json
new file mode 100644
index 00000000000..6d8ee06c25b
--- /dev/null
+++ b/avm/res/app/job/main.json
@@ -0,0 +1,406 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.27.1.19265",
+ "templateHash": "11649443218681434280"
+ },
+ "name": "Container App Jobs",
+ "description": "This module deploys a Container App Job.",
+ "owner": "Azure/module-maintainers"
+ },
+ "definitions": {
+ "managedIdentitiesType": {
+ "type": "object",
+ "properties": {
+ "systemAssigned": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enables system assigned managed identity on the resource."
+ }
+ },
+ "userAssignedResourceIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID(s) to assign to the resource."
+ }
+ }
+ },
+ "nullable": true
+ },
+ "lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ }
+ },
+ "nullable": true
+ },
+ "roleAssignmentType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "roleDefinitionIdOrName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to."
+ }
+ },
+ "principalType": {
+ "type": "string",
+ "allowedValues": [
+ "Device",
+ "ForeignGroup",
+ "Group",
+ "ServicePrincipal",
+ "User"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The description of the role assignment."
+ }
+ },
+ "condition": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"."
+ }
+ },
+ "conditionVersion": {
+ "type": "string",
+ "allowedValues": [
+ "2.0"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Version of the condition."
+ }
+ },
+ "delegatedManagedIdentityResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Resource Id of the delegated managed identity resource."
+ }
+ }
+ }
+ },
+ "nullable": true
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the Container App."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all Resources."
+ }
+ },
+ "environmentResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of environment."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ },
+ "registries": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Collection of private container registry credentials for containers used by the Container app."
+ }
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentitiesType",
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource."
+ }
+ },
+ "roleAssignments": {
+ "$ref": "#/definitions/roleAssignmentType",
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "containers": {
+ "type": "array",
+ "metadata": {
+ "description": "Required. List of container definitions for the Container App."
+ }
+ },
+ "initContainersTemplate": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of specialized containers that run before app containers."
+ }
+ },
+ "eventTriggerConfig": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Required if TriggerType is Event. Configuration of an event driven job."
+ }
+ },
+ "scheduleTriggerConfig": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Required if TriggerType is Schedule. Configuration of a schedule based job."
+ }
+ },
+ "manualTriggerConfig": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Required if TriggerType is Manual. Configuration of a manual job."
+ }
+ },
+ "replicaRetryLimit": {
+ "type": "int",
+ "defaultValue": 0,
+ "metadata": {
+ "description": "Optional. The maximum number of times a replica can be retried."
+ }
+ },
+ "workloadProfileName": {
+ "type": "string",
+ "defaultValue": "Consumption",
+ "metadata": {
+ "description": "Optional. The name of the workload profile to use."
+ }
+ },
+ "secrets": {
+ "type": "secureObject",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The secrets of the Container App."
+ }
+ },
+ "volumes": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of volume definitions for the Container App."
+ }
+ },
+ "replicaTimeout": {
+ "type": "int",
+ "defaultValue": 1800,
+ "metadata": {
+ "description": "Optional. Maximum number of seconds a replica is allowed to run."
+ }
+ },
+ "triggerType": {
+ "type": "string",
+ "allowedValues": [
+ "Event",
+ "Manual",
+ "Schedule"
+ ],
+ "metadata": {
+ "description": "Required. Trigger type of the job."
+ }
+ }
+ },
+ "variables": {
+ "secretList": "[tryGet(parameters('secrets'), 'secureList')]",
+ "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]",
+ "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]",
+ "builtInRoleNames": {
+ "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]",
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
+ "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
+ "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]",
+ "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]"
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2023-07-01",
+ "name": "[format('46d3xbcp.res.app-job.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]",
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "resources": [],
+ "outputs": {
+ "telemetry": {
+ "type": "String",
+ "value": "For more information, see https://aka.ms/avm/TelemetryInfo"
+ }
+ }
+ }
+ }
+ },
+ "job": {
+ "type": "Microsoft.App/jobs",
+ "apiVersion": "2023-05-01",
+ "name": "[parameters('name')]",
+ "tags": "[parameters('tags')]",
+ "location": "[parameters('location')]",
+ "identity": "[variables('identity')]",
+ "properties": {
+ "environmentId": "[parameters('environmentResourceId')]",
+ "configuration": {
+ "eventTriggerConfig": "[if(equals(parameters('triggerType'), 'Event'), parameters('eventTriggerConfig'), null())]",
+ "manualTriggerConfig": "[if(equals(parameters('triggerType'), 'Manual'), parameters('manualTriggerConfig'), null())]",
+ "scheduleTriggerConfig": "[if(equals(parameters('triggerType'), 'Schedule'), parameters('scheduleTriggerConfig'), null())]",
+ "replicaRetryLimit": "[parameters('replicaRetryLimit')]",
+ "replicaTimeout": "[parameters('replicaTimeout')]",
+ "registries": "[parameters('registries')]",
+ "secrets": "[variables('secretList')]",
+ "triggerType": "[parameters('triggerType')]"
+ },
+ "template": {
+ "containers": "[parameters('containers')]",
+ "initContainers": "[parameters('initContainersTemplate')]",
+ "volumes": "[parameters('volumes')]"
+ },
+ "workloadProfileName": "[parameters('workloadProfileName')]"
+ }
+ },
+ "job_lock": {
+ "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]",
+ "type": "Microsoft.Authorization/locks",
+ "apiVersion": "2020-05-01",
+ "scope": "[format('Microsoft.App/jobs/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]",
+ "properties": {
+ "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]",
+ "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]"
+ },
+ "dependsOn": [
+ "job"
+ ]
+ },
+ "automationAccount_roleAssignments": {
+ "copy": {
+ "name": "automationAccount_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.App/jobs/{0}', parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.App/jobs', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]",
+ "properties": {
+ "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]",
+ "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]",
+ "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]",
+ "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]",
+ "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]",
+ "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]",
+ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]"
+ },
+ "dependsOn": [
+ "job"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the Container App Job."
+ },
+ "value": "[resourceId('Microsoft.App/jobs', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the Container App Job was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Container App Job."
+ },
+ "value": "[parameters('name')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('job', '2023-05-01', 'full').location]"
+ },
+ "systemAssignedMIPrincipalId": {
+ "type": "string",
+ "metadata": {
+ "description": "The principal ID of the system assigned identity."
+ },
+ "value": "[coalesce(tryGet(tryGet(reference('job', '2023-05-01', 'full'), 'identity'), 'principalId'), '')]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/app/job/tests/e2e/defaults/dependencies.bicep b/avm/res/app/job/tests/e2e/defaults/dependencies.bicep
new file mode 100644
index 00000000000..bb2af3d0f83
--- /dev/null
+++ b/avm/res/app/job/tests/e2e/defaults/dependencies.bicep
@@ -0,0 +1,21 @@
+@description('Required. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Environment to create.')
+param managedEnvironmentName string
+
+resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
+ name: managedEnvironmentName
+ location: location
+ properties: {
+ workloadProfiles: [
+ {
+ workloadProfileType: 'Consumption'
+ name: 'Consumption'
+ }
+ ]
+ }
+}
+
+@description('The resource ID of the created Managed Environment.')
+output managedEnvironmentResourceId string = managedEnvironment.id
diff --git a/avm/res/app/job/tests/e2e/defaults/main.test.bicep b/avm/res/app/job/tests/e2e/defaults/main.test.bicep
new file mode 100644
index 00000000000..546fa7c4c83
--- /dev/null
+++ b/avm/res/app/job/tests/e2e/defaults/main.test.bicep
@@ -0,0 +1,74 @@
+targetScope = 'subscription'
+
+metadata name = 'Using only defaults'
+metadata description = 'This instance deploys the module with the minimum set of required parameters.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-app.job-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ajmin'
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '#_namePrefix_#'
+
+// =========== //
+// Deployments //
+// =========== //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-paramNested'
+ params: {
+ location: resourceLocation
+ managedEnvironmentName: 'dep-${namePrefix}-menv-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId
+ location: resourceLocation
+ triggerType: 'Manual'
+ manualTriggerConfig: {
+ replicaCompletionCount: 1
+ parallelism: 1
+ }
+ containers: [
+ {
+ name: 'simple-hello-world-container'
+ image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
+ resources: {
+ // workaround as 'float' values are not supported in Bicep, yet the resource providers expects them. Related issue: https://github.com/Azure/bicep/issues/1386
+ cpu: json('0.25')
+ memory: '0.5Gi'
+ }
+ }
+ ]
+ }
+ }
+]
diff --git a/avm/res/app/job/tests/e2e/max/dependencies.bicep b/avm/res/app/job/tests/e2e/max/dependencies.bicep
new file mode 100644
index 00000000000..b03d4aca931
--- /dev/null
+++ b/avm/res/app/job/tests/e2e/max/dependencies.bicep
@@ -0,0 +1,40 @@
+@description('Required. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Environment for Container Apps to create.')
+param managedEnvironmentName string
+
+@description('Required. The name of the managed identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the workload profile to create.')
+param workloadProfileName string
+
+resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
+ name: managedEnvironmentName
+ location: location
+ properties: {
+ workloadProfiles: [
+ {
+ name: workloadProfileName
+ workloadProfileType: 'D4'
+ maximumCount: 1
+ minimumCount: 1
+ }
+ ]
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Managed Environment.')
+output managedEnvironmentResourceId string = managedEnvironment.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/avm/res/app/job/tests/e2e/max/main.test.bicep b/avm/res/app/job/tests/e2e/max/main.test.bicep
new file mode 100644
index 00000000000..18ae51956b3
--- /dev/null
+++ b/avm/res/app/job/tests/e2e/max/main.test.bicep
@@ -0,0 +1,136 @@
+targetScope = 'subscription'
+
+metadata name = 'Using large parameter set'
+metadata description = 'This instance deploys the module with most of its features enabled.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-app.job-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ajmax'
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '#_namePrefix_#'
+
+// =========== //
+// Deployments //
+// =========== //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-paramNested'
+ params: {
+ location: resourceLocation
+ managedEnvironmentName: 'dep-${namePrefix}-menv-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ workloadProfileName: serviceShort
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Env: 'test'
+ }
+ environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId
+ workloadProfileName: serviceShort
+ location: resourceLocation
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourceIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ secrets: {
+ secureList: [
+ {
+ name: 'customtest'
+ value: guid(deployment().name)
+ }
+ ]
+ }
+ triggerType: 'Manual'
+ manualTriggerConfig: {
+ replicaCompletionCount: 1
+ parallelism: 1
+ }
+ containers: [
+ {
+ name: 'simple-hello-world-container'
+ image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
+ resources: {
+ // workaround as 'float' values are not supported in Bicep, yet the resource providers expects them. Related issue: https://github.com/Azure/bicep/issues/1386
+ cpu: json('0.25')
+ memory: '0.5Gi'
+ }
+ probes: [
+ {
+ type: 'Liveness'
+ httpGet: {
+ path: '/health'
+ port: 8080
+ httpHeaders: [
+ {
+ name: 'Custom-Header'
+ value: 'Awesome'
+ }
+ ]
+ }
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ }
+ ]
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Owner'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ {
+ roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ {
+ roleDefinitionIdOrName: subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ 'acdd72a7-3385-48ef-bd42-f606fba81ae7'
+ )
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ }
+ }
+]
diff --git a/avm/res/app/job/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/app/job/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 00000000000..b03d4aca931
--- /dev/null
+++ b/avm/res/app/job/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,40 @@
+@description('Required. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Environment for Container Apps to create.')
+param managedEnvironmentName string
+
+@description('Required. The name of the managed identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the workload profile to create.')
+param workloadProfileName string
+
+resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
+ name: managedEnvironmentName
+ location: location
+ properties: {
+ workloadProfiles: [
+ {
+ name: workloadProfileName
+ workloadProfileType: 'D4'
+ maximumCount: 1
+ minimumCount: 1
+ }
+ ]
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Managed Environment.')
+output managedEnvironmentResourceId string = managedEnvironment.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/avm/res/app/job/tests/e2e/waf-aligned/main.test.bicep b/avm/res/app/job/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 00000000000..93b2d344ab1
--- /dev/null
+++ b/avm/res/app/job/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,98 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-app.job-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ajwaf'
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '#_namePrefix_#'
+
+// =========== //
+// Deployments //
+// =========== //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-paramNested'
+ params: {
+ location: resourceLocation
+ managedEnvironmentName: 'dep-${namePrefix}-menv-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ workloadProfileName: serviceShort
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Env: 'test'
+ }
+ environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId
+ workloadProfileName: serviceShort
+ location: resourceLocation
+ triggerType: 'Manual'
+ manualTriggerConfig: {
+ replicaCompletionCount: 1
+ parallelism: 1
+ }
+ containers: [
+ {
+ name: 'simple-hello-world-container'
+ image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
+ resources: {
+ // workaround as 'float' values are not supported in Bicep, yet the resource providers expects them. Related issue: https://github.com/Azure/bicep/issues/1386
+ cpu: json('0.25')
+ memory: '0.5Gi'
+ }
+ probes: [
+ {
+ type: 'Liveness'
+ httpGet: {
+ path: '/health'
+ port: 8080
+ httpHeaders: [
+ {
+ name: 'Custom-Header'
+ value: 'Awesome'
+ }
+ ]
+ }
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ }
+ ]
+ }
+ ]
+ }
+ }
+]
diff --git a/avm/res/app/job/version.json b/avm/res/app/job/version.json
new file mode 100644
index 00000000000..7fa401bdf78
--- /dev/null
+++ b/avm/res/app/job/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
+ "version": "0.1",
+ "pathFilters": [
+ "./main.json"
+ ]
+}
From 25b69089e72243b2b5e4acb668350c7dbe44444e Mon Sep 17 00:00:00 2001
From: Erika Gressi <56914614+eriqua@users.noreply.github.com>
Date: Sun, 12 May 2024 20:59:32 +0200
Subject: [PATCH 34/52] fix: Remove ptn readme (#1916)
## Description
Remove ptn readme after the folder has been populated
## Pipeline Reference
| Pipeline |
| -------- |
| |
## Type of Change
- [x] Update to CI Environment or utlities (Non-module effecting
changes)
- [ ] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
## Checklist
- [x] I'm sure there are no other open Pull Requests for the same
update/change
- [ ] I have run `Set-AVMModule` locally to generate the supporting
module files.
- [ ] My corresponding pipelines / checks run clean and green without
any errors or warnings
---
avm/ptn/readme.md | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 avm/ptn/readme.md
diff --git a/avm/ptn/readme.md b/avm/ptn/readme.md
deleted file mode 100644
index 933815b8f84..00000000000
--- a/avm/ptn/readme.md
+++ /dev/null
@@ -1 +0,0 @@
-TODO: Add patterns
From f859bf29347200da1c81208ef1db6377cb29da05 Mon Sep 17 00:00:00 2001
From: Alexander Sehr
Date: Mon, 13 May 2024 14:44:13 +0200
Subject: [PATCH 35/52] fix: Added case handling for `loadFromFile()` functions
that introduce generated variables in ARM JSON files (#1917)
## Description
- When using a Bicep function like `loadFileAsBase64()` or
`loadFromJson`, the compiled ARM template contains a generated variable
with a name like `$fxv#1` or `$fxv#2`
- This variable currently fails the Pester test 'Variable names should
be camel-cased'
- This PR updates the test by
- Adding support for the aforementioned deterministic variable name
- Updating the test to return the incorrect variables as opposed to just
claim that there's at least one incorrect variable
## Pipeline Reference
| Pipeline |
| -------- |
|
[![avm.res.key-vault.vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml/badge.svg?branch=users%2Falsehr%2FloadFromJson&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml)
|
|
[![avm.res.storage.storage-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=users%2Falsehr%2FloadFromJson&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml)
(Pester passed) |
## Type of Change
- [x] Update to CI Environment or utlities (Non-module effecting
changes)
- [ ] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
---------
Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com>
---
.../compliance/module.tests.ps1 | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1
index e2df8ed6560..92e9b154c59 100644
--- a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1
+++ b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1
@@ -810,17 +810,17 @@ Describe 'Module tests' -Tag 'Module' {
return
}
- $CamelCasingFlag = @()
- $Variable = $templateFileContent.variables.Keys
-
- foreach ($Variab in $Variable) {
- if ($Variab.substring(0, 1) -cnotmatch '[a-z]' -or $Variab -match '-') {
- $CamelCasingFlag += $false
- } else {
- $CamelCasingFlag += $true
+ $incorrectVariables = @()
+ $Variables = $templateFileContent.variables.Keys
+
+ foreach ($variable in $Variables) {
+ # ^[a-z]+[a-zA-Z]+$ = starts with lower-case letter & may have uppercase letter later
+ # ^\$fxv#[0-9]+$ = starts with [$fxv#] & ends with a number. This function value is created as a variable when using a Bicep function like loadFileAsBase64() or loadFromJson()
+ if ($variable -cnotmatch '^[a-z]+[a-zA-Z]+$|^\$fxv#[0-9]+$' -or $variable -match '-') {
+ $incorrectVariables += $variable
}
}
- $CamelCasingFlag | Should -Not -Contain $false
+ $incorrectVariables | Should -BeNullOrEmpty
}
}
From 65d14291291b3122d5e83619098f7ee5fd90b8aa Mon Sep 17 00:00:00 2001
From: Kris Baranek
Date: Mon, 13 May 2024 20:38:03 +0200
Subject: [PATCH 36/52] feat: New Module `avm/res/communication/email-service`
(#1582)
## Description
New Module `avm/res/communication/email-service`.
Related Module Proposal:
https://github.com/Azure/Azure-Verified-Modules/issues/749
## Adding a new module
- [x] A proposal has been submitted and approved.
- [ ] I have included "Closes #{module_proposal_issue_number}" in the PR
description.
- [ ] I have run `brm validate` locally to verify the module files.
- [x] I have run deployment tests locally to ensure the module is
deployable.
## Pipeline references
| Pipeline |
| - |
|
[![avm.res.communication.email-service](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.communication.email-service.yml/badge.svg?branch=users%2Fkrbar%2FemailSvcModule)](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.communication.email-service.yml)
|
---------
Co-authored-by: Alexander Sehr
---
.github/CODEOWNERS | 1 +
.github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 +
.../avm.res.communication.email-service.yml | 90 +++
avm/res/communication/email-service/README.md | 558 ++++++++++++++
.../email-service/domain/README.md | 255 ++++++
.../email-service/domain/main.bicep | 166 ++++
.../email-service/domain/main.json | 387 ++++++++++
.../domain/sender-username/README.md | 92 +++
.../domain/sender-username/main.bicep | 52 ++
.../domain/sender-username/main.json | 81 ++
.../communication/email-service/main.bicep | 179 +++++
avm/res/communication/email-service/main.json | 725 ++++++++++++++++++
.../tests/e2e/defaults/main.test.bicep | 49 ++
.../tests/e2e/max/dependencies.bicep | 13 +
.../tests/e2e/max/main.test.bicep | 133 ++++
.../tests/e2e/waf-aligned/main.test.bicep | 54 ++
.../communication/email-service/version.json | 7 +
17 files changed, 2843 insertions(+)
create mode 100644 .github/workflows/avm.res.communication.email-service.yml
create mode 100644 avm/res/communication/email-service/README.md
create mode 100644 avm/res/communication/email-service/domain/README.md
create mode 100644 avm/res/communication/email-service/domain/main.bicep
create mode 100644 avm/res/communication/email-service/domain/main.json
create mode 100644 avm/res/communication/email-service/domain/sender-username/README.md
create mode 100644 avm/res/communication/email-service/domain/sender-username/main.bicep
create mode 100644 avm/res/communication/email-service/domain/sender-username/main.json
create mode 100644 avm/res/communication/email-service/main.bicep
create mode 100644 avm/res/communication/email-service/main.json
create mode 100644 avm/res/communication/email-service/tests/e2e/defaults/main.test.bicep
create mode 100644 avm/res/communication/email-service/tests/e2e/max/dependencies.bicep
create mode 100644 avm/res/communication/email-service/tests/e2e/max/main.test.bicep
create mode 100644 avm/res/communication/email-service/tests/e2e/waf-aligned/main.test.bicep
create mode 100644 avm/res/communication/email-service/version.json
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 893e030b61b..217d9d8f8cc 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -24,6 +24,7 @@
/avm/res/cache/redis/ @Azure/avm-res-cache-redis-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/cdn/profile/ @Azure/avm-res-cdn-profile-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/cognitive-services/account/ @Azure/avm-res-cognitiveservices-account-module-owners-bicep @Azure/avm-core-team-technical-bicep
+/avm/res/communication/email-service/ @Azure/avm-res-communication-emailservice-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/compute/availability-set/ @Azure/avm-res-compute-availabilityset-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/compute/disk/ @Azure/avm-res-compute-disk-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/compute/disk-encryption-set/ @Azure/avm-res-compute-diskencryptionset-module-owners-bicep @Azure/avm-core-team-technical-bicep
diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml
index bd18fad337c..9765814bacb 100644
--- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml
+++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml
@@ -58,6 +58,7 @@ body:
- "avm/res/cache/redis"
- "avm/res/cdn/profile"
- "avm/res/cognitive-services/account"
+ - "avm/res/communication/email-service"
- "avm/res/compute/availability-set"
- "avm/res/compute/disk"
- "avm/res/compute/disk-encryption-set"
diff --git a/.github/workflows/avm.res.communication.email-service.yml b/.github/workflows/avm.res.communication.email-service.yml
new file mode 100644
index 00000000000..f47e87b1390
--- /dev/null
+++ b/.github/workflows/avm.res.communication.email-service.yml
@@ -0,0 +1,90 @@
+name: "avm.res.communication.email-service"
+
+on:
+ schedule:
+ - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month)
+ workflow_dispatch:
+ inputs:
+ staticValidation:
+ type: boolean
+ description: "Execute static validation"
+ required: false
+ default: true
+ deploymentValidation:
+ type: boolean
+ description: "Execute deployment validation"
+ required: false
+ default: true
+ removeDeployment:
+ type: boolean
+ description: "Remove deployed module"
+ required: false
+ default: true
+ customLocation:
+ type: string
+ description: "Default location overwrite (e.g., eastus)"
+ required: false
+ push:
+ branches:
+ - main
+ paths:
+ - ".github/actions/templates/avm-**"
+ - ".github/workflows/avm.template.module.yml"
+ - ".github/workflows/avm.res.communication.email-service.yml"
+ - "avm/res/communication/email-service/**"
+ - "avm/utilities/pipelines/**"
+ - "!avm/utilities/pipelines/platform/**"
+ - "!*/**/README.md"
+
+env:
+ modulePath: "avm/res/communication/email-service"
+ workflowPath: ".github/workflows/avm.res.communication.email-service.yml"
+
+concurrency:
+ group: ${{ github.workflow }}
+
+jobs:
+ ###########################
+ # Initialize pipeline #
+ ###########################
+ job_initialize_pipeline:
+ runs-on: ubuntu-latest
+ name: "Initialize pipeline"
+ steps:
+ - name: "Checkout"
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: "Set input parameters to output variables"
+ id: get-workflow-param
+ uses: ./.github/actions/templates/avm-getWorkflowInput
+ with:
+ workflowPath: "${{ env.workflowPath}}"
+ - name: "Get module test file paths"
+ id: get-module-test-file-paths
+ uses: ./.github/actions/templates/avm-getModuleTestFiles
+ with:
+ modulePath: "${{ env.modulePath }}"
+ outputs:
+ workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }}
+ moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }}
+ psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }}
+ modulePath: "${{ env.modulePath }}"
+
+ ##############################
+ # Call reusable workflow #
+ ##############################
+ call-workflow-passing-data:
+ name: "Run"
+ permissions:
+ id-token: write # For OIDC
+ contents: write # For release tags
+ needs:
+ - job_initialize_pipeline
+ uses: ./.github/workflows/avm.template.module.yml
+ with:
+ workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}"
+ moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}"
+ psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}"
+ modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}"
+ secrets: inherit
diff --git a/avm/res/communication/email-service/README.md b/avm/res/communication/email-service/README.md
new file mode 100644
index 00000000000..92df127665b
--- /dev/null
+++ b/avm/res/communication/email-service/README.md
@@ -0,0 +1,558 @@
+# Email Services `[Microsoft.Communication/emailServices]`
+
+This module deploys an Email Service
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Usage examples](#Usage-examples)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+- [Data Collection](#Data-Collection)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) |
+| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) |
+| `Microsoft.Communication/emailServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Communication/emailServices) |
+| `Microsoft.Communication/emailServices/domains` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Communication/emailServices/domains) |
+| `Microsoft.Communication/emailServices/domains/senderUsernames` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Communication/emailServices/domains/senderUsernames) |
+
+## Usage examples
+
+The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository.
+
+>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order.
+
+>**Note**: To reference the module, please use the following syntax `br/public:avm/res/communication/email-service:`.
+
+- [Using only defaults](#example-1-using-only-defaults)
+- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
+
+### Example 1: _Using only defaults_
+
+This instance deploys the module with the minimum set of required parameters.
+
+
+
+
+via Bicep module
+
+```bicep
+module emailService 'br/public:avm/res/communication/email-service:' = {
+ name: 'emailServiceDeployment'
+ params: {
+ // Required parameters
+ dataLocation: 'Europe'
+ name: 'cesmin001'
+ // Non-required parameters
+ location: 'global'
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "dataLocation": {
+ "value": "Europe"
+ },
+ "name": {
+ "value": "cesmin001"
+ },
+ // Non-required parameters
+ "location": {
+ "value": "global"
+ }
+ }
+}
+```
+
+
+
+
+### Example 2: _Using large parameter set_
+
+This instance deploys the module with most of its features enabled.
+
+
+
+
+via Bicep module
+
+```bicep
+module emailService 'br/public:avm/res/communication/email-service:' = {
+ name: 'emailServiceDeployment'
+ params: {
+ // Required parameters
+ dataLocation: 'United States'
+ name: 'cesmax001'
+ // Non-required parameters
+ domains: [
+ {
+ domainManagement: 'AzureManaged'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ name: 'AzureManagedDomain'
+ roleAssignments: [
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: 'Owner'
+ }
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
+ }
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: ''
+ }
+ ]
+ senderUsernames: [
+ {
+ displayName: 'Do Not Reply'
+ name: 'donotreply'
+ userName: 'DoNotReply'
+ }
+ {
+ displayName: 'Customer Service'
+ name: 'customerservice'
+ userName: 'CustomerService'
+ }
+ ]
+ tags: {
+ Role: 'DeploymentValidation'
+ }
+ userEngagementTracking: 'Enabled'
+ }
+ ]
+ location: 'global'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: 'Owner'
+ }
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
+ }
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: ''
+ }
+ ]
+ tags: {
+ Environment: 'Non-Prod'
+ 'hidden-title': 'This is visible in the resource name'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "dataLocation": {
+ "value": "United States"
+ },
+ "name": {
+ "value": "cesmax001"
+ },
+ // Non-required parameters
+ "domains": {
+ "value": [
+ {
+ "domainManagement": "AzureManaged",
+ "lock": {
+ "kind": "CanNotDelete",
+ "name": "myCustomLockName"
+ },
+ "name": "AzureManagedDomain",
+ "roleAssignments": [
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": "Owner"
+ },
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c"
+ },
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": ""
+ }
+ ],
+ "senderUsernames": [
+ {
+ "displayName": "Do Not Reply",
+ "name": "donotreply",
+ "userName": "DoNotReply"
+ },
+ {
+ "displayName": "Customer Service",
+ "name": "customerservice",
+ "userName": "CustomerService"
+ }
+ ],
+ "tags": {
+ "Role": "DeploymentValidation"
+ },
+ "userEngagementTracking": "Enabled"
+ }
+ ]
+ },
+ "location": {
+ "value": "global"
+ },
+ "lock": {
+ "value": {
+ "kind": "CanNotDelete",
+ "name": "myCustomLockName"
+ }
+ },
+ "roleAssignments": {
+ "value": [
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": "Owner"
+ },
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c"
+ },
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": ""
+ }
+ ]
+ },
+ "tags": {
+ "value": {
+ "Environment": "Non-Prod",
+ "hidden-title": "This is visible in the resource name",
+ "Role": "DeploymentValidation"
+ }
+ }
+ }
+}
+```
+
+
+
+
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+via Bicep module
+
+```bicep
+module emailService 'br/public:avm/res/communication/email-service:' = {
+ name: 'emailServiceDeployment'
+ params: {
+ // Required parameters
+ dataLocation: 'Germany'
+ name: 'ceswaf001'
+ // Non-required parameters
+ location: 'global'
+ tags: {
+ Environment: 'Non-Prod'
+ 'hidden-title': 'This is visible in the resource name'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "dataLocation": {
+ "value": "Germany"
+ },
+ "name": {
+ "value": "ceswaf001"
+ },
+ // Non-required parameters
+ "location": {
+ "value": "global"
+ },
+ "tags": {
+ "value": {
+ "Environment": "Non-Prod",
+ "hidden-title": "This is visible in the resource name",
+ "Role": "DeploymentValidation"
+ }
+ }
+ }
+}
+```
+
+
+
+
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`dataLocation`](#parameter-datalocation) | string | The location where the communication service stores its data at rest. |
+| [`name`](#parameter-name) | string | Name of the email service to create. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`domains`](#parameter-domains) | array | The domains to deploy into this namespace. |
+| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. |
+| [`location`](#parameter-location) | string | Location for all Resources. |
+| [`lock`](#parameter-lock) | object | The lock settings of the service. |
+| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. |
+| [`tags`](#parameter-tags) | object | Endpoint tags. |
+
+### Parameter: `dataLocation`
+
+The location where the communication service stores its data at rest.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `name`
+
+Name of the email service to create.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `domains`
+
+The domains to deploy into this namespace.
+
+- Required: No
+- Type: array
+
+### Parameter: `enableTelemetry`
+
+Enable/Disable usage telemetry for module.
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `location`
+
+Location for all Resources.
+
+- Required: No
+- Type: string
+- Default: `'global'`
+
+### Parameter: `lock`
+
+The lock settings of the service.
+
+- Required: No
+- Type: object
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`kind`](#parameter-lockkind) | string | Specify the type of lock. |
+| [`name`](#parameter-lockname) | string | Specify the name of lock. |
+
+### Parameter: `lock.kind`
+
+Specify the type of lock.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'CanNotDelete'
+ 'None'
+ 'ReadOnly'
+ ]
+ ```
+
+### Parameter: `lock.name`
+
+Specify the name of lock.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments`
+
+Array of role assignments to create.
+
+- Required: No
+- Type: array
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. |
+| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". |
+| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. |
+| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. |
+| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. |
+| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. |
+
+### Parameter: `roleAssignments.principalId`
+
+The principal ID of the principal (user/group/identity) to assign the role to.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `roleAssignments.roleDefinitionIdOrName`
+
+The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `roleAssignments.condition`
+
+The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.conditionVersion`
+
+Version of the condition.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ '2.0'
+ ]
+ ```
+
+### Parameter: `roleAssignments.delegatedManagedIdentityResourceId`
+
+The Resource Id of the delegated managed identity resource.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.description`
+
+The description of the role assignment.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.principalType`
+
+The principal type of the assigned principal ID.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'Device'
+ 'ForeignGroup'
+ 'Group'
+ 'ServicePrincipal'
+ 'User'
+ ]
+ ```
+
+### Parameter: `tags`
+
+Endpoint tags.
+
+- Required: No
+- Type: object
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `domainNamess` | array | The list of the email domain names. |
+| `domainResourceIds` | array | The list of the email domain resource ids. |
+| `location` | string | The location the email service was deployed into. |
+| `name` | string | The name of the email service. |
+| `resourceGroupName` | string | The resource group the email service was deployed into. |
+| `resourceId` | string | The resource ID of the email service. |
+
+## Cross-referenced modules
+
+_None_
+
+## Data Collection
+
+The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.
diff --git a/avm/res/communication/email-service/domain/README.md b/avm/res/communication/email-service/domain/README.md
new file mode 100644
index 00000000000..47e8dd60c2c
--- /dev/null
+++ b/avm/res/communication/email-service/domain/README.md
@@ -0,0 +1,255 @@
+# Email Services Domains `[Microsoft.Communication/emailServices/domains]`
+
+This module deploys an Email Service Domain
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+- [Data Collection](#Data-Collection)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) |
+| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) |
+| `Microsoft.Communication/emailServices/domains` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Communication/emailServices/domains) |
+| `Microsoft.Communication/emailServices/domains/senderUsernames` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Communication/emailServices/domains/senderUsernames) |
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | Name of the domain to create. |
+
+**Conditional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`emailServiceName`](#parameter-emailservicename) | string | The name of the parent email service. Required if the template is used in a standalone deployment. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`domainManagement`](#parameter-domainmanagement) | string | Describes how the Domain resource is being managed. |
+| [`location`](#parameter-location) | string | Location for all Resources. |
+| [`lock`](#parameter-lock) | object | The lock settings of the service. |
+| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. |
+| [`senderUsernames`](#parameter-senderusernames) | array | The domains to deploy into this namespace. |
+| [`tags`](#parameter-tags) | object | Endpoint tags. |
+| [`userEngagementTracking`](#parameter-userengagementtracking) | string | Describes whether user engagement tracking is enabled or disabled. |
+
+### Parameter: `name`
+
+Name of the domain to create.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `emailServiceName`
+
+The name of the parent email service. Required if the template is used in a standalone deployment.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `domainManagement`
+
+Describes how the Domain resource is being managed.
+
+- Required: No
+- Type: string
+- Default: `'AzureManaged'`
+- Allowed:
+ ```Bicep
+ [
+ 'AzureManaged'
+ 'CustomerManaged'
+ 'CustomerManagedInExchangeOnline'
+ ]
+ ```
+
+### Parameter: `location`
+
+Location for all Resources.
+
+- Required: No
+- Type: string
+- Default: `'global'`
+
+### Parameter: `lock`
+
+The lock settings of the service.
+
+- Required: No
+- Type: object
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`kind`](#parameter-lockkind) | string | Specify the type of lock. |
+| [`name`](#parameter-lockname) | string | Specify the name of lock. |
+
+### Parameter: `lock.kind`
+
+Specify the type of lock.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'CanNotDelete'
+ 'None'
+ 'ReadOnly'
+ ]
+ ```
+
+### Parameter: `lock.name`
+
+Specify the name of lock.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments`
+
+Array of role assignments to create.
+
+- Required: No
+- Type: array
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. |
+| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". |
+| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. |
+| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. |
+| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. |
+| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. |
+
+### Parameter: `roleAssignments.principalId`
+
+The principal ID of the principal (user/group/identity) to assign the role to.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `roleAssignments.roleDefinitionIdOrName`
+
+The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `roleAssignments.condition`
+
+The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.conditionVersion`
+
+Version of the condition.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ '2.0'
+ ]
+ ```
+
+### Parameter: `roleAssignments.delegatedManagedIdentityResourceId`
+
+The Resource Id of the delegated managed identity resource.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.description`
+
+The description of the role assignment.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.principalType`
+
+The principal type of the assigned principal ID.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'Device'
+ 'ForeignGroup'
+ 'Group'
+ 'ServicePrincipal'
+ 'User'
+ ]
+ ```
+
+### Parameter: `senderUsernames`
+
+The domains to deploy into this namespace.
+
+- Required: No
+- Type: array
+
+### Parameter: `tags`
+
+Endpoint tags.
+
+- Required: No
+- Type: object
+
+### Parameter: `userEngagementTracking`
+
+Describes whether user engagement tracking is enabled or disabled.
+
+- Required: No
+- Type: string
+- Default: `'Disabled'`
+- Allowed:
+ ```Bicep
+ [
+ 'Disabled'
+ 'Enabled'
+ ]
+ ```
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `name` | string | The name of the domain. |
+| `resourceGroupName` | string | The name of the resource group the domain was created in. |
+| `resourceId` | string | The resource ID of the domain. |
+
+## Cross-referenced modules
+
+_None_
+
+## Data Collection
+
+The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.
diff --git a/avm/res/communication/email-service/domain/main.bicep b/avm/res/communication/email-service/domain/main.bicep
new file mode 100644
index 00000000000..ebb7598e329
--- /dev/null
+++ b/avm/res/communication/email-service/domain/main.bicep
@@ -0,0 +1,166 @@
+metadata name = 'Email Services Domains'
+metadata description = 'This module deploys an Email Service Domain'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Conditional. The name of the parent email service. Required if the template is used in a standalone deployment.')
+param emailServiceName string
+
+@minLength(1)
+@maxLength(253)
+@description('Required. Name of the domain to create.')
+param name string
+
+@description('Optional. Location for all Resources.')
+param location string = 'global'
+
+@description('Optional. Endpoint tags.')
+param tags object?
+
+@allowed([
+ 'AzureManaged'
+ 'CustomerManaged'
+ 'CustomerManagedInExchangeOnline'
+])
+@description('Optional. Describes how the Domain resource is being managed.')
+param domainManagement string = 'AzureManaged'
+
+@allowed([
+ 'Enabled'
+ 'Disabled'
+])
+@description('Optional. Describes whether user engagement tracking is enabled or disabled.')
+param userEngagementTracking string = 'Disabled'
+
+@description('Optional. The domains to deploy into this namespace.')
+param senderUsernames array?
+
+@description('Optional. The lock settings of the service.')
+param lock lockType
+
+@description('Optional. Array of role assignments to create.')
+param roleAssignments roleAssignmentType
+
+var builtInRoleNames = {
+ Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
+ Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')
+ Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')
+ 'Role Based Access Control Administrator (Preview)': subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ 'f58310d9-a9f6-439a-9e8d-f62e7b41a168'
+ )
+ 'User Access Administrator': subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9'
+ )
+}
+
+// ============== //
+// Resources //
+// ============== //
+
+resource emailService 'Microsoft.Communication/emailServices@2023-04-01' existing = {
+ name: emailServiceName
+}
+
+resource domain 'Microsoft.Communication/emailServices/domains@2023-04-01' = {
+ name: name
+ location: location
+ tags: tags
+ parent: emailService
+ properties: {
+ domainManagement: domainManagement
+ userEngagementTracking: userEngagementTracking
+ }
+}
+
+module domain_senderUsernames 'sender-username/main.bicep' = [
+ for (senderUsername, index) in senderUsernames ?? []: {
+ name: '${uniqueString(deployment().name, location)}-domain-senderusername-${index}'
+ params: {
+ emailServiceName: emailService.name
+ domainName: domain.name
+ name: senderUsername.name
+ username: senderUsername.username
+ displayName: senderUsername.?displayName
+ }
+ }
+]
+
+resource domain_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
+ name: lock.?name ?? 'lock-${name}'
+ properties: {
+ level: lock.?kind ?? ''
+ notes: lock.?kind == 'CanNotDelete'
+ ? 'Cannot delete resource or child resources.'
+ : 'Cannot delete or modify the resource or child resources.'
+ }
+ scope: domain
+}
+
+resource domain_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [
+ for (roleAssignment, index) in (roleAssignments ?? []): {
+ name: guid(domain.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName)
+ properties: {
+ roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName)
+ ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName]
+ : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/')
+ ? roleAssignment.roleDefinitionIdOrName
+ : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)
+ principalId: roleAssignment.principalId
+ description: roleAssignment.?description
+ principalType: roleAssignment.?principalType
+ condition: roleAssignment.?condition
+ conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set
+ delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId
+ }
+ scope: domain
+ }
+]
+
+// ============ //
+// Outputs //
+// ============ //
+
+@description('The name of the domain.')
+output name string = domain.name
+
+@description('The resource ID of the domain.')
+output resourceId string = domain.id
+
+@description('The name of the resource group the domain was created in.')
+output resourceGroupName string = resourceGroup().name
+
+// ================ //
+// Definitions //
+// ================ //
+
+type lockType = {
+ @description('Optional. Specify the name of lock.')
+ name: string?
+
+ @description('Optional. Specify the type of lock.')
+ kind: ('CanNotDelete' | 'ReadOnly' | 'None')?
+}?
+
+type roleAssignmentType = {
+ @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.')
+ roleDefinitionIdOrName: string
+
+ @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.')
+ principalId: string
+
+ @description('Optional. The principal type of the assigned principal ID.')
+ principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')?
+
+ @description('Optional. The description of the role assignment.')
+ description: string?
+
+ @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".')
+ condition: string?
+
+ @description('Optional. Version of the condition.')
+ conditionVersion: '2.0'?
+
+ @description('Optional. The Resource Id of the delegated managed identity resource.')
+ delegatedManagedIdentityResourceId: string?
+}[]?
diff --git a/avm/res/communication/email-service/domain/main.json b/avm/res/communication/email-service/domain/main.json
new file mode 100644
index 00000000000..bf95f4c8ac3
--- /dev/null
+++ b/avm/res/communication/email-service/domain/main.json
@@ -0,0 +1,387 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.26.170.59819",
+ "templateHash": "3536602426579825020"
+ },
+ "name": "Email Services Domains",
+ "description": "This module deploys an Email Service Domain",
+ "owner": "Azure/module-maintainers"
+ },
+ "definitions": {
+ "lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ }
+ },
+ "nullable": true
+ },
+ "roleAssignmentType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "roleDefinitionIdOrName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to."
+ }
+ },
+ "principalType": {
+ "type": "string",
+ "allowedValues": [
+ "Device",
+ "ForeignGroup",
+ "Group",
+ "ServicePrincipal",
+ "User"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The description of the role assignment."
+ }
+ },
+ "condition": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"."
+ }
+ },
+ "conditionVersion": {
+ "type": "string",
+ "allowedValues": [
+ "2.0"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Version of the condition."
+ }
+ },
+ "delegatedManagedIdentityResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Resource Id of the delegated managed identity resource."
+ }
+ }
+ }
+ },
+ "nullable": true
+ }
+ },
+ "parameters": {
+ "emailServiceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent email service. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 253,
+ "metadata": {
+ "description": "Required. Name of the domain to create."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "global",
+ "metadata": {
+ "description": "Optional. Location for all Resources."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Endpoint tags."
+ }
+ },
+ "domainManagement": {
+ "type": "string",
+ "defaultValue": "AzureManaged",
+ "allowedValues": [
+ "AzureManaged",
+ "CustomerManaged",
+ "CustomerManagedInExchangeOnline"
+ ],
+ "metadata": {
+ "description": "Optional. Describes how the Domain resource is being managed."
+ }
+ },
+ "userEngagementTracking": {
+ "type": "string",
+ "defaultValue": "Disabled",
+ "allowedValues": [
+ "Enabled",
+ "Disabled"
+ ],
+ "metadata": {
+ "description": "Optional. Describes whether user engagement tracking is enabled or disabled."
+ }
+ },
+ "senderUsernames": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The domains to deploy into this namespace."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "roleAssignments": {
+ "$ref": "#/definitions/roleAssignmentType",
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ }
+ },
+ "variables": {
+ "builtInRoleNames": {
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
+ "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
+ "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]",
+ "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]"
+ }
+ },
+ "resources": {
+ "emailService": {
+ "existing": true,
+ "type": "Microsoft.Communication/emailServices",
+ "apiVersion": "2023-04-01",
+ "name": "[parameters('emailServiceName')]"
+ },
+ "domain": {
+ "type": "Microsoft.Communication/emailServices/domains",
+ "apiVersion": "2023-04-01",
+ "name": "[format('{0}/{1}', parameters('emailServiceName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "domainManagement": "[parameters('domainManagement')]",
+ "userEngagementTracking": "[parameters('userEngagementTracking')]"
+ },
+ "dependsOn": [
+ "emailService"
+ ]
+ },
+ "domain_lock": {
+ "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]",
+ "type": "Microsoft.Authorization/locks",
+ "apiVersion": "2020-05-01",
+ "scope": "[format('Microsoft.Communication/emailServices/{0}/domains/{1}', parameters('emailServiceName'), parameters('name'))]",
+ "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]",
+ "properties": {
+ "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]",
+ "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]"
+ },
+ "dependsOn": [
+ "domain"
+ ]
+ },
+ "domain_roleAssignments": {
+ "copy": {
+ "name": "domain_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Communication/emailServices/{0}/domains/{1}', parameters('emailServiceName'), parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.Communication/emailServices/domains', parameters('emailServiceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]",
+ "properties": {
+ "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]",
+ "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]",
+ "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]",
+ "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]",
+ "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]",
+ "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]",
+ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]"
+ },
+ "dependsOn": [
+ "domain"
+ ]
+ },
+ "domain_senderUsernames": {
+ "copy": {
+ "name": "domain_senderUsernames",
+ "count": "[length(coalesce(parameters('senderUsernames'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-domain-senderusername-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "emailServiceName": {
+ "value": "[parameters('emailServiceName')]"
+ },
+ "domainName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('senderUsernames'), createArray())[copyIndex()].name]"
+ },
+ "username": {
+ "value": "[coalesce(parameters('senderUsernames'), createArray())[copyIndex()].username]"
+ },
+ "displayName": {
+ "value": "[tryGet(coalesce(parameters('senderUsernames'), createArray())[copyIndex()], 'displayName')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.26.170.59819",
+ "templateHash": "11776160114645029549"
+ },
+ "name": "Sender Usernames",
+ "description": "This module deploys an Sender",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "emailServiceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent email service. Required if the template is used in a standalone deployment."
+ }
+ },
+ "domainName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent domain. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the sender username resource to create."
+ }
+ },
+ "username": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. A sender username to be used when sending emails."
+ }
+ },
+ "displayName": {
+ "type": "string",
+ "defaultValue": "[parameters('username')]",
+ "metadata": {
+ "description": "Optional. The display name for the senderUsername."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Communication/emailServices/domains/senderUsernames",
+ "apiVersion": "2023-04-01",
+ "name": "[format('{0}/{1}/{2}', parameters('emailServiceName'), parameters('domainName'), parameters('name'))]",
+ "properties": {
+ "username": "[parameters('username')]",
+ "displayName": "[parameters('displayName')]"
+ }
+ }
+ ],
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the sender username."
+ },
+ "value": "[resourceId('Microsoft.Communication/emailServices/domains/senderUsernames', parameters('emailServiceName'), parameters('domainName'), parameters('name'))]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the sender username."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the sender username was created in."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "domain",
+ "emailService"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the domain."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the domain."
+ },
+ "value": "[resourceId('Microsoft.Communication/emailServices/domains', parameters('emailServiceName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the domain was created in."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/communication/email-service/domain/sender-username/README.md b/avm/res/communication/email-service/domain/sender-username/README.md
new file mode 100644
index 00000000000..4ddec4e68bd
--- /dev/null
+++ b/avm/res/communication/email-service/domain/sender-username/README.md
@@ -0,0 +1,92 @@
+# Sender Usernames `[Microsoft.Communication/emailServices/domains/senderUsernames]`
+
+This module deploys an Sender
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+- [Data Collection](#Data-Collection)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+| `Microsoft.Communication/emailServices/domains/senderUsernames` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Communication/emailServices/domains/senderUsernames) |
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | Name of the sender username resource to create. |
+| [`username`](#parameter-username) | string | A sender username to be used when sending emails. |
+
+**Conditional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`domainName`](#parameter-domainname) | string | The name of the parent domain. Required if the template is used in a standalone deployment. |
+| [`emailServiceName`](#parameter-emailservicename) | string | The name of the parent email service. Required if the template is used in a standalone deployment. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`displayName`](#parameter-displayname) | string | The display name for the senderUsername. |
+
+### Parameter: `name`
+
+Name of the sender username resource to create.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `username`
+
+A sender username to be used when sending emails.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `domainName`
+
+The name of the parent domain. Required if the template is used in a standalone deployment.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `emailServiceName`
+
+The name of the parent email service. Required if the template is used in a standalone deployment.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `displayName`
+
+The display name for the senderUsername.
+
+- Required: No
+- Type: string
+- Default: `[parameters('username')]`
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `name` | string | The name of the sender username. |
+| `resourceGroupName` | string | The name of the resource group the sender username was created in. |
+| `resourceId` | string | The resource ID of the sender username. |
+
+## Cross-referenced modules
+
+_None_
+
+## Data Collection
+
+The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.
diff --git a/avm/res/communication/email-service/domain/sender-username/main.bicep b/avm/res/communication/email-service/domain/sender-username/main.bicep
new file mode 100644
index 00000000000..37c9c82562b
--- /dev/null
+++ b/avm/res/communication/email-service/domain/sender-username/main.bicep
@@ -0,0 +1,52 @@
+metadata name = 'Sender Usernames'
+metadata description = 'This module deploys an Sender'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Conditional. The name of the parent email service. Required if the template is used in a standalone deployment.')
+param emailServiceName string
+
+@description('Conditional. The name of the parent domain. Required if the template is used in a standalone deployment.')
+param domainName string
+
+@description('Required. Name of the sender username resource to create.')
+param name string
+
+@description('Required. A sender username to be used when sending emails.')
+param username string
+
+@description('Optional. The display name for the senderUsername.')
+param displayName string = username
+
+// ============== //
+// Resources //
+// ============== //
+
+resource emailService 'Microsoft.Communication/emailServices@2023-04-01' existing = {
+ name: emailServiceName
+
+ resource domain 'domains@2023-04-01' existing = {
+ name: domainName
+ }
+}
+
+resource senderUsername 'Microsoft.Communication/emailServices/domains/senderUsernames@2023-04-01' = {
+ name: name
+ parent: emailService::domain
+ properties: {
+ username: username
+ displayName: displayName
+ }
+}
+
+// ============ //
+// Outputs //
+// ============ //
+
+@description('The resource ID of the sender username.')
+output resourceId string = senderUsername.id
+
+@description('The name of the sender username.')
+output name string = senderUsername.name
+
+@description('The name of the resource group the sender username was created in.')
+output resourceGroupName string = resourceGroup().name
diff --git a/avm/res/communication/email-service/domain/sender-username/main.json b/avm/res/communication/email-service/domain/sender-username/main.json
new file mode 100644
index 00000000000..48abd0ade62
--- /dev/null
+++ b/avm/res/communication/email-service/domain/sender-username/main.json
@@ -0,0 +1,81 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.26.170.59819",
+ "templateHash": "11776160114645029549"
+ },
+ "name": "Sender Usernames",
+ "description": "This module deploys an Sender",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "emailServiceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent email service. Required if the template is used in a standalone deployment."
+ }
+ },
+ "domainName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent domain. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the sender username resource to create."
+ }
+ },
+ "username": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. A sender username to be used when sending emails."
+ }
+ },
+ "displayName": {
+ "type": "string",
+ "defaultValue": "[parameters('username')]",
+ "metadata": {
+ "description": "Optional. The display name for the senderUsername."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Communication/emailServices/domains/senderUsernames",
+ "apiVersion": "2023-04-01",
+ "name": "[format('{0}/{1}/{2}', parameters('emailServiceName'), parameters('domainName'), parameters('name'))]",
+ "properties": {
+ "username": "[parameters('username')]",
+ "displayName": "[parameters('displayName')]"
+ }
+ }
+ ],
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the sender username."
+ },
+ "value": "[resourceId('Microsoft.Communication/emailServices/domains/senderUsernames', parameters('emailServiceName'), parameters('domainName'), parameters('name'))]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the sender username."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the sender username was created in."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/communication/email-service/main.bicep b/avm/res/communication/email-service/main.bicep
new file mode 100644
index 00000000000..15492649fd2
--- /dev/null
+++ b/avm/res/communication/email-service/main.bicep
@@ -0,0 +1,179 @@
+metadata name = 'Email Services'
+metadata description = 'This module deploys an Email Service'
+metadata owner = 'Azure/module-maintainers'
+
+@minLength(1)
+@maxLength(63)
+@description('Required. Name of the email service to create.')
+param name string
+
+@description('Optional. Location for all Resources.')
+param location string = 'global'
+
+@description('Optional. Enable/Disable usage telemetry for module.')
+param enableTelemetry bool = true
+
+@description('Optional. Endpoint tags.')
+param tags object?
+
+@description('Optional. The lock settings of the service.')
+param lock lockType
+
+@description('Optional. Array of role assignments to create.')
+param roleAssignments roleAssignmentType
+
+@description('Required. The location where the communication service stores its data at rest.')
+param dataLocation string
+
+@description('Optional. The domains to deploy into this namespace.')
+param domains array?
+
+var builtInRoleNames = {
+ Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
+ Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')
+ Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')
+ 'Role Based Access Control Administrator (Preview)': subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ 'f58310d9-a9f6-439a-9e8d-f62e7b41a168'
+ )
+ 'User Access Administrator': subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9'
+ )
+}
+
+// ============== //
+// Resources //
+// ============== //
+
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: '46d3xbcp.res.communication-emailservice.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
+ properties: {
+ mode: 'Incremental'
+ template: {
+ '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
+ contentVersion: '1.0.0.0'
+ resources: []
+ outputs: {
+ telemetry: {
+ type: 'String'
+ value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
+ }
+ }
+ }
+ }
+}
+
+resource email 'Microsoft.Communication/emailServices@2023-04-01' = {
+ name: name
+ location: location
+ tags: tags
+ properties: {
+ dataLocation: dataLocation
+ }
+}
+
+module email_domains 'domain/main.bicep' = [
+ for (domain, index) in (domains ?? []): {
+ name: '${uniqueString(deployment().name, location)}-email-domain-${index}'
+ params: {
+ emailServiceName: email.name
+ name: domain.name
+ location: location
+ domainManagement: domain.?domainManagement
+ userEngagementTracking: domain.?userEngagementTracking
+ senderUsernames: domain.?senderUsernames
+ roleAssignments: domain.?roleAssignments
+ lock: domain.?lock ?? lock
+ tags: domain.?tags ?? tags
+ }
+ }
+]
+
+resource email_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
+ name: lock.?name ?? 'lock-${name}'
+ properties: {
+ level: lock.?kind ?? ''
+ notes: lock.?kind == 'CanNotDelete'
+ ? 'Cannot delete resource or child resources.'
+ : 'Cannot delete or modify the resource or child resources.'
+ }
+ scope: email
+}
+
+resource email_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [
+ for (roleAssignment, index) in (roleAssignments ?? []): {
+ name: guid(email.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName)
+ properties: {
+ roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName)
+ ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName]
+ : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/')
+ ? roleAssignment.roleDefinitionIdOrName
+ : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)
+ principalId: roleAssignment.principalId
+ description: roleAssignment.?description
+ principalType: roleAssignment.?principalType
+ condition: roleAssignment.?condition
+ conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set
+ delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId
+ }
+ scope: email
+ }
+]
+
+// ============ //
+// Outputs //
+// ============ //
+
+@description('The name of the email service.')
+output name string = email.name
+
+@description('The resource ID of the email service.')
+output resourceId string = email.id
+
+@description('The resource group the email service was deployed into.')
+output resourceGroupName string = resourceGroup().name
+
+@description('The location the email service was deployed into.')
+output location string = email.location
+
+@description('The list of the email domain resource ids.')
+output domainResourceIds array = [for (domain, index) in (domains ?? []): email_domains[index].outputs.resourceId]
+
+@description('The list of the email domain names.')
+output domainNamess array = [for (domain, index) in (domains ?? []): email_domains[index].outputs.name]
+
+// ================ //
+// Definitions //
+// ================ //
+
+type lockType = {
+ @description('Optional. Specify the name of lock.')
+ name: string?
+
+ @description('Optional. Specify the type of lock.')
+ kind: ('CanNotDelete' | 'ReadOnly' | 'None')?
+}?
+
+type roleAssignmentType = {
+ @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.')
+ roleDefinitionIdOrName: string
+
+ @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.')
+ principalId: string
+
+ @description('Optional. The principal type of the assigned principal ID.')
+ principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')?
+
+ @description('Optional. The description of the role assignment.')
+ description: string?
+
+ @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".')
+ condition: string?
+
+ @description('Optional. Version of the condition.')
+ conditionVersion: '2.0'?
+
+ @description('Optional. The Resource Id of the delegated managed identity resource.')
+ delegatedManagedIdentityResourceId: string?
+}[]?
diff --git a/avm/res/communication/email-service/main.json b/avm/res/communication/email-service/main.json
new file mode 100644
index 00000000000..815b3a746ad
--- /dev/null
+++ b/avm/res/communication/email-service/main.json
@@ -0,0 +1,725 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.26.170.59819",
+ "templateHash": "17252916584930209182"
+ },
+ "name": "Email Services",
+ "description": "This module deploys an Email Service",
+ "owner": "Azure/module-maintainers"
+ },
+ "definitions": {
+ "lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ }
+ },
+ "nullable": true
+ },
+ "roleAssignmentType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "roleDefinitionIdOrName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to."
+ }
+ },
+ "principalType": {
+ "type": "string",
+ "allowedValues": [
+ "Device",
+ "ForeignGroup",
+ "Group",
+ "ServicePrincipal",
+ "User"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The description of the role assignment."
+ }
+ },
+ "condition": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"."
+ }
+ },
+ "conditionVersion": {
+ "type": "string",
+ "allowedValues": [
+ "2.0"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Version of the condition."
+ }
+ },
+ "delegatedManagedIdentityResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Resource Id of the delegated managed identity resource."
+ }
+ }
+ }
+ },
+ "nullable": true
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 63,
+ "metadata": {
+ "description": "Required. Name of the email service to create."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "global",
+ "metadata": {
+ "description": "Optional. Location for all Resources."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Endpoint tags."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "roleAssignments": {
+ "$ref": "#/definitions/roleAssignmentType",
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "dataLocation": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The location where the communication service stores its data at rest."
+ }
+ },
+ "domains": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The domains to deploy into this namespace."
+ }
+ }
+ },
+ "variables": {
+ "builtInRoleNames": {
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
+ "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
+ "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]",
+ "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]"
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2023-07-01",
+ "name": "[format('46d3xbcp.res.communication-emailservice.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]",
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "resources": [],
+ "outputs": {
+ "telemetry": {
+ "type": "String",
+ "value": "For more information, see https://aka.ms/avm/TelemetryInfo"
+ }
+ }
+ }
+ }
+ },
+ "email": {
+ "type": "Microsoft.Communication/emailServices",
+ "apiVersion": "2023-04-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "dataLocation": "[parameters('dataLocation')]"
+ }
+ },
+ "email_lock": {
+ "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]",
+ "type": "Microsoft.Authorization/locks",
+ "apiVersion": "2020-05-01",
+ "scope": "[format('Microsoft.Communication/emailServices/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]",
+ "properties": {
+ "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]",
+ "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]"
+ },
+ "dependsOn": [
+ "email"
+ ]
+ },
+ "email_roleAssignments": {
+ "copy": {
+ "name": "email_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Communication/emailServices/{0}', parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.Communication/emailServices', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]",
+ "properties": {
+ "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]",
+ "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]",
+ "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]",
+ "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]",
+ "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]",
+ "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]",
+ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]"
+ },
+ "dependsOn": [
+ "email"
+ ]
+ },
+ "email_domains": {
+ "copy": {
+ "name": "email_domains",
+ "count": "[length(coalesce(parameters('domains'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-email-domain-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "emailServiceName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('domains'), createArray())[copyIndex()].name]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "domainManagement": {
+ "value": "[tryGet(coalesce(parameters('domains'), createArray())[copyIndex()], 'domainManagement')]"
+ },
+ "userEngagementTracking": {
+ "value": "[tryGet(coalesce(parameters('domains'), createArray())[copyIndex()], 'userEngagementTracking')]"
+ },
+ "senderUsernames": {
+ "value": "[tryGet(coalesce(parameters('domains'), createArray())[copyIndex()], 'senderUsernames')]"
+ },
+ "roleAssignments": {
+ "value": "[tryGet(coalesce(parameters('domains'), createArray())[copyIndex()], 'roleAssignments')]"
+ },
+ "lock": {
+ "value": "[coalesce(tryGet(coalesce(parameters('domains'), createArray())[copyIndex()], 'lock'), parameters('lock'))]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(coalesce(parameters('domains'), createArray())[copyIndex()], 'tags'), parameters('tags'))]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.26.170.59819",
+ "templateHash": "3536602426579825020"
+ },
+ "name": "Email Services Domains",
+ "description": "This module deploys an Email Service Domain",
+ "owner": "Azure/module-maintainers"
+ },
+ "definitions": {
+ "lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ }
+ },
+ "nullable": true
+ },
+ "roleAssignmentType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "roleDefinitionIdOrName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to."
+ }
+ },
+ "principalType": {
+ "type": "string",
+ "allowedValues": [
+ "Device",
+ "ForeignGroup",
+ "Group",
+ "ServicePrincipal",
+ "User"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The description of the role assignment."
+ }
+ },
+ "condition": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"."
+ }
+ },
+ "conditionVersion": {
+ "type": "string",
+ "allowedValues": [
+ "2.0"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Version of the condition."
+ }
+ },
+ "delegatedManagedIdentityResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Resource Id of the delegated managed identity resource."
+ }
+ }
+ }
+ },
+ "nullable": true
+ }
+ },
+ "parameters": {
+ "emailServiceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent email service. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 253,
+ "metadata": {
+ "description": "Required. Name of the domain to create."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "global",
+ "metadata": {
+ "description": "Optional. Location for all Resources."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Endpoint tags."
+ }
+ },
+ "domainManagement": {
+ "type": "string",
+ "defaultValue": "AzureManaged",
+ "allowedValues": [
+ "AzureManaged",
+ "CustomerManaged",
+ "CustomerManagedInExchangeOnline"
+ ],
+ "metadata": {
+ "description": "Optional. Describes how the Domain resource is being managed."
+ }
+ },
+ "userEngagementTracking": {
+ "type": "string",
+ "defaultValue": "Disabled",
+ "allowedValues": [
+ "Enabled",
+ "Disabled"
+ ],
+ "metadata": {
+ "description": "Optional. Describes whether user engagement tracking is enabled or disabled."
+ }
+ },
+ "senderUsernames": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The domains to deploy into this namespace."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "roleAssignments": {
+ "$ref": "#/definitions/roleAssignmentType",
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ }
+ },
+ "variables": {
+ "builtInRoleNames": {
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
+ "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
+ "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]",
+ "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]"
+ }
+ },
+ "resources": {
+ "emailService": {
+ "existing": true,
+ "type": "Microsoft.Communication/emailServices",
+ "apiVersion": "2023-04-01",
+ "name": "[parameters('emailServiceName')]"
+ },
+ "domain": {
+ "type": "Microsoft.Communication/emailServices/domains",
+ "apiVersion": "2023-04-01",
+ "name": "[format('{0}/{1}', parameters('emailServiceName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "domainManagement": "[parameters('domainManagement')]",
+ "userEngagementTracking": "[parameters('userEngagementTracking')]"
+ },
+ "dependsOn": [
+ "emailService"
+ ]
+ },
+ "domain_lock": {
+ "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]",
+ "type": "Microsoft.Authorization/locks",
+ "apiVersion": "2020-05-01",
+ "scope": "[format('Microsoft.Communication/emailServices/{0}/domains/{1}', parameters('emailServiceName'), parameters('name'))]",
+ "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]",
+ "properties": {
+ "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]",
+ "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]"
+ },
+ "dependsOn": [
+ "domain"
+ ]
+ },
+ "domain_roleAssignments": {
+ "copy": {
+ "name": "domain_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Communication/emailServices/{0}/domains/{1}', parameters('emailServiceName'), parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.Communication/emailServices/domains', parameters('emailServiceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]",
+ "properties": {
+ "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]",
+ "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]",
+ "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]",
+ "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]",
+ "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]",
+ "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]",
+ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]"
+ },
+ "dependsOn": [
+ "domain"
+ ]
+ },
+ "domain_senderUsernames": {
+ "copy": {
+ "name": "domain_senderUsernames",
+ "count": "[length(coalesce(parameters('senderUsernames'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-domain-senderusername-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "emailServiceName": {
+ "value": "[parameters('emailServiceName')]"
+ },
+ "domainName": {
+ "value": "[parameters('name')]"
+ },
+ "name": {
+ "value": "[coalesce(parameters('senderUsernames'), createArray())[copyIndex()].name]"
+ },
+ "username": {
+ "value": "[coalesce(parameters('senderUsernames'), createArray())[copyIndex()].username]"
+ },
+ "displayName": {
+ "value": "[tryGet(coalesce(parameters('senderUsernames'), createArray())[copyIndex()], 'displayName')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.26.170.59819",
+ "templateHash": "11776160114645029549"
+ },
+ "name": "Sender Usernames",
+ "description": "This module deploys an Sender",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "emailServiceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent email service. Required if the template is used in a standalone deployment."
+ }
+ },
+ "domainName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent domain. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the sender username resource to create."
+ }
+ },
+ "username": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. A sender username to be used when sending emails."
+ }
+ },
+ "displayName": {
+ "type": "string",
+ "defaultValue": "[parameters('username')]",
+ "metadata": {
+ "description": "Optional. The display name for the senderUsername."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Communication/emailServices/domains/senderUsernames",
+ "apiVersion": "2023-04-01",
+ "name": "[format('{0}/{1}/{2}', parameters('emailServiceName'), parameters('domainName'), parameters('name'))]",
+ "properties": {
+ "username": "[parameters('username')]",
+ "displayName": "[parameters('displayName')]"
+ }
+ }
+ ],
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the sender username."
+ },
+ "value": "[resourceId('Microsoft.Communication/emailServices/domains/senderUsernames', parameters('emailServiceName'), parameters('domainName'), parameters('name'))]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the sender username."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the sender username was created in."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "domain",
+ "emailService"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the domain."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the domain."
+ },
+ "value": "[resourceId('Microsoft.Communication/emailServices/domains', parameters('emailServiceName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the domain was created in."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "email"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the email service."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the email service."
+ },
+ "value": "[resourceId('Microsoft.Communication/emailServices', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the email service was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the email service was deployed into."
+ },
+ "value": "[reference('email', '2023-04-01', 'full').location]"
+ },
+ "domainResourceIds": {
+ "type": "array",
+ "metadata": {
+ "description": "The list of the email domain resource ids."
+ },
+ "copy": {
+ "count": "[length(coalesce(parameters('domains'), createArray()))]",
+ "input": "[reference(format('email_domains[{0}]', copyIndex())).outputs.resourceId.value]"
+ }
+ },
+ "domainNamess": {
+ "type": "array",
+ "metadata": {
+ "description": "The list of the email domain names."
+ },
+ "copy": {
+ "count": "[length(coalesce(parameters('domains'), createArray()))]",
+ "input": "[reference(format('email_domains[{0}]', copyIndex())).outputs.name.value]"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/communication/email-service/tests/e2e/defaults/main.test.bicep b/avm/res/communication/email-service/tests/e2e/defaults/main.test.bicep
new file mode 100644
index 00000000000..52182833301
--- /dev/null
+++ b/avm/res/communication/email-service/tests/e2e/defaults/main.test.bicep
@@ -0,0 +1,49 @@
+targetScope = 'subscription'
+
+metadata name = 'Using only defaults'
+metadata description = 'This instance deploys the module with the minimum set of required parameters.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-communication.emailservice-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'cesmin'
+
+@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.')
+param namePrefix string = '#_namePrefix_#'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ location: 'global'
+ dataLocation: 'Europe'
+ }
+ }
+]
diff --git a/avm/res/communication/email-service/tests/e2e/max/dependencies.bicep b/avm/res/communication/email-service/tests/e2e/max/dependencies.bicep
new file mode 100644
index 00000000000..d3677704327
--- /dev/null
+++ b/avm/res/communication/email-service/tests/e2e/max/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/avm/res/communication/email-service/tests/e2e/max/main.test.bicep b/avm/res/communication/email-service/tests/e2e/max/main.test.bicep
new file mode 100644
index 00000000000..b0b7e59d7f7
--- /dev/null
+++ b/avm/res/communication/email-service/tests/e2e/max/main.test.bicep
@@ -0,0 +1,133 @@
+targetScope = 'subscription'
+
+metadata name = 'Using large parameter set'
+metadata description = 'This instance deploys the module with most of its features enabled.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-communication.emailservice-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'cesmax'
+
+@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.')
+param namePrefix string = '#_namePrefix_#'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ location: resourceLocation
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ location: 'global'
+ dataLocation: 'United States'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Owner'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ {
+ roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ {
+ roleDefinitionIdOrName: subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ 'acdd72a7-3385-48ef-bd42-f606fba81ae7'
+ )
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ domains: [
+ {
+ name: 'AzureManagedDomain'
+ tags: {
+ Role: 'DeploymentValidation'
+ }
+ domainManagement: 'AzureManaged'
+ userEngagementTracking: 'Enabled'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Owner'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ {
+ roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ {
+ roleDefinitionIdOrName: subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ 'acdd72a7-3385-48ef-bd42-f606fba81ae7'
+ )
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ senderUsernames: [
+ {
+ name: 'donotreply'
+ userName: 'DoNotReply'
+ displayName: 'Do Not Reply'
+ }
+ {
+ name: 'customerservice'
+ userName: 'CustomerService'
+ displayName: 'Customer Service'
+ }
+ ]
+ }
+ ]
+ }
+ }
+]
diff --git a/avm/res/communication/email-service/tests/e2e/waf-aligned/main.test.bicep b/avm/res/communication/email-service/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 00000000000..0b064f161f3
--- /dev/null
+++ b/avm/res/communication/email-service/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,54 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-communication.emailservice-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ceswaf'
+
+@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.')
+param namePrefix string = '#_namePrefix_#'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ location: 'global'
+ dataLocation: 'Germany'
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ }
+]
diff --git a/avm/res/communication/email-service/version.json b/avm/res/communication/email-service/version.json
new file mode 100644
index 00000000000..8def869edeb
--- /dev/null
+++ b/avm/res/communication/email-service/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
+ "version": "0.1",
+ "pathFilters": [
+ "./main.json"
+ ]
+}
From d0204550c1837d3adecbcdd61845ea68ef138729 Mon Sep 17 00:00:00 2001
From: Kris Baranek
Date: Tue, 14 May 2024 08:58:02 +0200
Subject: [PATCH 37/52] feat: New Module
`avm/res/communication/communication-service` (#1605)
## Description
New Module `avm/res/communication/communication-service`.
Related Module Proposal:
https://github.com/Azure/Azure-Verified-Modules/issues/748
## Adding a new module
- [x] A proposal has been submitted and approved.
- [ ] I have included "Closes #{module_proposal_issue_number}" in the PR
description.
- [ ] I have run `brm validate` locally to verify the module files.
- [x] I have run deployment tests locally to ensure the module is
deployable.
## Pipeline references
| Pipeline |
| - |
|
[![avm.res.communication.communication-service](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.communication.communication-service.yml/badge.svg?branch=users%2Fkrbar%2FcommSvcModule)](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.communication.communication-service.yml)
|
---
.github/CODEOWNERS | 1 +
.github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 +
...es.communication.communication-service.yml | 90 +++
.../communication-service/README.md | 714 ++++++++++++++++++
.../communication-service/main.bicep | 263 +++++++
.../communication-service/main.json | 481 ++++++++++++
.../tests/e2e/defaults/main.test.bicep | 49 ++
.../tests/e2e/max/dependencies.bicep | 38 +
.../tests/e2e/max/main.test.bicep | 125 +++
.../tests/e2e/waf-aligned/main.test.bicep | 74 ++
.../communication-service/version.json | 7 +
11 files changed, 1843 insertions(+)
create mode 100644 .github/workflows/avm.res.communication.communication-service.yml
create mode 100644 avm/res/communication/communication-service/README.md
create mode 100644 avm/res/communication/communication-service/main.bicep
create mode 100644 avm/res/communication/communication-service/main.json
create mode 100644 avm/res/communication/communication-service/tests/e2e/defaults/main.test.bicep
create mode 100644 avm/res/communication/communication-service/tests/e2e/max/dependencies.bicep
create mode 100644 avm/res/communication/communication-service/tests/e2e/max/main.test.bicep
create mode 100644 avm/res/communication/communication-service/tests/e2e/waf-aligned/main.test.bicep
create mode 100644 avm/res/communication/communication-service/version.json
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 217d9d8f8cc..730d31af409 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -24,6 +24,7 @@
/avm/res/cache/redis/ @Azure/avm-res-cache-redis-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/cdn/profile/ @Azure/avm-res-cdn-profile-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/cognitive-services/account/ @Azure/avm-res-cognitiveservices-account-module-owners-bicep @Azure/avm-core-team-technical-bicep
+/avm/res/communication/communication-service/ @Azure/avm-res-communication-communicationservice-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/communication/email-service/ @Azure/avm-res-communication-emailservice-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/compute/availability-set/ @Azure/avm-res-compute-availabilityset-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/compute/disk/ @Azure/avm-res-compute-disk-module-owners-bicep @Azure/avm-core-team-technical-bicep
diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml
index 9765814bacb..43e4d477db4 100644
--- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml
+++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml
@@ -58,6 +58,7 @@ body:
- "avm/res/cache/redis"
- "avm/res/cdn/profile"
- "avm/res/cognitive-services/account"
+ - "avm/res/communication/communication-service"
- "avm/res/communication/email-service"
- "avm/res/compute/availability-set"
- "avm/res/compute/disk"
diff --git a/.github/workflows/avm.res.communication.communication-service.yml b/.github/workflows/avm.res.communication.communication-service.yml
new file mode 100644
index 00000000000..0679579e9ab
--- /dev/null
+++ b/.github/workflows/avm.res.communication.communication-service.yml
@@ -0,0 +1,90 @@
+name: "avm.res.communication.communication-service"
+
+on:
+ schedule:
+ - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month)
+ workflow_dispatch:
+ inputs:
+ staticValidation:
+ type: boolean
+ description: "Execute static validation"
+ required: false
+ default: true
+ deploymentValidation:
+ type: boolean
+ description: "Execute deployment validation"
+ required: false
+ default: true
+ removeDeployment:
+ type: boolean
+ description: "Remove deployed module"
+ required: false
+ default: true
+ customLocation:
+ type: string
+ description: "Default location overwrite (e.g., eastus)"
+ required: false
+ push:
+ branches:
+ - main
+ paths:
+ - ".github/actions/templates/avm-**"
+ - ".github/workflows/avm.template.module.yml"
+ - ".github/workflows/avm.res.communication.communication-service.yml"
+ - "avm/res/communication/communication-service/**"
+ - "avm/utilities/pipelines/**"
+ - "!avm/utilities/pipelines/platform/**"
+ - "!*/**/README.md"
+
+env:
+ modulePath: "avm/res/communication/communication-service"
+ workflowPath: ".github/workflows/avm.res.communication.communication-service.yml"
+
+concurrency:
+ group: ${{ github.workflow }}
+
+jobs:
+ ###########################
+ # Initialize pipeline #
+ ###########################
+ job_initialize_pipeline:
+ runs-on: ubuntu-latest
+ name: "Initialize pipeline"
+ steps:
+ - name: "Checkout"
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: "Set input parameters to output variables"
+ id: get-workflow-param
+ uses: ./.github/actions/templates/avm-getWorkflowInput
+ with:
+ workflowPath: "${{ env.workflowPath}}"
+ - name: "Get module test file paths"
+ id: get-module-test-file-paths
+ uses: ./.github/actions/templates/avm-getModuleTestFiles
+ with:
+ modulePath: "${{ env.modulePath }}"
+ outputs:
+ workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }}
+ moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }}
+ psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }}
+ modulePath: "${{ env.modulePath }}"
+
+ ##############################
+ # Call reusable workflow #
+ ##############################
+ call-workflow-passing-data:
+ name: "Run"
+ permissions:
+ id-token: write # For OIDC
+ contents: write # For release tags
+ needs:
+ - job_initialize_pipeline
+ uses: ./.github/workflows/avm.template.module.yml
+ with:
+ workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}"
+ moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}"
+ psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}"
+ modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}"
+ secrets: inherit
diff --git a/avm/res/communication/communication-service/README.md b/avm/res/communication/communication-service/README.md
new file mode 100644
index 00000000000..5370ac81fb5
--- /dev/null
+++ b/avm/res/communication/communication-service/README.md
@@ -0,0 +1,714 @@
+# Communication Services `[Microsoft.Communication/communicationServices]`
+
+This module deploys a Communication Service
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Usage examples](#Usage-examples)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+- [Data Collection](#Data-Collection)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) |
+| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) |
+| `Microsoft.Communication/communicationServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Communication/communicationServices) |
+| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) |
+
+## Usage examples
+
+The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository.
+
+>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order.
+
+>**Note**: To reference the module, please use the following syntax `br/public:avm/res/communication/communication-service:`.
+
+- [Using only defaults](#example-1-using-only-defaults)
+- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
+
+### Example 1: _Using only defaults_
+
+This instance deploys the module with the minimum set of required parameters.
+
+
+
+
+via Bicep module
+
+```bicep
+module communicationService 'br/public:avm/res/communication/communication-service:' = {
+ name: 'communicationServiceDeployment'
+ params: {
+ // Required parameters
+ dataLocation: 'Germany'
+ name: 'ccsmin001'
+ // Non-required parameters
+ location: 'global'
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "dataLocation": {
+ "value": "Germany"
+ },
+ "name": {
+ "value": "ccsmin001"
+ },
+ // Non-required parameters
+ "location": {
+ "value": "global"
+ }
+ }
+}
+```
+
+
+
+
+### Example 2: _Using large parameter set_
+
+This instance deploys the module with most of its features enabled.
+
+
+
+
+via Bicep module
+
+```bicep
+module communicationService 'br/public:avm/res/communication/communication-service:' = {
+ name: 'communicationServiceDeployment'
+ params: {
+ // Required parameters
+ dataLocation: 'Germany'
+ name: 'ccsmax001'
+ // Non-required parameters
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: ''
+ eventHubName: ''
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ name: 'customSetting'
+ storageAccountResourceId: ''
+ workspaceResourceId: ''
+ }
+ ]
+ linkedDomains: [
+ ''
+ ]
+ location: 'global'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ managedIdentities: {
+ systemAssigned: false
+ userAssignedResourceIds: [
+ ''
+ ]
+ }
+ roleAssignments: [
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: 'Owner'
+ }
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
+ }
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: ''
+ }
+ ]
+ tags: {
+ Environment: 'Non-Prod'
+ 'hidden-title': 'This is visible in the resource name'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "dataLocation": {
+ "value": "Germany"
+ },
+ "name": {
+ "value": "ccsmax001"
+ },
+ // Non-required parameters
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "",
+ "eventHubName": "",
+ "metricCategories": [
+ {
+ "category": "AllMetrics"
+ }
+ ],
+ "name": "customSetting",
+ "storageAccountResourceId": "",
+ "workspaceResourceId": ""
+ }
+ ]
+ },
+ "linkedDomains": {
+ "value": [
+ ""
+ ]
+ },
+ "location": {
+ "value": "global"
+ },
+ "lock": {
+ "value": {
+ "kind": "CanNotDelete",
+ "name": "myCustomLockName"
+ }
+ },
+ "managedIdentities": {
+ "value": {
+ "systemAssigned": false,
+ "userAssignedResourceIds": [
+ ""
+ ]
+ }
+ },
+ "roleAssignments": {
+ "value": [
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": "Owner"
+ },
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c"
+ },
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": ""
+ }
+ ]
+ },
+ "tags": {
+ "value": {
+ "Environment": "Non-Prod",
+ "hidden-title": "This is visible in the resource name",
+ "Role": "DeploymentValidation"
+ }
+ }
+ }
+}
+```
+
+
+
+
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+via Bicep module
+
+```bicep
+module communicationService 'br/public:avm/res/communication/communication-service:' = {
+ name: 'communicationServiceDeployment'
+ params: {
+ // Required parameters
+ dataLocation: 'Germany'
+ name: 'ccswaf001'
+ // Non-required parameters
+ diagnosticSettings: [
+ {
+ eventHubAuthorizationRuleResourceId: ''
+ eventHubName: ''
+ storageAccountResourceId: ''
+ workspaceResourceId: ''
+ }
+ ]
+ location: 'global'
+ tags: {
+ Environment: 'Non-Prod'
+ 'hidden-title': 'This is visible in the resource name'
+ Role: 'DeploymentValidation'
+ }
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "dataLocation": {
+ "value": "Germany"
+ },
+ "name": {
+ "value": "ccswaf001"
+ },
+ // Non-required parameters
+ "diagnosticSettings": {
+ "value": [
+ {
+ "eventHubAuthorizationRuleResourceId": "",
+ "eventHubName": "",
+ "storageAccountResourceId": "",
+ "workspaceResourceId": ""
+ }
+ ]
+ },
+ "location": {
+ "value": "global"
+ },
+ "tags": {
+ "value": {
+ "Environment": "Non-Prod",
+ "hidden-title": "This is visible in the resource name",
+ "Role": "DeploymentValidation"
+ }
+ }
+ }
+}
+```
+
+
+
+
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`dataLocation`](#parameter-datalocation) | string | The location where the communication service stores its data at rest. |
+| [`name`](#parameter-name) | string | Name of the communication service to create. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. |
+| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. |
+| [`linkedDomains`](#parameter-linkeddomains) | array | List of email Domain resource Ids. |
+| [`location`](#parameter-location) | string | Location for all Resources. |
+| [`lock`](#parameter-lock) | object | The lock settings of the service. |
+| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. |
+| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. |
+| [`tags`](#parameter-tags) | object | Resource tags. |
+
+### Parameter: `dataLocation`
+
+The location where the communication service stores its data at rest.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `name`
+
+Name of the communication service to create.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `diagnosticSettings`
+
+The diagnostic settings of the service.
+
+- Required: No
+- Type: array
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. |
+| [`eventHubName`](#parameter-diagnosticsettingseventhubname) | string | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. |
+| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. |
+| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. |
+| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. |
+| [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. |
+| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. |
+| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. |
+| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. |
+
+### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId`
+
+Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.
+
+- Required: No
+- Type: string
+
+### Parameter: `diagnosticSettings.eventHubName`
+
+Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.
+
+- Required: No
+- Type: string
+
+### Parameter: `diagnosticSettings.logAnalyticsDestinationType`
+
+A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'AzureDiagnostics'
+ 'Dedicated'
+ ]
+ ```
+
+### Parameter: `diagnosticSettings.logCategoriesAndGroups`
+
+The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection.
+
+- Required: No
+- Type: array
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. |
+| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. |
+| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. |
+
+### Parameter: `diagnosticSettings.logCategoriesAndGroups.category`
+
+Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.
+
+- Required: No
+- Type: string
+
+### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup`
+
+Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs.
+
+- Required: No
+- Type: string
+
+### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled`
+
+Enable or disable the category explicitly. Default is `true`.
+
+- Required: No
+- Type: bool
+
+### Parameter: `diagnosticSettings.marketplacePartnerResourceId`
+
+The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.
+
+- Required: No
+- Type: string
+
+### Parameter: `diagnosticSettings.metricCategories`
+
+The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection.
+
+- Required: No
+- Type: array
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`category`](#parameter-diagnosticsettingsmetriccategoriescategory) | string | Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`enabled`](#parameter-diagnosticsettingsmetriccategoriesenabled) | bool | Enable or disable the category explicitly. Default is `true`. |
+
+### Parameter: `diagnosticSettings.metricCategories.category`
+
+Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `diagnosticSettings.metricCategories.enabled`
+
+Enable or disable the category explicitly. Default is `true`.
+
+- Required: No
+- Type: bool
+
+### Parameter: `diagnosticSettings.name`
+
+The name of diagnostic setting.
+
+- Required: No
+- Type: string
+
+### Parameter: `diagnosticSettings.storageAccountResourceId`
+
+Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.
+
+- Required: No
+- Type: string
+
+### Parameter: `diagnosticSettings.workspaceResourceId`
+
+Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.
+
+- Required: No
+- Type: string
+
+### Parameter: `enableTelemetry`
+
+Enable/Disable usage telemetry for module.
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `linkedDomains`
+
+List of email Domain resource Ids.
+
+- Required: No
+- Type: array
+
+### Parameter: `location`
+
+Location for all Resources.
+
+- Required: No
+- Type: string
+- Default: `'global'`
+
+### Parameter: `lock`
+
+The lock settings of the service.
+
+- Required: No
+- Type: object
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`kind`](#parameter-lockkind) | string | Specify the type of lock. |
+| [`name`](#parameter-lockname) | string | Specify the name of lock. |
+
+### Parameter: `lock.kind`
+
+Specify the type of lock.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'CanNotDelete'
+ 'None'
+ 'ReadOnly'
+ ]
+ ```
+
+### Parameter: `lock.name`
+
+Specify the name of lock.
+
+- Required: No
+- Type: string
+
+### Parameter: `managedIdentities`
+
+The managed identity definition for this resource.
+
+- Required: No
+- Type: object
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. |
+| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. |
+
+### Parameter: `managedIdentities.systemAssigned`
+
+Enables system assigned managed identity on the resource.
+
+- Required: No
+- Type: bool
+
+### Parameter: `managedIdentities.userAssignedResourceIds`
+
+The resource ID(s) to assign to the resource.
+
+- Required: No
+- Type: array
+
+### Parameter: `roleAssignments`
+
+Array of role assignments to create.
+
+- Required: No
+- Type: array
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. |
+| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". |
+| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. |
+| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. |
+| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. |
+| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. |
+
+### Parameter: `roleAssignments.principalId`
+
+The principal ID of the principal (user/group/identity) to assign the role to.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `roleAssignments.roleDefinitionIdOrName`
+
+The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `roleAssignments.condition`
+
+The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.conditionVersion`
+
+Version of the condition.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ '2.0'
+ ]
+ ```
+
+### Parameter: `roleAssignments.delegatedManagedIdentityResourceId`
+
+The Resource Id of the delegated managed identity resource.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.description`
+
+The description of the role assignment.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.principalType`
+
+The principal type of the assigned principal ID.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'Device'
+ 'ForeignGroup'
+ 'Group'
+ 'ServicePrincipal'
+ 'User'
+ ]
+ ```
+
+### Parameter: `tags`
+
+Resource tags.
+
+- Required: No
+- Type: object
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `location` | string | The location the communication service was deployed into. |
+| `name` | string | The name of the communication service. |
+| `resourceGroupName` | string | The resource group the communication service was deployed into. |
+| `resourceId` | string | The resource ID of the communication service. |
+| `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. |
+
+## Cross-referenced modules
+
+_None_
+
+## Data Collection
+
+The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.
diff --git a/avm/res/communication/communication-service/main.bicep b/avm/res/communication/communication-service/main.bicep
new file mode 100644
index 00000000000..dbfe4343a98
--- /dev/null
+++ b/avm/res/communication/communication-service/main.bicep
@@ -0,0 +1,263 @@
+metadata name = 'Communication Services'
+metadata description = 'This module deploys a Communication Service'
+metadata owner = 'Azure/module-maintainers'
+
+@minLength(1)
+@maxLength(63)
+@description('Required. Name of the communication service to create.')
+param name string
+
+@description('Optional. Location for all Resources.')
+param location string = 'global'
+
+@description('Optional. The managed identity definition for this resource.')
+param managedIdentities managedIdentitiesType
+
+@description('Optional. Resource tags.')
+param tags object?
+
+@description('Optional. The lock settings of the service.')
+param lock lockType
+
+@description('Optional. The diagnostic settings of the service.')
+param diagnosticSettings diagnosticSettingType
+
+@description('Optional. Array of role assignments to create.')
+param roleAssignments roleAssignmentType
+
+@description('Required. The location where the communication service stores its data at rest.')
+param dataLocation string
+
+@description('Optional. List of email Domain resource Ids.')
+param linkedDomains string[]?
+
+@description('Optional. Enable/Disable usage telemetry for module.')
+param enableTelemetry bool = true
+
+var formattedUserAssignedIdentities = reduce(
+ map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }),
+ {},
+ (cur, next) => union(cur, next)
+) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} }
+
+var identity = !empty(managedIdentities)
+ ? {
+ type: (managedIdentities.?systemAssigned ?? false)
+ ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned')
+ : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None')
+ userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null
+ }
+ : null
+
+var builtInRoleNames = {
+ Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
+ Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')
+ Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')
+ 'Role Based Access Control Administrator': subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ 'f58310d9-a9f6-439a-9e8d-f62e7b41a168'
+ )
+ 'User Access Administrator': subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9'
+ )
+}
+
+// ============== //
+// Resources //
+// ============== //
+
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: '46d3xbcp.res.communication-communicationservice.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
+ properties: {
+ mode: 'Incremental'
+ template: {
+ '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
+ contentVersion: '1.0.0.0'
+ resources: []
+ outputs: {
+ telemetry: {
+ type: 'String'
+ value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
+ }
+ }
+ }
+ }
+}
+
+resource communicationService 'Microsoft.Communication/communicationServices@2023-04-01' = {
+ name: name
+ location: location
+ identity: identity
+ tags: tags
+ properties: {
+ dataLocation: dataLocation
+ linkedDomains: linkedDomains
+ }
+}
+
+resource communicationService_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
+ name: lock.?name ?? 'lock-${name}'
+ properties: {
+ level: lock.?kind ?? ''
+ notes: lock.?kind == 'CanNotDelete'
+ ? 'Cannot delete resource or child resources.'
+ : 'Cannot delete or modify the resource or child resources.'
+ }
+ scope: communicationService
+}
+
+resource communicationService_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [
+ for (diagnosticSetting, index) in (diagnosticSettings ?? []): {
+ name: diagnosticSetting.?name ?? '${name}-diagnosticSettings'
+ properties: {
+ storageAccountId: diagnosticSetting.?storageAccountResourceId
+ workspaceId: diagnosticSetting.?workspaceResourceId
+ eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId
+ eventHubName: diagnosticSetting.?eventHubName
+ metrics: [
+ for group in (diagnosticSetting.?metricCategories ?? [{ category: 'AllMetrics' }]): {
+ category: group.category
+ enabled: group.?enabled ?? true
+ timeGrain: null
+ }
+ ]
+ logs: [
+ for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' }]): {
+ categoryGroup: group.?categoryGroup
+ category: group.?category
+ enabled: group.?enabled ?? true
+ }
+ ]
+ marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId
+ logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType
+ }
+ scope: communicationService
+ }
+]
+
+resource communicationService_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [
+ for (roleAssignment, index) in (roleAssignments ?? []): {
+ name: guid(communicationService.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName)
+ properties: {
+ roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName)
+ ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName]
+ : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/')
+ ? roleAssignment.roleDefinitionIdOrName
+ : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)
+ principalId: roleAssignment.principalId
+ description: roleAssignment.?description
+ principalType: roleAssignment.?principalType
+ condition: roleAssignment.?condition
+ conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set
+ delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId
+ }
+ scope: communicationService
+ }
+]
+
+// ============ //
+// Outputs //
+// ============ //
+
+@description('The name of the communication service.')
+output name string = communicationService.name
+
+@description('The resource ID of the communication service.')
+output resourceId string = communicationService.id
+
+@description('The resource group the communication service was deployed into.')
+output resourceGroupName string = resourceGroup().name
+
+@description('The location the communication service was deployed into.')
+output location string = communicationService.location
+
+@description('The principal ID of the system assigned identity.')
+output systemAssignedMIPrincipalId string = communicationService.?identity.?principalId ?? ''
+
+// ================ //
+// Definitions //
+// ================ //
+
+type managedIdentitiesType = {
+ @description('Optional. Enables system assigned managed identity on the resource.')
+ systemAssigned: bool?
+
+ @description('Optional. The resource ID(s) to assign to the resource.')
+ userAssignedResourceIds: string[]?
+}?
+
+type lockType = {
+ @description('Optional. Specify the name of lock.')
+ name: string?
+
+ @description('Optional. Specify the type of lock.')
+ kind: ('CanNotDelete' | 'ReadOnly' | 'None')?
+}?
+
+type roleAssignmentType = {
+ @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.')
+ roleDefinitionIdOrName: string
+
+ @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.')
+ principalId: string
+
+ @description('Optional. The principal type of the assigned principal ID.')
+ principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')?
+
+ @description('Optional. The description of the role assignment.')
+ description: string?
+
+ @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".')
+ condition: string?
+
+ @description('Optional. Version of the condition.')
+ conditionVersion: '2.0'?
+
+ @description('Optional. The Resource Id of the delegated managed identity resource.')
+ delegatedManagedIdentityResourceId: string?
+}[]?
+
+type diagnosticSettingType = {
+ @description('Optional. The name of diagnostic setting.')
+ name: string?
+
+ @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection.')
+ logCategoriesAndGroups: {
+ @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.')
+ category: string?
+
+ @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs.')
+ categoryGroup: string?
+
+ @description('Optional. Enable or disable the category explicitly. Default is `true`.')
+ enabled: bool?
+ }[]?
+
+ @description('Optional. The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection.')
+ metricCategories: {
+ @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.')
+ category: string
+
+ @description('Optional. Enable or disable the category explicitly. Default is `true`.')
+ enabled: bool?
+ }[]?
+
+ @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.')
+ logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics')?
+
+ @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.')
+ workspaceResourceId: string?
+
+ @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.')
+ storageAccountResourceId: string?
+
+ @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.')
+ eventHubAuthorizationRuleResourceId: string?
+
+ @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.')
+ eventHubName: string?
+
+ @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.')
+ marketplacePartnerResourceId: string?
+}[]?
diff --git a/avm/res/communication/communication-service/main.json b/avm/res/communication/communication-service/main.json
new file mode 100644
index 00000000000..85b0cbf040f
--- /dev/null
+++ b/avm/res/communication/communication-service/main.json
@@ -0,0 +1,481 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.26.170.59819",
+ "templateHash": "12398183463909693728"
+ },
+ "name": "Communication Services",
+ "description": "This module deploys a Communication Service",
+ "owner": "Azure/module-maintainers"
+ },
+ "definitions": {
+ "managedIdentitiesType": {
+ "type": "object",
+ "properties": {
+ "systemAssigned": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enables system assigned managed identity on the resource."
+ }
+ },
+ "userAssignedResourceIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID(s) to assign to the resource."
+ }
+ }
+ },
+ "nullable": true
+ },
+ "lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ }
+ },
+ "nullable": true
+ },
+ "roleAssignmentType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "roleDefinitionIdOrName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to."
+ }
+ },
+ "principalType": {
+ "type": "string",
+ "allowedValues": [
+ "Device",
+ "ForeignGroup",
+ "Group",
+ "ServicePrincipal",
+ "User"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The description of the role assignment."
+ }
+ },
+ "condition": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"."
+ }
+ },
+ "conditionVersion": {
+ "type": "string",
+ "allowedValues": [
+ "2.0"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Version of the condition."
+ }
+ },
+ "delegatedManagedIdentityResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Resource Id of the delegated managed identity resource."
+ }
+ }
+ }
+ },
+ "nullable": true
+ },
+ "diagnosticSettingType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of diagnostic setting."
+ }
+ },
+ "logCategoriesAndGroups": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "category": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here."
+ }
+ },
+ "categoryGroup": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs."
+ }
+ },
+ "enabled": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable or disable the category explicitly. Default is `true`."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection."
+ }
+ },
+ "metricCategories": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "category": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics."
+ }
+ },
+ "enabled": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable or disable the category explicitly. Default is `true`."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection."
+ }
+ },
+ "logAnalyticsDestinationType": {
+ "type": "string",
+ "allowedValues": [
+ "AzureDiagnostics",
+ "Dedicated"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type."
+ }
+ },
+ "workspaceResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "storageAccountResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "eventHubAuthorizationRuleResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to."
+ }
+ },
+ "eventHubName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "marketplacePartnerResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs."
+ }
+ }
+ }
+ },
+ "nullable": true
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 63,
+ "metadata": {
+ "description": "Required. Name of the communication service to create."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "global",
+ "metadata": {
+ "description": "Optional. Location for all Resources."
+ }
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentitiesType",
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource tags."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "diagnosticSettings": {
+ "$ref": "#/definitions/diagnosticSettingType",
+ "metadata": {
+ "description": "Optional. The diagnostic settings of the service."
+ }
+ },
+ "roleAssignments": {
+ "$ref": "#/definitions/roleAssignmentType",
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "dataLocation": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The location where the communication service stores its data at rest."
+ }
+ },
+ "linkedDomains": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of email Domain resource Ids."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ }
+ },
+ "variables": {
+ "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]",
+ "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]",
+ "builtInRoleNames": {
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
+ "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
+ "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]",
+ "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]"
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2023-07-01",
+ "name": "[format('46d3xbcp.res.communication-communicationservice.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]",
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "resources": [],
+ "outputs": {
+ "telemetry": {
+ "type": "String",
+ "value": "For more information, see https://aka.ms/avm/TelemetryInfo"
+ }
+ }
+ }
+ }
+ },
+ "communicationService": {
+ "type": "Microsoft.Communication/communicationServices",
+ "apiVersion": "2023-04-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "identity": "[variables('identity')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "dataLocation": "[parameters('dataLocation')]",
+ "linkedDomains": "[parameters('linkedDomains')]"
+ }
+ },
+ "communicationService_lock": {
+ "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]",
+ "type": "Microsoft.Authorization/locks",
+ "apiVersion": "2020-05-01",
+ "scope": "[format('Microsoft.Communication/communicationServices/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]",
+ "properties": {
+ "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]",
+ "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]"
+ },
+ "dependsOn": [
+ "communicationService"
+ ]
+ },
+ "communicationService_diagnosticSettings": {
+ "copy": {
+ "name": "communicationService_diagnosticSettings",
+ "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]"
+ },
+ "type": "Microsoft.Insights/diagnosticSettings",
+ "apiVersion": "2021-05-01-preview",
+ "scope": "[format('Microsoft.Communication/communicationServices/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]",
+ "properties": {
+ "copy": [
+ {
+ "name": "metrics",
+ "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]",
+ "input": {
+ "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]",
+ "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]",
+ "timeGrain": null
+ }
+ },
+ {
+ "name": "logs",
+ "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]",
+ "input": {
+ "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]",
+ "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]",
+ "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]"
+ }
+ }
+ ],
+ "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]",
+ "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]",
+ "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]",
+ "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]",
+ "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]",
+ "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]"
+ },
+ "dependsOn": [
+ "communicationService"
+ ]
+ },
+ "communicationService_roleAssignments": {
+ "copy": {
+ "name": "communicationService_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Communication/communicationServices/{0}', parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.Communication/communicationServices', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]",
+ "properties": {
+ "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]",
+ "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]",
+ "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]",
+ "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]",
+ "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]",
+ "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]",
+ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]"
+ },
+ "dependsOn": [
+ "communicationService"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the communication service."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the communication service."
+ },
+ "value": "[resourceId('Microsoft.Communication/communicationServices', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the communication service was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the communication service was deployed into."
+ },
+ "value": "[reference('communicationService', '2023-04-01', 'full').location]"
+ },
+ "systemAssignedMIPrincipalId": {
+ "type": "string",
+ "metadata": {
+ "description": "The principal ID of the system assigned identity."
+ },
+ "value": "[coalesce(tryGet(tryGet(reference('communicationService', '2023-04-01', 'full'), 'identity'), 'principalId'), '')]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/communication/communication-service/tests/e2e/defaults/main.test.bicep b/avm/res/communication/communication-service/tests/e2e/defaults/main.test.bicep
new file mode 100644
index 00000000000..f9e33c9d23f
--- /dev/null
+++ b/avm/res/communication/communication-service/tests/e2e/defaults/main.test.bicep
@@ -0,0 +1,49 @@
+targetScope = 'subscription'
+
+metadata name = 'Using only defaults'
+metadata description = 'This instance deploys the module with the minimum set of required parameters.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-communication-communicationservices-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ccsmin'
+
+@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.')
+param namePrefix string = '#_namePrefix_#'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ location: 'global'
+ dataLocation: 'Germany'
+ }
+ }
+]
diff --git a/avm/res/communication/communication-service/tests/e2e/max/dependencies.bicep b/avm/res/communication/communication-service/tests/e2e/max/dependencies.bicep
new file mode 100644
index 00000000000..ef29480b6d6
--- /dev/null
+++ b/avm/res/communication/communication-service/tests/e2e/max/dependencies.bicep
@@ -0,0 +1,38 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Email Service to create.')
+param emailServiceName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource emailService 'Microsoft.Communication/emailServices@2023-04-01' = {
+ name: emailServiceName
+ location: 'global'
+ properties: {
+ dataLocation: 'Germany'
+ }
+
+ resource domain 'domains@2023-04-01' = {
+ name: 'AzureManagedDomain'
+ location: 'global'
+ properties: {
+ domainManagement: 'AzureManaged'
+ }
+ }
+}
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Email Service Domain.')
+output emailDomainResourceId string = emailService::domain.id
diff --git a/avm/res/communication/communication-service/tests/e2e/max/main.test.bicep b/avm/res/communication/communication-service/tests/e2e/max/main.test.bicep
new file mode 100644
index 00000000000..0fbc81cab2c
--- /dev/null
+++ b/avm/res/communication/communication-service/tests/e2e/max/main.test.bicep
@@ -0,0 +1,125 @@
+targetScope = 'subscription'
+
+metadata name = 'Using large parameter set'
+metadata description = 'This instance deploys the module with most of its features enabled.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-communication-communicationservices-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ccsmax'
+
+@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.')
+param namePrefix string = '#_namePrefix_#'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ emailServiceName: 'dep-${namePrefix}-email-${serviceShort}'
+ location: resourceLocation
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: resourceLocation
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ location: 'global'
+ dataLocation: 'Germany'
+ linkedDomains: [
+ nestedDependencies.outputs.emailDomainResourceId
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Owner'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ {
+ roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ {
+ roleDefinitionIdOrName: subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ 'acdd72a7-3385-48ef-bd42-f606fba81ae7'
+ )
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ managedIdentities: {
+ systemAssigned: false
+ userAssignedResourceIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ }
+]
diff --git a/avm/res/communication/communication-service/tests/e2e/waf-aligned/main.test.bicep b/avm/res/communication/communication-service/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 00000000000..5dd224b6bf1
--- /dev/null
+++ b/avm/res/communication/communication-service/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,74 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-communication-communicationservices-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ccswaf'
+
+@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.')
+param namePrefix string = '#_namePrefix_#'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: resourceLocation
+ }
+}
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ location: 'global'
+ dataLocation: 'Germany'
+ diagnosticSettings: [
+ {
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ }
+]
diff --git a/avm/res/communication/communication-service/version.json b/avm/res/communication/communication-service/version.json
new file mode 100644
index 00000000000..8def869edeb
--- /dev/null
+++ b/avm/res/communication/communication-service/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
+ "version": "0.1",
+ "pathFilters": [
+ "./main.json"
+ ]
+}
From b2790c0e96299afbcf4bb0ebe82e61ec901948a8 Mon Sep 17 00:00:00 2001
From: rodney-almeida <64196999+rodney-almeida@users.noreply.github.com>
Date: Tue, 14 May 2024 08:50:17 +0100
Subject: [PATCH 38/52] feat: Create\Update User Defined Types with sensitive
values to include @secure() decorator. -
`avm/res/container-instance/container-group` (#1919)
## Description
Update containerType (user defined type) to set
environmentVariables.secureValue to be of type secureString.
Followed the same logic and created the user defined type for
imageRegistryCredentials (imageRegistryCredentialType) as it also has a
password attribute.
Fixes #1868
Closes #1868
-->
## Pipeline Reference
| Pipeline |
| -------- |
[![avm.res.container-instance.container-group](https://github.com/rodney-almeida/bicep-registry-modules/actions/workflows/avm.res.container-instance.container-group.yml/badge.svg?branch=rodney-almeida-1868)](https://github.com/rodney-almeida/bicep-registry-modules/actions/workflows/avm.res.container-instance.container-group.yml)
## Type of Change
- [ ] Update to CI Environment or utlities (Non-module effecting
changes)
- [ ] Azure Verified Module updates:
- [X] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [X] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
## Checklist
- [X] I'm sure there are no other open Pull Requests for the same
update/change
- [X] I have run `Set-AVMModule` locally to generate the supporting
module files.
- [X] My corresponding pipelines / checks run clean and green without
any errors or warnings
---------
Co-authored-by: Rodney Almeida
---
.../container-group/README.md | 76 ++++++++++++-
.../container-group/main.bicep | 104 ++++++++++--------
.../container-group/main.json | 53 ++++++++-
.../tests/e2e/max/main.test.bicep | 11 +-
4 files changed, 189 insertions(+), 55 deletions(-)
diff --git a/avm/res/container-instance/container-group/README.md b/avm/res/container-instance/container-group/README.md
index c93906a3f9c..983ca8cf49b 100644
--- a/avm/res/container-instance/container-group/README.md
+++ b/avm/res/container-instance/container-group/README.md
@@ -355,7 +355,16 @@ module containerGroup 'br/public:avm/res/container-instance/container-group:
Date: Wed, 15 May 2024 23:53:28 +0200
Subject: [PATCH 39/52] feat: New pattern
`avm/ptn/authorization/resource-role-assignment` (#1905)
## Description
Adding new pattern `avm/ptn/authorization/resource-role-assignment`
https://github.com/Azure/Azure-Verified-Modules/issues/939
Migration of `modules/authorization/resource-scope-role-assignment`
Closes #937
## Pipeline Reference
| Pipeline |
| -------- |
|
[![avm.ptn.authorization.resource-role-assignment](https://github.com/peterbud/bicep-registry-modules/actions/workflows/avm.ptn.authorization.resource-role-assignment.yml/badge.svg?branch=resource-role-assignment)](https://github.com/peterbud/bicep-registry-modules/actions/workflows/avm.ptn.authorization.resource-role-assignment.yml)
|
## Type of Change
- [ ] Update to CI Environment or utlities (Non-module effecting
changes)
- [ ] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
## Checklist
- [x] I'm sure there are no other open Pull Requests for the same
update/change
- [x] I have run `Set-AVMModule` locally to generate the supporting
module files.
- [x] My corresponding pipelines / checks run clean and green without
any errors or warnings
---
.github/CODEOWNERS | 1 +
.github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 +
...authorization.resource-role-assignment.yml | 90 ++++++
.../resource-role-assignment/README.md | 259 ++++++++++++++++++
.../resource-role-assignment/main.bicep | 108 ++++++++
.../resource-role-assignment/main.json | 219 +++++++++++++++
.../modules/generic-role-assignment.json | 56 ++++
.../tests/e2e/all/dependencies.bicep | 28 ++
.../tests/e2e/all/main.test.bicep | 61 +++++
.../tests/e2e/defaults/dependencies.bicep | 28 ++
.../tests/e2e/defaults/main.test.bicep | 59 ++++
.../resource-role-assignment/version.json | 7 +
12 files changed, 917 insertions(+)
create mode 100644 .github/workflows/avm.ptn.authorization.resource-role-assignment.yml
create mode 100644 avm/ptn/authorization/resource-role-assignment/README.md
create mode 100644 avm/ptn/authorization/resource-role-assignment/main.bicep
create mode 100644 avm/ptn/authorization/resource-role-assignment/main.json
create mode 100644 avm/ptn/authorization/resource-role-assignment/modules/generic-role-assignment.json
create mode 100644 avm/ptn/authorization/resource-role-assignment/tests/e2e/all/dependencies.bicep
create mode 100644 avm/ptn/authorization/resource-role-assignment/tests/e2e/all/main.test.bicep
create mode 100644 avm/ptn/authorization/resource-role-assignment/tests/e2e/defaults/dependencies.bicep
create mode 100644 avm/ptn/authorization/resource-role-assignment/tests/e2e/defaults/main.test.bicep
create mode 100644 avm/ptn/authorization/resource-role-assignment/version.json
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 730d31af409..3829d6ef269 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -4,6 +4,7 @@
/avm/ @Azure/avm-core-team-technical-bicep
/avm/utilities/ @Azure/avm-core-team-technical-bicep
/avm/ptn/authorization/policy-assignment/ @Azure/avm-ptn-authorization-policyassignment-module-owners-bicep @Azure/avm-core-team-technical-bicep
+/avm/ptn/authorization/resource-role-assignment/ @Azure/avm-ptn-authorization-resourceroleassignment-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/ptn/authorization/role-assignment/ @Azure/avm-ptn-authorization-roleassignment-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/ptn/security/security-center/ @Azure/avm-ptn-security-securitycenter-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep @Azure/avm-core-team-technical-bicep
diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml
index 43e4d477db4..ab3d0c78670 100644
--- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml
+++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml
@@ -39,6 +39,7 @@ body:
options:
- ""
- "avm/ptn/authorization/policy-assignment"
+ - "avm/ptn/authorization/resource-role-assignment"
- "avm/ptn/authorization/role-assignment"
# - "avm/ptn/avd-lza/insights"
# - "avm/ptn/avd-lza/management-plane"
diff --git a/.github/workflows/avm.ptn.authorization.resource-role-assignment.yml b/.github/workflows/avm.ptn.authorization.resource-role-assignment.yml
new file mode 100644
index 00000000000..979d22d8dfa
--- /dev/null
+++ b/.github/workflows/avm.ptn.authorization.resource-role-assignment.yml
@@ -0,0 +1,90 @@
+name: "avm.ptn.authorization.resource-role-assignment"
+
+on:
+ schedule:
+ - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month)
+ workflow_dispatch:
+ inputs:
+ staticValidation:
+ type: boolean
+ description: "Execute static validation"
+ required: false
+ default: true
+ deploymentValidation:
+ type: boolean
+ description: "Execute deployment validation"
+ required: false
+ default: true
+ removeDeployment:
+ type: boolean
+ description: "Remove deployed module"
+ required: false
+ default: true
+ customLocation:
+ type: string
+ description: "Default location overwrite (e.g., eastus)"
+ required: false
+ push:
+ branches:
+ - main
+ paths:
+ - ".github/actions/templates/avm-**"
+ - ".github/workflows/avm.template.module.yml"
+ - ".github/workflows/avm.ptn.authorization.resource-role-assignment.yml"
+ - "avm/ptn/authorization/resource-role-assignment/**"
+ - "avm/utilities/pipelines/**"
+ - "!avm/utilities/pipelines/platform/**"
+ - "!*/**/README.md"
+
+env:
+ modulePath: "avm/ptn/authorization/resource-role-assignment"
+ workflowPath: ".github/workflows/avm.ptn.authorization.resource-role-assignment.yml"
+
+concurrency:
+ group: ${{ github.workflow }}
+
+jobs:
+ ###########################
+ # Initialize pipeline #
+ ###########################
+ job_initialize_pipeline:
+ runs-on: ubuntu-latest
+ name: "Initialize pipeline"
+ steps:
+ - name: "Checkout"
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: "Set input parameters to output variables"
+ id: get-workflow-param
+ uses: ./.github/actions/templates/avm-getWorkflowInput
+ with:
+ workflowPath: "${{ env.workflowPath}}"
+ - name: "Get module test file paths"
+ id: get-module-test-file-paths
+ uses: ./.github/actions/templates/avm-getModuleTestFiles
+ with:
+ modulePath: "${{ env.modulePath }}"
+ outputs:
+ workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }}
+ moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }}
+ psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }}
+ modulePath: "${{ env.modulePath }}"
+
+ ##############################
+ # Call reusable workflow #
+ ##############################
+ call-workflow-passing-data:
+ name: "Run"
+ permissions:
+ id-token: write # For OIDC
+ contents: write # For release tags
+ needs:
+ - job_initialize_pipeline
+ uses: ./.github/workflows/avm.template.module.yml
+ with:
+ workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}"
+ moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}"
+ psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}"
+ modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}"
+ secrets: inherit
diff --git a/avm/ptn/authorization/resource-role-assignment/README.md b/avm/ptn/authorization/resource-role-assignment/README.md
new file mode 100644
index 00000000000..3c440de2259
--- /dev/null
+++ b/avm/ptn/authorization/resource-role-assignment/README.md
@@ -0,0 +1,259 @@
+# Resource-scoped role assignment `[Microsoft.Authorization/resourceroleassignment]`
+
+This module deploys a Role Assignment for a specific resource.
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Usage examples](#Usage-examples)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+- [Data Collection](#Data-Collection)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+
+## Usage examples
+
+The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository.
+
+>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order.
+
+>**Note**: To reference the module, please use the following syntax `br/public:avm/ptn/authorization/resource-role-assignment:`.
+
+- [Resource Role Assignments](#example-1-resource-role-assignments)
+- [Resource Role Assignments](#example-2-resource-role-assignments)
+
+### Example 1: _Resource Role Assignments_
+
+This module deploys a Resource Role Assignment using all parameters.
+
+
+
+
+via Bicep module
+
+```bicep
+module resourceRoleAssignment 'br/public:avm/ptn/authorization/resource-role-assignment:' = {
+ name: 'resourceRoleAssignmentDeployment'
+ params: {
+ // Required parameters
+ principalId: ''
+ resourceId: ''
+ roleDefinitionId: '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1'
+ // Non-required parameters
+ description: 'Assign Storage Blob Data Reader role to the managed identity on the storage account.'
+ principalType: 'ServicePrincipal'
+ roleName: 'Storage Blob Data Reader'
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "principalId": {
+ "value": ""
+ },
+ "resourceId": {
+ "value": ""
+ },
+ "roleDefinitionId": {
+ "value": "2a2b9908-6ea1-4ae2-8e65-a410df84e7d1"
+ },
+ // Non-required parameters
+ "description": {
+ "value": "Assign Storage Blob Data Reader role to the managed identity on the storage account."
+ },
+ "principalType": {
+ "value": "ServicePrincipal"
+ },
+ "roleName": {
+ "value": "Storage Blob Data Reader"
+ }
+ }
+}
+```
+
+
+
+
+### Example 2: _Resource Role Assignments_
+
+This module deploys a Resource Role Assignment using minimal parameters.
+
+
+
+
+via Bicep module
+
+```bicep
+module resourceRoleAssignment 'br/public:avm/ptn/authorization/resource-role-assignment:' = {
+ name: 'resourceRoleAssignmentDeployment'
+ params: {
+ // Required parameters
+ principalId: ''
+ resourceId: ''
+ roleDefinitionId: '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1'
+ // Non-required parameters
+ principalType: 'ServicePrincipal'
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "principalId": {
+ "value": ""
+ },
+ "resourceId": {
+ "value": ""
+ },
+ "roleDefinitionId": {
+ "value": "2a2b9908-6ea1-4ae2-8e65-a410df84e7d1"
+ },
+ // Non-required parameters
+ "principalType": {
+ "value": "ServicePrincipal"
+ }
+ }
+}
+```
+
+
+
+
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`principalId`](#parameter-principalid) | string | The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). |
+| [`resourceId`](#parameter-resourceid) | string | The scope for the role assignment, fully qualified resourceId. |
+| [`roleDefinitionId`](#parameter-roledefinitionid) | string | The role definition ID for the role assignment. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`description`](#parameter-description) | string | The description of role assignment. |
+| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. |
+| [`name`](#parameter-name) | string | The unique guid name for the role assignment. |
+| [`principalType`](#parameter-principaltype) | string | The principal type of the assigned principal ID. |
+| [`roleName`](#parameter-rolename) | string | The name for the role, used for logging. |
+
+### Parameter: `principalId`
+
+The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).
+
+- Required: Yes
+- Type: string
+
+### Parameter: `resourceId`
+
+The scope for the role assignment, fully qualified resourceId.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `roleDefinitionId`
+
+The role definition ID for the role assignment.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `description`
+
+The description of role assignment.
+
+- Required: No
+- Type: string
+- Default: `''`
+
+### Parameter: `enableTelemetry`
+
+Enable/Disable usage telemetry for module.
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `name`
+
+The unique guid name for the role assignment.
+
+- Required: No
+- Type: string
+- Default: `[guid(parameters('resourceId'), parameters('principalId'), if(contains(parameters('roleDefinitionId'), '/providers/Microsoft.Authorization/roleDefinitions/'), parameters('roleDefinitionId'), subscriptionResourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))))]`
+
+### Parameter: `principalType`
+
+The principal type of the assigned principal ID.
+
+- Required: No
+- Type: string
+- Default: `''`
+- Allowed:
+ ```Bicep
+ [
+ ''
+ 'Device'
+ 'ForeignGroup'
+ 'Group'
+ 'ServicePrincipal'
+ 'User'
+ ]
+ ```
+
+### Parameter: `roleName`
+
+The name for the role, used for logging.
+
+- Required: No
+- Type: string
+- Default: `''`
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `name` | string | The GUID of the Role Assignment. |
+| `resourceGroupName` | string | The name of the resource group the role assignment was applied at. |
+| `resourceId` | string | The resource ID of the Role Assignment. |
+| `roleName` | string | The name for the role, used for logging. |
+
+## Cross-referenced modules
+
+_None_
+
+## Data Collection
+
+The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.
diff --git a/avm/ptn/authorization/resource-role-assignment/main.bicep b/avm/ptn/authorization/resource-role-assignment/main.bicep
new file mode 100644
index 00000000000..37e0afe82b9
--- /dev/null
+++ b/avm/ptn/authorization/resource-role-assignment/main.bicep
@@ -0,0 +1,108 @@
+metadata name = 'Resource-scoped role assignment'
+metadata description = 'This module deploys a Role Assignment for a specific resource.'
+metadata owner = 'Azure/module-maintainers'
+
+@sys.description('Required. The scope for the role assignment, fully qualified resourceId.')
+param resourceId string
+
+@sys.description('Optional. The unique guid name for the role assignment.')
+param name string = guid(
+ resourceId,
+ principalId,
+ contains(roleDefinitionId, '/providers/Microsoft.Authorization/roleDefinitions/')
+ ? roleDefinitionId
+ : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId)
+)
+
+@sys.description('Required. The role definition ID for the role assignment.')
+param roleDefinitionId string
+
+@sys.description('Optional. The name for the role, used for logging.')
+param roleName string = ''
+
+@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).')
+param principalId string
+
+@sys.description('Optional. The principal type of the assigned principal ID.')
+@allowed([
+ 'ServicePrincipal'
+ 'Group'
+ 'User'
+ 'ForeignGroup'
+ 'Device'
+ ''
+])
+param principalType string = ''
+
+@sys.description('Optional. The description of role assignment.')
+param description string = ''
+
+@sys.description('Optional. Enable/Disable usage telemetry for module.')
+param enableTelemetry bool = true
+
+// =============== //
+// Definitions //
+// =============== //
+
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: '46d3xbcp.ptn.authorization-resourceroleassignment.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name), 0, 4)}'
+ properties: {
+ mode: 'Incremental'
+ template: {
+ '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
+ contentVersion: '1.0.0.0'
+ resources: []
+ outputs: {
+ telemetry: {
+ type: 'String'
+ value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
+ }
+ }
+ }
+ }
+}
+
+resource resourceRoleAssignment 'Microsoft.Resources/deployments@2023-07-01' = {
+ name: '${guid(resourceId, principalId, roleDefinitionId)}-ResourceRoleAssignment'
+ properties: {
+ mode: 'Incremental'
+ expressionEvaluationOptions: {
+ scope: 'Outer'
+ }
+ template: loadJsonContent('modules/generic-role-assignment.json')
+ parameters: {
+ scope: {
+ value: resourceId
+ }
+ name: {
+ value: name
+ }
+ roleDefinitionId: {
+ value: contains(roleDefinitionId, '/providers/Microsoft.Authorization/roleDefinitions/')
+ ? roleDefinitionId
+ : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId)
+ }
+ principalId: {
+ value: principalId
+ }
+ principalType: {
+ value: principalType
+ }
+ description: {
+ value: description
+ }
+ }
+ }
+}
+
+@sys.description('The GUID of the Role Assignment.')
+output name string = name
+
+@sys.description('The name for the role, used for logging.')
+output roleName string = roleName
+
+@sys.description('The resource ID of the Role Assignment.')
+output resourceId string = resourceRoleAssignment.properties.outputs.roleAssignmentId.value
+
+@sys.description('The name of the resource group the role assignment was applied at.')
+output resourceGroupName string = resourceGroup().name
diff --git a/avm/ptn/authorization/resource-role-assignment/main.json b/avm/ptn/authorization/resource-role-assignment/main.json
new file mode 100644
index 00000000000..f576453bd93
--- /dev/null
+++ b/avm/ptn/authorization/resource-role-assignment/main.json
@@ -0,0 +1,219 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.26.170.59819",
+ "templateHash": "17107961067773605935"
+ },
+ "name": "Resource-scoped role assignment",
+ "description": "This module deploys a Role Assignment for a specific resource.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The scope for the role assignment, fully qualified resourceId."
+ }
+ },
+ "name": {
+ "type": "string",
+ "defaultValue": "[guid(parameters('resourceId'), parameters('principalId'), if(contains(parameters('roleDefinitionId'), '/providers/Microsoft.Authorization/roleDefinitions/'), parameters('roleDefinitionId'), subscriptionResourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))))]",
+ "metadata": {
+ "description": "Optional. The unique guid name for the role assignment."
+ }
+ },
+ "roleDefinitionId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The role definition ID for the role assignment."
+ }
+ },
+ "roleName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The name for the role, used for logging."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity)."
+ }
+ },
+ "principalType": {
+ "type": "string",
+ "defaultValue": "",
+ "allowedValues": [
+ "ServicePrincipal",
+ "Group",
+ "User",
+ "ForeignGroup",
+ "Device",
+ ""
+ ],
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The description of role assignment."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ }
+ },
+ "variables": {
+ "$fxv#0": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "scope": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "roleDefinitionId": {
+ "type": "string"
+ },
+ "principalId": {
+ "type": "string"
+ },
+ "principalType": {
+ "type": "string",
+ "allowedValues": [
+ "Device",
+ "ForeignGroup",
+ "Group",
+ "ServicePrincipal",
+ "User",
+ ""
+ ],
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string"
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[[parameters('scope')]",
+ "name": "[[parameters('name')]",
+ "properties": {
+ "roleDefinitionId": "[[parameters('roleDefinitionId')]",
+ "principalId": "[[parameters('principalId')]",
+ "principalType": "[[parameters('principalType')]",
+ "description": "[[parameters('description')]"
+ }
+ }
+ ],
+ "outputs": {
+ "roleAssignmentId": {
+ "type": "string",
+ "value": "[[extensionResourceId(parameters('scope'), 'Microsoft.Authorization/roleAssignments', parameters('name'))]"
+ }
+ }
+ }
+ },
+ "resources": [
+ {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2023-07-01",
+ "name": "[format('46d3xbcp.ptn.authorization-resourceroleassignment.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]",
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "resources": [],
+ "outputs": {
+ "telemetry": {
+ "type": "String",
+ "value": "For more information, see https://aka.ms/avm/TelemetryInfo"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2023-07-01",
+ "name": "[format('{0}-ResourceRoleAssignment', guid(parameters('resourceId'), parameters('principalId'), parameters('roleDefinitionId')))]",
+ "properties": {
+ "mode": "Incremental",
+ "expressionEvaluationOptions": {
+ "scope": "Outer"
+ },
+ "template": "[variables('$fxv#0')]",
+ "parameters": {
+ "scope": {
+ "value": "[parameters('resourceId')]"
+ },
+ "name": {
+ "value": "[parameters('name')]"
+ },
+ "roleDefinitionId": {
+ "value": "[if(contains(parameters('roleDefinitionId'), '/providers/Microsoft.Authorization/roleDefinitions/'), parameters('roleDefinitionId'), subscriptionResourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId')))]"
+ },
+ "principalId": {
+ "value": "[parameters('principalId')]"
+ },
+ "principalType": {
+ "value": "[parameters('principalType')]"
+ },
+ "description": {
+ "value": "[parameters('description')]"
+ }
+ }
+ }
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The GUID of the Role Assignment."
+ },
+ "value": "[parameters('name')]"
+ },
+ "roleName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name for the role, used for logging."
+ },
+ "value": "[parameters('roleName')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the Role Assignment."
+ },
+ "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-ResourceRoleAssignment', guid(parameters('resourceId'), parameters('principalId'), parameters('roleDefinitionId')))), '2023-07-01').outputs.roleAssignmentId.value]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the role assignment was applied at."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+}
diff --git a/avm/ptn/authorization/resource-role-assignment/modules/generic-role-assignment.json b/avm/ptn/authorization/resource-role-assignment/modules/generic-role-assignment.json
new file mode 100644
index 00000000000..2e18bcc9556
--- /dev/null
+++ b/avm/ptn/authorization/resource-role-assignment/modules/generic-role-assignment.json
@@ -0,0 +1,56 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "scope": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "roleDefinitionId": {
+ "type": "string"
+ },
+ "principalId": {
+ "type": "string"
+ },
+ "principalType": {
+ "type": "string",
+ "allowedValues": [
+ "Device",
+ "ForeignGroup",
+ "Group",
+ "ServicePrincipal",
+ "User",
+ ""
+ ],
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string"
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[parameters('scope')]",
+ "name": "[parameters('name')]",
+ "properties": {
+ "roleDefinitionId": "[parameters('roleDefinitionId')]",
+ "principalId": "[parameters('principalId')]",
+ "principalType": "[parameters('principalType')]",
+ "description": "[parameters('description')]"
+ }
+ }
+ ],
+ "outputs": {
+ "roleAssignmentId": {
+ "type": "string",
+ "value": "[extensionResourceId(parameters('scope'), 'Microsoft.Authorization/roleAssignments', parameters('name'))]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/ptn/authorization/resource-role-assignment/tests/e2e/all/dependencies.bicep b/avm/ptn/authorization/resource-role-assignment/tests/e2e/all/dependencies.bicep
new file mode 100644
index 00000000000..195c02ed8a5
--- /dev/null
+++ b/avm/ptn/authorization/resource-role-assignment/tests/e2e/all/dependencies.bicep
@@ -0,0 +1,28 @@
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Storage Account to create.')
+param storageAccountName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {
+ name: storageAccountName
+ location: location
+ sku: {
+ name: 'Standard_LRS'
+ }
+ kind: 'StorageV2'
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Storage Account.')
+output storageAccountResourceId string = storageAccount.id
diff --git a/avm/ptn/authorization/resource-role-assignment/tests/e2e/all/main.test.bicep b/avm/ptn/authorization/resource-role-assignment/tests/e2e/all/main.test.bicep
new file mode 100644
index 00000000000..2bc06f024de
--- /dev/null
+++ b/avm/ptn/authorization/resource-role-assignment/tests/e2e/all/main.test.bicep
@@ -0,0 +1,61 @@
+targetScope = 'subscription'
+metadata name = 'Resource Role Assignments'
+metadata description = 'This module deploys a Resource Role Assignment using all parameters.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-authorization.resourceroleassignment-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'arraall'
+
+@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.')
+param namePrefix string = '#_namePrefix_#'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ storageAccountName: 'dep${namePrefix}st${serviceShort}'
+ location: resourceLocation
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ name: '${guid(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ scope: resourceGroup
+ params: {
+ resourceId: nestedDependencies.outputs.storageAccountResourceId
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ roleDefinitionId: '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1'
+ roleName: 'Storage Blob Data Reader'
+ description: 'Assign Storage Blob Data Reader role to the managed identity on the storage account.'
+ }
+ }
+]
diff --git a/avm/ptn/authorization/resource-role-assignment/tests/e2e/defaults/dependencies.bicep b/avm/ptn/authorization/resource-role-assignment/tests/e2e/defaults/dependencies.bicep
new file mode 100644
index 00000000000..ac2fbefc476
--- /dev/null
+++ b/avm/ptn/authorization/resource-role-assignment/tests/e2e/defaults/dependencies.bicep
@@ -0,0 +1,28 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Storage Account to create.')
+param storageAccountName string
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {
+ name: storageAccountName
+ location: location
+ sku: {
+ name: 'Standard_LRS'
+ }
+ kind: 'StorageV2'
+}
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Storage Account.')
+output storageAccountResourceId string = storageAccount.id
diff --git a/avm/ptn/authorization/resource-role-assignment/tests/e2e/defaults/main.test.bicep b/avm/ptn/authorization/resource-role-assignment/tests/e2e/defaults/main.test.bicep
new file mode 100644
index 00000000000..252442bc345
--- /dev/null
+++ b/avm/ptn/authorization/resource-role-assignment/tests/e2e/defaults/main.test.bicep
@@ -0,0 +1,59 @@
+targetScope = 'subscription'
+metadata name = 'Resource Role Assignments'
+metadata description = 'This module deploys a Resource Role Assignment using minimal parameters.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-authorization.resourceroleassignment-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'arramin'
+
+@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.')
+param namePrefix string = '#_namePrefix_#'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies'
+ params: {
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ storageAccountName: 'dep${namePrefix}st${serviceShort}'
+ location: resourceLocation
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ name: '${guid(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ scope: resourceGroup
+ params: {
+ resourceId: nestedDependencies.outputs.storageAccountResourceId
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ roleDefinitionId: '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1' // Storage Blob Data Reader
+ }
+ }
+]
diff --git a/avm/ptn/authorization/resource-role-assignment/version.json b/avm/ptn/authorization/resource-role-assignment/version.json
new file mode 100644
index 00000000000..8def869edeb
--- /dev/null
+++ b/avm/ptn/authorization/resource-role-assignment/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
+ "version": "0.1",
+ "pathFilters": [
+ "./main.json"
+ ]
+}
From dc7914a45c79950231f26b0f704a89fc4c3d3613 Mon Sep 17 00:00:00 2001
From: Alexander Sehr
Date: Thu, 16 May 2024 10:15:06 +0200
Subject: [PATCH 40/52] fix: MachineLearningWorkspace - Updated to latest PE
schema (#1792)
## Description
Updated to latest PE schema
Fixes #1791
## Pipeline Reference
| Pipeline |
| -------- |
|
[![avm.res.machine-learning-services.workspace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.machine-learning-services.workspace.yml/badge.svg?branch=users%2Falsehr%2F1791_PEUpdate&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.machine-learning-services.workspace.yml)
|
## Type of Change
- [ ] Update to CI Environment or utlities (Non-module effecting
changes)
- [ ] Azure Verified Module updates:
- [x] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
---
.../workspace/README.md | 20 +++++++++++--
.../workspace/main.bicep | 14 +++++++--
.../workspace/main.json | 30 ++++++++++++++-----
.../workspace/version.json | 2 +-
4 files changed, 53 insertions(+), 13 deletions(-)
diff --git a/avm/res/machine-learning-services/workspace/README.md b/avm/res/machine-learning-services/workspace/README.md
index d123fc8d10c..6661c38dead 100644
--- a/avm/res/machine-learning-services/workspace/README.md
+++ b/avm/res/machine-learning-services/workspace/README.md
@@ -1057,10 +1057,12 @@ Configuration details for private endpoints. For security reasons, it is recomme
| [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. |
| [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. |
| [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. |
-| [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. Restricted to 140 chars. |
+| [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. |
| [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. |
| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. |
| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. |
+| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. |
+| [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. |
| [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. |
| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". |
| [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. |
@@ -1230,7 +1232,7 @@ Specify the name of lock.
### Parameter: `privateEndpoints.manualConnectionRequestMessage`
-A message passed to the owner of the remote resource with the manual connection request. Restricted to 140 chars.
+A message passed to the owner of the remote resource with the manual connection request.
- Required: No
- Type: string
@@ -1256,6 +1258,20 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g
- Required: No
- Type: array
+### Parameter: `privateEndpoints.privateLinkServiceConnectionName`
+
+The name of the private link connection to create.
+
+- Required: No
+- Type: string
+
+### Parameter: `privateEndpoints.resourceGroupName`
+
+Specify if you want to deploy the Private Endpoint into a different resource group than the main resource.
+
+- Required: No
+- Type: string
+
### Parameter: `privateEndpoints.roleAssignments`
Array of role assignments to create.
diff --git a/avm/res/machine-learning-services/workspace/main.bicep b/avm/res/machine-learning-services/workspace/main.bicep
index ece1f942f0f..035e2f2814e 100644
--- a/avm/res/machine-learning-services/workspace/main.bicep
+++ b/avm/res/machine-learning-services/workspace/main.bicep
@@ -309,9 +309,10 @@ resource workspace_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@202
module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [
for (privateEndpoint, index) in (privateEndpoints ?? []): {
name: '${uniqueString(deployment().name, location)}-workspace-PrivateEndpoint-${index}'
+ scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '')
params: {
name: privateEndpoint.?name ?? 'pep-${last(split(workspace.id, '/'))}-${privateEndpoint.?service ?? 'amlworkspace'}-${index}'
- privateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections != true
+ privateLinkServiceConnections: privateEndpoint.?isManualConnection != true
? [
{
name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(workspace.id, '/'))}-${privateEndpoint.?service ?? 'amlworkspace'}-${index}'
@@ -324,7 +325,7 @@ module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.
}
]
: null
- manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections == true
+ manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true
? [
{
name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(workspace.id, '/'))}-${privateEndpoint.?service ?? 'amlworkspace'}-${index}'
@@ -447,6 +448,9 @@ type privateEndpointType = {
@sys.description('Optional. The location to deploy the private endpoint to.')
location: string?
+ @sys.description('Optional. The name of the private link connection to create.')
+ privateLinkServiceConnectionName: string?
+
@sys.description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".')
service: string?
@@ -462,7 +466,8 @@ type privateEndpointType = {
@sys.description('Optional. If Manual Private Link Connection is required.')
isManualConnection: bool?
- @sys.description('Optional. A message passed to the owner of the remote resource with the manual connection request. Restricted to 140 chars.')
+ @sys.description('Optional. A message passed to the owner of the remote resource with the manual connection request.')
+ @maxLength(140)
manualConnectionRequestMessage: string?
@sys.description('Optional. Custom DNS configurations.')
@@ -509,6 +514,9 @@ type privateEndpointType = {
@sys.description('Optional. Enable/Disable usage telemetry for module.')
enableTelemetry: bool?
+
+ @sys.description('Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource.')
+ resourceGroupName: string?
}[]?
type diagnosticSettingType = {
diff --git a/avm/res/machine-learning-services/workspace/main.json b/avm/res/machine-learning-services/workspace/main.json
index d11a6904f5e..59d6b5bf9b9 100644
--- a/avm/res/machine-learning-services/workspace/main.json
+++ b/avm/res/machine-learning-services/workspace/main.json
@@ -5,8 +5,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "16054212084136992012"
+ "version": "0.26.170.59819",
+ "templateHash": "3104036699030674643"
},
"name": "Machine Learning Services Workspaces",
"description": "This module deploys a Machine Learning Services Workspace.",
@@ -145,6 +145,13 @@
"description": "Optional. The location to deploy the private endpoint to."
}
},
+ "privateLinkServiceConnectionName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the private link connection to create."
+ }
+ },
"service": {
"type": "string",
"nullable": true,
@@ -185,8 +192,9 @@
"manualConnectionRequestMessage": {
"type": "string",
"nullable": true,
+ "maxLength": 140,
"metadata": {
- "description": "Optional. A message passed to the owner of the remote resource with the manual connection request. Restricted to 140 chars."
+ "description": "Optional. A message passed to the owner of the remote resource with the manual connection request."
}
},
"customDnsConfigs": {
@@ -303,6 +311,13 @@
"metadata": {
"description": "Optional. Enable/Disable usage telemetry for module."
}
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource."
+ }
}
}
},
@@ -861,8 +876,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "2355820538616279194"
+ "version": "0.26.170.59819",
+ "templateHash": "16998034314964845307"
},
"name": "Machine Learning Services Workspaces Computes",
"description": "This module deploys a Machine Learning Services Workspaces Compute.\n\nAttaching a compute is not idempotent and will fail in case you try to redeploy over an existing compute in AML (see parameter `deployCompute`).",
@@ -1080,6 +1095,7 @@
"type": "Microsoft.Resources/deployments",
"apiVersion": "2022-09-01",
"name": "[format('{0}-workspace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]",
"properties": {
"expressionEvaluationOptions": {
"scope": "inner"
@@ -1089,8 +1105,8 @@
"name": {
"value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex()))]"
},
- "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace')))))), createObject('value', null()))]",
- "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]",
+ "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace')))))), createObject('value', null()))]",
+ "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]",
"subnetResourceId": {
"value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]"
},
diff --git a/avm/res/machine-learning-services/workspace/version.json b/avm/res/machine-learning-services/workspace/version.json
index 83083db6945..1c035df49f2 100644
--- a/avm/res/machine-learning-services/workspace/version.json
+++ b/avm/res/machine-learning-services/workspace/version.json
@@ -1,6 +1,6 @@
{
"$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
- "version": "0.1",
+ "version": "0.2",
"pathFilters": [
"./main.json"
]
From dee18fe01f941b74b131036386b20a9fa463e58e Mon Sep 17 00:00:00 2001
From: hundredacres
Date: Thu, 16 May 2024 01:54:18 -0700
Subject: [PATCH 41/52] fix: Resolve bug with declaring Public IP Prefix use on
managementIP (#1939)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Description
Resolved issue when specifying a public IP prefix for the management IP
address. Updated to use latest PublicIPAddress AVM module. Updated API
version of Microsoft.Network/publicIPAddresses used in tests. Updated
formatting of zone param default values. Also added new e2e tests for
Public IP Prefix usage.
Fixes #1867
Closes #1867
## Pipeline Reference
| Pipeline |
| -------- |
|
[![avm.res.network.azure-firewall](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml/badge.svg?branch=fix%2Fissue%2F1867)](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml)
|
## Type of Change
- [ ] Update to CI Environment or utlities (Non-module effecting
changes)
- [X] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [X] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [X] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [X] Update to documentation
## Checklist
- [X] I'm sure there are no other open Pull Requests for the same
update/change
- [X] I have run `Set-AVMModule` locally to generate the supporting
module files.
- [X] My corresponding pipelines / checks run clean and green without
any errors or warnings
---------
Co-authored-by: Máté Barabás
Co-authored-by: Rainer Halanek <61878316+rahalan@users.noreply.github.com>
Co-authored-by: JFolberth
---
avm/res/network/azure-firewall/README.md | 228 +++++++++++++++--
avm/res/network/azure-firewall/main.bicep | 238 +++++++++---------
avm/res/network/azure-firewall/main.json | 174 ++++++++++---
.../e2e/publicipprefix/dependencies.bicep | 56 +++++
.../tests/e2e/publicipprefix/main.test.bicep | 75 ++++++
avm/res/network/azure-firewall/version.json | 4 +-
6 files changed, 596 insertions(+), 179 deletions(-)
create mode 100644 avm/res/network/azure-firewall/tests/e2e/publicipprefix/dependencies.bicep
create mode 100644 avm/res/network/azure-firewall/tests/e2e/publicipprefix/main.test.bicep
diff --git a/avm/res/network/azure-firewall/README.md b/avm/res/network/azure-firewall/README.md
index da0018a179d..e821af3f602 100644
--- a/avm/res/network/azure-firewall/README.md
+++ b/avm/res/network/azure-firewall/README.md
@@ -19,7 +19,7 @@ This module deploys an Azure Firewall.
| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) |
| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) |
| `Microsoft.Network/azureFirewalls` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/azureFirewalls) |
-| `Microsoft.Network/publicIPAddresses` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/publicIPAddresses) |
+| `Microsoft.Network/publicIPAddresses` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPAddresses) |
## Usage examples
@@ -29,15 +29,113 @@ The following section provides usage examples for the module, which were used to
>**Note**: To reference the module, please use the following syntax `br/public:avm/res/network/azure-firewall:`.
-- [Add-PIP](#example-1-add-pip)
-- [Custom-PIP](#example-2-custom-pip)
-- [Using only defaults](#example-3-using-only-defaults)
-- [Hub-commom](#example-4-hub-commom)
-- [Hub-min](#example-5-hub-min)
-- [Using large parameter set](#example-6-using-large-parameter-set)
-- [WAF-aligned](#example-7-waf-aligned)
+- [Issue-1867](#example-1-issue-1867)
+- [Add-PIP](#example-2-add-pip)
+- [Custom-PIP](#example-3-custom-pip)
+- [Using only defaults](#example-4-using-only-defaults)
+- [Hub-commom](#example-5-hub-commom)
+- [Hub-min](#example-6-hub-min)
+- [Using large parameter set](#example-7-using-large-parameter-set)
+- [Public-IP-Prefix](#example-8-public-ip-prefix)
+- [WAF-aligned](#example-9-waf-aligned)
-### Example 1: _Add-PIP_
+### Example 1: _Issue-1867_
+
+Validating reported bug 1867
+
+
+
+
+via Bicep module
+
+```bicep
+module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
+ name: 'azureFirewallDeployment'
+ params: {
+ // Required parameters
+ name: 'nafcustom001'
+ // Non-required parameters
+ azureSkuTier: 'Basic'
+ firewallPolicyId: ''
+ location: ''
+ managementIPAddressObject: {
+ managementIPAllocationMethod: 'Static'
+ managementIPPrefixResourceId: ''
+ name: 'managementIP01'
+ skuName: 'Standard'
+ skuTier: 'Regional'
+ }
+ publicIPAddressObject: {
+ name: 'publicIP01'
+ publicIPAllocationMethod: 'Static'
+ publicIPPrefixResourceId: ''
+ skuName: 'Standard'
+ skuTier: 'Regional'
+ }
+ virtualNetworkResourceId: ''
+ zones: []
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nafcustom001"
+ },
+ // Non-required parameters
+ "azureSkuTier": {
+ "value": "Basic"
+ },
+ "firewallPolicyId": {
+ "value": ""
+ },
+ "location": {
+ "value": ""
+ },
+ "managementIPAddressObject": {
+ "value": {
+ "managementIPAllocationMethod": "Static",
+ "managementIPPrefixResourceId": "",
+ "name": "managementIP01",
+ "skuName": "Standard",
+ "skuTier": "Regional"
+ }
+ },
+ "publicIPAddressObject": {
+ "value": {
+ "name": "publicIP01",
+ "publicIPAllocationMethod": "Static",
+ "publicIPPrefixResourceId": "",
+ "skuName": "Standard",
+ "skuTier": "Regional"
+ }
+ },
+ "virtualNetworkResourceId": {
+ "value": ""
+ },
+ "zones": {
+ "value": []
+ }
+ }
+}
+```
+
+
+
+
+### Example 2: _Add-PIP_
This instance deploys the module and attaches an existing public IP address.
@@ -129,7 +227,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 2: _Custom-PIP_
+### Example 3: _Custom-PIP_
This instance deploys the module and will create a public IP address.
@@ -239,7 +337,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 3: _Using only defaults_
+### Example 4: _Using only defaults_
This instance deploys the module with the minimum set of required parameters.
@@ -291,7 +389,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 4: _Hub-commom_
+### Example 5: _Hub-commom_
This instance deploys the module a vWAN in a typical hub setting.
@@ -359,7 +457,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 5: _Hub-min_
+### Example 6: _Hub-min_
This instance deploys the module a vWAN minimum hub setting.
@@ -423,7 +521,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 6: _Using large parameter set_
+### Example 7: _Using large parameter set_
This instance deploys the module with most of its features enabled.
@@ -777,7 +875,99 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 7: _WAF-aligned_
+### Example 8: _Public-IP-Prefix_
+
+This instance deploys the module and will use a public IP prefix.
+
+
+
+
+via Bicep module
+
+```bicep
+module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
+ name: 'azureFirewallDeployment'
+ params: {
+ // Required parameters
+ name: 'nafpip001'
+ // Non-required parameters
+ azureSkuTier: 'Basic'
+ location: ''
+ managementIPAddressObject: {
+ managementIPAllocationMethod: 'Static'
+ managementIPPrefixResourceId: ''
+ name: 'managementIP01'
+ skuName: 'Standard'
+ skuTier: 'Regional'
+ }
+ publicIPAddressObject: {
+ name: 'publicIP01'
+ publicIPAllocationMethod: 'Static'
+ publicIPPrefixResourceId: ''
+ skuName: 'Standard'
+ skuTier: 'Regional'
+ }
+ virtualNetworkResourceId: ''
+ zones: []
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "name": {
+ "value": "nafpip001"
+ },
+ // Non-required parameters
+ "azureSkuTier": {
+ "value": "Basic"
+ },
+ "location": {
+ "value": ""
+ },
+ "managementIPAddressObject": {
+ "value": {
+ "managementIPAllocationMethod": "Static",
+ "managementIPPrefixResourceId": "",
+ "name": "managementIP01",
+ "skuName": "Standard",
+ "skuTier": "Regional"
+ }
+ },
+ "publicIPAddressObject": {
+ "value": {
+ "name": "publicIP01",
+ "publicIPAllocationMethod": "Static",
+ "publicIPPrefixResourceId": "",
+ "skuName": "Standard",
+ "skuTier": "Regional"
+ }
+ },
+ "virtualNetworkResourceId": {
+ "value": ""
+ },
+ "zones": {
+ "value": []
+ }
+ }
+}
+```
+
+
+
+
+### Example 9: _WAF-aligned_
This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
@@ -2033,9 +2223,9 @@ Zone numbers e.g. 1,2,3.
- Default:
```Bicep
[
- '1'
- '2'
- '3'
+ 1
+ 2
+ 3
]
```
@@ -2060,7 +2250,7 @@ This section gives you an overview of all local-referenced module files (i.e., o
| Reference | Type |
| :-- | :-- |
-| `br/public:avm/res/network/public-ip-address:0.2.1` | Remote reference |
+| `br/public:avm/res/network/public-ip-address:0.4.0` | Remote reference |
## Data Collection
diff --git a/avm/res/network/azure-firewall/main.bicep b/avm/res/network/azure-firewall/main.bicep
index 31765349135..54d4661cf2c 100644
--- a/avm/res/network/azure-firewall/main.bicep
+++ b/avm/res/network/azure-firewall/main.bicep
@@ -61,9 +61,9 @@ param threatIntelMode string = 'Deny'
@description('Optional. Zone numbers e.g. 1,2,3.')
param zones array = [
- '1'
- '2'
- '3'
+ 1
+ 2
+ 3
]
@description('Optional. The diagnostic settings of the service.')
@@ -143,7 +143,7 @@ var managementIPConfiguration = {
id: '${virtualNetworkResourceId}/subnets/AzureFirewallManagementSubnet' // The subnet name must be AzureFirewallManagementSubnet for a 'Basic' SKU tier firewall
}
},
- (!empty(publicIPResourceID) || !empty(managementIPAddressObject))
+ (!empty(managementIPResourceID) || !empty(managementIPAddressObject))
? {
// Use existing Management Public IP, new Management Public IP created in this module, or none if neither
publicIPAddress: {
@@ -169,138 +169,140 @@ var builtInRoleNames = {
)
}
-resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' =
- if (enableTelemetry) {
- name: '46d3xbcp.res.network-azurefirewall.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
- properties: {
- mode: 'Incremental'
- template: {
- '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
- contentVersion: '1.0.0.0'
- resources: []
- outputs: {
- telemetry: {
- type: 'String'
- value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
- }
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: '46d3xbcp.res.network-azurefirewall.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
+ properties: {
+ mode: 'Incremental'
+ template: {
+ '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
+ contentVersion: '1.0.0.0'
+ resources: []
+ outputs: {
+ telemetry: {
+ type: 'String'
+ value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
}
}
}
}
+}
-module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.2.1' =
- if (empty(publicIPResourceID) && azureSkuName == 'AZFW_VNet') {
- name: '${uniqueString(deployment().name, location)}-Firewall-PIP'
- params: {
- name: publicIPAddressObject.name
- publicIpPrefixResourceId: contains(publicIPAddressObject, 'publicIPPrefixResourceId')
- ? (!(empty(publicIPAddressObject.publicIPPrefixResourceId))
- ? publicIPAddressObject.publicIPPrefixResourceId
- : '')
- : ''
- publicIPAllocationMethod: contains(publicIPAddressObject, 'publicIPAllocationMethod')
- ? (!(empty(publicIPAddressObject.publicIPAllocationMethod))
- ? publicIPAddressObject.publicIPAllocationMethod
- : 'Static')
- : 'Static'
- skuName: contains(publicIPAddressObject, 'skuName')
- ? (!(empty(publicIPAddressObject.skuName)) ? publicIPAddressObject.skuName : 'Standard')
- : 'Standard'
- skuTier: contains(publicIPAddressObject, 'skuTier')
- ? (!(empty(publicIPAddressObject.skuTier)) ? publicIPAddressObject.skuTier : 'Regional')
- : 'Regional'
- roleAssignments: contains(publicIPAddressObject, 'roleAssignments')
- ? (!empty(publicIPAddressObject.roleAssignments) ? publicIPAddressObject.roleAssignments : [])
- : []
- diagnosticSettings: publicIPAddressObject.?diagnosticSettings
- location: location
- lock: lock
- tags: publicIPAddressObject.?tags ?? tags
- zones: zones
- enableTelemetry: publicIPAddressObject.?enableTelemetry ?? enableTelemetry
- }
+module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.4.0' = if (empty(publicIPResourceID) && azureSkuName == 'AZFW_VNet') {
+ name: '${uniqueString(deployment().name, location)}-Firewall-PIP'
+ params: {
+ name: publicIPAddressObject.name
+ publicIpPrefixResourceId: contains(publicIPAddressObject, 'publicIPPrefixResourceId')
+ ? (!(empty(publicIPAddressObject.publicIPPrefixResourceId)) ? publicIPAddressObject.publicIPPrefixResourceId : '')
+ : ''
+ publicIPAllocationMethod: contains(publicIPAddressObject, 'publicIPAllocationMethod')
+ ? (!(empty(publicIPAddressObject.publicIPAllocationMethod))
+ ? publicIPAddressObject.publicIPAllocationMethod
+ : 'Static')
+ : 'Static'
+ skuName: contains(publicIPAddressObject, 'skuName')
+ ? (!(empty(publicIPAddressObject.skuName)) ? publicIPAddressObject.skuName : 'Standard')
+ : 'Standard'
+ skuTier: contains(publicIPAddressObject, 'skuTier')
+ ? (!(empty(publicIPAddressObject.skuTier)) ? publicIPAddressObject.skuTier : 'Regional')
+ : 'Regional'
+ roleAssignments: contains(publicIPAddressObject, 'roleAssignments')
+ ? (!empty(publicIPAddressObject.roleAssignments) ? publicIPAddressObject.roleAssignments : [])
+ : []
+ diagnosticSettings: publicIPAddressObject.?diagnosticSettings
+ location: location
+ lock: lock
+ tags: publicIPAddressObject.?tags ?? tags
+ zones: zones
+ enableTelemetry: publicIPAddressObject.?enableTelemetry ?? enableTelemetry
}
+}
// create a Management Public IP address if one is not provided and the flag is true
-module managementIPAddress 'br/public:avm/res/network/public-ip-address:0.2.1' =
- if (isCreateDefaultManagementIP && azureSkuName == 'AZFW_VNet') {
- name: '${uniqueString(deployment().name, location)}-Firewall-MIP'
- params: {
- name: contains(managementIPAddressObject, 'name')
- ? (!(empty(managementIPAddressObject.name)) ? managementIPAddressObject.name : '${name}-mip')
- : '${name}-mip'
- publicIpPrefixResourceId: contains(managementIPAddressObject, 'managementIPPrefixResourceId')
- ? (!(empty(managementIPAddressObject.publicIPPrefixResourceId))
- ? managementIPAddressObject.publicIPPrefixResourceId
- : '')
- : ''
- publicIPAllocationMethod: contains(managementIPAddressObject, 'managementIPAllocationMethod')
- ? (!(empty(managementIPAddressObject.publicIPAllocationMethod))
- ? managementIPAddressObject.publicIPAllocationMethod
- : 'Static')
- : 'Static'
- skuName: contains(managementIPAddressObject, 'skuName')
- ? (!(empty(managementIPAddressObject.skuName)) ? managementIPAddressObject.skuName : 'Standard')
- : 'Standard'
- skuTier: contains(managementIPAddressObject, 'skuTier')
- ? (!(empty(managementIPAddressObject.skuTier)) ? managementIPAddressObject.skuTier : 'Regional')
- : 'Regional'
- roleAssignments: contains(managementIPAddressObject, 'roleAssignments')
- ? (!empty(managementIPAddressObject.roleAssignments) ? managementIPAddressObject.roleAssignments : [])
- : []
- diagnosticSettings: managementIPAddressObject.?diagnosticSettings
- location: location
- tags: managementIPAddressObject.?tags ?? tags
- zones: zones
- enableTelemetry: managementIPAddressObject.?enableTelemetry ?? enableTelemetry
- }
+module managementIPAddress 'br/public:avm/res/network/public-ip-address:0.4.0' = if (isCreateDefaultManagementIP && azureSkuName == 'AZFW_VNet') {
+ name: '${uniqueString(deployment().name, location)}-Firewall-MIP'
+ params: {
+ name: contains(managementIPAddressObject, 'name')
+ ? (!(empty(managementIPAddressObject.name)) ? managementIPAddressObject.name : '${name}-mip')
+ : '${name}-mip'
+ publicIpPrefixResourceId: contains(managementIPAddressObject, 'managementIPPrefixResourceId')
+ ? (!(empty(managementIPAddressObject.managementIPPrefixResourceId))
+ ? managementIPAddressObject.managementIPPrefixResourceId
+ : '')
+ : ''
+ publicIPAllocationMethod: contains(managementIPAddressObject, 'managementIPAllocationMethod')
+ ? (!(empty(managementIPAddressObject.managementIPAllocationMethod))
+ ? managementIPAddressObject.managementIPAllocationMethod
+ : 'Static')
+ : 'Static'
+ skuName: contains(managementIPAddressObject, 'skuName')
+ ? (!(empty(managementIPAddressObject.skuName)) ? managementIPAddressObject.skuName : 'Standard')
+ : 'Standard'
+ skuTier: contains(managementIPAddressObject, 'skuTier')
+ ? (!(empty(managementIPAddressObject.skuTier)) ? managementIPAddressObject.skuTier : 'Regional')
+ : 'Regional'
+ roleAssignments: contains(managementIPAddressObject, 'roleAssignments')
+ ? (!empty(managementIPAddressObject.roleAssignments) ? managementIPAddressObject.roleAssignments : [])
+ : []
+ diagnosticSettings: managementIPAddressObject.?diagnosticSettings
+ location: location
+ tags: managementIPAddressObject.?tags ?? tags
+ zones: zones
+ enableTelemetry: managementIPAddressObject.?enableTelemetry ?? enableTelemetry
}
+}
resource azureFirewall 'Microsoft.Network/azureFirewalls@2023-04-01' = {
name: name
location: location
zones: length(zones) == 0 ? null : zones
tags: tags
- properties: azureSkuName == 'AZFW_VNet' ? {
- threatIntelMode: threatIntelMode
- firewallPolicy: !empty(firewallPolicyId) ? {
- id: firewallPolicyId
- } : null
- ipConfigurations: ipConfigurations
- managementIpConfiguration: requiresManagementIp ? managementIPConfiguration : null
- sku: {
- name: azureSkuName
- tier: azureSkuTier
- }
- applicationRuleCollections: applicationRuleCollections ?? []
- natRuleCollections: natRuleCollections ?? []
- networkRuleCollections: networkRuleCollections ?? []
- } : {
- firewallPolicy: !empty(firewallPolicyId) ? {
- id: firewallPolicyId
- } : null
- sku: {
- name: azureSkuName
- tier: azureSkuTier
- }
- hubIPAddresses: !empty(hubIPAddresses) ? hubIPAddresses : null
- virtualHub: !empty(virtualHubId) ? {
- id: virtualHubId
- } : null
- }
+ properties: azureSkuName == 'AZFW_VNet'
+ ? {
+ threatIntelMode: threatIntelMode
+ firewallPolicy: !empty(firewallPolicyId)
+ ? {
+ id: firewallPolicyId
+ }
+ : null
+ ipConfigurations: ipConfigurations
+ managementIpConfiguration: requiresManagementIp ? managementIPConfiguration : null
+ sku: {
+ name: azureSkuName
+ tier: azureSkuTier
+ }
+ applicationRuleCollections: applicationRuleCollections ?? []
+ natRuleCollections: natRuleCollections ?? []
+ networkRuleCollections: networkRuleCollections ?? []
+ }
+ : {
+ firewallPolicy: !empty(firewallPolicyId)
+ ? {
+ id: firewallPolicyId
+ }
+ : null
+ sku: {
+ name: azureSkuName
+ tier: azureSkuTier
+ }
+ hubIPAddresses: !empty(hubIPAddresses) ? hubIPAddresses : null
+ virtualHub: !empty(virtualHubId)
+ ? {
+ id: virtualHubId
+ }
+ : null
+ }
}
-resource azureFirewall_lock 'Microsoft.Authorization/locks@2020-05-01' =
- if (!empty(lock ?? {}) && lock.?kind != 'None') {
- name: lock.?name ?? 'lock-${name}'
- properties: {
- level: lock.?kind ?? ''
- notes: lock.?kind == 'CanNotDelete'
- ? 'Cannot delete resource or child resources.'
- : 'Cannot delete or modify the resource or child resources.'
- }
- scope: azureFirewall
+resource azureFirewall_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
+ name: lock.?name ?? 'lock-${name}'
+ properties: {
+ level: lock.?kind ?? ''
+ notes: lock.?kind == 'CanNotDelete'
+ ? 'Cannot delete resource or child resources.'
+ : 'Cannot delete or modify the resource or child resources.'
}
+ scope: azureFirewall
+}
resource azureFirewall_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [
for (diagnosticSetting, index) in (diagnosticSettings ?? []): {
diff --git a/avm/res/network/azure-firewall/main.json b/avm/res/network/azure-firewall/main.json
index 877e4dbea24..47361a85e2a 100644
--- a/avm/res/network/azure-firewall/main.json
+++ b/avm/res/network/azure-firewall/main.json
@@ -5,8 +5,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "3831870271484638637"
+ "version": "0.27.1.19265",
+ "templateHash": "5299175231300175796"
},
"name": "Azure Firewalls",
"description": "This module deploys an Azure Firewall.",
@@ -776,9 +776,9 @@
"zones": {
"type": "array",
"defaultValue": [
- "1",
- "2",
- "3"
+ 1,
+ 2,
+ 3
],
"metadata": {
"description": "Optional. Zone numbers e.g. 1,2,3."
@@ -876,7 +876,7 @@
"location": "[parameters('location')]",
"zones": "[if(equals(length(parameters('zones')), 0), null(), parameters('zones'))]",
"tags": "[parameters('tags')]",
- "properties": "[if(equals(variables('azureSkuName'), 'AZFW_VNet'), createObject('threatIntelMode', parameters('threatIntelMode'), 'firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'ipConfigurations', concat(createArray(createObject('name', if(not(empty(parameters('publicIPResourceID'))), last(split(parameters('publicIPResourceID'), '/')), reference('publicIPAddress').outputs.name.value), 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallSubnet', parameters('virtualNetworkResourceId')))), if(or(not(empty(parameters('publicIPResourceID'))), not(empty(parameters('publicIPAddressObject')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('publicIPResourceID'))), parameters('publicIPResourceID'), reference('publicIPAddress').outputs.resourceId.value))), createObject())))), variables('additionalPublicIpConfigurationsVar')), 'managementIpConfiguration', if(variables('requiresManagementIp'), createObject('name', if(not(empty(parameters('managementIPResourceID'))), last(split(parameters('managementIPResourceID'), '/')), reference('managementIPAddress').outputs.name.value), 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallManagementSubnet', parameters('virtualNetworkResourceId')))), if(or(not(empty(parameters('publicIPResourceID'))), not(empty(parameters('managementIPAddressObject')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('managementIPResourceID'))), parameters('managementIPResourceID'), reference('managementIPAddress').outputs.resourceId.value))), createObject()))), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'applicationRuleCollections', coalesce(parameters('applicationRuleCollections'), createArray()), 'natRuleCollections', coalesce(parameters('natRuleCollections'), createArray()), 'networkRuleCollections', coalesce(parameters('networkRuleCollections'), createArray())), createObject('firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'hubIPAddresses', if(not(empty(parameters('hubIPAddresses'))), parameters('hubIPAddresses'), null()), 'virtualHub', if(not(empty(parameters('virtualHubId'))), createObject('id', parameters('virtualHubId')), null())))]",
+ "properties": "[if(equals(variables('azureSkuName'), 'AZFW_VNet'), createObject('threatIntelMode', parameters('threatIntelMode'), 'firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'ipConfigurations', concat(createArray(createObject('name', if(not(empty(parameters('publicIPResourceID'))), last(split(parameters('publicIPResourceID'), '/')), reference('publicIPAddress').outputs.name.value), 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallSubnet', parameters('virtualNetworkResourceId')))), if(or(not(empty(parameters('publicIPResourceID'))), not(empty(parameters('publicIPAddressObject')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('publicIPResourceID'))), parameters('publicIPResourceID'), reference('publicIPAddress').outputs.resourceId.value))), createObject())))), variables('additionalPublicIpConfigurationsVar')), 'managementIpConfiguration', if(variables('requiresManagementIp'), createObject('name', if(not(empty(parameters('managementIPResourceID'))), last(split(parameters('managementIPResourceID'), '/')), reference('managementIPAddress').outputs.name.value), 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallManagementSubnet', parameters('virtualNetworkResourceId')))), if(or(not(empty(parameters('managementIPResourceID'))), not(empty(parameters('managementIPAddressObject')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('managementIPResourceID'))), parameters('managementIPResourceID'), reference('managementIPAddress').outputs.resourceId.value))), createObject()))), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'applicationRuleCollections', coalesce(parameters('applicationRuleCollections'), createArray()), 'natRuleCollections', coalesce(parameters('natRuleCollections'), createArray()), 'networkRuleCollections', coalesce(parameters('networkRuleCollections'), createArray())), createObject('firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'hubIPAddresses', if(not(empty(parameters('hubIPAddresses'))), parameters('hubIPAddresses'), null()), 'virtualHub', if(not(empty(parameters('virtualHubId'))), createObject('id', parameters('virtualHubId')), null())))]",
"dependsOn": [
"managementIPAddress",
"publicIPAddress"
@@ -1004,8 +1004,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.23.1.45101",
- "templateHash": "3488076626994379707"
+ "version": "0.26.54.24096",
+ "templateHash": "4718335757080871925"
},
"name": "Public IP Addresses",
"description": "This module deploys a Public IP Address.",
@@ -1054,7 +1054,7 @@
"type": "string",
"nullable": true,
"metadata": {
- "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\""
+ "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"."
}
},
"conditionVersion": {
@@ -1148,11 +1148,14 @@
"type": "object",
"properties": {
"id": {
- "type": "string"
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address."
+ }
}
},
"metadata": {
- "description": "Required. The DDoS protection plan ID associated with the public IP address."
+ "description": "Required. The DDoS protection plan associated with the public IP address."
}
},
"protectionMode": {
@@ -1196,12 +1199,19 @@
"metadata": {
"description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs."
}
+ },
+ "enabled": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable or disable the category explicitly. Default is `true`."
+ }
}
}
},
"nullable": true,
"metadata": {
- "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection."
+ "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection."
}
},
"metricCategories": {
@@ -1214,12 +1224,19 @@
"metadata": {
"description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics."
}
+ },
+ "enabled": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable or disable the category explicitly. Default is `true`."
+ }
}
}
},
"nullable": true,
"metadata": {
- "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection."
+ "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection."
}
},
"logAnalyticsDestinationType": {
@@ -1300,7 +1317,19 @@
},
"zones": {
"type": "array",
- "nullable": true,
+ "items": {
+ "type": "int"
+ },
+ "defaultValue": [
+ 1,
+ 2,
+ 3
+ ],
+ "allowedValues": [
+ 1,
+ 2,
+ 3
+ ],
"metadata": {
"description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from."
}
@@ -1418,7 +1447,7 @@
"condition": "[parameters('enableTelemetry')]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2023-07-01",
- "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.2.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]",
+ "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]",
"properties": {
"mode": "Incremental",
"template": {
@@ -1436,7 +1465,7 @@
},
"publicIpAddress": {
"type": "Microsoft.Network/publicIPAddresses",
- "apiVersion": "2023-04-01",
+ "apiVersion": "2023-09-01",
"name": "[parameters('name')]",
"location": "[parameters('location')]",
"tags": "[parameters('tags')]",
@@ -1444,7 +1473,7 @@
"name": "[parameters('skuName')]",
"tier": "[parameters('skuTier')]"
},
- "zones": "[parameters('zones')]",
+ "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]",
"properties": {
"ddosSettings": "[parameters('ddosSettings')]",
"dnsSettings": "[parameters('dnsSettings')]",
@@ -1452,7 +1481,7 @@
"publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]",
"publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]",
"idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]",
- "ipTags": []
+ "ipTags": null
}
},
"publicIpAddress_lock": {
@@ -1501,12 +1530,30 @@
"scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]",
"name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]",
"properties": {
+ "copy": [
+ {
+ "name": "metrics",
+ "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]",
+ "input": {
+ "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]",
+ "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]",
+ "timeGrain": null
+ }
+ },
+ {
+ "name": "logs",
+ "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]",
+ "input": {
+ "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]",
+ "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]",
+ "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]"
+ }
+ }
+ ],
"storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]",
"workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]",
"eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]",
"eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]",
- "metrics": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics', 'timeGrain', null(), 'enabled', true())))]",
- "logs": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs', 'enabled', true())))]",
"marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]",
"logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]"
},
@@ -1542,14 +1589,14 @@
"metadata": {
"description": "The public IP address of the public IP address resource."
},
- "value": "[if(contains(reference('publicIpAddress'), 'ipAddress'), reference('publicIpAddress').ipAddress, '')]"
+ "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]"
},
"location": {
"type": "string",
"metadata": {
"description": "The location the resource was deployed into."
},
- "value": "[reference('publicIpAddress', '2023-04-01', 'full').location]"
+ "value": "[reference('publicIpAddress', '2023-09-01', 'full').location]"
}
}
}
@@ -1567,8 +1614,8 @@
"mode": "Incremental",
"parameters": {
"name": "[if(contains(parameters('managementIPAddressObject'), 'name'), if(not(empty(parameters('managementIPAddressObject').name)), createObject('value', parameters('managementIPAddressObject').name), createObject('value', format('{0}-mip', parameters('name')))), createObject('value', format('{0}-mip', parameters('name'))))]",
- "publicIpPrefixResourceId": "[if(contains(parameters('managementIPAddressObject'), 'managementIPPrefixResourceId'), if(not(empty(parameters('managementIPAddressObject').publicIPPrefixResourceId)), createObject('value', parameters('managementIPAddressObject').publicIPPrefixResourceId), createObject('value', '')), createObject('value', ''))]",
- "publicIPAllocationMethod": "[if(contains(parameters('managementIPAddressObject'), 'managementIPAllocationMethod'), if(not(empty(parameters('managementIPAddressObject').publicIPAllocationMethod)), createObject('value', parameters('managementIPAddressObject').publicIPAllocationMethod), createObject('value', 'Static')), createObject('value', 'Static'))]",
+ "publicIpPrefixResourceId": "[if(contains(parameters('managementIPAddressObject'), 'managementIPPrefixResourceId'), if(not(empty(parameters('managementIPAddressObject').managementIPPrefixResourceId)), createObject('value', parameters('managementIPAddressObject').managementIPPrefixResourceId), createObject('value', '')), createObject('value', ''))]",
+ "publicIPAllocationMethod": "[if(contains(parameters('managementIPAddressObject'), 'managementIPAllocationMethod'), if(not(empty(parameters('managementIPAddressObject').managementIPAllocationMethod)), createObject('value', parameters('managementIPAddressObject').managementIPAllocationMethod), createObject('value', 'Static')), createObject('value', 'Static'))]",
"skuName": "[if(contains(parameters('managementIPAddressObject'), 'skuName'), if(not(empty(parameters('managementIPAddressObject').skuName)), createObject('value', parameters('managementIPAddressObject').skuName), createObject('value', 'Standard')), createObject('value', 'Standard'))]",
"skuTier": "[if(contains(parameters('managementIPAddressObject'), 'skuTier'), if(not(empty(parameters('managementIPAddressObject').skuTier)), createObject('value', parameters('managementIPAddressObject').skuTier), createObject('value', 'Regional')), createObject('value', 'Regional'))]",
"roleAssignments": "[if(contains(parameters('managementIPAddressObject'), 'roleAssignments'), if(not(empty(parameters('managementIPAddressObject').roleAssignments)), createObject('value', parameters('managementIPAddressObject').roleAssignments), createObject('value', createArray())), createObject('value', createArray()))]",
@@ -1595,8 +1642,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.23.1.45101",
- "templateHash": "3488076626994379707"
+ "version": "0.26.54.24096",
+ "templateHash": "4718335757080871925"
},
"name": "Public IP Addresses",
"description": "This module deploys a Public IP Address.",
@@ -1645,7 +1692,7 @@
"type": "string",
"nullable": true,
"metadata": {
- "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\""
+ "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"."
}
},
"conditionVersion": {
@@ -1739,11 +1786,14 @@
"type": "object",
"properties": {
"id": {
- "type": "string"
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address."
+ }
}
},
"metadata": {
- "description": "Required. The DDoS protection plan ID associated with the public IP address."
+ "description": "Required. The DDoS protection plan associated with the public IP address."
}
},
"protectionMode": {
@@ -1787,12 +1837,19 @@
"metadata": {
"description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs."
}
+ },
+ "enabled": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable or disable the category explicitly. Default is `true`."
+ }
}
}
},
"nullable": true,
"metadata": {
- "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection."
+ "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection."
}
},
"metricCategories": {
@@ -1805,12 +1862,19 @@
"metadata": {
"description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics."
}
+ },
+ "enabled": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable or disable the category explicitly. Default is `true`."
+ }
}
}
},
"nullable": true,
"metadata": {
- "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection."
+ "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection."
}
},
"logAnalyticsDestinationType": {
@@ -1891,7 +1955,19 @@
},
"zones": {
"type": "array",
- "nullable": true,
+ "items": {
+ "type": "int"
+ },
+ "defaultValue": [
+ 1,
+ 2,
+ 3
+ ],
+ "allowedValues": [
+ 1,
+ 2,
+ 3
+ ],
"metadata": {
"description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from."
}
@@ -2009,7 +2085,7 @@
"condition": "[parameters('enableTelemetry')]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2023-07-01",
- "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.2.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]",
+ "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]",
"properties": {
"mode": "Incremental",
"template": {
@@ -2027,7 +2103,7 @@
},
"publicIpAddress": {
"type": "Microsoft.Network/publicIPAddresses",
- "apiVersion": "2023-04-01",
+ "apiVersion": "2023-09-01",
"name": "[parameters('name')]",
"location": "[parameters('location')]",
"tags": "[parameters('tags')]",
@@ -2035,7 +2111,7 @@
"name": "[parameters('skuName')]",
"tier": "[parameters('skuTier')]"
},
- "zones": "[parameters('zones')]",
+ "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]",
"properties": {
"ddosSettings": "[parameters('ddosSettings')]",
"dnsSettings": "[parameters('dnsSettings')]",
@@ -2043,7 +2119,7 @@
"publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]",
"publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]",
"idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]",
- "ipTags": []
+ "ipTags": null
}
},
"publicIpAddress_lock": {
@@ -2092,12 +2168,30 @@
"scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]",
"name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]",
"properties": {
+ "copy": [
+ {
+ "name": "metrics",
+ "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]",
+ "input": {
+ "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]",
+ "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]",
+ "timeGrain": null
+ }
+ },
+ {
+ "name": "logs",
+ "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]",
+ "input": {
+ "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]",
+ "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]",
+ "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]"
+ }
+ }
+ ],
"storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]",
"workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]",
"eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]",
"eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]",
- "metrics": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics', 'timeGrain', null(), 'enabled', true())))]",
- "logs": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs', 'enabled', true())))]",
"marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]",
"logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]"
},
@@ -2133,14 +2227,14 @@
"metadata": {
"description": "The public IP address of the public IP address resource."
},
- "value": "[if(contains(reference('publicIpAddress'), 'ipAddress'), reference('publicIpAddress').ipAddress, '')]"
+ "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]"
},
"location": {
"type": "string",
"metadata": {
"description": "The location the resource was deployed into."
},
- "value": "[reference('publicIpAddress', '2023-04-01', 'full').location]"
+ "value": "[reference('publicIpAddress', '2023-09-01', 'full').location]"
}
}
}
diff --git a/avm/res/network/azure-firewall/tests/e2e/publicipprefix/dependencies.bicep b/avm/res/network/azure-firewall/tests/e2e/publicipprefix/dependencies.bicep
new file mode 100644
index 00000000000..fc6a6247519
--- /dev/null
+++ b/avm/res/network/azure-firewall/tests/e2e/publicipprefix/dependencies.bicep
@@ -0,0 +1,56 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Virtual Network to create.')
+param virtualNetworkName string
+
+@description('Required. The name of the Public IP Prefix to create.')
+param publicIPPrefixName string
+
+var addressPrefix = '10.0.0.0/16'
+
+resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: virtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ addressPrefix
+ ]
+ }
+ subnets: [
+ {
+ name: 'AzureFirewallSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 20, 0)
+ }
+ }
+ {
+ name: 'AzureFirewallManagementSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 20, 1)
+ }
+ }
+ ]
+ }
+}
+
+resource publicIPPrefix 'Microsoft.Network/publicIPPrefixes@2023-11-01' = {
+ name: publicIPPrefixName
+ location: location
+ sku: {
+ name: 'Standard'
+ tier: 'Regional'
+ }
+ properties: {
+ prefixLength: 30
+ publicIPAddressVersion: 'IPv4'
+ }
+ zones: []
+}
+
+@description('The resource ID of the created Virtual Network.')
+output virtualNetworkResourceId string = virtualNetwork.id
+
+@description('The resource ID of the created Public IP Prefix')
+output publicIPPrefixResourceId string = publicIPPrefix.id
diff --git a/avm/res/network/azure-firewall/tests/e2e/publicipprefix/main.test.bicep b/avm/res/network/azure-firewall/tests/e2e/publicipprefix/main.test.bicep
new file mode 100644
index 00000000000..b047e5199a3
--- /dev/null
+++ b/avm/res/network/azure-firewall/tests/e2e/publicipprefix/main.test.bicep
@@ -0,0 +1,75 @@
+targetScope = 'subscription'
+
+metadata name = 'Public-IP-Prefix'
+metadata description = 'This instance deploys the module and will use a public IP prefix.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.azurefirewalls-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'nafpip'
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '#_namePrefix_#'
+
+// ============ //
+// Dependencies //
+// ============ //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies'
+ params: {
+ location: resourceLocation
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ publicIPPrefixName: 'dep-${namePrefix}-pip-prefix-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: resourceLocation
+ name: '${namePrefix}${serviceShort}001'
+ virtualNetworkResourceId: nestedDependencies.outputs.virtualNetworkResourceId
+ publicIPAddressObject: {
+ name: 'publicIP01'
+ publicIPAllocationMethod: 'Static'
+ publicIPPrefixResourceId: nestedDependencies.outputs.publicIPPrefixResourceId
+ skuName: 'Standard'
+ skuTier: 'Regional'
+ }
+ azureSkuTier: 'Basic'
+ managementIPAddressObject: {
+ name: 'managementIP01'
+ managementIPAllocationMethod: 'Static'
+ managementIPPrefixResourceId: nestedDependencies.outputs.publicIPPrefixResourceId
+ skuName: 'Standard'
+ skuTier: 'Regional'
+ }
+ zones: []
+ }
+ }
+]
diff --git a/avm/res/network/azure-firewall/version.json b/avm/res/network/azure-firewall/version.json
index 1c035df49f2..b3d560b1ad0 100644
--- a/avm/res/network/azure-firewall/version.json
+++ b/avm/res/network/azure-firewall/version.json
@@ -1,7 +1,7 @@
{
"$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
- "version": "0.2",
+ "version": "0.3",
"pathFilters": [
"./main.json"
]
-}
\ No newline at end of file
+}
From ad5701ac9b1e38ec2f70af9668a31a9f3b1902f1 Mon Sep 17 00:00:00 2001
From: Alexander Sehr
Date: Thu, 16 May 2024 17:36:50 +0200
Subject: [PATCH 42/52] fix: Removed migration left-overs from Virtual-Hub
module (#1941)
## Description
Removed migration left-overs from Virtual-Hub module
## Pipeline Reference
| Pipeline |
| -------- |
|
[![avm.res.network.virtual-hub](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.virtual-hub.yml/badge.svg?branch=users%2Falsehr%2FoutdatedTelemetryReferences&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.virtual-hub.yml)
|
## Type of Change
- [ ] Update to CI Environment or utlities (Non-module effecting
changes)
- [x] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
---
.../virtual-hub/hub-route-table/README.md | 9 ---
.../virtual-hub/hub-route-table/main.bicep | 22 ------
.../virtual-hub/hub-route-table/main.json | 31 +-------
.../hub-virtual-network-connection/README.md | 9 ---
.../hub-virtual-network-connection/main.bicep | 22 ------
.../hub-virtual-network-connection/main.json | 31 +-------
avm/res/network/virtual-hub/main.bicep | 56 ++++++-------
avm/res/network/virtual-hub/main.json | 79 ++-----------------
8 files changed, 37 insertions(+), 222 deletions(-)
diff --git a/avm/res/network/virtual-hub/hub-route-table/README.md b/avm/res/network/virtual-hub/hub-route-table/README.md
index a7214ef715e..a2075469531 100644
--- a/avm/res/network/virtual-hub/hub-route-table/README.md
+++ b/avm/res/network/virtual-hub/hub-route-table/README.md
@@ -34,7 +34,6 @@ This module deploys a Virtual Hub Route Table.
| Parameter | Type | Description |
| :-- | :-- | :-- |
-| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. |
| [`labels`](#parameter-labels) | array | List of labels associated with this route table. |
| [`routes`](#parameter-routes) | array | List of all routes. |
@@ -52,14 +51,6 @@ The name of the parent virtual hub. Required if the template is used in a standa
- Required: Yes
- Type: string
-### Parameter: `enableTelemetry`
-
-Enable/Disable usage telemetry for module.
-
-- Required: No
-- Type: bool
-- Default: `True`
-
### Parameter: `labels`
List of labels associated with this route table.
diff --git a/avm/res/network/virtual-hub/hub-route-table/main.bicep b/avm/res/network/virtual-hub/hub-route-table/main.bicep
index 059374a501c..5fb0aeeffa7 100644
--- a/avm/res/network/virtual-hub/hub-route-table/main.bicep
+++ b/avm/res/network/virtual-hub/hub-route-table/main.bicep
@@ -14,28 +14,6 @@ param labels array = []
@description('Optional. List of all routes.')
param routes array = []
-@description('Optional. Enable/Disable usage telemetry for module.')
-param enableTelemetry bool = true
-
-resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' =
- if (enableTelemetry) {
- name: '46d3xbcp.res.network-virtualhub.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name), 0, 4)}'
- properties: {
- mode: 'Incremental'
- template: {
- '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
- contentVersion: '1.0.0.0'
- resources: []
- outputs: {
- telemetry: {
- type: 'String'
- value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
- }
- }
- }
- }
- }
-
resource virtualHub 'Microsoft.Network/virtualHubs@2022-11-01' existing = {
name: virtualHubName
}
diff --git a/avm/res/network/virtual-hub/hub-route-table/main.json b/avm/res/network/virtual-hub/hub-route-table/main.json
index 0188d1b4bc6..88f3d4c3e60 100644
--- a/avm/res/network/virtual-hub/hub-route-table/main.json
+++ b/avm/res/network/virtual-hub/hub-route-table/main.json
@@ -4,8 +4,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "8788910381196616673"
+ "version": "0.27.1.19265",
+ "templateHash": "13312295345359302348"
},
"name": "Virtual Hub Route Tables",
"description": "This module deploys a Virtual Hub Route Table.",
@@ -37,36 +37,9 @@
"metadata": {
"description": "Optional. List of all routes."
}
- },
- "enableTelemetry": {
- "type": "bool",
- "defaultValue": true,
- "metadata": {
- "description": "Optional. Enable/Disable usage telemetry for module."
- }
}
},
"resources": [
- {
- "condition": "[parameters('enableTelemetry')]",
- "type": "Microsoft.Resources/deployments",
- "apiVersion": "2021-04-01",
- "name": "[format('46d3xbcp.res.network-virtualhub.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]",
- "properties": {
- "mode": "Incremental",
- "template": {
- "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
- "contentVersion": "1.0.0.0",
- "resources": [],
- "outputs": {
- "telemetry": {
- "type": "String",
- "value": "For more information, see https://aka.ms/avm/TelemetryInfo"
- }
- }
- }
- }
- },
{
"type": "Microsoft.Network/virtualHubs/hubRouteTables",
"apiVersion": "2022-11-01",
diff --git a/avm/res/network/virtual-hub/hub-virtual-network-connection/README.md b/avm/res/network/virtual-hub/hub-virtual-network-connection/README.md
index da9f0e13884..43d153f7a76 100644
--- a/avm/res/network/virtual-hub/hub-virtual-network-connection/README.md
+++ b/avm/res/network/virtual-hub/hub-virtual-network-connection/README.md
@@ -36,7 +36,6 @@ This module deploys a Virtual Hub Virtual Network Connection.
| Parameter | Type | Description |
| :-- | :-- | :-- |
| [`enableInternetSecurity`](#parameter-enableinternetsecurity) | bool | Enable internet security. |
-| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. |
| [`routingConfiguration`](#parameter-routingconfiguration) | object | Routing Configuration indicating the associated and propagated route tables for this connection. |
### Parameter: `name`
@@ -68,14 +67,6 @@ Enable internet security.
- Type: bool
- Default: `True`
-### Parameter: `enableTelemetry`
-
-Enable/Disable usage telemetry for module.
-
-- Required: No
-- Type: bool
-- Default: `True`
-
### Parameter: `routingConfiguration`
Routing Configuration indicating the associated and propagated route tables for this connection.
diff --git a/avm/res/network/virtual-hub/hub-virtual-network-connection/main.bicep b/avm/res/network/virtual-hub/hub-virtual-network-connection/main.bicep
index 3bc4c7e4ecb..f9751898ec2 100644
--- a/avm/res/network/virtual-hub/hub-virtual-network-connection/main.bicep
+++ b/avm/res/network/virtual-hub/hub-virtual-network-connection/main.bicep
@@ -17,28 +17,6 @@ param remoteVirtualNetworkId string
@description('Optional. Routing Configuration indicating the associated and propagated route tables for this connection.')
param routingConfiguration object = {}
-@description('Optional. Enable/Disable usage telemetry for module.')
-param enableTelemetry bool = true
-
-resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' =
- if (enableTelemetry) {
- name: '46d3xbcp.res.network-virtualhub.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name), 0, 4)}'
- properties: {
- mode: 'Incremental'
- template: {
- '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
- contentVersion: '1.0.0.0'
- resources: []
- outputs: {
- telemetry: {
- type: 'String'
- value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
- }
- }
- }
- }
- }
-
resource virtualHub 'Microsoft.Network/virtualHubs@2022-11-01' existing = {
name: virtualHubName
}
diff --git a/avm/res/network/virtual-hub/hub-virtual-network-connection/main.json b/avm/res/network/virtual-hub/hub-virtual-network-connection/main.json
index 47c9ebd0257..82bb00a3568 100644
--- a/avm/res/network/virtual-hub/hub-virtual-network-connection/main.json
+++ b/avm/res/network/virtual-hub/hub-virtual-network-connection/main.json
@@ -4,8 +4,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "10987823499253979342"
+ "version": "0.27.1.19265",
+ "templateHash": "449308154693946367"
},
"name": "Virtual Hub Virtual Network Connections",
"description": "This module deploys a Virtual Hub Virtual Network Connection.",
@@ -43,36 +43,9 @@
"metadata": {
"description": "Optional. Routing Configuration indicating the associated and propagated route tables for this connection."
}
- },
- "enableTelemetry": {
- "type": "bool",
- "defaultValue": true,
- "metadata": {
- "description": "Optional. Enable/Disable usage telemetry for module."
- }
}
},
"resources": [
- {
- "condition": "[parameters('enableTelemetry')]",
- "type": "Microsoft.Resources/deployments",
- "apiVersion": "2021-04-01",
- "name": "[format('46d3xbcp.res.network-virtualhub.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]",
- "properties": {
- "mode": "Incremental",
- "template": {
- "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
- "contentVersion": "1.0.0.0",
- "resources": [],
- "outputs": {
- "telemetry": {
- "type": "String",
- "value": "For more information, see https://aka.ms/avm/TelemetryInfo"
- }
- }
- }
- }
- },
{
"type": "Microsoft.Network/virtualHubs/hubVirtualNetworkConnections",
"apiVersion": "2022-11-01",
diff --git a/avm/res/network/virtual-hub/main.bicep b/avm/res/network/virtual-hub/main.bicep
index 58c7ad44d29..138902ccaa3 100644
--- a/avm/res/network/virtual-hub/main.bicep
+++ b/avm/res/network/virtual-hub/main.bicep
@@ -79,29 +79,26 @@ param lock lockType
@description('Optional. Enable/Disable usage telemetry for module.')
param enableTelemetry bool = true
-var enableReferencedModulesTelemetry = false
-
-resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' =
- if (enableTelemetry) {
- name: take(
- '46d3xbcp.res.network-virtualhub.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}',
- 64
- )
- properties: {
- mode: 'Incremental'
- template: {
- '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
- contentVersion: '1.0.0.0'
- resources: []
- outputs: {
- telemetry: {
- type: 'String'
- value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
- }
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: take(
+ '46d3xbcp.res.network-virtualhub.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}',
+ 64
+ )
+ properties: {
+ mode: 'Incremental'
+ template: {
+ '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
+ contentVersion: '1.0.0.0'
+ resources: []
+ outputs: {
+ telemetry: {
+ type: 'String'
+ value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
}
}
}
}
+}
resource virtualHub 'Microsoft.Network/virtualHubs@2022-11-01' = {
name: name
@@ -152,17 +149,16 @@ resource virtualHub 'Microsoft.Network/virtualHubs@2022-11-01' = {
}
}
-resource virtualHub_lock 'Microsoft.Authorization/locks@2020-05-01' =
- if (!empty(lock ?? {}) && lock.?kind != 'None') {
- name: lock.?name ?? 'lock-${name}'
- properties: {
- level: lock.?kind ?? ''
- notes: lock.?kind == 'CanNotDelete'
- ? 'Cannot delete resource or child resources.'
- : 'Cannot delete or modify the resource or child resources.'
- }
- scope: virtualHub
+resource virtualHub_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
+ name: lock.?name ?? 'lock-${name}'
+ properties: {
+ level: lock.?kind ?? ''
+ notes: lock.?kind == 'CanNotDelete'
+ ? 'Cannot delete resource or child resources.'
+ : 'Cannot delete or modify the resource or child resources.'
}
+ scope: virtualHub
+}
module virtualHub_routeTables 'hub-route-table/main.bicep' = [
for (routeTable, index) in hubRouteTables: {
@@ -172,7 +168,6 @@ module virtualHub_routeTables 'hub-route-table/main.bicep' = [
name: routeTable.name
labels: contains(routeTable, 'labels') ? routeTable.labels : []
routes: contains(routeTable, 'routes') ? routeTable.routes : []
- enableTelemetry: enableReferencedModulesTelemetry
}
}
]
@@ -190,7 +185,6 @@ module virtualHub_hubVirtualNetworkConnections 'hub-virtual-network-connection/m
routingConfiguration: contains(virtualNetworkConnection, 'routingConfiguration')
? virtualNetworkConnection.routingConfiguration
: {}
- enableTelemetry: enableReferencedModulesTelemetry
}
dependsOn: [
virtualHub_routeTables
diff --git a/avm/res/network/virtual-hub/main.json b/avm/res/network/virtual-hub/main.json
index 5b75ef5cc54..1178d893c23 100644
--- a/avm/res/network/virtual-hub/main.json
+++ b/avm/res/network/virtual-hub/main.json
@@ -5,8 +5,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "2150058177195720553"
+ "version": "0.27.1.19265",
+ "templateHash": "7421243394882467292"
},
"name": "Virtual Hubs",
"description": "This module deploys a Virtual Hub.\nIf you are planning to deploy a Secure Virtual Hub (with an Azure Firewall integrated), please refer to the Azure Firewall module.",
@@ -201,9 +201,6 @@
}
}
},
- "variables": {
- "enableReferencedModulesTelemetry": false
- },
"resources": {
"avmTelemetry": {
"condition": "[parameters('enableTelemetry')]",
@@ -286,10 +283,7 @@
"value": "[parameters('hubRouteTables')[copyIndex()].name]"
},
"labels": "[if(contains(parameters('hubRouteTables')[copyIndex()], 'labels'), createObject('value', parameters('hubRouteTables')[copyIndex()].labels), createObject('value', createArray()))]",
- "routes": "[if(contains(parameters('hubRouteTables')[copyIndex()], 'routes'), createObject('value', parameters('hubRouteTables')[copyIndex()].routes), createObject('value', createArray()))]",
- "enableTelemetry": {
- "value": "[variables('enableReferencedModulesTelemetry')]"
- }
+ "routes": "[if(contains(parameters('hubRouteTables')[copyIndex()], 'routes'), createObject('value', parameters('hubRouteTables')[copyIndex()].routes), createObject('value', createArray()))]"
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
@@ -297,8 +291,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "8788910381196616673"
+ "version": "0.27.1.19265",
+ "templateHash": "13312295345359302348"
},
"name": "Virtual Hub Route Tables",
"description": "This module deploys a Virtual Hub Route Table.",
@@ -330,36 +324,9 @@
"metadata": {
"description": "Optional. List of all routes."
}
- },
- "enableTelemetry": {
- "type": "bool",
- "defaultValue": true,
- "metadata": {
- "description": "Optional. Enable/Disable usage telemetry for module."
- }
}
},
"resources": [
- {
- "condition": "[parameters('enableTelemetry')]",
- "type": "Microsoft.Resources/deployments",
- "apiVersion": "2021-04-01",
- "name": "[format('46d3xbcp.res.network-virtualhub.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]",
- "properties": {
- "mode": "Incremental",
- "template": {
- "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
- "contentVersion": "1.0.0.0",
- "resources": [],
- "outputs": {
- "telemetry": {
- "type": "String",
- "value": "For more information, see https://aka.ms/avm/TelemetryInfo"
- }
- }
- }
- }
- },
{
"type": "Microsoft.Network/virtualHubs/hubRouteTables",
"apiVersion": "2022-11-01",
@@ -423,10 +390,7 @@
"remoteVirtualNetworkId": {
"value": "[parameters('hubVirtualNetworkConnections')[copyIndex()].remoteVirtualNetworkId]"
},
- "routingConfiguration": "[if(contains(parameters('hubVirtualNetworkConnections')[copyIndex()], 'routingConfiguration'), createObject('value', parameters('hubVirtualNetworkConnections')[copyIndex()].routingConfiguration), createObject('value', createObject()))]",
- "enableTelemetry": {
- "value": "[variables('enableReferencedModulesTelemetry')]"
- }
+ "routingConfiguration": "[if(contains(parameters('hubVirtualNetworkConnections')[copyIndex()], 'routingConfiguration'), createObject('value', parameters('hubVirtualNetworkConnections')[copyIndex()].routingConfiguration), createObject('value', createObject()))]"
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
@@ -434,8 +398,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "10987823499253979342"
+ "version": "0.27.1.19265",
+ "templateHash": "449308154693946367"
},
"name": "Virtual Hub Virtual Network Connections",
"description": "This module deploys a Virtual Hub Virtual Network Connection.",
@@ -473,36 +437,9 @@
"metadata": {
"description": "Optional. Routing Configuration indicating the associated and propagated route tables for this connection."
}
- },
- "enableTelemetry": {
- "type": "bool",
- "defaultValue": true,
- "metadata": {
- "description": "Optional. Enable/Disable usage telemetry for module."
- }
}
},
"resources": [
- {
- "condition": "[parameters('enableTelemetry')]",
- "type": "Microsoft.Resources/deployments",
- "apiVersion": "2021-04-01",
- "name": "[format('46d3xbcp.res.network-virtualhub.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]",
- "properties": {
- "mode": "Incremental",
- "template": {
- "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
- "contentVersion": "1.0.0.0",
- "resources": [],
- "outputs": {
- "telemetry": {
- "type": "String",
- "value": "For more information, see https://aka.ms/avm/TelemetryInfo"
- }
- }
- }
- }
- },
{
"type": "Microsoft.Network/virtualHubs/hubVirtualNetworkConnections",
"apiVersion": "2022-11-01",
From a11ba135a25ede00de5c5296c04d39ee842aff3c Mon Sep 17 00:00:00 2001
From: hundredacres
Date: Fri, 17 May 2024 02:49:46 -0700
Subject: [PATCH 43/52] fix: Minor edits to get version publishing working
(#1953)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Description
Minor change to resolve version publishing issues.
## Pipeline Reference
| Pipeline |
| -------- |
|
[![avm.res.network.azure-firewall](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml/badge.svg?branch=fix%2Fazure_firewall%2Fversion)](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml)
|
## Type of Change
- [X] Update to CI Environment or utlities (Non-module effecting
changes)
- [ ] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [X] Update to documentation
## Checklist
- [X] I'm sure there are no other open Pull Requests for the same
update/change
- [X] I have run `Set-AVMModule` locally to generate the supporting
module files.
- [X] My corresponding pipelines / checks run clean and green without
any errors or warnings
---------
Co-authored-by: Máté Barabás
Co-authored-by: Rainer Halanek <61878316+rahalan@users.noreply.github.com>
Co-authored-by: JFolberth
---
avm/res/network/azure-firewall/README.md | 135 +++-------------------
avm/res/network/azure-firewall/main.bicep | 6 +-
avm/res/network/azure-firewall/main.json | 8 +-
3 files changed, 26 insertions(+), 123 deletions(-)
diff --git a/avm/res/network/azure-firewall/README.md b/avm/res/network/azure-firewall/README.md
index e821af3f602..33d03091d5a 100644
--- a/avm/res/network/azure-firewall/README.md
+++ b/avm/res/network/azure-firewall/README.md
@@ -29,113 +29,16 @@ The following section provides usage examples for the module, which were used to
>**Note**: To reference the module, please use the following syntax `br/public:avm/res/network/azure-firewall:`.
-- [Issue-1867](#example-1-issue-1867)
-- [Add-PIP](#example-2-add-pip)
-- [Custom-PIP](#example-3-custom-pip)
-- [Using only defaults](#example-4-using-only-defaults)
-- [Hub-commom](#example-5-hub-commom)
-- [Hub-min](#example-6-hub-min)
-- [Using large parameter set](#example-7-using-large-parameter-set)
-- [Public-IP-Prefix](#example-8-public-ip-prefix)
-- [WAF-aligned](#example-9-waf-aligned)
+- [Add-PIP](#example-1-add-pip)
+- [Custom-PIP](#example-2-custom-pip)
+- [Using only defaults](#example-3-using-only-defaults)
+- [Hub-commom](#example-4-hub-commom)
+- [Hub-min](#example-5-hub-min)
+- [Using large parameter set](#example-6-using-large-parameter-set)
+- [Public-IP-Prefix](#example-7-public-ip-prefix)
+- [WAF-aligned](#example-8-waf-aligned)
-### Example 1: _Issue-1867_
-
-Validating reported bug 1867
-
-
-
-
-via Bicep module
-
-```bicep
-module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
- name: 'azureFirewallDeployment'
- params: {
- // Required parameters
- name: 'nafcustom001'
- // Non-required parameters
- azureSkuTier: 'Basic'
- firewallPolicyId: ''
- location: ''
- managementIPAddressObject: {
- managementIPAllocationMethod: 'Static'
- managementIPPrefixResourceId: ''
- name: 'managementIP01'
- skuName: 'Standard'
- skuTier: 'Regional'
- }
- publicIPAddressObject: {
- name: 'publicIP01'
- publicIPAllocationMethod: 'Static'
- publicIPPrefixResourceId: ''
- skuName: 'Standard'
- skuTier: 'Regional'
- }
- virtualNetworkResourceId: ''
- zones: []
- }
-}
-```
-
-
-
-
-
-
-via JSON Parameter file
-
-```json
-{
- "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
- "contentVersion": "1.0.0.0",
- "parameters": {
- // Required parameters
- "name": {
- "value": "nafcustom001"
- },
- // Non-required parameters
- "azureSkuTier": {
- "value": "Basic"
- },
- "firewallPolicyId": {
- "value": ""
- },
- "location": {
- "value": ""
- },
- "managementIPAddressObject": {
- "value": {
- "managementIPAllocationMethod": "Static",
- "managementIPPrefixResourceId": "",
- "name": "managementIP01",
- "skuName": "Standard",
- "skuTier": "Regional"
- }
- },
- "publicIPAddressObject": {
- "value": {
- "name": "publicIP01",
- "publicIPAllocationMethod": "Static",
- "publicIPPrefixResourceId": "",
- "skuName": "Standard",
- "skuTier": "Regional"
- }
- },
- "virtualNetworkResourceId": {
- "value": ""
- },
- "zones": {
- "value": []
- }
- }
-}
-```
-
-
-
-
-### Example 2: _Add-PIP_
+### Example 1: _Add-PIP_
This instance deploys the module and attaches an existing public IP address.
@@ -227,7 +130,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 3: _Custom-PIP_
+### Example 2: _Custom-PIP_
This instance deploys the module and will create a public IP address.
@@ -337,7 +240,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 4: _Using only defaults_
+### Example 3: _Using only defaults_
This instance deploys the module with the minimum set of required parameters.
@@ -389,7 +292,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 5: _Hub-commom_
+### Example 4: _Hub-commom_
This instance deploys the module a vWAN in a typical hub setting.
@@ -457,7 +360,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 6: _Hub-min_
+### Example 5: _Hub-min_
This instance deploys the module a vWAN minimum hub setting.
@@ -521,7 +424,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 7: _Using large parameter set_
+### Example 6: _Using large parameter set_
This instance deploys the module with most of its features enabled.
@@ -875,7 +778,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 8: _Public-IP-Prefix_
+### Example 7: _Public-IP-Prefix_
This instance deploys the module and will use a public IP prefix.
@@ -967,7 +870,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {
-### Example 9: _WAF-aligned_
+### Example 8: _WAF-aligned_
This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
@@ -2234,12 +2137,12 @@ Zone numbers e.g. 1,2,3.
| Output | Type | Description |
| :-- | :-- | :-- |
-| `applicationRuleCollections` | array | List of Application Rule Collections. |
+| `applicationRuleCollections` | array | List of Application Rule Collections used by Azure Firewall. |
| `ipConfAzureFirewallSubnet` | object | The Public IP configuration object for the Azure Firewall Subnet. |
| `location` | string | The location the resource was deployed into. |
| `name` | string | The name of the Azure Firewall. |
-| `natRuleCollections` | array | Collection of NAT rule collections used by Azure Firewall. |
-| `networkRuleCollections` | array | List of Network Rule Collections. |
+| `natRuleCollections` | array | List of NAT rule collections used by Azure Firewall. |
+| `networkRuleCollections` | array | List of Network Rule Collections used by Azure Firewall. |
| `privateIp` | string | The private IP of the Azure firewall. |
| `resourceGroupName` | string | The resource group the Azure firewall was deployed into. |
| `resourceId` | string | The resource ID of the Azure Firewall. |
diff --git a/avm/res/network/azure-firewall/main.bicep b/avm/res/network/azure-firewall/main.bicep
index 54d4661cf2c..38c7819be33 100644
--- a/avm/res/network/azure-firewall/main.bicep
+++ b/avm/res/network/azure-firewall/main.bicep
@@ -372,13 +372,13 @@ output ipConfAzureFirewallSubnet object = contains(azureFirewall.properties, 'ip
? azureFirewall.properties.ipConfigurations[0]
: {}
-@description('List of Application Rule Collections.')
+@description('List of Application Rule Collections used by Azure Firewall.')
output applicationRuleCollections array = applicationRuleCollections ?? []
-@description('List of Network Rule Collections.')
+@description('List of Network Rule Collections used by Azure Firewall.')
output networkRuleCollections array = networkRuleCollections ?? []
-@description('Collection of NAT rule collections used by Azure Firewall.')
+@description('List of NAT rule collections used by Azure Firewall.')
output natRuleCollections array = natRuleCollections ?? []
@description('The location the resource was deployed into.')
diff --git a/avm/res/network/azure-firewall/main.json b/avm/res/network/azure-firewall/main.json
index 47361a85e2a..8ff6f7c228b 100644
--- a/avm/res/network/azure-firewall/main.json
+++ b/avm/res/network/azure-firewall/main.json
@@ -6,7 +6,7 @@
"_generator": {
"name": "bicep",
"version": "0.27.1.19265",
- "templateHash": "5299175231300175796"
+ "templateHash": "18119107920030966169"
},
"name": "Azure Firewalls",
"description": "This module deploys an Azure Firewall.",
@@ -2280,21 +2280,21 @@
"applicationRuleCollections": {
"type": "array",
"metadata": {
- "description": "List of Application Rule Collections."
+ "description": "List of Application Rule Collections used by Azure Firewall."
},
"value": "[coalesce(parameters('applicationRuleCollections'), createArray())]"
},
"networkRuleCollections": {
"type": "array",
"metadata": {
- "description": "List of Network Rule Collections."
+ "description": "List of Network Rule Collections used by Azure Firewall."
},
"value": "[coalesce(parameters('networkRuleCollections'), createArray())]"
},
"natRuleCollections": {
"type": "array",
"metadata": {
- "description": "Collection of NAT rule collections used by Azure Firewall."
+ "description": "List of NAT rule collections used by Azure Firewall."
},
"value": "[coalesce(parameters('natRuleCollections'), createArray())]"
},
From ef6a568da2f255adaa69f1c34fbdb7a7bcc0971b Mon Sep 17 00:00:00 2001
From: hundredacres
Date: Sun, 19 May 2024 00:16:33 -0700
Subject: [PATCH 44/52] fix: Adding support for managedidentity (#1972)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Description
Update API version to support adding an identity to the resource.
Fixes #1922
Closes #1922
## Pipeline Reference
| Pipeline |
| -------- |
|
[![avm.res.app.managed-environment](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.app.managed-environment.yml/badge.svg?branch=fix%2Fissue%2F1922)](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.app.managed-environment.yml)
|
## Type of Change
- [ ] Update to CI Environment or utlities (Non-module effecting
changes)
- [X] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [X] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [X] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
## Checklist
- [X] I'm sure there are no other open Pull Requests for the same
update/change
- [X] I have run `Set-AVMModule` locally to generate the supporting
module files.
- [X] My corresponding pipelines / checks run clean and green without
any errors or warnings
---------
Co-authored-by: Máté Barabás
Co-authored-by: Rainer Halanek <61878316+rahalan@users.noreply.github.com>
Co-authored-by: JFolberth
---
avm/res/app/managed-environment/README.md | 46 +++++++++++++++++-
avm/res/app/managed-environment/main.bicep | 33 ++++++++++++-
avm/res/app/managed-environment/main.json | 47 +++++++++++++++++--
.../tests/e2e/max/dependencies.bicep | 3 ++
.../tests/e2e/max/main.test.bicep | 6 +++
avm/res/app/managed-environment/version.json | 2 +-
6 files changed, 130 insertions(+), 7 deletions(-)
diff --git a/avm/res/app/managed-environment/README.md b/avm/res/app/managed-environment/README.md
index feac29db4e6..935d035505c 100644
--- a/avm/res/app/managed-environment/README.md
+++ b/avm/res/app/managed-environment/README.md
@@ -15,7 +15,7 @@ This module deploys an App Managed Environment (also known as a Container App En
| Resource Type | API Version |
| :-- | :-- |
-| `Microsoft.App/managedEnvironments` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2023-05-01/managedEnvironments) |
+| `Microsoft.App/managedEnvironments` | [2023-11-02-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2023-11-02-preview/managedEnvironments) |
| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) |
| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) |
@@ -151,6 +151,12 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:'
kind: 'CanNotDelete'
name: 'myCustomLockName'
}
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourceIds: [
+ ''
+ ]
+ }
platformReservedCidr: '172.17.17.0/24'
platformReservedDnsIP: '172.17.17.17'
roleAssignments: [
@@ -227,6 +233,14 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:'
"name": "myCustomLockName"
}
},
+ "managedIdentities": {
+ "value": {
+ "systemAssigned": true,
+ "userAssignedResourceIds": [
+ ""
+ ]
+ }
+ },
"platformReservedCidr": {
"value": "172.17.17.0/24"
},
@@ -460,6 +474,7 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:'
| [`location`](#parameter-location) | string | Location for all Resources. |
| [`lock`](#parameter-lock) | object | The lock settings of the service. |
| [`logsDestination`](#parameter-logsdestination) | string | Logs destination. |
+| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. |
| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. |
| [`tags`](#parameter-tags) | object | Tags of the resource. |
| [`zoneRedundant`](#parameter-zoneredundant) | bool | Whether or not this Managed Environment is zone-redundant. |
@@ -634,6 +649,34 @@ Logs destination.
- Type: string
- Default: `'log-analytics'`
+### Parameter: `managedIdentities`
+
+The managed identity definition for this resource.
+
+- Required: No
+- Type: object
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. |
+| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. |
+
+### Parameter: `managedIdentities.systemAssigned`
+
+Enables system assigned managed identity on the resource.
+
+- Required: No
+- Type: bool
+
+### Parameter: `managedIdentities.userAssignedResourceIds`
+
+The resource ID(s) to assign to the resource.
+
+- Required: No
+- Type: array
+
### Parameter: `roleAssignments`
Array of role assignments to create.
@@ -748,6 +791,7 @@ Whether or not this Managed Environment is zone-redundant.
| `name` | string | The name of the Managed Environment. |
| `resourceGroupName` | string | The name of the resource group the Managed Environment was deployed into. |
| `resourceId` | string | The resource ID of the Managed Environment. |
+| `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. |
## Cross-referenced modules
diff --git a/avm/res/app/managed-environment/main.bicep b/avm/res/app/managed-environment/main.bicep
index 6ed8dc58fd8..c609ded7159 100644
--- a/avm/res/app/managed-environment/main.bicep
+++ b/avm/res/app/managed-environment/main.bicep
@@ -14,6 +14,9 @@ param location string = resourceGroup().location
@description('Optional. Tags of the resource.')
param tags object?
+@description('Optional. The managed identity definition for this resource.')
+param managedIdentities managedIdentitiesType
+
@description('Optional. Array of role assignments to create.')
param roleAssignments roleAssignmentType
@@ -69,6 +72,22 @@ param workloadProfiles array = []
@description('Conditional. Name of the infrastructure resource group. If not provided, it will be set with a default value. Required if zoneRedundant is set to true to make the resource WAF compliant.')
param infrastructureResourceGroupName string = take('ME_${name}', 63)
+var formattedUserAssignedIdentities = reduce(
+ map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }),
+ {},
+ (cur, next) => union(cur, next)
+) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} }
+
+
+var identity = !empty(managedIdentities)
+ ? {
+ type: (managedIdentities.?systemAssigned ?? false)
+ ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned')
+ : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None')
+ userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null
+ }
+ : null
+
var builtInRoleNames = {
Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')
@@ -108,10 +127,11 @@ resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06
scope: resourceGroup(split(logAnalyticsWorkspaceResourceId, '/')[2], split(logAnalyticsWorkspaceResourceId, '/')[4])
}
-resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
+resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-11-02-preview' = {
name: name
location: location
tags: tags
+ identity: identity
properties: {
appLogsConfiguration: {
destination: logsDestination
@@ -184,6 +204,9 @@ output name string = managedEnvironment.name
@description('The resource ID of the Managed Environment.')
output resourceId string = managedEnvironment.id
+@description('The principal ID of the system assigned identity.')
+output systemAssignedMIPrincipalId string = managedEnvironment.?identity.?principalId ?? ''
+
@description('The Default domain of the Managed Environment.')
output defaultDomain string = managedEnvironment.properties.defaultDomain
@@ -191,6 +214,14 @@ output defaultDomain string = managedEnvironment.properties.defaultDomain
// Definitions //
// =============== //
+type managedIdentitiesType = {
+ @description('Optional. Enables system assigned managed identity on the resource.')
+ systemAssigned: bool?
+
+ @description('Optional. The resource ID(s) to assign to the resource.')
+ userAssignedResourceIds: string[]?
+}?
+
type lockType = {
@description('Optional. Specify the name of lock.')
name: string?
diff --git a/avm/res/app/managed-environment/main.json b/avm/res/app/managed-environment/main.json
index 13537daa444..2eafa79846d 100644
--- a/avm/res/app/managed-environment/main.json
+++ b/avm/res/app/managed-environment/main.json
@@ -5,14 +5,37 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.170.59819",
- "templateHash": "17801833041272323788"
+ "version": "0.27.1.19265",
+ "templateHash": "15666134926564437864"
},
"name": "App ManagedEnvironments",
"description": "This module deploys an App Managed Environment (also known as a Container App Environment).",
"owner": "Azure/module-maintainers"
},
"definitions": {
+ "managedIdentitiesType": {
+ "type": "object",
+ "properties": {
+ "systemAssigned": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enables system assigned managed identity on the resource."
+ }
+ },
+ "userAssignedResourceIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID(s) to assign to the resource."
+ }
+ }
+ },
+ "nullable": true
+ },
"lockType": {
"type": "object",
"properties": {
@@ -132,6 +155,12 @@
"description": "Optional. Tags of the resource."
}
},
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentitiesType",
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource."
+ }
+ },
"roleAssignments": {
"$ref": "#/definitions/roleAssignmentType",
"metadata": {
@@ -251,6 +280,8 @@
}
},
"variables": {
+ "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]",
+ "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]",
"builtInRoleNames": {
"Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
"Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
@@ -291,10 +322,11 @@
},
"managedEnvironment": {
"type": "Microsoft.App/managedEnvironments",
- "apiVersion": "2023-05-01",
+ "apiVersion": "2023-11-02-preview",
"name": "[parameters('name')]",
"location": "[parameters('location')]",
"tags": "[parameters('tags')]",
+ "identity": "[variables('identity')]",
"properties": {
"appLogsConfiguration": {
"destination": "[parameters('logsDestination')]",
@@ -375,7 +407,7 @@
"metadata": {
"description": "The location the resource was deployed into."
},
- "value": "[reference('managedEnvironment', '2023-05-01', 'full').location]"
+ "value": "[reference('managedEnvironment', '2023-11-02-preview', 'full').location]"
},
"name": {
"type": "string",
@@ -391,6 +423,13 @@
},
"value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]"
},
+ "systemAssignedMIPrincipalId": {
+ "type": "string",
+ "metadata": {
+ "description": "The principal ID of the system assigned identity."
+ },
+ "value": "[coalesce(tryGet(tryGet(reference('managedEnvironment', '2023-11-02-preview', 'full'), 'identity'), 'principalId'), '')]"
+ },
"defaultDomain": {
"type": "string",
"metadata": {
diff --git a/avm/res/app/managed-environment/tests/e2e/max/dependencies.bicep b/avm/res/app/managed-environment/tests/e2e/max/dependencies.bicep
index ca9894d9dd4..ebeb013d5a3 100644
--- a/avm/res/app/managed-environment/tests/e2e/max/dependencies.bicep
+++ b/avm/res/app/managed-environment/tests/e2e/max/dependencies.bicep
@@ -67,3 +67,6 @@ output subnetResourceId string = virtualNetwork.properties.subnets[0].id
@description('The principal ID of the created Managed Identity.')
output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
diff --git a/avm/res/app/managed-environment/tests/e2e/max/main.test.bicep b/avm/res/app/managed-environment/tests/e2e/max/main.test.bicep
index c48a95ec30d..919c7ab40f3 100644
--- a/avm/res/app/managed-environment/tests/e2e/max/main.test.bicep
+++ b/avm/res/app/managed-environment/tests/e2e/max/main.test.bicep
@@ -68,6 +68,12 @@ module testDeployment '../../../main.bicep' = [
platformReservedDnsIP: '172.17.17.17'
infrastructureSubnetId: nestedDependencies.outputs.subnetResourceId
infrastructureResourceGroupName: 'me-${resourceGroupName}'
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourceIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
roleAssignments: [
{
roleDefinitionIdOrName: 'Owner'
diff --git a/avm/res/app/managed-environment/version.json b/avm/res/app/managed-environment/version.json
index 96236a61ba0..04a0dd1a80d 100644
--- a/avm/res/app/managed-environment/version.json
+++ b/avm/res/app/managed-environment/version.json
@@ -1,6 +1,6 @@
{
"$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
- "version": "0.4",
+ "version": "0.5",
"pathFilters": [
"./main.json"
]
From 86cfa8621eec707cba22912affb179b641960b92 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?=
Date: Sun, 19 May 2024 09:45:33 +0200
Subject: [PATCH 45/52] chore: `res/app/job` - not orphaned anymore (#1969)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Description
I removed the orphaned hint.
## Pipeline Reference
| Pipeline |
| -------- |
|
[![avm.res.app.job](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.app.job.yml/badge.svg?branch=app-job-owner-change)](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.app.job.yml)
|
The pipeline fails, because the module is marked as orphaned and is
expecting the orphaned.md, which I removed ¯\\_(ツ)_/¯
## Type of Change
- [ ] Update to CI Environment or utlities (Non-module effecting
changes)
- [x] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [x] Update to documentation
## Checklist
- [x] I'm sure there are no other open Pull Requests for the same
update/change
- [x] I have run `Set-AVMModule` locally to generate the supporting
module files.
- [x] My corresponding pipelines / checks run clean and green without
any errors or warnings
---
avm/res/app/job/ORPHANED.md | 4 ----
avm/res/app/job/README.md | 5 -----
avm/res/app/job/main.json | 4 ++--
3 files changed, 2 insertions(+), 11 deletions(-)
delete mode 100644 avm/res/app/job/ORPHANED.md
diff --git a/avm/res/app/job/ORPHANED.md b/avm/res/app/job/ORPHANED.md
deleted file mode 100644
index ef8fa911d2b..00000000000
--- a/avm/res/app/job/ORPHANED.md
+++ /dev/null
@@ -1,4 +0,0 @@
-⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️
-
-- Only security and bug fixes are being handled by the AVM core team at present.
-- If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)!
\ No newline at end of file
diff --git a/avm/res/app/job/README.md b/avm/res/app/job/README.md
index 93e9c6e55a0..ccca0ee8107 100644
--- a/avm/res/app/job/README.md
+++ b/avm/res/app/job/README.md
@@ -1,10 +1,5 @@
# Container App Jobs `[Microsoft.App/jobs]`
-> ⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️
->
-> - Only security and bug fixes are being handled by the AVM core team at present.
-> - If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)!
-
This module deploys a Container App Job.
## Navigation
diff --git a/avm/res/app/job/main.json b/avm/res/app/job/main.json
index 6d8ee06c25b..58fed4a24e2 100644
--- a/avm/res/app/job/main.json
+++ b/avm/res/app/job/main.json
@@ -5,8 +5,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.27.1.19265",
- "templateHash": "11649443218681434280"
+ "version": "0.26.170.59819",
+ "templateHash": "3096359783958038878"
},
"name": "Container App Jobs",
"description": "This module deploys a Container App Job.",
From bc8d57bf4981567080523edaca9e3b98129fdbe2 Mon Sep 17 00:00:00 2001
From: Alexander Sehr
Date: Mon, 20 May 2024 11:04:51 +0200
Subject: [PATCH 46/52] fix: Added `-Culture 'en-US'` to all `Sort-Object`
operations (#1976)
## Description
- Added `-Culture 'en-US'` to all `Sort-Object` operations to mitigate
issues some contributors with a different configured culture have
- Re-ran the generation for all files (no changes)
Related to https://github.com/Azure/ResourceModules/issues/3722, but
needs to be fixed there seperately
## Type of Change
- [x] Update to CI Environment or utlities (Non-module effecting
changes)
- [ ] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
---------
Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com>
---
.../tests/unit/avm.core.team.tests.ps1 | 2 +-
.../tests/unit/avm.core.team.tests.ps1 | 2 +-
.../Initialize-DeploymentRemoval.ps1 | 4 ++--
.../Get-ResourceIdsAsFormattedObjectList.ps1 | 2 +-
.../helper/Remove-Deployment.ps1 | 2 +-
.../Invoke-AvmJsonModuleIndexGeneration.ps1 | 6 +++---
.../publish/helper/Get-ModulesToPublish.ps1 | 2 +-
.../sharedScripts/Add-YamlListToFile.ps1 | 2 +-
.../sharedScripts/Set-EnvironmentOnAgent.ps1 | 2 +-
.../sharedScripts/Set-ModuleReadMe.ps1 | 14 ++++++-------
.../helper/ConvertTo-OrderedHashtable.ps1 | 8 ++++----
.../helper/Get-CrossReferencedModuleList.ps1 | 8 ++++----
.../helper/Get-SpecsAlignedResourceName.ps1 | 4 ++--
.../Convert-TokensInFileList.ps1 | 2 +-
.../compliance/Set-PesterGitHubOutput.ps1 | 8 ++++----
.../compliance/module.tests.ps1 | 20 +++++++++----------
.../psrule/Set-PSRuleGitHubOutput.ps1 | 4 ++--
.../tools/Invoke-WorkflowsForBranch.ps1 | 2 +-
avm/utilities/tools/Test-ModuleLocally.ps1 | 2 +-
19 files changed, 48 insertions(+), 48 deletions(-)
diff --git a/avm/res/compute/virtual-machine/tests/unit/avm.core.team.tests.ps1 b/avm/res/compute/virtual-machine/tests/unit/avm.core.team.tests.ps1
index 15532c48ed5..a6229b35e40 100644
--- a/avm/res/compute/virtual-machine/tests/unit/avm.core.team.tests.ps1
+++ b/avm/res/compute/virtual-machine/tests/unit/avm.core.team.tests.ps1
@@ -17,7 +17,7 @@ BeforeAll {
. (Join-Path $RepoRootPath 'avm' 'utilities' 'pipelines' 'sharedScripts' 'helper' 'Get-IsParameterRequired.ps1')
if ($moduleFolderPaths.Count -gt 1) {
- $topLevelModuleTemplatePath = $moduleFolderPaths | Sort-Object | Select-Object -First 1
+ $topLevelModuleTemplatePath = $moduleFolderPaths | Sort-Object -Culture 'en-US' | Select-Object -First 1
} else {
$topLevelModuleTemplatePath = $moduleFolderPaths
}
diff --git a/avm/res/network/nat-gateway/tests/unit/avm.core.team.tests.ps1 b/avm/res/network/nat-gateway/tests/unit/avm.core.team.tests.ps1
index ee6d4dc0838..eb8c8928b07 100644
--- a/avm/res/network/nat-gateway/tests/unit/avm.core.team.tests.ps1
+++ b/avm/res/network/nat-gateway/tests/unit/avm.core.team.tests.ps1
@@ -17,7 +17,7 @@ BeforeAll {
. (Join-Path $RepoRootPath 'avm' 'utilities' 'pipelines' 'sharedScripts' 'helper' 'Get-IsParameterRequired.ps1')
if ($moduleFolderPaths.Count -gt 1) {
- $topLevelModuleTemplatePath = $moduleFolderPaths | Sort-Object | Select-Object -First 1
+ $topLevelModuleTemplatePath = $moduleFolderPaths | Sort-Object -Culture 'en-US' | Select-Object -First 1
} else {
$topLevelModuleTemplatePath = $moduleFolderPaths
}
diff --git a/avm/utilities/pipelines/e2eValidation/resourceRemoval/Initialize-DeploymentRemoval.ps1 b/avm/utilities/pipelines/e2eValidation/resourceRemoval/Initialize-DeploymentRemoval.ps1
index 2ceed9e64b1..d5d4c20503b 100644
--- a/avm/utilities/pipelines/e2eValidation/resourceRemoval/Initialize-DeploymentRemoval.ps1
+++ b/avm/utilities/pipelines/e2eValidation/resourceRemoval/Initialize-DeploymentRemoval.ps1
@@ -134,11 +134,11 @@ function Initialize-DeploymentRemoval {
if ($PurgeTestResources) {
# Resources
$filteredResourceIds = (Get-AzResource).ResourceId | Where-Object { $_ -like '*dep-*' }
- $ResourceIds += ($filteredResourceIds | Sort-Object -Unique)
+ $ResourceIds += ($filteredResourceIds | Sort-Object -Culture 'en-US' -Unique)
# Resource groups
$filteredResourceGroupIds = (Get-AzResourceGroup).ResourceId | Where-Object { $_ -like '*dep-*' }
- $ResourceIds += ($filteredResourceGroupIds | Sort-Object -Unique)
+ $ResourceIds += ($filteredResourceGroupIds | Sort-Object -Culture 'en-US' -Unique)
}
# Invoke removal
diff --git a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Get-ResourceIdsAsFormattedObjectList.ps1 b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Get-ResourceIdsAsFormattedObjectList.ps1
index 69f83360ebd..731b62ee33e 100644
--- a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Get-ResourceIdsAsFormattedObjectList.ps1
+++ b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Get-ResourceIdsAsFormattedObjectList.ps1
@@ -78,7 +78,7 @@ function Get-ResourceIdsAsFormattedObjectList {
$allResourceGroupResources = Get-AzResource -ResourceGroupName $resourceGroupName -Name '*'
}
$expandedResources = $allResourceGroupResources | Where-Object { $_.ResourceId.startswith($resourceId) }
- $expandedResources = $expandedResources | Sort-Object -Descending -Property { $_.ResourceId.Split('/').Count }
+ $expandedResources = $expandedResources | Sort-Object -Culture 'en-US' -Descending -Property { $_.ResourceId.Split('/').Count }
foreach ($resource in $expandedResources) {
$formattedResources += @{
resourceId = $resource.ResourceId
diff --git a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-Deployment.ps1 b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-Deployment.ps1
index fdea2ed2397..00a0d50676d 100644
--- a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-Deployment.ps1
+++ b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-Deployment.ps1
@@ -116,7 +116,7 @@ function Remove-Deployment {
# Pre-Filter & order items
# ========================
- $rawTargetResourceIdsToRemove = $deployedTargetResources | Sort-Object -Property { $_.Split('/').Count } -Descending | Select-Object -Unique
+ $rawTargetResourceIdsToRemove = $deployedTargetResources | Sort-Object -Culture 'en-US' -Property { $_.Split('/').Count } -Descending | Select-Object -Unique
Write-Verbose ('Total number of deployment target resources after pre-filtering (duplicates) & ordering items [{0}]' -f $rawTargetResourceIdsToRemove.Count) -Verbose
# Format items
diff --git a/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 b/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1
index 2704177b8e6..b48b67f8641 100644
--- a/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1
+++ b/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1
@@ -98,7 +98,7 @@ function Invoke-AvmJsonModuleIndexGeneration {
Write-Error "Error message: $($_.Exception.Message)"
continue
}
- $tags = $tagListResponse.tags | Sort-Object
+ $tags = $tagListResponse.tags | Sort-Object -Culture 'en-US'
$properties = [ordered]@{}
foreach ($tag in $tags) {
@@ -190,7 +190,7 @@ function Invoke-AvmJsonModuleIndexGeneration {
} else {
# If the module exists, merge the tags and properties
$mergedModule = $initialMergeOfJsonFilesData[$module.moduleName]
- $mergedModule.tags = @(($mergedModule.tags + $module.tags) | Sort-Object -Unique)
+ $mergedModule.tags = @(($mergedModule.tags + $module.tags) | Sort-Object -Culture 'en-US' -Unique)
# Merge properties
foreach ($property in $module.properties.PSObject.Properties) {
@@ -205,7 +205,7 @@ function Invoke-AvmJsonModuleIndexGeneration {
$mergedModuleIndexData = $initialMergeOfJsonFilesData.Values
# Sort the modules by their names
- $sortedMergedModuleIndexData = $mergedModuleIndexData | Sort-Object moduleName
+ $sortedMergedModuleIndexData = $mergedModuleIndexData | Sort-Object -Culture 'en-US' -Property 'moduleName'
Write-Verbose "Convert mergedModuleIndexData variable to JSON and save as 'moduleIndex.json'" -Verbose
$sortedMergedModuleIndexData | ConvertTo-Json -Depth 10 | Out-File -FilePath $moduleIndexJsonFilePath
diff --git a/avm/utilities/pipelines/publish/helper/Get-ModulesToPublish.ps1 b/avm/utilities/pipelines/publish/helper/Get-ModulesToPublish.ps1
index 0ba3ef62cf2..e7f2ca0471f 100644
--- a/avm/utilities/pipelines/publish/helper/Get-ModulesToPublish.ps1
+++ b/avm/utilities/pipelines/publish/helper/Get-ModulesToPublish.ps1
@@ -102,7 +102,7 @@ function Get-TemplateFileToPublish {
$TemplateFilesToPublish = $relevantPaths | ForEach-Object {
Find-TemplateFile -Path $_ -Verbose
- } | Sort-Object -Unique -Descending
+ } | Sort-Object -Culture 'en-US' -Unique -Descending
if ($TemplateFilesToPublish.Count -eq 0) {
Write-Verbose 'No template file found in the modified module.' -Verbose
diff --git a/avm/utilities/pipelines/sharedScripts/Add-YamlListToFile.ps1 b/avm/utilities/pipelines/sharedScripts/Add-YamlListToFile.ps1
index b81cec6cad7..4a7732c651b 100644
--- a/avm/utilities/pipelines/sharedScripts/Add-YamlListToFile.ps1
+++ b/avm/utilities/pipelines/sharedScripts/Add-YamlListToFile.ps1
@@ -69,7 +69,7 @@ function Add-YamlListToFile {
throw "No key-value pairs found in List: $ListName"
}
# Process key value pairs in the list
- foreach ($Key in ($KeyValuePair.Keys.split(' ') | Sort-Object)) {
+ foreach ($Key in ($KeyValuePair.Keys.split(' ') | Sort-Object -Culture 'en-US')) {
Write-Verbose ('Setting environment variable [{0}] with value [{1}]' -f $Key, $KeyValuePair[$Key]) -Verbose
Write-Output "$Key=$($KeyValuePair[$Key])" | Out-File -FilePath $OutputFilePath -Encoding 'utf-8' -Append
}
diff --git a/avm/utilities/pipelines/sharedScripts/Set-EnvironmentOnAgent.ps1 b/avm/utilities/pipelines/sharedScripts/Set-EnvironmentOnAgent.ps1
index 12de5cec071..bcfb7253abb 100644
--- a/avm/utilities/pipelines/sharedScripts/Set-EnvironmentOnAgent.ps1
+++ b/avm/utilities/pipelines/sharedScripts/Set-EnvironmentOnAgent.ps1
@@ -63,7 +63,7 @@ function Install-CustomModule {
$alreadyInstalled = $alreadyInstalled | Where-Object { $_.Version -eq $Module.Version }
} else {
# Get latest in case of multiple
- $alreadyInstalled = ($alreadyInstalled | Sort-Object -Property Version -Descending)[0]
+ $alreadyInstalled = ($alreadyInstalled | Sort-Object -Culture 'en-US' -Property 'Version' -Descending)[0]
}
Write-Verbose ('Module [{0}] already installed with version [{1}]' -f $alreadyInstalled.Name, $alreadyInstalled.Version) -Verbose
continue
diff --git a/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 b/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1
index 43ee7b2dd72..83c923f6011 100644
--- a/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1
+++ b/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1
@@ -95,7 +95,7 @@ function Set-ResourceTypesSection {
$RelevantResourceTypeObjects = Get-NestedResourceList $TemplateFileContent | Where-Object {
$_.type -notin $ResourceTypesToExclude -and $_
- } | Select-Object 'Type', 'ApiVersion' -Unique | Sort-Object Type -Culture 'en-US'
+ } | Select-Object 'Type', 'ApiVersion' -Unique | Sort-Object -Culture 'en-US' -Property 'Type'
$ProgressPreference = 'SilentlyContinue'
$VerbosePreference = 'SilentlyContinue'
@@ -277,9 +277,9 @@ function Set-DefinitionSection {
# Filter to relevant items
if (-not $Properties) {
# Top-level invocation
- [array] $categoryParameters = $TemplateFileContent.parameters.Values | Where-Object { $_.metadata.description -like "$category. *" } | Sort-Object -Property 'Name' -Culture 'en-US'
+ [array] $categoryParameters = $TemplateFileContent.parameters.Values | Where-Object { $_.metadata.description -like "$category. *" } | Sort-Object -Culture 'en-US' -Property 'Name'
} else {
- $categoryParameters = $Properties.Values | Where-Object { $_.metadata.description -like "$category. *" } | Sort-Object -Property 'Name' -Culture 'en-US'
+ $categoryParameters = $Properties.Values | Where-Object { $_.metadata.description -like "$category. *" } | Sort-Object -Culture 'en-US' -Property 'Name'
}
$tableSectionContent += @(
@@ -657,13 +657,13 @@ function Set-CrossReferencesSection {
$dependencies = $CrossReferencedModuleList[$FullModuleIdentifier]
if ($dependencies.Keys -contains 'localPathReferences' -and $dependencies['localPathReferences']) {
- foreach ($reference in ($dependencies['localPathReferences'] | Sort-Object)) {
+ foreach ($reference in ($dependencies['localPathReferences'] | Sort-Object -Culture 'en-US')) {
$SectionContent += ("| ``{0}`` | {1} |" -f $reference, 'Local reference')
}
}
if ($dependencies.Keys -contains 'remoteReferences' -and $dependencies['remoteReferences']) {
- foreach ($reference in ($dependencies['remoteReferences'] | Sort-Object)) {
+ foreach ($reference in ($dependencies['remoteReferences'] | Sort-Object -Culture 'en-US')) {
$SectionContent += ("| ``{0}`` | {1} |" -f $reference, 'Remote reference')
}
}
@@ -1283,11 +1283,11 @@ function Set-UsageExamplesSection {
$moduleNameCamelCase = $First.Tolower() + (Get-Culture).TextInfo.ToTitleCase($Rest) -Replace '-'
}
- $testFilePaths = (Get-ChildItem -Path $ModuleRoot -Recurse -Filter 'main.test.bicep').FullName | Sort-Object
+ $testFilePaths = (Get-ChildItem -Path $ModuleRoot -Recurse -Filter 'main.test.bicep').FullName | Sort-Object -Culture 'en-US'
$RequiredParametersList = $TemplateFileContent.parameters.Keys | Where-Object {
Get-IsParameterRequired -TemplateFileContent $TemplateFileContent -Parameter $TemplateFileContent.parameters[$_]
- } | Sort-Object
+ } | Sort-Object -Culture 'en-US'
############################
## Process test files ##
diff --git a/avm/utilities/pipelines/sharedScripts/helper/ConvertTo-OrderedHashtable.ps1 b/avm/utilities/pipelines/sharedScripts/helper/ConvertTo-OrderedHashtable.ps1
index cfd446753ea..f2707264997 100644
--- a/avm/utilities/pipelines/sharedScripts/helper/ConvertTo-OrderedHashtable.ps1
+++ b/avm/utilities/pipelines/sharedScripts/helper/ConvertTo-OrderedHashtable.ps1
@@ -63,7 +63,7 @@ function ConvertTo-OrderedHashtable {
return $JSONObject # E.g. in primitive data types [1,2,3]
}
- foreach ($currentLevelKey in ($JSONObject.Keys | Sort-Object)) {
+ foreach ($currentLevelKey in ($JSONObject.Keys | Sort-Object -Culture 'en-US')) {
if ($null -eq $JSONObject[$currentLevelKey]) {
# Handle case in which the value is 'null' and hence has no type
@@ -83,7 +83,7 @@ function ConvertTo-OrderedHashtable {
foreach ($array in $arrayElements) {
if ($array.Count -gt 1) {
# Only sort for arrays with more than one item. Otherwise single-item arrays are casted
- $array = $array | Sort-Object
+ $array = $array | Sort-Object -Culture 'en-US'
}
$arrayOutput += , (ConvertTo-OrderedHashtable -JSONInputObject ($array | ConvertTo-Json -Depth 99))
}
@@ -97,13 +97,13 @@ function ConvertTo-OrderedHashtable {
# Case: Primitive data types
$primitiveElements = $JSONObject[$currentLevelKey] | Where-Object { $_.GetType().BaseType.Name -notin @('Array', 'Hashtable') } | ConvertTo-Json -Depth 99 | ConvertFrom-Json -AsHashtable -NoEnumerate -Depth 99
if ($primitiveElements.Count -gt 1) {
- $primitiveElements = $primitiveElements | Sort-Object
+ $primitiveElements = $primitiveElements | Sort-Object -Culture 'en-US'
}
$arrayOutput += $primitiveElements
if ($array.Count -gt 1) {
# Only sort for arrays with more than one item. Otherwise single-item arrays are casted
- $arrayOutput = $arrayOutput | Sort-Object
+ $arrayOutput = $arrayOutput | Sort-Object -Culture 'en-US'
}
$orderedLevel[$currentLevelKey] = $arrayOutput
}
diff --git a/avm/utilities/pipelines/sharedScripts/helper/Get-CrossReferencedModuleList.ps1 b/avm/utilities/pipelines/sharedScripts/helper/Get-CrossReferencedModuleList.ps1
index 0d34d96f61d..f9031b160ba 100644
--- a/avm/utilities/pipelines/sharedScripts/helper/Get-CrossReferencedModuleList.ps1
+++ b/avm/utilities/pipelines/sharedScripts/helper/Get-CrossReferencedModuleList.ps1
@@ -96,9 +96,9 @@ function Get-ReferenceObject {
}
return @{
- resourceReferences = $resultSet.resourceReferences | Sort-Object -Unique
- remoteReferences = $resultSet.remoteReferences | Sort-Object -Unique
- localPathReferences = $resultSet.localPathReferences | Sort-Object -Unique
+ resourceReferences = $resultSet.resourceReferences | Sort-Object -Culture 'en-US' -Unique
+ remoteReferences = $resultSet.remoteReferences | Sort-Object -Culture 'en-US' -Unique
+ localPathReferences = $resultSet.localPathReferences | Sort-Object -Culture 'en-US' -Unique
}
}
#endregion
@@ -155,7 +155,7 @@ function Get-CrossReferencedModuleList {
$moduleTemplatePaths = (Get-ChildItem -Path $path -Recurse -File -Filter '*.bicep').FullName | Where-Object {
# No files inthe [/utilities/tools/] folder and none in the [/tests/] folder
$_ -notmatch '.*[\\|\/]tools[\\|\/].*|.*[\\|\/]tests[\\|\/].*'
- } | Sort-Object
+ } | Sort-Object -Culture 'en-US'
$templateMap = @{}
foreach ($moduleTemplatePath in $moduleTemplatePaths) {
$templateMap[$moduleTemplatePath] = Get-Content -Path $moduleTemplatePath
diff --git a/avm/utilities/pipelines/sharedScripts/helper/Get-SpecsAlignedResourceName.ps1 b/avm/utilities/pipelines/sharedScripts/helper/Get-SpecsAlignedResourceName.ps1
index 356a552c483..b71fb9d849b 100644
--- a/avm/utilities/pipelines/sharedScripts/helper/Get-SpecsAlignedResourceName.ps1
+++ b/avm/utilities/pipelines/sharedScripts/helper/Get-SpecsAlignedResourceName.ps1
@@ -76,7 +76,7 @@ function Get-SpecsAlignedResourceName {
$rawProviderNamespace, $rawResourceType = $reducedResourceIdentifier -Split '[\/|\\]', 2 # e.g. 'keyvault' & 'vaults/keys'
# Find provider namespace
- $foundProviderNamespaceMatches = ($specs.Keys | Sort-Object) | Where-Object { $_ -like "Microsoft.$rawProviderNamespace*" }
+ $foundProviderNamespaceMatches = ($specs.Keys | Sort-Object -Culture 'en-US') | Where-Object { $_ -like "Microsoft.$rawProviderNamespace*" }
if (-not $foundProviderNamespaceMatches) {
$providerNamespace = "Microsoft.$rawProviderNamespace"
@@ -86,7 +86,7 @@ function Get-SpecsAlignedResourceName {
}
# Find resource type
- $innerResourceTypes = $specs[$providerNamespace].Keys | Sort-Object
+ $innerResourceTypes = $specs[$providerNamespace].Keys | Sort-Object -Culture 'en-US'
$rawResourceTypeElem = $rawResourceType -split '[\/|\\]'
$reducedResourceTypeElements = $rawResourceTypeElem | ForEach-Object { Get-ReducedWordString -StringToReduce $_ }
diff --git a/avm/utilities/pipelines/sharedScripts/tokenReplacement/Convert-TokensInFileList.ps1 b/avm/utilities/pipelines/sharedScripts/tokenReplacement/Convert-TokensInFileList.ps1
index bcae7411d64..806041c1220 100644
--- a/avm/utilities/pipelines/sharedScripts/tokenReplacement/Convert-TokensInFileList.ps1
+++ b/avm/utilities/pipelines/sharedScripts/tokenReplacement/Convert-TokensInFileList.ps1
@@ -70,7 +70,7 @@ function Convert-TokensInFileList {
process {
# Combine All Input Token Types, Remove Duplicates and Only Select entries with on empty values
- $FilteredTokens = ($Tokens | Sort-Object -Unique).Clone()
+ $FilteredTokens = ($Tokens | Sort-Object -Culture 'en-US' -Unique).Clone()
@($FilteredTokens.Keys) | ForEach-Object {
if ([String]::IsNullOrEmpty($FilteredTokens[$_])) {
$FilteredTokens.Remove($_)
diff --git a/avm/utilities/pipelines/staticValidation/compliance/Set-PesterGitHubOutput.ps1 b/avm/utilities/pipelines/staticValidation/compliance/Set-PesterGitHubOutput.ps1
index 70e939e949b..d28f09fa20a 100644
--- a/avm/utilities/pipelines/staticValidation/compliance/Set-PesterGitHubOutput.ps1
+++ b/avm/utilities/pipelines/staticValidation/compliance/Set-PesterGitHubOutput.ps1
@@ -128,7 +128,7 @@ function Set-PesterGitHubOutput {
'| Name | Error | Source |',
'| :-- | :-- | :-- |'
)
- foreach ($failedTest in ($failedTests | Sort-Object -Property { $PSItem.ExpandedName })) {
+ foreach ($failedTest in ($failedTests | Sort-Object -Culture 'en-US' -Property { $PSItem.ExpandedName })) {
$intermediateNameElements = $failedTest.Path
$intermediateNameElements[-1] = '**{0}**' -f $failedTest.ExpandedName
@@ -175,7 +175,7 @@ function Set-PesterGitHubOutput {
'| Name | Source |',
'| :-- | :-- |'
)
- foreach ($passedTest in ($passedTests | Sort-Object -Property { $PSItem.ExpandedName }) ) {
+ foreach ($passedTest in ($passedTests | Sort-Object -Culture 'en-US' -Property { $PSItem.ExpandedName }) ) {
$intermediateNameElements = $passedTest.Path
$intermediateNameElements[-1] = '**{0}**' -f $passedTest.ExpandedName
@@ -221,7 +221,7 @@ function Set-PesterGitHubOutput {
'| Name | Reason | Source |',
'| :-- | :-- | :-- |'
)
- foreach ($skippedTest in ($skippedTests | Sort-Object -Property { $PSItem.ExpandedName }) ) {
+ foreach ($skippedTest in ($skippedTests | Sort-Object -Culture 'en-US' -Property { $PSItem.ExpandedName }) ) {
$intermediateNameElements = $skippedTest.Path
$intermediateNameElements[-1] = '**{0}**' -f $skippedTest.ExpandedName
@@ -269,7 +269,7 @@ function Set-PesterGitHubOutput {
'| Name | Warning | Source |',
'| :-- | :-- | :-- |'
)
- foreach ($test in ($testsWithWarnings | Sort-Object -Property { $PSItem.ExpandedName }) ) {
+ foreach ($test in ($testsWithWarnings | Sort-Object -Culture 'en-US' -Property { $PSItem.ExpandedName }) ) {
foreach ($warning in $test.StandardOutput.Warning) {
$intermediateNameElements = $test.Path
$intermediateNameElements[-1] = '**{0}**' -f $test.ExpandedName
diff --git a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1
index 92e9b154c59..86e82537524 100644
--- a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1
+++ b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1
@@ -36,7 +36,7 @@ Import-Module (Join-Path $PSScriptRoot 'helper' 'helper.psm1') -Force
$pathsToBuild = [System.Collections.ArrayList]@()
$pathsToBuild += $moduleFolderPaths | ForEach-Object { Join-Path $_ 'main.bicep' }
foreach ($moduleFolderPath in $moduleFolderPaths) {
- if ($testFilePaths = ((Get-ChildItem -Path $moduleFolderPath -Recurse -Filter 'main.test.bicep').FullName | Sort-Object)) {
+ if ($testFilePaths = ((Get-ChildItem -Path $moduleFolderPath -Recurse -Filter 'main.test.bicep').FullName | Sort-Object -Culture 'en-US')) {
$pathsToBuild += $testFilePaths
}
}
@@ -533,7 +533,7 @@ Describe 'Module tests' -Tag 'Module' {
}
$incorrectParameters = @()
- foreach ($parameter in ($templateFileParameters.PSBase.Keys | Sort-Object)) {
+ foreach ($parameter in ($templateFileParameters.PSBase.Keys | Sort-Object -Culture 'en-US')) {
# Parameters in the object are formatted like
# - tags
# - customerManagedKey.keyVaultResourceId
@@ -560,7 +560,7 @@ Describe 'Module tests' -Tag 'Module' {
}
$incorrectParameters = @()
- foreach ($parameter in ($templateFileParameters.PSBase.Keys | Sort-Object)) {
+ foreach ($parameter in ($templateFileParameters.PSBase.Keys | Sort-Object -Culture 'en-US')) {
$data = $templateFileParameters.$parameter.metadata.description
if ($data -notmatch '(?s)^[A-Z][a-zA-Z]+\. .+\.$') {
$incorrectParameters += $parameter
@@ -584,7 +584,7 @@ Describe 'Module tests' -Tag 'Module' {
}
$incorrectParameters = @()
- foreach ($parameter in ($templateFileParameters.PSBase.Keys | Sort-Object)) {
+ foreach ($parameter in ($templateFileParameters.PSBase.Keys | Sort-Object -Culture 'en-US')) {
$data = $templateFileParameters.$parameter.metadata.description
switch -regex ($data) {
'^Conditional. .*' {
@@ -605,7 +605,7 @@ Describe 'Module tests' -Tag 'Module' {
)
$incorrectParameters = @()
- foreach ($parameter in ($templateFileParameters.PSBase.Keys | Sort-Object)) {
+ foreach ($parameter in ($templateFileParameters.PSBase.Keys | Sort-Object -Culture 'en-US')) {
$isRequired = Get-IsParameterRequired -TemplateFileContent $templateFileContent -Parameter $templateFileParameters.$parameter
if (-not $isRequired) {
@@ -1184,12 +1184,12 @@ Describe 'Governance tests' {
# Should be at correct location
$incorrectLines = @()
- foreach ($finding in (Compare-Object $listedModules ($listedModules | Sort-Object) -SyncWindow 0)) {
+ foreach ($finding in (Compare-Object $listedModules ($listedModules | Sort-Object -Culture 'en-US') -SyncWindow 0)) {
if ($finding.SideIndicator -eq '<=') {
$incorrectLines += $finding.InputObject
}
}
- $incorrectLines = $incorrectLines | Sort-Object -Unique
+ $incorrectLines = $incorrectLines | Sort-Object -Culture 'en-US' -Unique
$incorrectLines.Count | Should -Be 0 -Because ('the number of modules that are not in the correct alphabetical order in the issue template should be zero ([ref](https://azure.github.io/Azure-Verified-Modules/specs/bicep/#id-bcpnfr15---category-contributionsupport---avm-module-issue-template-file)).However, the following incorrectly located lines were found:{0}
' -f ($incorrectLines -join ''))
}
@@ -1203,7 +1203,7 @@ Describe 'Test file tests' -Tag 'TestTemplate' {
foreach ($moduleFolderPath in $moduleFolderPaths) {
if (Test-Path (Join-Path $moduleFolderPath 'tests')) {
- $testFilePaths = (Get-ChildItem -Path $moduleFolderPath -Recurse -Filter 'main.test.bicep').FullName | Sort-Object
+ $testFilePaths = (Get-ChildItem -Path $moduleFolderPath -Recurse -Filter 'main.test.bicep').FullName | Sort-Object -Culture 'en-US'
foreach ($testFilePath in $testFilePaths) {
$testFileContent = Get-Content $testFilePath
$resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/'
@@ -1346,7 +1346,7 @@ Describe 'API version tests' -Tag 'ApiCheck' {
$nestedResources = Get-NestedResourceList -TemplateFileContent $templateFileContent | Where-Object {
$_.type -notin @('Microsoft.Resources/deployments') -and $_
- } | Select-Object 'Type', 'ApiVersion' -Unique | Sort-Object Type
+ } | Select-Object 'Type', 'ApiVersion' -Unique | Sort-Object -Culture 'en-US' -Property 'Type'
foreach ($resource in $nestedResources) {
@@ -1449,7 +1449,7 @@ Describe 'API version tests' -Tag 'ApiCheck' {
$approvedApiVersions += $resourceTypeApiVersions | Where-Object { $_ -notlike '*-preview' } | Select-Object -Last 5
}
- $approvedApiVersions = $approvedApiVersions | Sort-Object -Unique -Descending
+ $approvedApiVersions = $approvedApiVersions | Sort-Object -Culture 'en-US' -Unique -Descending
if ($approvedApiVersions -notcontains $TargetApi) {
# Using a warning now instead of an error, as we don't want to block PRs for this.
diff --git a/avm/utilities/pipelines/staticValidation/psrule/Set-PSRuleGitHubOutput.ps1 b/avm/utilities/pipelines/staticValidation/psrule/Set-PSRuleGitHubOutput.ps1
index 15a8561bf2d..2bbffee2a18 100644
--- a/avm/utilities/pipelines/staticValidation/psrule/Set-PSRuleGitHubOutput.ps1
+++ b/avm/utilities/pipelines/staticValidation/psrule/Set-PSRuleGitHubOutput.ps1
@@ -46,8 +46,8 @@ function Set-PSRuleGitHubOutput {
return ''
} else {
$results = Import-Csv -Path $inputFilePath
- $passedRules += $results | Where-Object { $_.Outcome -EQ 'Pass' } | Sort-Object -Property 'TargetName', 'RuleName' -Unique
- $failedRules += $results | Where-Object { $_.Outcome -EQ 'Fail' } | Sort-Object -Property 'TargetName', 'RuleName' -Unique
+ $passedRules += $results | Where-Object { $_.Outcome -EQ 'Pass' } | Sort-Object -Culture 'en-US' -Property 'TargetName', 'RuleName' -Unique
+ $failedRules += $results | Where-Object { $_.Outcome -EQ 'Fail' } | Sort-Object -Culture 'en-US' -Property 'TargetName', 'RuleName' -Unique
######################
# Set output content #
diff --git a/avm/utilities/tools/Invoke-WorkflowsForBranch.ps1 b/avm/utilities/tools/Invoke-WorkflowsForBranch.ps1
index 848b9864e6f..cde84fa56b4 100644
--- a/avm/utilities/tools/Invoke-WorkflowsForBranch.ps1
+++ b/avm/utilities/tools/Invoke-WorkflowsForBranch.ps1
@@ -285,6 +285,6 @@ function Invoke-WorkflowsForBranch {
if ($gitHubWorkflowBadges.Count -gt 0) {
Write-Verbose 'GitHub Workflow Badges' -Verbose
Write-Verbose '======================' -Verbose
- Write-Verbose ($gitHubWorkflowBadges | Sort-Object | Out-String) -Verbose
+ Write-Verbose ($gitHubWorkflowBadges | Sort-Object -Culture 'en-US' | Out-String) -Verbose
}
}
diff --git a/avm/utilities/tools/Test-ModuleLocally.ps1 b/avm/utilities/tools/Test-ModuleLocally.ps1
index c4595eb8920..f29c2c90c15 100644
--- a/avm/utilities/tools/Test-ModuleLocally.ps1
+++ b/avm/utilities/tools/Test-ModuleLocally.ps1
@@ -216,7 +216,7 @@ function Test-ModuleLocally {
foreach ($testFilePath in $moduleTestFiles) {
$tokenConfiguration.FilePathList += (Get-LocallyReferencedFileList -FilePath $testFilePath)
}
- $tokenConfiguration.FilePathList = $tokenConfiguration.FilePathList | Sort-Object -Unique
+ $tokenConfiguration.FilePathList = $tokenConfiguration.FilePathList | Sort-Object -Culture 'en-US' -Unique
# Add other template files as they may contain the 'moduleVersion'
$tokenConfiguration.FilePathList += (Get-ChildItem -Path $moduleRoot -Recurse -File).FullName | Where-Object { $_ -match '.+(main.json|main.bicep)$' }
From 0b2ee1e5001c9fb4c0fc15ebf999edfc9cbd21e3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 20 May 2024 10:06:32 +0100
Subject: [PATCH 47/52] chore(deps): bump rexml from 3.2.5 to 3.2.8 in
/docs/jekyll (#1954)
Bumps [rexml](https://github.com/ruby/rexml) from 3.2.5 to 3.2.8.
Release notes
Sourced from rexml's
releases.
REXML 3.2.8 - 2024-05-16
Fixes
REXML 3.2.7 - 2024-05-16
Improvements
Fixes
-
XPath: Fixed a bug of normalize_space(array)
.
-
XPath: Fixed a bug that wrong position is used with nested path.
-
Fixed a bug that an exception message can't be generated for
invalid encoding XML.
... (truncated)
Changelog
Sourced from rexml's
changelog.
3.2.8 - 2024-05-16 {#version-3-2-8}
Fixes
3.2.7 - 2024-05-16 {#version-3-2-7}
Improvements
Fixes
-
XPath: Fixed a bug of normalize_space(array)
.
-
XPath: Fixed a bug that wrong position is used with nested path.
-
Fixed a bug that an exception message can't be generated for
... (truncated)
Commits
[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rexml&package-manager=bundler&previous-version=3.2.5&new-version=3.2.8)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/Azure/bicep-registry-modules/network/alerts).
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
docs/jekyll/Gemfile.lock | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/docs/jekyll/Gemfile.lock b/docs/jekyll/Gemfile.lock
index e96a5c5a286..ada6db56ccb 100644
--- a/docs/jekyll/Gemfile.lock
+++ b/docs/jekyll/Gemfile.lock
@@ -63,11 +63,13 @@ GEM
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
- rexml (3.2.5)
+ rexml (3.2.8)
+ strscan (>= 3.0.9)
rouge (4.0.0)
safe_yaml (1.0.5)
sassc (2.4.0)
ffi (~> 1.9)
+ strscan (3.1.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
unicode-display_width (2.3.0)
From 7f9c0893518f8df23c72733d9c8b64660c226a74 Mon Sep 17 00:00:00 2001
From: Alexander Sehr
Date: Mon, 20 May 2024 12:22:51 +0200
Subject: [PATCH 48/52] fix: Fixed nested UDT resolution if array definition is
in parameter, not the type (#1970)
## Description
- Fixed nested UDT resolution if array definition is in parameter, not
the type
This is now supported
```bicep
param test myArrayType[]
type myArrayType = { ... }
```
Before it was only
```bicep
param test myArrayType
type myArrayType = { ... }[]
```
- Updated how the `Set-ModuleReadMe` script throws errors in case a
cateogory is missing.
- Fixed incorrect parameter metadata for a module
- Re-ran generation for ALL modules
## Pipeline Reference
| Pipeline |
| -------- |
|
[![avm.res.document-db.database-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.document-db.database-account.yml/badge.svg?branch=users%2Falsehr%2FnestedReadMeFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.document-db.database-account.yml)
|
|
[![avm.res.key-vault.vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml/badge.svg?branch=users%2Falsehr%2FnestedReadMeFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml)
|
## Type of Change
- [x] Update to CI Environment or utlities (Non-module effecting
changes)
- [ ] Azure Verified Module updates:
- [x] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
---------
Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com>
---
.../document-db/database-account/README.md | 227 ++++++++++++++++++
.../document-db/database-account/main.bicep | 137 ++++++-----
.../document-db/database-account/main.json | 42 ++--
.../registration-definition/README.md | 47 ++++
.../sharedScripts/Set-ModuleReadMe.ps1 | 24 +-
5 files changed, 380 insertions(+), 97 deletions(-)
diff --git a/avm/res/document-db/database-account/README.md b/avm/res/document-db/database-account/README.md
index fa45217f9cf..30434814bc2 100644
--- a/avm/res/document-db/database-account/README.md
+++ b/avm/res/document-db/database-account/README.md
@@ -2583,6 +2583,40 @@ Default to the location where the account is deployed. Locations enabled for the
- Type: array
- Default: `[]`
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`failoverPriority`](#parameter-locationsfailoverpriority) | int | The failover priority of the region. A failover priority of 0 indicates a write region. The maximum value for a failover priority = (total number of regions - 1). Failover priority values must be unique for each of the regions in which the database account exists. |
+| [`locationName`](#parameter-locationslocationname) | string | The name of the region. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`isZoneRedundant`](#parameter-locationsiszoneredundant) | bool | Default to true. Flag to indicate whether or not this region is an AvailabilityZone region |
+
+### Parameter: `locations.failoverPriority`
+
+The failover priority of the region. A failover priority of 0 indicates a write region. The maximum value for a failover priority = (total number of regions - 1). Failover priority values must be unique for each of the regions in which the database account exists.
+
+- Required: Yes
+- Type: int
+
+### Parameter: `locations.locationName`
+
+The name of the region.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `locations.isZoneRedundant`
+
+Default to true. Flag to indicate whether or not this region is an AvailabilityZone region
+
+- Required: No
+- Type: bool
+
### Parameter: `lock`
The lock settings of the service.
@@ -3296,6 +3330,199 @@ SQL Databases configurations.
- Type: array
- Default: `[]`
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-sqldatabasesname) | string | Name of the SQL database . |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`autoscaleSettingsMaxThroughput`](#parameter-sqldatabasesautoscalesettingsmaxthroughput) | int | Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. |
+| [`containers`](#parameter-sqldatabasescontainers) | array | Array of containers to deploy in the SQL database. |
+| [`throughput`](#parameter-sqldatabasesthroughput) | int | Default to 400. Request units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. |
+
+### Parameter: `sqlDatabases.name`
+
+Name of the SQL database .
+
+- Required: Yes
+- Type: string
+
+### Parameter: `sqlDatabases.autoscaleSettingsMaxThroughput`
+
+Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled.
+
+- Required: No
+- Type: int
+
+### Parameter: `sqlDatabases.containers`
+
+Array of containers to deploy in the SQL database.
+
+- Required: No
+- Type: array
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-sqldatabasescontainersname) | string | Name of the container. |
+| [`paths`](#parameter-sqldatabasescontainerspaths) | array | List of paths using which data within the container can be partitioned. For kind=MultiHash it can be up to 3. For anything else it needs to be exactly 1. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`analyticalStorageTtl`](#parameter-sqldatabasescontainersanalyticalstoragettl) | int | Default to 0. Indicates how long data should be retained in the analytical store, for a container. Analytical store is enabled when ATTL is set with a value other than 0. If the value is set to -1, the analytical store retains all historical data, irrespective of the retention of the data in the transactional store. |
+| [`autoscaleSettingsMaxThroughput`](#parameter-sqldatabasescontainersautoscalesettingsmaxthroughput) | int | Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. |
+| [`conflictResolutionPolicy`](#parameter-sqldatabasescontainersconflictresolutionpolicy) | object | The conflict resolution policy for the container. Conflicts and conflict resolution policies are applicable if the Azure Cosmos DB account is configured with multiple write regions. |
+| [`defaultTtl`](#parameter-sqldatabasescontainersdefaultttl) | int | Default to -1. Default time to live (in seconds). With Time to Live or TTL, Azure Cosmos DB provides the ability to delete items automatically from a container after a certain time period. If the value is set to "-1", it is equal to infinity, and items don't expire by default. |
+| [`indexingPolicy`](#parameter-sqldatabasescontainersindexingpolicy) | object | Indexing policy of the container. |
+| [`kind`](#parameter-sqldatabasescontainerskind) | string | Default to Hash. Indicates the kind of algorithm used for partitioning. |
+| [`throughput`](#parameter-sqldatabasescontainersthroughput) | int | Default to 400. Request Units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. |
+| [`uniqueKeyPolicyKeys`](#parameter-sqldatabasescontainersuniquekeypolicykeys) | array | The unique key policy configuration containing a list of unique keys that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service. |
+
+### Parameter: `sqlDatabases.containers.name`
+
+Name of the container.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `sqlDatabases.containers.paths`
+
+List of paths using which data within the container can be partitioned. For kind=MultiHash it can be up to 3. For anything else it needs to be exactly 1.
+
+- Required: Yes
+- Type: array
+
+### Parameter: `sqlDatabases.containers.analyticalStorageTtl`
+
+Default to 0. Indicates how long data should be retained in the analytical store, for a container. Analytical store is enabled when ATTL is set with a value other than 0. If the value is set to -1, the analytical store retains all historical data, irrespective of the retention of the data in the transactional store.
+
+- Required: No
+- Type: int
+
+### Parameter: `sqlDatabases.containers.autoscaleSettingsMaxThroughput`
+
+Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled.
+
+- Required: No
+- Type: int
+
+### Parameter: `sqlDatabases.containers.conflictResolutionPolicy`
+
+The conflict resolution policy for the container. Conflicts and conflict resolution policies are applicable if the Azure Cosmos DB account is configured with multiple write regions.
+
+- Required: No
+- Type: object
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`mode`](#parameter-sqldatabasescontainersconflictresolutionpolicymode) | string | Indicates the conflict resolution mode. |
+
+**Conditional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`conflictResolutionPath`](#parameter-sqldatabasescontainersconflictresolutionpolicyconflictresolutionpath) | string | The conflict resolution path in the case of LastWriterWins mode. Required if `mode` is set to 'LastWriterWins'. |
+| [`conflictResolutionProcedure`](#parameter-sqldatabasescontainersconflictresolutionpolicyconflictresolutionprocedure) | string | The procedure to resolve conflicts in the case of custom mode. Required if `mode` is set to 'Custom'. |
+
+### Parameter: `sqlDatabases.containers.conflictResolutionPolicy.mode`
+
+Indicates the conflict resolution mode.
+
+- Required: Yes
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'Custom'
+ 'LastWriterWins'
+ ]
+ ```
+
+### Parameter: `sqlDatabases.containers.conflictResolutionPolicy.conflictResolutionPath`
+
+The conflict resolution path in the case of LastWriterWins mode. Required if `mode` is set to 'LastWriterWins'.
+
+- Required: No
+- Type: string
+
+### Parameter: `sqlDatabases.containers.conflictResolutionPolicy.conflictResolutionProcedure`
+
+The procedure to resolve conflicts in the case of custom mode. Required if `mode` is set to 'Custom'.
+
+- Required: No
+- Type: string
+
+### Parameter: `sqlDatabases.containers.defaultTtl`
+
+Default to -1. Default time to live (in seconds). With Time to Live or TTL, Azure Cosmos DB provides the ability to delete items automatically from a container after a certain time period. If the value is set to "-1", it is equal to infinity, and items don't expire by default.
+
+- Required: No
+- Type: int
+
+### Parameter: `sqlDatabases.containers.indexingPolicy`
+
+Indexing policy of the container.
+
+- Required: No
+- Type: object
+
+### Parameter: `sqlDatabases.containers.kind`
+
+Default to Hash. Indicates the kind of algorithm used for partitioning.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'Hash'
+ 'MultiHash'
+ ]
+ ```
+
+### Parameter: `sqlDatabases.containers.throughput`
+
+Default to 400. Request Units per second. Will be ignored if autoscaleSettingsMaxThroughput is used.
+
+- Required: No
+- Type: int
+
+### Parameter: `sqlDatabases.containers.uniqueKeyPolicyKeys`
+
+The unique key policy configuration containing a list of unique keys that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service.
+
+- Required: No
+- Type: array
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`paths`](#parameter-sqldatabasescontainersuniquekeypolicykeyspaths) | array | List of paths must be unique for each document in the Azure Cosmos DB service |
+
+### Parameter: `sqlDatabases.containers.uniqueKeyPolicyKeys.paths`
+
+List of paths must be unique for each document in the Azure Cosmos DB service
+
+- Required: Yes
+- Type: array
+
+### Parameter: `sqlDatabases.throughput`
+
+Default to 400. Request units per second. Will be ignored if autoscaleSettingsMaxThroughput is used.
+
+- Required: No
+- Type: int
+
### Parameter: `tags`
Tags of the Database Account resource.
diff --git a/avm/res/document-db/database-account/main.bicep b/avm/res/document-db/database-account/main.bicep
index 5cbfbfe85da..65501fdbbf5 100644
--- a/avm/res/document-db/database-account/main.bicep
+++ b/avm/res/document-db/database-account/main.bicep
@@ -233,7 +233,7 @@ var virtualNetworkRules = [
}
]
-var databaseAccount_properties = union(
+var databaseAccountProperties = union(
{
databaseAccountOfferType: databaseAccountOfferType
},
@@ -308,24 +308,23 @@ var builtInRoleNames = {
)
}
-resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' =
- if (enableTelemetry) {
- name: '46d3xbcp.res.documentdb-databaseaccount.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
- properties: {
- mode: 'Incremental'
- template: {
- '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
- contentVersion: '1.0.0.0'
- resources: []
- outputs: {
- telemetry: {
- type: 'String'
- value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
- }
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: '46d3xbcp.res.documentdb-databaseaccount.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
+ properties: {
+ mode: 'Incremental'
+ template: {
+ '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
+ contentVersion: '1.0.0.0'
+ resources: []
+ outputs: {
+ telemetry: {
+ type: 'String'
+ value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
}
}
}
}
+}
resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = {
name: name
@@ -333,20 +332,19 @@ resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = {
tags: tags
identity: identity
kind: kind
- properties: databaseAccount_properties
+ properties: databaseAccountProperties
}
-resource databaseAccount_lock 'Microsoft.Authorization/locks@2020-05-01' =
- if (!empty(lock ?? {}) && lock.?kind != 'None') {
- name: lock.?name ?? 'lock-${name}'
- properties: {
- level: lock.?kind ?? ''
- notes: lock.?kind == 'CanNotDelete'
- ? 'Cannot delete resource or child resources.'
- : 'Cannot delete or modify the resource or child resources.'
- }
- scope: databaseAccount
+resource databaseAccount_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
+ name: lock.?name ?? 'lock-${name}'
+ properties: {
+ level: lock.?kind ?? ''
+ notes: lock.?kind == 'CanNotDelete'
+ ? 'Cannot delete resource or child resources.'
+ : 'Cannot delete or modify the resource or child resources.'
}
+ scope: databaseAccount
+}
resource databaseAccount_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [
for (diagnosticSetting, index) in (diagnosticSettings ?? []): {
@@ -490,49 +488,48 @@ module databaseAccount_privateEndpoints 'br/public:avm/res/network/private-endpo
}
]
-module keyVault 'modules/secrets-key-vault.bicep' =
- if (secretsKeyVault != null) {
- name: '${uniqueString(deployment().name, location)}-secrets-kv'
- scope: resourceGroup(secretsKeyVault.?resourceGroupName ?? resourceGroup().name)
- params: {
- keyVaultName: secretsKeyVault!.keyVaultName
+module keyVault 'modules/secrets-key-vault.bicep' = if (secretsKeyVault != null) {
+ name: '${uniqueString(deployment().name, location)}-secrets-kv'
+ scope: resourceGroup(secretsKeyVault.?resourceGroupName ?? resourceGroup().name)
+ params: {
+ keyVaultName: secretsKeyVault!.keyVaultName
- keySecrets: [
- {
- secretName: secretsKeyVault.?primaryWriteKeySecretName ?? 'Primary-Write-Key'
- secretValue: databaseAccount.listKeys().primaryMasterKey
- }
- {
- secretName: secretsKeyVault.?primaryReadOnlyKeySecretName ?? 'Primary-Readonly-Key'
- secretValue: databaseAccount.listKeys().primaryReadonlyMasterKey
- }
- {
- secretName: secretsKeyVault.?primaryWriteConnectionStringSecretName ?? 'Primary-Write-ConnectionString'
- secretValue: databaseAccount.listConnectionStrings().connectionStrings[0].connectionString
- }
- {
- secretName: secretsKeyVault.?primaryReadonlyConnectionStringSecretName ?? 'Primary-Readonly-ConnectionString'
- secretValue: databaseAccount.listConnectionStrings().connectionStrings[2].connectionString
- }
- {
- secretName: secretsKeyVault.?secondaryWriteKeySecretName ?? 'Secondary-Write-Key'
- secretValue: databaseAccount.listKeys().secondaryMasterKey
- }
- {
- secretName: secretsKeyVault.?secondaryReadonlyKeySecretName ?? 'Secondary-Readonly-Key'
- secretValue: databaseAccount.listKeys().secondaryReadonlyMasterKey
- }
- {
- secretName: secretsKeyVault.?secondaryWriteConnectionStringSecretName ?? 'Secondary-Write-ConnectionString'
- secretValue: databaseAccount.listConnectionStrings().connectionStrings[1].connectionString
- }
- {
- secretName: secretsKeyVault.?secondaryReadonlyConnectionStringSecretName ?? 'Secondary-Readonly-ConnectionString'
- secretValue: databaseAccount.listConnectionStrings().connectionStrings[3].connectionString
- }
- ]
- }
+ keySecrets: [
+ {
+ secretName: secretsKeyVault.?primaryWriteKeySecretName ?? 'Primary-Write-Key'
+ secretValue: databaseAccount.listKeys().primaryMasterKey
+ }
+ {
+ secretName: secretsKeyVault.?primaryReadOnlyKeySecretName ?? 'Primary-Readonly-Key'
+ secretValue: databaseAccount.listKeys().primaryReadonlyMasterKey
+ }
+ {
+ secretName: secretsKeyVault.?primaryWriteConnectionStringSecretName ?? 'Primary-Write-ConnectionString'
+ secretValue: databaseAccount.listConnectionStrings().connectionStrings[0].connectionString
+ }
+ {
+ secretName: secretsKeyVault.?primaryReadonlyConnectionStringSecretName ?? 'Primary-Readonly-ConnectionString'
+ secretValue: databaseAccount.listConnectionStrings().connectionStrings[2].connectionString
+ }
+ {
+ secretName: secretsKeyVault.?secondaryWriteKeySecretName ?? 'Secondary-Write-Key'
+ secretValue: databaseAccount.listKeys().secondaryMasterKey
+ }
+ {
+ secretName: secretsKeyVault.?secondaryReadonlyKeySecretName ?? 'Secondary-Readonly-Key'
+ secretValue: databaseAccount.listKeys().secondaryReadonlyMasterKey
+ }
+ {
+ secretName: secretsKeyVault.?secondaryWriteConnectionStringSecretName ?? 'Secondary-Write-ConnectionString'
+ secretValue: databaseAccount.listConnectionStrings().connectionStrings[1].connectionString
+ }
+ {
+ secretName: secretsKeyVault.?secondaryReadonlyConnectionStringSecretName ?? 'Secondary-Readonly-ConnectionString'
+ secretValue: databaseAccount.listConnectionStrings().connectionStrings[3].connectionString
+ }
+ ]
}
+}
@description('The name of the database account.')
output name string = databaseAccount.name
@@ -754,10 +751,10 @@ type sqlDatabaseType = {
@description('Optional. The conflict resolution policy for the container. Conflicts and conflict resolution policies are applicable if the Azure Cosmos DB account is configured with multiple write regions.')
conflictResolutionPolicy: {
- @description('Required if mode=LastWriterWins. The conflict resolution path in the case of LastWriterWins mode.')
+ @description('Conditional. The conflict resolution path in the case of LastWriterWins mode. Required if `mode` is set to \'LastWriterWins\'.')
conflictResolutionPath: string?
- @description('Required if mode=Custom. The procedure to resolve conflicts in the case of custom mode.')
+ @description('Conditional. The procedure to resolve conflicts in the case of custom mode. Required if `mode` is set to \'Custom\'.')
conflictResolutionProcedure: string?
@description('Required. Indicates the conflict resolution mode.')
@@ -780,7 +777,7 @@ type sqlDatabaseType = {
@description('Optional. The unique key policy configuration containing a list of unique keys that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service.')
uniqueKeyPolicyKeys: {
- @description('List of paths must be unique for each document in the Azure Cosmos DB service')
+ @description('Required. List of paths must be unique for each document in the Azure Cosmos DB service')
paths: string[]
}[]?
}[]?
diff --git a/avm/res/document-db/database-account/main.json b/avm/res/document-db/database-account/main.json
index 943fa8af420..e1a6ae712b1 100644
--- a/avm/res/document-db/database-account/main.json
+++ b/avm/res/document-db/database-account/main.json
@@ -5,8 +5,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "9794166259717067711"
+ "version": "0.27.1.19265",
+ "templateHash": "13227324731926560272"
},
"name": "DocumentDB Database Accounts",
"description": "This module deploys a DocumentDB Database Account.",
@@ -534,14 +534,14 @@
"type": "string",
"nullable": true,
"metadata": {
- "description": "Required if mode=LastWriterWins. The conflict resolution path in the case of LastWriterWins mode."
+ "description": "Conditional. The conflict resolution path in the case of LastWriterWins mode. Required if `mode` is set to 'LastWriterWins'."
}
},
"conflictResolutionProcedure": {
"type": "string",
"nullable": true,
"metadata": {
- "description": "Required if mode=Custom. The procedure to resolve conflicts in the case of custom mode."
+ "description": "Conditional. The procedure to resolve conflicts in the case of custom mode. Required if `mode` is set to 'Custom'."
}
},
"mode": {
@@ -605,7 +605,7 @@
"type": "string"
},
"metadata": {
- "description": "List of paths must be unique for each document in the Azure Cosmos DB service"
+ "description": "Required. List of paths must be unique for each document in the Azure Cosmos DB service"
}
}
}
@@ -1090,7 +1090,7 @@
],
"kind": "[if(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('gremlinDatabases')))), 'GlobalDocumentDB', if(not(empty(parameters('mongodbDatabases'))), 'MongoDB', 'GlobalDocumentDB'))]",
"backupPolicy": "[if(equals(parameters('backupPolicyType'), 'Continuous'), createObject('type', parameters('backupPolicyType'), 'continuousModeProperties', createObject('tier', parameters('backupPolicyContinuousTier'))), createObject('type', parameters('backupPolicyType'), 'periodicModeProperties', createObject('backupIntervalInMinutes', parameters('backupIntervalInMinutes'), 'backupRetentionIntervalInHours', parameters('backupRetentionIntervalInHours'), 'backupStorageRedundancy', parameters('backupStorageRedundancy'))))]",
- "databaseAccount_properties": "[union(createObject('databaseAccountOfferType', parameters('databaseAccountOfferType')), if(or(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('mongodbDatabases')))), not(empty(parameters('gremlinDatabases')))), createObject('consistencyPolicy', variables('consistencyPolicy')[parameters('defaultConsistencyLevel')], 'enableMultipleWriteLocations', parameters('enableMultipleWriteLocations'), 'locations', if(empty(variables('databaseAccount_locations')), variables('defaultFailoverLocation'), variables('databaseAccount_locations')), 'ipRules', variables('ipRules'), 'virtualNetworkRules', variables('virtualNetworkRules'), 'networkAclBypass', coalesce(tryGet(parameters('networkRestrictions'), 'networkAclBypass'), 'AzureServices'), 'publicNetworkAccess', coalesce(tryGet(parameters('networkRestrictions'), 'publicNetworkAccess'), 'Enabled'), 'isVirtualNetworkFilterEnabled', or(not(empty(variables('ipRules'))), not(empty(variables('virtualNetworkRules')))), 'capabilities', variables('capabilities'), 'enableFreeTier', parameters('enableFreeTier'), 'backupPolicy', variables('backupPolicy'), 'enableAutomaticFailover', parameters('automaticFailover'), 'enableAnalyticalStorage', parameters('enableAnalyticalStorage')), createObject()), if(not(empty(parameters('sqlDatabases'))), createObject('disableLocalAuth', parameters('disableLocalAuth'), 'disableKeyBasedMetadataWriteAccess', parameters('disableKeyBasedMetadataWriteAccess')), createObject()), if(not(empty(parameters('mongodbDatabases'))), createObject('apiProperties', createObject('serverVersion', parameters('serverVersion'))), createObject()))]",
+ "databaseAccountProperties": "[union(createObject('databaseAccountOfferType', parameters('databaseAccountOfferType')), if(or(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('mongodbDatabases')))), not(empty(parameters('gremlinDatabases')))), createObject('consistencyPolicy', variables('consistencyPolicy')[parameters('defaultConsistencyLevel')], 'enableMultipleWriteLocations', parameters('enableMultipleWriteLocations'), 'locations', if(empty(variables('databaseAccount_locations')), variables('defaultFailoverLocation'), variables('databaseAccount_locations')), 'ipRules', variables('ipRules'), 'virtualNetworkRules', variables('virtualNetworkRules'), 'networkAclBypass', coalesce(tryGet(parameters('networkRestrictions'), 'networkAclBypass'), 'AzureServices'), 'publicNetworkAccess', coalesce(tryGet(parameters('networkRestrictions'), 'publicNetworkAccess'), 'Enabled'), 'isVirtualNetworkFilterEnabled', or(not(empty(variables('ipRules'))), not(empty(variables('virtualNetworkRules')))), 'capabilities', variables('capabilities'), 'enableFreeTier', parameters('enableFreeTier'), 'backupPolicy', variables('backupPolicy'), 'enableAutomaticFailover', parameters('automaticFailover'), 'enableAnalyticalStorage', parameters('enableAnalyticalStorage')), createObject()), if(not(empty(parameters('sqlDatabases'))), createObject('disableLocalAuth', parameters('disableLocalAuth'), 'disableKeyBasedMetadataWriteAccess', parameters('disableKeyBasedMetadataWriteAccess')), createObject()), if(not(empty(parameters('mongodbDatabases'))), createObject('apiProperties', createObject('serverVersion', parameters('serverVersion'))), createObject()))]",
"builtInRoleNames": {
"Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
"Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]",
@@ -1133,7 +1133,7 @@
"tags": "[parameters('tags')]",
"identity": "[variables('identity')]",
"kind": "[variables('kind')]",
- "properties": "[variables('databaseAccount_properties')]"
+ "properties": "[variables('databaseAccountProperties')]"
},
"databaseAccount_lock": {
"condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]",
@@ -1249,8 +1249,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "3826907101153138616"
+ "version": "0.27.1.19265",
+ "templateHash": "15534269553484112693"
},
"name": "DocumentDB Database Account SQL Databases",
"description": "This module deploys a SQL Database in a CosmosDB Account.",
@@ -1379,8 +1379,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "11250469246171251281"
+ "version": "0.27.1.19265",
+ "templateHash": "12755071159425783033"
},
"name": "DocumentDB Database Account SQL Database Containers",
"description": "This module deploys a SQL Database Container in a CosmosDB Account.",
@@ -1622,8 +1622,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "7706794569430424680"
+ "version": "0.27.1.19265",
+ "templateHash": "1527748615955553712"
},
"name": "DocumentDB Database Account MongoDB Databases",
"description": "This module deploys a MongoDB Database within a CosmosDB Account.",
@@ -1725,8 +1725,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "1989910090472113779"
+ "version": "0.27.1.19265",
+ "templateHash": "6235322895830297683"
},
"name": "DocumentDB Database Account MongoDB Database Collections",
"description": "This module deploys a MongoDB Database Collection.",
@@ -1881,8 +1881,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "13414524346849431368"
+ "version": "0.27.1.19265",
+ "templateHash": "6889435067791947905"
},
"name": "DocumentDB Database Account Gremlin Databases",
"description": "This module deploys a Gremlin Database within a CosmosDB Account.",
@@ -1985,8 +1985,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "2823004405398378621"
+ "version": "0.27.1.19265",
+ "templateHash": "3987394297554402770"
},
"name": "DocumentDB Database Accounts Gremlin Databases Graphs",
"description": "This module deploys a DocumentDB Database Accounts Gremlin Database Graph.",
@@ -2848,8 +2848,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "3376784638805058420"
+ "version": "0.27.1.19265",
+ "templateHash": "17423703517558214368"
}
},
"definitions": {
diff --git a/avm/res/managed-services/registration-definition/README.md b/avm/res/managed-services/registration-definition/README.md
index 1197adf8ab3..51a35a1643f 100644
--- a/avm/res/managed-services/registration-definition/README.md
+++ b/avm/res/managed-services/registration-definition/README.md
@@ -412,6 +412,53 @@ Specify an array of objects, containing object of Azure Active Directory princip
- Required: Yes
- Type: array
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`principalId`](#parameter-authorizationsprincipalid) | string | The identifier of the Azure Active Directory principal. |
+| [`roleDefinitionId`](#parameter-authorizationsroledefinitionid) | string | The identifier of the Azure built-in role that defines the permissions that the Azure Active Directory principal will have on the projected scope. |
+
+**Conditional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`delegatedRoleDefinitionIds`](#parameter-authorizationsdelegatedroledefinitionids) | array | The list of role definition ids which define all the permissions that the user in the authorization can assign to other principals. Required if the `roleDefinitionId` refers to the User Access Administrator Role. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`principalIdDisplayName`](#parameter-authorizationsprincipaliddisplayname) | string | The display name of the Azure Active Directory principal. |
+
+### Parameter: `authorizations.principalId`
+
+The identifier of the Azure Active Directory principal.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `authorizations.roleDefinitionId`
+
+The identifier of the Azure built-in role that defines the permissions that the Azure Active Directory principal will have on the projected scope.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `authorizations.delegatedRoleDefinitionIds`
+
+The list of role definition ids which define all the permissions that the user in the authorization can assign to other principals. Required if the `roleDefinitionId` refers to the User Access Administrator Role.
+
+- Required: No
+- Type: array
+
+### Parameter: `authorizations.principalIdDisplayName`
+
+The display name of the Azure Active Directory principal.
+
+- Required: No
+- Type: string
+
### Parameter: `managedByTenantId`
Specify the tenant ID of the tenant which homes the principals you are delegating permissions to.
diff --git a/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 b/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1
index 83c923f6011..673e5929fe1 100644
--- a/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1
+++ b/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1
@@ -248,16 +248,22 @@ function Set-DefinitionSection {
$descriptions = $TemplateFileContent.parameters.Values.metadata.description
# Add name as property for later reference
$TemplateFileContent.parameters.Keys | ForEach-Object { $TemplateFileContent.parameters[$_]['name'] = $_ }
+
+ # Error handling: Throw error if any parameter is missing a category
+ if ($paramsWithoutCategory = $TemplateFileContent.parameters.Values | Where-Object { $_.metadata.description -notmatch '^\w+?\.' }) {
+ $formattedParam = $paramsWithoutCategory | ForEach-Object { [PSCustomObject]@{ name = $_.name; description = $_.metadata.description } } | ConvertTo-Json -Compress
+ Write-Error ("Each parameter description should start with a category like [Required. / Optional. / Conditional. ]. The following parameters are missing such a category: `n$formattedParam`n")
+ }
} else {
$descriptions = $Properties.Values.metadata.description
# Add name as property for later reference
$Properties.Keys | ForEach-Object { $Properties[$_]['name'] = $_ }
- }
- # Error handling: Throw error if any parameter is missing a category
- if ($paramsWithoutCategory = $TemplateFileContent.parameters.Values | Where-Object { $_.metadata.description -notmatch '^\w+?\.' }) {
- $formattedParam = $paramsWithoutCategory | ForEach-Object { [PSCustomObject]@{ name = $_.name; description = $_.metadata.description } } | ConvertTo-Json -Compress
- throw ("Each parameter description should start with a category like [Required. / Optional. / Conditional. ]. The following parameters are missing such a category: `n$formattedParam`n")
+ # Error handling: Throw error if any parameter is missing a category
+ if ($paramsWithoutCategory = $Properties.Values | Where-Object { $_.metadata.description -notmatch '^\w+?\.' }) {
+ $formattedParam = $paramsWithoutCategory | ForEach-Object { [PSCustomObject]@{ name = $_.name; description = $_.metadata.description } } | ConvertTo-Json -Compress
+ Write-Error ("Each parameter description should start with a category like [Required. / Optional. / Conditional. ]. The following parameters are missing such a category: `n$formattedParam`n")
+ }
}
# Get the module parameter categories
@@ -306,10 +312,16 @@ function Set-DefinitionSection {
$type = $definition['type']
$rawAllowedValues = $definition['allowedValues']
} elseif ($parameter.Keys -contains 'items' -and $parameter.items.type -in @('object', 'array') -or $parameter.type -eq 'object') {
- # Array has nested non-primitive type (array/object)
+ # Array has nested non-primitive type (array/object) - and if array, the the UDT itself is declared as the array
$definition = $parameter
$type = $parameter.type
$rawAllowedValues = $parameter.allowedValues
+ } elseif ($parameter.Keys -contains 'items' -and $parameter.items.keys -contains '$ref') {
+ # Array has nested non-primitive type (array) - and the parameter is defined as an array of the UDT
+ $identifier = Split-Path $parameter.items.'$ref' -Leaf
+ $definition = $TemplateFileContent.definitions[$identifier]
+ $type = $parameter.type
+ $rawAllowedValues = $definition['allowedValues']
} else {
$definition = $null
$type = $parameter.type
From 17e37eecfff1a5f22e8f117ad8ffe8f7db4d7535 Mon Sep 17 00:00:00 2001
From: Rainer Halanek <61878316+rahalan@users.noreply.github.com>
Date: Tue, 21 May 2024 16:08:36 +0200
Subject: [PATCH 49/52] feat: update logic to create a meaningful issue, if the
module list template is out of sync with the CSV files (#1918)
If the template file is not in sync with the CSV files, an issue was
created. The old issue was not very helpful, thus the new issue will
show exactly what it is about and shows in detail the diff. Here you can
see how such an issue will look like:
https://github.com/Azure/GHPolicyTest/issues/189
---
.../platform/Sync-AvmModulesList.ps1 | 134 ++++++++++++++----
1 file changed, 109 insertions(+), 25 deletions(-)
diff --git a/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 b/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1
index 7a2af595fa9..d816729452d 100644
--- a/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1
+++ b/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1
@@ -30,42 +30,122 @@ function Sync-AvmModulesList {
. (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Get-AvmCsvData.ps1')
. (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Add-GithubIssueToProject.ps1')
- $workflowFilePath = Join-Path $RepoRoot '.github' 'ISSUE_TEMPLATE' 'avm_module_issue.yml'
-
# get CSV data
- $modules = Get-AvmCsvData -ModuleIndex 'Bicep-Resource' | Select-Object -Property 'ModuleName'
- $patterns = Get-AvmCsvData -ModuleIndex 'Bicep-Pattern' | Select-Object -Property 'ModuleName'
-
- # build new strings
- $prefix = ' - "'
- $postfix = '"'
- $newModuleLines = $modules | ForEach-Object { $prefix + $_.ModuleName + $postfix }
- $newPatternLines = $patterns | ForEach-Object { $prefix + $_.ModuleName + $postfix }
+ $targetModules = Get-AvmCsvData -ModuleIndex 'Bicep-Resource' | Where-Object { ($_.ModuleStatus -eq 'Available :green_circle:') -or ($_.ModuleStatus -eq 'Orphaned :eyes:') } | Select-Object -ExpandProperty 'ModuleName' | Sort-Object
+ $targetPatterns = Get-AvmCsvData -ModuleIndex 'Bicep-Pattern' | Where-Object { ($_.ModuleStatus -eq 'Available :green_circle:') -or ($_.ModuleStatus -eq 'Orphaned :eyes:') } | Select-Object -ExpandProperty 'ModuleName' | Sort-Object
- # parse workflow file
- $workflowFileLines = Get-Content $workflowFilePath
+ $issueTemplatePath = Join-Path $RepoRoot '.github' 'ISSUE_TEMPLATE' 'avm_module_issue.yml'
+ $issueTemplateContent = Get-Content $issueTemplatePath
+
+ # Identify listed modules
$startIndex = 0
- $endIndex = 0
+ while ($issueTemplateContent[$startIndex] -notmatch '^\s*#?\s*\-\s+\"avm\/.+\"' -and $startIndex -ne $issueTemplateContent.Length) {
+ $startIndex++
+ }
+
+ $endIndex = $startIndex
+ while ($issueTemplateContent[$endIndex] -match '.*- "avm\/.*' -and $endIndex -ne $issueTemplateContent.Length) {
+ $endIndex++
+ }
+ $endIndex-- # Go one back to last module line
+
+ $listedModules = $issueTemplateContent[$startIndex..$endIndex] | ForEach-Object { $_ -replace '.*- "(avm\/.*)".*', '$1' } | Where-Object { $_ -match 'avm\/res\/.*' }
+ $listedPatterns = $issueTemplateContent[$startIndex..$endIndex] | ForEach-Object { $_ -replace '.*- "(avm\/.*)".*', '$1' } | Where-Object { $_ -match 'avm\/ptn\/.*' }
+
+ $body = ''
+
+ $missingModules = $targetModules | Where { $listedModules -NotContains $_ }
+ $unexpectedModules = $listedModules | Where { $targetModules -NotContains $_ }
+ $unexpectedPatterns = $listedPatterns | Where { $targetPatterns -NotContains $_ }
+ $missingPatterns = $targetPatterns | Where { $listedPatterns -NotContains $_ }
+
+ if ($missingModules.Count -gt 0)
+ {
+ $body += @"
+**Missing resource modules:**
+
+$($missingModules -join ([Environment]::NewLine))
+$([Environment]::NewLine)
+"@
+ }
+
+ if ($unexpectedModules.Count -gt 0)
+ {
+ $body += @"
+**Unexpected resource modules:**
+
+$($unexpectedModules -join ([Environment]::NewLine))
+$([Environment]::NewLine)
+"@
+ }
- for ($lineNumber = 0; $lineNumber -lt $workflowFileLines.Count; $lineNumber++) {
- if ($startIndex -gt 0 -and (-not ($workflowFileLines[$lineNumber]).Trim().StartsWith('- "avm/'))) {
- $endIndex = $lineNumber
- break
+ if ($missingPatterns.Count -gt 0)
+ {
+ $body += @"
+**Missing pattern modules:**
+
+$($missingPatterns -join ([Environment]::NewLine))
+$([Environment]::NewLine)
+"@
+ }
+
+ if ($unexpectedPatterns.Count -gt 0)
+ {
+ $body += @"
+**Unexpected pattern modules:**
+
+$($unexpectedPatterns -join ([Environment]::NewLine))
+$([Environment]::NewLine)
+"@
+ }
+
+ # Should be at correct location
+ $incorrectModuleLines = @()
+ foreach ($finding in (Compare-Object $listedModules ($listedModules | Sort-Object) -SyncWindow 0)) {
+ if ($finding.SideIndicator -eq '<=') {
+ $incorrectModuleLines += $finding.InputObject
}
+ }
+ $incorrectModuleLines = $incorrectModuleLines | Sort-Object -Unique
- if (($workflowFileLines[$lineNumber]).Trim() -eq '- "Other, as defined below..."') {
- $startIndex = $lineNumber
+ if ($incorrectModuleLines.Count -gt 0)
+ {
+ $body += @"
+**Resource modules that are not correctly sorted:**
+
+$($incorrectModuleLines -join ([Environment]::NewLine))
+$([Environment]::NewLine)
+"@
+ }
+
+ $incorrectPatternLines = @()
+ foreach ($finding in (Compare-Object $listedPatterns ($listedPatterns | Sort-Object) -SyncWindow 0)) {
+ if ($finding.SideIndicator -eq '<=') {
+ $incorrectPatternLines += $finding.InputObject
}
}
+ $incorrectPatternLines = $incorrectPatternLines | Sort-Object -Unique
- $oldLines = $workflowFileLines[($startIndex + 1)..($endIndex - 1)]
- $newLines = $newModuleLines + $newPatternLines
- $body = $newLines -join ([Environment]::NewLine)
+ if ($incorrectPatternLines.Count -gt 0)
+ {
+ $body += @"
+**Pattern modules that are not correctly sorted:**
- if ($oldLines -ne $newLines) {
- $title = '[AVM core] Module(s) missing from AVM Module Issue template'
+$($incorrectPatternLines -join ([Environment]::NewLine))
+$([Environment]::NewLine)
+"@
+ }
+
+ if ($body -ne '') {
+ $title = '[AVM core] AVM Module Issue template is not in sync with published resource modules and pattern modules list'
$label = 'Type: AVM :a: :v: :m:,Type: Hygiene :broom:,Needs: Triage :mag:'
- $issues = gh issue list --state open --limit 500 --label $label --json 'title' --repo $Repo | ConvertFrom-Json -Depth 100
+ $issues = gh issue list --state open --limit 500 --label $label --json 'title,url' --repo $Repo | ConvertFrom-Json -Depth 100
+
+ $body = @"
+> [!IMPORTANT]
+> The file [avm_module_issue.yml](https://github.com/Azure/bicep-registry-modules/blob/main/.github/ISSUE_TEMPLATE/avm_module_issue.yml?plain=1) which lists all modules when creating a new issue, is not in sync with the CSV files, that can be found under [resource modules](https://aka.ms/avm/index/bicep/res/csv) and [pattern modules](https://aka.ms/avm/index/bicep/ptn/csv). These CSV files are the single source of truth regarding published modules. Please update the ``avm_module_issue.yml`` accordingly. Please see the following differences that were found.
+$([Environment]::NewLine)
+"@ + $body
if ($issues.title -notcontains $title) {
# create issue
@@ -74,5 +154,9 @@ function Sync-AvmModulesList {
$ProjectNumber = 538 # AVM - Issue Triage
Add-GithubIssueToProject -Repo $Repo -ProjectNumber $ProjectNumber -IssueUrl $issueUrl
}
+ else {
+ # update body
+ gh issue edit $issues[0].url --body $body --repo $Repo
+ }
}
}
From 7954f1657dd5494ff730f5f12ba4ec2f9b40e5e7 Mon Sep 17 00:00:00 2001
From: rodney-almeida <64196999+rodney-almeida@users.noreply.github.com>
Date: Tue, 21 May 2024 15:23:48 +0100
Subject: [PATCH 50/52] fix: Add check to only apply quarantinePolicyStatus and
trustPolicyStatus on Premium SKUs (#1967)
---
avm/res/container-registry/registry/README.md | 8 +-
.../container-registry/registry/main.bicep | 109 +++++++++---------
avm/res/container-registry/registry/main.json | 31 +++--
3 files changed, 71 insertions(+), 77 deletions(-)
diff --git a/avm/res/container-registry/registry/README.md b/avm/res/container-registry/registry/README.md
index 5d0fd4ae87c..90db8524cde 100644
--- a/avm/res/container-registry/registry/README.md
+++ b/avm/res/container-registry/registry/README.md
@@ -611,7 +611,7 @@ module registry 'br/public:avm/res/container-registry/registry:' = {
| [`networkRuleSetIpRules`](#parameter-networkrulesetiprules) | array | The IP ACL rules. Note, requires the 'acrSku' to be 'Premium'. |
| [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'acrSku' to be 'Premium'. |
| [`publicNetworkAccess`](#parameter-publicnetworkaccess) | string | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkRuleSetIpRules are not set. Note, requires the 'acrSku' to be 'Premium'. |
-| [`quarantinePolicyStatus`](#parameter-quarantinepolicystatus) | string | The value that indicates whether the quarantine policy is enabled or not. |
+| [`quarantinePolicyStatus`](#parameter-quarantinepolicystatus) | string | The value that indicates whether the quarantine policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'. |
| [`replications`](#parameter-replications) | array | All replications to create. |
| [`retentionPolicyDays`](#parameter-retentionpolicydays) | int | The number of days to retain an untagged manifest after which it gets purged. |
| [`retentionPolicyStatus`](#parameter-retentionpolicystatus) | string | The value that indicates whether the retention policy is enabled or not. |
@@ -619,7 +619,7 @@ module registry 'br/public:avm/res/container-registry/registry:' = {
| [`softDeletePolicyDays`](#parameter-softdeletepolicydays) | int | The number of days after which a soft-deleted item is permanently deleted. |
| [`softDeletePolicyStatus`](#parameter-softdeletepolicystatus) | string | Soft Delete policy status. Default is disabled. |
| [`tags`](#parameter-tags) | object | Tags of the resource. |
-| [`trustPolicyStatus`](#parameter-trustpolicystatus) | string | The value that indicates whether the trust policy is enabled or not. |
+| [`trustPolicyStatus`](#parameter-trustpolicystatus) | string | The value that indicates whether the trust policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'. |
| [`webhooks`](#parameter-webhooks) | array | All webhooks to create. |
| [`zoneRedundancy`](#parameter-zoneredundancy) | string | Whether or not zone redundancy is enabled for this container registry. |
@@ -1362,7 +1362,7 @@ Whether or not public network access is allowed for this resource. For security
### Parameter: `quarantinePolicyStatus`
-The value that indicates whether the quarantine policy is enabled or not.
+The value that indicates whether the quarantine policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'.
- Required: No
- Type: string
@@ -1526,7 +1526,7 @@ Tags of the resource.
### Parameter: `trustPolicyStatus`
-The value that indicates whether the trust policy is enabled or not.
+The value that indicates whether the trust policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'.
- Required: No
- Type: string
diff --git a/avm/res/container-registry/registry/main.bicep b/avm/res/container-registry/registry/main.bicep
index 126074e0665..059b59f804d 100644
--- a/avm/res/container-registry/registry/main.bicep
+++ b/avm/res/container-registry/registry/main.bicep
@@ -35,14 +35,14 @@ param exportPolicyStatus string = 'disabled'
'disabled'
'enabled'
])
-@description('Optional. The value that indicates whether the quarantine policy is enabled or not.')
+@description('Optional. The value that indicates whether the quarantine policy is enabled or not. Note, requires the \'acrSku\' to be \'Premium\'.')
param quarantinePolicyStatus string = 'disabled'
@allowed([
'disabled'
'enabled'
])
-@description('Optional. The value that indicates whether the trust policy is enabled or not.')
+@description('Optional. The value that indicates whether the trust policy is enabled or not. Note, requires the \'acrSku\' to be \'Premium\'.')
param trustPolicyStatus string = 'disabled'
@allowed([
@@ -148,8 +148,8 @@ var formattedUserAssignedIdentities = reduce(
var identity = !empty(managedIdentities)
? {
type: (managedIdentities.?systemAssigned ?? false)
- ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned')
- : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : null)
+ ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned, UserAssigned' : 'SystemAssigned')
+ : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None')
userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null
}
: null
@@ -183,47 +183,43 @@ var builtInRoleNames = {
)
}
-resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' =
- if (enableTelemetry) {
- name: '46d3xbcp.res.containerregistry-registry.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
- properties: {
- mode: 'Incremental'
- template: {
- '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
- contentVersion: '1.0.0.0'
- resources: []
- outputs: {
- telemetry: {
- type: 'String'
- value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
- }
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: '46d3xbcp.res.containerregistry-registry.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
+ properties: {
+ mode: 'Incremental'
+ template: {
+ '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
+ contentVersion: '1.0.0.0'
+ resources: []
+ outputs: {
+ telemetry: {
+ type: 'String'
+ value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
}
}
}
}
+}
-resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing =
- if (!empty(customerManagedKey.?keyVaultResourceId)) {
- name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/'))
- scope: resourceGroup(
- split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2],
- split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]
- )
-
- resource cMKKey 'keys@2023-02-01' existing =
- if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) {
- name: customerManagedKey.?keyName ?? 'dummyKey'
- }
- }
+resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) {
+ name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/'))
+ scope: resourceGroup(
+ split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2],
+ split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4]
+ )
-resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing =
- if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) {
- name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/'))
- scope: resourceGroup(
- split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2],
- split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]
- )
+ resource cMKKey 'keys@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) {
+ name: customerManagedKey.?keyName ?? 'dummyKey'
}
+}
+
+resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) {
+ name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/'))
+ scope: resourceGroup(
+ split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2],
+ split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4]
+ )
+}
resource registry 'Microsoft.ContainerRegistry/registries@2023-06-01-preview' = {
name: name
@@ -258,13 +254,17 @@ resource registry 'Microsoft.ContainerRegistry/registries@2023-06-01-preview' =
status: exportPolicyStatus
}
: null
- quarantinePolicy: {
- status: quarantinePolicyStatus
- }
- trustPolicy: {
- type: 'Notary'
- status: trustPolicyStatus
- }
+ quarantinePolicy: acrSku == 'Premium'
+ ? {
+ status: quarantinePolicyStatus
+ }
+ : null
+ trustPolicy: acrSku == 'Premium'
+ ? {
+ type: 'Notary'
+ status: trustPolicyStatus
+ }
+ : null
retentionPolicy: acrSku == 'Premium'
? {
days: retentionPolicyDays
@@ -341,17 +341,16 @@ module registry_webhooks 'webhook/main.bicep' = [
}
]
-resource registry_lock 'Microsoft.Authorization/locks@2020-05-01' =
- if (!empty(lock ?? {}) && lock.?kind != 'None') {
- name: lock.?name ?? 'lock-${name}'
- properties: {
- level: lock.?kind ?? ''
- notes: lock.?kind == 'CanNotDelete'
- ? 'Cannot delete resource or child resources.'
- : 'Cannot delete or modify the resource or child resources.'
- }
- scope: registry
+resource registry_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
+ name: lock.?name ?? 'lock-${name}'
+ properties: {
+ level: lock.?kind ?? ''
+ notes: lock.?kind == 'CanNotDelete'
+ ? 'Cannot delete resource or child resources.'
+ : 'Cannot delete or modify the resource or child resources.'
}
+ scope: registry
+}
resource registry_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [
for (diagnosticSetting, index) in (diagnosticSettings ?? []): {
diff --git a/avm/res/container-registry/registry/main.json b/avm/res/container-registry/registry/main.json
index b1762109724..d9cba422988 100644
--- a/avm/res/container-registry/registry/main.json
+++ b/avm/res/container-registry/registry/main.json
@@ -5,8 +5,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "15445287807627348186"
+ "version": "0.27.1.19265",
+ "templateHash": "10889694252440800848"
},
"name": "Azure Container Registries (ACR)",
"description": "This module deploys an Azure Container Registry (ACR).",
@@ -523,7 +523,7 @@
"enabled"
],
"metadata": {
- "description": "Optional. The value that indicates whether the quarantine policy is enabled or not."
+ "description": "Optional. The value that indicates whether the quarantine policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'."
}
},
"trustPolicyStatus": {
@@ -534,7 +534,7 @@
"enabled"
],
"metadata": {
- "description": "Optional. The value that indicates whether the trust policy is enabled or not."
+ "description": "Optional. The value that indicates whether the trust policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'."
}
},
"retentionPolicyStatus": {
@@ -717,7 +717,7 @@
},
"variables": {
"formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]",
- "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]",
+ "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]",
"builtInRoleNames": {
"AcrDelete": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c2f4ef07-c644-48eb-af81-4b1b4947fb11')]",
"AcrImageSigner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6cef56e8-d556-48e5-a04f-b8e64114680f')]",
@@ -802,13 +802,8 @@
"status": "[parameters('azureADAuthenticationAsArmPolicyStatus')]"
},
"exportPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('exportPolicyStatus')), null())]",
- "quarantinePolicy": {
- "status": "[parameters('quarantinePolicyStatus')]"
- },
- "trustPolicy": {
- "type": "Notary",
- "status": "[parameters('trustPolicyStatus')]"
- },
+ "quarantinePolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('quarantinePolicyStatus')), null())]",
+ "trustPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('type', 'Notary', 'status', parameters('trustPolicyStatus')), null())]",
"retentionPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('days', parameters('retentionPolicyDays'), 'status', parameters('retentionPolicyStatus')), null())]",
"softDeletePolicy": {
"retentionDays": "[parameters('softDeletePolicyDays')]",
@@ -943,8 +938,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "16451316437757175736"
+ "version": "0.27.1.19265",
+ "templateHash": "17370607380629293508"
},
"name": "Azure Container Registry (ACR) Replications",
"description": "This module deploys an Azure Container Registry (ACR) Replication.",
@@ -1091,8 +1086,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "17108035841365544326"
+ "version": "0.27.1.19265",
+ "templateHash": "15228477210534278013"
},
"name": "Container Registries Cache",
"description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache)).",
@@ -1232,8 +1227,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "10731035117081750792"
+ "version": "0.27.1.19265",
+ "templateHash": "12261942841024526503"
},
"name": "Azure Container Registry (ACR) Webhooks",
"description": "This module deploys an Azure Container Registry (ACR) Webhook.",
From 5cd812498a197ef7268bddf41114127b4c77146a Mon Sep 17 00:00:00 2001
From: Alexander Sehr
Date: Tue, 21 May 2024 18:24:02 +0200
Subject: [PATCH 51/52] fix: Fixed nested UDT resolution in compliance tests
(#1971)
## Description
- Added missing case from parameter resolution in tests similar to #1970
- Fixed Cosmos DB
- Ran script for all modules
Supporting
```bicep
param test myArrayType[]
type myArrayType = { ... }
```
Before it was only
```bicep
param test myArrayType
type myArrayType = { ... }[]
```
## Pipeline Reference
| Pipeline |
| -------- |
[![avm.ptn.authorization.policy-assignment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.authorization.policy-assignment.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.authorization.policy-assignment.yml)
[![avm.ptn.authorization.role-assignment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.authorization.role-assignment.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.authorization.role-assignment.yml)
[![avm.ptn.policy-insights.remediation](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.policy-insights.remediation.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.policy-insights.remediation.yml)
[![avm.ptn.security.security-center](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.security.security-center.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.security.security-center.yml)
[![avm.res.aad.domain-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.aad.domain-service.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.aad.domain-service.yml)
[![avm.res.analysis-services.server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.analysis-services.server.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.analysis-services.server.yml)
[![avm.res.api-management.service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.api-management.service.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.api-management.service.yml)
[![avm.res.app-configuration.configuration-store](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app-configuration.configuration-store.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app-configuration.configuration-store.yml)
[![avm.res.app.container-app](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.container-app.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.container-app.yml)
[![avm.res.app.job](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.job.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.job.yml)
[![avm.res.app.managed-environment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.managed-environment.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.managed-environment.yml)
[![avm.res.automation.automation-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.automation.automation-account.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.automation.automation-account.yml)
[![avm.res.batch.batch-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.batch.batch-account.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.batch.batch-account.yml)
[![avm.res.cache.redis](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml)
[![avm.res.cdn.profile](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cdn.profile.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cdn.profile.yml)
[![avm.res.cognitive-services.account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml)
[![avm.res.communication.communication-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.communication.communication-service.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.communication.communication-service.yml)
[![avm.res.communication.email-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.communication.email-service.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.communication.email-service.yml)
[![avm.res.compute.availability-set](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.availability-set.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.availability-set.yml)
[![avm.res.compute.disk-encryption-set](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.disk-encryption-set.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.disk-encryption-set.yml)
[![avm.res.compute.disk](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.disk.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.disk.yml)
[![avm.res.compute.gallery](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.gallery.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.gallery.yml)
[![avm.res.compute.image](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.image.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.image.yml)
[![avm.res.compute.proximity-placement-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.proximity-placement-group.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.proximity-placement-group.yml)
[![avm.res.compute.ssh-public-key](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.ssh-public-key.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.ssh-public-key.yml)
[![avm.res.compute.virtual-machine-scale-set](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine-scale-set.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine-scale-set.yml)
[![avm.res.compute.virtual-machine](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine.yml)
[![avm.res.consumption.budget](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.consumption.budget.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.consumption.budget.yml)
[![avm.res.container-instance.container-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-instance.container-group.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-instance.container-group.yml)
[![avm.res.container-registry.registry](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml)
[![avm.res.container-service.managed-cluster](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml)
[![avm.res.data-factory.factory](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml)
[![avm.res.data-protection.backup-vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-protection.backup-vault.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-protection.backup-vault.yml)
[![avm.res.databricks.access-connector](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.databricks.access-connector.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.databricks.access-connector.yml)
[![avm.res.databricks.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml)
[![avm.res.db-for-my-sql.flexible-server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.db-for-my-sql.flexible-server.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.db-for-my-sql.flexible-server.yml)
[![avm.res.db-for-postgre-sql.flexible-server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.db-for-postgre-sql.flexible-server.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.db-for-postgre-sql.flexible-server.yml)
(unrelated)
[![avm.res.desktop-virtualization.application-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.application-group.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.application-group.yml)
[![avm.res.desktop-virtualization.host-pool](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.host-pool.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.host-pool.yml)
[![avm.res.desktop-virtualization.scaling-plan](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.scaling-plan.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.scaling-plan.yml)
[![avm.res.desktop-virtualization.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.workspace.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.workspace.yml)
[![avm.res.dev-test-lab.lab](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.dev-test-lab.lab.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.dev-test-lab.lab.yml)
[![avm.res.digital-twins.digital-twins-instance](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.digital-twins.digital-twins-instance.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.digital-twins.digital-twins-instance.yml)
[![avm.res.document-db.database-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.document-db.database-account.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.document-db.database-account.yml)
[![avm.res.event-grid.domain](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.domain.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.domain.yml)
[![avm.res.event-grid.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.namespace.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.namespace.yml)
[![avm.res.event-grid.system-topic](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.system-topic.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.system-topic.yml)
[![avm.res.event-grid.topic](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.topic.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.topic.yml)
[![avm.res.event-hub.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-hub.namespace.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-hub.namespace.yml)
[![avm.res.health-bot.health-bot](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.health-bot.health-bot.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.health-bot.health-bot.yml)
[![avm.res.healthcare-apis.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.healthcare-apis.workspace.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.healthcare-apis.workspace.yml)
[![avm.res.insights.action-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.action-group.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.action-group.yml)
[![avm.res.insights.activity-log-alert](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.activity-log-alert.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.activity-log-alert.yml)
[![avm.res.insights.component](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.component.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.component.yml)
[![avm.res.insights.data-collection-endpoint](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-endpoint.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-endpoint.yml)
[![avm.res.insights.data-collection-rule](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-rule.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-rule.yml)
[![avm.res.insights.diagnostic-setting](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.diagnostic-setting.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.diagnostic-setting.yml)
[![avm.res.insights.metric-alert](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.metric-alert.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.metric-alert.yml)
[![avm.res.insights.private-link-scope](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.private-link-scope.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.private-link-scope.yml)
[![avm.res.insights.scheduled-query-rule](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.scheduled-query-rule.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.scheduled-query-rule.yml)
[![avm.res.insights.webtest](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.webtest.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.webtest.yml)
[![avm.res.key-vault.vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml)
[![avm.res.kubernetes-configuration.extension](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kubernetes-configuration.extension.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kubernetes-configuration.extension.yml)
[![avm.res.kubernetes-configuration.flux-configuration](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kubernetes-configuration.flux-configuration.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kubernetes-configuration.flux-configuration.yml)
[![avm.res.load-test-service.load-test](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.load-test-service.load-test.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.load-test-service.load-test.yml)
[![avm.res.logic.workflow](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.logic.workflow.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.logic.workflow.yml)
[![avm.res.machine-learning-services.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.machine-learning-services.workspace.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.machine-learning-services.workspace.yml)
[![avm.res.maintenance.maintenance-configuration](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.maintenance.maintenance-configuration.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.maintenance.maintenance-configuration.yml)
[![avm.res.managed-identity.user-assigned-identity](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.managed-identity.user-assigned-identity.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.managed-identity.user-assigned-identity.yml)
[![avm.res.managed-services.registration-definition](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.managed-services.registration-definition.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.managed-services.registration-definition.yml)
[![avm.res.management.management-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.management.management-group.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.management.management-group.yml)
[![avm.res.net-app.net-app-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.net-app.net-app-account.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.net-app.net-app-account.yml)
[![avm.res.network.application-gateway-web-application-firewall-policy](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway-web-application-firewall-policy.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway-web-application-firewall-policy.yml)
[![avm.res.network.application-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway.yml)
[![avm.res.network.application-security-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-security-group.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-security-group.yml)
[![avm.res.network.azure-firewall](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml)
[![avm.res.network.bastion-host](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.bastion-host.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.bastion-host.yml)
[![avm.res.network.connection](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.connection.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.connection.yml)
[![avm.res.network.ddos-protection-plan](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.ddos-protection-plan.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.ddos-protection-plan.yml)
[![avm.res.network.dns-forwarding-ruleset](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-forwarding-ruleset.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-forwarding-ruleset.yml)
[![avm.res.network.dns-resolver](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-resolver.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-resolver.yml)
[![avm.res.network.dns-zone](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-zone.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-zone.yml)
[![avm.res.network.express-route-circuit](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.express-route-circuit.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.express-route-circuit.yml)
[![avm.res.network.express-route-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.express-route-gateway.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.express-route-gateway.yml)
[![avm.res.network.firewall-policy](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.firewall-policy.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.firewall-policy.yml)
[![avm.res.network.front-door-web-application-firewall-policy](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.front-door-web-application-firewall-policy.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.front-door-web-application-firewall-policy.yml)
[![avm.res.network.front-door](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.front-door.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.front-door.yml)
[![avm.res.network.ip-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.ip-group.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.ip-group.yml)
[![avm.res.network.load-balancer](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.load-balancer.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.load-balancer.yml)
[![avm.res.network.local-network-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.local-network-gateway.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.local-network-gateway.yml)
[![avm.res.network.nat-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.nat-gateway.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.nat-gateway.yml)
[![avm.res.network.network-interface](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-interface.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-interface.yml)
[![avm.res.network.network-manager](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-manager.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-manager.yml)
[![avm.res.network.network-security-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-security-group.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-security-group.yml)
[![avm.res.network.network-watcher](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-watcher.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-watcher.yml)
[![avm.res.network.private-dns-zone](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-dns-zone.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-dns-zone.yml)
[![avm.res.network.private-endpoint](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-endpoint.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-endpoint.yml)
[![avm.res.network.private-link-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-link-service.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-link-service.yml)
[![avm.res.network.public-ip-address](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-address.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-address.yml)
[![avm.res.network.public-ip-prefix](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-prefix.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-prefix.yml)
[![avm.res.network.route-table](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.route-table.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.route-table.yml)
[![avm.res.network.service-endpoint-policy](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.service-endpoint-policy.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.service-endpoint-policy.yml)
[![avm.res.network.trafficmanagerprofile](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.trafficmanagerprofile.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.trafficmanagerprofile.yml)
[![avm.res.network.virtual-hub](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-hub.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-hub.yml)
[![avm.res.network.virtual-network-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network-gateway.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network-gateway.yml)
[![avm.res.network.virtual-network](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network.yml)
[![avm.res.network.virtual-wan](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-wan.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-wan.yml)
[![avm.res.network.vpn-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.vpn-gateway.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.vpn-gateway.yml)
[![avm.res.network.vpn-site](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.vpn-site.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.vpn-site.yml)
[![avm.res.operational-insights.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.operational-insights.workspace.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.operational-insights.workspace.yml)
[![avm.res.operations-management.solution](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.operations-management.solution.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.operations-management.solution.yml)
[![avm.res.power-bi-dedicated.capacity](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.power-bi-dedicated.capacity.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.power-bi-dedicated.capacity.yml)
[![avm.res.purview.account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.purview.account.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.purview.account.yml)
[![avm.res.recovery-services.vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml)
[![avm.res.relay.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.relay.namespace.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.relay.namespace.yml)
[![avm.res.resource-graph.query](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resource-graph.query.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resource-graph.query.yml)
[![avm.res.resources.deployment-script](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resources.deployment-script.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resources.deployment-script.yml)
[![avm.res.resources.resource-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resources.resource-group.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resources.resource-group.yml)
[![avm.res.search.search-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.search.search-service.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.search.search-service.yml)
[![avm.res.service-bus.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml)
[![avm.res.service-fabric.cluster](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-fabric.cluster.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-fabric.cluster.yml)
[![avm.res.signal-r-service.signal-r](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.signal-r.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.signal-r.yml)
[![avm.res.signal-r-service.web-pub-sub](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.web-pub-sub.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.web-pub-sub.yml)
[![avm.res.sql.instance-pool](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.instance-pool.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.instance-pool.yml)
[![avm.res.sql.managed-instance](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.managed-instance.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.managed-instance.yml)
[![avm.res.sql.server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml)
[![avm.res.storage.storage-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml)
(unrelated)
[![avm.res.synapse.private-link-hub](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.private-link-hub.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.private-link-hub.yml)
[![avm.res.synapse.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.workspace.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.workspace.yml)
[![avm.res.virtual-machine-images.image-template](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.virtual-machine-images.image-template.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.virtual-machine-images.image-template.yml)
[![avm.res.web.connection](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.connection.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.connection.yml)
[![avm.res.web.hosting-environment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.hosting-environment.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.hosting-environment.yml)
[![avm.res.web.serverfarm](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml)
[![avm.res.web.site](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.site.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.site.yml)
[![avm.res.web.static-site](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.static-site.yml/badge.svg?branch=users%2Falsehr%2FudtTestsFix&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.static-site.yml)
## Type of Change
- [x] Update to CI Environment or utlities (Non-module effecting
changes)
- [ ] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
---------
Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com>
---
avm/res/document-db/database-account/README.md | 8 ++++----
avm/res/document-db/database-account/main.bicep | 4 ++--
avm/res/document-db/database-account/main.json | 6 +++---
.../staticValidation/compliance/helper/helper.psm1 | 6 +++++-
.../staticValidation/compliance/module.tests.ps1 | 4 ++--
5 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/avm/res/document-db/database-account/README.md b/avm/res/document-db/database-account/README.md
index 30434814bc2..73c60b1d9be 100644
--- a/avm/res/document-db/database-account/README.md
+++ b/avm/res/document-db/database-account/README.md
@@ -2594,7 +2594,7 @@ Default to the location where the account is deployed. Locations enabled for the
| Parameter | Type | Description |
| :-- | :-- | :-- |
-| [`isZoneRedundant`](#parameter-locationsiszoneredundant) | bool | Default to true. Flag to indicate whether or not this region is an AvailabilityZone region |
+| [`isZoneRedundant`](#parameter-locationsiszoneredundant) | bool | Default to true. Flag to indicate whether or not this region is an AvailabilityZone region. |
### Parameter: `locations.failoverPriority`
@@ -2612,7 +2612,7 @@ The name of the region.
### Parameter: `locations.isZoneRedundant`
-Default to true. Flag to indicate whether or not this region is an AvailabilityZone region
+Default to true. Flag to indicate whether or not this region is an AvailabilityZone region.
- Required: No
- Type: bool
@@ -3507,11 +3507,11 @@ The unique key policy configuration containing a list of unique keys that enforc
| Parameter | Type | Description |
| :-- | :-- | :-- |
-| [`paths`](#parameter-sqldatabasescontainersuniquekeypolicykeyspaths) | array | List of paths must be unique for each document in the Azure Cosmos DB service |
+| [`paths`](#parameter-sqldatabasescontainersuniquekeypolicykeyspaths) | array | List of paths must be unique for each document in the Azure Cosmos DB service. |
### Parameter: `sqlDatabases.containers.uniqueKeyPolicyKeys.paths`
-List of paths must be unique for each document in the Azure Cosmos DB service
+List of paths must be unique for each document in the Azure Cosmos DB service.
- Required: Yes
- Type: array
diff --git a/avm/res/document-db/database-account/main.bicep b/avm/res/document-db/database-account/main.bicep
index 65501fdbbf5..9e95fc47a22 100644
--- a/avm/res/document-db/database-account/main.bicep
+++ b/avm/res/document-db/database-account/main.bicep
@@ -715,7 +715,7 @@ type failoverLocationsType = {
@description('Required. The failover priority of the region. A failover priority of 0 indicates a write region. The maximum value for a failover priority = (total number of regions - 1). Failover priority values must be unique for each of the regions in which the database account exists.')
failoverPriority: int
- @description('Optional. Default to true. Flag to indicate whether or not this region is an AvailabilityZone region')
+ @description('Optional. Default to true. Flag to indicate whether or not this region is an AvailabilityZone region.')
isZoneRedundant: bool?
@description('Required. The name of the region.')
@@ -777,7 +777,7 @@ type sqlDatabaseType = {
@description('Optional. The unique key policy configuration containing a list of unique keys that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service.')
uniqueKeyPolicyKeys: {
- @description('Required. List of paths must be unique for each document in the Azure Cosmos DB service')
+ @description('Required. List of paths must be unique for each document in the Azure Cosmos DB service.')
paths: string[]
}[]?
}[]?
diff --git a/avm/res/document-db/database-account/main.json b/avm/res/document-db/database-account/main.json
index e1a6ae712b1..f5121fe2cd4 100644
--- a/avm/res/document-db/database-account/main.json
+++ b/avm/res/document-db/database-account/main.json
@@ -6,7 +6,7 @@
"_generator": {
"name": "bicep",
"version": "0.27.1.19265",
- "templateHash": "13227324731926560272"
+ "templateHash": "8399904898851436683"
},
"name": "DocumentDB Database Accounts",
"description": "This module deploys a DocumentDB Database Account.",
@@ -456,7 +456,7 @@
"type": "bool",
"nullable": true,
"metadata": {
- "description": "Optional. Default to true. Flag to indicate whether or not this region is an AvailabilityZone region"
+ "description": "Optional. Default to true. Flag to indicate whether or not this region is an AvailabilityZone region."
}
},
"locationName": {
@@ -605,7 +605,7 @@
"type": "string"
},
"metadata": {
- "description": "Required. List of paths must be unique for each document in the Azure Cosmos DB service"
+ "description": "Required. List of paths must be unique for each document in the Azure Cosmos DB service."
}
}
}
diff --git a/avm/utilities/pipelines/staticValidation/compliance/helper/helper.psm1 b/avm/utilities/pipelines/staticValidation/compliance/helper/helper.psm1
index cd761d66a9d..d5839c3cafc 100644
--- a/avm/utilities/pipelines/staticValidation/compliance/helper/helper.psm1
+++ b/avm/utilities/pipelines/staticValidation/compliance/helper/helper.psm1
@@ -246,8 +246,12 @@ function Resolve-ReadMeParameterList {
$definition = $TemplateFileContent.definitions[$identifier]
# $type = $definition['type']
} elseif ($parameter.Keys -contains 'items' -and $parameter.items.type -in @('object', 'array') -or $parameter.type -eq 'object') {
- # Array has nested non-primitive type (array/object)
+ # Array has nested non-primitive type (array/object) - and if array, the the UDT itself is declared as the array
$definition = $parameter
+ } elseif ($parameter.Keys -contains 'items' -and $parameter.items.keys -contains '$ref') {
+ # Array has nested non-primitive type (array) - and the parameter is defined as an array of the UDT
+ $identifier = Split-Path $parameter.items.'$ref' -Leaf
+ $definition = $TemplateFileContent.definitions[$identifier]
} else {
$definition = $null
}
diff --git a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1
index 86e82537524..6ac67f95ac5 100644
--- a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1
+++ b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1
@@ -814,9 +814,9 @@ Describe 'Module tests' -Tag 'Module' {
$Variables = $templateFileContent.variables.Keys
foreach ($variable in $Variables) {
- # ^[a-z]+[a-zA-Z]+$ = starts with lower-case letter & may have uppercase letter later
+ # ^[a-z]+[a-zA-Z0-9]+$ = starts with lower-case letter & may have uppercase letter or numbers later
# ^\$fxv#[0-9]+$ = starts with [$fxv#] & ends with a number. This function value is created as a variable when using a Bicep function like loadFileAsBase64() or loadFromJson()
- if ($variable -cnotmatch '^[a-z]+[a-zA-Z]+$|^\$fxv#[0-9]+$' -or $variable -match '-') {
+ if ($variable -cnotmatch '^[a-z]+[a-zA-Z0-9]+$|^\$fxv#[0-9]+$' -or $variable -match '-') {
$incorrectVariables += $variable
}
}
From 07699bdb709809e49fd61d85b931f280a6407d58 Mon Sep 17 00:00:00 2001
From: MichielVanHerreweghe
<169037533+MichielVanHerreweghe@users.noreply.github.com>
Date: Tue, 21 May 2024 22:10:02 +0200
Subject: [PATCH 52/52] feat: Container App added Sticky Session Afinity
options (#1936)
## Description
Added the option to enable session affinity on the container app
## Pipeline Reference
| Pipeline |
| -------- |
|
[![avm.res.app.container-app](https://github.com/MichielVanHerreweghe/bicep-registry-modules/actions/workflows/avm.res.app.container-app.yml/badge.svg?branch=users%2Fmichielvanherreweghe%2Fcontainer-app-ingress-affinity)](https://github.com/MichielVanHerreweghe/bicep-registry-modules/actions/workflows/avm.res.app.container-app.yml)
|
## Type of Change
- [ ] Update to CI Environment or utlities (Non-module effecting
changes)
- [x] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [x] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
## Checklist
- [x] I'm sure there are no other open Pull Requests for the same
update/change
- [x] I have run `Set-AVMModule` locally to generate the supporting
module files.
- [x] My corresponding pipelines / checks run clean and green without
any errors or warnings
---------
Co-authored-by: Zach Trocinski <30884663+oZakari@users.noreply.github.com>
---
avm/res/app/container-app/README.md | 16 ++++++++
avm/res/app/container-app/main.bicep | 56 +++++++++++++++-----------
avm/res/app/container-app/main.json | 18 ++++++++-
avm/res/app/container-app/version.json | 4 +-
4 files changed, 66 insertions(+), 28 deletions(-)
diff --git a/avm/res/app/container-app/README.md b/avm/res/app/container-app/README.md
index 236de183449..794ec770c08 100644
--- a/avm/res/app/container-app/README.md
+++ b/avm/res/app/container-app/README.md
@@ -484,6 +484,7 @@ module containerApp 'br/public:avm/res/app/container-app:' = {
| [`scaleMinReplicas`](#parameter-scaleminreplicas) | int | Minimum number of container replicas. Defaults to 3 if not set. |
| [`scaleRules`](#parameter-scalerules) | array | Scaling rules. |
| [`secrets`](#parameter-secrets) | secureObject | The secrets of the Container App. |
+| [`stickySessionsAffinity`](#parameter-stickysessionsaffinity) | string | Bool indicating if the Container App should enable session affinity. |
| [`tags`](#parameter-tags) | object | Tags of the resource. |
| [`trafficLabel`](#parameter-trafficlabel) | string | Associates a traffic label with a revision. Label name should be consist of lower case alphanumeric characters or dashes. |
| [`trafficLatestRevision`](#parameter-trafficlatestrevision) | bool | Indicates that the traffic weight belongs to a latest stable revision. |
@@ -834,6 +835,21 @@ The secrets of the Container App.
- Type: secureObject
- Default: `{}`
+### Parameter: `stickySessionsAffinity`
+
+Bool indicating if the Container App should enable session affinity.
+
+- Required: No
+- Type: string
+- Default: `'none'`
+- Allowed:
+ ```Bicep
+ [
+ 'none'
+ 'sticky'
+ ]
+ ```
+
### Parameter: `tags`
Tags of the resource.
diff --git a/avm/res/app/container-app/main.bicep b/avm/res/app/container-app/main.bicep
index 7ef6ed7246e..8ff404eaf96 100644
--- a/avm/res/app/container-app/main.bicep
+++ b/avm/res/app/container-app/main.bicep
@@ -11,6 +11,13 @@ param location string = resourceGroup().location
@description('Optional. Bool indicating if the App exposes an external HTTP endpoint.')
param ingressExternal bool = true
+@allowed([
+ 'none'
+ 'sticky'
+])
+@description('Optional. Bool indicating if the Container App should enable session affinity.')
+param stickySessionsAffinity string = 'none'
+
@allowed([
'auto'
'http'
@@ -144,24 +151,23 @@ var builtInRoleNames = {
)
}
-resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' =
- if (enableTelemetry) {
- name: '46d3xbcp.res.app-containerapp.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
- properties: {
- mode: 'Incremental'
- template: {
- '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
- contentVersion: '1.0.0.0'
- resources: []
- outputs: {
- telemetry: {
- type: 'String'
- value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
- }
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: '46d3xbcp.res.app-containerapp.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
+ properties: {
+ mode: 'Incremental'
+ template: {
+ '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
+ contentVersion: '1.0.0.0'
+ resources: []
+ outputs: {
+ telemetry: {
+ type: 'String'
+ value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
}
}
}
}
+}
resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
name: name
@@ -180,6 +186,9 @@ resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
external: ingressExternal
ipSecurityRestrictions: !empty(ipSecurityRestrictions) ? ipSecurityRestrictions : null
targetPort: ingressTargetPort
+ stickySessions: {
+ affinity: stickySessionsAffinity
+ }
traffic: [
{
label: trafficLabel
@@ -209,17 +218,16 @@ resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
}
}
-resource containerApp_lock 'Microsoft.Authorization/locks@2020-05-01' =
- if (!empty(lock ?? {}) && lock.?kind != 'None') {
- name: lock.?name ?? 'lock-${name}'
- properties: {
- level: lock.?kind ?? ''
- notes: lock.?kind == 'CanNotDelete'
- ? 'Cannot delete resource or child resources.'
- : 'Cannot delete or modify the resource or child resources.'
- }
- scope: containerApp
+resource containerApp_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
+ name: lock.?name ?? 'lock-${name}'
+ properties: {
+ level: lock.?kind ?? ''
+ notes: lock.?kind == 'CanNotDelete'
+ ? 'Cannot delete resource or child resources.'
+ : 'Cannot delete or modify the resource or child resources.'
}
+ scope: containerApp
+}
resource containerApp_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [
for (roleAssignment, index) in (roleAssignments ?? []): {
diff --git a/avm/res/app/container-app/main.json b/avm/res/app/container-app/main.json
index 97c1bf140af..2139556b3b8 100644
--- a/avm/res/app/container-app/main.json
+++ b/avm/res/app/container-app/main.json
@@ -5,8 +5,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.170.59819",
- "templateHash": "11829581770848647649"
+ "version": "0.27.1.19265",
+ "templateHash": "6545722573689550634"
},
"name": "Container Apps",
"description": "This module deploys a Container App.",
@@ -149,6 +149,17 @@
"description": "Optional. Bool indicating if the App exposes an external HTTP endpoint."
}
},
+ "stickySessionsAffinity": {
+ "type": "string",
+ "defaultValue": "none",
+ "allowedValues": [
+ "none",
+ "sticky"
+ ],
+ "metadata": {
+ "description": "Optional. Bool indicating if the Container App should enable session affinity."
+ }
+ },
"ingressTransport": {
"type": "string",
"defaultValue": "auto",
@@ -411,6 +422,9 @@
"external": "[parameters('ingressExternal')]",
"ipSecurityRestrictions": "[if(not(empty(parameters('ipSecurityRestrictions'))), parameters('ipSecurityRestrictions'), null())]",
"targetPort": "[parameters('ingressTargetPort')]",
+ "stickySessions": {
+ "affinity": "[parameters('stickySessionsAffinity')]"
+ },
"traffic": [
{
"label": "[parameters('trafficLabel')]",
diff --git a/avm/res/app/container-app/version.json b/avm/res/app/container-app/version.json
index 9481fea58ee..c177b1bb58b 100644
--- a/avm/res/app/container-app/version.json
+++ b/avm/res/app/container-app/version.json
@@ -1,7 +1,7 @@
{
"$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
- "version": "0.2",
+ "version": "0.3",
"pathFilters": [
"./main.json"
]
-}
+}
\ No newline at end of file