* vpn gateway and connection added

* updates

* moved to solution model

* cleaned up with uidefinition

* added short readme

* readme added

* updated readme.md

* removed sh

* vnet peerings updates added

* peering property update ordering complete

* various updates

* updates

* works no firewall rules yet

* update

* update

* update

* documentation updates

* update

* final

* GitHub Action: Build Bicep to JSON

* removed solution.parameters.json

* updated mlz from main

* GitHub Action: Build Bicep to JSON

* GitHub Action: Build Bicep to JSON

---------

Co-authored-by: brsteel@microsoft.com <brsteel@microsoft.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Jason Masten <jamasten@microsoft.com>
This commit is contained in:
Brooke Steele 2024-11-19 12:10:11 -05:00 коммит произвёл GitHub
Родитель 054f6f92ed
Коммит b7010237f7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
15 изменённых файлов: 2496 добавлений и 116 удалений

Просмотреть файл

@ -0,0 +1,199 @@
# VPN Gateway MLZ Add-On
## Introduction
This document provides details on a Bicep script that deploys a VPN Gateway, Local Network Gateway, VPN connection, and related resources in Azure, integrating into an existing MLZ network deployment. It includes descriptions of all parameters, required parameters, instructions on building and deploying the ARM template, and steps to create a template specification from the Bicep script.
The deployment is intended to provide an on-prem VPN gateway to connect into the MLZ network through the Hub vNet and route to all spokes. A route table is created for the vpn gateway and attached to the GatewaySubnet in the Hub network, and firewall rules added to allow connectivity.
This allows for the firewall to serve as a protection between the on-prem internal network and the Azure spoke networks.
Additionally, it covers the modules used within the script and their roles in the deployment process.
---
## Parameters
### 1. **vgwName** (string) - Required
- **Description:** The name of the VPN Gateway. Provided as an input parameter to the solution when deployed.
### 2. **vgwLocation** (string) - Optional (default: location of the resource group)
- **Description:** The Azure region for deploying the VPN Gateway. If not provided, it defaults to the location of the resource group. Provided as an input parameter to the solution when deployed.
### 3. **vgwPublicIpAddressNames** (array) - Required
- **Description:** The names of the public IP addresses associated with the VPN Gateway. Requires two for redundancy. Provided as an input parameter to the solution when deployed.
### 4. **vgwSku** (string) - Optional (default: `'VpnGw2'`)
- **Description:** The SKU (size) of the VPN Gateway. Allowed values: `VpnGw2`, `VpnGw3`, `VpnGw4`, `VpnGw5`. The default can be changed in the "solution.bicep" file.
### 5. **localNetworkGatewayName** (string) - Required
- **Description:** The name of the Local Network Gateway. Provided as an input parameter to the solution when deployed.
### 6. **localGatewayIpAddress** (string) - Required
- **Description:** The IP address of the Local Network Gateway. This must be a public IP address or a reachable IP from the Azure environment. Provided as an input parameter to the solution when deployed.
### 7. **allowedAzureAddressPrefixes** (array) - Required
- **Description:** A list of address prefixes of the peered spoke networks that will be allowed to access the networks through the VPN gateway. This is used in an Azure firewall rule. Provided as an input parameter to the solution when deployed.
### 8. **localAddressPrefixes** (array) - Required
- **Description:** A list of address prefixes of the local network routable through the VPN Gateway. This controls what networks can be accessed from Azure through the VPN Gateway. This is also used in an Azure firewall rule. Provided as an input parameter to the solution when deployed.
### 9. **useSharedKey** (bool) - Required
- **Description:** Indicates whether to use a shared key or a Key Vault certificate URI for the VPN connection. If false, a URL to a pre-existing keyvault stored certificate must be used instead. Provided as an input parameter to the solution when deployed.
### 10. **sharedKey** (string) - Required if `useSharedKey = true`
- **Description:** The shared key for the VPN connection. This parameter is secured. A "true" value uses shared key which is provided in the portal or command prompt at deployment. A "false" value requires that a keyVaultCertificateUri is provided. Remove this from the parameters file before deployment to ensure the deployment will prompt for the value to avoid storing the secret in the file.
### 11. **keyVaultCertificateUri** (string) - Optional (default: `''`)
- **Description:** The URI of the Key Vault certificate for the VPN connection. Only used if `useSharedKey = false`. Must be a valid URI starting with `https://` and containing `/secrets/`. Provided as an input parameter to the solution when deployed.
### 12. **hubVirtualNetworkResourceId** (string) - Required
- **Description:** The resource ID of the hub virtual network. Can be found on the "Properties" blade on the vNet in the Azure portal. Provided as an input parameter to the solution when deployed.
### 13. **vnetResourceIdList** (array) - Required
- **Description:** A list of peered virtual networks that will use the VPN Gateway. The peerings will be updated to allow gateway transit and use. Can be found on the "Properties" blade on the vNet in the Azure portal. Provided as an input parameter to the solution when deployed.
### 14. **azureFirewallName** (string) - Required
- **Description:** The name of the Azure firewall in the hub network used to control all traffic through the VPN gateway and all spoke networks. Provided as an input parameter to the solution when deployed.
### 14. **routeTableName** (string) - Required
- **Description:** The name of the VPN Gateway route table that is used to control the gateway subnet routing overrides necessary to push all traffic through the Azure firewall. Provided as an input parameter to the solution when deployed.
---
## Modules Used in the Script
This Bicep script calls several external modules to deploy resources efficiently and modularly. Here's an overview of each module and what it does:
### 1. **VPN Gateway Module**
- **File:** `modules/vpn-gateway.bicep`
- **Description:** This module deploys the Virtual Network Gateway (VPN Gateway) in a specified resource group. The VPN Gateway enables secure cross-premises connectivity.
- **Parameters:**
- `vgwName`: The name of the VPN Gateway. Provided as an input parameter to the solution when deployed.
- `vgwLocation`: The location where the VPN Gateway will be deployed. Provided as an input parameter to the solution when deployed.
- `publicIpAddressNames`: The names of the public IP addresses associated with the VPN Gateway. Provided as an input parameter to the solution when deployed.
- `vgwsku`: The SKU of the VPN to be deployed. Provided as an input parameter to the solution when deployed.
- `vnetName`: The name of the hub virtual network to which the VPN Gateway will be connected. Derived from the hub virtual network resource id provided as an input parameter when deployed.
### 2. **Local Network Gateway Module**
- **File:** `modules/local-network-gateway.bicep`
- **Description:** This module deploys the Local Network Gateway, which defines the on-premises network's configuration and connectivity to Azure. It includes the on-premises gateway's public IP address and the network address ranges to route through the VPN connection.
- **Parameters:**
- `vgwlocation`: The location of the Local Network Gateway. Provided as an input parameter to the solution when deployed.
- `localNetworkGatewayName`: The name of the Local Network Gateway. Provided as an input parameter to the solution when deployed.
- `gatewayIpAddress`: The public IP address of the Local Network Gateway. Provided as an input parameter to the solution when deployed.
- `addressPrefixes`: The local address prefixes (network ranges) of the on-premises network. Provided as an input parameter to the solution when deployed.
### 3. **VPN Connection Module**
The VPN connection module contains these most commonly used IPSEC configuration settings:
``
saLifeTimeSeconds: 3600
saDataSizeKilobytes: 102400000
ipsecEncryption: 'AES256'
ipsecIntegrity: 'SHA256'
ikeEncryption: 'AES256'
ikeIntegrity: 'SHA256'
dhGroup: 'DHGroup2'
pfsGroup: 'PFS2'
``
Change these in the module file directly to modify connection settings for deployment.
- **File:** `modules/vpn-connection.bicep`
- **Description:** This module creates the VPN connection between the VPN Gateway in Azure and the Local Network Gateway (on-premises network). It can use either a shared key or a Key Vault certificate for secure authentication.
- **Parameters:**
- `vpnConnectionName`: The name of the VPN connection. Provided as an input parameter to the solution when deployed.
- `vgwlocation`: The location of the VPN Gateway. Provided as an input parameter to the solution when deployed.
- `vpnGatewayName`: The name of the VPN Gateway. Provided as an input parameter to the solution when deployed.
- `vpnGatewayResourceGroupName`: The resource group where the VPN Gateway is deployed. Gateway is placed in the hub virtual network resource group, the name is extracted from the hub virtual network resource group id provided in the parameters when deployed.
- `sharedKey`: The shared key for the VPN connection (if using shared key authentication). Provided as an input parameter to the solution when deployed. Ensure the shared key and value are not provided in the parameters file before deployment to ensure prompting for the value at deployment time.
- `keyVaultCertificateUri`: The URI of the Key Vault certificate (if using certificate-based authentication). Provided as an input parameter to the solution when deployed, if shared key is not used.
- `localNetworkGatewayName`: The name of the Local Network Gateway. Provided as an input parameter to the solution when deployed.
### 4. **Retrieve Existing Module**
- **File:** `modules/retrieve-existing.bicep`
- **Description:** This module retrieves the list of virtual network peerings associated with a virtual network. The peerings allow networks to communicate securely with each other within the same Azure region or across regions. This module is also used to retrieve information from other existing resources depending on the parameters used.
- **Parameters:**
- `vnetResourceId`: The resource ID of the virtual network for which peerings are being retrieved. Provided as an input parameter to the solution when deployed.
### 5. **VNet Peerings Module**
- **File:** `modules/vnet-peerings.bicep`
- **Description:** After retrieving the peerings for a virtual network, this module updates the peerings to reflect the new VPN Gateway configuration. This allows peered networks to utilize the VPN Gateway for cross-premises connectivity.
- **Parameters:**
- `vnetResourceId`: The resource ID of the virtual network. Provided as an input parameter to the solution when deployed.
- `peeringsList`: The list of virtual network peerings to be updated. Returned values from the retrieve-existing.bicep module.
### 6. **Route Table Module**
- **File:** `modules/route-table.bicep`
- **Description:** This module creates the route table for the VPN gateway.
- **Parameters:**
- `routeTableName`: The route table name. Provided as an input parameter to the solution when deployed.
### 7. **Route Definition**
- **File:** `modules/route-definition.bicep`
- **Description:** This module builds the route construct to be used when adding the route, as multiple routes need to be added. Virtual appliance is hard coded as the next hop type.
- **Parameters:**
- `firewallIpAddress`: The IP address of the firewall, used as the next hop IP address. Returned value from the retrieve-existing.bicep module.
- `addressPrefixes`: The address prefixes used in the route being built. Provided as an input parameter to the solution when deployed.
### 8. **Routes Module**
- **File:** `modules/routes.bicep`
- **Description:** This module creates the routes in a route table.
- **Parameters:**
- `routeTableName`: The route table name. Provided as an input parameter to the solution when deployed.
- `routeName`: The name of the route. Value defaulted in the solution.bicep file, the value is: "route-#" with an increment number depending on the number of routes being added.
- `addressSpace`: The CIDR address prefix being routed. Provided as an input parameter to the solution when deployed.
- `nextHopType`: The type of next hop, defaulted to appliance in the solution.bicep file.
- `nextHopIpAddress`: The IP address of the next hop. In this implementation, the firewall IP address. Provided as an input parameter to the solution when deployed.
### 8. **Firewall Rules Module**
- **File:** `modules/firewall-rules.bicep `
- **Description:** This module creates the firewall rules to allow spoke and vpn address prefixes access to eachother.
- **Parameters:**
- `allowVnetAddressSpaces`: The CIDR address prefixes of peered Azure spoke vnets. Provided as an input parameter to the solution when deployed.
- `onPremAddressSpaces`: The CIDR address prefixes of the onprem networks to be allowed. Provided as an input parameter to the solution when deployed.
- `firewallPolicyId`: The firewall policy attached to the hub firewall. The solution will dynamically retrieve this value.
- `priorityValue`: The priority value for the firewall rule. The solution defines this at a value of 300 in the main bicep named solution.bicep.
The rule is hardcoded to allow any protocol to any address in the rule. Customize firewall-rules.bicep to change behavior.
## Removal of VPN Gateway
1. Delete the VPN Connection in the MLZ Hub resource group.
2. Delete the Local Network Gateway in the MLZ Hub resource group.
3. Delete the VPN Gateway in the MLZ Hub resource group.
4. Navigate to the Hub vNet, and go to Peerings:
a. Open each peering in the list.
b. Uncheck "Allow gateway or route server in '' to forward traffic to ''.
c. Click "save".
5. Navigate to each spoke vNet represented in the peerings list.
a. Open the peering to the Hub network.
b. Uncheck "Enable '' to use '' remote gateway.
c. Click "save".
6. Navigate to each spoke network resource group.
a. Open the Route table in the group.
b. Choose "Routes".
c. Delete the VPN routes in the list.

