From aeb5d5b3d746e2a652c59cec6c43d436cb8bc54c Mon Sep 17 00:00:00 2001
From: Fabio Masciotra
+
+
+
+### Example 2: _Using large parameter set_
+
+This instance deploys the module with most of its features enabled.
+
+
+
+
+
+
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | Name of the VPN Site. |
+| [`virtualWanId`](#parameter-virtualwanid) | string | Resource ID of the virtual WAN to link to. |
+
+**Conditional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`addressPrefixes`](#parameter-addressprefixes) | array | An array of IP address ranges that can be used by subnets of the virtual network. Required if no bgpProperties or VPNSiteLinks are configured. |
+| [`bgpProperties`](#parameter-bgpproperties) | object | BGP settings details. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead. Required if no addressPrefixes or VPNSiteLinks are configured. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`deviceProperties`](#parameter-deviceproperties) | object | List of properties of the device. |
+| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). |
+| [`ipAddress`](#parameter-ipaddress) | string | The IP-address for the VPN-site. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead. |
+| [`isSecuritySite`](#parameter-issecuritysite) | bool | IsSecuritySite flag. |
+| [`location`](#parameter-location) | string | Location where all resources will be created. |
+| [`lock`](#parameter-lock) | object | The lock settings of the service. |
+| [`o365Policy`](#parameter-o365policy) | object | The Office365 breakout policy. |
+| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. |
+| [`tags`](#parameter-tags) | object | Tags of the resource. |
+| [`vpnSiteLinks`](#parameter-vpnsitelinks) | array | List of all VPN site links. |
+
+### Parameter: `name`
+
+Name of the VPN Site.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `virtualWanId`
+
+Resource ID of the virtual WAN to link to.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `addressPrefixes`
+
+An array of IP address ranges that can be used by subnets of the virtual network. Required if no bgpProperties or VPNSiteLinks are configured.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+### Parameter: `bgpProperties`
+
+BGP settings details. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead. Required if no addressPrefixes or VPNSiteLinks are configured.
+
+- Required: No
+- Type: object
+- Default: `{}`
+
+### Parameter: `deviceProperties`
+
+List of properties of the device.
+
+- Required: No
+- Type: object
+- Default: `{}`
+
+### Parameter: `enableTelemetry`
+
+Enable telemetry via a Globally Unique Identifier (GUID).
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `ipAddress`
+
+The IP-address for the VPN-site. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead.
+
+- Required: No
+- Type: string
+- Default: `''`
+
+### Parameter: `isSecuritySite`
+
+IsSecuritySite flag.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `location`
+
+Location where all resources will be created.
+
+- 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: `o365Policy`
+
+The Office365 breakout policy.
+
+- Required: No
+- Type: object
+- Default: `{}`
+
+### 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`
+
+Tags of the resource.
+
+- Required: No
+- Type: object
+
+### Parameter: `vpnSiteLinks`
+
+List of all VPN site links.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `location` | string | The location the resource was deployed into. |
+| `name` | string | The name of the VPN site. |
+| `resourceGroupName` | string | The resource group the VPN site was deployed into. |
+| `resourceId` | string | The resource ID of the VPN site. |
+
+## Cross-referenced modules
+
+_None_
+
+## Notes
+
+### Parameter Usage `deviceProperties`
+
+
diff --git a/avm/res/network/vpn-site/main.bicep b/avm/res/network/vpn-site/main.bicep
new file mode 100644
index 0000000000..8e56b34ffa
--- /dev/null
+++ b/avm/res/network/vpn-site/main.bicep
@@ -0,0 +1,162 @@
+metadata name = 'VPN Sites'
+metadata description = 'This module deploys a VPN Site.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Required. Name of the VPN Site.')
+param name string
+
+@description('Required. Resource ID of the virtual WAN to link to.')
+param virtualWanId string
+
+@description('Optional. Location where all resources will be created.')
+param location string = resourceGroup().location
+
+@description('Optional. Tags of the resource.')
+param tags object?
+
+@description('Conditional. An array of IP address ranges that can be used by subnets of the virtual network. Required if no bgpProperties or VPNSiteLinks are configured.')
+param addressPrefixes array = []
+
+@description('Conditional. BGP settings details. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead. Required if no addressPrefixes or VPNSiteLinks are configured.')
+param bgpProperties object = {}
+
+@description('Optional. List of properties of the device.')
+param deviceProperties object = {}
+
+@description('Optional. The IP-address for the VPN-site. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead.')
+param ipAddress string = ''
+
+@description('Optional. IsSecuritySite flag.')
+param isSecuritySite bool = false
+
+@description('Optional. The Office365 breakout policy.')
+param o365Policy object = {}
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableTelemetry bool = true
+
+@description('Optional. List of all VPN site links.')
+param vpnSiteLinks 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')
+ 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')
+ 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: take('46d3xbcp.res.network-vpnsite.${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 vpnSite 'Microsoft.Network/vpnSites@2023-04-01' = {
+ name: name
+ location: location
+ tags: tags
+ properties: {
+ addressSpace: !empty(addressPrefixes) ? {
+ addressPrefixes: addressPrefixes
+ } : null
+ bgpProperties: !empty(bgpProperties) ? bgpProperties : null
+ deviceProperties: !empty(deviceProperties) ? deviceProperties : null
+ ipAddress: !empty(ipAddress) ? ipAddress : null
+ isSecuritySite: isSecuritySite
+ o365Policy: !empty(o365Policy) ? o365Policy : null
+ virtualWan: {
+ id: virtualWanId
+ }
+ vpnSiteLinks: !empty(vpnSiteLinks) ? vpnSiteLinks : null
+ }
+}
+
+resource vpnSite_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: vpnSite
+}
+
+resource vpnSite_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in (roleAssignments ?? []): {
+ name: guid(vpnSite.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: vpnSite
+}]
+
+@description('The name of the VPN site.')
+output name string = vpnSite.name
+
+@description('The resource ID of the VPN site.')
+output resourceId string = vpnSite.id
+
+@description('The resource group the VPN site was deployed into.')
+output resourceGroupName string = resourceGroup().name
+
+@description('The location the resource was deployed into.')
+output location string = vpnSite.location
+
+// =============== //
+// Definitions //
+// =============== //
+
+type lockType = {
+ @description('Optional. Specify the name of lock.')
+ name: string?
+
+ @description('Optional. Specify the type of lock.')
+ kind: ('CanNotDelete' | 'ReadOnly' | 'None')?
+}?
+
+type roleAssignmentType = {
+ @description('Required. The 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/network/vpn-site/main.json b/avm/res/network/vpn-site/main.json
new file mode 100644
index 0000000000..883d64853f
--- /dev/null
+++ b/avm/res/network/vpn-site/main.json
@@ -0,0 +1,321 @@
+{
+ "$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.23.1.45101",
+ "templateHash": "1751598884441695432"
+ },
+ "name": "VPN Sites",
+ "description": "This module deploys a VPN Site.",
+ "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",
+ "metadata": {
+ "description": "Required. Name of the VPN Site."
+ }
+ },
+ "virtualWanId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of the virtual WAN to link to."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location where all resources will be created."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ },
+ "addressPrefixes": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Conditional. An array of IP address ranges that can be used by subnets of the virtual network. Required if no bgpProperties or VPNSiteLinks are configured."
+ }
+ },
+ "bgpProperties": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Conditional. BGP settings details. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead. Required if no addressPrefixes or VPNSiteLinks are configured."
+ }
+ },
+ "deviceProperties": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. List of properties of the device."
+ }
+ },
+ "ipAddress": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The IP-address for the VPN-site. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead."
+ }
+ },
+ "isSecuritySite": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. IsSecuritySite flag."
+ }
+ },
+ "o365Policy": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. The Office365 breakout policy."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)."
+ }
+ },
+ "vpnSiteLinks": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. List of all VPN site links."
+ }
+ },
+ "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')]",
+ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]",
+ "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": "[take(format('46d3xbcp.res.network-vpnsite.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('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"
+ }
+ }
+ }
+ }
+ },
+ "vpnSite": {
+ "type": "Microsoft.Network/vpnSites",
+ "apiVersion": "2023-04-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "addressSpace": "[if(not(empty(parameters('addressPrefixes'))), createObject('addressPrefixes', parameters('addressPrefixes')), null())]",
+ "bgpProperties": "[if(not(empty(parameters('bgpProperties'))), parameters('bgpProperties'), null())]",
+ "deviceProperties": "[if(not(empty(parameters('deviceProperties'))), parameters('deviceProperties'), null())]",
+ "ipAddress": "[if(not(empty(parameters('ipAddress'))), parameters('ipAddress'), null())]",
+ "isSecuritySite": "[parameters('isSecuritySite')]",
+ "o365Policy": "[if(not(empty(parameters('o365Policy'))), parameters('o365Policy'), null())]",
+ "virtualWan": {
+ "id": "[parameters('virtualWanId')]"
+ },
+ "vpnSiteLinks": "[if(not(empty(parameters('vpnSiteLinks'))), parameters('vpnSiteLinks'), null())]"
+ }
+ },
+ "vpnSite_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.Network/vpnSites/{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": [
+ "vpnSite"
+ ]
+ },
+ "vpnSite_roleAssignments": {
+ "copy": {
+ "name": "vpnSite_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Network/vpnSites/{0}', parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.Network/vpnSites', 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": [
+ "vpnSite"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the VPN site."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the VPN site."
+ },
+ "value": "[resourceId('Microsoft.Network/vpnSites', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the VPN site was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('vpnSite', '2023-04-01', 'full').location]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/network/vpn-site/tests/e2e/defaults/dependencies.bicep b/avm/res/network/vpn-site/tests/e2e/defaults/dependencies.bicep
new file mode 100644
index 0000000000..bb151ad9d8
--- /dev/null
+++ b/avm/res/network/vpn-site/tests/e2e/defaults/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Required. The name of the virtual WAN to create.')
+param virtualWANName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+resource virtualWan 'Microsoft.Network/virtualWans@2023-04-01' = {
+ name: virtualWANName
+ location: location
+}
+
+@description('The resource ID of the created Virtual WAN.')
+output virtualWWANResourceId string = virtualWan.id
diff --git a/avm/res/network/vpn-site/tests/e2e/defaults/main.test.bicep b/avm/res/network/vpn-site/tests/e2e/defaults/main.test.bicep
new file mode 100644
index 0000000000..8a288af220
--- /dev/null
+++ b/avm/res/network/vpn-site/tests/e2e/defaults/main.test.bicep
@@ -0,0 +1,62 @@
+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}-network.vpnSites-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'nvsmin'
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ location:location
+ virtualWANName: 'dep-${namePrefix}-vw-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: location
+ name: '${namePrefix}-${serviceShort}'
+ virtualWanId: nestedDependencies.outputs.virtualWWANResourceId
+ addressPrefixes: [
+ '10.0.0.0/16'
+ ]
+ ipAddress: '1.2.3.4'
+ }
+}]
+
+
diff --git a/avm/res/network/vpn-site/tests/e2e/max/dependencies.bicep b/avm/res/network/vpn-site/tests/e2e/max/dependencies.bicep
new file mode 100644
index 0000000000..8e2694c27f
--- /dev/null
+++ b/avm/res/network/vpn-site/tests/e2e/max/dependencies.bicep
@@ -0,0 +1,24 @@
+@description('Required. The name of the managed identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the virtual WAN to create.')
+param virtualWANName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource virtualWan 'Microsoft.Network/virtualWans@2023-04-01' = {
+ name: virtualWANName
+ location: location
+}
+
+@description('The principal ID of the created managed identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Virtual WAN.')
+output virtualWWANResourceId string = virtualWan.id
diff --git a/avm/res/network/vpn-site/tests/e2e/max/main.test.bicep b/avm/res/network/vpn-site/tests/e2e/max/main.test.bicep
new file mode 100644
index 0000000000..463da28b48
--- /dev/null
+++ b/avm/res/network/vpn-site/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}-network.vpnSites-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'nvsmax'
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ location:location
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ virtualWANName: 'dep-${namePrefix}-vw-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: location
+ name: '${namePrefix}-${serviceShort}'
+ virtualWanId: nestedDependencies.outputs.virtualWWANResourceId
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ tagA: 'valueA'
+ tagB: 'valueB'
+ }
+ deviceProperties: {
+ linkSpeedInMbps: 0
+ }
+ vpnSiteLinks: [
+ {
+ name: '${namePrefix}-vSite-${serviceShort}'
+ properties: {
+ bgpProperties: {
+ asn: 65010
+ bgpPeeringAddress: '1.1.1.1'
+ }
+ ipAddress: '1.2.3.4'
+ linkProperties: {
+ linkProviderName: 'contoso'
+ linkSpeedInMbps: 5
+ }
+ }
+ }
+ {
+ name: 'Link1'
+ properties: {
+ bgpProperties: {
+ asn: 65020
+ bgpPeeringAddress: '192.168.1.0'
+ }
+ ipAddress: '2.2.2.2'
+ linkProperties: {
+ linkProviderName: 'contoso'
+ linkSpeedInMbps: 5
+ }
+ }
+ }
+ ]
+ o365Policy: {
+ breakOutCategories: {
+ optimize: true
+ allow: true
+ default: true
+ }
+ }
+ 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/network/vpn-site/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/network/vpn-site/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..8e2694c27f
--- /dev/null
+++ b/avm/res/network/vpn-site/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,24 @@
+@description('Required. The name of the managed identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the virtual WAN to create.')
+param virtualWANName string
+
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource virtualWan 'Microsoft.Network/virtualWans@2023-04-01' = {
+ name: virtualWANName
+ location: location
+}
+
+@description('The principal ID of the created managed identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
+
+@description('The resource ID of the created Virtual WAN.')
+output virtualWWANResourceId string = virtualWan.id
diff --git a/avm/res/network/vpn-site/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/vpn-site/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..1aedb61567
--- /dev/null
+++ b/avm/res/network/vpn-site/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,108 @@
+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}-network.vpnSites-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'nvswaf'
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ location:location
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ virtualWANName: 'dep-${namePrefix}-vw-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: location
+ name: '${namePrefix}-${serviceShort}'
+ virtualWanId: nestedDependencies.outputs.virtualWWANResourceId
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ tagA: 'valueA'
+ tagB: 'valueB'
+ }
+ deviceProperties: {
+ linkSpeedInMbps: 0
+ }
+ vpnSiteLinks: [
+ {
+ name: '${namePrefix}-vSite-${serviceShort}'
+ properties: {
+ bgpProperties: {
+ asn: 65010
+ bgpPeeringAddress: '1.1.1.1'
+ }
+ ipAddress: '1.2.3.4'
+ linkProperties: {
+ linkProviderName: 'contoso'
+ linkSpeedInMbps: 5
+ }
+ }
+ }
+ {
+ name: 'Link1'
+ properties: {
+ bgpProperties: {
+ asn: 65020
+ bgpPeeringAddress: '192.168.1.0'
+ }
+ ipAddress: '2.2.2.2'
+ linkProperties: {
+ linkProviderName: 'contoso'
+ linkSpeedInMbps: 5
+ }
+ }
+ }
+ ]
+ o365Policy: {
+ breakOutCategories: {
+ optimize: true
+ allow: true
+ default: true
+ }
+ }
+ }
+}]
+
+
diff --git a/avm/res/network/vpn-site/version.json b/avm/res/network/vpn-site/version.json
new file mode 100644
index 0000000000..8def869ede
--- /dev/null
+++ b/avm/res/network/vpn-site/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
+ "version": "0.1",
+ "pathFilters": [
+ "./main.json"
+ ]
+}
From 31bad2aa326ecab7a3815a37c0ce38909a345734 Mon Sep 17 00:00:00 2001
From: Kris Baranek
+
+
+
+### Example 2: _Using large parameter set_
+
+This instance deploys the module with most of its features enabled.
+
+
+
+
+
+
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | Name of the Azure Compute Gallery. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`applications`](#parameter-applications) | array | Applications to create. |
+| [`description`](#parameter-description) | string | Description of the Azure Shared Image Gallery. |
+| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. |
+| [`images`](#parameter-images) | array | Images to create. |
+| [`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 assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. |
+| [`tags`](#parameter-tags) | object | Tags for all resources. |
+
+### Parameter: `name`
+
+Name of the Azure Compute Gallery.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `applications`
+
+Applications to create.
+
+- Required: No
+- Type: array
+
+### Parameter: `description`
+
+Description of the Azure Shared Image Gallery.
+
+- Required: No
+- Type: string
+
+### Parameter: `enableTelemetry`
+
+Enable/Disable usage telemetry for module.
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `images`
+
+Images to create.
+
+- 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: `roleAssignments`
+
+Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.
+
+- 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 name of the role to assign. If it cannot be found you can specify the role definition ID instead. |
+
+**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 name of the role to assign. If it cannot be found you can specify the role definition ID instead.
+
+- 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`
+
+Tags for all resources.
+
+- Required: No
+- Type: object
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `location` | string | The location the resource was deployed into. |
+| `name` | string | The name of the deployed image gallery. |
+| `resourceGroupName` | string | The resource group of the deployed image gallery. |
+| `resourceId` | string | The resource ID of the deployed image gallery. |
+
+## Cross-referenced modules
+
+_None_
diff --git a/avm/res/compute/gallery/application/README.md b/avm/res/compute/gallery/application/README.md
new file mode 100644
index 0000000000..44732656d1
--- /dev/null
+++ b/avm/res/compute/gallery/application/README.md
@@ -0,0 +1,337 @@
+# Compute Galleries Applications `[Microsoft.Compute/galleries/applications]`
+
+This module deploys an Azure Compute Gallery Application.
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+- [Notes](#Notes)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) |
+| `Microsoft.Compute/galleries/applications` | [2022-03-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-03-03/galleries/applications) |
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | Name of the application definition. |
+
+**Conditional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`galleryName`](#parameter-galleryname) | string | The name of the parent Azure Compute Gallery. Required if the template is used in a standalone deployment. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`customActions`](#parameter-customactions) | array | A list of custom actions that can be performed with all of the Gallery Application Versions within this Gallery Application. |
+| [`description`](#parameter-description) | string | The description of this gallery Application Definition resource. This property is updatable. |
+| [`endOfLifeDate`](#parameter-endoflifedate) | string | The end of life date of the gallery Image Definition. This property can be used for decommissioning purposes. This property is updatable. Allowed format: 2020-01-10T23:00:00.000Z. |
+| [`eula`](#parameter-eula) | string | The Eula agreement for the gallery Application Definition. Has to be a valid URL. |
+| [`location`](#parameter-location) | string | Location for all resources. |
+| [`privacyStatementUri`](#parameter-privacystatementuri) | string | The privacy statement uri. Has to be a valid URL. |
+| [`releaseNoteUri`](#parameter-releasenoteuri) | string | The release note uri. Has to be a valid URL. |
+| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. |
+| [`supportedOSType`](#parameter-supportedostype) | string | This property allows you to specify the supported type of the OS that application is built for. |
+| [`tags`](#parameter-tags) | object | Tags for all resources. |
+
+### Parameter: `name`
+
+Name of the application definition.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `galleryName`
+
+The name of the parent Azure Compute Gallery. Required if the template is used in a standalone deployment.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `customActions`
+
+A list of custom actions that can be performed with all of the Gallery Application Versions within this Gallery Application.
+
+- Required: No
+- Type: array
+
+### Parameter: `description`
+
+The description of this gallery Application Definition resource. This property is updatable.
+
+- Required: No
+- Type: string
+
+### Parameter: `endOfLifeDate`
+
+The end of life date of the gallery Image Definition. This property can be used for decommissioning purposes. This property is updatable. Allowed format: 2020-01-10T23:00:00.000Z.
+
+- Required: No
+- Type: string
+
+### Parameter: `eula`
+
+The Eula agreement for the gallery Application Definition. Has to be a valid URL.
+
+- Required: No
+- Type: string
+
+### Parameter: `location`
+
+Location for all resources.
+
+- Required: No
+- Type: string
+- Default: `[resourceGroup().location]`
+
+### Parameter: `privacyStatementUri`
+
+The privacy statement uri. Has to be a valid URL.
+
+- Required: No
+- Type: string
+
+### Parameter: `releaseNoteUri`
+
+The release note uri. Has to be a valid URL.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments`
+
+Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.
+
+- 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 name of the role to assign. If it cannot be found you can specify the role definition ID instead. |
+
+**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 name of the role to assign. If it cannot be found you can specify the role definition ID instead.
+
+- 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: `supportedOSType`
+
+This property allows you to specify the supported type of the OS that application is built for.
+
+- Required: No
+- Type: string
+- Default: `'Windows'`
+- Allowed:
+ ```Bicep
+ [
+ 'Linux'
+ 'Windows'
+ ]
+ ```
+
+### Parameter: `tags`
+
+Tags for all resources.
+
+- Required: No
+- Type: object
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `location` | string | The location the resource was deployed into. |
+| `name` | string | The name of the image. |
+| `resourceGroupName` | string | The resource group the image was deployed into. |
+| `resourceId` | string | The resource ID of the image. |
+
+## Cross-referenced modules
+
+_None_
+
+## Notes
+
+### Parameter Usage: `customActions`
+
+Create a list of custom actions that can be performed with all of the Gallery Application Versions within this Gallery Application.
+
+
diff --git a/avm/res/compute/gallery/application/main.bicep b/avm/res/compute/gallery/application/main.bicep
new file mode 100644
index 0000000000..26cd930c44
--- /dev/null
+++ b/avm/res/compute/gallery/application/main.bicep
@@ -0,0 +1,125 @@
+metadata name = 'Compute Galleries Applications'
+metadata description = 'This module deploys an Azure Compute Gallery Application.'
+metadata owner = 'Azure/module-maintainers'
+
+@sys.description('Required. Name of the application definition.')
+param name string
+
+@sys.description('Optional. Location for all resources.')
+param location string = resourceGroup().location
+
+@sys.description('Conditional. The name of the parent Azure Compute Gallery. Required if the template is used in a standalone deployment.')
+@minLength(1)
+param galleryName string
+
+@sys.description('Optional. The description of this gallery Application Definition resource. This property is updatable.')
+param description string?
+
+@sys.description('Optional. The Eula agreement for the gallery Application Definition. Has to be a valid URL.')
+param eula string?
+
+@sys.description('Optional. The privacy statement uri. Has to be a valid URL.')
+param privacyStatementUri string?
+
+@sys.description('Optional. The release note uri. Has to be a valid URL.')
+param releaseNoteUri string?
+
+@sys.description('Optional. This property allows you to specify the supported type of the OS that application is built for.')
+@allowed([
+ 'Windows'
+ 'Linux'
+])
+param supportedOSType string = 'Windows'
+
+@sys.description('Optional. The end of life date of the gallery Image Definition. This property can be used for decommissioning purposes. This property is updatable. Allowed format: 2020-01-10T23:00:00.000Z.')
+param endOfLifeDate string?
+
+@sys.description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.')
+param roleAssignments roleAssignmentType
+
+@sys.description('Optional. Tags for all resources.')
+param tags object?
+
+@sys.description('Optional. A list of custom actions that can be performed with all of the Gallery Application Versions within this Gallery Application.')
+param customActions array?
+
+var builtInRoleNames = {
+ 'Compute Gallery Sharing Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1ef6a3be-d0ac-425d-8c01-acb62866290b')
+ 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 gallery 'Microsoft.Compute/galleries@2022-03-03' existing = {
+ name: galleryName
+}
+
+resource application 'Microsoft.Compute/galleries/applications@2022-03-03' = {
+ name: name
+ parent: gallery
+ location: location
+ tags: tags
+ properties: {
+ customActions: customActions
+ description: description ?? ''
+ endOfLifeDate: endOfLifeDate ?? ''
+ eula: eula ?? ''
+ privacyStatementUri: privacyStatementUri ?? ''
+ releaseNoteUri: releaseNoteUri ?? ''
+ supportedOSType: supportedOSType
+ }
+}
+
+resource application_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in (roleAssignments ?? []): {
+ name: guid(application.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: application
+}]
+
+@sys.description('The resource group the image was deployed into.')
+output resourceGroupName string = resourceGroup().name
+
+@sys.description('The resource ID of the image.')
+output resourceId string = application.id
+
+@sys.description('The name of the image.')
+output name string = application.name
+
+@sys.description('The location the resource was deployed into.')
+output location string = application.location
+// =============== //
+// Definitions //
+// =============== //
+
+type roleAssignmentType = {
+ @sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.')
+ roleDefinitionIdOrName: string
+
+ @sys.description('Required. The principal ID of the principal (user/group/identity) to assign the role to.')
+ principalId: string
+
+ @sys.description('Optional. The principal type of the assigned principal ID.')
+ principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')?
+
+ @sys.description('Optional. The description of the role assignment.')
+ description: string?
+
+ @sys.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?
+
+ @sys.description('Optional. Version of the condition.')
+ conditionVersion: '2.0'?
+
+ @sys.description('Optional. The Resource Id of the delegated managed identity resource.')
+ delegatedManagedIdentityResourceId: string?
+}[]?
diff --git a/avm/res/compute/gallery/application/main.json b/avm/res/compute/gallery/application/main.json
new file mode 100644
index 0000000000..e4227db2f0
--- /dev/null
+++ b/avm/res/compute/gallery/application/main.json
@@ -0,0 +1,260 @@
+{
+ "$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.23.1.45101",
+ "templateHash": "9406373999607697824"
+ },
+ "name": "Compute Galleries Applications",
+ "description": "This module deploys an Azure Compute Gallery Application.",
+ "owner": "Azure/module-maintainers"
+ },
+ "definitions": {
+ "roleAssignmentType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "roleDefinitionIdOrName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to."
+ }
+ },
+ "principalType": {
+ "type": "string",
+ "allowedValues": [
+ "Device",
+ "ForeignGroup",
+ "Group",
+ "ServicePrincipal",
+ "User"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The description of the role assignment."
+ }
+ },
+ "condition": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\""
+ }
+ },
+ "conditionVersion": {
+ "type": "string",
+ "allowedValues": [
+ "2.0"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Version of the condition."
+ }
+ },
+ "delegatedManagedIdentityResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Resource Id of the delegated managed identity resource."
+ }
+ }
+ }
+ },
+ "nullable": true
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the application definition."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "galleryName": {
+ "type": "string",
+ "minLength": 1,
+ "metadata": {
+ "description": "Conditional. The name of the parent Azure Compute Gallery. Required if the template is used in a standalone deployment."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The description of this gallery Application Definition resource. This property is updatable."
+ }
+ },
+ "eula": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Eula agreement for the gallery Application Definition. Has to be a valid URL."
+ }
+ },
+ "privacyStatementUri": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The privacy statement uri. Has to be a valid URL."
+ }
+ },
+ "releaseNoteUri": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The release note uri. Has to be a valid URL."
+ }
+ },
+ "supportedOSType": {
+ "type": "string",
+ "defaultValue": "Windows",
+ "allowedValues": [
+ "Windows",
+ "Linux"
+ ],
+ "metadata": {
+ "description": "Optional. This property allows you to specify the supported type of the OS that application is built for."
+ }
+ },
+ "endOfLifeDate": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The end of life date of the gallery Image Definition. This property can be used for decommissioning purposes. This property is updatable. Allowed format: 2020-01-10T23:00:00.000Z."
+ }
+ },
+ "roleAssignments": {
+ "$ref": "#/definitions/roleAssignmentType",
+ "metadata": {
+ "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags for all resources."
+ }
+ },
+ "customActions": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A list of custom actions that can be performed with all of the Gallery Application Versions within this Gallery Application."
+ }
+ }
+ },
+ "variables": {
+ "builtInRoleNames": {
+ "Compute Gallery Sharing Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1ef6a3be-d0ac-425d-8c01-acb62866290b')]",
+ "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": {
+ "gallery": {
+ "existing": true,
+ "type": "Microsoft.Compute/galleries",
+ "apiVersion": "2022-03-03",
+ "name": "[parameters('galleryName')]"
+ },
+ "application": {
+ "type": "Microsoft.Compute/galleries/applications",
+ "apiVersion": "2022-03-03",
+ "name": "[format('{0}/{1}', parameters('galleryName'), parameters('name'))]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "customActions": "[parameters('customActions')]",
+ "description": "[coalesce(parameters('description'), '')]",
+ "endOfLifeDate": "[coalesce(parameters('endOfLifeDate'), '')]",
+ "eula": "[coalesce(parameters('eula'), '')]",
+ "privacyStatementUri": "[coalesce(parameters('privacyStatementUri'), '')]",
+ "releaseNoteUri": "[coalesce(parameters('releaseNoteUri'), '')]",
+ "supportedOSType": "[parameters('supportedOSType')]"
+ },
+ "dependsOn": [
+ "gallery"
+ ]
+ },
+ "application_roleAssignments": {
+ "copy": {
+ "name": "application_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Compute/galleries/{0}/applications/{1}', parameters('galleryName'), parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.Compute/galleries/applications', parameters('galleryName'), 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": [
+ "application"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the image was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the image."
+ },
+ "value": "[resourceId('Microsoft.Compute/galleries/applications', parameters('galleryName'), parameters('name'))]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the image."
+ },
+ "value": "[parameters('name')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('application', '2022-03-03', 'full').location]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/compute/gallery/image/README.md b/avm/res/compute/gallery/image/README.md
new file mode 100644
index 0000000000..334842274a
--- /dev/null
+++ b/avm/res/compute/gallery/image/README.md
@@ -0,0 +1,405 @@
+# Compute Galleries Image Definitions `[Microsoft.Compute/galleries/images]`
+
+This module deploys an Azure Compute Gallery Image Definition.
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) |
+| `Microsoft.Compute/galleries/images` | [2022-03-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-03-03/galleries/images) |
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | Name of the image definition. |
+
+**Conditional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`galleryName`](#parameter-galleryname) | string | The name of the parent Azure Shared Image Gallery. Required if the template is used in a standalone deployment. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`description`](#parameter-description) | string | The description of this gallery Image Definition resource. This property is updatable. |
+| [`endOfLife`](#parameter-endoflife) | string | The end of life date of the gallery Image Definition. This property can be used for decommissioning purposes. This property is updatable. Allowed format: 2020-01-10T23:00:00.000Z. |
+| [`eula`](#parameter-eula) | string | The Eula agreement for the gallery Image Definition. Has to be a valid URL. |
+| [`excludedDiskTypes`](#parameter-excludeddisktypes) | array | List of the excluded disk types. E.g. Standard_LRS. |
+| [`hyperVGeneration`](#parameter-hypervgeneration) | string | The hypervisor generation of the Virtual Machine.via Bicep module
+
+```bicep
+module vpnSite 'br/public:avm/res/network/vpn-site: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": "nvsmin"
+ },
+ "virtualWanId": {
+ "value": "via Bicep module
+
+```bicep
+module vpnSite 'br/public:avm/res/network/vpn-site: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": "nvsmax"
+ },
+ "virtualWanId": {
+ "value": "via Bicep module
+
+```bicep
+module vpnSite 'br/public:avm/res/network/vpn-site: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": "nvswaf"
+ },
+ "virtualWanId": {
+ "value": "Parameter JSON format
+
+```json
+"deviceProperties": {
+ "value": {
+ "deviceModel": "morty",
+ "deviceVendor": "contoso",
+ "linkSpeedInMbps": 0
+ }
+}
+```
+
+Bicep format
+
+```bicep
+deviceProperties: {
+ deviceModel: 'morty'
+ deviceVendor: 'contoso'
+ linkSpeedInMbps: 0
+}
+```
+
+via Bicep module
+
+```bicep
+module gallery 'br/public:avm/res/compute/gallery: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": "cgmin001"
+ },
+ // Non-required parameters
+ "location": {
+ "value": "via Bicep module
+
+```bicep
+module gallery 'br/public:avm/res/compute/gallery: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": "cgmax001"
+ },
+ // Non-required parameters
+ "applications": {
+ "value": [
+ {
+ "name": "cgmax-appd-001"
+ },
+ {
+ "name": "cgmax-appd-002",
+ "roleAssignments": [
+ {
+ "principalId": "via Bicep module
+
+```bicep
+module gallery 'br/public:avm/res/compute/gallery: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": "cgwaf001"
+ },
+ // Non-required parameters
+ "applications": {
+ "value": [
+ {
+ "name": "cgwaf-appd-001"
+ },
+ {
+ "name": "cgwaf-appd-002",
+ "supportedOSType": "Windows"
+ }
+ ]
+ },
+ "images": {
+ "value": [
+ {
+ "name": "az-imgd-ws-001"
+ },
+ {
+ "hyperVGeneration": "V1",
+ "maxRecommendedMemory": 16,
+ "maxRecommendedvCPUs": 8,
+ "minRecommendedMemory": 4,
+ "minRecommendedvCPUs": 2,
+ "name": "az-imgd-ws-002",
+ "offer": "WindowsServer",
+ "osState": "Generalized",
+ "osType": "Windows",
+ "publisher": "MicrosoftWindowsServer",
+ "sku": "2022-datacenter-azure-edition"
+ },
+ {
+ "hyperVGeneration": "V2",
+ "isHibernateSupported": "true",
+ "maxRecommendedMemory": 16,
+ "maxRecommendedvCPUs": 8,
+ "minRecommendedMemory": 4,
+ "minRecommendedvCPUs": 2,
+ "name": "az-imgd-ws-003",
+ "offer": "WindowsServer",
+ "osState": "Generalized",
+ "osType": "Windows",
+ "publisher": "MicrosoftWindowsServer",
+ "sku": "2022-datacenter-azure-edition-hibernate"
+ },
+ {
+ "hyperVGeneration": "V2",
+ "isAcceleratedNetworkSupported": "true",
+ "maxRecommendedMemory": 16,
+ "maxRecommendedvCPUs": 8,
+ "minRecommendedMemory": 4,
+ "minRecommendedvCPUs": 2,
+ "name": "az-imgd-ws-004",
+ "offer": "WindowsServer",
+ "osState": "Generalized",
+ "osType": "Windows",
+ "publisher": "MicrosoftWindowsServer",
+ "sku": "2022-datacenter-azure-edition-accnet"
+ },
+ {
+ "hyperVGeneration": "V2",
+ "maxRecommendedMemory": 16,
+ "maxRecommendedvCPUs": 4,
+ "minRecommendedMemory": 4,
+ "minRecommendedvCPUs": 2,
+ "name": "az-imgd-wdtl-002",
+ "offer": "WindowsDesktop",
+ "osState": "Generalized",
+ "osType": "Windows",
+ "publisher": "MicrosoftWindowsDesktop",
+ "securityType": "TrustedLaunch",
+ "sku": "Win11-21H2"
+ },
+ {
+ "hyperVGeneration": "V2",
+ "maxRecommendedMemory": 32,
+ "maxRecommendedvCPUs": 4,
+ "minRecommendedMemory": 4,
+ "minRecommendedvCPUs": 1,
+ "name": "az-imgd-us-001",
+ "offer": "0001-com-ubuntu-server-focal",
+ "osState": "Generalized",
+ "osType": "Linux",
+ "publisher": "canonical",
+ "sku": "20_04-lts-gen2"
+ }
+ ]
+ },
+ "location": {
+ "value": "Parameter JSON format
+
+```json
+"customActions": {
+ "value": [
+ {
+ "description": "This is a sample custom action",
+ "name": "Name of the custom action 1 (Required). Must be unique within the Compute Gallery",
+ "parameters": [
+ {
+ "defaultValue": "Default Value of Parameter1. Only applies to string types.",
+ "description": "a description value to help others understands what it means.",
+ "name": "The parameter name. (Required)",
+ "required": True,
+ "type": "ConfigurationDataBlob, LogOutputBlob, or String"
+ },
+ {
+ "defaultValue": "Default Value of Parameter2. Only applies to string types.",
+ "description": "a description value to help others understands what it means.",
+ "name": "The parameter name. (Required)",
+ "required": False,
+ "type": "ConfigurationDataBlob, LogOutputBlob, or String"
+ }
+ ],
+ "script": "The script to run when executing this custom action. (Required)"
+ },
+ {
+ "description": "This is another sample custom action",
+ "name": "Name of the custom action 2 (Required). Must be unique within the Compute Gallery",
+ "parameters": [
+ {
+ "defaultValue": "Default Value of Parameter1. Only applies to string types.",
+ "description": "a description value to help others understands what it means.",
+ "name": "The parameter name. (Required)",
+ "required": True,
+ "type": "ConfigurationDataBlob, LogOutputBlob, or String"
+ }
+ ],
+ "script": "The script to run when executing this custom action. (Required)"
+ }
+ ]
+}
+```
+
+Bicep format
+
+```bicep
+customActions: [
+ {
+ description: "This is a sample custom action"
+ name: "Name of the custom action 1 (Required). Must be unique within the Compute Gallery"
+ parameters: [
+ {
+ defaultValue: "Default Value of Parameter 1. Only applies to string types."
+ description: "a description value to help others understands what it means."
+ name: "The parameter name. (Required)"
+ required: True,
+ type: "ConfigurationDataBlob, LogOutputBlob, or String"
+ }
+ {
+ defaultValue: "Default Value of Parameter 2. Only applies to string types."
+ description: "a description value to help others understands what it means."
+ name: "The parameter name. (Required)"
+ required: True,
+ type: "ConfigurationDataBlob, LogOutputBlob, or String"
+ }
+ ]
+ script: "The script to run when executing this custom action. (Required)"
+ }
+ {
+ description: "This is another sample custom action"
+ name: "Name of the custom action 2 (Required). Must be unique within the Compute Gallery"
+ parameters: [
+ {
+ defaultValue: "Default Value of Parameter. Only applies to string types."
+ description: "a description value to help others understands what it means."
+ name: "The paramter name. (Required)"
+ required: True,
+ type: "ConfigurationDataBlob, LogOutputBlob, or String"
+ }
+ ]
+ script: "The script to run when executing this custom action. (Required)"
+ }
+]
+```
+
+
+
+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": "ncmin001"
+ },
+ "virtualNetworkGateway1": {
+ "value": {
+ "id": "
+
+### Example 2: _Using large parameter set_
+
+This instance deploys the module with most of its features enabled.
+
+
+via Bicep module
+
+```bicep
+module connection 'br/public:avm/res/network/connection:
+
+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": "ncmax001"
+ },
+ "virtualNetworkGateway1": {
+ "value": {
+ "id": "
+
+### 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 connection 'br/public:avm/res/network/connection:
+
+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": "ncwaf001"
+ },
+ "virtualNetworkGateway1": {
+ "value": {
+ "id": "
+
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | Remote connection name. |
+| [`virtualNetworkGateway1`](#parameter-virtualnetworkgateway1) | object | The primary Virtual Network Gateway. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`authorizationKey`](#parameter-authorizationkey) | securestring | The Authorization Key to connect to an Express Route Circuit. Used for connection type [ExpressRoute]. |
+| [`connectionMode`](#parameter-connectionmode) | string | The connection connectionMode for this connection. Available for IPSec connections. |
+| [`connectionProtocol`](#parameter-connectionprotocol) | string | Connection connectionProtocol used for this connection. Available for IPSec connections. |
+| [`connectionType`](#parameter-connectiontype) | string | Gateway connection connectionType. |
+| [`customIPSecPolicy`](#parameter-customipsecpolicy) | object | The IPSec Policies to be considered by this connection. |
+| [`dpdTimeoutSeconds`](#parameter-dpdtimeoutseconds) | int | The dead peer detection timeout of this connection in seconds. Setting the timeout to shorter periods will cause IKE to rekey more aggressively, causing the connection to appear to be disconnected in some instances. The general recommendation is to set the timeout between 30 to 45 seconds. |
+| [`enableBgp`](#parameter-enablebgp) | bool | Value to specify if BGP is enabled or not. |
+| [`enablePrivateLinkFastPath`](#parameter-enableprivatelinkfastpath) | bool | Bypass the ExpressRoute gateway when accessing private-links. ExpressRoute FastPath (expressRouteGatewayBypass) must be enabled. Only available when connection connectionType is Express Route. |
+| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). |
+| [`expressRouteGatewayBypass`](#parameter-expressroutegatewaybypass) | bool | Bypass ExpressRoute Gateway for data forwarding. Only available when connection connectionType is Express Route. |
+| [`localNetworkGateway2`](#parameter-localnetworkgateway2) | object | The local network gateway. Used for connection type [IPsec]. |
+| [`location`](#parameter-location) | string | Location for all resources. |
+| [`lock`](#parameter-lock) | object | The lock settings of the service. |
+| [`peer`](#parameter-peer) | object | The remote peer. Used for connection connectionType [ExpressRoute]. |
+| [`routingWeight`](#parameter-routingweight) | int | The weight added to routes learned from this BGP speaker. |
+| [`tags`](#parameter-tags) | object | Tags of the resource. |
+| [`useLocalAzureIpAddress`](#parameter-uselocalazureipaddress) | bool | Use private local Azure IP for the connection. Only available for IPSec Virtual Network Gateways that use the Azure Private IP Property. |
+| [`usePolicyBasedTrafficSelectors`](#parameter-usepolicybasedtrafficselectors) | bool | Enable policy-based traffic selectors. |
+| [`virtualNetworkGateway2`](#parameter-virtualnetworkgateway2) | object | The remote Virtual Network Gateway. Used for connection connectionType [Vnet2Vnet]. |
+| [`vpnSharedKey`](#parameter-vpnsharedkey) | securestring | Specifies a VPN shared key. The same value has to be specified on both Virtual Network Gateways. |
+
+### Parameter: `name`
+
+Remote connection name.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `virtualNetworkGateway1`
+
+The primary Virtual Network Gateway.
+
+- Required: Yes
+- Type: object
+
+### Parameter: `authorizationKey`
+
+The Authorization Key to connect to an Express Route Circuit. Used for connection type [ExpressRoute].
+
+- Required: No
+- Type: securestring
+- Default: `''`
+
+### Parameter: `connectionMode`
+
+The connection connectionMode for this connection. Available for IPSec connections.
+
+- Required: No
+- Type: string
+- Default: `'Default'`
+- Allowed:
+ ```Bicep
+ [
+ 'Default'
+ 'InitiatorOnly'
+ 'ResponderOnly'
+ ]
+ ```
+
+### Parameter: `connectionProtocol`
+
+Connection connectionProtocol used for this connection. Available for IPSec connections.
+
+- Required: No
+- Type: string
+- Default: `'IKEv2'`
+- Allowed:
+ ```Bicep
+ [
+ 'IKEv1'
+ 'IKEv2'
+ ]
+ ```
+
+### Parameter: `connectionType`
+
+Gateway connection connectionType.
+
+- Required: No
+- Type: string
+- Default: `'IPsec'`
+- Allowed:
+ ```Bicep
+ [
+ 'ExpressRoute'
+ 'IPsec'
+ 'Vnet2Vnet'
+ 'VPNClient'
+ ]
+ ```
+
+### Parameter: `customIPSecPolicy`
+
+The IPSec Policies to be considered by this connection.
+
+- Required: No
+- Type: object
+- Default:
+ ```Bicep
+ {
+ dhGroup: ''
+ ikeEncryption: ''
+ ikeIntegrity: ''
+ ipsecEncryption: ''
+ ipsecIntegrity: ''
+ pfsGroup: ''
+ saDataSizeKilobytes: 0
+ saLifeTimeSeconds: 0
+ }
+ ```
+
+### Parameter: `dpdTimeoutSeconds`
+
+The dead peer detection timeout of this connection in seconds. Setting the timeout to shorter periods will cause IKE to rekey more aggressively, causing the connection to appear to be disconnected in some instances. The general recommendation is to set the timeout between 30 to 45 seconds.
+
+- Required: No
+- Type: int
+- Default: `45`
+
+### Parameter: `enableBgp`
+
+Value to specify if BGP is enabled or not.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `enablePrivateLinkFastPath`
+
+Bypass the ExpressRoute gateway when accessing private-links. ExpressRoute FastPath (expressRouteGatewayBypass) must be enabled. Only available when connection connectionType is Express Route.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `enableTelemetry`
+
+Enable telemetry via a Globally Unique Identifier (GUID).
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `expressRouteGatewayBypass`
+
+Bypass ExpressRoute Gateway for data forwarding. Only available when connection connectionType is Express Route.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `localNetworkGateway2`
+
+The local network gateway. Used for connection type [IPsec].
+
+- Required: No
+- Type: object
+- Default: `{}`
+
+### 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: `peer`
+
+The remote peer. Used for connection connectionType [ExpressRoute].
+
+- Required: No
+- Type: object
+- Default: `{}`
+
+### Parameter: `routingWeight`
+
+The weight added to routes learned from this BGP speaker.
+
+- Required: No
+- Type: int
+
+### Parameter: `tags`
+
+Tags of the resource.
+
+- Required: No
+- Type: object
+
+### Parameter: `useLocalAzureIpAddress`
+
+Use private local Azure IP for the connection. Only available for IPSec Virtual Network Gateways that use the Azure Private IP Property.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `usePolicyBasedTrafficSelectors`
+
+Enable policy-based traffic selectors.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `virtualNetworkGateway2`
+
+The remote Virtual Network Gateway. Used for connection connectionType [Vnet2Vnet].
+
+- Required: No
+- Type: object
+- Default: `{}`
+
+### Parameter: `vpnSharedKey`
+
+Specifies a VPN shared key. The same value has to be specified on both Virtual Network Gateways.
+
+- Required: No
+- Type: securestring
+- Default: `''`
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `location` | string | The location the resource was deployed into. |
+| `name` | string | The name of the remote connection. |
+| `resourceGroupName` | string | The resource group the remote connection was deployed into. |
+| `resourceId` | string | The resource ID of the remote connection. |
+
+## Cross-referenced modules
+
+_None_
+
+## Notes
+
+### Parameter Usage: `localNetworkGateway2`
+
+The local virtual network gateway object.
+
+Parameter JSON format
+
+```json
+"localNetworkGateway2": {
+ "value": {
+ "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/localNetworkGateways/myGateway"
+ }
+}
+```
+
+Bicep format
+
+```bicep
+localNetworkGateway2: {
+ id: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/localNetworkGateways/myGateway'
+}
+```
+
+
+
+### Parameter Usage: `peer`
+
+The remote peer object used for ExpressRoute connections
+
+Parameter JSON format
+
+```json
+"peer": {
+ "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/expressRouteCircuits/expressRoute"
+}
+```
+
+Bicep format
+
+```bicep
+'peer': {
+ id: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/expressRouteCircuits/expressRoute'
+}
+```
+
+
+
+### Parameter Usage: `customIPSecPolicy`
+
+If ipsecEncryption parameter is empty, customIPSecPolicy will not be deployed. The parameter file should look like below.
+
+Parameter JSON format
+
+```json
+"customIPSecPolicy": {
+ "value": {
+ "saLifeTimeSeconds": 0,
+ "saDataSizeKilobytes": 0,
+ "ipsecEncryption": "",
+ "ipsecIntegrity": "",
+ "ikeEncryption": "",
+ "ikeIntegrity": "",
+ "dhGroup": "",
+ "pfsGroup": ""
+ }
+}
+```
+
+Bicep format
+
+```bicep
+customIPSecPolicy: {
+ saLifeTimeSeconds: 0
+ saDataSizeKilobytes: 0
+ ipsecEncryption: ''
+ ipsecIntegrity: ''
+ ikeEncryption: ''
+ ikeIntegrity: ''
+ dhGroup: ''
+ pfsGroup: ''
+}
+```
+
+
+
+Format of the full customIPSecPolicy parameter in parameter file.
+
+Parameter JSON format
+
+```json
+"customIPSecPolicy": {
+ "value": {
+ "saLifeTimeSeconds": 28800,
+ "saDataSizeKilobytes": 102400000,
+ "ipsecEncryption": "AES256",
+ "ipsecIntegrity": "SHA256",
+ "ikeEncryption": "AES256",
+ "ikeIntegrity": "SHA256",
+ "dhGroup": "DHGroup14",
+ "pfsGroup": "None"
+ }
+}
+```
+
+Bicep format
+
+```bicep
+customIPSecPolicy: {
+ saLifeTimeSeconds: 28800
+ saDataSizeKilobytes: 102400000
+ ipsecEncryption: 'AES256'
+ ipsecIntegrity: 'SHA256'
+ ikeEncryption: 'AES256'
+ ikeIntegrity: 'SHA256'
+ dhGroup: 'DHGroup14'
+ pfsGroup: 'None'
+}
+```
+
+
diff --git a/avm/res/network/connection/main.bicep b/avm/res/network/connection/main.bicep
new file mode 100644
index 0000000000..d92c249591
--- /dev/null
+++ b/avm/res/network/connection/main.bicep
@@ -0,0 +1,184 @@
+metadata name = 'Virtual Network Gateway Connections'
+metadata description = 'This module deploys a Virtual Network Gateway Connection.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Required. Remote connection name.')
+param name string
+
+@description('Optional. Specifies a VPN shared key. The same value has to be specified on both Virtual Network Gateways.')
+@secure()
+param vpnSharedKey string = ''
+
+@description('Optional. Location for all resources.')
+param location string = resourceGroup().location
+
+@description('Optional. Gateway connection connectionType.')
+@allowed([
+ 'IPsec'
+ 'Vnet2Vnet'
+ 'ExpressRoute'
+ 'VPNClient'
+])
+param connectionType string = 'IPsec'
+
+@description('Optional. Value to specify if BGP is enabled or not.')
+param enableBgp bool = false
+
+@allowed([
+ 'Default'
+ 'InitiatorOnly'
+ 'ResponderOnly'
+])
+@description('Optional. The connection connectionMode for this connection. Available for IPSec connections.')
+param connectionMode string = 'Default'
+
+@allowed([
+ 'IKEv1'
+ 'IKEv2'
+])
+@description('Optional. Connection connectionProtocol used for this connection. Available for IPSec connections.')
+param connectionProtocol string = 'IKEv2'
+
+@minValue(9)
+@maxValue(3600)
+@description('Optional. The dead peer detection timeout of this connection in seconds. Setting the timeout to shorter periods will cause IKE to rekey more aggressively, causing the connection to appear to be disconnected in some instances. The general recommendation is to set the timeout between 30 to 45 seconds.')
+param dpdTimeoutSeconds int = 45
+
+@description('Optional. Enable policy-based traffic selectors.')
+param usePolicyBasedTrafficSelectors bool = false
+
+@description('Optional. Bypass the ExpressRoute gateway when accessing private-links. ExpressRoute FastPath (expressRouteGatewayBypass) must be enabled. Only available when connection connectionType is Express Route.')
+param enablePrivateLinkFastPath bool = false
+
+@description('Optional. Bypass ExpressRoute Gateway for data forwarding. Only available when connection connectionType is Express Route.')
+param expressRouteGatewayBypass bool = false
+
+@description('Optional. Use private local Azure IP for the connection. Only available for IPSec Virtual Network Gateways that use the Azure Private IP Property.')
+param useLocalAzureIpAddress bool = false
+
+@description('Optional. The IPSec Policies to be considered by this connection.')
+param customIPSecPolicy object = {
+ saLifeTimeSeconds: 0
+ saDataSizeKilobytes: 0
+ ipsecEncryption: ''
+ ipsecIntegrity: ''
+ ikeEncryption: ''
+ ikeIntegrity: ''
+ dhGroup: ''
+ pfsGroup: ''
+}
+
+@description('Optional. The weight added to routes learned from this BGP speaker.')
+param routingWeight int?
+
+@description('Optional. The lock settings of the service.')
+param lock lockType
+
+@description('Optional. Tags of the resource.')
+param tags object?
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableTelemetry bool = true
+
+@description('Required. The primary Virtual Network Gateway.')
+param virtualNetworkGateway1 object
+
+@description('Optional. The remote Virtual Network Gateway. Used for connection connectionType [Vnet2Vnet].')
+param virtualNetworkGateway2 object = {}
+
+@description('Optional. The remote peer. Used for connection connectionType [ExpressRoute].')
+param peer object = {}
+
+@description('Optional. The Authorization Key to connect to an Express Route Circuit. Used for connection type [ExpressRoute].')
+@secure()
+param authorizationKey string = ''
+
+@description('Optional. The local network gateway. Used for connection type [IPsec].')
+param localNetworkGateway2 object = {}
+
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: '46d3xbcp.res.network-connection.${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 connection 'Microsoft.Network/connections@2023-04-01' = {
+ name: name
+ location: location
+ tags: tags
+ properties: {
+ connectionType: connectionType
+ connectionMode: connectionType == 'IPsec' ? connectionMode : null
+ connectionProtocol: connectionType == 'IPsec' ? connectionProtocol : null
+ dpdTimeoutSeconds: connectionType == 'IPsec' ? dpdTimeoutSeconds : null
+ enablePrivateLinkFastPath: connectionType == 'ExpressRoute' ? enablePrivateLinkFastPath : null
+ expressRouteGatewayBypass: connectionType == 'ExpressRoute' ? expressRouteGatewayBypass : null
+ virtualNetworkGateway1: virtualNetworkGateway1
+ virtualNetworkGateway2: connectionType == 'Vnet2Vnet' ? virtualNetworkGateway2 : null
+ localNetworkGateway2: connectionType == 'IPsec' ? localNetworkGateway2 : null
+ peer: connectionType == 'ExpressRoute' ? peer : null
+ authorizationKey: connectionType == 'ExpressRoute' && !empty(authorizationKey) ? authorizationKey : null
+ sharedKey: connectionType != 'ExpressRoute' ? vpnSharedKey : null
+ usePolicyBasedTrafficSelectors: usePolicyBasedTrafficSelectors
+ ipsecPolicies: !empty(customIPSecPolicy.ipsecEncryption) ? [
+ {
+ saLifeTimeSeconds: customIPSecPolicy.saLifeTimeSeconds
+ saDataSizeKilobytes: customIPSecPolicy.saDataSizeKilobytes
+ ipsecEncryption: customIPSecPolicy.ipsecEncryption
+ ipsecIntegrity: customIPSecPolicy.ipsecIntegrity
+ ikeEncryption: customIPSecPolicy.ikeEncryption
+ ikeIntegrity: customIPSecPolicy.ikeIntegrity
+ dhGroup: customIPSecPolicy.dhGroup
+ pfsGroup: customIPSecPolicy.pfsGroup
+ }
+ ] : customIPSecPolicy.ipsecEncryption
+ routingWeight: routingWeight
+ enableBgp: enableBgp
+ useLocalAzureIpAddress: connectionType == 'IPsec' ? useLocalAzureIpAddress : null
+ }
+}
+
+resource connection_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: connection
+}
+
+@description('The resource group the remote connection was deployed into.')
+output resourceGroupName string = resourceGroup().name
+
+@description('The name of the remote connection.')
+output name string = connection.name
+
+@description('The resource ID of the remote connection.')
+output resourceId string = connection.id
+
+@description('The location the resource was deployed into.')
+output location string = connection.location
+
+// =============== //
+// Definitions //
+// =============== //
+
+type lockType = {
+ @description('Optional. Specify the name of lock.')
+ name: string?
+
+ @description('Optional. Specify the type of lock.')
+ kind: ('CanNotDelete' | 'ReadOnly' | 'None')?
+}?
diff --git a/avm/res/network/connection/main.json b/avm/res/network/connection/main.json
new file mode 100644
index 0000000000..194cae7c00
--- /dev/null
+++ b/avm/res/network/connection/main.json
@@ -0,0 +1,313 @@
+{
+ "$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.23.1.45101",
+ "templateHash": "15709311136731868568"
+ },
+ "name": "Virtual Network Gateway Connections",
+ "description": "This module deploys a Virtual Network Gateway Connection.",
+ "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
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Remote connection name."
+ }
+ },
+ "vpnSharedKey": {
+ "type": "securestring",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Specifies a VPN shared key. The same value has to be specified on both Virtual Network Gateways."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "connectionType": {
+ "type": "string",
+ "defaultValue": "IPsec",
+ "allowedValues": [
+ "IPsec",
+ "Vnet2Vnet",
+ "ExpressRoute",
+ "VPNClient"
+ ],
+ "metadata": {
+ "description": "Optional. Gateway connection connectionType."
+ }
+ },
+ "enableBgp": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Value to specify if BGP is enabled or not."
+ }
+ },
+ "connectionMode": {
+ "type": "string",
+ "defaultValue": "Default",
+ "allowedValues": [
+ "Default",
+ "InitiatorOnly",
+ "ResponderOnly"
+ ],
+ "metadata": {
+ "description": "Optional. The connection connectionMode for this connection. Available for IPSec connections."
+ }
+ },
+ "connectionProtocol": {
+ "type": "string",
+ "defaultValue": "IKEv2",
+ "allowedValues": [
+ "IKEv1",
+ "IKEv2"
+ ],
+ "metadata": {
+ "description": "Optional. Connection connectionProtocol used for this connection. Available for IPSec connections."
+ }
+ },
+ "dpdTimeoutSeconds": {
+ "type": "int",
+ "defaultValue": 45,
+ "minValue": 9,
+ "maxValue": 3600,
+ "metadata": {
+ "description": "Optional. The dead peer detection timeout of this connection in seconds. Setting the timeout to shorter periods will cause IKE to rekey more aggressively, causing the connection to appear to be disconnected in some instances. The general recommendation is to set the timeout between 30 to 45 seconds."
+ }
+ },
+ "usePolicyBasedTrafficSelectors": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable policy-based traffic selectors."
+ }
+ },
+ "enablePrivateLinkFastPath": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Bypass the ExpressRoute gateway when accessing private-links. ExpressRoute FastPath (expressRouteGatewayBypass) must be enabled. Only available when connection connectionType is Express Route."
+ }
+ },
+ "expressRouteGatewayBypass": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Bypass ExpressRoute Gateway for data forwarding. Only available when connection connectionType is Express Route."
+ }
+ },
+ "useLocalAzureIpAddress": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Use private local Azure IP for the connection. Only available for IPSec Virtual Network Gateways that use the Azure Private IP Property."
+ }
+ },
+ "customIPSecPolicy": {
+ "type": "object",
+ "defaultValue": {
+ "saLifeTimeSeconds": 0,
+ "saDataSizeKilobytes": 0,
+ "ipsecEncryption": "",
+ "ipsecIntegrity": "",
+ "ikeEncryption": "",
+ "ikeIntegrity": "",
+ "dhGroup": "",
+ "pfsGroup": ""
+ },
+ "metadata": {
+ "description": "Optional. The IPSec Policies to be considered by this connection."
+ }
+ },
+ "routingWeight": {
+ "type": "int",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The weight added to routes learned from this BGP speaker."
+ }
+ },
+ "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."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)."
+ }
+ },
+ "virtualNetworkGateway1": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. The primary Virtual Network Gateway."
+ }
+ },
+ "virtualNetworkGateway2": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. The remote Virtual Network Gateway. Used for connection connectionType [Vnet2Vnet]."
+ }
+ },
+ "peer": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. The remote peer. Used for connection connectionType [ExpressRoute]."
+ }
+ },
+ "authorizationKey": {
+ "type": "securestring",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The Authorization Key to connect to an Express Route Circuit. Used for connection type [ExpressRoute]."
+ }
+ },
+ "localNetworkGateway2": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. The local network gateway. Used for connection type [IPsec]."
+ }
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2023-07-01",
+ "name": "[format('46d3xbcp.res.network-connection.{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"
+ }
+ }
+ }
+ }
+ },
+ "connection": {
+ "type": "Microsoft.Network/connections",
+ "apiVersion": "2023-04-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "connectionType": "[parameters('connectionType')]",
+ "connectionMode": "[if(equals(parameters('connectionType'), 'IPsec'), parameters('connectionMode'), null())]",
+ "connectionProtocol": "[if(equals(parameters('connectionType'), 'IPsec'), parameters('connectionProtocol'), null())]",
+ "dpdTimeoutSeconds": "[if(equals(parameters('connectionType'), 'IPsec'), parameters('dpdTimeoutSeconds'), null())]",
+ "enablePrivateLinkFastPath": "[if(equals(parameters('connectionType'), 'ExpressRoute'), parameters('enablePrivateLinkFastPath'), null())]",
+ "expressRouteGatewayBypass": "[if(equals(parameters('connectionType'), 'ExpressRoute'), parameters('expressRouteGatewayBypass'), null())]",
+ "virtualNetworkGateway1": "[parameters('virtualNetworkGateway1')]",
+ "virtualNetworkGateway2": "[if(equals(parameters('connectionType'), 'Vnet2Vnet'), parameters('virtualNetworkGateway2'), null())]",
+ "localNetworkGateway2": "[if(equals(parameters('connectionType'), 'IPsec'), parameters('localNetworkGateway2'), null())]",
+ "peer": "[if(equals(parameters('connectionType'), 'ExpressRoute'), parameters('peer'), null())]",
+ "authorizationKey": "[if(and(equals(parameters('connectionType'), 'ExpressRoute'), not(empty(parameters('authorizationKey')))), parameters('authorizationKey'), null())]",
+ "sharedKey": "[if(not(equals(parameters('connectionType'), 'ExpressRoute')), parameters('vpnSharedKey'), null())]",
+ "usePolicyBasedTrafficSelectors": "[parameters('usePolicyBasedTrafficSelectors')]",
+ "ipsecPolicies": "[if(not(empty(parameters('customIPSecPolicy').ipsecEncryption)), createArray(createObject('saLifeTimeSeconds', parameters('customIPSecPolicy').saLifeTimeSeconds, 'saDataSizeKilobytes', parameters('customIPSecPolicy').saDataSizeKilobytes, 'ipsecEncryption', parameters('customIPSecPolicy').ipsecEncryption, 'ipsecIntegrity', parameters('customIPSecPolicy').ipsecIntegrity, 'ikeEncryption', parameters('customIPSecPolicy').ikeEncryption, 'ikeIntegrity', parameters('customIPSecPolicy').ikeIntegrity, 'dhGroup', parameters('customIPSecPolicy').dhGroup, 'pfsGroup', parameters('customIPSecPolicy').pfsGroup)), parameters('customIPSecPolicy').ipsecEncryption)]",
+ "routingWeight": "[parameters('routingWeight')]",
+ "enableBgp": "[parameters('enableBgp')]",
+ "useLocalAzureIpAddress": "[if(equals(parameters('connectionType'), 'IPsec'), parameters('useLocalAzureIpAddress'), null())]"
+ }
+ },
+ "connection_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.Network/connections/{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": [
+ "connection"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the remote connection was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the remote connection."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the remote connection."
+ },
+ "value": "[resourceId('Microsoft.Network/connections', parameters('name'))]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('connection', '2023-04-01', 'full').location]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/network/connection/tests/e2e/defaults/dependencies.bicep b/avm/res/network/connection/tests/e2e/defaults/dependencies.bicep
new file mode 100644
index 0000000000..a8398dc99e
--- /dev/null
+++ b/avm/res/network/connection/tests/e2e/defaults/dependencies.bicep
@@ -0,0 +1,132 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the primary Public IP to create.')
+param primaryPublicIPName string
+
+@description('Required. The name of the primary VNET to create.')
+param primaryVirtualNetworkName string
+
+@description('Required. The name of the primary Virtual Network Gateway to create.')
+param primaryVirtualNetworkGatewayName string
+
+@description('Required. The name of the secondary Public IP to create.')
+param secondaryPublicIPName string
+
+@description('Required. The name of the secondary VNET to create.')
+param secondaryVirtualNetworkName string
+
+@description('Required. The name of the secondary Virtual Network Gateway to create.')
+param secondaryVirtualNetworkGatewayName string
+
+resource primaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: primaryVirtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ '10.0.0.0/24'
+ ]
+ }
+ subnets: [
+ {
+ name: 'GatewaySubnet'
+ properties: {
+ addressPrefix: '10.0.0.0/24'
+ }
+ }
+ ]
+ }
+}
+
+resource primaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
+ name: primaryPublicIPName
+ location: location
+}
+
+resource primaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = {
+ name: primaryVirtualNetworkGatewayName
+ location: location
+ properties: {
+ gatewayType: 'Vpn'
+ ipConfigurations: [
+ {
+ name: 'default'
+ properties: {
+ privateIPAllocationMethod: 'Dynamic'
+ subnet: {
+ id: primaryVirtualNetwork.properties.subnets[0].id
+ }
+ publicIPAddress: {
+ id: primaryPublicIP.id
+ }
+ }
+ }
+ ]
+ vpnType: 'RouteBased'
+ vpnGatewayGeneration: 'Generation2'
+ sku: {
+ name: 'VpnGw2'
+ tier: 'VpnGw2'
+ }
+ }
+}
+
+resource secondaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: secondaryVirtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ '10.0.1.0/24'
+ ]
+ }
+ subnets: [
+ {
+ name: 'GatewaySubnet'
+ properties: {
+ addressPrefix: '10.0.1.0/24'
+ }
+ }
+ ]
+ }
+}
+
+resource secondaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
+ name: secondaryPublicIPName
+ location: location
+}
+
+resource secondaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = {
+ name: secondaryVirtualNetworkGatewayName
+ location: location
+ properties: {
+ gatewayType: 'Vpn'
+ ipConfigurations: [
+ {
+ name: 'default'
+ properties: {
+ privateIPAllocationMethod: 'Dynamic'
+ subnet: {
+ id: secondaryVirtualNetwork.properties.subnets[0].id
+ }
+ publicIPAddress: {
+ id: secondaryPublicIP.id
+ }
+ }
+ }
+ ]
+ vpnType: 'RouteBased'
+ vpnGatewayGeneration: 'Generation2'
+ sku: {
+ name: 'VpnGw2'
+ tier: 'VpnGw2'
+ }
+ }
+}
+
+@description('The resource ID of the created primary Virtual Network Gateway.')
+output primaryVNETGatewayResourceID string = primaryVNETGateway.id
+
+@description('The resource ID of the created secondary Virtual Network Gateway.')
+output secondaryVNETGatewayResourceID string = secondaryVNETGateway.id
diff --git a/avm/res/network/connection/tests/e2e/defaults/main.test.bicep b/avm/res/network/connection/tests/e2e/defaults/main.test.bicep
new file mode 100644
index 0000000000..346655cf86
--- /dev/null
+++ b/avm/res/network/connection/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}-network.connections-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'ncmin'
+
+@description('Optional. The password to leverage for the shared key.')
+@secure()
+param password string = newGuid()
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ location:location
+ primaryPublicIPName: 'dep-${namePrefix}-pip-${serviceShort}-1'
+ primaryVirtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}-1'
+ primaryVirtualNetworkGatewayName: 'dep-${namePrefix}-vpn-gw-${serviceShort}-1'
+ secondaryPublicIPName: 'dep-${namePrefix}-pip-${serviceShort}-2'
+ secondaryVirtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}-2'
+ secondaryVirtualNetworkGatewayName: 'dep-${namePrefix}-vpn-gw-${serviceShort}-2'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: location
+ name: '${namePrefix}${serviceShort}001'
+ virtualNetworkGateway1: {
+ id: nestedDependencies.outputs.primaryVNETGatewayResourceID
+ }
+ virtualNetworkGateway2: {
+ id: nestedDependencies.outputs.secondaryVNETGatewayResourceID
+ }
+ connectionType: 'Vnet2Vnet'
+ vpnSharedKey: password
+ }
+}]
+
+
diff --git a/avm/res/network/connection/tests/e2e/max/dependencies.bicep b/avm/res/network/connection/tests/e2e/max/dependencies.bicep
new file mode 100644
index 0000000000..a8398dc99e
--- /dev/null
+++ b/avm/res/network/connection/tests/e2e/max/dependencies.bicep
@@ -0,0 +1,132 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the primary Public IP to create.')
+param primaryPublicIPName string
+
+@description('Required. The name of the primary VNET to create.')
+param primaryVirtualNetworkName string
+
+@description('Required. The name of the primary Virtual Network Gateway to create.')
+param primaryVirtualNetworkGatewayName string
+
+@description('Required. The name of the secondary Public IP to create.')
+param secondaryPublicIPName string
+
+@description('Required. The name of the secondary VNET to create.')
+param secondaryVirtualNetworkName string
+
+@description('Required. The name of the secondary Virtual Network Gateway to create.')
+param secondaryVirtualNetworkGatewayName string
+
+resource primaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: primaryVirtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ '10.0.0.0/24'
+ ]
+ }
+ subnets: [
+ {
+ name: 'GatewaySubnet'
+ properties: {
+ addressPrefix: '10.0.0.0/24'
+ }
+ }
+ ]
+ }
+}
+
+resource primaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
+ name: primaryPublicIPName
+ location: location
+}
+
+resource primaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = {
+ name: primaryVirtualNetworkGatewayName
+ location: location
+ properties: {
+ gatewayType: 'Vpn'
+ ipConfigurations: [
+ {
+ name: 'default'
+ properties: {
+ privateIPAllocationMethod: 'Dynamic'
+ subnet: {
+ id: primaryVirtualNetwork.properties.subnets[0].id
+ }
+ publicIPAddress: {
+ id: primaryPublicIP.id
+ }
+ }
+ }
+ ]
+ vpnType: 'RouteBased'
+ vpnGatewayGeneration: 'Generation2'
+ sku: {
+ name: 'VpnGw2'
+ tier: 'VpnGw2'
+ }
+ }
+}
+
+resource secondaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: secondaryVirtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ '10.0.1.0/24'
+ ]
+ }
+ subnets: [
+ {
+ name: 'GatewaySubnet'
+ properties: {
+ addressPrefix: '10.0.1.0/24'
+ }
+ }
+ ]
+ }
+}
+
+resource secondaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
+ name: secondaryPublicIPName
+ location: location
+}
+
+resource secondaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = {
+ name: secondaryVirtualNetworkGatewayName
+ location: location
+ properties: {
+ gatewayType: 'Vpn'
+ ipConfigurations: [
+ {
+ name: 'default'
+ properties: {
+ privateIPAllocationMethod: 'Dynamic'
+ subnet: {
+ id: secondaryVirtualNetwork.properties.subnets[0].id
+ }
+ publicIPAddress: {
+ id: secondaryPublicIP.id
+ }
+ }
+ }
+ ]
+ vpnType: 'RouteBased'
+ vpnGatewayGeneration: 'Generation2'
+ sku: {
+ name: 'VpnGw2'
+ tier: 'VpnGw2'
+ }
+ }
+}
+
+@description('The resource ID of the created primary Virtual Network Gateway.')
+output primaryVNETGatewayResourceID string = primaryVNETGateway.id
+
+@description('The resource ID of the created secondary Virtual Network Gateway.')
+output secondaryVNETGatewayResourceID string = secondaryVNETGateway.id
diff --git a/avm/res/network/connection/tests/e2e/max/main.test.bicep b/avm/res/network/connection/tests/e2e/max/main.test.bicep
new file mode 100644
index 0000000000..198a0ec4d5
--- /dev/null
+++ b/avm/res/network/connection/tests/e2e/max/main.test.bicep
@@ -0,0 +1,86 @@
+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}-network.connections-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'ncmax'
+
+@description('Optional. The password to leverage for the shared key.')
+@secure()
+param password string = newGuid()
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ location:location
+ primaryPublicIPName: 'dep-${namePrefix}-pip-${serviceShort}-1'
+ primaryVirtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}-1'
+ primaryVirtualNetworkGatewayName: 'dep-${namePrefix}-vpn-gw-${serviceShort}-1'
+ secondaryPublicIPName: 'dep-${namePrefix}-pip-${serviceShort}-2'
+ secondaryVirtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}-2'
+ secondaryVirtualNetworkGatewayName: 'dep-${namePrefix}-vpn-gw-${serviceShort}-2'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: location
+ name: '${namePrefix}${serviceShort}001'
+ virtualNetworkGateway1: {
+ id: nestedDependencies.outputs.primaryVNETGatewayResourceID
+ }
+ enableBgp: false
+ usePolicyBasedTrafficSelectors: false
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ virtualNetworkGateway2: {
+ id: nestedDependencies.outputs.secondaryVNETGatewayResourceID
+ }
+ connectionType: 'Vnet2Vnet'
+ dpdTimeoutSeconds: 45
+ vpnSharedKey: password
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}]
+
+
diff --git a/avm/res/network/connection/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/network/connection/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a8398dc99e
--- /dev/null
+++ b/avm/res/network/connection/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,132 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the primary Public IP to create.')
+param primaryPublicIPName string
+
+@description('Required. The name of the primary VNET to create.')
+param primaryVirtualNetworkName string
+
+@description('Required. The name of the primary Virtual Network Gateway to create.')
+param primaryVirtualNetworkGatewayName string
+
+@description('Required. The name of the secondary Public IP to create.')
+param secondaryPublicIPName string
+
+@description('Required. The name of the secondary VNET to create.')
+param secondaryVirtualNetworkName string
+
+@description('Required. The name of the secondary Virtual Network Gateway to create.')
+param secondaryVirtualNetworkGatewayName string
+
+resource primaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: primaryVirtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ '10.0.0.0/24'
+ ]
+ }
+ subnets: [
+ {
+ name: 'GatewaySubnet'
+ properties: {
+ addressPrefix: '10.0.0.0/24'
+ }
+ }
+ ]
+ }
+}
+
+resource primaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
+ name: primaryPublicIPName
+ location: location
+}
+
+resource primaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = {
+ name: primaryVirtualNetworkGatewayName
+ location: location
+ properties: {
+ gatewayType: 'Vpn'
+ ipConfigurations: [
+ {
+ name: 'default'
+ properties: {
+ privateIPAllocationMethod: 'Dynamic'
+ subnet: {
+ id: primaryVirtualNetwork.properties.subnets[0].id
+ }
+ publicIPAddress: {
+ id: primaryPublicIP.id
+ }
+ }
+ }
+ ]
+ vpnType: 'RouteBased'
+ vpnGatewayGeneration: 'Generation2'
+ sku: {
+ name: 'VpnGw2'
+ tier: 'VpnGw2'
+ }
+ }
+}
+
+resource secondaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = {
+ name: secondaryVirtualNetworkName
+ location: location
+ properties: {
+ addressSpace: {
+ addressPrefixes: [
+ '10.0.1.0/24'
+ ]
+ }
+ subnets: [
+ {
+ name: 'GatewaySubnet'
+ properties: {
+ addressPrefix: '10.0.1.0/24'
+ }
+ }
+ ]
+ }
+}
+
+resource secondaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
+ name: secondaryPublicIPName
+ location: location
+}
+
+resource secondaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = {
+ name: secondaryVirtualNetworkGatewayName
+ location: location
+ properties: {
+ gatewayType: 'Vpn'
+ ipConfigurations: [
+ {
+ name: 'default'
+ properties: {
+ privateIPAllocationMethod: 'Dynamic'
+ subnet: {
+ id: secondaryVirtualNetwork.properties.subnets[0].id
+ }
+ publicIPAddress: {
+ id: secondaryPublicIP.id
+ }
+ }
+ }
+ ]
+ vpnType: 'RouteBased'
+ vpnGatewayGeneration: 'Generation2'
+ sku: {
+ name: 'VpnGw2'
+ tier: 'VpnGw2'
+ }
+ }
+}
+
+@description('The resource ID of the created primary Virtual Network Gateway.')
+output primaryVNETGatewayResourceID string = primaryVNETGateway.id
+
+@description('The resource ID of the created secondary Virtual Network Gateway.')
+output secondaryVNETGatewayResourceID string = secondaryVNETGateway.id
diff --git a/avm/res/network/connection/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/connection/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..cd0d333d1a
--- /dev/null
+++ b/avm/res/network/connection/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,83 @@
+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}-network.connections-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'ncwaf'
+
+@description('Optional. The password to leverage for the shared key.')
+@secure()
+param password string = newGuid()
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ location:location
+ primaryPublicIPName: 'dep-${namePrefix}-pip-${serviceShort}-1'
+ primaryVirtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}-1'
+ primaryVirtualNetworkGatewayName: 'dep-${namePrefix}-vpn-gw-${serviceShort}-1'
+ secondaryPublicIPName: 'dep-${namePrefix}-pip-${serviceShort}-2'
+ secondaryVirtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}-2'
+ secondaryVirtualNetworkGatewayName: 'dep-${namePrefix}-vpn-gw-${serviceShort}-2'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: location
+ name: '${namePrefix}${serviceShort}001'
+ virtualNetworkGateway1: {
+ id: nestedDependencies.outputs.primaryVNETGatewayResourceID
+ }
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ virtualNetworkGateway2: {
+ id: nestedDependencies.outputs.secondaryVNETGatewayResourceID
+ }
+ connectionType: 'Vnet2Vnet'
+ vpnSharedKey: password
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}]
+
+
diff --git a/avm/res/network/connection/version.json b/avm/res/network/connection/version.json
new file mode 100644
index 0000000000..8def869ede
--- /dev/null
+++ b/avm/res/network/connection/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
+ "version": "0.1",
+ "pathFilters": [
+ "./main.json"
+ ]
+}
From c89ea1c96581c938da29998ba03a17a774151ad7 Mon Sep 17 00:00:00 2001
From: Fabio Masciotra
+
+
+
+### Example 2: _Using large parameter set_
+
+This instance deploys the module with most of its features enabled.
+
+
+
+
+
+
+### Example 3: _Combine a generated and provided Public IP Prefix_
+
+This example shows how you can provide a Public IP Prefix to the module, while also generating one in the module.
+
+
+
+
+
+
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | Name of the Azure Bastion resource. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. |
+| [`idleTimeoutInMinutes`](#parameter-idletimeoutinminutes) | int | The idle timeout of the NAT gateway. |
+| [`location`](#parameter-location) | string | Location for all resources. |
+| [`lock`](#parameter-lock) | object | The lock settings of the service. |
+| [`publicIPAddressObjects`](#parameter-publicipaddressobjects) | array | Specifies the properties of the Public IPs to create and be used by the NAT Gateway. |
+| [`publicIPPrefixObjects`](#parameter-publicipprefixobjects) | array | Specifies the properties of the Public IP Prefixes to create and be used by the NAT Gateway. |
+| [`publicIPPrefixResourceIds`](#parameter-publicipprefixresourceids) | array | Existing Public IP Prefixes resource IDs to use for the NAT Gateway. |
+| [`publicIpResourceIds`](#parameter-publicipresourceids) | array | Existing Public IP Address resource IDs to use for the NAT Gateway. |
+| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. |
+| [`tags`](#parameter-tags) | object | Tags for the resource. |
+| [`zones`](#parameter-zones) | array | A list of availability zones denoting the zone in which Nat Gateway should be deployed. |
+
+### Parameter: `name`
+
+Name of the Azure Bastion resource.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `enableTelemetry`
+
+Enable/Disable usage telemetry for module.
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `idleTimeoutInMinutes`
+
+The idle timeout of the NAT gateway.
+
+- Required: No
+- Type: int
+- Default: `5`
+
+### 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: `publicIPAddressObjects`
+
+Specifies the properties of the Public IPs to create and be used by the NAT Gateway.
+
+- Required: No
+- Type: array
+
+### Parameter: `publicIPPrefixObjects`
+
+Specifies the properties of the Public IP Prefixes to create and be used by the NAT Gateway.
+
+- Required: No
+- Type: array
+
+### Parameter: `publicIPPrefixResourceIds`
+
+Existing Public IP Prefixes resource IDs to use for the NAT Gateway.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+### Parameter: `publicIpResourceIds`
+
+Existing Public IP Address resource IDs to use for the NAT Gateway.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+### 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`
+
+Tags for the resource.
+
+- Required: No
+- Type: object
+
+### Parameter: `zones`
+
+A list of availability zones denoting the zone in which Nat Gateway should be deployed.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `location` | string | The location the resource was deployed into. |
+| `name` | string | The name of the NAT Gateway. |
+| `resourceGroupName` | string | The resource group the NAT Gateway was deployed into. |
+| `resourceId` | string | The resource ID of the NAT Gateway. |
+
+## Cross-referenced modules
+
+This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs).
+
+| Reference | Type |
+| :-- | :-- |
+| `res/network/public-ip-prefix` | Local reference |
+| `br/public:avm/res/network/public-ip-address:0.2.1` | Remote reference |
diff --git a/avm/res/network/nat-gateway/main.bicep b/avm/res/network/nat-gateway/main.bicep
new file mode 100644
index 0000000000..536e51e716
--- /dev/null
+++ b/avm/res/network/nat-gateway/main.bicep
@@ -0,0 +1,238 @@
+metadata name = 'NAT Gateways'
+metadata description = 'This module deploys a NAT Gateway.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Required. Name of the Azure Bastion resource.')
+param name string
+
+@description('Optional. The idle timeout of the NAT gateway.')
+param idleTimeoutInMinutes int = 5
+
+@description('Optional. Existing Public IP Address resource IDs to use for the NAT Gateway.')
+param publicIpResourceIds array = []
+
+@description('Optional. Existing Public IP Prefixes resource IDs to use for the NAT Gateway.')
+param publicIPPrefixResourceIds array = []
+
+@description('Optional. Specifies the properties of the Public IPs to create and be used by the NAT Gateway.')
+param publicIPAddressObjects array?
+
+@description('Optional. Specifies the properties of the Public IP Prefixes to create and be used by the NAT Gateway.')
+param publicIPPrefixObjects array?
+
+@description('Optional. A list of availability zones denoting the zone in which Nat Gateway should be deployed.')
+param zones array = []
+
+@description('Optional. Location for all resources.')
+param location string = resourceGroup().location
+
+@description('Optional. The lock settings of the service.')
+param lock lockType
+
+@description('Optional. Array of role assignments to create.')
+param roleAssignments roleAssignmentType
+
+@description('Optional. Tags for the resource.')
+param tags object?
+
+@description('Optional. Enable/Disable usage telemetry for module.')
+param enableTelemetry bool = true
+
+var builtInRoleNames = {
+ Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
+ 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')
+ 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: take('46d3xbcp.res.network-natgateway.${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'
+ }
+ }
+ }
+ }
+}
+
+module publicIPAddresses 'br/public:avm/res/network/public-ip-address:0.2.1' = [for (publicIPAddressObject, index) in (publicIPAddressObjects ?? []): {
+ name: '${uniqueString(deployment().name, location)}-NatGw-PIP-${index}'
+ params: {
+ name: contains(publicIPAddressObject, 'name') ? publicIPAddressObject.name : '${name}-pip'
+ location: location
+ lock: publicIPAddressObject.?lock ?? lock
+ diagnosticSettings: publicIPAddressObject.?diagnosticSettings
+ publicIPAddressVersion: contains(publicIPAddressObject, 'publicIPAddressVersion') ? publicIPAddressObject.publicIPAddressVersion : 'IPv4'
+ publicIPAllocationMethod: 'Static'
+ publicIpPrefixResourceId: contains(publicIPAddressObject, 'publicIPPrefixResourceId') ? publicIPAddressObject.publicIPPrefixResourceId : ''
+ roleAssignments: contains(publicIPAddressObject, 'roleAssignments') ? publicIPAddressObject.roleAssignments : []
+ skuName: 'Standard'
+ skuTier: contains(publicIPAddressObject, 'skuTier') ? publicIPAddressObject.skuTier : 'Regional'
+ tags: publicIPAddressObject.?tags ?? tags
+ zones: contains(publicIPAddressObject, 'zones') ? publicIPAddressObject.zones : []
+ }
+}]
+
+module formattedPublicIpResourceIds 'modules/formatResourceId.bicep' = {
+ name: 'formattedPublicIpResourceIds'
+ params: {
+ generatedResourceIds: [for (obj, index) in (publicIPAddressObjects ?? []): publicIPAddresses[index].outputs.resourceId]
+ providedResourceIds: publicIpResourceIds
+ }
+}
+
+module publicIPPrefixes '../public-ip-prefix/main.bicep' = [for (publicIPPrefixObject, index) in (publicIPPrefixObjects ?? []): {
+ name: '${uniqueString(deployment().name, location)}-NatGw-Prefix-PIP-${index}'
+ params: {
+ name: contains(publicIPPrefixObject, 'name') ? publicIPPrefixObject.name : '${name}-pip'
+ location: location
+ lock: publicIPPrefixObject.?lock ?? lock
+ prefixLength: publicIPPrefixObject.prefixLength
+ customIPPrefix: publicIPPrefixObject.?customIPPrefix
+ roleAssignments: publicIPPrefixObject.?roleAssignments
+ tags: publicIPPrefixObject.?tags ?? tags
+ }
+}]
+module formattedPublicIpPrefixResourceIds 'modules/formatResourceId.bicep' = {
+ name: 'formattedPublicIpPrefixResourceIds'
+ params: {
+ generatedResourceIds: [for (obj, index) in (publicIPPrefixObjects ?? []): publicIPPrefixes[index].outputs.resourceId]
+ providedResourceIds: publicIPPrefixResourceIds
+
+ }
+}
+
+// NAT GATEWAY
+// ===========
+resource natGateway 'Microsoft.Network/natGateways@2023-04-01' = {
+ name: name
+ location: location
+ tags: tags
+ sku: {
+ name: 'Standard'
+ }
+ properties: {
+ idleTimeoutInMinutes: idleTimeoutInMinutes
+ publicIpPrefixes: formattedPublicIpPrefixResourceIds.outputs.formattedResourceIds
+ publicIpAddresses: formattedPublicIpResourceIds.outputs.formattedResourceIds
+ }
+ zones: zones
+}
+
+resource natGateway_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: natGateway
+}
+
+resource natGateway_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in (roleAssignments ?? []): {
+ name: guid(natGateway.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: natGateway
+}]
+
+@description('The name of the NAT Gateway.')
+output name string = natGateway.name
+
+@description('The resource ID of the NAT Gateway.')
+output resourceId string = natGateway.id
+
+@description('The resource group the NAT Gateway was deployed into.')
+output resourceGroupName string = resourceGroup().name
+
+@description('The location the resource was deployed into.')
+output location string = natGateway.location
+
+// =============== //
+// Definitions //
+// =============== //
+
+type lockType = {
+ @description('Optional. Specify the name of lock.')
+ name: string?
+
+ @description('Optional. Specify the type of lock.')
+ kind: ('CanNotDelete' | 'ReadOnly' | 'None')?
+}?
+
+type roleAssignmentType = {
+ @description('Required. The 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. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to \'\' to disable log collection.')
+ metricCategories: {
+ @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to \'AllMetrics\' to collect all metrics.')
+ category: string
+ }[]?
+
+ @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.')
+ logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics')?
+
+ @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/network/nat-gateway/main.json b/avm/res/network/nat-gateway/main.json
new file mode 100644
index 0000000000..fc3df92085
--- /dev/null
+++ b/avm/res/network/nat-gateway/main.json
@@ -0,0 +1,1448 @@
+{
+ "$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.23.1.45101",
+ "templateHash": "14427571589662041171"
+ },
+ "name": "NAT Gateways",
+ "description": "This module deploys a NAT Gateway.",
+ "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
+ },
+ "diagnosticSettingType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of diagnostic setting."
+ }
+ },
+ "logCategoriesAndGroups": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "category": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here."
+ }
+ },
+ "categoryGroup": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection."
+ }
+ },
+ "metricCategories": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "category": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to 'AllMetrics' to collect all metrics."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection."
+ }
+ },
+ "logAnalyticsDestinationType": {
+ "type": "string",
+ "allowedValues": [
+ "AzureDiagnostics",
+ "Dedicated"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type."
+ }
+ },
+ "workspaceResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "storageAccountResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "eventHubAuthorizationRuleResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to."
+ }
+ },
+ "eventHubName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "marketplacePartnerResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs."
+ }
+ }
+ }
+ },
+ "nullable": true
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the Azure Bastion resource."
+ }
+ },
+ "idleTimeoutInMinutes": {
+ "type": "int",
+ "defaultValue": 5,
+ "metadata": {
+ "description": "Optional. The idle timeout of the NAT gateway."
+ }
+ },
+ "publicIpResourceIds": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. Existing Public IP Address resource IDs to use for the NAT Gateway."
+ }
+ },
+ "publicIPPrefixResourceIds": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. Existing Public IP Prefixes resource IDs to use for the NAT Gateway."
+ }
+ },
+ "publicIPAddressObjects": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the properties of the Public IPs to create and be used by the NAT Gateway."
+ }
+ },
+ "publicIPPrefixObjects": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specifies the properties of the Public IP Prefixes to create and be used by the NAT Gateway."
+ }
+ },
+ "zones": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. A list of availability zones denoting the zone in which Nat Gateway should be deployed."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "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."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags for the resource."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ }
+ },
+ "variables": {
+ "builtInRoleNames": {
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]",
+ "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": "[take(format('46d3xbcp.res.network-natgateway.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('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"
+ }
+ }
+ }
+ }
+ },
+ "natGateway": {
+ "type": "Microsoft.Network/natGateways",
+ "apiVersion": "2023-04-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "sku": {
+ "name": "Standard"
+ },
+ "properties": {
+ "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]",
+ "publicIpPrefixes": "[reference('formattedPublicIpPrefixResourceIds').outputs.formattedResourceIds.value]",
+ "publicIpAddresses": "[reference('formattedPublicIpResourceIds').outputs.formattedResourceIds.value]"
+ },
+ "zones": "[parameters('zones')]",
+ "dependsOn": [
+ "formattedPublicIpPrefixResourceIds",
+ "formattedPublicIpResourceIds"
+ ]
+ },
+ "natGateway_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.Network/natGateways/{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": [
+ "natGateway"
+ ]
+ },
+ "natGateway_roleAssignments": {
+ "copy": {
+ "name": "natGateway_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Network/natGateways/{0}', parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.Network/natGateways', 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": [
+ "natGateway"
+ ]
+ },
+ "publicIPAddresses": {
+ "copy": {
+ "name": "publicIPAddresses",
+ "count": "[length(coalesce(parameters('publicIPAddressObjects'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-NatGw-PIP-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": "[if(contains(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'name'), createObject('value', coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()].name), createObject('value', format('{0}-pip', parameters('name'))))]",
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "lock": {
+ "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'lock'), parameters('lock'))]"
+ },
+ "diagnosticSettings": {
+ "value": "[tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'diagnosticSettings')]"
+ },
+ "publicIPAddressVersion": "[if(contains(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'publicIPAddressVersion'), createObject('value', coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()].publicIPAddressVersion), createObject('value', 'IPv4'))]",
+ "publicIPAllocationMethod": {
+ "value": "Static"
+ },
+ "publicIpPrefixResourceId": "[if(contains(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'publicIPPrefixResourceId'), createObject('value', coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()].publicIPPrefixResourceId), createObject('value', ''))]",
+ "roleAssignments": "[if(contains(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'roleAssignments'), createObject('value', coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()].roleAssignments), createObject('value', createArray()))]",
+ "skuName": {
+ "value": "Standard"
+ },
+ "skuTier": "[if(contains(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'skuTier'), createObject('value', coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()].skuTier), createObject('value', 'Regional'))]",
+ "tags": {
+ "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'tags'), parameters('tags'))]"
+ },
+ "zones": "[if(contains(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'zones'), createObject('value', coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()].zones), createObject('value', createArray()))]"
+ },
+ "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.23.1.45101",
+ "templateHash": "3488076626994379707"
+ },
+ "name": "Public IP Addresses",
+ "description": "This module deploys a Public IP Address.",
+ "owner": "Azure/module-maintainers"
+ },
+ "definitions": {
+ "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
+ },
+ "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
+ },
+ "dnsSettingsType": {
+ "type": "object",
+ "properties": {
+ "domainNameLabel": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The domain name label. The concatenation of the domain name label and the regionalized DNS zone make up the fully qualified domain name associated with the public IP address. If a domain name label is specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system."
+ }
+ },
+ "domainNameLabelScope": {
+ "type": "string",
+ "allowedValues": [
+ "",
+ "NoReuse",
+ "ResourceGroupReuse",
+ "SubscriptionReuse",
+ "TenantReuse"
+ ],
+ "metadata": {
+ "description": "Required. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN."
+ }
+ },
+ "fqdn": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Fully Qualified Domain Name of the A DNS record associated with the public IP. This is the concatenation of the domainNameLabel and the regionalized DNS zone."
+ }
+ },
+ "reverseFqdn": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The reverse FQDN. A user-visible, fully qualified domain name that resolves to this public IP address. If the reverseFqdn is specified, then a PTR DNS record is created pointing from the IP address in the in-addr.arpa domain to the reverse FQDN."
+ }
+ }
+ }
+ },
+ "ddosSettingsType": {
+ "type": "object",
+ "properties": {
+ "ddosProtectionPlan": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ }
+ },
+ "metadata": {
+ "description": "Required. The DDoS protection plan ID associated with the public IP address."
+ }
+ },
+ "protectionMode": {
+ "type": "string",
+ "allowedValues": [
+ "Enabled"
+ ],
+ "metadata": {
+ "description": "Required. The DDoS protection policy customizations."
+ }
+ }
+ }
+ },
+ "diagnosticSettingType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of diagnostic setting."
+ }
+ },
+ "logCategoriesAndGroups": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "category": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here."
+ }
+ },
+ "categoryGroup": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection."
+ }
+ },
+ "metricCategories": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "category": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection."
+ }
+ },
+ "logAnalyticsDestinationType": {
+ "type": "string",
+ "allowedValues": [
+ "AzureDiagnostics",
+ "Dedicated"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type."
+ }
+ },
+ "workspaceResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "storageAccountResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "eventHubAuthorizationRuleResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to."
+ }
+ },
+ "eventHubName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub."
+ }
+ },
+ "marketplacePartnerResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs."
+ }
+ }
+ }
+ },
+ "nullable": true
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the Public IP Address."
+ }
+ },
+ "publicIpPrefixResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix."
+ }
+ },
+ "publicIPAllocationMethod": {
+ "type": "string",
+ "defaultValue": "Static",
+ "allowedValues": [
+ "Dynamic",
+ "Static"
+ ],
+ "metadata": {
+ "description": "Optional. The public IP address allocation method."
+ }
+ },
+ "zones": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from."
+ }
+ },
+ "publicIPAddressVersion": {
+ "type": "string",
+ "defaultValue": "IPv4",
+ "allowedValues": [
+ "IPv4",
+ "IPv6"
+ ],
+ "metadata": {
+ "description": "Optional. IP address version."
+ }
+ },
+ "dnsSettings": {
+ "$ref": "#/definitions/dnsSettingsType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The DNS settings of the public IP address."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "skuName": {
+ "type": "string",
+ "defaultValue": "Standard",
+ "allowedValues": [
+ "Basic",
+ "Standard"
+ ],
+ "metadata": {
+ "description": "Optional. Name of a public IP address SKU."
+ }
+ },
+ "skuTier": {
+ "type": "string",
+ "defaultValue": "Regional",
+ "allowedValues": [
+ "Global",
+ "Regional"
+ ],
+ "metadata": {
+ "description": "Optional. Tier of a public IP address SKU."
+ }
+ },
+ "ddosSettings": {
+ "$ref": "#/definitions/ddosSettingsType",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The DDoS protection plan configuration associated with the public IP address."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "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."
+ }
+ },
+ "idleTimeoutInMinutes": {
+ "type": "int",
+ "defaultValue": 4,
+ "metadata": {
+ "description": "Optional. The idle timeout of the public IP address."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ },
+ "diagnosticSettings": {
+ "$ref": "#/definitions/diagnosticSettingType",
+ "metadata": {
+ "description": "Optional. The diagnostic settings of the service."
+ }
+ }
+ },
+ "variables": {
+ "builtInRoleNames": {
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]",
+ "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]",
+ "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]",
+ "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]",
+ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]",
+ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
+ "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]",
+ "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')]"
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "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))]",
+ "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"
+ }
+ }
+ }
+ }
+ },
+ "publicIpAddress": {
+ "type": "Microsoft.Network/publicIPAddresses",
+ "apiVersion": "2023-04-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "sku": {
+ "name": "[parameters('skuName')]",
+ "tier": "[parameters('skuTier')]"
+ },
+ "zones": "[parameters('zones')]",
+ "properties": {
+ "ddosSettings": "[parameters('ddosSettings')]",
+ "dnsSettings": "[parameters('dnsSettings')]",
+ "publicIPAddressVersion": "[parameters('publicIPAddressVersion')]",
+ "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]",
+ "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]",
+ "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]",
+ "ipTags": []
+ }
+ },
+ "publicIpAddress_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.Network/publicIPAddresses/{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": [
+ "publicIpAddress"
+ ]
+ },
+ "publicIpAddress_roleAssignments": {
+ "copy": {
+ "name": "publicIpAddress_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', 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": [
+ "publicIpAddress"
+ ]
+ },
+ "publicIpAddress_diagnosticSettings": {
+ "copy": {
+ "name": "publicIpAddress_diagnosticSettings",
+ "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]"
+ },
+ "type": "Microsoft.Insights/diagnosticSettings",
+ "apiVersion": "2021-05-01-preview",
+ "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]",
+ "properties": {
+ "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]",
+ "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]",
+ "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]",
+ "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]",
+ "metrics": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics', 'timeGrain', null(), 'enabled', true())))]",
+ "logs": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs', 'enabled', true())))]",
+ "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]",
+ "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]"
+ },
+ "dependsOn": [
+ "publicIpAddress"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the public IP address was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the public IP address."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the public IP address."
+ },
+ "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]"
+ },
+ "ipAddress": {
+ "type": "string",
+ "metadata": {
+ "description": "The public IP address of the public IP address resource."
+ },
+ "value": "[if(contains(reference('publicIpAddress'), 'ipAddress'), reference('publicIpAddress').ipAddress, '')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('publicIpAddress', '2023-04-01', 'full').location]"
+ }
+ }
+ }
+ }
+ },
+ "formattedPublicIpResourceIds": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "formattedPublicIpResourceIds",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "generatedResourceIds": {
+ "copy": [
+ {
+ "name": "value",
+ "count": "[length(coalesce(parameters('publicIPAddressObjects'), createArray()))]",
+ "input": "[reference(format('publicIPAddresses[{0}]', copyIndex('value'))).outputs.resourceId.value]"
+ }
+ ]
+ },
+ "providedResourceIds": {
+ "value": "[parameters('publicIpResourceIds')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.23.1.45101",
+ "templateHash": "16528829671778949522"
+ }
+ },
+ "parameters": {
+ "generatedResourceIds": {
+ "type": "array",
+ "defaultValue": []
+ },
+ "providedResourceIds": {
+ "type": "array",
+ "defaultValue": []
+ }
+ },
+ "resources": [],
+ "outputs": {
+ "formattedResourceIds": {
+ "type": "array",
+ "copy": {
+ "count": "[length(concat(parameters('generatedResourceIds'), parameters('providedResourceIds')))]",
+ "input": {
+ "id": "[concat(parameters('generatedResourceIds'), parameters('providedResourceIds'))[copyIndex()]]"
+ }
+ }
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "publicIPAddresses"
+ ]
+ },
+ "publicIPPrefixes": {
+ "copy": {
+ "name": "publicIPPrefixes",
+ "count": "[length(coalesce(parameters('publicIPPrefixObjects'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-NatGw-Prefix-PIP-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": "[if(contains(coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()], 'name'), createObject('value', coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()].name), createObject('value', format('{0}-pip', parameters('name'))))]",
+ "location": {
+ "value": "[parameters('location')]"
+ },
+ "lock": {
+ "value": "[coalesce(tryGet(coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()], 'lock'), parameters('lock'))]"
+ },
+ "prefixLength": {
+ "value": "[coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()].prefixLength]"
+ },
+ "customIPPrefix": {
+ "value": "[tryGet(coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()], 'customIPPrefix')]"
+ },
+ "roleAssignments": {
+ "value": "[tryGet(coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()], 'roleAssignments')]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(coalesce(parameters('publicIPPrefixObjects'), 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.23.1.45101",
+ "templateHash": "10775565777623704767"
+ },
+ "name": "Public IP Prefixes",
+ "description": "This module deploys a Public IP Prefix.",
+ "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,
+ "metadata": {
+ "description": "Required. Name of the Public IP Prefix."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "prefixLength": {
+ "type": "int",
+ "minValue": 28,
+ "maxValue": 31,
+ "metadata": {
+ "description": "Required. Length of the Public IP Prefix."
+ }
+ },
+ "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."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ },
+ "customIPPrefix": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. The customIpPrefix that this prefix is associated with. A custom IP address prefix is a contiguous range of IP addresses owned by an external customer and provisioned into a subscription. When a custom IP prefix is in Provisioned, Commissioning, or Commissioned state, a linked public IP prefix can be created. Either as a subset of the custom IP prefix range or the entire range."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ }
+ },
+ "variables": {
+ "builtInRoleNames": {
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]",
+ "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.network-publicipprefix.{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"
+ }
+ }
+ }
+ }
+ },
+ "publicIpPrefix": {
+ "type": "Microsoft.Network/publicIPPrefixes",
+ "apiVersion": "2023-04-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "sku": {
+ "name": "Standard"
+ },
+ "properties": {
+ "customIPPrefix": "[if(not(empty(parameters('customIPPrefix'))), parameters('customIPPrefix'), null())]",
+ "publicIPAddressVersion": "IPv4",
+ "prefixLength": "[parameters('prefixLength')]"
+ }
+ },
+ "publicIpPrefix_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.Network/publicIPPrefixes/{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": [
+ "publicIpPrefix"
+ ]
+ },
+ "publicIpPrefix_roleAssignments": {
+ "copy": {
+ "name": "publicIpPrefix_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Network/publicIPPrefixes/{0}', parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.Network/publicIPPrefixes', 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": [
+ "publicIpPrefix"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the public IP prefix."
+ },
+ "value": "[resourceId('Microsoft.Network/publicIPPrefixes', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the public IP prefix was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the public IP prefix."
+ },
+ "value": "[parameters('name')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('publicIpPrefix', '2023-04-01', 'full').location]"
+ }
+ }
+ }
+ }
+ },
+ "formattedPublicIpPrefixResourceIds": {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "formattedPublicIpPrefixResourceIds",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "generatedResourceIds": {
+ "copy": [
+ {
+ "name": "value",
+ "count": "[length(coalesce(parameters('publicIPPrefixObjects'), createArray()))]",
+ "input": "[reference(format('publicIPPrefixes[{0}]', copyIndex('value'))).outputs.resourceId.value]"
+ }
+ ]
+ },
+ "providedResourceIds": {
+ "value": "[parameters('publicIPPrefixResourceIds')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.23.1.45101",
+ "templateHash": "16528829671778949522"
+ }
+ },
+ "parameters": {
+ "generatedResourceIds": {
+ "type": "array",
+ "defaultValue": []
+ },
+ "providedResourceIds": {
+ "type": "array",
+ "defaultValue": []
+ }
+ },
+ "resources": [],
+ "outputs": {
+ "formattedResourceIds": {
+ "type": "array",
+ "copy": {
+ "count": "[length(concat(parameters('generatedResourceIds'), parameters('providedResourceIds')))]",
+ "input": {
+ "id": "[concat(parameters('generatedResourceIds'), parameters('providedResourceIds'))[copyIndex()]]"
+ }
+ }
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "publicIPPrefixes"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the NAT Gateway."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the NAT Gateway."
+ },
+ "value": "[resourceId('Microsoft.Network/natGateways', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the NAT Gateway was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('natGateway', '2023-04-01', 'full').location]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/network/nat-gateway/modules/formatResourceId.bicep b/avm/res/network/nat-gateway/modules/formatResourceId.bicep
new file mode 100644
index 0000000000..b4aa1ad772
--- /dev/null
+++ b/avm/res/network/nat-gateway/modules/formatResourceId.bicep
@@ -0,0 +1,6 @@
+param generatedResourceIds array = []
+param providedResourceIds array = []
+
+output formattedResourceIds array = [for resourceId in concat(generatedResourceIds, providedResourceIds): {
+ id: resourceId
+}]
diff --git a/avm/res/network/nat-gateway/tests/e2e/defaults/main.test.bicep b/avm/res/network/nat-gateway/tests/e2e/defaults/main.test.bicep
new file mode 100644
index 0000000000..f98611c101
--- /dev/null
+++ b/avm/res/network/nat-gateway/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)
+// e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg'
+param resourceGroupName string = 'dep-${namePrefix}-network.natgateway-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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.')
+// e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test
+param serviceShort string = 'nngmin'
+
+@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: location
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ // You parameters go here
+ location: location
+ name: '${namePrefix}${serviceShort}001'
+ }
+}]
diff --git a/avm/res/network/nat-gateway/tests/e2e/max/dependencies.bicep b/avm/res/network/nat-gateway/tests/e2e/max/dependencies.bicep
new file mode 100644
index 0000000000..a7f42aee7b
--- /dev/null
+++ b/avm/res/network/nat-gateway/tests/e2e/max/dependencies.bicep
@@ -0,0 +1,13 @@
+@description('Optional. The location to deploy 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/network/nat-gateway/tests/e2e/max/main.test.bicep b/avm/res/network/nat-gateway/tests/e2e/max/main.test.bicep
new file mode 100644
index 0000000000..d443a1c30e
--- /dev/null
+++ b/avm/res/network/nat-gateway/tests/e2e/max/main.test.bicep
@@ -0,0 +1,137 @@
+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}-network.natgateway-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'nngmax'
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ location:location
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ }
+}
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: location
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ publicIPAddressObjects: [
+ {
+ name: '${namePrefix}${serviceShort}001-pip'
+ 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'
+ }
+ ]
+ skuTier: 'Regional'
+ zones: [
+ '1'
+ '2'
+ '3'
+ ]
+ diagnosticSettings: [
+ {
+ name: 'customSetting'
+ metricCategories: [
+ {
+ category: 'AllMetrics'
+ }
+ ]
+ eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName
+ eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId
+ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId
+ workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
+ }
+ ]
+ }
+ ]
+ 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'
+ }
+ }
+}]
diff --git a/avm/res/network/nat-gateway/tests/e2e/prefixCombined/main.test.bicep b/avm/res/network/nat-gateway/tests/e2e/prefixCombined/main.test.bicep
new file mode 100644
index 0000000000..ebb524443b
--- /dev/null
+++ b/avm/res/network/nat-gateway/tests/e2e/prefixCombined/main.test.bicep
@@ -0,0 +1,56 @@
+targetScope = 'subscription'
+
+metadata name = 'Combine a generated and provided Public IP Prefix'
+metadata description = 'This example shows how you can provide a Public IP Prefix to the module, while also generating one in the module.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-network.natgateway-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'nngcprx'
+
+
+@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: location
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: location
+ name: '${namePrefix}${serviceShort}001'
+ publicIPPrefixObjects: [
+ {
+ name: '${namePrefix}${serviceShort}001-pippre'
+ prefixLength: 30
+ tags: {
+ 'hidden-title': 'CustomTag'
+ }
+ }
+ ]
+ }
+}]
diff --git a/avm/res/network/nat-gateway/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/nat-gateway/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..3d95875960
--- /dev/null
+++ b/avm/res/network/nat-gateway/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,95 @@
+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}-network.natgateway-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'nngwaf'
+
+@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: location
+}
+
+
+// Diagnostics
+// ===========
+module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-diagnosticDependencies'
+ params: {
+ storageAccountName: 'dep${namePrefix}diasa${serviceShort}01'
+ logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}'
+ eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}'
+ eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: location
+ name: '${namePrefix}${serviceShort}001'
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ publicIPAddressObjects: [
+ {
+ name: '${namePrefix}${serviceShort}001-pip'
+ skuTier: 'Regional'
+ zones: [
+ '1'
+ '2'
+ '3'
+ ]
+ 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/network/nat-gateway/version.json b/avm/res/network/nat-gateway/version.json
new file mode 100644
index 0000000000..40ec00be0e
--- /dev/null
+++ b/avm/res/network/nat-gateway/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
+ "version": "1.0",
+ "pathFilters": [
+ "./main.json"
+ ]
+}
From 77913e2ccdc705947e05d0036154c3cb30e669c5 Mon Sep 17 00:00:00 2001
From: Fabio Masciotra
+
+
+
+### Example 2: _Using large parameter set_
+
+This instance deploys the module with most of its features enabled.
+
+
+
+
+
+
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | Name of the VPN gateway. |
+| [`virtualHubResourceId`](#parameter-virtualhubresourceid) | string | The resource ID of a virtual Hub to connect to. Note: The virtual Hub and Gateway must be deployed into the same location. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`bgpSettings`](#parameter-bgpsettings) | object | BGP settings details. |
+| [`enableBgpRouteTranslationForNat`](#parameter-enablebgproutetranslationfornat) | bool | Enable BGP routes translation for NAT on this VPN gateway. |
+| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). |
+| [`isRoutingPreferenceInternet`](#parameter-isroutingpreferenceinternet) | bool | Enable routing preference property for the public IP interface of the VPN gateway. |
+| [`location`](#parameter-location) | string | Location where all resources will be created. |
+| [`lock`](#parameter-lock) | object | The lock settings of the service. |
+| [`natRules`](#parameter-natrules) | array | List of all the NAT Rules to associate with the gateway. |
+| [`tags`](#parameter-tags) | object | Tags of the resource. |
+| [`vpnConnections`](#parameter-vpnconnections) | array | The VPN connections to create in the VPN gateway. |
+| [`vpnGatewayScaleUnit`](#parameter-vpngatewayscaleunit) | int | The scale unit for this VPN gateway. |
+
+### Parameter: `name`
+
+Name of the VPN gateway.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `virtualHubResourceId`
+
+The resource ID of a virtual Hub to connect to. Note: The virtual Hub and Gateway must be deployed into the same location.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `bgpSettings`
+
+BGP settings details.
+
+- Required: No
+- Type: object
+- Default: `{}`
+
+### Parameter: `enableBgpRouteTranslationForNat`
+
+Enable BGP routes translation for NAT on this VPN gateway.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `enableTelemetry`
+
+Enable telemetry via a Globally Unique Identifier (GUID).
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `isRoutingPreferenceInternet`
+
+Enable routing preference property for the public IP interface of the VPN gateway.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `location`
+
+Location where all resources will be created.
+
+- 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: `natRules`
+
+List of all the NAT Rules to associate with the gateway.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+### Parameter: `tags`
+
+Tags of the resource.
+
+- Required: No
+- Type: object
+
+### Parameter: `vpnConnections`
+
+The VPN connections to create in the VPN gateway.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+### Parameter: `vpnGatewayScaleUnit`
+
+The scale unit for this VPN gateway.
+
+- Required: No
+- Type: int
+- Default: `2`
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `location` | string | The location the resource was deployed into. |
+| `name` | string | The name of the VPN gateway. |
+| `resourceGroupName` | string | The name of the resource group the VPN gateway was deployed into. |
+| `resourceId` | string | The resource ID of the VPN gateway. |
+
+## Cross-referenced modules
+
+_None_
+
+## Notes
+
+### Parameter Usage: `bgpSettings`
+
+
diff --git a/avm/res/network/vpn-gateway/main.bicep b/avm/res/network/vpn-gateway/main.bicep
new file mode 100644
index 0000000000..1b7b5f8f4d
--- /dev/null
+++ b/avm/res/network/vpn-gateway/main.bicep
@@ -0,0 +1,160 @@
+metadata name = 'VPN Gateways'
+metadata description = 'This module deploys a VPN Gateway.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Required. Name of the VPN gateway.')
+param name string
+
+@description('Optional. Location where all resources will be created.')
+param location string = resourceGroup().location
+
+@description('Optional. The VPN connections to create in the VPN gateway.')
+param vpnConnections array = []
+
+@description('Optional. List of all the NAT Rules to associate with the gateway.')
+param natRules array = []
+
+@description('Required. The resource ID of a virtual Hub to connect to. Note: The virtual Hub and Gateway must be deployed into the same location.')
+param virtualHubResourceId string
+
+@description('Optional. BGP settings details.')
+param bgpSettings object = {}
+
+@description('Optional. Enable BGP routes translation for NAT on this VPN gateway.')
+param enableBgpRouteTranslationForNat bool = false
+
+@description('Optional. Enable routing preference property for the public IP interface of the VPN gateway.')
+param isRoutingPreferenceInternet bool = false
+
+@description('Optional. The scale unit for this VPN gateway.')
+param vpnGatewayScaleUnit int = 2
+
+@description('Optional. Tags of the resource.')
+param tags object?
+
+@description('Optional. The lock settings of the service.')
+param lock lockType
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableTelemetry bool = true
+
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: take('46d3xbcp.res.network-vpngateway.${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 vpnGateway 'Microsoft.Network/vpnGateways@2023-04-01' = {
+ name: name
+ location: location
+ tags: tags
+ properties: {
+ bgpSettings: bgpSettings
+ enableBgpRouteTranslationForNat: enableBgpRouteTranslationForNat
+ isRoutingPreferenceInternet: isRoutingPreferenceInternet
+ vpnGatewayScaleUnit: vpnGatewayScaleUnit
+ connections: [for (connection, index) in vpnConnections: {
+ name: connection.name
+ properties: {
+ connectionBandwidth: connection.?connectionBandwidth
+ enableBgp: connection.?enableBgp
+ enableInternetSecurity: connection.?enableInternetSecurity
+ remoteVpnSite: contains(connection, 'remoteVpnSiteResourceId') ? {
+ id: connection.remoteVpnSiteResourceId
+ } : null
+ enableRateLimiting: connection.?enableRateLimiting
+ routingConfiguration: connection.?routingConfiguration
+ routingWeight: connection.?routingWeight
+ sharedKey: connection.?sharedKey
+ useLocalAzureIpAddress: connection.?useLocalAzureIpAddress
+ usePolicyBasedTrafficSelectors: connection.?usePolicyBasedTrafficSelectors
+ vpnConnectionProtocolType: connection.?vpnConnectionProtocolType
+ ipsecPolicies: connection.?ipsecPolicies
+ trafficSelectorPolicies: connection.?trafficSelectorPolicies
+ vpnLinkConnections: connection.?vpnLinkConnections
+ }
+ }]
+ virtualHub: {
+ id: virtualHubResourceId
+ }
+ }
+}
+
+resource vpnGateway_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: vpnGateway
+}
+
+module vpnGateway_natRules 'nat-rule/main.bicep' = [for (natRule, index) in natRules: {
+ name: '${deployment().name}-NATRule-${index}'
+ params: {
+ name: natRule.name
+ vpnGatewayName: vpnGateway.name
+ externalMappings: contains(natRule, 'externalMappings') ? natRule.externalMappings : []
+ internalMappings: contains(natRule, 'internalMappings') ? natRule.internalMappings : []
+ ipConfigurationId: contains(natRule, 'ipConfigurationId') ? natRule.ipConfigurationId : ''
+ mode: contains(natRule, 'mode') ? natRule.mode : ''
+ type: contains(natRule, 'type') ? natRule.type : ''
+ }
+}]
+
+module vpnGateway_vpnConnections 'vpn-connection/main.bicep' = [for (connection, index) in vpnConnections: {
+ name: '${deployment().name}-Connection-${index}'
+ params: {
+ name: connection.name
+ vpnGatewayName: vpnGateway.name
+ connectionBandwidth: connection.?connectionBandwidth
+ enableBgp: connection.?enableBgp
+ enableInternetSecurity: connection.?enableInternetSecurity
+ remoteVpnSiteResourceId: connection.?remoteVpnSiteResourceId
+ enableRateLimiting: connection.?enableRateLimiting
+ routingConfiguration: connection.?routingConfiguration
+ routingWeight: connection.?routingWeight
+ sharedKey: connection.?sharedKey
+ useLocalAzureIpAddress: connection.?useLocalAzureIpAddress
+ usePolicyBasedTrafficSelectors: connection.?usePolicyBasedTrafficSelectors
+ vpnConnectionProtocolType: connection.?vpnConnectionProtocolType
+ trafficSelectorPolicies: connection.?trafficSelectorPolicies
+ vpnLinkConnections: connection.?vpnLinkConnections
+ }
+}]
+
+@description('The name of the VPN gateway.')
+output name string = vpnGateway.name
+
+@description('The resource ID of the VPN gateway.')
+output resourceId string = vpnGateway.id
+
+@description('The name of the resource group the VPN gateway was deployed into.')
+output resourceGroupName string = resourceGroup().name
+
+@description('The location the resource was deployed into.')
+output location string = vpnGateway.location
+
+// =============== //
+// Definitions //
+// =============== //
+
+type lockType = {
+ @description('Optional. Specify the name of lock.')
+ name: string?
+
+ @description('Optional. Specify the type of lock.')
+ kind: ('CanNotDelete' | 'ReadOnly' | 'None')?
+}?
diff --git a/avm/res/network/vpn-gateway/main.json b/avm/res/network/vpn-gateway/main.json
new file mode 100644
index 0000000000..8482c23f93
--- /dev/null
+++ b/avm/res/network/vpn-gateway/main.json
@@ -0,0 +1,614 @@
+{
+ "$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.23.1.45101",
+ "templateHash": "7269875381422514128"
+ },
+ "name": "VPN Gateways",
+ "description": "This module deploys a VPN Gateway.",
+ "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
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the VPN gateway."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location where all resources will be created."
+ }
+ },
+ "vpnConnections": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. The VPN connections to create in the VPN gateway."
+ }
+ },
+ "natRules": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. List of all the NAT Rules to associate with the gateway."
+ }
+ },
+ "virtualHubResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource ID of a virtual Hub to connect to. Note: The virtual Hub and Gateway must be deployed into the same location."
+ }
+ },
+ "bgpSettings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. BGP settings details."
+ }
+ },
+ "enableBgpRouteTranslationForNat": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable BGP routes translation for NAT on this VPN gateway."
+ }
+ },
+ "isRoutingPreferenceInternet": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable routing preference property for the public IP interface of the VPN gateway."
+ }
+ },
+ "vpnGatewayScaleUnit": {
+ "type": "int",
+ "defaultValue": 2,
+ "metadata": {
+ "description": "Optional. The scale unit for this VPN gateway."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)."
+ }
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2023-07-01",
+ "name": "[take(format('46d3xbcp.res.network-vpngateway.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('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"
+ }
+ }
+ }
+ }
+ },
+ "vpnGateway": {
+ "type": "Microsoft.Network/vpnGateways",
+ "apiVersion": "2023-04-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "copy": [
+ {
+ "name": "connections",
+ "count": "[length(parameters('vpnConnections'))]",
+ "input": {
+ "name": "[parameters('vpnConnections')[copyIndex('connections')].name]",
+ "properties": {
+ "connectionBandwidth": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'connectionBandwidth')]",
+ "enableBgp": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'enableBgp')]",
+ "enableInternetSecurity": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'enableInternetSecurity')]",
+ "remoteVpnSite": "[if(contains(parameters('vpnConnections')[copyIndex('connections')], 'remoteVpnSiteResourceId'), createObject('id', parameters('vpnConnections')[copyIndex('connections')].remoteVpnSiteResourceId), null())]",
+ "enableRateLimiting": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'enableRateLimiting')]",
+ "routingConfiguration": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'routingConfiguration')]",
+ "routingWeight": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'routingWeight')]",
+ "sharedKey": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'sharedKey')]",
+ "useLocalAzureIpAddress": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'useLocalAzureIpAddress')]",
+ "usePolicyBasedTrafficSelectors": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'usePolicyBasedTrafficSelectors')]",
+ "vpnConnectionProtocolType": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'vpnConnectionProtocolType')]",
+ "ipsecPolicies": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'ipsecPolicies')]",
+ "trafficSelectorPolicies": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'trafficSelectorPolicies')]",
+ "vpnLinkConnections": "[tryGet(parameters('vpnConnections')[copyIndex('connections')], 'vpnLinkConnections')]"
+ }
+ }
+ }
+ ],
+ "bgpSettings": "[parameters('bgpSettings')]",
+ "enableBgpRouteTranslationForNat": "[parameters('enableBgpRouteTranslationForNat')]",
+ "isRoutingPreferenceInternet": "[parameters('isRoutingPreferenceInternet')]",
+ "vpnGatewayScaleUnit": "[parameters('vpnGatewayScaleUnit')]",
+ "virtualHub": {
+ "id": "[parameters('virtualHubResourceId')]"
+ }
+ }
+ },
+ "vpnGateway_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.Network/vpnGateways/{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": [
+ "vpnGateway"
+ ]
+ },
+ "vpnGateway_natRules": {
+ "copy": {
+ "name": "vpnGateway_natRules",
+ "count": "[length(parameters('natRules'))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-NATRule-{1}', deployment().name, copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[parameters('natRules')[copyIndex()].name]"
+ },
+ "vpnGatewayName": {
+ "value": "[parameters('name')]"
+ },
+ "externalMappings": "[if(contains(parameters('natRules')[copyIndex()], 'externalMappings'), createObject('value', parameters('natRules')[copyIndex()].externalMappings), createObject('value', createArray()))]",
+ "internalMappings": "[if(contains(parameters('natRules')[copyIndex()], 'internalMappings'), createObject('value', parameters('natRules')[copyIndex()].internalMappings), createObject('value', createArray()))]",
+ "ipConfigurationId": "[if(contains(parameters('natRules')[copyIndex()], 'ipConfigurationId'), createObject('value', parameters('natRules')[copyIndex()].ipConfigurationId), createObject('value', ''))]",
+ "mode": "[if(contains(parameters('natRules')[copyIndex()], 'mode'), createObject('value', parameters('natRules')[copyIndex()].mode), createObject('value', ''))]",
+ "type": "[if(contains(parameters('natRules')[copyIndex()], 'type'), createObject('value', parameters('natRules')[copyIndex()].type), createObject('value', ''))]"
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.23.1.45101",
+ "templateHash": "6015512722948676501"
+ },
+ "name": "VPN Gateway NAT Rules",
+ "description": "This module deploys a VPN Gateway NAT Rule.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the NAT rule."
+ }
+ },
+ "vpnGatewayName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent VPN gateway this NAT rule is associated with. Required if the template is used in a standalone deployment."
+ }
+ },
+ "externalMappings": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. An address prefix range of destination IPs on the outside network that source IPs will be mapped to. In other words, your post-NAT address prefix range."
+ }
+ },
+ "internalMappings": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. An address prefix range of source IPs on the inside network that will be mapped to a set of external IPs. In other words, your pre-NAT address prefix range."
+ }
+ },
+ "ipConfigurationId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. A NAT rule must be configured to a specific VPN Gateway instance. This is applicable to Dynamic NAT only. Static NAT rules are automatically applied to both VPN Gateway instances."
+ }
+ },
+ "mode": {
+ "type": "string",
+ "defaultValue": "",
+ "allowedValues": [
+ "",
+ "EgressSnat",
+ "IngressSnat"
+ ],
+ "metadata": {
+ "description": "Optional. The type of NAT rule for VPN NAT. IngressSnat mode (also known as Ingress Source NAT) is applicable to traffic entering the Azure hub's site-to-site VPN gateway. EgressSnat mode (also known as Egress Source NAT) is applicable to traffic leaving the Azure hub's Site-to-site VPN gateway."
+ }
+ },
+ "type": {
+ "type": "string",
+ "defaultValue": "",
+ "allowedValues": [
+ "",
+ "Dynamic",
+ "Static"
+ ],
+ "metadata": {
+ "description": "Optional. The type of NAT rule for VPN NAT. Static one-to-one NAT establishes a one-to-one relationship between an internal address and an external address while Dynamic NAT assigns an IP and port based on availability."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Network/vpnGateways/natRules",
+ "apiVersion": "2023-04-01",
+ "name": "[format('{0}/{1}', parameters('vpnGatewayName'), parameters('name'))]",
+ "properties": {
+ "externalMappings": "[parameters('externalMappings')]",
+ "internalMappings": "[parameters('internalMappings')]",
+ "ipConfigurationId": "[if(not(empty(parameters('ipConfigurationId'))), parameters('ipConfigurationId'), null())]",
+ "mode": "[if(not(empty(parameters('mode'))), parameters('mode'), null())]",
+ "type": "[if(not(empty(parameters('type'))), parameters('type'), null())]"
+ }
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the NAT rule."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the NAT rule."
+ },
+ "value": "[resourceId('Microsoft.Network/vpnGateways/natRules', parameters('vpnGatewayName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the NAT rule was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vpnGateway"
+ ]
+ },
+ "vpnGateway_vpnConnections": {
+ "copy": {
+ "name": "vpnGateway_vpnConnections",
+ "count": "[length(parameters('vpnConnections'))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-Connection-{1}', deployment().name, copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[parameters('vpnConnections')[copyIndex()].name]"
+ },
+ "vpnGatewayName": {
+ "value": "[parameters('name')]"
+ },
+ "connectionBandwidth": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'connectionBandwidth')]"
+ },
+ "enableBgp": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'enableBgp')]"
+ },
+ "enableInternetSecurity": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'enableInternetSecurity')]"
+ },
+ "remoteVpnSiteResourceId": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'remoteVpnSiteResourceId')]"
+ },
+ "enableRateLimiting": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'enableRateLimiting')]"
+ },
+ "routingConfiguration": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'routingConfiguration')]"
+ },
+ "routingWeight": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'routingWeight')]"
+ },
+ "sharedKey": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'sharedKey')]"
+ },
+ "useLocalAzureIpAddress": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'useLocalAzureIpAddress')]"
+ },
+ "usePolicyBasedTrafficSelectors": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'usePolicyBasedTrafficSelectors')]"
+ },
+ "vpnConnectionProtocolType": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'vpnConnectionProtocolType')]"
+ },
+ "trafficSelectorPolicies": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'trafficSelectorPolicies')]"
+ },
+ "vpnLinkConnections": {
+ "value": "[tryGet(parameters('vpnConnections')[copyIndex()], 'vpnLinkConnections')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.23.1.45101",
+ "templateHash": "12494321080277868905"
+ },
+ "name": "VPN Gateway VPN Connections",
+ "description": "This module deploys a VPN Gateway VPN Connection.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the VPN connection."
+ }
+ },
+ "vpnGatewayName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent VPN gateway this VPN connection is associated with. Required if the template is used in a standalone deployment."
+ }
+ },
+ "ipsecPolicies": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. The IPSec policies to be considered by this connection."
+ }
+ },
+ "trafficSelectorPolicies": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. The traffic selector policies to be considered by this connection."
+ }
+ },
+ "vpnLinkConnections": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. List of all VPN site link connections to the gateway."
+ }
+ },
+ "routingConfiguration": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Routing configuration indicating the associated and propagated route tables for this connection."
+ }
+ },
+ "usePolicyBasedTrafficSelectors": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable policy-based traffic selectors."
+ }
+ },
+ "useLocalAzureIpAddress": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Use local Azure IP to initiate connection."
+ }
+ },
+ "enableRateLimiting": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable rate limiting."
+ }
+ },
+ "enableInternetSecurity": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable internet security."
+ }
+ },
+ "enableBgp": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable BGP flag."
+ }
+ },
+ "routingWeight": {
+ "type": "int",
+ "defaultValue": 0,
+ "metadata": {
+ "description": "Optional. Routing weight for VPN connection."
+ }
+ },
+ "connectionBandwidth": {
+ "type": "int",
+ "defaultValue": 10,
+ "metadata": {
+ "description": "Optional. Expected bandwidth in MBPS."
+ }
+ },
+ "vpnConnectionProtocolType": {
+ "type": "string",
+ "defaultValue": "IKEv2",
+ "allowedValues": [
+ "IKEv1",
+ "IKEv2"
+ ],
+ "metadata": {
+ "description": "Optional. Gateway connection protocol."
+ }
+ },
+ "sharedKey": {
+ "type": "securestring",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. SharedKey for the VPN connection."
+ }
+ },
+ "remoteVpnSiteResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Reference to a VPN site to link to."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Network/vpnGateways/vpnConnections",
+ "apiVersion": "2023-04-01",
+ "name": "[format('{0}/{1}', parameters('vpnGatewayName'), parameters('name'))]",
+ "properties": {
+ "connectionBandwidth": "[parameters('connectionBandwidth')]",
+ "enableBgp": "[parameters('enableBgp')]",
+ "enableInternetSecurity": "[parameters('enableInternetSecurity')]",
+ "enableRateLimiting": "[parameters('enableRateLimiting')]",
+ "ipsecPolicies": "[parameters('ipsecPolicies')]",
+ "remoteVpnSite": "[if(not(empty(parameters('remoteVpnSiteResourceId'))), createObject('id', parameters('remoteVpnSiteResourceId')), null())]",
+ "routingConfiguration": "[parameters('routingConfiguration')]",
+ "routingWeight": "[parameters('routingWeight')]",
+ "sharedKey": "[parameters('sharedKey')]",
+ "trafficSelectorPolicies": "[parameters('trafficSelectorPolicies')]",
+ "useLocalAzureIpAddress": "[parameters('useLocalAzureIpAddress')]",
+ "usePolicyBasedTrafficSelectors": "[parameters('usePolicyBasedTrafficSelectors')]",
+ "vpnConnectionProtocolType": "[parameters('vpnConnectionProtocolType')]",
+ "vpnLinkConnections": "[parameters('vpnLinkConnections')]"
+ }
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the VPN connection."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the VPN connection."
+ },
+ "value": "[resourceId('Microsoft.Network/vpnGateways/vpnConnections', parameters('vpnGatewayName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the VPN connection was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "vpnGateway"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the VPN gateway."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the VPN gateway."
+ },
+ "value": "[resourceId('Microsoft.Network/vpnGateways', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the VPN gateway was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('vpnGateway', '2023-04-01', 'full').location]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/network/vpn-gateway/nat-rule/README.md b/avm/res/network/vpn-gateway/nat-rule/README.md
new file mode 100644
index 0000000000..8c38b734ed
--- /dev/null
+++ b/avm/res/network/vpn-gateway/nat-rule/README.md
@@ -0,0 +1,123 @@
+# VPN Gateway NAT Rules `[Microsoft.Network/vpnGateways/natRules]`
+
+This module deploys a VPN Gateway NAT Rule.
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+| `Microsoft.Network/vpnGateways/natRules` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/vpnGateways/natRules) |
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | The name of the NAT rule. |
+
+**Conditional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`vpnGatewayName`](#parameter-vpngatewayname) | string | The name of the parent VPN gateway this NAT rule is associated with. Required if the template is used in a standalone deployment. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`externalMappings`](#parameter-externalmappings) | array | An address prefix range of destination IPs on the outside network that source IPs will be mapped to. In other words, your post-NAT address prefix range. |
+| [`internalMappings`](#parameter-internalmappings) | array | An address prefix range of source IPs on the inside network that will be mapped to a set of external IPs. In other words, your pre-NAT address prefix range. |
+| [`ipConfigurationId`](#parameter-ipconfigurationid) | string | A NAT rule must be configured to a specific VPN Gateway instance. This is applicable to Dynamic NAT only. Static NAT rules are automatically applied to both VPN Gateway instances. |
+| [`mode`](#parameter-mode) | string | The type of NAT rule for VPN NAT. IngressSnat mode (also known as Ingress Source NAT) is applicable to traffic entering the Azure hub's site-to-site VPN gateway. EgressSnat mode (also known as Egress Source NAT) is applicable to traffic leaving the Azure hub's Site-to-site VPN gateway. |
+| [`type`](#parameter-type) | string | The type of NAT rule for VPN NAT. Static one-to-one NAT establishes a one-to-one relationship between an internal address and an external address while Dynamic NAT assigns an IP and port based on availability. |
+
+### Parameter: `name`
+
+The name of the NAT rule.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `vpnGatewayName`
+
+The name of the parent VPN gateway this NAT rule is associated with. Required if the template is used in a standalone deployment.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `externalMappings`
+
+An address prefix range of destination IPs on the outside network that source IPs will be mapped to. In other words, your post-NAT address prefix range.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+### Parameter: `internalMappings`
+
+An address prefix range of source IPs on the inside network that will be mapped to a set of external IPs. In other words, your pre-NAT address prefix range.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+### Parameter: `ipConfigurationId`
+
+A NAT rule must be configured to a specific VPN Gateway instance. This is applicable to Dynamic NAT only. Static NAT rules are automatically applied to both VPN Gateway instances.
+
+- Required: No
+- Type: string
+- Default: `''`
+
+### Parameter: `mode`
+
+The type of NAT rule for VPN NAT. IngressSnat mode (also known as Ingress Source NAT) is applicable to traffic entering the Azure hub's site-to-site VPN gateway. EgressSnat mode (also known as Egress Source NAT) is applicable to traffic leaving the Azure hub's Site-to-site VPN gateway.
+
+- Required: No
+- Type: string
+- Default: `''`
+- Allowed:
+ ```Bicep
+ [
+ ''
+ 'EgressSnat'
+ 'IngressSnat'
+ ]
+ ```
+
+### Parameter: `type`
+
+The type of NAT rule for VPN NAT. Static one-to-one NAT establishes a one-to-one relationship between an internal address and an external address while Dynamic NAT assigns an IP and port based on availability.
+
+- Required: No
+- Type: string
+- Default: `''`
+- Allowed:
+ ```Bicep
+ [
+ ''
+ 'Dynamic'
+ 'Static'
+ ]
+ ```
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `name` | string | The name of the NAT rule. |
+| `resourceGroupName` | string | The name of the resource group the NAT rule was deployed into. |
+| `resourceId` | string | The resource ID of the NAT rule. |
+
+## Cross-referenced modules
+
+_None_
diff --git a/avm/res/network/vpn-gateway/nat-rule/main.bicep b/avm/res/network/vpn-gateway/nat-rule/main.bicep
new file mode 100644
index 0000000000..cf5b272641
--- /dev/null
+++ b/avm/res/network/vpn-gateway/nat-rule/main.bicep
@@ -0,0 +1,59 @@
+metadata name = 'VPN Gateway NAT Rules'
+metadata description = 'This module deploys a VPN Gateway NAT Rule.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Required. The name of the NAT rule.')
+param name string
+
+@description('Conditional. The name of the parent VPN gateway this NAT rule is associated with. Required if the template is used in a standalone deployment.')
+param vpnGatewayName string
+
+@description('Optional. An address prefix range of destination IPs on the outside network that source IPs will be mapped to. In other words, your post-NAT address prefix range.')
+param externalMappings array = []
+
+@description('Optional. An address prefix range of source IPs on the inside network that will be mapped to a set of external IPs. In other words, your pre-NAT address prefix range.')
+param internalMappings array = []
+
+@description('Optional. A NAT rule must be configured to a specific VPN Gateway instance. This is applicable to Dynamic NAT only. Static NAT rules are automatically applied to both VPN Gateway instances.')
+param ipConfigurationId string = ''
+
+@description('Optional. The type of NAT rule for VPN NAT. IngressSnat mode (also known as Ingress Source NAT) is applicable to traffic entering the Azure hub\'s site-to-site VPN gateway. EgressSnat mode (also known as Egress Source NAT) is applicable to traffic leaving the Azure hub\'s Site-to-site VPN gateway.')
+@allowed([
+ ''
+ 'EgressSnat'
+ 'IngressSnat'
+])
+param mode string = ''
+
+@description('Optional. The type of NAT rule for VPN NAT. Static one-to-one NAT establishes a one-to-one relationship between an internal address and an external address while Dynamic NAT assigns an IP and port based on availability.')
+@allowed([
+ ''
+ 'Dynamic'
+ 'Static'
+])
+param type string = ''
+
+resource vpnGateway 'Microsoft.Network/vpnGateways@2023-04-01' existing = {
+ name: vpnGatewayName
+}
+
+resource natRule 'Microsoft.Network/vpnGateways/natRules@2023-04-01' = {
+ name: name
+ parent: vpnGateway
+ properties: {
+ externalMappings: externalMappings
+ internalMappings: internalMappings
+ ipConfigurationId: !empty(ipConfigurationId) ? ipConfigurationId : null
+ mode: !empty(mode) ? any(mode) : null
+ type: !empty(type) ? any(type) : null
+ }
+}
+
+@description('The name of the NAT rule.')
+output name string = natRule.name
+
+@description('The resource ID of the NAT rule.')
+output resourceId string = natRule.id
+
+@description('The name of the resource group the NAT rule was deployed into.')
+output resourceGroupName string = resourceGroup().name
diff --git a/avm/res/network/vpn-gateway/nat-rule/main.json b/avm/res/network/vpn-gateway/nat-rule/main.json
new file mode 100644
index 0000000000..077e369959
--- /dev/null
+++ b/avm/res/network/vpn-gateway/nat-rule/main.json
@@ -0,0 +1,110 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.23.1.45101",
+ "templateHash": "6015512722948676501"
+ },
+ "name": "VPN Gateway NAT Rules",
+ "description": "This module deploys a VPN Gateway NAT Rule.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the NAT rule."
+ }
+ },
+ "vpnGatewayName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent VPN gateway this NAT rule is associated with. Required if the template is used in a standalone deployment."
+ }
+ },
+ "externalMappings": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. An address prefix range of destination IPs on the outside network that source IPs will be mapped to. In other words, your post-NAT address prefix range."
+ }
+ },
+ "internalMappings": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. An address prefix range of source IPs on the inside network that will be mapped to a set of external IPs. In other words, your pre-NAT address prefix range."
+ }
+ },
+ "ipConfigurationId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. A NAT rule must be configured to a specific VPN Gateway instance. This is applicable to Dynamic NAT only. Static NAT rules are automatically applied to both VPN Gateway instances."
+ }
+ },
+ "mode": {
+ "type": "string",
+ "defaultValue": "",
+ "allowedValues": [
+ "",
+ "EgressSnat",
+ "IngressSnat"
+ ],
+ "metadata": {
+ "description": "Optional. The type of NAT rule for VPN NAT. IngressSnat mode (also known as Ingress Source NAT) is applicable to traffic entering the Azure hub's site-to-site VPN gateway. EgressSnat mode (also known as Egress Source NAT) is applicable to traffic leaving the Azure hub's Site-to-site VPN gateway."
+ }
+ },
+ "type": {
+ "type": "string",
+ "defaultValue": "",
+ "allowedValues": [
+ "",
+ "Dynamic",
+ "Static"
+ ],
+ "metadata": {
+ "description": "Optional. The type of NAT rule for VPN NAT. Static one-to-one NAT establishes a one-to-one relationship between an internal address and an external address while Dynamic NAT assigns an IP and port based on availability."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Network/vpnGateways/natRules",
+ "apiVersion": "2023-04-01",
+ "name": "[format('{0}/{1}', parameters('vpnGatewayName'), parameters('name'))]",
+ "properties": {
+ "externalMappings": "[parameters('externalMappings')]",
+ "internalMappings": "[parameters('internalMappings')]",
+ "ipConfigurationId": "[if(not(empty(parameters('ipConfigurationId'))), parameters('ipConfigurationId'), null())]",
+ "mode": "[if(not(empty(parameters('mode'))), parameters('mode'), null())]",
+ "type": "[if(not(empty(parameters('type'))), parameters('type'), null())]"
+ }
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the NAT rule."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the NAT rule."
+ },
+ "value": "[resourceId('Microsoft.Network/vpnGateways/natRules', parameters('vpnGatewayName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the NAT rule was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/network/vpn-gateway/tests/e2e/defaults/dependencies.bicep b/avm/res/network/vpn-gateway/tests/e2e/defaults/dependencies.bicep
new file mode 100644
index 0000000000..3b2439f31c
--- /dev/null
+++ b/avm/res/network/vpn-gateway/tests/e2e/defaults/dependencies.bicep
@@ -0,0 +1,27 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Optional. The name of the Virtual Hub to create.')
+param virtualHubName string
+
+@description('Required. The name of the virtual WAN to create.')
+param virtualWANName string
+
+resource virtualWan 'Microsoft.Network/virtualWans@2023-04-01' = {
+ name: virtualWANName
+ location: location
+}
+
+resource virtualHub 'Microsoft.Network/virtualHubs@2022-01-01' = {
+ name: virtualHubName
+ location: location
+ properties: {
+ virtualWan: {
+ id: virtualWan.id
+ }
+ addressPrefix: '10.1.0.0/16'
+ }
+}
+
+@description('The resource ID of the created Virtual Hub.')
+output virtualHubResourceId string = virtualHub.id
diff --git a/avm/res/network/vpn-gateway/tests/e2e/defaults/main.test.bicep b/avm/res/network/vpn-gateway/tests/e2e/defaults/main.test.bicep
new file mode 100644
index 0000000000..6f4e6a45f1
--- /dev/null
+++ b/avm/res/network/vpn-gateway/tests/e2e/defaults/main.test.bicep
@@ -0,0 +1,59 @@
+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}-network.vpngateways-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'vpngmin'
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ location:location
+ virtualHubName: 'dep-${namePrefix}-vh-${serviceShort}'
+ virtualWANName: 'dep-${namePrefix}-vw-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: location
+ name: '${namePrefix}${serviceShort}001'
+ virtualHubResourceId: nestedDependencies.outputs.virtualHubResourceId
+ }
+}]
+
+
diff --git a/avm/res/network/vpn-gateway/tests/e2e/max/dependencies.bicep b/avm/res/network/vpn-gateway/tests/e2e/max/dependencies.bicep
new file mode 100644
index 0000000000..a15b268388
--- /dev/null
+++ b/avm/res/network/vpn-gateway/tests/e2e/max/dependencies.bicep
@@ -0,0 +1,49 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Optional. The name of the Virtual Hub to create.')
+param virtualHubName string
+
+@description('Optional. The name of the VPN Site to create.')
+param vpnSiteName string
+
+@description('Required. The name of the virtual WAN to create.')
+param virtualWANName string
+
+resource virtualWan 'Microsoft.Network/virtualWans@2023-04-01' = {
+ name: virtualWANName
+ location: location
+}
+
+resource virtualHub 'Microsoft.Network/virtualHubs@2022-01-01' = {
+ name: virtualHubName
+ location: location
+ properties: {
+ virtualWan: {
+ id: virtualWan.id
+ }
+ addressPrefix: '10.0.0.0/24'
+ }
+}
+
+resource vpnSite 'Microsoft.Network/vpnSites@2023-04-01' = {
+ name: vpnSiteName
+ location: location
+ properties: {
+ virtualWan: {
+ id: virtualWan.id
+ }
+ addressSpace: {
+ addressPrefixes: [
+ '10.1.0.0/16'
+ ]
+ }
+ ipAddress: '10.1.0.0'
+ }
+}
+
+@description('The resource ID of the created Virtual Hub.')
+output virtualHubResourceId string = virtualHub.id
+
+@description('The resource ID of the created VPN site.')
+output vpnSiteResourceId string = vpnSite.id
diff --git a/avm/res/network/vpn-gateway/tests/e2e/max/main.test.bicep b/avm/res/network/vpn-gateway/tests/e2e/max/main.test.bicep
new file mode 100644
index 0000000000..1b146064ef
--- /dev/null
+++ b/avm/res/network/vpn-gateway/tests/e2e/max/main.test.bicep
@@ -0,0 +1,103 @@
+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}-network.vpngateways-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'vpngmax'
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ location:location
+ virtualHubName: 'dep-${namePrefix}-vh-${serviceShort}'
+ virtualWANName: 'dep-${namePrefix}-vw-${serviceShort}'
+ vpnSiteName: 'dep-${namePrefix}-vs-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: location
+ name: '${namePrefix}${serviceShort}001'
+ virtualHubResourceId: nestedDependencies.outputs.virtualHubResourceId
+ bgpSettings: {
+ asn: 65515
+ peerWeight: 0
+ }
+ vpnConnections: [
+ {
+ connectionBandwidth: 100
+ enableBgp: false
+ name: 'Connection-${last(split(nestedDependencies.outputs.vpnSiteResourceId, '/'))}'
+ remoteVpnSiteResourceId: nestedDependencies.outputs.vpnSiteResourceId
+ enableInternetSecurity: true
+ vpnConnectionProtocolType: 'IKEv2'
+ enableRateLimiting: false
+ useLocalAzureIpAddress: false
+ usePolicyBasedTrafficSelectors: false
+ routingWeight: 0
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ natRules: [
+ {
+ externalMappings: [
+ {
+ addressSpace: '192.168.21.0/24'
+ }
+ ]
+ internalMappings: [
+ {
+ addressSpace: '10.4.0.0/24'
+ }
+ ]
+ mode: 'EgressSnat'
+ name: 'natRule1'
+ type: 'Static'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}]
+
+
diff --git a/avm/res/network/vpn-gateway/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/network/vpn-gateway/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a15b268388
--- /dev/null
+++ b/avm/res/network/vpn-gateway/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,49 @@
+@description('Optional. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Optional. The name of the Virtual Hub to create.')
+param virtualHubName string
+
+@description('Optional. The name of the VPN Site to create.')
+param vpnSiteName string
+
+@description('Required. The name of the virtual WAN to create.')
+param virtualWANName string
+
+resource virtualWan 'Microsoft.Network/virtualWans@2023-04-01' = {
+ name: virtualWANName
+ location: location
+}
+
+resource virtualHub 'Microsoft.Network/virtualHubs@2022-01-01' = {
+ name: virtualHubName
+ location: location
+ properties: {
+ virtualWan: {
+ id: virtualWan.id
+ }
+ addressPrefix: '10.0.0.0/24'
+ }
+}
+
+resource vpnSite 'Microsoft.Network/vpnSites@2023-04-01' = {
+ name: vpnSiteName
+ location: location
+ properties: {
+ virtualWan: {
+ id: virtualWan.id
+ }
+ addressSpace: {
+ addressPrefixes: [
+ '10.1.0.0/16'
+ ]
+ }
+ ipAddress: '10.1.0.0'
+ }
+}
+
+@description('The resource ID of the created Virtual Hub.')
+output virtualHubResourceId string = virtualHub.id
+
+@description('The resource ID of the created VPN site.')
+output vpnSiteResourceId string = vpnSite.id
diff --git a/avm/res/network/vpn-gateway/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/vpn-gateway/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..0353359af5
--- /dev/null
+++ b/avm/res/network/vpn-gateway/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,103 @@
+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}-network.vpngateways-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'vpngwaf'
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ location:location
+ virtualHubName: 'dep-${namePrefix}-vh-${serviceShort}'
+ virtualWANName: 'dep-${namePrefix}-vw-${serviceShort}'
+ vpnSiteName: 'dep-${namePrefix}-vs-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ location: location
+ name: '${namePrefix}${serviceShort}001'
+ virtualHubResourceId: nestedDependencies.outputs.virtualHubResourceId
+ bgpSettings: {
+ asn: 65515
+ peerWeight: 0
+ }
+ vpnConnections: [
+ {
+ connectionBandwidth: 100
+ enableBgp: false
+ name: 'Connection-${last(split(nestedDependencies.outputs.vpnSiteResourceId, '/'))}'
+ remoteVpnSiteResourceId: nestedDependencies.outputs.vpnSiteResourceId
+ enableInternetSecurity: true
+ vpnConnectionProtocolType: 'IKEv2'
+ enableRateLimiting: false
+ useLocalAzureIpAddress: false
+ usePolicyBasedTrafficSelectors: false
+ routingWeight: 0
+ }
+ ]
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ natRules: [
+ {
+ externalMappings: [
+ {
+ addressSpace: '192.168.21.0/24'
+ }
+ ]
+ internalMappings: [
+ {
+ addressSpace: '10.4.0.0/24'
+ }
+ ]
+ mode: 'EgressSnat'
+ name: 'natRule1'
+ type: 'Static'
+ }
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}]
+
+
diff --git a/avm/res/network/vpn-gateway/version.json b/avm/res/network/vpn-gateway/version.json
new file mode 100644
index 0000000000..8def869ede
--- /dev/null
+++ b/avm/res/network/vpn-gateway/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
+ "version": "0.1",
+ "pathFilters": [
+ "./main.json"
+ ]
+}
diff --git a/avm/res/network/vpn-gateway/vpn-connection/README.md b/avm/res/network/vpn-gateway/vpn-connection/README.md
new file mode 100644
index 0000000000..520447f305
--- /dev/null
+++ b/avm/res/network/vpn-gateway/vpn-connection/README.md
@@ -0,0 +1,255 @@
+# VPN Gateway VPN Connections `[Microsoft.Network/vpnGateways/vpnConnections]`
+
+This module deploys a VPN Gateway VPN Connection.
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+- [Notes](#Notes)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+| `Microsoft.Network/vpnGateways/vpnConnections` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/vpnGateways/vpnConnections) |
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | The name of the VPN connection. |
+
+**Conditional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`vpnGatewayName`](#parameter-vpngatewayname) | string | The name of the parent VPN gateway this VPN connection is associated with. Required if the template is used in a standalone deployment. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`connectionBandwidth`](#parameter-connectionbandwidth) | int | Expected bandwidth in MBPS. |
+| [`enableBgp`](#parameter-enablebgp) | bool | Enable BGP flag. |
+| [`enableInternetSecurity`](#parameter-enableinternetsecurity) | bool | Enable internet security. |
+| [`enableRateLimiting`](#parameter-enableratelimiting) | bool | Enable rate limiting. |
+| [`ipsecPolicies`](#parameter-ipsecpolicies) | array | The IPSec policies to be considered by this connection. |
+| [`remoteVpnSiteResourceId`](#parameter-remotevpnsiteresourceid) | string | Reference to a VPN site to link to. |
+| [`routingConfiguration`](#parameter-routingconfiguration) | object | Routing configuration indicating the associated and propagated route tables for this connection. |
+| [`routingWeight`](#parameter-routingweight) | int | Routing weight for VPN connection. |
+| [`sharedKey`](#parameter-sharedkey) | securestring | SharedKey for the VPN connection. |
+| [`trafficSelectorPolicies`](#parameter-trafficselectorpolicies) | array | The traffic selector policies to be considered by this connection. |
+| [`useLocalAzureIpAddress`](#parameter-uselocalazureipaddress) | bool | Use local Azure IP to initiate connection. |
+| [`usePolicyBasedTrafficSelectors`](#parameter-usepolicybasedtrafficselectors) | bool | Enable policy-based traffic selectors. |
+| [`vpnConnectionProtocolType`](#parameter-vpnconnectionprotocoltype) | string | Gateway connection protocol. |
+| [`vpnLinkConnections`](#parameter-vpnlinkconnections) | array | List of all VPN site link connections to the gateway. |
+
+### Parameter: `name`
+
+The name of the VPN connection.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `vpnGatewayName`
+
+The name of the parent VPN gateway this VPN connection is associated with. Required if the template is used in a standalone deployment.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `connectionBandwidth`
+
+Expected bandwidth in MBPS.
+
+- Required: No
+- Type: int
+- Default: `10`
+
+### Parameter: `enableBgp`
+
+Enable BGP flag.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `enableInternetSecurity`
+
+Enable internet security.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `enableRateLimiting`
+
+Enable rate limiting.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `ipsecPolicies`
+
+The IPSec policies to be considered by this connection.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+### Parameter: `remoteVpnSiteResourceId`
+
+Reference to a VPN site to link to.
+
+- Required: No
+- Type: string
+- Default: `''`
+
+### Parameter: `routingConfiguration`
+
+Routing configuration indicating the associated and propagated route tables for this connection.
+
+- Required: No
+- Type: object
+- Default: `{}`
+
+### Parameter: `routingWeight`
+
+Routing weight for VPN connection.
+
+- Required: No
+- Type: int
+- Default: `0`
+
+### Parameter: `sharedKey`
+
+SharedKey for the VPN connection.
+
+- Required: No
+- Type: securestring
+- Default: `''`
+
+### Parameter: `trafficSelectorPolicies`
+
+The traffic selector policies to be considered by this connection.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+### Parameter: `useLocalAzureIpAddress`
+
+Use local Azure IP to initiate connection.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `usePolicyBasedTrafficSelectors`
+
+Enable policy-based traffic selectors.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `vpnConnectionProtocolType`
+
+Gateway connection protocol.
+
+- Required: No
+- Type: string
+- Default: `'IKEv2'`
+- Allowed:
+ ```Bicep
+ [
+ 'IKEv1'
+ 'IKEv2'
+ ]
+ ```
+
+### Parameter: `vpnLinkConnections`
+
+List of all VPN site link connections to the gateway.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `name` | string | The name of the VPN connection. |
+| `resourceGroupName` | string | The name of the resource group the VPN connection was deployed into. |
+| `resourceId` | string | The resource ID of the VPN connection. |
+
+## Cross-referenced modules
+
+_None_
+
+## Notes
+
+### Parameter Usage: `routingConfiguration`
+
+
diff --git a/avm/res/network/vpn-gateway/vpn-connection/main.bicep b/avm/res/network/vpn-gateway/vpn-connection/main.bicep
new file mode 100644
index 0000000000..f189154ce4
--- /dev/null
+++ b/avm/res/network/vpn-gateway/vpn-connection/main.bicep
@@ -0,0 +1,92 @@
+metadata name = 'VPN Gateway VPN Connections'
+metadata description = 'This module deploys a VPN Gateway VPN Connection.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Required. The name of the VPN connection.')
+param name string
+
+@description('Conditional. The name of the parent VPN gateway this VPN connection is associated with. Required if the template is used in a standalone deployment.')
+param vpnGatewayName string
+
+@description('Optional. The IPSec policies to be considered by this connection.')
+param ipsecPolicies array = []
+
+@description('Optional. The traffic selector policies to be considered by this connection.')
+param trafficSelectorPolicies array = []
+
+@description('Optional. List of all VPN site link connections to the gateway.')
+param vpnLinkConnections array = []
+
+@description('Optional. Routing configuration indicating the associated and propagated route tables for this connection.')
+param routingConfiguration object = {}
+
+@description('Optional. Enable policy-based traffic selectors.')
+param usePolicyBasedTrafficSelectors bool = false
+
+@description('Optional. Use local Azure IP to initiate connection.')
+param useLocalAzureIpAddress bool = false
+
+@description('Optional. Enable rate limiting.')
+param enableRateLimiting bool = false
+
+@description('Optional. Enable internet security.')
+param enableInternetSecurity bool = false
+
+@description('Optional. Enable BGP flag.')
+param enableBgp bool = false
+
+@description('Optional. Routing weight for VPN connection.')
+param routingWeight int = 0
+
+@description('Optional. Expected bandwidth in MBPS.')
+param connectionBandwidth int = 10
+
+@description('Optional. Gateway connection protocol.')
+@allowed([
+ 'IKEv1'
+ 'IKEv2'
+])
+param vpnConnectionProtocolType string = 'IKEv2'
+
+@description('Optional. SharedKey for the VPN connection.')
+@secure()
+param sharedKey string = ''
+
+@description('Optional. Reference to a VPN site to link to.')
+param remoteVpnSiteResourceId string = ''
+
+resource vpnGateway 'Microsoft.Network/vpnGateways@2023-04-01' existing = {
+ name: vpnGatewayName
+}
+
+resource vpnConnection 'Microsoft.Network/vpnGateways/vpnConnections@2023-04-01' = {
+ name: name
+ parent: vpnGateway
+ properties: {
+ connectionBandwidth: connectionBandwidth
+ enableBgp: enableBgp
+ enableInternetSecurity: enableInternetSecurity
+ enableRateLimiting: enableRateLimiting
+ ipsecPolicies: ipsecPolicies
+ remoteVpnSite: !empty(remoteVpnSiteResourceId) ? {
+ id: remoteVpnSiteResourceId
+ } : null
+ routingConfiguration: routingConfiguration
+ routingWeight: routingWeight
+ sharedKey: sharedKey
+ trafficSelectorPolicies: trafficSelectorPolicies
+ useLocalAzureIpAddress: useLocalAzureIpAddress
+ usePolicyBasedTrafficSelectors: usePolicyBasedTrafficSelectors
+ vpnConnectionProtocolType: vpnConnectionProtocolType
+ vpnLinkConnections: vpnLinkConnections
+ }
+}
+
+@description('The name of the VPN connection.')
+output name string = vpnConnection.name
+
+@description('The resource ID of the VPN connection.')
+output resourceId string = vpnConnection.id
+
+@description('The name of the resource group the VPN connection was deployed into.')
+output resourceGroupName string = resourceGroup().name
diff --git a/avm/res/network/vpn-gateway/vpn-connection/main.json b/avm/res/network/vpn-gateway/vpn-connection/main.json
new file mode 100644
index 0000000000..c47ab3e50d
--- /dev/null
+++ b/avm/res/network/vpn-gateway/vpn-connection/main.json
@@ -0,0 +1,176 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.23.1.45101",
+ "templateHash": "12494321080277868905"
+ },
+ "name": "VPN Gateway VPN Connections",
+ "description": "This module deploys a VPN Gateway VPN Connection.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the VPN connection."
+ }
+ },
+ "vpnGatewayName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent VPN gateway this VPN connection is associated with. Required if the template is used in a standalone deployment."
+ }
+ },
+ "ipsecPolicies": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. The IPSec policies to be considered by this connection."
+ }
+ },
+ "trafficSelectorPolicies": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. The traffic selector policies to be considered by this connection."
+ }
+ },
+ "vpnLinkConnections": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. List of all VPN site link connections to the gateway."
+ }
+ },
+ "routingConfiguration": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Routing configuration indicating the associated and propagated route tables for this connection."
+ }
+ },
+ "usePolicyBasedTrafficSelectors": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable policy-based traffic selectors."
+ }
+ },
+ "useLocalAzureIpAddress": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Use local Azure IP to initiate connection."
+ }
+ },
+ "enableRateLimiting": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable rate limiting."
+ }
+ },
+ "enableInternetSecurity": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable internet security."
+ }
+ },
+ "enableBgp": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Enable BGP flag."
+ }
+ },
+ "routingWeight": {
+ "type": "int",
+ "defaultValue": 0,
+ "metadata": {
+ "description": "Optional. Routing weight for VPN connection."
+ }
+ },
+ "connectionBandwidth": {
+ "type": "int",
+ "defaultValue": 10,
+ "metadata": {
+ "description": "Optional. Expected bandwidth in MBPS."
+ }
+ },
+ "vpnConnectionProtocolType": {
+ "type": "string",
+ "defaultValue": "IKEv2",
+ "allowedValues": [
+ "IKEv1",
+ "IKEv2"
+ ],
+ "metadata": {
+ "description": "Optional. Gateway connection protocol."
+ }
+ },
+ "sharedKey": {
+ "type": "securestring",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. SharedKey for the VPN connection."
+ }
+ },
+ "remoteVpnSiteResourceId": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. Reference to a VPN site to link to."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Network/vpnGateways/vpnConnections",
+ "apiVersion": "2023-04-01",
+ "name": "[format('{0}/{1}', parameters('vpnGatewayName'), parameters('name'))]",
+ "properties": {
+ "connectionBandwidth": "[parameters('connectionBandwidth')]",
+ "enableBgp": "[parameters('enableBgp')]",
+ "enableInternetSecurity": "[parameters('enableInternetSecurity')]",
+ "enableRateLimiting": "[parameters('enableRateLimiting')]",
+ "ipsecPolicies": "[parameters('ipsecPolicies')]",
+ "remoteVpnSite": "[if(not(empty(parameters('remoteVpnSiteResourceId'))), createObject('id', parameters('remoteVpnSiteResourceId')), null())]",
+ "routingConfiguration": "[parameters('routingConfiguration')]",
+ "routingWeight": "[parameters('routingWeight')]",
+ "sharedKey": "[parameters('sharedKey')]",
+ "trafficSelectorPolicies": "[parameters('trafficSelectorPolicies')]",
+ "useLocalAzureIpAddress": "[parameters('useLocalAzureIpAddress')]",
+ "usePolicyBasedTrafficSelectors": "[parameters('usePolicyBasedTrafficSelectors')]",
+ "vpnConnectionProtocolType": "[parameters('vpnConnectionProtocolType')]",
+ "vpnLinkConnections": "[parameters('vpnLinkConnections')]"
+ }
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the VPN connection."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the VPN connection."
+ },
+ "value": "[resourceId('Microsoft.Network/vpnGateways/vpnConnections', parameters('vpnGatewayName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the VPN connection was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+}
\ No newline at end of file
From 2d404d1743287c1cd34ac7207d308d69ca4fe69b Mon Sep 17 00:00:00 2001
From: Alexander Sehr
+
+
+
+### Example 2: _Using large parameter set_
+
+This instance deploys the module with most of its features enabled.
+
+
+
+
+
+
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | Name of the static site. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`allowConfigFileUpdates`](#parameter-allowconfigfileupdates) | bool | False if config file is locked for this static web app; otherwise, true. |
+| [`appSettings`](#parameter-appsettings) | object | Static site app settings. |
+| [`branch`](#parameter-branch) | string | The branch name of the GitHub repository. |
+| [`buildProperties`](#parameter-buildproperties) | object | Build properties for the static site. |
+| [`customDomains`](#parameter-customdomains) | array | The custom domains associated with this static site. The deployment will fail as long as the validation records are not present. |
+| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable telemetry via a Globally Unique Identifier (GUID). |
+| [`enterpriseGradeCdnStatus`](#parameter-enterprisegradecdnstatus) | string | State indicating the status of the enterprise grade CDN serving traffic to the static web app. |
+| [`functionAppSettings`](#parameter-functionappsettings) | object | Function app settings. |
+| [`linkedBackend`](#parameter-linkedbackend) | object | Object with "resourceId" and "location" of the a user defined function app. |
+| [`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. |
+| [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'sku' to be 'Standard'. |
+| [`provider`](#parameter-provider) | string | The provider that submitted the last deployment to the primary environment of the static site. |
+| [`repositoryToken`](#parameter-repositorytoken) | securestring | The Personal Access Token for accessing the GitHub repository. |
+| [`repositoryUrl`](#parameter-repositoryurl) | string | The name of the GitHub repository. |
+| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. |
+| [`sku`](#parameter-sku) | string | Type of static site to deploy. |
+| [`stagingEnvironmentPolicy`](#parameter-stagingenvironmentpolicy) | string | State indicating whether staging environments are allowed or not allowed for a static web app. |
+| [`tags`](#parameter-tags) | object | Tags of the resource. |
+| [`templateProperties`](#parameter-templateproperties) | object | Template Options for the static site. |
+
+### Parameter: `name`
+
+Name of the static site.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `allowConfigFileUpdates`
+
+False if config file is locked for this static web app; otherwise, true.
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `appSettings`
+
+Static site app settings.
+
+- Required: No
+- Type: object
+- Default: `{}`
+
+### Parameter: `branch`
+
+The branch name of the GitHub repository.
+
+- Required: No
+- Type: string
+
+### Parameter: `buildProperties`
+
+Build properties for the static site.
+
+- Required: No
+- Type: object
+
+### Parameter: `customDomains`
+
+The custom domains associated with this static site. The deployment will fail as long as the validation records are not present.
+
+- Required: No
+- Type: array
+- Default: `[]`
+
+### Parameter: `enableTelemetry`
+
+Enable telemetry via a Globally Unique Identifier (GUID).
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `enterpriseGradeCdnStatus`
+
+State indicating the status of the enterprise grade CDN serving traffic to the static web app.
+
+- Required: No
+- Type: string
+- Default: `'Disabled'`
+- Allowed:
+ ```Bicep
+ [
+ 'Disabled'
+ 'Disabling'
+ 'Enabled'
+ 'Enabling'
+ ]
+ ```
+
+### Parameter: `functionAppSettings`
+
+Function app settings.
+
+- Required: No
+- Type: object
+- Default: `{}`
+
+### Parameter: `linkedBackend`
+
+Object with "resourceId" and "location" of the a user defined function app.
+
+- Required: No
+- Type: object
+- Default: `{}`
+
+### 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: `privateEndpoints`
+
+Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'sku' to be 'Standard'.
+
+- Required: No
+- Type: array
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`subnetResourceId`](#parameter-privateendpointssubnetresourceid) | string | Resource ID of the subnet where the endpoint needs to be created. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`applicationSecurityGroupResourceIds`](#parameter-privateendpointsapplicationsecuritygroupresourceids) | array | Application security groups in which the private endpoint IP configuration is included. |
+| [`customDnsConfigs`](#parameter-privateendpointscustomdnsconfigs) | array | Custom DNS configurations. |
+| [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. |
+| [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. |
+| [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. |
+| [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. |
+| [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. |
+| [`manualPrivateLinkServiceConnections`](#parameter-privateendpointsmanualprivatelinkserviceconnections) | array | Manual PrivateLink Service Connections. |
+| [`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. |
+| [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. |
+| [`service`](#parameter-privateendpointsservice) | string | The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". |
+| [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. |
+
+### Parameter: `privateEndpoints.subnetResourceId`
+
+Resource ID of the subnet where the endpoint needs to be created.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `privateEndpoints.applicationSecurityGroupResourceIds`
+
+Application security groups in which the private endpoint IP configuration is included.
+
+- Required: No
+- Type: array
+
+### Parameter: `privateEndpoints.customDnsConfigs`
+
+Custom DNS configurations.
+
+- Required: No
+- Type: array
+
+### Parameter: `privateEndpoints.customNetworkInterfaceName`
+
+The custom name of the network interface attached to the private endpoint.
+
+- Required: No
+- Type: string
+
+### Parameter: `privateEndpoints.enableTelemetry`
+
+Enable/Disable usage telemetry for module.
+
+- Required: No
+- Type: bool
+
+### Parameter: `privateEndpoints.ipConfigurations`
+
+A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints.
+
+- Required: No
+- Type: array
+
+### Parameter: `privateEndpoints.location`
+
+The location to deploy the private endpoint to.
+
+- Required: No
+- Type: string
+
+### Parameter: `privateEndpoints.lock`
+
+Specify the type of lock.
+
+- Required: No
+- Type: object
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`kind`](#parameter-privateendpointslockkind) | string | Specify the type of lock. |
+| [`name`](#parameter-privateendpointslockname) | string | Specify the name of lock. |
+
+### Parameter: `privateEndpoints.lock.kind`
+
+Specify the type of lock.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'CanNotDelete'
+ 'None'
+ 'ReadOnly'
+ ]
+ ```
+
+### Parameter: `privateEndpoints.lock.name`
+
+Specify the name of lock.
+
+- Required: No
+- Type: string
+
+### Parameter: `privateEndpoints.manualPrivateLinkServiceConnections`
+
+Manual PrivateLink Service Connections.
+
+- Required: No
+- Type: array
+
+### Parameter: `privateEndpoints.name`
+
+The name of the private endpoint.
+
+- Required: No
+- Type: string
+
+### Parameter: `privateEndpoints.privateDnsZoneGroupName`
+
+The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided.
+
+- Required: No
+- Type: string
+
+### Parameter: `privateEndpoints.privateDnsZoneResourceIds`
+
+The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.
+
+- Required: No
+- Type: array
+
+### Parameter: `privateEndpoints.roleAssignments`
+
+Array of role assignments to create.
+
+- Required: No
+- Type: array
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`principalId`](#parameter-privateendpointsroleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. |
+| [`roleDefinitionIdOrName`](#parameter-privateendpointsroleassignmentsroledefinitionidorname) | 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-privateendpointsroleassignmentscondition) | 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-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. |
+| [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. |
+| [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. |
+| [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. |
+
+### Parameter: `privateEndpoints.roleAssignments.principalId`
+
+The principal ID of the principal (user/group/identity) to assign the role to.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `privateEndpoints.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: `privateEndpoints.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: `privateEndpoints.roleAssignments.conditionVersion`
+
+Version of the condition.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ '2.0'
+ ]
+ ```
+
+### Parameter: `privateEndpoints.roleAssignments.delegatedManagedIdentityResourceId`
+
+The Resource Id of the delegated managed identity resource.
+
+- Required: No
+- Type: string
+
+### Parameter: `privateEndpoints.roleAssignments.description`
+
+The description of the role assignment.
+
+- Required: No
+- Type: string
+
+### Parameter: `privateEndpoints.roleAssignments.principalType`
+
+The principal type of the assigned principal ID.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'Device'
+ 'ForeignGroup'
+ 'Group'
+ 'ServicePrincipal'
+ 'User'
+ ]
+ ```
+
+### Parameter: `privateEndpoints.service`
+
+The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob".
+
+- Required: No
+- Type: string
+
+### Parameter: `privateEndpoints.tags`
+
+Tags to be applied on all resources/resource groups in this deployment.
+
+- Required: No
+- Type: object
+
+### Parameter: `provider`
+
+The provider that submitted the last deployment to the primary environment of the static site.
+
+- Required: No
+- Type: string
+- Default: `'None'`
+
+### Parameter: `repositoryToken`
+
+The Personal Access Token for accessing the GitHub repository.
+
+- Required: No
+- Type: securestring
+
+### Parameter: `repositoryUrl`
+
+The name of the GitHub repository.
+
+- 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: `sku`
+
+Type of static site to deploy.
+
+- Required: No
+- Type: string
+- Default: `'Free'`
+- Allowed:
+ ```Bicep
+ [
+ 'Free'
+ 'Standard'
+ ]
+ ```
+
+### Parameter: `stagingEnvironmentPolicy`
+
+State indicating whether staging environments are allowed or not allowed for a static web app.
+
+- Required: No
+- Type: string
+- Default: `'Enabled'`
+- Allowed:
+ ```Bicep
+ [
+ 'Disabled'
+ 'Enabled'
+ ]
+ ```
+
+### Parameter: `tags`
+
+Tags of the resource.
+
+- Required: No
+- Type: object
+
+### Parameter: `templateProperties`
+
+Template Options for the static site.
+
+- Required: No
+- Type: object
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `defaultHostname` | string | The default autogenerated hostname for the static site. |
+| `location` | string | The location the resource was deployed into. |
+| `name` | string | The name of the static site. |
+| `resourceGroupName` | string | The resource group the static site was deployed into. |
+| `resourceId` | string | The resource ID of the static site. |
+| `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. |
+
+## Cross-referenced modules
+
+This section gives you an overview of all local-referenced module files (i.e., other CARML modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs).
+
+| Reference | Type |
+| :-- | :-- |
+| `br/public:avm/res/network/private-endpoint:0.3.1` | Remote reference |
diff --git a/avm/res/web/static-site/config/README.md b/avm/res/web/static-site/config/README.md
new file mode 100644
index 0000000000..cd8319d99d
--- /dev/null
+++ b/avm/res/web/static-site/config/README.md
@@ -0,0 +1,72 @@
+# Static Web App Site Config `[Microsoft.Web/staticSites/config]`
+
+This module deploys a Static Web App Site Config.
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+| `Microsoft.Web/staticSites/config` | [2022-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/staticSites/config) |
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`kind`](#parameter-kind) | string | Type of settings to apply. |
+| [`properties`](#parameter-properties) | object | App settings. |
+
+**Conditional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`staticSiteName`](#parameter-staticsitename) | string | The name of the parent Static Web App. Required if the template is used in a standalone deployment. |
+
+### Parameter: `kind`
+
+Type of settings to apply.
+
+- Required: Yes
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'appsettings'
+ 'functionappsettings'
+ ]
+ ```
+
+### Parameter: `properties`
+
+App settings.
+
+- Required: Yes
+- Type: object
+
+### Parameter: `staticSiteName`
+
+The name of the parent Static Web App. Required if the template is used in a standalone deployment.
+
+- Required: Yes
+- Type: string
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `name` | string | The name of the config. |
+| `resourceGroupName` | string | The name of the resource group the config was created in. |
+| `resourceId` | string | The resource ID of the config. |
+
+## Cross-referenced modules
+
+_None_
diff --git a/avm/res/web/static-site/config/main.bicep b/avm/res/web/static-site/config/main.bicep
new file mode 100644
index 0000000000..d2b1a3267c
--- /dev/null
+++ b/avm/res/web/static-site/config/main.bicep
@@ -0,0 +1,36 @@
+metadata name = 'Static Web App Site Config'
+metadata description = 'This module deploys a Static Web App Site Config.'
+metadata owner = 'Azure/module-maintainers'
+
+@allowed([
+ 'appsettings'
+ 'functionappsettings'
+])
+@description('Required. Type of settings to apply.')
+param kind string
+
+@description('Required. App settings.')
+param properties object
+
+@description('Conditional. The name of the parent Static Web App. Required if the template is used in a standalone deployment.')
+param staticSiteName string
+
+resource staticSite 'Microsoft.Web/staticSites@2022-03-01' existing = {
+ name: staticSiteName
+}
+
+resource config 'Microsoft.Web/staticSites/config@2022-03-01' = {
+ #disable-next-line BCP225 // Disables incorrect error that `name` cannot be determined at compile time.
+ name: kind
+ parent: staticSite
+ properties: properties
+}
+
+@description('The name of the config.')
+output name string = config.name
+
+@description('The resource ID of the config.')
+output resourceId string = config.id
+
+@description('The name of the resource group the config was created in.')
+output resourceGroupName string = resourceGroup().name
diff --git a/avm/res/web/static-site/config/main.json b/avm/res/web/static-site/config/main.json
new file mode 100644
index 0000000000..94cece2fe0
--- /dev/null
+++ b/avm/res/web/static-site/config/main.json
@@ -0,0 +1,69 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.24.24.22086",
+ "templateHash": "12360558347482884602"
+ },
+ "name": "Static Web App Site Config",
+ "description": "This module deploys a Static Web App Site Config.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "appsettings",
+ "functionappsettings"
+ ],
+ "metadata": {
+ "description": "Required. Type of settings to apply."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. App settings."
+ }
+ },
+ "staticSiteName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Static Web App. Required if the template is used in a standalone deployment."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Web/staticSites/config",
+ "apiVersion": "2022-03-01",
+ "name": "[format('{0}/{1}', parameters('staticSiteName'), parameters('kind'))]",
+ "properties": "[parameters('properties')]"
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the config."
+ },
+ "value": "[parameters('kind')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the config."
+ },
+ "value": "[resourceId('Microsoft.Web/staticSites/config', parameters('staticSiteName'), parameters('kind'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the config was created in."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/web/static-site/custom-domain/README.md b/avm/res/web/static-site/custom-domain/README.md
new file mode 100644
index 0000000000..1c5893aac6
--- /dev/null
+++ b/avm/res/web/static-site/custom-domain/README.md
@@ -0,0 +1,71 @@
+# Static Web App Site Custom Domains `[Microsoft.Web/staticSites/customDomains]`
+
+This module deploys a Static Web App Site Custom Domain.
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+| `Microsoft.Web/staticSites/customDomains` | [2022-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/2022-03-01/staticSites/customDomains) |
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | The custom domain name. |
+
+**Conditional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`staticSiteName`](#parameter-staticsitename) | string | The name of the parent Static Web App. Required if the template is used in a standalone deployment. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`validationMethod`](#parameter-validationmethod) | string | Validation method for adding a custom domain. |
+
+### Parameter: `name`
+
+The custom domain name.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `staticSiteName`
+
+The name of the parent Static Web App. Required if the template is used in a standalone deployment.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `validationMethod`
+
+Validation method for adding a custom domain.
+
+- Required: No
+- Type: string
+- Default: `'cname-delegation'`
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `name` | string | The name of the static site custom domain. |
+| `resourceGroupName` | string | The resource group the static site custom domain was deployed into. |
+| `resourceId` | string | The resource ID of the static site custom domain. |
+
+## Cross-referenced modules
+
+_None_
diff --git a/avm/res/web/static-site/custom-domain/main.bicep b/avm/res/web/static-site/custom-domain/main.bicep
new file mode 100644
index 0000000000..fb7c51def1
--- /dev/null
+++ b/avm/res/web/static-site/custom-domain/main.bicep
@@ -0,0 +1,33 @@
+metadata name = 'Static Web App Site Custom Domains'
+metadata description = 'This module deploys a Static Web App Site Custom Domain.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Required. The custom domain name.')
+param name string
+
+@description('Conditional. The name of the parent Static Web App. Required if the template is used in a standalone deployment.')
+param staticSiteName string
+
+@description('Optional. Validation method for adding a custom domain.')
+param validationMethod string = 'cname-delegation'
+
+resource staticSite 'Microsoft.Web/staticSites@2022-03-01' existing = {
+ name: staticSiteName
+}
+
+resource customDomain 'Microsoft.Web/staticSites/customDomains@2022-03-01' = {
+ name: name
+ parent: staticSite
+ properties: {
+ validationMethod: validationMethod
+ }
+}
+
+@description('The name of the static site custom domain.')
+output name string = customDomain.name
+
+@description('The resource ID of the static site custom domain.')
+output resourceId string = customDomain.id
+
+@description('The resource group the static site custom domain was deployed into.')
+output resourceGroupName string = resourceGroup().name
diff --git a/avm/res/web/static-site/custom-domain/main.json b/avm/res/web/static-site/custom-domain/main.json
new file mode 100644
index 0000000000..29a6ad82d3
--- /dev/null
+++ b/avm/res/web/static-site/custom-domain/main.json
@@ -0,0 +1,68 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.24.24.22086",
+ "templateHash": "15929667345294936427"
+ },
+ "name": "Static Web App Site Custom Domains",
+ "description": "This module deploys a Static Web App Site Custom Domain.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The custom domain name."
+ }
+ },
+ "staticSiteName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Static Web App. Required if the template is used in a standalone deployment."
+ }
+ },
+ "validationMethod": {
+ "type": "string",
+ "defaultValue": "cname-delegation",
+ "metadata": {
+ "description": "Optional. Validation method for adding a custom domain."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Web/staticSites/customDomains",
+ "apiVersion": "2022-03-01",
+ "name": "[format('{0}/{1}', parameters('staticSiteName'), parameters('name'))]",
+ "properties": {
+ "validationMethod": "[parameters('validationMethod')]"
+ }
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the static site custom domain."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the static site custom domain."
+ },
+ "value": "[resourceId('Microsoft.Web/staticSites/customDomains', parameters('staticSiteName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the static site custom domain was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/web/static-site/linked-backend/README.md b/avm/res/web/static-site/linked-backend/README.md
new file mode 100644
index 0000000000..468acc399b
--- /dev/null
+++ b/avm/res/web/static-site/linked-backend/README.md
@@ -0,0 +1,80 @@
+# Static Web App Site Linked Backends `[Microsoft.Web/staticSites/linkedBackends]`
+
+This module deploys a Custom Function App into a Static Web App Site using the Linked Backends property.
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+
+## Resource Types
+
+| Resource Type | API Version |
+| :-- | :-- |
+| `Microsoft.Web/staticSites/linkedBackends` | [2022-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/2022-03-01/staticSites/linkedBackends) |
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`backendResourceId`](#parameter-backendresourceid) | string | The resource ID of the backend linked to the static site. |
+
+**Conditional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`staticSiteName`](#parameter-staticsitename) | string | The name of the parent Static Web App. Required if the template is used in a standalone deployment. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`name`](#parameter-name) | string | Name of the backend to link to the static site. |
+| [`region`](#parameter-region) | string | The region of the backend linked to the static site. |
+
+### Parameter: `backendResourceId`
+
+The resource ID of the backend linked to the static site.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `staticSiteName`
+
+The name of the parent Static Web App. Required if the template is used in a standalone deployment.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `name`
+
+Name of the backend to link to the static site.
+
+- Required: No
+- Type: string
+- Default: `[uniqueString(parameters('backendResourceId'))]`
+
+### Parameter: `region`
+
+The region of the backend linked to the static site.
+
+- Required: No
+- Type: string
+- Default: `[resourceGroup().location]`
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `name` | string | The name of the static site linked backend. |
+| `resourceGroupName` | string | The resource group the static site linked backend was deployed into. |
+| `resourceId` | string | The resource ID of the static site linked backend. |
+
+## Cross-referenced modules
+
+_None_
diff --git a/avm/res/web/static-site/linked-backend/main.bicep b/avm/res/web/static-site/linked-backend/main.bicep
new file mode 100644
index 0000000000..3fc6a38be5
--- /dev/null
+++ b/avm/res/web/static-site/linked-backend/main.bicep
@@ -0,0 +1,37 @@
+metadata name = 'Static Web App Site Linked Backends'
+metadata description = 'This module deploys a Custom Function App into a Static Web App Site using the Linked Backends property.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Required. The resource ID of the backend linked to the static site.')
+param backendResourceId string
+
+@description('Optional. The region of the backend linked to the static site.')
+param region string = resourceGroup().location
+
+@description('Conditional. The name of the parent Static Web App. Required if the template is used in a standalone deployment.')
+param staticSiteName string
+
+@description('Optional. Name of the backend to link to the static site.')
+param name string = uniqueString(backendResourceId)
+
+resource staticSite 'Microsoft.Web/staticSites@2022-03-01' existing = {
+ name: staticSiteName
+}
+
+resource linkedBackend 'Microsoft.Web/staticSites/linkedBackends@2022-03-01' = {
+ name: name
+ parent: staticSite
+ properties: {
+ backendResourceId: backendResourceId
+ region: region
+ }
+}
+
+@description('The name of the static site linked backend.')
+output name string = linkedBackend.name
+
+@description('The resource ID of the static site linked backend.')
+output resourceId string = linkedBackend.id
+
+@description('The resource group the static site linked backend was deployed into.')
+output resourceGroupName string = resourceGroup().name
diff --git a/avm/res/web/static-site/linked-backend/main.json b/avm/res/web/static-site/linked-backend/main.json
new file mode 100644
index 0000000000..73c8f32387
--- /dev/null
+++ b/avm/res/web/static-site/linked-backend/main.json
@@ -0,0 +1,76 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.24.24.22086",
+ "templateHash": "7695569913720977108"
+ },
+ "name": "Static Web App Site Linked Backends",
+ "description": "This module deploys a Custom Function App into a Static Web App Site using the Linked Backends property.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "backendResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource ID of the backend linked to the static site."
+ }
+ },
+ "region": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The region of the backend linked to the static site."
+ }
+ },
+ "staticSiteName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Static Web App. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "defaultValue": "[uniqueString(parameters('backendResourceId'))]",
+ "metadata": {
+ "description": "Optional. Name of the backend to link to the static site."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Web/staticSites/linkedBackends",
+ "apiVersion": "2022-03-01",
+ "name": "[format('{0}/{1}', parameters('staticSiteName'), parameters('name'))]",
+ "properties": {
+ "backendResourceId": "[parameters('backendResourceId')]",
+ "region": "[parameters('region')]"
+ }
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the static site linked backend."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the static site linked backend."
+ },
+ "value": "[resourceId('Microsoft.Web/staticSites/linkedBackends', parameters('staticSiteName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the static site linked backend was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/web/static-site/main.bicep b/avm/res/web/static-site/main.bicep
new file mode 100644
index 0000000000..86d0308e35
--- /dev/null
+++ b/avm/res/web/static-site/main.bicep
@@ -0,0 +1,362 @@
+metadata name = 'Static Web Apps'
+metadata description = 'This module deploys a Static Web App.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Required. Name of the static site.')
+@minLength(1)
+@maxLength(40)
+param name string
+
+@allowed([
+ 'Free'
+ 'Standard'
+])
+@description('Optional. Type of static site to deploy.')
+param sku string = 'Free'
+
+@description('Optional. False if config file is locked for this static web app; otherwise, true.')
+param allowConfigFileUpdates bool = true
+
+@description('Optional. Location for all resources.')
+param location string = resourceGroup().location
+
+@allowed([
+ 'Enabled'
+ 'Disabled'
+])
+@description('Optional. State indicating whether staging environments are allowed or not allowed for a static web app.')
+param stagingEnvironmentPolicy string = 'Enabled'
+
+@allowed([
+ 'Disabled'
+ 'Disabling'
+ 'Enabled'
+ 'Enabling'
+])
+@description('Optional. State indicating the status of the enterprise grade CDN serving traffic to the static web app.')
+param enterpriseGradeCdnStatus string = 'Disabled'
+
+@description('Optional. Build properties for the static site.')
+param buildProperties object?
+
+@description('Optional. Template Options for the static site.')
+param templateProperties object?
+
+@description('Optional. The provider that submitted the last deployment to the primary environment of the static site.')
+param provider string = 'None'
+
+@secure()
+@description('Optional. The Personal Access Token for accessing the GitHub repository.')
+param repositoryToken string?
+
+@description('Optional. The name of the GitHub repository.')
+param repositoryUrl string?
+
+@description('Optional. The branch name of the GitHub repository.')
+param branch string?
+
+@description('Optional. The managed identity definition for this resource.')
+param managedIdentities managedIdentitiesType
+
+@description('Optional. The lock settings of the service.')
+param lock lockType
+
+@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the \'sku\' to be \'Standard\'.')
+param privateEndpoints privateEndpointType
+
+@description('Optional. Tags of the resource.')
+param tags object?
+
+@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).')
+param enableTelemetry bool = true
+
+@description('Optional. Array of role assignments to create.')
+param roleAssignments roleAssignmentType
+
+@description('Optional. Object with "resourceId" and "location" of the a user defined function app.')
+param linkedBackend object = {}
+
+@description('Optional. Static site app settings.')
+param appSettings object = {}
+
+@description('Optional. Function app settings.')
+param functionAppSettings object = {}
+
+@description('Optional. The custom domains associated with this static site. The deployment will fail as long as the validation records are not present.')
+param customDomains array = []
+
+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' : null)
+ 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 (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')
+ 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')
+ 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')
+ 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')
+}
+
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: '46d3xbcp.res.web-staticSite.${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 staticSite 'Microsoft.Web/staticSites@2021-03-01' = {
+ name: name
+ location: location
+ tags: tags
+ identity: identity
+ sku: {
+ name: sku
+ tier: sku
+ }
+ properties: {
+ allowConfigFileUpdates: allowConfigFileUpdates
+ stagingEnvironmentPolicy: stagingEnvironmentPolicy
+ enterpriseGradeCdnStatus: enterpriseGradeCdnStatus
+ provider: !empty(provider) ? provider : 'None'
+ branch: branch
+ buildProperties: buildProperties
+ repositoryToken: repositoryToken
+ repositoryUrl: repositoryUrl
+ templateProperties: templateProperties
+ }
+}
+
+module staticSite_linkedBackend 'linked-backend/main.bicep' = if (!empty(linkedBackend)) {
+ name: '${uniqueString(deployment().name, location)}-StaticSite-UserDefinedFunction'
+ params: {
+ staticSiteName: staticSite.name
+ backendResourceId: linkedBackend.resourceId
+ region: contains(linkedBackend, 'location') ? linkedBackend.location : location
+ }
+}
+
+module staticSite_appSettings 'config/main.bicep' = if (!empty(appSettings)) {
+ name: '${uniqueString(deployment().name, location)}-StaticSite-appSettings'
+ params: {
+ kind: 'appsettings'
+ staticSiteName: staticSite.name
+ properties: appSettings
+ }
+}
+
+module staticSite_functionAppSettings 'config/main.bicep' = if (!empty(functionAppSettings)) {
+ name: '${uniqueString(deployment().name, location)}-StaticSite-functionAppSettings'
+ params: {
+ kind: 'functionappsettings'
+ staticSiteName: staticSite.name
+ properties: functionAppSettings
+ }
+}
+
+module staticSite_customDomains 'custom-domain/main.bicep' = [for (customDomain, index) in customDomains: {
+ name: '${uniqueString(deployment().name, location)}-StaticSite-customDomains-${index}'
+ params: {
+ name: customDomain
+ staticSiteName: staticSite.name
+ validationMethod: indexOf(customDomain, '.') == lastIndexOf(customDomain, '.') ? 'dns-txt-token' : 'cname-delegation'
+ }
+}]
+
+resource staticSite_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: staticSite
+}
+
+resource staticSite_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in (roleAssignments ?? []): {
+ name: guid(staticSite.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: staticSite
+}]
+
+module staticSite_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.3.1' = [for (privateEndpoint, index) in (privateEndpoints ?? []): {
+ name: '${uniqueString(deployment().name, location)}-staticSite-PrivateEndpoint-${index}'
+ params: {
+ privateLinkServiceConnections: [
+ {
+ name: name
+ properties: {
+ privateLinkServiceId: staticSite.id
+ groupIds: [
+ privateEndpoint.?service ?? 'staticSites'
+ ]
+ }
+ }
+ ]
+ name: privateEndpoint.?name ?? 'pep-${last(split(staticSite.id, '/'))}-${privateEndpoint.?service ?? 'staticSites'}-${index}'
+ subnetResourceId: privateEndpoint.subnetResourceId
+ enableTelemetry: enableTelemetry
+ location: privateEndpoint.?location ?? reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location
+ lock: privateEndpoint.?lock ?? lock
+ privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName
+ privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds
+ roleAssignments: privateEndpoint.?roleAssignments
+ tags: privateEndpoint.?tags ?? tags
+ manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections
+ customDnsConfigs: privateEndpoint.?customDnsConfigs
+ ipConfigurations: privateEndpoint.?ipConfigurations
+ applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds
+ customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName
+ }
+}]
+
+@description('The name of the static site.')
+output name string = staticSite.name
+
+@description('The resource ID of the static site.')
+output resourceId string = staticSite.id
+
+@description('The resource group the static site was deployed into.')
+output resourceGroupName string = resourceGroup().name
+
+@description('The principal ID of the system assigned identity.')
+output systemAssignedMIPrincipalId string = (managedIdentities.?systemAssigned ?? false) && contains(staticSite.identity, 'principalId') ? staticSite.identity.principalId : ''
+
+@description('The location the resource was deployed into.')
+output location string = staticSite.location
+
+@description('The default autogenerated hostname for the static site.')
+output defaultHostname string = staticSite.properties.defaultHostname
+
+// =============== //
+// 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 privateEndpointType = {
+ @description('Optional. The name of the private endpoint.')
+ name: string?
+
+ @description('Optional. The location to deploy the private endpoint to.')
+ location: string?
+
+ @description('Optional. The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob".')
+ service: string?
+
+ @description('Required. Resource ID of the subnet where the endpoint needs to be created.')
+ subnetResourceId: string
+
+ @description('Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided.')
+ privateDnsZoneGroupName: string?
+
+ @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.')
+ privateDnsZoneResourceIds: string[]?
+
+ @description('Optional. Custom DNS configurations.')
+ customDnsConfigs: {
+ @description('Required. Fqdn that resolves to private endpoint ip address.')
+ fqdn: string?
+
+ @description('Required. A list of private ip addresses of the private endpoint.')
+ ipAddresses: string[]
+ }[]?
+
+ @description('Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints.')
+ ipConfigurations: {
+ @description('Required. The name of the resource that is unique within a resource group.')
+ name: string
+
+ @description('Required. Properties of private endpoint IP configurations.')
+ properties: {
+ @description('Required. The ID of a group obtained from the remote resource that this private endpoint should connect to.')
+ groupId: string
+
+ @description('Required. The member name of a group obtained from the remote resource that this private endpoint should connect to.')
+ memberName: string
+
+ @description('Required. A private ip address obtained from the private endpoint\'s subnet.')
+ privateIPAddress: string
+ }
+ }[]?
+
+ @description('Optional. Application security groups in which the private endpoint IP configuration is included.')
+ applicationSecurityGroupResourceIds: string[]?
+
+ @description('Optional. The custom name of the network interface attached to the private endpoint.')
+ customNetworkInterfaceName: string?
+
+ @description('Optional. Specify the type of lock.')
+ lock: lockType
+
+ @description('Optional. Array of role assignments to create.')
+ roleAssignments: roleAssignmentType
+
+ @description('Optional. Tags to be applied on all resources/resource groups in this deployment.')
+ tags: object?
+
+ @description('Optional. Manual PrivateLink Service Connections.')
+ manualPrivateLinkServiceConnections: array?
+
+ @description('Optional. Enable/Disable usage telemetry for module.')
+ enableTelemetry: bool?
+}[]?
diff --git a/avm/res/web/static-site/main.json b/avm/res/web/static-site/main.json
new file mode 100644
index 0000000000..414d493f8f
--- /dev/null
+++ b/avm/res/web/static-site/main.json
@@ -0,0 +1,1664 @@
+{
+ "$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.24.24.22086",
+ "templateHash": "1837229798638976594"
+ },
+ "name": "Static Web Apps",
+ "description": "This module deploys a Static Web App.",
+ "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
+ },
+ "privateEndpointType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the private endpoint."
+ }
+ },
+ "location": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The location to deploy the private endpoint to."
+ }
+ },
+ "service": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"."
+ }
+ },
+ "subnetResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of the subnet where the endpoint needs to be created."
+ }
+ },
+ "privateDnsZoneGroupName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided."
+ }
+ },
+ "privateDnsZoneResourceIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones."
+ }
+ },
+ "customDnsConfigs": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "fqdn": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Required. Fqdn that resolves to private endpoint ip address."
+ }
+ },
+ "ipAddresses": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "metadata": {
+ "description": "Required. A list of private ip addresses of the private endpoint."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Custom DNS configurations."
+ }
+ },
+ "ipConfigurations": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the resource that is unique within a resource group."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "groupId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to."
+ }
+ },
+ "memberName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to."
+ }
+ },
+ "privateIPAddress": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. A private ip address obtained from the private endpoint's subnet."
+ }
+ }
+ },
+ "metadata": {
+ "description": "Required. Properties of private endpoint IP configurations."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints."
+ }
+ },
+ "applicationSecurityGroupResourceIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Application security groups in which the private endpoint IP configuration is included."
+ }
+ },
+ "customNetworkInterfaceName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The custom name of the network interface attached to the private endpoint."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ },
+ "roleAssignments": {
+ "$ref": "#/definitions/roleAssignmentType",
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags to be applied on all resources/resource groups in this deployment."
+ }
+ },
+ "manualPrivateLinkServiceConnections": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Manual PrivateLink Service Connections."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ }
+ }
+ },
+ "nullable": true
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 40,
+ "metadata": {
+ "description": "Required. Name of the static site."
+ }
+ },
+ "sku": {
+ "type": "string",
+ "defaultValue": "Free",
+ "allowedValues": [
+ "Free",
+ "Standard"
+ ],
+ "metadata": {
+ "description": "Optional. Type of static site to deploy."
+ }
+ },
+ "allowConfigFileUpdates": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. False if config file is locked for this static web app; otherwise, true."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all resources."
+ }
+ },
+ "stagingEnvironmentPolicy": {
+ "type": "string",
+ "defaultValue": "Enabled",
+ "allowedValues": [
+ "Enabled",
+ "Disabled"
+ ],
+ "metadata": {
+ "description": "Optional. State indicating whether staging environments are allowed or not allowed for a static web app."
+ }
+ },
+ "enterpriseGradeCdnStatus": {
+ "type": "string",
+ "defaultValue": "Disabled",
+ "allowedValues": [
+ "Disabled",
+ "Disabling",
+ "Enabled",
+ "Enabling"
+ ],
+ "metadata": {
+ "description": "Optional. State indicating the status of the enterprise grade CDN serving traffic to the static web app."
+ }
+ },
+ "buildProperties": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Build properties for the static site."
+ }
+ },
+ "templateProperties": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Template Options for the static site."
+ }
+ },
+ "provider": {
+ "type": "string",
+ "defaultValue": "None",
+ "metadata": {
+ "description": "Optional. The provider that submitted the last deployment to the primary environment of the static site."
+ }
+ },
+ "repositoryToken": {
+ "type": "securestring",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Personal Access Token for accessing the GitHub repository."
+ }
+ },
+ "repositoryUrl": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the GitHub repository."
+ }
+ },
+ "branch": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The branch name of the GitHub repository."
+ }
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentitiesType",
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "privateEndpoints": {
+ "$ref": "#/definitions/privateEndpointType",
+ "metadata": {
+ "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'sku' to be 'Standard'."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)."
+ }
+ },
+ "roleAssignments": {
+ "$ref": "#/definitions/roleAssignmentType",
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "linkedBackend": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Object with \"resourceId\" and \"location\" of the a user defined function app."
+ }
+ },
+ "appSettings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Static site app settings."
+ }
+ },
+ "functionAppSettings": {
+ "type": "object",
+ "defaultValue": {},
+ "metadata": {
+ "description": "Optional. Function app settings."
+ }
+ },
+ "customDomains": {
+ "type": "array",
+ "defaultValue": [],
+ "metadata": {
+ "description": "Optional. The custom domains associated with this static site. The deployment will fail as long as the validation records are not present."
+ }
+ }
+ },
+ "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())]",
+ "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')]",
+ "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]",
+ "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]"
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2023-07-01",
+ "name": "[format('46d3xbcp.res.web-staticSite.{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"
+ }
+ }
+ }
+ }
+ },
+ "staticSite": {
+ "type": "Microsoft.Web/staticSites",
+ "apiVersion": "2021-03-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "identity": "[variables('identity')]",
+ "sku": {
+ "name": "[parameters('sku')]",
+ "tier": "[parameters('sku')]"
+ },
+ "properties": {
+ "allowConfigFileUpdates": "[parameters('allowConfigFileUpdates')]",
+ "stagingEnvironmentPolicy": "[parameters('stagingEnvironmentPolicy')]",
+ "enterpriseGradeCdnStatus": "[parameters('enterpriseGradeCdnStatus')]",
+ "provider": "[if(not(empty(parameters('provider'))), parameters('provider'), 'None')]",
+ "branch": "[parameters('branch')]",
+ "buildProperties": "[parameters('buildProperties')]",
+ "repositoryToken": "[parameters('repositoryToken')]",
+ "repositoryUrl": "[parameters('repositoryUrl')]",
+ "templateProperties": "[parameters('templateProperties')]"
+ }
+ },
+ "staticSite_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.Web/staticSites/{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": [
+ "staticSite"
+ ]
+ },
+ "staticSite_roleAssignments": {
+ "copy": {
+ "name": "staticSite_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Web/staticSites/{0}', parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.Web/staticSites', 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": [
+ "staticSite"
+ ]
+ },
+ "staticSite_linkedBackend": {
+ "condition": "[not(empty(parameters('linkedBackend')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-StaticSite-UserDefinedFunction', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "staticSiteName": {
+ "value": "[parameters('name')]"
+ },
+ "backendResourceId": {
+ "value": "[parameters('linkedBackend').resourceId]"
+ },
+ "region": "[if(contains(parameters('linkedBackend'), 'location'), createObject('value', parameters('linkedBackend').location), createObject('value', parameters('location')))]"
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.24.24.22086",
+ "templateHash": "7695569913720977108"
+ },
+ "name": "Static Web App Site Linked Backends",
+ "description": "This module deploys a Custom Function App into a Static Web App Site using the Linked Backends property.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "backendResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource ID of the backend linked to the static site."
+ }
+ },
+ "region": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. The region of the backend linked to the static site."
+ }
+ },
+ "staticSiteName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Static Web App. Required if the template is used in a standalone deployment."
+ }
+ },
+ "name": {
+ "type": "string",
+ "defaultValue": "[uniqueString(parameters('backendResourceId'))]",
+ "metadata": {
+ "description": "Optional. Name of the backend to link to the static site."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Web/staticSites/linkedBackends",
+ "apiVersion": "2022-03-01",
+ "name": "[format('{0}/{1}', parameters('staticSiteName'), parameters('name'))]",
+ "properties": {
+ "backendResourceId": "[parameters('backendResourceId')]",
+ "region": "[parameters('region')]"
+ }
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the static site linked backend."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the static site linked backend."
+ },
+ "value": "[resourceId('Microsoft.Web/staticSites/linkedBackends', parameters('staticSiteName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the static site linked backend was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "staticSite"
+ ]
+ },
+ "staticSite_appSettings": {
+ "condition": "[not(empty(parameters('appSettings')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-StaticSite-appSettings', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "kind": {
+ "value": "appsettings"
+ },
+ "staticSiteName": {
+ "value": "[parameters('name')]"
+ },
+ "properties": {
+ "value": "[parameters('appSettings')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.24.24.22086",
+ "templateHash": "12360558347482884602"
+ },
+ "name": "Static Web App Site Config",
+ "description": "This module deploys a Static Web App Site Config.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "appsettings",
+ "functionappsettings"
+ ],
+ "metadata": {
+ "description": "Required. Type of settings to apply."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. App settings."
+ }
+ },
+ "staticSiteName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Static Web App. Required if the template is used in a standalone deployment."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Web/staticSites/config",
+ "apiVersion": "2022-03-01",
+ "name": "[format('{0}/{1}', parameters('staticSiteName'), parameters('kind'))]",
+ "properties": "[parameters('properties')]"
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the config."
+ },
+ "value": "[parameters('kind')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the config."
+ },
+ "value": "[resourceId('Microsoft.Web/staticSites/config', parameters('staticSiteName'), parameters('kind'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the config was created in."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "staticSite"
+ ]
+ },
+ "staticSite_functionAppSettings": {
+ "condition": "[not(empty(parameters('functionAppSettings')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-StaticSite-functionAppSettings', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "kind": {
+ "value": "functionappsettings"
+ },
+ "staticSiteName": {
+ "value": "[parameters('name')]"
+ },
+ "properties": {
+ "value": "[parameters('functionAppSettings')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.24.24.22086",
+ "templateHash": "12360558347482884602"
+ },
+ "name": "Static Web App Site Config",
+ "description": "This module deploys a Static Web App Site Config.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "appsettings",
+ "functionappsettings"
+ ],
+ "metadata": {
+ "description": "Required. Type of settings to apply."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "metadata": {
+ "description": "Required. App settings."
+ }
+ },
+ "staticSiteName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Static Web App. Required if the template is used in a standalone deployment."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Web/staticSites/config",
+ "apiVersion": "2022-03-01",
+ "name": "[format('{0}/{1}', parameters('staticSiteName'), parameters('kind'))]",
+ "properties": "[parameters('properties')]"
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the config."
+ },
+ "value": "[parameters('kind')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the config."
+ },
+ "value": "[resourceId('Microsoft.Web/staticSites/config', parameters('staticSiteName'), parameters('kind'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the config was created in."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "staticSite"
+ ]
+ },
+ "staticSite_customDomains": {
+ "copy": {
+ "name": "staticSite_customDomains",
+ "count": "[length(parameters('customDomains'))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-StaticSite-customDomains-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[parameters('customDomains')[copyIndex()]]"
+ },
+ "staticSiteName": {
+ "value": "[parameters('name')]"
+ },
+ "validationMethod": "[if(equals(indexOf(parameters('customDomains')[copyIndex()], '.'), lastIndexOf(parameters('customDomains')[copyIndex()], '.')), createObject('value', 'dns-txt-token'), createObject('value', 'cname-delegation'))]"
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.24.24.22086",
+ "templateHash": "15929667345294936427"
+ },
+ "name": "Static Web App Site Custom Domains",
+ "description": "This module deploys a Static Web App Site Custom Domain.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The custom domain name."
+ }
+ },
+ "staticSiteName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent Static Web App. Required if the template is used in a standalone deployment."
+ }
+ },
+ "validationMethod": {
+ "type": "string",
+ "defaultValue": "cname-delegation",
+ "metadata": {
+ "description": "Optional. Validation method for adding a custom domain."
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Web/staticSites/customDomains",
+ "apiVersion": "2022-03-01",
+ "name": "[format('{0}/{1}', parameters('staticSiteName'), parameters('name'))]",
+ "properties": {
+ "validationMethod": "[parameters('validationMethod')]"
+ }
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the static site custom domain."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the static site custom domain."
+ },
+ "value": "[resourceId('Microsoft.Web/staticSites/customDomains', parameters('staticSiteName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the static site custom domain was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "staticSite"
+ ]
+ },
+ "staticSite_privateEndpoints": {
+ "copy": {
+ "name": "staticSite_privateEndpoints",
+ "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-staticSite-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "privateLinkServiceConnections": {
+ "value": [
+ {
+ "name": "[parameters('name')]",
+ "properties": {
+ "privateLinkServiceId": "[resourceId('Microsoft.Web/staticSites', parameters('name'))]",
+ "groupIds": [
+ "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'staticSites')]"
+ ]
+ }
+ }
+ ]
+ },
+ "name": {
+ "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Web/staticSites', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'staticSites'), copyIndex()))]"
+ },
+ "subnetResourceId": {
+ "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]"
+ },
+ "enableTelemetry": {
+ "value": "[parameters('enableTelemetry')]"
+ },
+ "location": {
+ "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]"
+ },
+ "lock": {
+ "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]"
+ },
+ "privateDnsZoneGroupName": {
+ "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]"
+ },
+ "privateDnsZoneResourceIds": {
+ "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]"
+ },
+ "roleAssignments": {
+ "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]"
+ },
+ "tags": {
+ "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]"
+ },
+ "manualPrivateLinkServiceConnections": {
+ "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections')]"
+ },
+ "customDnsConfigs": {
+ "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]"
+ },
+ "ipConfigurations": {
+ "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]"
+ },
+ "applicationSecurityGroupResourceIds": {
+ "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]"
+ },
+ "customNetworkInterfaceName": {
+ "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]"
+ }
+ },
+ "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.23.1.45101",
+ "templateHash": "2821141217598568122"
+ },
+ "name": "Private Endpoints",
+ "description": "This module deploys a Private Endpoint.",
+ "owner": "Azure/module-maintainers"
+ },
+ "definitions": {
+ "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
+ },
+ "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
+ },
+ "ipConfigurationsType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the resource that is unique within a resource group."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "groupId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to."
+ }
+ },
+ "memberName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to."
+ }
+ },
+ "privateIPAddress": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. A private ip address obtained from the private endpoint's subnet."
+ }
+ }
+ },
+ "metadata": {
+ "description": "Required. Properties of private endpoint IP configurations."
+ }
+ }
+ }
+ },
+ "nullable": true
+ },
+ "manualPrivateLinkServiceConnectionsType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the private link service connection."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "groupIds": {
+ "type": "array",
+ "metadata": {
+ "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to."
+ }
+ },
+ "privateLinkServiceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource id of private link service."
+ }
+ },
+ "requestMessage": {
+ "type": "string",
+ "metadata": {
+ "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars."
+ }
+ }
+ },
+ "metadata": {
+ "description": "Required. Properties of private link service connection."
+ }
+ }
+ }
+ },
+ "nullable": true
+ },
+ "privateLinkServiceConnectionsType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The name of the private link service connection."
+ }
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "groupIds": {
+ "type": "array",
+ "metadata": {
+ "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to."
+ }
+ },
+ "privateLinkServiceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resource id of private link service."
+ }
+ },
+ "requestMessage": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars."
+ }
+ }
+ },
+ "metadata": {
+ "description": "Required. Properties of private link service connection."
+ }
+ }
+ }
+ },
+ "nullable": true
+ },
+ "customDnsConfigType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "fqdn": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Fqdn that resolves to private endpoint ip address."
+ }
+ },
+ "ipAddresses": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "metadata": {
+ "description": "Required. A list of private ip addresses of the private endpoint."
+ }
+ }
+ }
+ },
+ "nullable": true
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the private endpoint resource to create."
+ }
+ },
+ "subnetResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of the subnet where the endpoint needs to be created."
+ }
+ },
+ "applicationSecurityGroupResourceIds": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Application security groups in which the private endpoint IP configuration is included."
+ }
+ },
+ "customNetworkInterfaceName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The custom name of the network interface attached to the private endpoint."
+ }
+ },
+ "ipConfigurations": {
+ "$ref": "#/definitions/ipConfigurationsType",
+ "metadata": {
+ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints."
+ }
+ },
+ "privateDnsZoneGroupName": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided."
+ }
+ },
+ "privateDnsZoneResourceIds": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all Resources."
+ }
+ },
+ "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."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags to be applied on all resources/resource groups in this deployment."
+ }
+ },
+ "customDnsConfigs": {
+ "$ref": "#/definitions/customDnsConfigType",
+ "metadata": {
+ "description": "Optional. Custom DNS configurations."
+ }
+ },
+ "manualPrivateLinkServiceConnections": {
+ "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType",
+ "metadata": {
+ "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource."
+ }
+ },
+ "privateLinkServiceConnections": {
+ "$ref": "#/definitions/privateLinkServiceConnectionsType",
+ "metadata": {
+ "description": "Optional. A grouping of information about the connection to the remote resource."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ }
+ },
+ "variables": {
+ "builtInRoleNames": {
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]",
+ "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]",
+ "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]",
+ "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]",
+ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]",
+ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
+ "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]",
+ "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')]"
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2023-07-01",
+ "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.3.1', '.', '-'), 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"
+ }
+ }
+ }
+ }
+ },
+ "privateEndpoint": {
+ "type": "Microsoft.Network/privateEndpoints",
+ "apiVersion": "2023-04-01",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "properties": {
+ "copy": [
+ {
+ "name": "applicationSecurityGroups",
+ "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]",
+ "input": {
+ "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]"
+ }
+ }
+ ],
+ "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]",
+ "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]",
+ "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]",
+ "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]",
+ "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]",
+ "subnet": {
+ "id": "[parameters('subnetResourceId')]"
+ }
+ }
+ },
+ "privateEndpoint_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.Network/privateEndpoints/{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": [
+ "privateEndpoint"
+ ]
+ },
+ "privateEndpoint_roleAssignments": {
+ "copy": {
+ "name": "privateEndpoint_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', 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": [
+ "privateEndpoint"
+ ]
+ },
+ "privateEndpoint_privateDnsZoneGroup": {
+ "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "name": {
+ "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]"
+ },
+ "privateDNSResourceIds": {
+ "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]"
+ },
+ "privateEndpointName": {
+ "value": "[parameters('name')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.23.1.45101",
+ "templateHash": "18168683629401652671"
+ },
+ "name": "Private Endpoint Private DNS Zone Groups",
+ "description": "This module deploys a Private Endpoint Private DNS Zone Group.",
+ "owner": "Azure/module-maintainers"
+ },
+ "parameters": {
+ "privateEndpointName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment."
+ }
+ },
+ "privateDNSResourceIds": {
+ "type": "array",
+ "minLength": 1,
+ "maxLength": 5,
+ "metadata": {
+ "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones."
+ }
+ },
+ "name": {
+ "type": "string",
+ "defaultValue": "default",
+ "metadata": {
+ "description": "Optional. The name of the private DNS zone group."
+ }
+ }
+ },
+ "variables": {
+ "copy": [
+ {
+ "name": "privateDnsZoneConfigs",
+ "count": "[length(parameters('privateDNSResourceIds'))]",
+ "input": {
+ "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]",
+ "properties": {
+ "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]"
+ }
+ }
+ }
+ ]
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups",
+ "apiVersion": "2023-04-01",
+ "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]",
+ "properties": {
+ "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]"
+ }
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the private endpoint DNS zone group."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the private endpoint DNS zone group."
+ },
+ "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the private endpoint DNS zone group was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "privateEndpoint"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the private endpoint was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the private endpoint."
+ },
+ "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the private endpoint."
+ },
+ "value": "[parameters('name')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "staticSite"
+ ]
+ }
+ },
+ "outputs": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the static site."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the static site."
+ },
+ "value": "[resourceId('Microsoft.Web/staticSites', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the static site was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "systemAssignedMIPrincipalId": {
+ "type": "string",
+ "metadata": {
+ "description": "The principal ID of the system assigned identity."
+ },
+ "value": "[if(and(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), contains(reference('staticSite', '2021-03-01', 'full').identity, 'principalId')), reference('staticSite', '2021-03-01', 'full').identity.principalId, '')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('staticSite', '2021-03-01', 'full').location]"
+ },
+ "defaultHostname": {
+ "type": "string",
+ "metadata": {
+ "description": "The default autogenerated hostname for the static site."
+ },
+ "value": "[reference('staticSite').defaultHostname]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/web/static-site/tests/e2e/defaults/main.test.bicep b/avm/res/web/static-site/tests/e2e/defaults/main.test.bicep
new file mode 100644
index 0000000000..91f09b1142
--- /dev/null
+++ b/avm/res/web/static-site/tests/e2e/defaults/main.test.bicep
@@ -0,0 +1,46 @@
+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}-web.staticsites-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'wssmin'
+
+@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: location
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ location: location
+ }
+}]
diff --git a/avm/res/web/static-site/tests/e2e/max/dependencies.bicep b/avm/res/web/static-site/tests/e2e/max/dependencies.bicep
new file mode 100644
index 0000000000..7939cfd2d2
--- /dev/null
+++ b/avm/res/web/static-site/tests/e2e/max/dependencies.bicep
@@ -0,0 +1,94 @@
+@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 Managed Identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the Function App to create.')
+param siteName string
+
+@description('Required. The name of the Server Farm to create.')
+param serverFarmName 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: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.azurestaticapps.net'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
+ name: managedIdentityName
+ location: location
+}
+
+resource serverFarm 'Microsoft.Web/serverfarms@2022-03-01' = {
+ name: serverFarmName
+ location: location
+ sku: {
+ name: 'S1'
+ tier: 'Standard'
+ size: 'S1'
+ family: 'S'
+ capacity: 1
+ }
+ properties: {}
+}
+
+resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
+ name: siteName
+ location: location
+ kind: 'functionapp'
+ properties: {
+ serverFarmId: serverFarm.id
+ }
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+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
+
+@description('The resource ID of the created Private DNS zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
+
+@description('The resource ID of the created Function App.')
+output siteResourceId string = functionApp.id
diff --git a/avm/res/web/static-site/tests/e2e/max/main.test.bicep b/avm/res/web/static-site/tests/e2e/max/main.test.bicep
new file mode 100644
index 0000000000..6cfea6e8e3
--- /dev/null
+++ b/avm/res/web/static-site/tests/e2e/max/main.test.bicep
@@ -0,0 +1,118 @@
+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}-web.staticsites-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'wssmax'
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ siteName: 'dep-${namePrefix}-fa-${serviceShort}'
+ serverFarmName: 'dep-${namePrefix}-sf-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ allowConfigFileUpdates: true
+ enterpriseGradeCdnStatus: 'Disabled'
+ location: location
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ privateEndpoints: [
+ {
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ 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'
+ }
+ ]
+ sku: 'Standard'
+ stagingEnvironmentPolicy: 'Enabled'
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourceIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ appSettings: {
+ foo: 'bar'
+ setting: 1
+ }
+ functionAppSettings: {
+ foo: 'bar'
+ setting: 1
+ }
+ linkedBackend: {
+ resourceId: nestedDependencies.outputs.siteResourceId
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}]
diff --git a/avm/res/web/static-site/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/web/static-site/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..8a6f1f158a
--- /dev/null
+++ b/avm/res/web/static-site/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,80 @@
+@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 Function App to create.')
+param siteName string
+
+@description('Required. The name of the Server Farm to create.')
+param serverFarmName 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: 'defaultSubnet'
+ properties: {
+ addressPrefix: cidrSubnet(addressPrefix, 16, 0)
+ }
+ }
+ ]
+ }
+}
+
+resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
+ name: 'privatelink.azurestaticapps.net'
+ location: 'global'
+
+ resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = {
+ name: '${virtualNetwork.name}-vnetlink'
+ location: 'global'
+ properties: {
+ virtualNetwork: {
+ id: virtualNetwork.id
+ }
+ registrationEnabled: false
+ }
+ }
+}
+
+resource serverFarm 'Microsoft.Web/serverfarms@2022-03-01' = {
+ name: serverFarmName
+ location: location
+ sku: {
+ name: 'S1'
+ tier: 'Standard'
+ size: 'S1'
+ family: 'S'
+ capacity: 1
+ }
+ properties: {}
+}
+
+resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
+ name: siteName
+ location: location
+ kind: 'functionapp'
+ properties: {
+ serverFarmId: serverFarm.id
+ }
+}
+
+@description('The resource ID of the created Virtual Network Subnet.')
+output subnetResourceId string = virtualNetwork.properties.subnets[0].id
+
+@description('The resource ID of the created Private DNS zone.')
+output privateDNSZoneResourceId string = privateDNSZone.id
+
+@description('The resource ID of the created Function App.')
+output siteResourceId string = functionApp.id
diff --git a/avm/res/web/static-site/tests/e2e/waf-aligned/main.test.bicep b/avm/res/web/static-site/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..6abca487cc
--- /dev/null
+++ b/avm/res/web/static-site/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,94 @@
+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}-web.staticsites-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'wsswaf'
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
+ siteName: 'dep-${namePrefix}-fa-${serviceShort}'
+ serverFarmName: 'dep-${namePrefix}-sf-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ allowConfigFileUpdates: true
+ enterpriseGradeCdnStatus: 'Disabled'
+ location: location
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ privateEndpoints: [
+ {
+ subnetResourceId: nestedDependencies.outputs.subnetResourceId
+ privateDnsZoneResourceIds: [
+ nestedDependencies.outputs.privateDNSZoneResourceId
+ ]
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+ ]
+ sku: 'Standard'
+ stagingEnvironmentPolicy: 'Enabled'
+ appSettings: {
+ foo: 'bar'
+ setting: 1
+ }
+ functionAppSettings: {
+ foo: 'bar'
+ setting: 1
+ }
+ linkedBackend: {
+ resourceId: nestedDependencies.outputs.siteResourceId
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}]
diff --git a/avm/res/web/static-site/version.json b/avm/res/web/static-site/version.json
new file mode 100644
index 0000000000..8def869ede
--- /dev/null
+++ b/avm/res/web/static-site/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
+ "version": "0.1",
+ "pathFilters": [
+ "./main.json"
+ ]
+}
From 3160157f2124c33386266385949ee9cd99ee4792 Mon Sep 17 00:00:00 2001
From: Kris Baranek
+
+
+
+### Example 2: _Using only defaults_
+
+This instance deploys the module with the minimum set of required parameters.
+
+
+
+
+
+
+### Example 3: _Using large parameter set_
+
+This instance deploys the module with most of its features enabled.
+
+
+
+
+
+
+### Example 4: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+
+
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`keyName`](#parameter-keyname) | string | Key URL (with version) pointing to a key or secret in KeyVault. |
+| [`keyVaultResourceId`](#parameter-keyvaultresourceid) | string | Resource ID of the KeyVault containing the key or secret. |
+| [`name`](#parameter-name) | string | The name of the disk encryption set that is being created. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. |
+| [`encryptionType`](#parameter-encryptiontype) | string | The type of key used to encrypt the data of the disk. For security reasons, it is recommended to set encryptionType to EncryptionAtRestWithPlatformAndCustomerKeys. |
+| [`federatedClientId`](#parameter-federatedclientid) | string | Multi-tenant application client ID to access key vault in a different tenant. Setting the value to "None" will clear the property. |
+| [`keyVersion`](#parameter-keyversion) | string | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. |
+| [`location`](#parameter-location) | string | Resource location. |
+| [`lock`](#parameter-lock) | object | The lock settings of the service. |
+| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. At least one identity type is required. |
+| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. |
+| [`rotationToLatestKeyVersionEnabled`](#parameter-rotationtolatestkeyversionenabled) | bool | Set this flag to true to enable auto-updating of this disk encryption set to the latest key version. |
+| [`tags`](#parameter-tags) | object | Tags of the disk encryption resource. |
+
+### Parameter: `keyName`
+
+Key URL (with version) pointing to a key or secret in KeyVault.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `keyVaultResourceId`
+
+Resource ID of the KeyVault containing the key or secret.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `name`
+
+The name of the disk encryption set that is being created.
+
+- Required: Yes
+- Type: string
+
+### Parameter: `enableTelemetry`
+
+Enable/Disable usage telemetry for module.
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `encryptionType`
+
+The type of key used to encrypt the data of the disk. For security reasons, it is recommended to set encryptionType to EncryptionAtRestWithPlatformAndCustomerKeys.
+
+- Required: No
+- Type: string
+- Default: `'EncryptionAtRestWithPlatformAndCustomerKeys'`
+- Allowed:
+ ```Bicep
+ [
+ 'EncryptionAtRestWithCustomerKey'
+ 'EncryptionAtRestWithPlatformAndCustomerKeys'
+ ]
+ ```
+
+### Parameter: `federatedClientId`
+
+Multi-tenant application client ID to access key vault in a different tenant. Setting the value to "None" will clear the property.
+
+- Required: No
+- Type: string
+- Default: `'None'`
+
+### Parameter: `keyVersion`
+
+The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.
+
+- Required: No
+- Type: string
+- Default: `''`
+
+### Parameter: `location`
+
+Resource location.
+
+- 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. At least one identity type is required.
+
+- Required: No
+- Type: object
+- Default:
+ ```Bicep
+ {
+ systemAssigned: true
+ }
+ ```
+
+**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: `rotationToLatestKeyVersionEnabled`
+
+Set this flag to true to enable auto-updating of this disk encryption set to the latest key version.
+
+- Required: No
+- Type: bool
+- Default: `False`
+
+### Parameter: `tags`
+
+Tags of the disk encryption resource.
+
+- Required: No
+- Type: object
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `identities` | object | The idenities of the disk encryption set. |
+| `keyVaultName` | string | The name of the key vault with the disk encryption key. |
+| `location` | string | The location the resource was deployed into. |
+| `name` | string | The name of the disk encryption set. |
+| `resourceGroupName` | string | The resource group the disk encryption set was deployed into. |
+| `resourceId` | string | The resource ID of the disk encryption set. |
+| `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. |
+
+## Cross-referenced modules
+
+_None_
diff --git a/avm/res/compute/disk-encryption-set/main.bicep b/avm/res/compute/disk-encryption-set/main.bicep
new file mode 100644
index 0000000000..7040ad754a
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/main.bicep
@@ -0,0 +1,217 @@
+metadata name = 'Disk Encryption Sets'
+metadata description = 'This module deploys a Disk Encryption Set. The module will attempt to set permissions on the provided Key Vault for any used user-assigned identity.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Required. The name of the disk encryption set that is being created.')
+param name string
+
+@description('Optional. Resource location.')
+param location string = resourceGroup().location
+
+@description('Optional. The lock settings of the service.')
+param lock lockType
+
+@description('Required. Resource ID of the KeyVault containing the key or secret.')
+param keyVaultResourceId string
+
+@description('Required. Key URL (with version) pointing to a key or secret in KeyVault.')
+param keyName string
+
+@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.')
+param keyVersion string = ''
+
+@description('Optional. The type of key used to encrypt the data of the disk. For security reasons, it is recommended to set encryptionType to EncryptionAtRestWithPlatformAndCustomerKeys.')
+@allowed([
+ 'EncryptionAtRestWithCustomerKey'
+ 'EncryptionAtRestWithPlatformAndCustomerKeys'
+])
+param encryptionType string = 'EncryptionAtRestWithPlatformAndCustomerKeys'
+
+@description('Optional. Multi-tenant application client ID to access key vault in a different tenant. Setting the value to "None" will clear the property.')
+param federatedClientId string = 'None'
+
+@description('Optional. Set this flag to true to enable auto-updating of this disk encryption set to the latest key version.')
+param rotationToLatestKeyVersionEnabled bool = false
+
+@description('Optional. The managed identity definition for this resource. At least one identity type is required.')
+param managedIdentities managedIdentitiesType = {
+ systemAssigned: true
+}
+
+@description('Optional. Array of role assignments to create.')
+param roleAssignments roleAssignmentType
+
+@description('Optional. Tags of the disk encryption resource.')
+param tags object?
+
+@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' : null)
+ userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null
+} : null
+
+var builtInRoleNames = {
+
+ Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
+ 'Data Operator for Managed Disks': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '959f8984-c045-4866-89c7-12bf9737be2e')
+ 'Disk Backup Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e5e47e6-65f7-47ef-90b5-e5dd4d455f24')
+ 'Disk Pool Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')
+ 'Disk Restore Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b50d9833-a0cb-478e-945f-707fcc997c13')
+ 'Disk Snapshot Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7efff54f-a5b4-42b5-a1c5-5411624893ce')
+ 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.compute-diskencryptionset.${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 keyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = {
+ name: last(split(keyVaultResourceId, '/'))!
+ scope: resourceGroup(split(keyVaultResourceId, '/')[2], split(keyVaultResourceId, '/')[4])
+
+ resource key 'keys@2021-10-01' existing = {
+ name: keyName
+ }
+}
+
+// Note: This is only enabled for user-assigned identities as the service's system-assigned identity isn't available during its initial deployment
+module keyVaultPermissions 'modules/nested_keyVaultPermissions.bicep' = [for (userAssignedIdentityResourceId, index) in (managedIdentities.?userAssignedResourceIds ?? []): {
+ name: '${uniqueString(deployment().name, location)}-DiskEncrSet-KVPermissions-${index}'
+ params: {
+ keyName: keyName
+ keyVaultResourceId: keyVaultResourceId
+ userAssignedIdentityResourceId: userAssignedIdentityResourceId
+ rbacAuthorizationEnabled: keyVault.properties.enableRbacAuthorization
+ location: location
+ }
+ scope: resourceGroup(split(keyVaultResourceId, '/')[2], split(keyVaultResourceId, '/')[4])
+}]
+
+resource diskEncryptionSet 'Microsoft.Compute/diskEncryptionSets@2022-07-02' = {
+ name: name
+ location: location
+ tags: tags
+ identity: identity
+ properties: {
+ activeKey: {
+ sourceVault: {
+ id: keyVaultResourceId
+ }
+ keyUrl: !empty(keyVersion) ? '${keyVault::key.properties.keyUri}/${keyVersion}' : keyVault::key.properties.keyUriWithVersion
+ }
+ encryptionType: encryptionType
+ federatedClientId: federatedClientId
+ rotationToLatestKeyVersionEnabled: rotationToLatestKeyVersionEnabled
+ }
+ dependsOn: [
+ keyVaultPermissions
+ ]
+}
+
+resource diskEncryptionSet_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in (roleAssignments ?? []): {
+ name: guid(diskEncryptionSet.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: diskEncryptionSet
+}]
+
+resource diskEncryptionSet_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: diskEncryptionSet
+}
+
+@description('The resource ID of the disk encryption set.')
+output resourceId string = diskEncryptionSet.id
+
+@description('The name of the disk encryption set.')
+output name string = diskEncryptionSet.name
+
+@description('The resource group the disk encryption set was deployed into.')
+output resourceGroupName string = resourceGroup().name
+
+@description('The principal ID of the system assigned identity.')
+output systemAssignedMIPrincipalId string = (managedIdentities.?systemAssigned ?? false) && contains(diskEncryptionSet.identity, 'principalId') ? diskEncryptionSet.identity.principalId : ''
+
+@description('The idenities of the disk encryption set.')
+output identities object = diskEncryptionSet.identity
+
+@description('The name of the key vault with the disk encryption key.')
+output keyVaultName string = last(split(keyVaultResourceId, '/'))!
+
+@description('The location the resource was deployed into.')
+output location string = diskEncryptionSet.location
+
+// =============== //
+// 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/compute/disk-encryption-set/main.json b/avm/res/compute/disk-encryption-set/main.json
new file mode 100644
index 0000000000..6912994035
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/main.json
@@ -0,0 +1,738 @@
+{
+ "$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.23.1.45101",
+ "templateHash": "8898450237590542842"
+ },
+ "name": "Disk Encryption Sets",
+ "description": "This module deploys a Disk Encryption Set. The module will attempt to set permissions on the provided Key Vault for any used user-assigned identity.",
+ "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."
+ }
+ }
+ }
+ },
+ "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. The name of the disk encryption set that is being created."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Resource location."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "keyVaultResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of the KeyVault containing the key or secret."
+ }
+ },
+ "keyName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Key URL (with version) pointing to a key or secret in KeyVault."
+ }
+ },
+ "keyVersion": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used."
+ }
+ },
+ "encryptionType": {
+ "type": "string",
+ "defaultValue": "EncryptionAtRestWithPlatformAndCustomerKeys",
+ "allowedValues": [
+ "EncryptionAtRestWithCustomerKey",
+ "EncryptionAtRestWithPlatformAndCustomerKeys"
+ ],
+ "metadata": {
+ "description": "Optional. The type of key used to encrypt the data of the disk. For security reasons, it is recommended to set encryptionType to EncryptionAtRestWithPlatformAndCustomerKeys."
+ }
+ },
+ "federatedClientId": {
+ "type": "string",
+ "defaultValue": "None",
+ "metadata": {
+ "description": "Optional. Multi-tenant application client ID to access key vault in a different tenant. Setting the value to \"None\" will clear the property."
+ }
+ },
+ "rotationToLatestKeyVersionEnabled": {
+ "type": "bool",
+ "defaultValue": false,
+ "metadata": {
+ "description": "Optional. Set this flag to true to enable auto-updating of this disk encryption set to the latest key version."
+ }
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentitiesType",
+ "defaultValue": {
+ "systemAssigned": true
+ },
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource. At least one identity type is required."
+ }
+ },
+ "roleAssignments": {
+ "$ref": "#/definitions/roleAssignmentType",
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the disk encryption resource."
+ }
+ },
+ "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', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]",
+ "builtInRoleNames": {
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "Data Operator for Managed Disks": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '959f8984-c045-4866-89c7-12bf9737be2e')]",
+ "Disk Backup Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e5e47e6-65f7-47ef-90b5-e5dd4d455f24')]",
+ "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]",
+ "Disk Restore Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b50d9833-a0cb-478e-945f-707fcc997c13')]",
+ "Disk Snapshot Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7efff54f-a5b4-42b5-a1c5-5411624893ce')]",
+ "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": {
+ "keyVault::key": {
+ "existing": true,
+ "type": "Microsoft.KeyVault/vaults/keys",
+ "apiVersion": "2021-10-01",
+ "subscriptionId": "[split(parameters('keyVaultResourceId'), '/')[2]]",
+ "resourceGroup": "[split(parameters('keyVaultResourceId'), '/')[4]]",
+ "name": "[format('{0}/{1}', last(split(parameters('keyVaultResourceId'), '/')), parameters('keyName'))]",
+ "dependsOn": [
+ "keyVault"
+ ]
+ },
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2023-07-01",
+ "name": "[format('46d3xbcp.res.compute-diskencryptionset.{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"
+ }
+ }
+ }
+ }
+ },
+ "keyVault": {
+ "existing": true,
+ "type": "Microsoft.KeyVault/vaults",
+ "apiVersion": "2021-10-01",
+ "subscriptionId": "[split(parameters('keyVaultResourceId'), '/')[2]]",
+ "resourceGroup": "[split(parameters('keyVaultResourceId'), '/')[4]]",
+ "name": "[last(split(parameters('keyVaultResourceId'), '/'))]"
+ },
+ "diskEncryptionSet": {
+ "type": "Microsoft.Compute/diskEncryptionSets",
+ "apiVersion": "2022-07-02",
+ "name": "[parameters('name')]",
+ "location": "[parameters('location')]",
+ "tags": "[parameters('tags')]",
+ "identity": "[variables('identity')]",
+ "properties": {
+ "activeKey": {
+ "sourceVault": {
+ "id": "[parameters('keyVaultResourceId')]"
+ },
+ "keyUrl": "[if(not(empty(parameters('keyVersion'))), format('{0}/{1}', reference('keyVault::key').keyUri, parameters('keyVersion')), reference('keyVault::key').keyUriWithVersion)]"
+ },
+ "encryptionType": "[parameters('encryptionType')]",
+ "federatedClientId": "[parameters('federatedClientId')]",
+ "rotationToLatestKeyVersionEnabled": "[parameters('rotationToLatestKeyVersionEnabled')]"
+ },
+ "dependsOn": [
+ "keyVault",
+ "keyVaultPermissions"
+ ]
+ },
+ "diskEncryptionSet_roleAssignments": {
+ "copy": {
+ "name": "diskEncryptionSet_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Compute/diskEncryptionSets/{0}', parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.Compute/diskEncryptionSets', 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": [
+ "diskEncryptionSet"
+ ]
+ },
+ "diskEncryptionSet_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.Compute/diskEncryptionSets/{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": [
+ "diskEncryptionSet"
+ ]
+ },
+ "keyVaultPermissions": {
+ "copy": {
+ "name": "keyVaultPermissions",
+ "count": "[length(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()))]"
+ },
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-DiskEncrSet-KVPermissions-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]",
+ "subscriptionId": "[split(parameters('keyVaultResourceId'), '/')[2]]",
+ "resourceGroup": "[split(parameters('keyVaultResourceId'), '/')[4]]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "keyName": {
+ "value": "[parameters('keyName')]"
+ },
+ "keyVaultResourceId": {
+ "value": "[parameters('keyVaultResourceId')]"
+ },
+ "userAssignedIdentityResourceId": {
+ "value": "[coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray())[copyIndex()]]"
+ },
+ "rbacAuthorizationEnabled": {
+ "value": "[reference('keyVault').enableRbacAuthorization]"
+ },
+ "location": {
+ "value": "[parameters('location')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.23.1.45101",
+ "templateHash": "3415943548000075516"
+ }
+ },
+ "parameters": {
+ "rbacAuthorizationEnabled": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Required. A boolean to specify whether or not the used Key Vault has RBAC authentication enabled or not."
+ }
+ },
+ "userAssignedIdentityResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The resourceID of the User Assigned Identity to assign permissions to."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Resource location."
+ }
+ },
+ "keyVaultResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of the KeyVault containing the key or secret."
+ }
+ },
+ "keyName": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the key to set the permissions for."
+ }
+ }
+ },
+ "resources": [
+ {
+ "condition": "[equals(parameters('rbacAuthorizationEnabled'), true())]",
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.KeyVault/vaults/{0}/keys/{1}', last(split(parameters('keyVaultResourceId'), '/')), parameters('keyName'))]",
+ "name": "[guid(format('msi-{0}-{1}-{2}-Key-Reader-RoleAssignment', resourceId('Microsoft.KeyVault/vaults/keys', last(split(parameters('keyVaultResourceId'), '/')), parameters('keyName')), parameters('location'), parameters('userAssignedIdentityResourceId')))]",
+ "properties": {
+ "principalId": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('userAssignedIdentityResourceId'), '/')[2], split(parameters('userAssignedIdentityResourceId'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(parameters('userAssignedIdentityResourceId'), '/'))), '2023-01-31').principalId]",
+ "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424')]",
+ "principalType": "ServicePrincipal"
+ }
+ },
+ {
+ "condition": "[not(equals(parameters('rbacAuthorizationEnabled'), true()))]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-DiskEncrSet-KVAccessPolicies', uniqueString(deployment().name, parameters('location')))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "keyVaultName": {
+ "value": "[last(split(parameters('keyVaultResourceId'), '/'))]"
+ },
+ "accessPolicies": {
+ "value": [
+ {
+ "tenantId": "[subscription().tenantId]",
+ "objectId": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('userAssignedIdentityResourceId'), '/')[2], split(parameters('userAssignedIdentityResourceId'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(parameters('userAssignedIdentityResourceId'), '/'))), '2023-01-31').principalId]",
+ "permissions": {
+ "keys": [
+ "get",
+ "wrapKey",
+ "unwrapKey"
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "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.23.1.45101",
+ "templateHash": "3185669298547069044"
+ },
+ "name": "Key Vault Access Policies",
+ "description": "This module deploys a Key Vault Access Policy.",
+ "owner": "Azure/module-maintainers"
+ },
+ "definitions": {
+ "accessPoliciesType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "tenantId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The tenant ID that is used for authenticating requests to the key vault."
+ }
+ },
+ "objectId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The object ID of a user, service principal or security group in the tenant for the vault."
+ }
+ },
+ "applicationId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Application ID of the client making request on behalf of a principal."
+ }
+ },
+ "permissions": {
+ "type": "object",
+ "properties": {
+ "keys": {
+ "type": "array",
+ "allowedValues": [
+ "all",
+ "backup",
+ "create",
+ "decrypt",
+ "delete",
+ "encrypt",
+ "get",
+ "getrotationpolicy",
+ "import",
+ "list",
+ "purge",
+ "recover",
+ "release",
+ "restore",
+ "rotate",
+ "setrotationpolicy",
+ "sign",
+ "unwrapKey",
+ "update",
+ "verify",
+ "wrapKey"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Permissions to keys."
+ }
+ },
+ "secrets": {
+ "type": "array",
+ "allowedValues": [
+ "all",
+ "backup",
+ "delete",
+ "get",
+ "list",
+ "purge",
+ "recover",
+ "restore",
+ "set"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Permissions to secrets."
+ }
+ },
+ "certificates": {
+ "type": "array",
+ "allowedValues": [
+ "all",
+ "backup",
+ "create",
+ "delete",
+ "deleteissuers",
+ "get",
+ "getissuers",
+ "import",
+ "list",
+ "listissuers",
+ "managecontacts",
+ "manageissuers",
+ "purge",
+ "recover",
+ "restore",
+ "setissuers",
+ "update"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Permissions to certificates."
+ }
+ },
+ "storage": {
+ "type": "array",
+ "allowedValues": [
+ "all",
+ "backup",
+ "delete",
+ "deletesas",
+ "get",
+ "getsas",
+ "list",
+ "listsas",
+ "purge",
+ "recover",
+ "regeneratekey",
+ "restore",
+ "set",
+ "setsas",
+ "update"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Permissions to storage accounts."
+ }
+ }
+ }
+ }
+ }
+ },
+ "nullable": true
+ }
+ },
+ "parameters": {
+ "keyVaultName": {
+ "type": "string",
+ "metadata": {
+ "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment."
+ }
+ },
+ "accessPolicies": {
+ "$ref": "#/definitions/accessPoliciesType",
+ "metadata": {
+ "description": "Optional. An array of 0 to 16 identities that have access to the key vault. All identities in the array must use the same tenant ID as the key vault's tenant ID."
+ }
+ }
+ },
+ "variables": {
+ "copy": [
+ {
+ "name": "formattedAccessPolicies",
+ "count": "[length(coalesce(parameters('accessPolicies'), createArray()))]",
+ "input": {
+ "applicationId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')], 'applicationId'), '')]",
+ "objectId": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')].objectId]",
+ "permissions": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')].permissions]",
+ "tenantId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')], 'tenantId'), tenant().tenantId)]"
+ }
+ }
+ ]
+ },
+ "resources": {
+ "keyVault": {
+ "existing": true,
+ "type": "Microsoft.KeyVault/vaults",
+ "apiVersion": "2022-07-01",
+ "name": "[parameters('keyVaultName')]"
+ },
+ "policies": {
+ "type": "Microsoft.KeyVault/vaults/accessPolicies",
+ "apiVersion": "2022-07-01",
+ "name": "[format('{0}/{1}', parameters('keyVaultName'), 'add')]",
+ "properties": {
+ "accessPolicies": "[variables('formattedAccessPolicies')]"
+ },
+ "dependsOn": [
+ "keyVault"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the access policies assignment was created in."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the access policies assignment."
+ },
+ "value": "add"
+ },
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the access policies assignment."
+ },
+ "value": "[resourceId('Microsoft.KeyVault/vaults/accessPolicies', parameters('keyVaultName'), 'add')]"
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ "dependsOn": [
+ "keyVault"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the disk encryption set."
+ },
+ "value": "[resourceId('Microsoft.Compute/diskEncryptionSets', parameters('name'))]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the disk encryption set."
+ },
+ "value": "[parameters('name')]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource group the disk encryption set was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "systemAssignedMIPrincipalId": {
+ "type": "string",
+ "metadata": {
+ "description": "The principal ID of the system assigned identity."
+ },
+ "value": "[if(and(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), contains(reference('diskEncryptionSet', '2022-07-02', 'full').identity, 'principalId')), reference('diskEncryptionSet', '2022-07-02', 'full').identity.principalId, '')]"
+ },
+ "identities": {
+ "type": "object",
+ "metadata": {
+ "description": "The idenities of the disk encryption set."
+ },
+ "value": "[reference('diskEncryptionSet', '2022-07-02', 'full').identity]"
+ },
+ "keyVaultName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the key vault with the disk encryption key."
+ },
+ "value": "[last(split(parameters('keyVaultResourceId'), '/'))]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('diskEncryptionSet', '2022-07-02', 'full').location]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/compute/disk-encryption-set/modules/key-vault.vault.access-policy.bicep b/avm/res/compute/disk-encryption-set/modules/key-vault.vault.access-policy.bicep
new file mode 100644
index 0000000000..5968e0f28c
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/modules/key-vault.vault.access-policy.bicep
@@ -0,0 +1,65 @@
+metadata name = 'Key Vault Access Policies'
+metadata description = 'This module deploys a Key Vault Access Policy.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment.')
+param keyVaultName string
+
+@description('Optional. An array of 0 to 16 identities that have access to the key vault. All identities in the array must use the same tenant ID as the key vault\'s tenant ID.')
+param accessPolicies accessPoliciesType
+
+var formattedAccessPolicies = [for accessPolicy in (accessPolicies ?? []): {
+ applicationId: accessPolicy.?applicationId ?? ''
+ objectId: accessPolicy.objectId
+ permissions: accessPolicy.permissions
+ tenantId: accessPolicy.?tenantId ?? tenant().tenantId
+}]
+
+resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
+ name: keyVaultName
+}
+
+resource policies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = {
+ name: 'add'
+ parent: keyVault
+ properties: {
+ accessPolicies: formattedAccessPolicies
+ }
+}
+
+@description('The name of the resource group the access policies assignment was created in.')
+output resourceGroupName string = resourceGroup().name
+
+@description('The name of the access policies assignment.')
+output name string = policies.name
+
+@description('The resource ID of the access policies assignment.')
+output resourceId string = policies.id
+
+// ================ //
+// Definitions //
+// ================ //
+type accessPoliciesType = {
+ @description('Optional. The tenant ID that is used for authenticating requests to the key vault.')
+ tenantId: string?
+
+ @description('Required. The object ID of a user, service principal or security group in the tenant for the vault.')
+ objectId: string
+
+ @description('Optional. Application ID of the client making request on behalf of a principal.')
+ applicationId: string?
+
+ permissions: {
+ @description('Optional. Permissions to keys.')
+ keys: ('all' | 'backup' | 'create' | 'decrypt' | 'delete' | 'encrypt' | 'get' | 'getrotationpolicy' | 'import' | 'list' | 'purge' | 'recover' | 'release' | 'restore' | 'rotate' | 'setrotationpolicy' | 'sign' | 'unwrapKey' | 'update' | 'verify' | 'wrapKey')[]?
+
+ @description('Optional. Permissions to secrets.')
+ secrets: ('all' | 'backup' | 'delete' | 'get' | 'list' | 'purge' | 'recover' | 'restore' | 'set')[]?
+
+ @description('Optional. Permissions to certificates.')
+ certificates: ('all' | 'backup' | 'create' | 'delete' | 'deleteissuers' | 'get' | 'getissuers' | 'import' | 'list' | 'listissuers' | 'managecontacts' | 'manageissuers' | 'purge' | 'recover' | 'restore' | 'setissuers' | 'update')[]?
+
+ @description('Optional. Permissions to storage accounts.')
+ storage: ('all' | 'backup' | 'delete' | 'deletesas' | 'get' | 'getsas' | 'list' | 'listsas' | 'purge' | 'recover' | 'regeneratekey' | 'restore' | 'set' | 'setsas' | 'update')[]?
+ }
+}[]?
diff --git a/avm/res/compute/disk-encryption-set/modules/nested_keyVaultPermissions.bicep b/avm/res/compute/disk-encryption-set/modules/nested_keyVaultPermissions.bicep
new file mode 100644
index 0000000000..af30f69436
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/modules/nested_keyVaultPermissions.bicep
@@ -0,0 +1,66 @@
+@description('Required. A boolean to specify whether or not the used Key Vault has RBAC authentication enabled or not.')
+param rbacAuthorizationEnabled bool = true
+
+@description('Required. The resourceID of the User Assigned Identity to assign permissions to.')
+param userAssignedIdentityResourceId string
+
+@description('Optional. Resource location.')
+param location string = resourceGroup().location
+
+@description('Required. Resource ID of the KeyVault containing the key or secret.')
+param keyVaultResourceId string
+
+@description('Required. Name of the key to set the permissions for.')
+param keyName string
+
+resource keyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = {
+ name: last(split(keyVaultResourceId, '/'))!
+
+ resource key 'keys@2021-10-01' existing = {
+ name: keyName
+ }
+}
+
+resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = {
+ name: last(split(userAssignedIdentityResourceId, '/'))!
+ scope: resourceGroup(split(userAssignedIdentityResourceId, '/')[2], split(userAssignedIdentityResourceId, '/')[4])
+
+}
+
+// =============== //
+// Role Assignment //
+// =============== //
+
+resource keyVaultKeyRBAC 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (rbacAuthorizationEnabled == true) {
+ name: guid('msi-${keyVault::key.id}-${location}-${userAssignedIdentityResourceId}-Key-Reader-RoleAssignment')
+ scope: keyVault::key
+ properties: {
+ principalId: userAssignedIdentity.properties.principalId
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') // Key Vault Crypto User
+ principalType: 'ServicePrincipal'
+ }
+}
+
+// ============= //
+// Access Policy //
+// ============= //
+
+module keyVaultAccessPolicies 'key-vault.vault.access-policy.bicep' = if (rbacAuthorizationEnabled != true) {
+ name: '${uniqueString(deployment().name, location)}-DiskEncrSet-KVAccessPolicies'
+ params: {
+ keyVaultName: last(split(keyVaultResourceId, '/'))!
+ accessPolicies: [
+ {
+ tenantId: subscription().tenantId
+ objectId: userAssignedIdentity.properties.principalId
+ permissions: {
+ keys: [
+ 'get'
+ 'wrapKey'
+ 'unwrapKey'
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/dependencies.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/dependencies.bicep
new file mode 100644
index 0000000000..2024e8644e
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/dependencies.bicep
@@ -0,0 +1,51 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Key Vault to create.')
+param keyVaultName string
+
+@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
+}
+
+resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
+ name: keyVaultName
+ location: location
+ properties: {
+ sku: {
+ family: 'A'
+ name: 'standard'
+ }
+ tenantId: tenant().tenantId
+ enablePurgeProtection: true // Required by disk encryption set
+ softDeleteRetentionInDays: 7
+ enabledForTemplateDeployment: true
+ enabledForDiskEncryption: true
+ enabledForDeployment: true
+ enableRbacAuthorization: false
+ accessPolicies: []
+ }
+
+ resource key 'keys@2022-07-01' = {
+ name: 'keyEncryptionKey'
+ properties: {
+ kty: 'RSA'
+ }
+ }
+}
+
+@description('The resource ID of the created Key Vault.')
+output keyVaultResourceId string = keyVault.id
+
+@description('The name of the created encryption key.')
+output keyName string = keyVault::key.name
+
+@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/compute/disk-encryption-set/tests/e2e/accessPolicies/main.test.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/main.test.bicep
new file mode 100644
index 0000000000..467a612a95
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/main.test.bicep
@@ -0,0 +1,90 @@
+targetScope = 'subscription'
+
+metadata name = 'Using Key Vault with the Access Policy permission model'
+metadata description = 'This instance uses a Key Vault with the Access Policy permission model. If no permissions on the Key Vault are set, the module attempts to add the permissions for you.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-compute.diskencryptionsets-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'cdesap'
+
+@description('Generated. Used as a basis for unique resource names.')
+param baseTime string = utcNow('u')
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total)
+ keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ location: location
+ keyName: nestedDependencies.outputs.keyName
+ keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId
+ 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: true
+ userAssignedResourceIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}]
diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/defaults/dependencies.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/defaults/dependencies.bicep
new file mode 100644
index 0000000000..22b4344b68
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/tests/e2e/defaults/dependencies.bicep
@@ -0,0 +1,37 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Key Vault to create.')
+param keyVaultName string
+
+resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
+ name: keyVaultName
+ location: location
+ properties: {
+ sku: {
+ family: 'A'
+ name: 'standard'
+ }
+ tenantId: tenant().tenantId
+ enablePurgeProtection: true // Required by disk encryption set
+ softDeleteRetentionInDays: 7
+ enabledForTemplateDeployment: true
+ enabledForDiskEncryption: true
+ enabledForDeployment: true
+ enableRbacAuthorization: true
+ accessPolicies: []
+ }
+
+ resource key 'keys@2022-07-01' = {
+ name: 'keyEncryptionKey'
+ properties: {
+ kty: 'RSA'
+ }
+ }
+}
+
+@description('The resource ID of the created Key Vault.')
+output keyVaultResourceId string = keyVault.id
+
+@description('The name of the created encryption key.')
+output keyName string = keyVault::key.name
diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/defaults/main.test.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/defaults/main.test.bicep
new file mode 100644
index 0000000000..e412b1a6b1
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/tests/e2e/defaults/main.test.bicep
@@ -0,0 +1,61 @@
+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}-compute.diskencryptionsets-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'cdesmin'
+
+@description('Generated. Used as a basis for unique resource names.')
+param baseTime string = utcNow('u')
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total)
+ keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ keyName: nestedDependencies.outputs.keyName
+ keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId
+ location: location
+ }
+}]
diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/max/dependencies.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/max/dependencies.bicep
new file mode 100644
index 0000000000..62321ebe98
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/tests/e2e/max/dependencies.bicep
@@ -0,0 +1,51 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Key Vault to create.')
+param keyVaultName string
+
+@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
+}
+
+resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
+ name: keyVaultName
+ location: location
+ properties: {
+ sku: {
+ family: 'A'
+ name: 'standard'
+ }
+ tenantId: tenant().tenantId
+ enablePurgeProtection: true // Required by disk encryption set
+ softDeleteRetentionInDays: 7
+ enabledForTemplateDeployment: true
+ enabledForDiskEncryption: true
+ enabledForDeployment: true
+ enableRbacAuthorization: true
+ accessPolicies: []
+ }
+
+ resource key 'keys@2022-07-01' = {
+ name: 'keyEncryptionKey'
+ properties: {
+ kty: 'RSA'
+ }
+ }
+}
+
+@description('The resource ID of the created Key Vault.')
+output keyVaultResourceId string = keyVault.id
+
+@description('The name of the created encryption key.')
+output keyName string = keyVault::key.name
+
+@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/compute/disk-encryption-set/tests/e2e/max/main.test.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/max/main.test.bicep
new file mode 100644
index 0000000000..463a300683
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/tests/e2e/max/main.test.bicep
@@ -0,0 +1,93 @@
+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}-compute.diskencryptionsets-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'cdesmax'
+
+@description('Generated. Used as a basis for unique resource names.')
+param baseTime string = utcNow('u')
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total)
+ keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ location: location
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ keyName: nestedDependencies.outputs.keyName
+ keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId
+ 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: {
+ userAssignedResourceIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}]
diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..a893a4e5de
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,48 @@
+@description('Optional. The location to deploy to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Key Vault to create.')
+param keyVaultName string
+
+@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
+}
+
+resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
+ name: keyVaultName
+ location: location
+ properties: {
+ sku: {
+ family: 'A'
+ name: 'standard'
+ }
+ tenantId: tenant().tenantId
+ enablePurgeProtection: true // Required by disk encryption set
+ softDeleteRetentionInDays: 7
+ enabledForTemplateDeployment: true
+ enabledForDiskEncryption: true
+ enabledForDeployment: true
+ enableRbacAuthorization: true
+ accessPolicies: []
+ }
+
+ resource key 'keys@2022-07-01' = {
+ name: 'keyEncryptionKey'
+ properties: {
+ kty: 'RSA'
+ }
+ }
+}
+
+@description('The resource ID of the created Key Vault.')
+output keyVaultResourceId string = keyVault.id
+
+@description('The name of the created encryption key.')
+output keyName string = keyVault::key.name
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/main.test.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/main.test.bicep
new file mode 100644
index 0000000000..a269813c1d
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,72 @@
+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}-compute.diskencryptionsets-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param location 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 = 'cdeswaf'
+
+@description('Generated. Used as a basis for unique resource names.')
+param baseTime string = utcNow('u')
+
+@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: location
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-nestedDependencies'
+ params: {
+ // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total)
+ keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ location: location
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ location: location
+ keyName: nestedDependencies.outputs.keyName
+ keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId
+ managedIdentities: {
+ userAssignedResourceIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Environment: 'Non-Prod'
+ Role: 'DeploymentValidation'
+ }
+ }
+}]
diff --git a/avm/res/compute/disk-encryption-set/version.json b/avm/res/compute/disk-encryption-set/version.json
new file mode 100644
index 0000000000..83083db694
--- /dev/null
+++ b/avm/res/compute/disk-encryption-set/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
+ "version": "0.1",
+ "pathFilters": [
+ "./main.json"
+ ]
+}
\ No newline at end of file
via Bicep module
+
+```bicep
+module natGateway 'br/public:avm/res/network/nat-gateway: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": "nngmin001"
+ },
+ // Non-required parameters
+ "location": {
+ "value": "via Bicep module
+
+```bicep
+module natGateway 'br/public:avm/res/network/nat-gateway: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": "nngmax001"
+ },
+ // Non-required parameters
+ "location": {
+ "value": "via Bicep module
+
+```bicep
+module natGateway 'br/public:avm/res/network/nat-gateway: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": "nngcprx001"
+ },
+ // Non-required parameters
+ "location": {
+ "value": "via Bicep module
+
+```bicep
+module natGateway 'br/public:avm/res/network/nat-gateway: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": "nngwaf001"
+ },
+ // Non-required parameters
+ "location": {
+ "value": "via Bicep module
+
+```bicep
+module vpnGateway 'br/public:avm/res/network/vpn-gateway: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": "vpngmin001"
+ },
+ "virtualHubResourceId": {
+ "value": "via Bicep module
+
+```bicep
+module vpnGateway 'br/public:avm/res/network/vpn-gateway: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": "vpngmax001"
+ },
+ "virtualHubResourceId": {
+ "value": "via Bicep module
+
+```bicep
+module vpnGateway 'br/public:avm/res/network/vpn-gateway: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": "vpngwaf001"
+ },
+ "virtualHubResourceId": {
+ "value": "Parameter JSON format
+
+```json
+"bgpSettings": {
+ "asn": 65515,
+ "peerWeight": 0,
+ "bgpPeeringAddresses": [
+ {
+ "ipconfigurationId": "Instance0",
+ "defaultBgpIpAddresses": [
+ "10.0.0.12"
+ ],
+ "customBgpIpAddresses": [],
+ "tunnelIpAddresses": [
+ "20.84.35.53",
+ "10.0.0.4"
+ ]
+ },
+ {
+ "ipconfigurationId": "Instance1",
+ "defaultBgpIpAddresses": [
+ "10.0.0.13"
+ ],
+ "customBgpIpAddresses": [],
+ "tunnelIpAddresses": [
+ "20.84.34.225",
+ "10.0.0.5"
+ ]
+ }
+ ]
+}
+```
+
+Bicep format
+
+```bicep
+bgpSettings: {
+ asn: 65515
+ peerWeight: 0
+ bgpPeeringAddresses: [
+ {
+ ipconfigurationId: 'Instance0'
+ defaultBgpIpAddresses: [
+ '10.0.0.12'
+ ]
+ customBgpIpAddresses: []
+ tunnelIpAddresses: [
+ '20.84.35.53'
+ '10.0.0.4'
+ ]
+ }
+ {
+ ipconfigurationId: 'Instance1'
+ defaultBgpIpAddresses: [
+ '10.0.0.13'
+ ]
+ customBgpIpAddresses: []
+ tunnelIpAddresses: [
+ '20.84.34.225'
+ '10.0.0.5'
+ ]
+ }
+ ]
+}
+```
+
+Parameter JSON format
+
+```json
+"routingConfiguration": {
+ "associatedRouteTable": {
+ "id": "/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/SampleVirtualHub/hubRouteTables/defaultRouteTable"
+ },
+ "propagatedRouteTables": {
+ "labels": [
+ "default"
+ ],
+ "ids": [
+ {
+ "id": "/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/SampleVirtualHub/hubRouteTables/defaultRouteTable"
+ }
+ ]
+ },
+ "vnetRoutes": {
+ "staticRoutes": []
+ }
+}
+```
+
+Bicep format
+
+```bicep
+routingConfiguration: {
+ associatedRouteTable: {
+ id: '/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/SampleVirtualHub/hubRouteTables/defaultRouteTable'
+ }
+ propagatedRouteTables: {
+ labels: [
+ 'default'
+ ]
+ ids: [
+ {
+ id: '/subscriptions/[[subscriptionId]]/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/SampleVirtualHub/hubRouteTables/defaultRouteTable'
+ }
+ ]
+ }
+ vnetRoutes: {
+ staticRoutes: []
+ }
+}
+```
+
+via Bicep module
+
+```bicep
+module staticSite 'br/public:avm/res/web/static-site: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": "wssmin001"
+ },
+ // Non-required parameters
+ "location": {
+ "value": "via Bicep module
+
+```bicep
+module staticSite 'br/public:avm/res/web/static-site: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": "wssmax001"
+ },
+ // Non-required parameters
+ "allowConfigFileUpdates": {
+ "value": true
+ },
+ "appSettings": {
+ "value": {
+ "foo": "bar",
+ "setting": 1
+ }
+ },
+ "enterpriseGradeCdnStatus": {
+ "value": "Disabled"
+ },
+ "functionAppSettings": {
+ "value": {
+ "foo": "bar",
+ "setting": 1
+ }
+ },
+ "linkedBackend": {
+ "value": {
+ "resourceId": "via Bicep module
+
+```bicep
+module staticSite 'br/public:avm/res/web/static-site: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": "wsswaf001"
+ },
+ // Non-required parameters
+ "allowConfigFileUpdates": {
+ "value": true
+ },
+ "appSettings": {
+ "value": {
+ "foo": "bar",
+ "setting": 1
+ }
+ },
+ "enterpriseGradeCdnStatus": {
+ "value": "Disabled"
+ },
+ "functionAppSettings": {
+ "value": {
+ "foo": "bar",
+ "setting": 1
+ }
+ },
+ "linkedBackend": {
+ "value": {
+ "resourceId": "via Bicep module
+
+```bicep
+module diskEncryptionSet 'br/public:avm/res/compute/disk-encryption-set: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
+ "keyName": {
+ "value": "via Bicep module
+
+```bicep
+module diskEncryptionSet 'br/public:avm/res/compute/disk-encryption-set: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
+ "keyName": {
+ "value": "via Bicep module
+
+```bicep
+module diskEncryptionSet 'br/public:avm/res/compute/disk-encryption-set: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
+ "keyName": {
+ "value": "via Bicep module
+
+```bicep
+module diskEncryptionSet 'br/public:avm/res/compute/disk-encryption-set: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
+ "keyName": {
+ "value": "