Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
327 changes: 306 additions & 21 deletions src/add-ons/virtual-network-gateway/README.md

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions src/add-ons/virtual-network-gateway/modules/firewall-info.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@description('Resource ID of the existing Azure Firewall')
param azureFirewallResourceId string

// Derive resource group and name from the resource ID
var firewallSubscriptionId = split(azureFirewallResourceId, '/')[2]
var firewallResourceGroupName = split(azureFirewallResourceId, '/')[4]
var firewallName = split(azureFirewallResourceId, '/')[8]

resource azureFirewall 'Microsoft.Network/azureFirewalls@2023-11-01' existing = {
scope: resourceGroup(firewallSubscriptionId, firewallResourceGroupName)
name: firewallName
}

// Direct property access is safe because resource declaration is unconditional
output firewallPrivateIp string = azureFirewall.properties.ipConfigurations[0].properties.privateIPAddress
output firewallPolicyId string = azureFirewall.properties.firewallPolicy.id

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
@description('Name of the route table to create')
param routeTableName string
param routeTableName string

@description('Disable BGP route propagation (true = static override behavior). Set to false to allow learned routes).')
param disableBgpRoutePropagation bool = true

resource routeTable 'Microsoft.Network/routeTables@2021-02-01' = {
name: routeTableName
location: resourceGroup().location
properties: {
disableBgpRoutePropagation: true
disableBgpRoutePropagation: disableBgpRoutePropagation
}
}

Expand Down
20 changes: 20 additions & 0 deletions src/add-ons/virtual-network-gateway/modules/subnet-info.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@description('Resource ID of the existing virtual network')
param vnetResourceId string
@description('Name of the subnet to inspect')
param subnetName string

var vnetSubscriptionId = split(vnetResourceId, '/')[2]
var vnetResourceGroupName = split(vnetResourceId, '/')[4]
var vnetName = split(vnetResourceId, '/')[8]

resource vnet 'Microsoft.Network/virtualNetworks@2023-11-01' existing = {
scope: resourceGroup(vnetSubscriptionId, vnetResourceGroupName)
name: vnetName
}

resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' existing = {
parent: vnet
name: subnetName
}

output subnetAddressPrefix string = subnet.properties.addressPrefix
17 changes: 17 additions & 0 deletions src/add-ons/virtual-network-gateway/modules/vnet-info.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@description('Resource ID of the existing virtual network')
param vnetResourceId string

var vnetSubscriptionId = split(vnetResourceId, '/')[2]
var vnetResourceGroupName = split(vnetResourceId, '/')[4]
var vnetName = split(vnetResourceId, '/')[8]

resource vnet 'Microsoft.Network/virtualNetworks@2023-11-01' existing = {
scope: resourceGroup(vnetSubscriptionId, vnetResourceGroupName)
name: vnetName
}