Просмотреть файл

@ -0,0 +1,29 @@
@description('virtual network resource ID that holds the subnet')
param vnetResourceId string
@description('route table resource ID to associate with the subnet')
param routeTableResourceId string
@description('name of the subnet to associate with the route table')
param subnetName string
@description('address prefix of the gateway subnet')
param subnetAddressPrefix string
// Reference the existing Virtual Network
resource existingVnet 'Microsoft.Network/virtualNetworks@2023-11-01' existing = {
name: last(split(vnetResourceId, '/'))
}
// Update the GatewaySubnet to associate the existing Route Table
resource gatewaySubnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = {
parent: existingVnet
name: subnetName
properties: {
addressPrefix: subnetAddressPrefix
routeTable: {
id: resourceId('Microsoft.Network/routeTables', last(split(routeTableResourceId, '/')))
}
}
}

Просмотреть файл

@ -0,0 +1,70 @@
@description('The list of virtual network resource IDs to be used as the source IP groups')
param allowVnetAddressSpaces array
@description('Address prefixes of the on-premises network')
param onPremAddressSpaces array
@description('Name of the firewall policy')
param firewallPolicyId string
@description('The priority value for the rule collection')
@minValue(300)
@maxValue(65000)
param priorityValue int
// Define the firewall policy reference
resource firewallPolicy 'Microsoft.Network/firewallPolicies@2023-04-01' existing = {
name: last(split(firewallPolicyId, '/'))
}
// First rule set: Source = allowedIpGroup, Destination = onPremIpGroup
var outboundRules = {
name: 'AllowAzureToOnPremRule' // Unique rule name using index
ruleType: 'NetworkRule'
sourceAddresses: allowVnetAddressSpaces
destinationAddresses: onPremAddressSpaces
destinationPorts: [
'*' // Modify this as needed
]
ipProtocols: [
'Any' // Modify this as needed
]
}
// Second rule set (reverse): Source = onPremIpGroup, Destination = allowedIpGroup
var inboundRules = {
name: 'AllowOnPremToAzureRule' // Unique rule name using index
ruleType: 'NetworkRule'
sourceAddresses: onPremAddressSpaces
destinationAddresses: allowVnetAddressSpaces
destinationPorts: [
'*' // Modify this as needed
]
ipProtocols: [
'Any' // Modify this as needed
]
}
// Define the rule collection group, referencing existing IP groups for source and destination
resource allowVgwCollection 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2023-04-01' = {
name: 'VgwNetworkRuleCollectionGroup'
parent: firewallPolicy
properties: {
priority: priorityValue
ruleCollections: [
{
name: 'AllowVgw'
priority: priorityValue
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
action: {
type: 'Allow'
}
rules: [
outboundRules
inboundRules
]
}
]
}
}

