From c92a8c2bb229a777ae6812c06a09dfcb4fa28cb4 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Fri, 6 Sep 2024 09:48:26 +0200 Subject: [PATCH] feat: Subet - Several improvements (#3105) ## Description - Added UDTs for Peering & Subnet - Removed subnet property from VNET deployment - Upgraded API versions - Added IPv6 test and made required changes - Adjusted diverse properties (to simplify interface) - Added WAF-aligned prefix to peering name > **Beware:** This PR contains breaking changes due to the interface updates Closes - #2020 - #2689 - #1025 - #2860 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.virtual-network](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network.yml/badge.svg?branch=users%2Falsehr%2FvnetUpdate&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --------- Co-authored-by: John --- avm/res/network/virtual-network/README.md | 575 +++++++++++++++--- avm/res/network/virtual-network/main.bicep | 258 ++++---- avm/res/network/virtual-network/main.json | 546 +++++++++++++---- .../network/virtual-network/subnet/README.md | 97 +-- .../network/virtual-network/subnet/main.bicep | 65 +- .../network/virtual-network/subnet/main.json | 89 ++- .../tests/e2e/ipv6/main.test.bicep | 61 ++ .../tests/e2e/max/main.test.bicep | 21 +- .../tests/e2e/vnetPeering/main.test.bicep | 5 +- .../tests/e2e/waf-aligned/main.test.bicep | 25 +- avm/res/network/virtual-network/version.json | 2 +- .../virtual-network-peering/README.md | 12 +- .../virtual-network-peering/main.bicep | 10 +- .../virtual-network-peering/main.json | 12 +- 14 files changed, 1307 insertions(+), 471 deletions(-) create mode 100644 avm/res/network/virtual-network/tests/e2e/ipv6/main.test.bicep diff --git a/avm/res/network/virtual-network/README.md b/avm/res/network/virtual-network/README.md index b053c4a5be..4b6fa95d30 100644 --- a/avm/res/network/virtual-network/README.md +++ b/avm/res/network/virtual-network/README.md @@ -18,9 +18,9 @@ This module deploys a Virtual Network (vNet). | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/virtualNetworks` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks) | -| `Microsoft.Network/virtualNetworks/subnets` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks/subnets) | -| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks/virtualNetworkPeerings) | +| `Microsoft.Network/virtualNetworks` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks) | +| `Microsoft.Network/virtualNetworks/subnets` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks/subnets) | +| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks/virtualNetworkPeerings) | ## Usage examples @@ -31,9 +31,10 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/network/virtual-network:`. - [Using only defaults](#example-1-using-only-defaults) -- [Using large parameter set](#example-2-using-large-parameter-set) -- [Deploying a bi-directional peering](#example-3-deploying-a-bi-directional-peering) -- [WAF-aligned](#example-4-waf-aligned) +- [Using an IPv6 address space](#example-2-using-an-ipv6-address-space) +- [Using large parameter set](#example-3-using-large-parameter-set) +- [Deploying a bi-directional peering](#example-4-deploying-a-bi-directional-peering) +- [WAF-aligned](#example-5-waf-aligned) ### Example 1: _Using only defaults_ @@ -91,7 +92,85 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = {

-### Example 2: _Using large parameter set_ +### Example 2: _Using an IPv6 address space_ + +This instance deploys the module using an IPv6 address space. + + +

+ +via Bicep module + +```bicep +module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { + name: 'virtualNetworkDeployment' + params: { + // Required parameters + addressPrefixes: [ + '10.0.0.0/21' + 'fd00:592b:3014::/64' + ] + name: 'nvnipv6001' + // Non-required parameters + location: '' + subnets: [ + { + addressPrefixes: [ + '10.0.0.0/24' + 'fd00:592b:3014::/64' + ] + name: 'ipv6-subnet' + } + ] + } +} +``` + +
+

+ +

+ +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 + "addressPrefixes": { + "value": [ + "10.0.0.0/21", + "fd00:592b:3014::/64" + ] + }, + "name": { + "value": "nvnipv6001" + }, + // Non-required parameters + "location": { + "value": "" + }, + "subnets": { + "value": [ + { + "addressPrefixes": [ + "10.0.0.0/24", + "fd00:592b:3014::/64" + ], + "name": "ipv6-subnet" + } + ] + } + } +} +``` + +
+

+ +### Example 3: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -181,24 +260,13 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { ] routeTableResourceId: '' serviceEndpoints: [ - { - service: 'Microsoft.Storage' - } - { - service: 'Microsoft.Sql' - } + 'Microsoft.Sql' + 'Microsoft.Storage' ] } { addressPrefix: '' - delegations: [ - { - name: 'netappDel' - properties: { - serviceName: 'Microsoft.Netapp/volumes' - } - } - ] + delegation: 'Microsoft.Netapp/volumes' name: 'az-subnet-x-002' networkSecurityGroupResourceId: '' } @@ -341,24 +409,13 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { ], "routeTableResourceId": "", "serviceEndpoints": [ - { - "service": "Microsoft.Storage" - }, - { - "service": "Microsoft.Sql" - } + "Microsoft.Sql", + "Microsoft.Storage" ] }, { "addressPrefix": "", - "delegations": [ - { - "name": "netappDel", - "properties": { - "serviceName": "Microsoft.Netapp/volumes" - } - } - ], + "delegation": "Microsoft.Netapp/volumes", "name": "az-subnet-x-002", "networkSecurityGroupResourceId": "" }, @@ -401,7 +458,7 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = {

-### Example 3: _Deploying a bi-directional peering_ +### Example 4: _Deploying a bi-directional peering_ This instance deploys the module with both an inbound and outbound peering. @@ -430,7 +487,7 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { remotePeeringAllowVirtualNetworkAccess: true remotePeeringEnabled: true remotePeeringName: 'customName' - remoteVirtualNetworkId: '' + remoteVirtualNetworkResourceId: '' useRemoteGateways: false } ] @@ -493,7 +550,7 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { "remotePeeringAllowVirtualNetworkAccess": true, "remotePeeringEnabled": true, "remotePeeringName": "customName", - "remoteVirtualNetworkId": "", + "remoteVirtualNetworkResourceId": "", "useRemoteGateways": false } ] @@ -529,7 +586,7 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = {

-### Example 4: _WAF-aligned_ +### Example 5: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Well-Architected Framework. @@ -568,10 +625,6 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { ] flowTimeoutInMinutes: 20 location: '' - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } subnets: [ { addressPrefix: '' @@ -590,24 +643,13 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { ] routeTableResourceId: '' serviceEndpoints: [ - { - service: 'Microsoft.Storage' - } - { - service: 'Microsoft.Sql' - } + 'Microsoft.Sql' + 'Microsoft.Storage' ] } { addressPrefix: '' - delegations: [ - { - name: 'netappDel' - properties: { - serviceName: 'Microsoft.Netapp/volumes' - } - } - ] + delegation: 'Microsoft.Netapp/volumes' name: 'az-subnet-x-002' networkSecurityGroupResourceId: '' } @@ -687,12 +729,6 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { "location": { "value": "" }, - "lock": { - "value": { - "kind": "CanNotDelete", - "name": "myCustomLockName" - } - }, "subnets": { "value": [ { @@ -712,24 +748,13 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { ], "routeTableResourceId": "", "serviceEndpoints": [ - { - "service": "Microsoft.Storage" - }, - { - "service": "Microsoft.Sql" - } + "Microsoft.Sql", + "Microsoft.Storage" ] }, { "addressPrefix": "", - "delegations": [ - { - "name": "netappDel", - "properties": { - "serviceName": "Microsoft.Netapp/volumes" - } - } - ], + "delegation": "Microsoft.Netapp/volumes", "name": "az-subnet-x-002", "networkSecurityGroupResourceId": "" }, @@ -782,13 +807,15 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`dnsServers`](#parameter-dnsservers) | array | DNS Servers associated to the Virtual Network. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`enableVmProtection`](#parameter-enablevmprotection) | bool | Indicates if VM protection is enabled for all the subnets in the virtual network. | | [`flowTimeoutInMinutes`](#parameter-flowtimeoutinminutes) | int | The flow timeout in minutes for the Virtual Network, which is used to enable connection tracking for intra-VM flows. Possible values are between 4 and 30 minutes. Default value 0 will set the property to null. | | [`location`](#parameter-location) | string | Location for all resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | -| [`peerings`](#parameter-peerings) | array | Virtual Network Peerings configurations. | +| [`peerings`](#parameter-peerings) | array | Virtual Network Peering configurations. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`subnets`](#parameter-subnets) | array | An Array of subnets to deploy to the Virtual Network. | | [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`virtualNetworkBgpCommunity`](#parameter-virtualnetworkbgpcommunity) | string | The BGP community associated with the virtual network. | | [`vnetEncryption`](#parameter-vnetencryption) | bool | Indicates if encryption is enabled on virtual network and if VM without encryption is allowed in encrypted VNet. Requires the EnableVNetEncryption feature to be registered for the subscription and a supported region to use this property. | | [`vnetEncryptionEnforcement`](#parameter-vnetencryptionenforcement) | string | If the encrypted VNet allows VM that does not support encryption. Can only be used when vnetEncryption is enabled. | @@ -812,7 +839,6 @@ Resource ID of the DDoS protection plan to assign the VNET to. If it's left blan - Required: No - Type: string -- Default: `''` ### Parameter: `diagnosticSettings` @@ -966,7 +992,6 @@ DNS Servers associated to the Virtual Network. - Required: No - Type: array -- Default: `[]` ### Parameter: `enableTelemetry` @@ -976,6 +1001,13 @@ Enable/Disable usage telemetry for module. - Type: bool - Default: `True` +### Parameter: `enableVmProtection` + +Indicates if VM protection is enabled for all the subnets in the virtual network. + +- Required: No +- Type: bool + ### Parameter: `flowTimeoutInMinutes` The flow timeout in minutes for the Virtual Network, which is used to enable connection tracking for intra-VM flows. Possible values are between 4 and 30 minutes. Default value 0 will set the property to null. @@ -1030,11 +1062,132 @@ Specify the name of lock. ### Parameter: `peerings` -Virtual Network Peerings configurations. +Virtual Network Peering configurations. - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`remoteVirtualNetworkResourceId`](#parameter-peeringsremotevirtualnetworkresourceid) | string | The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowForwardedTraffic`](#parameter-peeringsallowforwardedtraffic) | bool | Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true. | +| [`allowGatewayTransit`](#parameter-peeringsallowgatewaytransit) | bool | If gateway links can be used in remote virtual networking to link to this virtual network. Default is false. | +| [`allowVirtualNetworkAccess`](#parameter-peeringsallowvirtualnetworkaccess) | bool | Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true. | +| [`doNotVerifyRemoteGateways`](#parameter-peeringsdonotverifyremotegateways) | bool | Do not verify the provisioning state of the remote gateway. Default is true. | +| [`name`](#parameter-peeringsname) | string | The Name of VNET Peering resource. If not provided, default value will be peer-localVnetName-remoteVnetName. | +| [`remotePeeringAllowForwardedTraffic`](#parameter-peeringsremotepeeringallowforwardedtraffic) | bool | Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true. | +| [`remotePeeringAllowGatewayTransit`](#parameter-peeringsremotepeeringallowgatewaytransit) | bool | If gateway links can be used in remote virtual networking to link to this virtual network. Default is false. | +| [`remotePeeringAllowVirtualNetworkAccess`](#parameter-peeringsremotepeeringallowvirtualnetworkaccess) | bool | Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true. | +| [`remotePeeringDoNotVerifyRemoteGateways`](#parameter-peeringsremotepeeringdonotverifyremotegateways) | bool | Do not verify the provisioning state of the remote gateway. Default is true. | +| [`remotePeeringEnabled`](#parameter-peeringsremotepeeringenabled) | bool | Deploy the outbound and the inbound peering. | +| [`remotePeeringName`](#parameter-peeringsremotepeeringname) | string | The name of the VNET Peering resource in the remove Virtual Network. If not provided, default value will be peer-remoteVnetName-localVnetName. | +| [`remotePeeringUseRemoteGateways`](#parameter-peeringsremotepeeringuseremotegateways) | bool | If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false. | +| [`useRemoteGateways`](#parameter-peeringsuseremotegateways) | bool | If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false. | + +### Parameter: `peerings.remoteVirtualNetworkResourceId` + +The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID. + +- Required: Yes +- Type: string + +### Parameter: `peerings.allowForwardedTraffic` + +Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true. + +- Required: No +- Type: bool + +### Parameter: `peerings.allowGatewayTransit` + +If gateway links can be used in remote virtual networking to link to this virtual network. Default is false. + +- Required: No +- Type: bool + +### Parameter: `peerings.allowVirtualNetworkAccess` + +Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true. + +- Required: No +- Type: bool + +### Parameter: `peerings.doNotVerifyRemoteGateways` + +Do not verify the provisioning state of the remote gateway. Default is true. + +- Required: No +- Type: bool + +### Parameter: `peerings.name` + +The Name of VNET Peering resource. If not provided, default value will be peer-localVnetName-remoteVnetName. + +- Required: No +- Type: string + +### Parameter: `peerings.remotePeeringAllowForwardedTraffic` + +Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true. + +- Required: No +- Type: bool + +### Parameter: `peerings.remotePeeringAllowGatewayTransit` + +If gateway links can be used in remote virtual networking to link to this virtual network. Default is false. + +- Required: No +- Type: bool + +### Parameter: `peerings.remotePeeringAllowVirtualNetworkAccess` + +Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true. + +- Required: No +- Type: bool + +### Parameter: `peerings.remotePeeringDoNotVerifyRemoteGateways` + +Do not verify the provisioning state of the remote gateway. Default is true. + +- Required: No +- Type: bool + +### Parameter: `peerings.remotePeeringEnabled` + +Deploy the outbound and the inbound peering. + +- Required: No +- Type: bool + +### Parameter: `peerings.remotePeeringName` + +The name of the VNET Peering resource in the remove Virtual Network. If not provided, default value will be peer-remoteVnetName-localVnetName. + +- Required: No +- Type: string + +### Parameter: `peerings.remotePeeringUseRemoteGateways` + +If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false. + +- Required: No +- Type: bool + +### Parameter: `peerings.useRemoteGateways` + +If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false. + +- Required: No +- Type: bool ### Parameter: `roleAssignments` @@ -1139,7 +1292,254 @@ An Array of subnets to deploy to the Virtual Network. - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-subnetsname) | string | The Name of the subnet resource. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`addressPrefix`](#parameter-subnetsaddressprefix) | string | The address prefix for the subnet. Required if `addressPrefixes` is empty. | +| [`addressPrefixes`](#parameter-subnetsaddressprefixes) | array | List of address prefixes for the subnet. Required if `addressPrefix` is empty. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`applicationGatewayIPConfigurations`](#parameter-subnetsapplicationgatewayipconfigurations) | array | Application gateway IP configurations of virtual network resource. | +| [`defaultOutboundAccess`](#parameter-subnetsdefaultoutboundaccess) | bool | Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet. | +| [`delegation`](#parameter-subnetsdelegation) | string | The delegation to enable on the subnet. | +| [`natGatewayResourceId`](#parameter-subnetsnatgatewayresourceid) | string | The resource ID of the NAT Gateway to use for the subnet. | +| [`networkSecurityGroupResourceId`](#parameter-subnetsnetworksecuritygroupresourceid) | string | The resource ID of the network security group to assign to the subnet. | +| [`privateEndpointNetworkPolicies`](#parameter-subnetsprivateendpointnetworkpolicies) | string | enable or disable apply network policies on private endpoint in the subnet. | +| [`privateLinkServiceNetworkPolicies`](#parameter-subnetsprivatelinkservicenetworkpolicies) | string | enable or disable apply network policies on private link service in the subnet. | +| [`roleAssignments`](#parameter-subnetsroleassignments) | array | Array of role assignments to create. | +| [`routeTableResourceId`](#parameter-subnetsroutetableresourceid) | string | The resource ID of the route table to assign to the subnet. | +| [`serviceEndpointPolicies`](#parameter-subnetsserviceendpointpolicies) | array | An array of service endpoint policies. | +| [`serviceEndpoints`](#parameter-subnetsserviceendpoints) | array | The service endpoints to enable on the subnet. | +| [`sharingScope`](#parameter-subnetssharingscope) | string | Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty. | + +### Parameter: `subnets.name` + +The Name of the subnet resource. + +- Required: Yes +- Type: string + +### Parameter: `subnets.addressPrefix` + +The address prefix for the subnet. Required if `addressPrefixes` is empty. + +- Required: No +- Type: string + +### Parameter: `subnets.addressPrefixes` + +List of address prefixes for the subnet. Required if `addressPrefix` is empty. + +- Required: No +- Type: array + +### Parameter: `subnets.applicationGatewayIPConfigurations` + +Application gateway IP configurations of virtual network resource. + +- Required: No +- Type: array + +### Parameter: `subnets.defaultOutboundAccess` + +Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet. + +- Required: No +- Type: bool + +### Parameter: `subnets.delegation` + +The delegation to enable on the subnet. + +- Required: No +- Type: string + +### Parameter: `subnets.natGatewayResourceId` + +The resource ID of the NAT Gateway to use for the subnet. + +- Required: No +- Type: string + +### Parameter: `subnets.networkSecurityGroupResourceId` + +The resource ID of the network security group to assign to the subnet. + +- Required: No +- Type: string + +### Parameter: `subnets.privateEndpointNetworkPolicies` + +enable or disable apply network policies on private endpoint in the subnet. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '' + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `subnets.privateLinkServiceNetworkPolicies` + +enable or disable apply network policies on private link service in the subnet. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '' + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `subnets.roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-subnetsroleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-subnetsroleassignmentsroledefinitionidorname) | 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-subnetsroleassignmentscondition) | 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-subnetsroleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-subnetsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-subnetsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-subnetsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-subnetsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `subnets.roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `subnets.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: `subnets.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: `subnets.roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `subnets.roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `subnets.roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `subnets.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `subnets.roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `subnets.routeTableResourceId` + +The resource ID of the route table to assign to the subnet. + +- Required: No +- Type: string + +### Parameter: `subnets.serviceEndpointPolicies` + +An array of service endpoint policies. + +- Required: No +- Type: array + +### Parameter: `subnets.serviceEndpoints` + +The service endpoints to enable on the subnet. + +- Required: No +- Type: array + +### Parameter: `subnets.sharingScope` + +Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'DelegatedServices' + 'Tenant' + ] + ``` ### Parameter: `tags` @@ -1148,6 +1548,13 @@ Tags of the resource. - Required: No - Type: object +### Parameter: `virtualNetworkBgpCommunity` + +The BGP community associated with the virtual network. + +- Required: No +- Type: string + ### Parameter: `vnetEncryption` Indicates if encryption is enabled on virtual network and if VM without encryption is allowed in encrypted VNet. Requires the EnableVNetEncryption feature to be registered for the subscription and a supported region to use this property. @@ -1195,7 +1602,7 @@ As the virtual network peering array allows you to deploy not only a one-way but | Parameter Name | Type | Default Value | Possible Values | Description | | :-- | :-- | :-- | :-- | :-- | | `remotePeeringEnabled` | bool | `false` | | Optional. Set to true to also deploy the reverse peering for the configured remote virtual networks to the local network | -| `remotePeeringName` | string | `'${last(split(peering.remoteVirtualNetworkId, '/'))}-${name}'` | | Optional. The Name of Vnet Peering resource. If not provided, default value will be - | +| `remotePeeringName` | string | `'${last(split(peering.remoteVirtualNetworkId, '/'))}-${name}'` | | Optional. The Name of VNET Peering resource. If not provided, default value will be - | | `remotePeeringAllowForwardedTraffic` | bool | `true` | | Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. | | `remotePeeringAllowGatewayTransit` | bool | `false` | | Optional. If gateway links can be used in remote virtual networking to link to this virtual network. | | `remotePeeringAllowVirtualNetworkAccess` | bool | `true` | | Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. | diff --git a/avm/res/network/virtual-network/main.bicep b/avm/res/network/virtual-network/main.bicep index 0ca96b3195..8df104e242 100644 --- a/avm/res/network/virtual-network/main.bicep +++ b/avm/res/network/virtual-network/main.bicep @@ -11,17 +11,20 @@ param location string = resourceGroup().location @description('Required. An Array of 1 or more IP Address Prefixes for the Virtual Network.') param addressPrefixes array +@description('Optional. The BGP community associated with the virtual network.') +param virtualNetworkBgpCommunity string? + @description('Optional. An Array of subnets to deploy to the Virtual Network.') -param subnets array = [] +param subnets subnetType[]? @description('Optional. DNS Servers associated to the Virtual Network.') -param dnsServers array = [] +param dnsServers string[]? @description('Optional. Resource ID of the DDoS protection plan to assign the VNET to. If it\'s left blank, DDoS protection will not be configured. If it\'s provided, the VNET created by this template will be attached to the referenced DDoS protection plan. The DDoS protection plan can exist in the same or in a different subscription.') -param ddosProtectionPlanResourceId string = '' +param ddosProtectionPlanResourceId string? -@description('Optional. Virtual Network Peerings configurations.') -param peerings array = [] +@description('Optional. Virtual Network Peering configurations.') +param peerings peeringType[]? @description('Optional. Indicates if encryption is enabled on virtual network and if VM without encryption is allowed in encrypted VNet. Requires the EnableVNetEncryption feature to be registered for the subscription and a supported region to use this property.') param vnetEncryption bool = false @@ -52,6 +55,9 @@ param tags object? @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true +@description('Optional. Indicates if VM protection is enabled for all the subnets in the virtual network.') +param enableVmProtection bool? + var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') 'Network Contributor': subscriptionResourceId( @@ -104,7 +110,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-11-01' = { +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' = { name: name location: location tags: tags @@ -112,6 +118,11 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-11-01' = { addressSpace: { addressPrefixes: addressPrefixes } + bgpCommunities: !empty(virtualNetworkBgpCommunity) + ? { + virtualNetworkCommunity: virtualNetworkBgpCommunity! + } + : null ddosProtectionPlan: !empty(ddosProtectionPlanResourceId) ? { id: ddosProtectionPlanResourceId @@ -130,135 +141,69 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-11-01' = { } : null flowTimeoutInMinutes: flowTimeoutInMinutes != 0 ? flowTimeoutInMinutes : null - subnets: [ - for subnet in subnets: { - name: subnet.name - properties: { - addressPrefix: subnet.addressPrefix - addressPrefixes: contains(subnet, 'addressPrefixes') ? subnet.addressPrefixes : [] - applicationGatewayIPConfigurations: contains(subnet, 'applicationGatewayIPConfigurations') - ? subnet.applicationGatewayIPConfigurations - : [] - delegations: contains(subnet, 'delegations') ? subnet.delegations : [] - ipAllocations: contains(subnet, 'ipAllocations') ? subnet.ipAllocations : [] - natGateway: contains(subnet, 'natGatewayResourceId') && !empty(subnet.natGatewayResourceId) - ? { - id: subnet.natGatewayResourceId - } - : null - networkSecurityGroup: contains(subnet, 'networkSecurityGroupResourceId') && !empty(subnet.networkSecurityGroupResourceId) - ? { - id: subnet.networkSecurityGroupResourceId - } - : null - privateEndpointNetworkPolicies: contains(subnet, 'privateEndpointNetworkPolicies') - ? subnet.privateEndpointNetworkPolicies - : null - privateLinkServiceNetworkPolicies: contains(subnet, 'privateLinkServiceNetworkPolicies') - ? subnet.privateLinkServiceNetworkPolicies - : null - routeTable: contains(subnet, 'routeTableResourceId') && !empty(subnet.routeTableResourceId) - ? { - id: subnet.routeTableResourceId - } - : null - serviceEndpoints: contains(subnet, 'serviceEndpoints') ? subnet.serviceEndpoints : [] - serviceEndpointPolicies: contains(subnet, 'serviceEndpointPolicies') ? subnet.serviceEndpointPolicies : [] - } - } - ] + enableVmProtection: enableVmProtection } } -//NOTE Start: ------------------------------------ -// The below module (virtualNetwork_subnets) is a duplicate of the child resource (subnets) defined in the parent module (virtualNetwork). -// The reason it exists so that deployment validation tests can be performed on the child module (subnets), in case that module needed to be deployed alone outside of this template. -// The reason for duplication is due to the current design for the (virtualNetworks) resource from Azure, where if the child module (subnets) does not exist within it, causes -// an issue, where the child resource (subnets) gets all of its properties removed, hence not as 'idempotent' as it should be. See https://github.com/Azure/azure-quickstart-templates/issues/2786 for more details. -// You can safely remove the below child module (virtualNetwork_subnets) in your consumption of the module (virtualNetworks) to reduce the template size and duplication. -//NOTE End : ------------------------------------ - @batchSize(1) module virtualNetwork_subnets 'subnet/main.bicep' = [ - for (subnet, index) in subnets: { + for (subnet, index) in (subnets ?? []): { name: '${uniqueString(deployment().name, location)}-subnet-${index}' params: { virtualNetworkName: virtualNetwork.name name: subnet.name - addressPrefix: subnet.addressPrefix - addressPrefixes: contains(subnet, 'addressPrefixes') ? subnet.addressPrefixes : [] - applicationGatewayIPConfigurations: contains(subnet, 'applicationGatewayIPConfigurations') - ? subnet.applicationGatewayIPConfigurations - : [] - delegations: contains(subnet, 'delegations') ? subnet.delegations : [] - ipAllocations: contains(subnet, 'ipAllocations') ? subnet.ipAllocations : [] - natGatewayResourceId: contains(subnet, 'natGatewayResourceId') ? subnet.natGatewayResourceId : '' - networkSecurityGroupResourceId: contains(subnet, 'networkSecurityGroupResourceId') - ? subnet.networkSecurityGroupResourceId - : '' - privateEndpointNetworkPolicies: contains(subnet, 'privateEndpointNetworkPolicies') - ? subnet.privateEndpointNetworkPolicies - : '' - privateLinkServiceNetworkPolicies: contains(subnet, 'privateLinkServiceNetworkPolicies') - ? subnet.privateLinkServiceNetworkPolicies - : '' - roleAssignments: contains(subnet, 'roleAssignments') ? subnet.roleAssignments : [] - routeTableResourceId: contains(subnet, 'routeTableResourceId') ? subnet.routeTableResourceId : '' - serviceEndpointPolicies: contains(subnet, 'serviceEndpointPolicies') ? subnet.serviceEndpointPolicies : [] - serviceEndpoints: contains(subnet, 'serviceEndpoints') ? subnet.serviceEndpoints : [] + addressPrefix: subnet.?addressPrefix + addressPrefixes: subnet.?addressPrefixes + applicationGatewayIPConfigurations: subnet.?applicationGatewayIPConfigurations + delegation: subnet.?delegation + natGatewayResourceId: subnet.?natGatewayResourceId + networkSecurityGroupResourceId: subnet.?networkSecurityGroupResourceId + privateEndpointNetworkPolicies: subnet.?privateEndpointNetworkPolicies + privateLinkServiceNetworkPolicies: subnet.?privateLinkServiceNetworkPolicies + roleAssignments: subnet.?roleAssignments + routeTableResourceId: subnet.?routeTableResourceId + serviceEndpointPolicies: subnet.?serviceEndpointPolicies + serviceEndpoints: subnet.?serviceEndpoints + defaultOutboundAccess: subnet.?defaultOutboundAccess + sharingScope: subnet.?sharingScope } } ] // Local to Remote peering module virtualNetwork_peering_local 'virtual-network-peering/main.bicep' = [ - for (peering, index) in peerings: { + for (peering, index) in (peerings ?? []): { name: '${uniqueString(deployment().name, location)}-virtualNetworkPeering-local-${index}' params: { localVnetName: virtualNetwork.name - remoteVirtualNetworkId: peering.remoteVirtualNetworkId - name: contains(peering, 'name') ? peering.name : '${name}-${last(split(peering.remoteVirtualNetworkId, '/'))}' - allowForwardedTraffic: contains(peering, 'allowForwardedTraffic') ? peering.allowForwardedTraffic : true - allowGatewayTransit: contains(peering, 'allowGatewayTransit') ? peering.allowGatewayTransit : false - allowVirtualNetworkAccess: contains(peering, 'allowVirtualNetworkAccess') - ? peering.allowVirtualNetworkAccess - : true - doNotVerifyRemoteGateways: contains(peering, 'doNotVerifyRemoteGateways') - ? peering.doNotVerifyRemoteGateways - : true - useRemoteGateways: contains(peering, 'useRemoteGateways') ? peering.useRemoteGateways : false + remoteVirtualNetworkResourceId: peering.remoteVirtualNetworkResourceId + name: peering.?name + allowForwardedTraffic: peering.?allowForwardedTraffic + allowGatewayTransit: peering.?allowGatewayTransit + allowVirtualNetworkAccess: peering.?allowVirtualNetworkAccess + doNotVerifyRemoteGateways: peering.?doNotVerifyRemoteGateways + useRemoteGateways: peering.?useRemoteGateways } } ] // Remote to local peering (reverse) module virtualNetwork_peering_remote 'virtual-network-peering/main.bicep' = [ - for (peering, index) in peerings: if (contains(peering, 'remotePeeringEnabled') - ? peering.remotePeeringEnabled == true - : false) { + for (peering, index) in (peerings ?? []): if (peering.?remotePeeringEnabled ?? false) { name: '${uniqueString(deployment().name, location)}-virtualNetworkPeering-remote-${index}' - scope: resourceGroup(split(peering.remoteVirtualNetworkId, '/')[2], split(peering.remoteVirtualNetworkId, '/')[4]) + scope: resourceGroup( + split(peering.remoteVirtualNetworkResourceId, '/')[2], + split(peering.remoteVirtualNetworkResourceId, '/')[4] + ) params: { - localVnetName: last(split(peering.remoteVirtualNetworkId, '/'))! - remoteVirtualNetworkId: virtualNetwork.id - name: contains(peering, 'remotePeeringName') - ? peering.remotePeeringName - : '${last(split(peering.remoteVirtualNetworkId, '/'))}-${name}' - allowForwardedTraffic: contains(peering, 'remotePeeringAllowForwardedTraffic') - ? peering.remotePeeringAllowForwardedTraffic - : true - allowGatewayTransit: contains(peering, 'remotePeeringAllowGatewayTransit') - ? peering.remotePeeringAllowGatewayTransit - : false - allowVirtualNetworkAccess: contains(peering, 'remotePeeringAllowVirtualNetworkAccess') - ? peering.remotePeeringAllowVirtualNetworkAccess - : true - doNotVerifyRemoteGateways: contains(peering, 'remotePeeringDoNotVerifyRemoteGateways') - ? peering.remotePeeringDoNotVerifyRemoteGateways - : true - useRemoteGateways: contains(peering, 'remotePeeringUseRemoteGateways') - ? peering.remotePeeringUseRemoteGateways - : false + localVnetName: last(split(peering.remoteVirtualNetworkResourceId, '/')) + remoteVirtualNetworkResourceId: virtualNetwork.id + name: peering.?remotePeeringName + allowForwardedTraffic: peering.?remotePeeringAllowForwardedTraffic + allowGatewayTransit: peering.?remotePeeringAllowGatewayTransit + allowVirtualNetworkAccess: peering.?remotePeeringAllowVirtualNetworkAccess + doNotVerifyRemoteGateways: peering.?remotePeeringDoNotVerifyRemoteGateways + useRemoteGateways: peering.?remotePeeringUseRemoteGateways } } ] @@ -329,11 +274,11 @@ output resourceId string = virtualNetwork.id output name string = virtualNetwork.name @description('The names of the deployed subnets.') -output subnetNames array = [for subnet in subnets: subnet.name] +output subnetNames array = [for (subnet, index) in (subnets ?? []): virtualNetwork_subnets[index].outputs.name] @description('The resource IDs of the deployed subnets.') output subnetResourceIds array = [ - for subnet in subnets: az.resourceId('Microsoft.Network/virtualNetworks/subnets', name, subnet.name) + for (subnet, index) in (subnets ?? []): virtualNetwork_subnets[index].outputs.resourceId ] @description('The location the resource was deployed into.') @@ -420,3 +365,94 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type peeringType = { + @description('Optional. The Name of VNET Peering resource. If not provided, default value will be peer-localVnetName-remoteVnetName.') + name: string? + + @description('Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID.') + remoteVirtualNetworkResourceId: string + + @description('Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true.') + allowForwardedTraffic: bool? + + @description('Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false.') + allowGatewayTransit: bool? + + @description('Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true.') + allowVirtualNetworkAccess: bool? + + @description('Optional. Do not verify the provisioning state of the remote gateway. Default is true.') + doNotVerifyRemoteGateways: bool? + + @description('Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false.') + useRemoteGateways: bool? + + @description('Optional. Deploy the outbound and the inbound peering.') + remotePeeringEnabled: bool? + + @description('Optional. The name of the VNET Peering resource in the remove Virtual Network. If not provided, default value will be peer-remoteVnetName-localVnetName.') + remotePeeringName: string? + + @description('Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true.') + remotePeeringAllowForwardedTraffic: bool? + + @description('Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false.') + remotePeeringAllowGatewayTransit: bool? + + @description('Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true.') + remotePeeringAllowVirtualNetworkAccess: bool? + + @description('Optional. Do not verify the provisioning state of the remote gateway. Default is true.') + remotePeeringDoNotVerifyRemoteGateways: bool? + + @description('Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false.') + remotePeeringUseRemoteGateways: bool? +} + +type subnetType = { + @description('Required. The Name of the subnet resource.') + name: string + + @description('Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty.') + addressPrefix: string? + + @description('Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty.') + addressPrefixes: string[]? + + @description('Optional. Application gateway IP configurations of virtual network resource.') + applicationGatewayIPConfigurations: object[]? + + @description('Optional. The delegation to enable on the subnet.') + delegation: string? + + @description('Optional. The resource ID of the NAT Gateway to use for the subnet.') + natGatewayResourceId: string? + + @description('Optional. The resource ID of the network security group to assign to the subnet.') + networkSecurityGroupResourceId: string? + + @description('Optional. enable or disable apply network policies on private endpoint in the subnet.') + privateEndpointNetworkPolicies: ('Disabled' | 'Enabled' | '')? + + @description('Optional. enable or disable apply network policies on private link service in the subnet.') + privateLinkServiceNetworkPolicies: ('Disabled' | 'Enabled' | '')? + + @description('Optional. Array of role assignments to create.') + roleAssignments: roleAssignmentType + + @description('Optional. The resource ID of the route table to assign to the subnet.') + routeTableResourceId: string? + + @description('Optional. An array of service endpoint policies.') + serviceEndpointPolicies: object[]? + + @description('Optional. The service endpoints to enable on the subnet.') + serviceEndpoints: string[]? + + @description('Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet.') + defaultOutboundAccess: bool? + + @description('Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty.') + sharingScope: ('DelegatedServices' | 'Tenant')? +} diff --git a/avm/res/network/virtual-network/main.json b/avm/res/network/virtual-network/main.json index 615fb8fcb0..564c4a742c 100644 --- a/avm/res/network/virtual-network/main.json +++ b/avm/res/network/virtual-network/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "14023358203441656898" + "templateHash": "5706783765271345287" }, "name": "Virtual Networks", "description": "This module deploys a Virtual Network (vNet).", @@ -230,6 +230,242 @@ } }, "nullable": true + }, + "peeringType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be peer-localVnetName-remoteVnetName." + } + }, + "remoteVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true." + } + }, + "allowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false." + } + }, + "remotePeeringEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Deploy the outbound and the inbound peering." + } + }, + "remotePeeringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the VNET Peering resource in the remove Virtual Network. If not provided, default value will be peer-remoteVnetName-localVnetName." + } + }, + "remotePeeringAllowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true." + } + }, + "remotePeeringAllowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "remotePeeringAllowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true." + } + }, + "remotePeeringDoNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "remotePeeringUseRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false." + } + } + } + }, + "subnetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "allowedValues": [ + "", + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "allowedValues": [ + "", + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private link service in the subnet." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." + } + } + } } }, "parameters": { @@ -252,32 +488,48 @@ "description": "Required. An Array of 1 or more IP Address Prefixes for the Virtual Network." } }, + "virtualNetworkBgpCommunity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The BGP community associated with the virtual network." + } + }, "subnets": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/subnetType" + }, + "nullable": true, "metadata": { "description": "Optional. An Array of subnets to deploy to the Virtual Network." } }, "dnsServers": { "type": "array", - "defaultValue": [], + "items": { + "type": "string" + }, + "nullable": true, "metadata": { "description": "Optional. DNS Servers associated to the Virtual Network." } }, "ddosProtectionPlanResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. Resource ID of the DDoS protection plan to assign the VNET to. If it's left blank, DDoS protection will not be configured. If it's provided, the VNET created by this template will be attached to the referenced DDoS protection plan. The DDoS protection plan can exist in the same or in a different subscription." } }, "peerings": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/peeringType" + }, + "nullable": true, "metadata": { - "description": "Optional. Virtual Network Peerings configurations." + "description": "Optional. Virtual Network Peering configurations." } }, "vnetEncryption": { @@ -337,6 +589,13 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } + }, + "enableVmProtection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicates if VM protection is enabled for all the subnets in the virtual network." + } } }, "variables": { @@ -379,42 +638,21 @@ }, "virtualNetwork": { "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", "properties": { - "copy": [ - { - "name": "subnets", - "count": "[length(parameters('subnets'))]", - "input": { - "name": "[parameters('subnets')[copyIndex('subnets')].name]", - "properties": { - "addressPrefix": "[parameters('subnets')[copyIndex('subnets')].addressPrefix]", - "addressPrefixes": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'addressPrefixes'), parameters('subnets')[copyIndex('subnets')].addressPrefixes, createArray())]", - "applicationGatewayIPConfigurations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'applicationGatewayIPConfigurations'), parameters('subnets')[copyIndex('subnets')].applicationGatewayIPConfigurations, createArray())]", - "delegations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'delegations'), parameters('subnets')[copyIndex('subnets')].delegations, createArray())]", - "ipAllocations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'ipAllocations'), parameters('subnets')[copyIndex('subnets')].ipAllocations, createArray())]", - "natGateway": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'natGatewayResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].natGatewayResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].natGatewayResourceId), null())]", - "networkSecurityGroup": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'networkSecurityGroupResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].networkSecurityGroupResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].networkSecurityGroupResourceId), null())]", - "privateEndpointNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'privateEndpointNetworkPolicies'), parameters('subnets')[copyIndex('subnets')].privateEndpointNetworkPolicies, null())]", - "privateLinkServiceNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'privateLinkServiceNetworkPolicies'), parameters('subnets')[copyIndex('subnets')].privateLinkServiceNetworkPolicies, null())]", - "routeTable": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'routeTableResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].routeTableResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].routeTableResourceId), null())]", - "serviceEndpoints": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'serviceEndpoints'), parameters('subnets')[copyIndex('subnets')].serviceEndpoints, createArray())]", - "serviceEndpointPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'serviceEndpointPolicies'), parameters('subnets')[copyIndex('subnets')].serviceEndpointPolicies, createArray())]" - } - } - } - ], "addressSpace": { "addressPrefixes": "[parameters('addressPrefixes')]" }, + "bgpCommunities": "[if(not(empty(parameters('virtualNetworkBgpCommunity'))), createObject('virtualNetworkCommunity', parameters('virtualNetworkBgpCommunity')), null())]", "ddosProtectionPlan": "[if(not(empty(parameters('ddosProtectionPlanResourceId'))), createObject('id', parameters('ddosProtectionPlanResourceId')), null())]", "dhcpOptions": "[if(not(empty(parameters('dnsServers'))), createObject('dnsServers', array(parameters('dnsServers'))), null())]", "enableDdosProtection": "[not(empty(parameters('ddosProtectionPlanResourceId')))]", "encryption": "[if(equals(parameters('vnetEncryption'), true()), createObject('enabled', parameters('vnetEncryption'), 'enforcement', parameters('vnetEncryptionEnforcement')), null())]", - "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]" + "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]", + "enableVmProtection": "[parameters('enableVmProtection')]" } }, "virtualNetwork_lock": { @@ -497,7 +735,7 @@ "virtualNetwork_subnets": { "copy": { "name": "virtualNetwork_subnets", - "count": "[length(parameters('subnets'))]", + "count": "[length(coalesce(parameters('subnets'), createArray()))]", "mode": "serial", "batchSize": 1 }, @@ -514,23 +752,50 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('subnets')[copyIndex()].name]" + "value": "[coalesce(parameters('subnets'), createArray())[copyIndex()].name]" }, "addressPrefix": { - "value": "[parameters('subnets')[copyIndex()].addressPrefix]" + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefix')]" }, - "addressPrefixes": "[if(contains(parameters('subnets')[copyIndex()], 'addressPrefixes'), createObject('value', parameters('subnets')[copyIndex()].addressPrefixes), createObject('value', createArray()))]", - "applicationGatewayIPConfigurations": "[if(contains(parameters('subnets')[copyIndex()], 'applicationGatewayIPConfigurations'), createObject('value', parameters('subnets')[copyIndex()].applicationGatewayIPConfigurations), createObject('value', createArray()))]", - "delegations": "[if(contains(parameters('subnets')[copyIndex()], 'delegations'), createObject('value', parameters('subnets')[copyIndex()].delegations), createObject('value', createArray()))]", - "ipAllocations": "[if(contains(parameters('subnets')[copyIndex()], 'ipAllocations'), createObject('value', parameters('subnets')[copyIndex()].ipAllocations), createObject('value', createArray()))]", - "natGatewayResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'natGatewayResourceId'), createObject('value', parameters('subnets')[copyIndex()].natGatewayResourceId), createObject('value', ''))]", - "networkSecurityGroupResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'networkSecurityGroupResourceId'), createObject('value', parameters('subnets')[copyIndex()].networkSecurityGroupResourceId), createObject('value', ''))]", - "privateEndpointNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'privateEndpointNetworkPolicies'), createObject('value', parameters('subnets')[copyIndex()].privateEndpointNetworkPolicies), createObject('value', ''))]", - "privateLinkServiceNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'privateLinkServiceNetworkPolicies'), createObject('value', parameters('subnets')[copyIndex()].privateLinkServiceNetworkPolicies), createObject('value', ''))]", - "roleAssignments": "[if(contains(parameters('subnets')[copyIndex()], 'roleAssignments'), createObject('value', parameters('subnets')[copyIndex()].roleAssignments), createObject('value', createArray()))]", - "routeTableResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'routeTableResourceId'), createObject('value', parameters('subnets')[copyIndex()].routeTableResourceId), createObject('value', ''))]", - "serviceEndpointPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'serviceEndpointPolicies'), createObject('value', parameters('subnets')[copyIndex()].serviceEndpointPolicies), createObject('value', createArray()))]", - "serviceEndpoints": "[if(contains(parameters('subnets')[copyIndex()], 'serviceEndpoints'), createObject('value', parameters('subnets')[copyIndex()].serviceEndpoints), createObject('value', createArray()))]" + "addressPrefixes": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefixes')]" + }, + "applicationGatewayIPConfigurations": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'applicationGatewayIPConfigurations')]" + }, + "delegation": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'delegation')]" + }, + "natGatewayResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'natGatewayResourceId')]" + }, + "networkSecurityGroupResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'networkSecurityGroupResourceId')]" + }, + "privateEndpointNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateEndpointNetworkPolicies')]" + }, + "privateLinkServiceNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateLinkServiceNetworkPolicies')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "routeTableResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'routeTableResourceId')]" + }, + "serviceEndpointPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpointPolicies')]" + }, + "serviceEndpoints": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpoints')]" + }, + "defaultOutboundAccess": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'defaultOutboundAccess')]" + }, + "sharingScope": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'sharingScope')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -540,7 +805,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17087074157977382192" + "templateHash": "9295856151641467142" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet.", @@ -625,7 +890,7 @@ "name": { "type": "string", "metadata": { - "description": "Optional. The Name of the subnet resource." + "description": "Requird. The Name of the subnet resource." } }, "virtualNetworkName": { @@ -636,41 +901,45 @@ }, "addressPrefix": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The address prefix for the subnet." + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." } }, "networkSecurityGroupResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID of the network security group to assign to the subnet." } }, "routeTableResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID of the route table to assign to the subnet." } }, "serviceEndpoints": { "type": "array", + "items": { + "type": "string" + }, "defaultValue": [], "metadata": { "description": "Optional. The service endpoints to enable on the subnet." } }, - "delegations": { - "type": "array", - "defaultValue": [], + "delegation": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. The delegations to enable on the subnet." + "description": "Optional. The delegation to enable on the subnet." } }, "natGatewayResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." } @@ -684,7 +953,7 @@ "" ], "metadata": { - "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + "description": "Optional. Enable or disable apply network policies on private endpoint in the subnet." } }, "privateLinkServiceNetworkPolicies": { @@ -696,28 +965,42 @@ "" ], "metadata": { - "description": "Optional. enable or disable apply network policies on private link service in the subnet." + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." } }, "addressPrefixes": { "type": "array", - "defaultValue": [], + "items": { + "type": "string" + }, + "nullable": true, "metadata": { - "description": "Optional. List of address prefixes for the subnet." + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." } }, - "applicationGatewayIPConfigurations": { - "type": "array", - "defaultValue": [], + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, "metadata": { - "description": "Optional. Application gateway IP configurations of virtual network resource." + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." } }, - "ipAllocations": { + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." + } + }, + "applicationGatewayIPConfigurations": { "type": "array", "defaultValue": [], "metadata": { - "description": "Optional. Array of IpAllocation which reference this subnet." + "description": "Optional. Application gateway IP configurations of virtual network resource." } }, "serviceEndpointPolicies": { @@ -755,26 +1038,35 @@ "virtualNetwork": { "existing": true, "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[parameters('virtualNetworkName')]" }, "subnet": { "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", "properties": { + "copy": [ + { + "name": "serviceEndpoints", + "count": "[length(parameters('serviceEndpoints'))]", + "input": { + "service": "[parameters('serviceEndpoints')[copyIndex('serviceEndpoints')]]" + } + } + ], "addressPrefix": "[parameters('addressPrefix')]", + "addressPrefixes": "[parameters('addressPrefixes')]", "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", - "serviceEndpoints": "[parameters('serviceEndpoints')]", - "delegations": "[parameters('delegations')]", + "delegations": "[if(not(empty(parameters('delegation'))), createArray(createObject('name', parameters('delegation'), 'properties', createObject('serviceName', parameters('delegation')))), createArray())]", "privateEndpointNetworkPolicies": "[if(not(empty(parameters('privateEndpointNetworkPolicies'))), parameters('privateEndpointNetworkPolicies'), null())]", "privateLinkServiceNetworkPolicies": "[if(not(empty(parameters('privateLinkServiceNetworkPolicies'))), parameters('privateLinkServiceNetworkPolicies'), null())]", - "addressPrefixes": "[parameters('addressPrefixes')]", "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", - "ipAllocations": "[parameters('ipAllocations')]", - "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]" + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]", + "defaultOutboundAccess": "[parameters('defaultOutboundAccess')]", + "sharingScope": "[parameters('sharingScope')]" }, "dependsOn": [ "virtualNetwork" @@ -825,19 +1117,19 @@ }, "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" }, - "subnetAddressPrefix": { + "addressPrefix": { "type": "string", "metadata": { "description": "The address prefix for the subnet." }, - "value": "[reference('subnet').addressPrefix]" + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefix'), '')]" }, - "subnetAddressPrefixes": { + "addressPrefixes": { "type": "array", "metadata": { "description": "List of address prefixes for the subnet." }, - "value": "[if(not(empty(parameters('addressPrefixes'))), reference('subnet').addressPrefixes, createArray())]" + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefixes'), createArray())]" } } } @@ -849,7 +1141,7 @@ "virtualNetwork_peering_local": { "copy": { "name": "virtualNetwork_peering_local", - "count": "[length(parameters('peerings'))]" + "count": "[length(coalesce(parameters('peerings'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -863,15 +1155,27 @@ "localVnetName": { "value": "[parameters('name')]" }, - "remoteVirtualNetworkId": { - "value": "[parameters('peerings')[copyIndex()].remoteVirtualNetworkId]" + "remoteVirtualNetworkResourceId": { + "value": "[coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId]" }, - "name": "[if(contains(parameters('peerings')[copyIndex()], 'name'), createObject('value', parameters('peerings')[copyIndex()].name), createObject('value', format('{0}-{1}', parameters('name'), last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')))))]", - "allowForwardedTraffic": "[if(contains(parameters('peerings')[copyIndex()], 'allowForwardedTraffic'), createObject('value', parameters('peerings')[copyIndex()].allowForwardedTraffic), createObject('value', true()))]", - "allowGatewayTransit": "[if(contains(parameters('peerings')[copyIndex()], 'allowGatewayTransit'), createObject('value', parameters('peerings')[copyIndex()].allowGatewayTransit), createObject('value', false()))]", - "allowVirtualNetworkAccess": "[if(contains(parameters('peerings')[copyIndex()], 'allowVirtualNetworkAccess'), createObject('value', parameters('peerings')[copyIndex()].allowVirtualNetworkAccess), createObject('value', true()))]", - "doNotVerifyRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'doNotVerifyRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].doNotVerifyRemoteGateways), createObject('value', true()))]", - "useRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'useRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].useRemoteGateways), createObject('value', false()))]" + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'name')]" + }, + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowForwardedTraffic')]" + }, + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowGatewayTransit')]" + }, + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowVirtualNetworkAccess')]" + }, + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'doNotVerifyRemoteGateways')]" + }, + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'useRemoteGateways')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -880,7 +1184,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1725716682351191048" + "templateHash": "5206620163504251868" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering.", @@ -889,9 +1193,9 @@ "parameters": { "name": { "type": "string", - "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." } }, "localVnetName": { @@ -900,7 +1204,7 @@ "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." } }, - "remoteVirtualNetworkId": { + "remoteVirtualNetworkResourceId": { "type": "string", "metadata": { "description": "Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID." @@ -945,7 +1249,7 @@ "resources": [ { "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", "properties": { "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", @@ -954,7 +1258,7 @@ "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", "useRemoteGateways": "[parameters('useRemoteGateways')]", "remoteVirtualNetwork": { - "id": "[parameters('remoteVirtualNetworkId')]" + "id": "[parameters('remoteVirtualNetworkResourceId')]" } } } @@ -991,14 +1295,14 @@ "virtualNetwork_peering_remote": { "copy": { "name": "virtualNetwork_peering_remote", - "count": "[length(parameters('peerings'))]" + "count": "[length(coalesce(parameters('peerings'), createArray()))]" }, - "condition": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringEnabled'), equals(parameters('peerings')[copyIndex()].remotePeeringEnabled, true()), false())]", + "condition": "[coalesce(tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringEnabled'), false())]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-virtualNetworkPeering-remote-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')[2]]", - "resourceGroup": "[split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')[4]]", + "subscriptionId": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[2]]", + "resourceGroup": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1006,17 +1310,29 @@ "mode": "Incremental", "parameters": { "localVnetName": { - "value": "[last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/'))]" + "value": "[last(split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/'))]" }, - "remoteVirtualNetworkId": { + "remoteVirtualNetworkResourceId": { "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" }, - "name": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringName'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringName), createObject('value', format('{0}-{1}', last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')), parameters('name'))))]", - "allowForwardedTraffic": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowForwardedTraffic'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowForwardedTraffic), createObject('value', true()))]", - "allowGatewayTransit": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowGatewayTransit'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowGatewayTransit), createObject('value', false()))]", - "allowVirtualNetworkAccess": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowVirtualNetworkAccess), createObject('value', true()))]", - "doNotVerifyRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringDoNotVerifyRemoteGateways), createObject('value', true()))]", - "useRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringUseRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringUseRemoteGateways), createObject('value', false()))]" + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringName')]" + }, + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowForwardedTraffic')]" + }, + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowGatewayTransit')]" + }, + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess')]" + }, + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways')]" + }, + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringUseRemoteGateways')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1025,7 +1341,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1725716682351191048" + "templateHash": "5206620163504251868" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering.", @@ -1034,9 +1350,9 @@ "parameters": { "name": { "type": "string", - "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." } }, "localVnetName": { @@ -1045,7 +1361,7 @@ "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." } }, - "remoteVirtualNetworkId": { + "remoteVirtualNetworkResourceId": { "type": "string", "metadata": { "description": "Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID." @@ -1090,7 +1406,7 @@ "resources": [ { "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", "properties": { "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", @@ -1099,7 +1415,7 @@ "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", "useRemoteGateways": "[parameters('useRemoteGateways')]", "remoteVirtualNetwork": { - "id": "[parameters('remoteVirtualNetworkId')]" + "id": "[parameters('remoteVirtualNetworkResourceId')]" } } } @@ -1162,8 +1478,8 @@ "description": "The names of the deployed subnets." }, "copy": { - "count": "[length(parameters('subnets'))]", - "input": "[parameters('subnets')[copyIndex()].name]" + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.name.value]" } }, "subnetResourceIds": { @@ -1172,8 +1488,8 @@ "description": "The resource IDs of the deployed subnets." }, "copy": { - "count": "[length(parameters('subnets'))]", - "input": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('name'), parameters('subnets')[copyIndex()].name)]" + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.resourceId.value]" } }, "location": { @@ -1181,7 +1497,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('virtualNetwork', '2023-11-01', 'full').location]" + "value": "[reference('virtualNetwork', '2024-01-01', 'full').location]" } } } \ No newline at end of file diff --git a/avm/res/network/virtual-network/subnet/README.md b/avm/res/network/virtual-network/subnet/README.md index 246833a9db..e6269f8046 100644 --- a/avm/res/network/virtual-network/subnet/README.md +++ b/avm/res/network/virtual-network/subnet/README.md @@ -14,91 +14,82 @@ This module deploys a Virtual Network Subnet. | 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.Network/virtualNetworks/subnets` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks/subnets) | +| `Microsoft.Network/virtualNetworks/subnets` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks/subnets) | ## Parameters -**Required parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`addressPrefix`](#parameter-addressprefix) | string | The address prefix for the subnet. | - **Conditional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | +| [`addressPrefix`](#parameter-addressprefix) | string | The address prefix for the subnet. Required if `addressPrefixes` is empty. | +| [`addressPrefixes`](#parameter-addressprefixes) | array | List of address prefixes for the subnet. Required if `addressPrefix` is empty. | | [`virtualNetworkName`](#parameter-virtualnetworkname) | string | The name of the parent virtual network. Required if the template is used in a standalone deployment. | **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`addressPrefixes`](#parameter-addressprefixes) | array | List of address prefixes for the subnet. | | [`applicationGatewayIPConfigurations`](#parameter-applicationgatewayipconfigurations) | array | Application gateway IP configurations of virtual network resource. | -| [`delegations`](#parameter-delegations) | array | The delegations to enable on the subnet. | -| [`ipAllocations`](#parameter-ipallocations) | array | Array of IpAllocation which reference this subnet. | -| [`name`](#parameter-name) | string | The Name of the subnet resource. | +| [`defaultOutboundAccess`](#parameter-defaultoutboundaccess) | bool | Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet. | +| [`delegation`](#parameter-delegation) | string | The delegation to enable on the subnet. | | [`natGatewayResourceId`](#parameter-natgatewayresourceid) | string | The resource ID of the NAT Gateway to use for the subnet. | | [`networkSecurityGroupResourceId`](#parameter-networksecuritygroupresourceid) | string | The resource ID of the network security group to assign to the subnet. | -| [`privateEndpointNetworkPolicies`](#parameter-privateendpointnetworkpolicies) | string | enable or disable apply network policies on private endpoint in the subnet. | -| [`privateLinkServiceNetworkPolicies`](#parameter-privatelinkservicenetworkpolicies) | string | enable or disable apply network policies on private link service in the subnet. | +| [`privateEndpointNetworkPolicies`](#parameter-privateendpointnetworkpolicies) | string | Enable or disable apply network policies on private endpoint in the subnet. | +| [`privateLinkServiceNetworkPolicies`](#parameter-privatelinkservicenetworkpolicies) | string | Enable or disable apply network policies on private link service in the subnet. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`routeTableResourceId`](#parameter-routetableresourceid) | string | The resource ID of the route table to assign to the subnet. | | [`serviceEndpointPolicies`](#parameter-serviceendpointpolicies) | array | An array of service endpoint policies. | | [`serviceEndpoints`](#parameter-serviceendpoints) | array | The service endpoints to enable on the subnet. | +| [`sharingScope`](#parameter-sharingscope) | string | Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty. | -### Parameter: `addressPrefix` - -The address prefix for the subnet. +**Requird parameters** -- Required: Yes -- Type: string +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The Name of the subnet resource. | -### Parameter: `virtualNetworkName` +### Parameter: `addressPrefix` -The name of the parent virtual network. Required if the template is used in a standalone deployment. +The address prefix for the subnet. Required if `addressPrefixes` is empty. -- Required: Yes +- Required: No - Type: string ### Parameter: `addressPrefixes` -List of address prefixes for the subnet. +List of address prefixes for the subnet. Required if `addressPrefix` is empty. - Required: No - Type: array -- Default: `[]` -### Parameter: `applicationGatewayIPConfigurations` +### Parameter: `virtualNetworkName` -Application gateway IP configurations of virtual network resource. +The name of the parent virtual network. Required if the template is used in a standalone deployment. -- Required: No -- Type: array -- Default: `[]` +- Required: Yes +- Type: string -### Parameter: `delegations` +### Parameter: `applicationGatewayIPConfigurations` -The delegations to enable on the subnet. +Application gateway IP configurations of virtual network resource. - Required: No - Type: array - Default: `[]` -### Parameter: `ipAllocations` +### Parameter: `defaultOutboundAccess` -Array of IpAllocation which reference this subnet. +Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet. - Required: No -- Type: array -- Default: `[]` +- Type: bool -### Parameter: `name` +### Parameter: `delegation` -The Name of the subnet resource. +The delegation to enable on the subnet. -- Required: Yes +- Required: No - Type: string ### Parameter: `natGatewayResourceId` @@ -107,7 +98,6 @@ The resource ID of the NAT Gateway to use for the subnet. - Required: No - Type: string -- Default: `''` ### Parameter: `networkSecurityGroupResourceId` @@ -115,11 +105,10 @@ The resource ID of the network security group to assign to the subnet. - Required: No - Type: string -- Default: `''` ### Parameter: `privateEndpointNetworkPolicies` -enable or disable apply network policies on private endpoint in the subnet. +Enable or disable apply network policies on private endpoint in the subnet. - Required: No - Type: string @@ -135,7 +124,7 @@ enable or disable apply network policies on private endpoint in the subnet. ### Parameter: `privateLinkServiceNetworkPolicies` -enable or disable apply network policies on private link service in the subnet. +Enable or disable apply network policies on private link service in the subnet. - Required: No - Type: string @@ -252,7 +241,6 @@ The resource ID of the route table to assign to the subnet. - Required: No - Type: string -- Default: `''` ### Parameter: `serviceEndpointPolicies` @@ -270,15 +258,36 @@ The service endpoints to enable on the subnet. - Type: array - Default: `[]` +### Parameter: `sharingScope` + +Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'DelegatedServices' + 'Tenant' + ] + ``` + +### Parameter: `name` + +The Name of the subnet resource. + +- Required: Yes +- Type: string + ## Outputs | Output | Type | Description | | :-- | :-- | :-- | +| `addressPrefix` | string | The address prefix for the subnet. | +| `addressPrefixes` | array | List of address prefixes for the subnet. | | `name` | string | The name of the virtual network peering. | | `resourceGroupName` | string | The resource group the virtual network peering was deployed into. | | `resourceId` | string | The resource ID of the virtual network peering. | -| `subnetAddressPrefix` | string | The address prefix for the subnet. | -| `subnetAddressPrefixes` | array | List of address prefixes for the subnet. | ## Notes diff --git a/avm/res/network/virtual-network/subnet/main.bicep b/avm/res/network/virtual-network/subnet/main.bicep index 1be5d4bb9a..db1bac6476 100644 --- a/avm/res/network/virtual-network/subnet/main.bicep +++ b/avm/res/network/virtual-network/subnet/main.bicep @@ -2,31 +2,31 @@ metadata name = 'Virtual Network Subnets' metadata description = 'This module deploys a Virtual Network Subnet.' metadata owner = 'Azure/module-maintainers' -@description('Optional. The Name of the subnet resource.') +@description('Requird. The Name of the subnet resource.') param name string @description('Conditional. The name of the parent virtual network. Required if the template is used in a standalone deployment.') param virtualNetworkName string -@description('Required. The address prefix for the subnet.') -param addressPrefix string +@description('Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty.') +param addressPrefix string? @description('Optional. The resource ID of the network security group to assign to the subnet.') -param networkSecurityGroupResourceId string = '' +param networkSecurityGroupResourceId string? @description('Optional. The resource ID of the route table to assign to the subnet.') -param routeTableResourceId string = '' +param routeTableResourceId string? @description('Optional. The service endpoints to enable on the subnet.') -param serviceEndpoints array = [] +param serviceEndpoints string[] = [] -@description('Optional. The delegations to enable on the subnet.') -param delegations array = [] +@description('Optional. The delegation to enable on the subnet.') +param delegation string? @description('Optional. The resource ID of the NAT Gateway to use for the subnet.') -param natGatewayResourceId string = '' +param natGatewayResourceId string? -@description('Optional. enable or disable apply network policies on private endpoint in the subnet.') +@description('Optional. Enable or disable apply network policies on private endpoint in the subnet.') @allowed([ 'Disabled' 'Enabled' @@ -34,7 +34,7 @@ param natGatewayResourceId string = '' ]) param privateEndpointNetworkPolicies string = '' -@description('Optional. enable or disable apply network policies on private link service in the subnet.') +@description('Optional. Enable or disable apply network policies on private link service in the subnet.') @allowed([ 'Disabled' 'Enabled' @@ -42,15 +42,18 @@ param privateEndpointNetworkPolicies string = '' ]) param privateLinkServiceNetworkPolicies string = '' -@description('Optional. List of address prefixes for the subnet.') -param addressPrefixes array = [] +@description('Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty.') +param addressPrefixes string[]? + +@description('Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet.') +param defaultOutboundAccess bool? + +@description('Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty.') +param sharingScope ('DelegatedServices' | 'Tenant')? @description('Optional. Application gateway IP configurations of virtual network resource.') param applicationGatewayIPConfigurations array = [] -@description('Optional. Array of IpAllocation which reference this subnet.') -param ipAllocations array = [] - @description('Optional. An array of service endpoint policies.') param serviceEndpointPolicies array = [] @@ -86,15 +89,16 @@ var formattedRoleAssignments = [ }) ] -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-11-01' existing = { +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' existing = { name: virtualNetworkName } -resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = { +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2024-01-01' = { name: name parent: virtualNetwork properties: { addressPrefix: addressPrefix + addressPrefixes: addressPrefixes networkSecurityGroup: !empty(networkSecurityGroupResourceId) ? { id: networkSecurityGroupResourceId @@ -110,16 +114,29 @@ resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = { id: natGatewayResourceId } : null - serviceEndpoints: serviceEndpoints - delegations: delegations + serviceEndpoints: [ + for endpoint in serviceEndpoints: { + service: endpoint + } + ] + delegations: !empty(delegation) + ? [ + { + name: delegation + properties: { + serviceName: delegation + } + } + ] + : [] privateEndpointNetworkPolicies: !empty(privateEndpointNetworkPolicies) ? any(privateEndpointNetworkPolicies) : null privateLinkServiceNetworkPolicies: !empty(privateLinkServiceNetworkPolicies) ? any(privateLinkServiceNetworkPolicies) : null - addressPrefixes: addressPrefixes applicationGatewayIPConfigurations: applicationGatewayIPConfigurations - ipAllocations: ipAllocations serviceEndpointPolicies: serviceEndpointPolicies + defaultOutboundAccess: defaultOutboundAccess + sharingScope: sharingScope } } @@ -149,10 +166,10 @@ output name string = subnet.name output resourceId string = subnet.id @description('The address prefix for the subnet.') -output subnetAddressPrefix string = subnet.properties.addressPrefix +output addressPrefix string = subnet.properties.?addressPrefix ?? '' @description('List of address prefixes for the subnet.') -output subnetAddressPrefixes array = !empty(addressPrefixes) ? subnet.properties.addressPrefixes : [] +output addressPrefixes array = subnet.properties.?addressPrefixes ?? [] // =============== // // Definitions // diff --git a/avm/res/network/virtual-network/subnet/main.json b/avm/res/network/virtual-network/subnet/main.json index f516267f77..fa80b6adbd 100644 --- a/avm/res/network/virtual-network/subnet/main.json +++ b/avm/res/network/virtual-network/subnet/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17087074157977382192" + "templateHash": "9295856151641467142" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet.", @@ -91,7 +91,7 @@ "name": { "type": "string", "metadata": { - "description": "Optional. The Name of the subnet resource." + "description": "Requird. The Name of the subnet resource." } }, "virtualNetworkName": { @@ -102,41 +102,45 @@ }, "addressPrefix": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The address prefix for the subnet." + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." } }, "networkSecurityGroupResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID of the network security group to assign to the subnet." } }, "routeTableResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID of the route table to assign to the subnet." } }, "serviceEndpoints": { "type": "array", + "items": { + "type": "string" + }, "defaultValue": [], "metadata": { "description": "Optional. The service endpoints to enable on the subnet." } }, - "delegations": { - "type": "array", - "defaultValue": [], + "delegation": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. The delegations to enable on the subnet." + "description": "Optional. The delegation to enable on the subnet." } }, "natGatewayResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." } @@ -150,7 +154,7 @@ "" ], "metadata": { - "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + "description": "Optional. Enable or disable apply network policies on private endpoint in the subnet." } }, "privateLinkServiceNetworkPolicies": { @@ -162,28 +166,42 @@ "" ], "metadata": { - "description": "Optional. enable or disable apply network policies on private link service in the subnet." + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." } }, "addressPrefixes": { "type": "array", - "defaultValue": [], + "items": { + "type": "string" + }, + "nullable": true, "metadata": { - "description": "Optional. List of address prefixes for the subnet." + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." } }, - "applicationGatewayIPConfigurations": { - "type": "array", - "defaultValue": [], + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, "metadata": { - "description": "Optional. Application gateway IP configurations of virtual network resource." + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." } }, - "ipAllocations": { + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." + } + }, + "applicationGatewayIPConfigurations": { "type": "array", "defaultValue": [], "metadata": { - "description": "Optional. Array of IpAllocation which reference this subnet." + "description": "Optional. Application gateway IP configurations of virtual network resource." } }, "serviceEndpointPolicies": { @@ -221,26 +239,35 @@ "virtualNetwork": { "existing": true, "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[parameters('virtualNetworkName')]" }, "subnet": { "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", "properties": { + "copy": [ + { + "name": "serviceEndpoints", + "count": "[length(parameters('serviceEndpoints'))]", + "input": { + "service": "[parameters('serviceEndpoints')[copyIndex('serviceEndpoints')]]" + } + } + ], "addressPrefix": "[parameters('addressPrefix')]", + "addressPrefixes": "[parameters('addressPrefixes')]", "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", - "serviceEndpoints": "[parameters('serviceEndpoints')]", - "delegations": "[parameters('delegations')]", + "delegations": "[if(not(empty(parameters('delegation'))), createArray(createObject('name', parameters('delegation'), 'properties', createObject('serviceName', parameters('delegation')))), createArray())]", "privateEndpointNetworkPolicies": "[if(not(empty(parameters('privateEndpointNetworkPolicies'))), parameters('privateEndpointNetworkPolicies'), null())]", "privateLinkServiceNetworkPolicies": "[if(not(empty(parameters('privateLinkServiceNetworkPolicies'))), parameters('privateLinkServiceNetworkPolicies'), null())]", - "addressPrefixes": "[parameters('addressPrefixes')]", "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", - "ipAllocations": "[parameters('ipAllocations')]", - "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]" + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]", + "defaultOutboundAccess": "[parameters('defaultOutboundAccess')]", + "sharingScope": "[parameters('sharingScope')]" }, "dependsOn": [ "virtualNetwork" @@ -291,19 +318,19 @@ }, "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" }, - "subnetAddressPrefix": { + "addressPrefix": { "type": "string", "metadata": { "description": "The address prefix for the subnet." }, - "value": "[reference('subnet').addressPrefix]" + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefix'), '')]" }, - "subnetAddressPrefixes": { + "addressPrefixes": { "type": "array", "metadata": { "description": "List of address prefixes for the subnet." }, - "value": "[if(not(empty(parameters('addressPrefixes'))), reference('subnet').addressPrefixes, createArray())]" + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefixes'), createArray())]" } } } \ No newline at end of file diff --git a/avm/res/network/virtual-network/tests/e2e/ipv6/main.test.bicep b/avm/res/network/virtual-network/tests/e2e/ipv6/main.test.bicep new file mode 100644 index 0000000000..f1e732cda0 --- /dev/null +++ b/avm/res/network/virtual-network/tests/e2e/ipv6/main.test.bicep @@ -0,0 +1,61 @@ +targetScope = 'subscription' + +metadata name = 'Using an IPv6 address space' +metadata description = 'This instance deploys the module using an IPv6 address space.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.virtualnetworks-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nvnipv6' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + addressPrefixes: [ + '10.0.0.0/21' + 'fd00:592b:3014::/64' + ] + subnets: [ + { + name: 'ipv6-subnet' + addressPrefixes: [ + '10.0.0.0/24' + 'fd00:592b:3014::/64' + ] + } + ] + } + } +] diff --git a/avm/res/network/virtual-network/tests/e2e/max/main.test.bicep b/avm/res/network/virtual-network/tests/e2e/max/main.test.bicep index d0ba4e7d87..81cb3a9c27 100644 --- a/avm/res/network/virtual-network/tests/e2e/max/main.test.bicep +++ b/avm/res/network/virtual-network/tests/e2e/max/main.test.bicep @@ -149,24 +149,13 @@ module testDeployment '../../../main.bicep' = [ ] routeTableResourceId: nestedDependencies.outputs.routeTableResourceId serviceEndpoints: [ - { - service: 'Microsoft.Storage' - } - { - service: 'Microsoft.Sql' - } + 'Microsoft.Storage' + 'Microsoft.Sql' ] } { addressPrefix: cidrSubnet(addressPrefix, 24, 2) - delegations: [ - { - name: 'netappDel' - properties: { - serviceName: 'Microsoft.Netapp/volumes' - } - } - ] + delegation: 'Microsoft.Netapp/volumes' name: '${namePrefix}-az-subnet-x-002' networkSecurityGroupResourceId: nestedDependencies.outputs.networkSecurityGroupResourceId } @@ -200,9 +189,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] } ] diff --git a/avm/res/network/virtual-network/tests/e2e/vnetPeering/main.test.bicep b/avm/res/network/virtual-network/tests/e2e/vnetPeering/main.test.bicep index cbb86623e3..e2b47292d1 100644 --- a/avm/res/network/virtual-network/tests/e2e/vnetPeering/main.test.bicep +++ b/avm/res/network/virtual-network/tests/e2e/vnetPeering/main.test.bicep @@ -80,7 +80,7 @@ module testDeployment '../../../main.bicep' = [ remotePeeringAllowVirtualNetworkAccess: true remotePeeringEnabled: true remotePeeringName: 'customName' - remoteVirtualNetworkId: nestedDependencies.outputs.virtualNetworkResourceId + remoteVirtualNetworkResourceId: nestedDependencies.outputs.virtualNetworkResourceId useRemoteGateways: false } ] @@ -90,8 +90,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/network/virtual-network/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/virtual-network/tests/e2e/waf-aligned/main.test.bicep index b5f66c589d..3e1451af02 100644 --- a/avm/res/network/virtual-network/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/network/virtual-network/tests/e2e/waf-aligned/main.test.bicep @@ -91,10 +91,6 @@ module testDeployment '../../../main.bicep' = [ '10.0.1.4' '10.0.1.5' ] - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } flowTimeoutInMinutes: 20 subnets: [ { @@ -114,24 +110,13 @@ module testDeployment '../../../main.bicep' = [ ] routeTableResourceId: nestedDependencies.outputs.routeTableResourceId serviceEndpoints: [ - { - service: 'Microsoft.Storage' - } - { - service: 'Microsoft.Sql' - } + 'Microsoft.Storage' + 'Microsoft.Sql' ] } { addressPrefix: cidrSubnet(addressPrefix, 24, 2) - delegations: [ - { - name: 'netappDel' - properties: { - serviceName: 'Microsoft.Netapp/volumes' - } - } - ] + delegation: 'Microsoft.Netapp/volumes' name: '${namePrefix}-az-subnet-x-002' networkSecurityGroupResourceId: nestedDependencies.outputs.networkSecurityGroupResourceId } @@ -158,9 +143,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] } ] diff --git a/avm/res/network/virtual-network/version.json b/avm/res/network/virtual-network/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/network/virtual-network/version.json +++ b/avm/res/network/virtual-network/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/virtual-network/virtual-network-peering/README.md b/avm/res/network/virtual-network/virtual-network-peering/README.md index ee2c3cc542..5c61182dc1 100644 --- a/avm/res/network/virtual-network/virtual-network-peering/README.md +++ b/avm/res/network/virtual-network/virtual-network-peering/README.md @@ -12,7 +12,7 @@ This module deploys a Virtual Network Peering. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks/virtualNetworkPeerings) | +| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks/virtualNetworkPeerings) | ## Parameters @@ -20,7 +20,7 @@ This module deploys a Virtual Network Peering. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`remoteVirtualNetworkId`](#parameter-remotevirtualnetworkid) | string | The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID. | +| [`remoteVirtualNetworkResourceId`](#parameter-remotevirtualnetworkresourceid) | string | The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID. | **Conditional parameters** @@ -36,10 +36,10 @@ This module deploys a Virtual Network Peering. | [`allowGatewayTransit`](#parameter-allowgatewaytransit) | bool | If gateway links can be used in remote virtual networking to link to this virtual network. Default is false. | | [`allowVirtualNetworkAccess`](#parameter-allowvirtualnetworkaccess) | bool | Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true. | | [`doNotVerifyRemoteGateways`](#parameter-donotverifyremotegateways) | bool | If we need to verify the provisioning state of the remote gateway. Default is true. | -| [`name`](#parameter-name) | string | The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName. | +| [`name`](#parameter-name) | string | The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName. | | [`useRemoteGateways`](#parameter-useremotegateways) | bool | If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false. | -### Parameter: `remoteVirtualNetworkId` +### Parameter: `remoteVirtualNetworkResourceId` The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID. @@ -87,11 +87,11 @@ If we need to verify the provisioning state of the remote gateway. Default is tr ### Parameter: `name` -The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName. +The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName. - Required: No - Type: string -- Default: `[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]` +- Default: `[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]` ### Parameter: `useRemoteGateways` diff --git a/avm/res/network/virtual-network/virtual-network-peering/main.bicep b/avm/res/network/virtual-network/virtual-network-peering/main.bicep index 51e40dfb20..e3a95e780a 100644 --- a/avm/res/network/virtual-network/virtual-network-peering/main.bicep +++ b/avm/res/network/virtual-network/virtual-network-peering/main.bicep @@ -2,14 +2,14 @@ metadata name = 'Virtual Network Peerings' metadata description = 'This module deploys a Virtual Network Peering.' metadata owner = 'Azure/module-maintainers' -@description('Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName.') -param name string = '${localVnetName}-${last(split(remoteVirtualNetworkId, '/'))}' +@description('Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName.') +param name string = 'peer-${localVnetName}-${last(split(remoteVirtualNetworkResourceId, '/'))}' @description('Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment.') param localVnetName string @description('Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID.') -param remoteVirtualNetworkId string +param remoteVirtualNetworkResourceId string @description('Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true.') param allowForwardedTraffic bool = true @@ -30,7 +30,7 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-11-01' existing name: localVnetName } -resource virtualNetworkPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2023-11-01' = { +resource virtualNetworkPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2024-01-01' = { name: name parent: virtualNetwork properties: { @@ -40,7 +40,7 @@ resource virtualNetworkPeering 'Microsoft.Network/virtualNetworks/virtualNetwork doNotVerifyRemoteGateways: doNotVerifyRemoteGateways useRemoteGateways: useRemoteGateways remoteVirtualNetwork: { - id: remoteVirtualNetworkId + id: remoteVirtualNetworkResourceId } } } diff --git a/avm/res/network/virtual-network/virtual-network-peering/main.json b/avm/res/network/virtual-network/virtual-network-peering/main.json index 5bbd700968..422a6da9b7 100644 --- a/avm/res/network/virtual-network/virtual-network-peering/main.json +++ b/avm/res/network/virtual-network/virtual-network-peering/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1725716682351191048" + "templateHash": "5206620163504251868" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering.", @@ -14,9 +14,9 @@ "parameters": { "name": { "type": "string", - "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." } }, "localVnetName": { @@ -25,7 +25,7 @@ "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." } }, - "remoteVirtualNetworkId": { + "remoteVirtualNetworkResourceId": { "type": "string", "metadata": { "description": "Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID." @@ -70,7 +70,7 @@ "resources": [ { "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", "properties": { "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", @@ -79,7 +79,7 @@ "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", "useRemoteGateways": "[parameters('useRemoteGateways')]", "remoteVirtualNetwork": { - "id": "[parameters('remoteVirtualNetworkId')]" + "id": "[parameters('remoteVirtualNetworkResourceId')]" } } }