output vnetAddressSpace array = vnet.properties.addressSpace.addressPrefixes
output peeringsData object = {
vnetResourceId: vnetResourceId
peeringsList: vnet.properties.virtualNetworkPeerings
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var useKeyVaultCertificate = !empty(keyVaultCertificateUri)
var errorMsg = (useSharedKey && useKeyVaultCertificate) ? 'Cannot provide both sharedKey and keyVaultCertificateUri' : ''
var connectionSharedKey = useSharedKey ? sharedKey : null
var connectionIpsecPolicies = useKeyVaultCertificate ? [
// disable-next-line BCP035
{
saLifeTimeSeconds: 3600
saDataSizeKilobytes: 102400000
Expand All @@ -27,23 +28,30 @@ var connectionIpsecPolicies = useKeyVaultCertificate ? [
] : null

// Deploy the VPN connection only if the conditions are met
// Schema requires only id references for nested gateway objects; suppress warning.
// disable-next-line BCP035
resource vpnConnection 'Microsoft.Network/connections@2023-02-01' = if (empty(errorMsg)) {
name: vpnConnectionName
location: vgwlocation
properties: {
// disable-next-line BCP035
virtualNetworkGateway1: {
id: resourceId(vpnGatewayResourceGroupName, 'Microsoft.Network/virtualNetworkGateways', vpnGatewayName)
// Bicep type model expects properties; Azure accepts id-only. Provide empty object to satisfy linter.
properties: {}
}
// disable-next-line BCP035
localNetworkGateway2: {
id: resourceId(vpnGatewayResourceGroupName, 'Microsoft.Network/localNetworkGateways', localNetworkGatewayName)
properties: {}
}
connectionType: 'IPsec'
routingWeight: 10

sharedKey: connectionSharedKey

// Use ipsecPolicies if Key Vault certificate URI is provided
ipsecPolicies: connectionIpsecPolicies
ipsecPolicies: connectionIpsecPolicies == null ? [] : connectionIpsecPolicies

// Additional properties as required
enableBgp: false
Expand Down
69 changes: 29 additions & 40 deletions src/add-ons/virtual-network-gateway/solution.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ param virtualNetworkGatewaySku string = 'VpnGw2'
@description('Optional: Provide your own Firewall Policy rule collection groups. When non-empty, these override the default VGW-OnPrem group built by this template.')
param customFirewallRuleCollectionGroups array = []

@description('Include Hub <-> On-Prem allow firewall rules in the default group (off by default).')
param includeHubOnPrem bool = false



@description('Default CIDR to use when creating the GatewaySubnet if it does not exist.')
var defaultGatewaySubnetPrefix = '10.0.129.192/26'
Expand Down Expand Up @@ -97,7 +97,7 @@ module firewallRules 'modules/firewall-rules-vgw.bicep' = {
spokeAddressPrefixSets: collectSpokeAddresses.outputs.spokeAddressPrefixSets
localAddressPrefixes: localAddressPrefixes
firewallRuleCollectionGroups: customFirewallRuleCollectionGroups
includeHubOnPrem: includeHubOnPrem
// hub always included now; parameter removed
}
}

Expand Down Expand Up @@ -165,7 +165,7 @@ module vpnConnectionModule 'modules/vpn-connection.bicep' = {
}

// Loop through the vnetResourceIdList and to retrieve the peerings for each VNet
module retrieveVnetInfo 'modules/retrieve-existing.bicep' = [for (vnetId, i) in virtualNetworkResourceIdList: {
module retrieveVnetInfo 'modules/vnet-info.bicep' = [for (vnetId, i) in virtualNetworkResourceIdList: {
name: 'retrieveVnetPeerings-${deploymentNameSuffix}-${i}'
scope: resourceGroup(split(vnetId, '/')[2], split(vnetId, '/')[4])
params: {
Expand All @@ -174,7 +174,7 @@ module retrieveVnetInfo 'modules/retrieve-existing.bicep' = [for (vnetId, i) in
}]

// Get the hub virtual network peerings
module retrieveHubVnetInfo 'modules/retrieve-existing.bicep' = {
module retrieveHubVnetInfo 'modules/vnet-info.bicep' = {
name: 'retrieveHubVnetPeerings-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
Expand All @@ -183,13 +183,11 @@ module retrieveHubVnetInfo 'modules/retrieve-existing.bicep' = {
}

// Retrieve firewall private IP and (optionally) GatewaySubnet prefix without peering deps
module retrieveRouteTableInfo 'modules/retrieve-existing.bicep' = {
name: 'retrieveRouteTableInfo-${deploymentNameSuffix}'
module firewallInfo 'modules/firewall-info.bicep' = {
name: 'firewallInfo-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
vnetResourceId: hubVirtualNetworkResourceId
azureFirewallName: split(azureFirewallResourceId, '/')[8]
subnetName: ''
azureFirewallResourceId: azureFirewallResourceId
}
}
// Ensure the GatewaySubnet exists (or is configured) before associating the dedicated vgw route table
Expand Down Expand Up @@ -231,16 +229,18 @@ module updatePeerings 'modules/vnet-peerings.bicep' = [for (vnetId, i) in virtua
]
}]

// Create the route table for the VPN Gateway subnet, will route spoke vnets to through the firewall, overriding default behavior
// Always create the dedicated route table for forced tunneling via the firewall
module createRouteTable 'modules/route-table.bicep' = {
name: 'createVgwRouteTable-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
routeTableName: vgwRouteTableName
routeTableName: vgwRouteTableName
// Disable BGP propagation (no BGP usage; static enforcement)
disableBgpRoutePropagation: true
}
}

// Create the routes to the firewall for the spoke vnets
// Add static routes for each spoke via firewall (forced tunneling)
module createRoutes 'modules/routes.bicep' = [for (vnetResourceId, i) in virtualNetworkResourceIdList: {
name: 'createRoute-${i}-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
Expand All @@ -249,71 +249,60 @@ module createRoutes 'modules/routes.bicep' = [for (vnetResourceId, i) in virtual
addressSpace: retrieveVnetInfo[i].outputs.vnetAddressSpace
routeName: 'route-${i}'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: retrieveRouteTableInfo.outputs.firewallPrivateIp
nextHopIpAddress: firewallInfo.outputs.firewallPrivateIp
}
dependsOn: [
createRouteTable
]
}]

// Create the routes to the firewall for the hub vnet as an override to on-prem networks
module createHubRoutesToOnPrem 'modules/routes.bicep' = if (includeHubOnPrem) {
name: 'createOverrideRoutes-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
routeTableName: vgwRouteTableName
addressSpace: localAddressPrefixes
routeName: 'route-onprem-override'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: retrieveRouteTableInfo.outputs.firewallPrivateIp
}
dependsOn: [
createRouteTable
]
}

// Also add Hub VNet address prefixes to the VGW route table to steer Hub CIDRs through the firewall
module createHubCidrRoutes 'modules/routes.bicep' = if (includeHubOnPrem) {
// Add Hub VNet address prefixes to the VGW route table to steer Hub CIDRs through the firewall
module createHubCidrRoutes 'modules/routes.bicep' = {
name: 'createHubCidrRoutes-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
routeTableName: vgwRouteTableName
addressSpace: virtualNetwork_hub.properties.addressSpace.addressPrefixes
routeName: 'route-hub'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: retrieveRouteTableInfo.outputs.firewallPrivateIp
nextHopIpAddress: firewallInfo.outputs.firewallPrivateIp
}
dependsOn: [
createRouteTable
]
}

// Add the same on-prem override routes to the pre-existing hub workload route table so hub workloads egress to the firewall (optional)
module createHubRouteOverrides 'modules/routes.bicep' = if (includeHubOnPrem) {
// Add on-prem prefixes to the hub workload route table so hub workloads egress to the firewall
// Add on-prem prefixes to the hub workload route table so hub workloads egress to the firewall
module createHubRouteOverrides 'modules/routes.bicep' = {
name: 'createHubRouteOverrides-${deploymentNameSuffix}'
scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4])
params: {
routeTableName: hubRouteTableName
addressSpace: localAddressPrefixes
routeName: 'route-onprem-override'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: retrieveRouteTableInfo.outputs.firewallPrivateIp
nextHopIpAddress: firewallInfo.outputs.firewallPrivateIp
}
}

// Associate the vgw route table with the gateway subnet so the gateway routes traffic destined for spokes through the firewall
// Associate the vgw route table to GatewaySubnet
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
// safe: module only exists in same condition
// Safely pass the route table id (module exists only under same condition)
routeTableResourceId: createRouteTable.outputs.routeTableId
subnetName: 'GatewaySubnet'
subnetAddressPrefix: effectiveGatewaySubnetPrefix
}
dependsOn: [
ensureGatewaySubnet
vpnGatewayModule
ensureGatewaySubnet
vpnGatewayModule
]
}

// (Removed routingMode output; forced tunneling is mandatory now)

16 changes: 0 additions & 16 deletions src/add-ons/virtual-network-gateway/solution.bicepparam

This file was deleted.

Loading