Просмотреть файл

@ -0,0 +1,17 @@
param vgwlocation string = resourceGroup().location
param localNetworkGatewayName string
param gatewayIpAddress string
param addressPrefixes array
// Local Network Gateway configuration
resource localNetworkGateway 'Microsoft.Network/localNetworkGateways@2023-02-01' = {
name: localNetworkGatewayName
location: vgwlocation
properties: {
gatewayIpAddress: gatewayIpAddress
localNetworkAddressSpace: {
addressPrefixes: addressPrefixes
}
}
}

Просмотреть файл

@ -0,0 +1,58 @@
@description('Name of the Azure Firewall (optional)')
param azureFirewallName string = ''
@description('Name of the subnet (optional)')
param subnetName string = ''
@description('The resource ID of the existing spoke virtual network (optional)')
param vnetResourceId string = ''
@description('The name of the route table associated with the hub virtual network (optional)')
param routeTableName string = ''
resource vnetRouteTable 'Microsoft.Network/routeTables@2020-11-01' existing = if (!empty(routeTableName) && !empty(vnetResourceId)) {
scope: resourceGroup()
name: routeTableName
}
// Retrieve internal address of the firewall, conditionally
resource azureFirewall 'Microsoft.Network/azureFirewalls@2020-11-01' existing = if (!empty(azureFirewallName) && !empty(vnetResourceId)) {
scope: resourceGroup(split(vnetResourceId, '/')[2], split(vnetResourceId, '/')[4])
name: azureFirewallName
}
// Reference the existing Virtual Network using its resource ID, conditionally
resource vnetInfo 'Microsoft.Network/virtualNetworks@2020-11-01' existing = if (!empty(vnetResourceId)) {
scope: resourceGroup()
name: last(split(vnetResourceId, '/')) // Extract the VNet name from the resource ID
}
// Loop through the subnets to find the specified subnet, conditionally
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2020-11-01' existing = if (!empty(subnetName) && !empty(vnetResourceId)) {
parent: vnetInfo
name: subnetName
}
// Output the route table ID of the hub virtual network, if the route table name is provided
output routeTableId string = !empty(routeTableName) ? vnetRouteTable.id : 'N/A'
// Output the internal IP address of the firewall, if firewall parameters are provided
output firewallPrivateIp string = (!empty(azureFirewallName) && !empty(vnetResourceId)) ? azureFirewall.properties.ipConfigurations[0].properties.privateIPAddress : 'N/A'
// Output the firewall policy id attached to the firewall
output firewallPolicyId string = !empty(azureFirewallName) ? azureFirewall.properties.firewallPolicy.id : 'N/A'
// Output the address prefix of the GatewaySubnet, if the parameters are provided
output subnetAddressPrefix string = (!empty(subnetName) && !empty(vnetResourceId)) ? subnet.properties.addressPrefix : 'N/A'
// Output the address space of the VNet, if the VNet resource ID is provided
output vnetAddressSpace array = !empty(vnetResourceId) ? vnetInfo.properties.addressSpace.addressPrefixes : []
// Output the list of peerings from the VNet, if the VNet resource ID is provided
output peeringsData object = !empty(vnetResourceId) ? {
vnetResourceId: vnetResourceId
peeringsList: vnetInfo.properties.virtualNetworkPeerings
} : {
vnetResourceId: 'N/A'
peeringsList: []
}

Просмотреть файл

@ -0,0 +1,18 @@
// Assume hubVnetAddressSpace is a parameter or variable from another part of your script
@description('Address space prefixes of the virtual network')
param addressPrefixes array
@description('Private IP address of the Azure Firewall')
param firewallPrivateIp string
// Create a variable with the route definitions
output routes array = [
for i in range(0, length(addressPrefixes)): {
name: 'mlzToOnPrem-${i}' // Ensure unique route names
addressPrefix: addressPrefixes[i]
nextHopType: 'VirtualAppliance'
nextHopIpAddress: firewallPrivateIp
}
]

Просмотреть файл

@ -0,0 +1,12 @@
@description('Name of the route table to create')
param routeTableName string
resource routeTable 'Microsoft.Network/routeTables@2021-02-01' = {
name: routeTableName
location: resourceGroup().location
properties: {
disableBgpRoutePropagation: true
}
}
output routeTableId string = routeTable.id

Просмотреть файл

@ -0,0 +1,30 @@
@description('Name of the route table to create')
param routeTableName string
@description('Name of the route')
param routeName string
@description('CIDR prefixes for the route')
param addressSpace array
@description('The next hop type for the route')
param nextHopType string
@description('The next hop IP address for the route')
param nextHopIpAddress string
resource routeTable 'Microsoft.Network/routeTables@2021-02-01' existing = {
name: routeTableName
scope: resourceGroup()
}
// Loop over the address spaces and create routes
resource routes 'Microsoft.Network/routeTables/routes@2023-04-01' = [for (cidr, i) in addressSpace: {
parent: routeTable
name: '${routeName}-${i}'
properties: {
addressPrefix: cidr
nextHopType: nextHopType
nextHopIpAddress: nextHopIpAddress != '' ? nextHopIpAddress : null
}
}]

Просмотреть файл

@ -0,0 +1,35 @@
@description('The list of peerings to update')
param peeringsList array
@description('The resource ID of the existing virtual network')
param vnetResourceId string
// Extract the virtual network name from the resource ID
var vnetName = last(split(vnetResourceId, '/'))
// Generate the list of updated peerings
var updatedPeerings = [for peering in peeringsList: {
name: last(split(peering.id, '/')) // Extract the peering name from the ID
properties: {
allowGatewayTransit: contains(vnetName, '-hub-') ? true : peering.properties.allowGatewayTransit
useRemoteGateways: !contains(vnetName, '-hub-') ? true : peering.properties.useRemoteGateways
// allowGatewayTransit: contains(split(peering.id, '/')[8], '-hub-') ? true : peering.properties.allowGatewayTransit
// useRemoteGateways: !contains(split(peering.id, '/')[8], '-hub-') ? true : peering.properties.useRemoteGateways
allowForwardedTraffic: peering.properties.allowForwardedTraffic == null ? true : peering.properties.allowForwardedTraffic // Preserve existing value or set to true
remoteVirtualNetwork: peering.properties.remoteVirtualNetwork
}
}]
// Define the parent virtual network resource
resource vnet 'Microsoft.Network/virtualNetworks@2022-07-01' existing = {
name: vnetName
}
// Create or update the peerings within the virtual network context
resource peeringUpdates 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2021-02-01' = [for (peering, i) in updatedPeerings: {
parent: vnet
name: peering.name
properties: peering.properties
}]

Просмотреть файл

@ -0,0 +1,52 @@
param vpnConnectionName string
param vgwlocation string
param vpnGatewayName string
param vpnGatewayResourceGroupName string
param sharedKey string
param keyVaultCertificateUri string
param localNetworkGatewayName string
// Determine if either sharedKey or keyVaultCertificateUri is provided
var useSharedKey = !empty(sharedKey)
var useKeyVaultCertificate = !empty(keyVaultCertificateUri)
// Conditional validation through variables
var errorMsg = (useSharedKey && useKeyVaultCertificate) ? 'Cannot provide both sharedKey and keyVaultCertificateUri' : ''
var connectionSharedKey = useSharedKey ? sharedKey : null
var connectionIpsecPolicies = useKeyVaultCertificate ? [
{
saLifeTimeSeconds: 3600
saDataSizeKilobytes: 102400000
ipsecEncryption: 'AES256'
ipsecIntegrity: 'SHA256'
ikeEncryption: 'AES256'
ikeIntegrity: 'SHA256'
dhGroup: 'DHGroup2'
pfsGroup: 'PFS2'
}
] : null
// Deploy the VPN connection only if the conditions are met
resource vpnConnection 'Microsoft.Network/connections@2023-02-01' = if (empty(errorMsg)) {
name: vpnConnectionName
location: vgwlocation
properties: {
virtualNetworkGateway1: {
id: resourceId(vpnGatewayResourceGroupName, 'Microsoft.Network/virtualNetworkGateways', vpnGatewayName)
}
localNetworkGateway2: {
id: resourceId(vpnGatewayResourceGroupName, 'Microsoft.Network/localNetworkGateways', localNetworkGatewayName)
}
connectionType: 'IPsec'
routingWeight: 10
sharedKey: connectionSharedKey
// Use ipsecPolicies if Key Vault certificate URI is provided
ipsecPolicies: connectionIpsecPolicies
// Additional properties as required
enableBgp: false
usePolicyBasedTrafficSelectors: false
}
}

Просмотреть файл

@ -0,0 +1,81 @@
param vgwname string
param vgwlocation string = resourceGroup().location
param publicIpAddressNames array
param vgwsku string
param vnetName string
// Existing Virtual Network and Subnet
resource vnet 'Microsoft.Network/virtualNetworks@2023-02-01' existing = {
name: vnetName
}
// Reference the existing subnet within the specified Virtual Network
resource gatewaySubnet 'Microsoft.Network/virtualNetworks/subnets@2023-02-01' existing = {
parent: vnet
name: 'GatewaySubnet'
}
var gatewaySubnetId = gatewaySubnet.id
// Public IP Addresses
resource publicIpAddresses 'Microsoft.Network/publicIPAddresses@2023-02-01' = [for (pipname, index) in publicIpAddressNames: {
name: pipname
location: vgwlocation
sku: {
name: 'Standard'
}
properties: {
publicIPAllocationMethod: 'Static'
}
}]
var firstPublicIpAddressId = publicIpAddresses[0].id
var secondPublicIpAddressId = publicIpAddresses[1].id
// VPN Gateway
resource vpnGateway 'Microsoft.Network/virtualNetworkGateways@2023-02-01' = {
name: vgwname
location: vgwlocation
properties: {
gatewayType: 'Vpn'
ipConfigurations: [
{
name: 'first'
properties: {
privateIPAllocationMethod: 'Dynamic'
subnet: {
id: gatewaySubnetId
}
publicIPAddress: {
id: firstPublicIpAddressId
}
}
}
{
name: 'second'
properties: {
privateIPAllocationMethod: 'Dynamic'
publicIPAddress: {
id: secondPublicIpAddressId
}
subnet: {
id: gatewaySubnetId
}
}
}
]
activeActive: true
vpnType: 'RouteBased'
vpnGatewayGeneration: 'Generation2'
enableBgp: false
enablePrivateIpAddress: false
sku: {
name: vgwsku
tier: vgwsku
}
}
}

Просмотреть файл

@ -0,0 +1,60 @@
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vgwName": {
"value": "<example: mlz-vgw-hub-dev-va>"
},
"vgwLocation": {
"value": "usgovvirginia"
},
"vgwPublicIpAddressNames": {
"value": [
"<example: mlz-pip-vgw-hub-dev-va-1>",
"<example: mlz-pip-vgw-hub-dev-va-2"
]
},
"localNetworkGatewayName": {
"value": "<example: mlz-lng-hub-dev-va>"
},
"localGatewayIpAddress": {
"value": "<publicipaddressforonpremvpntermination>"
},
"allowedAzureAddressPrefixes": {
"value": [
"10.0.130.0/24",
"10.0.131.0/24",
"10.0.132.0/24",
"10.0.128.0/23"
]
},
"localAddressPrefixes": {
"value": [
"192.168.0.0/24",
"192.168.1.0/24"
]
},
"useSharedKey": {
"value": true
},
"hubVirtualNetworkResourceId": {
"value": "/subscriptions/<subscriptionid>/resourceGroups/<resourcegroupname>/providers/Microsoft.Network/virtualNetworks/<virtualnetworkname>"
},
"vnetResourceIdList": {
"value": [
"/subscriptions/<subscriptionid>/resourceGroups/<resourcegroupname>/providers/Microsoft.Network/virtualNetworks/<virtualnetworkname",
"/subscriptions/<subscriptionid>/resourceGroups/<resourcegroupname>/providers/Microsoft.Network/virtualNetworks/<virtualnetworkname",
"/subscriptions/<subscriptionid>/resourceGroups/<resourcegroupname>/providers/Microsoft.Network/virtualNetworks/<virtualnetworkname"
]
},
"azureFirewallName": {
"value": "<example: mlz-afw-hub-dev-va>"
},
"vgwRouteTableName": {
"value": "<example: mlz-vgw-rt-hub-dev-va>"
},
"hubVnetRouteTableName": {
"value": "<example: mlz-rt-hub-dev-va>"
}
}
}

Просмотреть файл

@ -0,0 +1,276 @@
targetScope = 'subscription'
@description('The name of the VPN Gateway.')
param vgwName string
@description('The Azure region location of the VPN Gateway.')
param vgwLocation string
@description('The names of the public IP addresses to use for the VPN Gateway.')
param vgwPublicIpAddressNames array
@description('The SKU of the VPN Gateway.')
@allowed(['VpnGw2', 'VpnGw3', 'VpnGw4', 'VpnGw5'])
param vgwSku string
@description('Local Network Gateway Name')
param localNetworkGatewayName string
@description('IP Address of the Local Network Gateway, must be a public IP address or be able to be connected to from MLZ network')
param localGatewayIpAddress string
@description('Azure address prefixes allowed to communicate to VPN Gateway to on-premises network')
param allowedAzureAddressPrefixes array
@description('Address prefixes of the Local Network which will be routable through the VPN Gateway')
param localAddressPrefixes array
@description('Choose whether to use a shared key or Key Vault certificate URI for the VPN connection.')
param useSharedKey bool
@description('The shared key to use for the VPN connection. If using the shared key, this must be provided.')
@secure()
param sharedKey string
@description('The URI of the Key Vault certificate to use for the VPN connection. If using a Key Vault certificate, this must be a valid URI.')
param keyVaultCertificateUri string = ''
@description('A suffix to use for naming deployments uniquely.')
param deploymentNameSuffix string = utcNow()
@description('The resource ID of the hub virtual network.')
param hubVirtualNetworkResourceId string
// Extracting the resource group name and virtual network name from the hub virtual network resource ID
var hubResourceGroupName = split(hubVirtualNetworkResourceId, '/')[4]
var hubVnetName = split(hubVirtualNetworkResourceId, '/')[8]
@description('List of peered networks that should use the VPN Gateway once configured.')
param vnetResourceIdList array
@description('The name of the Azure Firewall to retrieve the internal IP address from.')
param azureFirewallResourceId string
var azureFirewallName = split(azureFirewallResourceId, '/')[8]
@description('The name of the vgw route table to create')
param vgwRouteTableName string
@description('The name of the gateway subnet')
param gatewaySubnetName string = 'GatewaySubnet'
@description('The name of the hub virtual network route table')
param hubVnetRouteTableResourceId string
var hubVnetRouteTableName = split(hubVnetRouteTableResourceId, '/')[8]
// Conditional parameter assignment for VPN connection module
var vpnSharedKey = useSharedKey ? sharedKey : ''
var vpnKeyVaultUri = !useSharedKey ? keyVaultCertificateUri : ''
// Parameter validation
var isValidUri = contains(keyVaultCertificateUri, 'https://') && contains(keyVaultCertificateUri, '/secrets/')
// Conditional validation to ensure either sharedKey or keyVaultCertificateUri is used correctly
resource validateKeyOrUri 'Microsoft.Resources/deployments@2021-04-01' = if (!useSharedKey && !isValidUri) {
name: 'InvalidKeyVaultCertificateUri-${deploymentNameSuffix}'
properties: {
mode: 'Incremental'
parameters: {
message: {
value: 'Invalid Key Vault Certificate URI. It must start with "https://" and contain "/secrets/".'
}
}
templateLink: {
uri: 'https://validatemessage.com' // Placeholder for validation message, replace if needed
}
}
}
// calling Virtual Network Gateway Module
module vpnGatewayModule 'modules/vpn-gateway.bicep' = {
name: 'vpnGateway-${deploymentNameSuffix}'
scope: resourceGroup(hubResourceGroupName)
params: {
vgwname: vgwName
vgwlocation: vgwLocation
publicIpAddressNames: vgwPublicIpAddressNames
vgwsku: vgwSku
vnetName: hubVnetName
}
}
// calling Local Network Gateway Module
module localNetworkGatewayModule 'modules/local-network-gateway.bicep' = {
name: 'localNetworkGateway-${deploymentNameSuffix}'
scope: resourceGroup(hubResourceGroupName)
params: {
vgwlocation: vgwLocation
localNetworkGatewayName: localNetworkGatewayName
gatewayIpAddress: localGatewayIpAddress
addressPrefixes: localAddressPrefixes
}
}
// calling VPN Connection Module
module vpnConnectionModule 'modules/vpn-connection.bicep' = {
name: 'vpnConnection-${deploymentNameSuffix}'
scope: resourceGroup(hubResourceGroupName)
params: {
vpnConnectionName: '${vgwName}-to-${localNetworkGatewayName}'
vgwlocation: vgwLocation
vpnGatewayName: vgwName
vpnGatewayResourceGroupName: hubResourceGroupName
sharedKey: vpnSharedKey
keyVaultCertificateUri: vpnKeyVaultUri
localNetworkGatewayName: localNetworkGatewayName
}
dependsOn: [
vpnGatewayModule
localNetworkGatewayModule
validateKeyOrUri
]
}
// Loop through the vnetResourceIdList and to retrieve the peerings for each VNet
module retrieveVnetInfo 'modules/retrieve-existing.bicep' = [for (vnetId, i) in vnetResourceIdList: {
name: 'retrieveVnetPeerings-${deploymentNameSuffix}-${i}'
scope: resourceGroup(split(vnetId, '/')[2], split(vnetId, '/')[4])
params: {
vnetResourceId: vnetId
}
dependsOn: [
vpnConnectionModule
]
}]
// Get the hub virtual network peerings
module retrieveHubVnetInfo 'modules/retrieve-existing.bicep' = {
name: 'retrieveHubVnetPeerings-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
vnetResourceId: hubVirtualNetworkResourceId
}
dependsOn: [
vpnConnectionModule
]
}
// retrieve the route table information for the hub vnet including the firewall private IP and gateway subnet address space info to be used for the new vgw route table and routes
module retrieveRouteTableInfo 'modules/retrieve-existing.bicep' = {
name: 'retrieveRouteTableInfo-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
vnetResourceId: hubVirtualNetworkResourceId
azureFirewallName: azureFirewallName
subnetName: gatewaySubnetName
}
dependsOn: [
updatePeerings
]
}
// Call update the Hub peerings first to enable spokes to use the VPN Gateway, if not done first, spokes will fail their update
module updateHubPeerings 'modules/vnet-peerings.bicep' = {
name: 'updateHubPeerings-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
vnetResourceId: retrieveHubVnetInfo.outputs.peeringsData.vnetResourceId
peeringsList: retrieveHubVnetInfo.outputs.peeringsData.peeringsList
}
dependsOn: [
retrieveHubVnetInfo
retrieveVnetInfo
]
}
// Update the peerings for each spoke VNet to use the VPN Gateway
module updatePeerings 'modules/vnet-peerings.bicep' = [for (vnetId, i) in vnetResourceIdList: {
name: 'updatePeerings-${deploymentNameSuffix}-${i}'
scope: resourceGroup(split(vnetId, '/')[2], split(vnetId, '/')[4])
params: {
vnetResourceId: retrieveVnetInfo[i].outputs.peeringsData.vnetResourceId
peeringsList: retrieveVnetInfo[i].outputs.peeringsData.peeringsList
}
dependsOn: [
retrieveVnetInfo
updateHubPeerings
]
}]
// Create the route table for the VPN Gateway subnet, will route spoke vnets to through the firewall, overriding default behavior
module createRouteTable 'modules/route-table.bicep' = {
name: 'createVgwRouteTable-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
routeTableName: vgwRouteTableName
}
dependsOn: [
retrieveVnetInfo
retrieveRouteTableInfo
updateHubPeerings
updatePeerings
]
}
// Create the routes to the firewall for the spoke vnets, if vnet is not provided in the "allowedAzureAddressPrefixes" then the spoke will not be able to use the VPN Gateway
module createRoutes 'modules/routes.bicep' = [for (vnetResourceId, i) in vnetResourceIdList: {
name: 'createRoute-${i}-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
routeTableName: vgwRouteTableName
addressSpace: retrieveVnetInfo[i].outputs.vnetAddressSpace
routeName: 'route-${i}'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: retrieveRouteTableInfo.outputs.firewallPrivateIp
}
dependsOn: [
createRouteTable
]
}]
// Create the routes to the firewall for the hub vnet as and override to the onprem networks
module createHubRoutesToOnPrem 'modules/routes.bicep' = {
name: 'createOverrideRoutes-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
routeTableName: hubVnetRouteTableName
addressSpace: localAddressPrefixes
routeName: 'route-onprem-override'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: retrieveRouteTableInfo.outputs.firewallPrivateIp
}
dependsOn: [
createRouteTable
]
}
// Associate the vgw route table with the gateway subnet so the gateway routes traffic destined for spokes through the firewall
module associateRouteTable 'modules/associate-route-table.bicep' = {
name: 'associateRouteTable-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
vnetResourceId: hubVirtualNetworkResourceId
routeTableResourceId: createRouteTable.outputs.routeTableId
subnetName: gatewaySubnetName
subnetAddressPrefix: retrieveRouteTableInfo.outputs.subnetAddressPrefix
}
dependsOn: [
createRouteTable
]
}
// Create the firewall rules
module firewallRules 'modules/firewall-rules.bicep' = {
name: 'firewallRules-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
allowVnetAddressSpaces: allowedAzureAddressPrefixes
onPremAddressSpaces: localAddressPrefixes
firewallPolicyId: retrieveRouteTableInfo.outputs.firewallPolicyId
priorityValue: 300
}
dependsOn: [
associateRouteTable
]
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -4,8 +4,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "15343776706780459746"
"version": "0.31.92.45157",
"templateHash": "7670256562437793548"
}
},
"parameters": {
@ -887,8 +887,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "10924801470287352076"
"version": "0.31.92.45157",
"templateHash": "4114664614478275863"
}
},
"parameters": {
@ -967,8 +967,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "11992572431768878515"
"version": "0.31.92.45157",
"templateHash": "6411408920489493501"
}
},
"parameters": {
@ -1561,8 +1561,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "2502930168536032010"
"version": "0.31.92.45157",
"templateHash": "17619606927129129347"
}
},
"parameters": {
@ -1706,8 +1706,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "12782100486021815060"
"version": "0.31.92.45157",
"templateHash": "5311912111839888762"
}
},
"parameters": {
@ -1766,8 +1766,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "17445726037807437290"
"version": "0.31.92.45157",
"templateHash": "9371138343009436350"
}
},
"parameters": {
@ -1907,8 +1907,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "11630019985682391796"
"version": "0.31.92.45157",
"templateHash": "6531207997218247037"
}
},
"parameters": {
@ -2084,8 +2084,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "12984958392421507149"
"version": "0.31.92.45157",
"templateHash": "17606032368841347839"
}
},
"parameters": {
@ -2352,8 +2352,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "2196017082128829477"
"version": "0.31.92.45157",
"templateHash": "3681943409502537301"
}
},
"parameters": {
@ -2431,8 +2431,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "2196017082128829477"
"version": "0.31.92.45157",
"templateHash": "3681943409502537301"
}
},
"parameters": {
@ -2512,8 +2512,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "8417416771003518918"
"version": "0.31.92.45157",
"templateHash": "18425321023142226965"
}
},
"parameters": {
@ -2613,8 +2613,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "4071285799602638747"
"version": "0.31.92.45157",
"templateHash": "15002544166394392504"
}
},
"parameters": {
@ -2682,8 +2682,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "3465065949976146403"
"version": "0.31.92.45157",
"templateHash": "16817025486402215719"
}
},
"parameters": {
@ -2796,8 +2796,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "1730170030487163899"
"version": "0.31.92.45157",
"templateHash": "782319697243698272"
}
},
"parameters": {
@ -2886,8 +2886,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "1730170030487163899"
"version": "0.31.92.45157",
"templateHash": "782319697243698272"
}
},
"parameters": {
@ -3000,8 +3000,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "5808375390237466838"
"version": "0.31.92.45157",
"templateHash": "4750034338253477937"
}
},
"parameters": {
@ -3401,8 +3401,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "11567108053836979012"
"version": "0.31.92.45157",
"templateHash": "11818136489056939588"
}
},
"parameters": {
@ -3519,8 +3519,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "2196017082128829477"
"version": "0.31.92.45157",
"templateHash": "3681943409502537301"
}
},
"parameters": {
@ -3602,8 +3602,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "8417416771003518918"
"version": "0.31.92.45157",
"templateHash": "18425321023142226965"
}
},
"parameters": {
@ -3705,8 +3705,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "4071285799602638747"
"version": "0.31.92.45157",
"templateHash": "15002544166394392504"
}
},
"parameters": {
@ -3782,8 +3782,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "3465065949976146403"
"version": "0.31.92.45157",
"templateHash": "16817025486402215719"
}
},
"parameters": {
@ -3930,8 +3930,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "6489616383757058493"
"version": "0.31.92.45157",
"templateHash": "11553909803736438916"
}
},
"parameters": {
@ -3983,8 +3983,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "5031620623183573702"
"version": "0.31.92.45157",
"templateHash": "5574526676512163672"
}
},
"parameters": {
@ -4062,8 +4062,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "15799890372401066181"
"version": "0.31.92.45157",
"templateHash": "10267893616110384815"
}
},
"parameters": {
@ -4115,8 +4115,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "5031620623183573702"
"version": "0.31.92.45157",
"templateHash": "5574526676512163672"
}
},
"parameters": {
@ -4192,8 +4192,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "16841222955467860758"
"version": "0.31.92.45157",
"templateHash": "17171440191267536743"
}
},
"parameters": {
@ -4269,8 +4269,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "12405780209119797551"
"version": "0.31.92.45157",
"templateHash": "16077950968688123011"
}
},
"parameters": {
@ -4435,8 +4435,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "6386888682917235118"
"version": "0.31.92.45157",
"templateHash": "12787329163785242553"
}
},
"parameters": {
@ -4524,8 +4524,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "419730844167715947"
"version": "0.31.92.45157",
"templateHash": "11761568940379970751"
}
},
"parameters": {
@ -4782,8 +4782,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "7946295394028911325"
"version": "0.31.92.45157",
"templateHash": "4207798980384159491"
}
},
"parameters": {
@ -4862,8 +4862,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "967013811257719495"
"version": "0.31.92.45157",
"templateHash": "1966035938992047983"
}
},
"parameters": {
@ -4957,8 +4957,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "9115514582672423063"
"version": "0.31.92.45157",
"templateHash": "7930493629995578222"
}
},
"parameters": {
@ -5102,8 +5102,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "366255573709691198"
"version": "0.31.92.45157",
"templateHash": "17900321188332105834"
}
},
"parameters": {
@ -5188,8 +5188,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "11337913680813675353"
"version": "0.31.92.45157",
"templateHash": "17298378299072098272"
}
},
"parameters": {
@ -5356,8 +5356,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "6648745810362038848"
"version": "0.31.92.45157",
"templateHash": "12087616562036012055"
}
},
"parameters": {
@ -5484,8 +5484,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "18318483669505051648"
"version": "0.31.92.45157",
"templateHash": "2081045465267717136"
}
},
"parameters": {
@ -5712,8 +5712,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "12373038860746323248"
"version": "0.31.92.45157",
"templateHash": "15042212833055960396"
}
},
"parameters": {
@ -5889,8 +5889,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "10895428597231985214"
"version": "0.31.92.45157",
"templateHash": "2760766953842709390"
}
},
"parameters": {
@ -6049,8 +6049,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "13001914044795164689"
"version": "0.31.92.45157",
"templateHash": "10850741356290813493"
}
},
"parameters": {
@ -6371,8 +6371,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "8788916623054227962"
"version": "0.31.92.45157",
"templateHash": "2785700700806650182"
}
},
"parameters": {
@ -6538,8 +6538,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "16352081091904770478"
"version": "0.31.92.45157",
"templateHash": "10838080267928735788"
}
},
"parameters": {
@ -6846,8 +6846,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "8788916623054227962"
"version": "0.31.92.45157",
"templateHash": "2785700700806650182"
}
},
"parameters": {
@ -6991,8 +6991,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "15180554823445581333"
"version": "0.31.92.45157",
"templateHash": "15042903713059976655"
}
},
"parameters": {
@ -7108,8 +7108,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "10245709494871874056"
"version": "0.31.92.45157",
"templateHash": "1625826941635729014"
}
},
"parameters": {
@ -7392,8 +7392,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "11013785870428129557"
"version": "0.31.92.45157",
"templateHash": "11341470403202647858"
}
},
"parameters": {
@ -7478,8 +7478,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "5964906331561259426"
"version": "0.31.92.45157",
"templateHash": "4687229436121899773"
}
},
"parameters": {
@ -7565,8 +7565,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "3200577753830159376"
"version": "0.31.92.45157",
"templateHash": "6315472047633861096"
}
},
"parameters": {
@ -7652,8 +7652,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "5533529544253509904"
"version": "0.31.92.45157",
"templateHash": "2073766618455932098"
}
},
"parameters": {
@ -7734,8 +7734,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "169088610601729285"
"version": "0.31.92.45157",
"templateHash": "9546260853018527046"
}
},
"parameters": {
@ -7816,8 +7816,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "5104130163491279218"
"version": "0.31.92.45157",
"templateHash": "16372121177996394493"
}
},
"parameters": {
@ -7894,8 +7894,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "8958046244399156747"
"version": "0.31.92.45157",
"templateHash": "3821176451773778831"
}
},
"parameters": {
@ -7975,8 +7975,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "13721817451936402949"
"version": "0.31.92.45157",
"templateHash": "1721966359516622278"
}
},
"parameters": {
@ -8047,8 +8047,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "16505356842938617427"
"version": "0.31.92.45157",
"templateHash": "8510408576984746573"
}
},
"parameters": {
@ -8133,8 +8133,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "7145459344230861166"
"version": "0.31.92.45157",
"templateHash": "3495057416767671634"
}
},
"parameters": {
@ -8190,8 +8190,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "4206571908443996627"
"version": "0.31.92.45157",
"templateHash": "14528983897386416653"
}
},
"parameters": {
@ -8366,8 +8366,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "967013811257719495"
"version": "0.31.92.45157",
"templateHash": "1966035938992047983"
}
},
"parameters": {
@ -8464,8 +8464,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "4104174025531546645"
"version": "0.31.92.45157",
"templateHash": "17309660590425732791"
}
},
"parameters": {
@ -8528,8 +8528,8 @@
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.31.34.60546",
"templateHash": "2575529544484247921"
"version": "0.31.92.45157",
"templateHash": "17047820191891552534"
}
},
"parameters": {