🛞

ハブスポーク構成でのハブ間・スポーク間の疎通

に公開

はじめに

Azure に限らず、ネットワークモデルとして良く取られるものとして、"Hub & Spoke" ネットワークがあります。
これは、あたかもタイヤのハブ (軸) とスポーク (軸から延びる腕の部分) があって、ハブ ネットワークにスポーク ネットワークから共有されるサービを集約するといったネットワーク形態です。
この記事では、Hub & Spoke ネットワーク構成を取った際、良くトピックとして挙げられるスポーク ネットワーク間の疎通について、Azure での実現方法を確認していきます。

なお、Azure におけるスポーク間の通信については、こちら に詳しい説明があります。

スポーク ネットワーク間通信

スポーク ネットワークは、ワークロード毎に分けるように作ることが多く、そのため、スポーク ネットワークに閉じた通信が主なものとなります。
一方、ワークロード間など特定の通信が必要になるケースがあり、その場合においてスポーク ネットワーク間の通信を設計します。

なお、Azure においては Hub & Spoke ネットワークの実現方法として、以下の2通りの方法がありますが、
Virtual WAN を利用する場合、構成が楽な反面、きめ細かな制御ができないことがありますので、
2の方法で構成した場合の、スポーク間の疎通について実現方法を確認していきます。

  1. Virtual WAN を利用
  2. 仮想ネットワーク ピアリングで構成

構成

東日本、西日本でそれぞれ、Hub & Spoke 構成を取った場合の構成をしており、東西のハブ ネットワークを経由して接続されています。
ただし、仮想ネットワーク ピアリング (VNet ピアリング) は 推移的なルーティングをサポートしていない ため、隣り合っていない VNet へのアクセスについては、
明示的なルーティングの設定が必要になります。

また、下部の運用 VNet は構成に必須ではなく、各スポーク ネットワークにあるワークロードの保守を行うためのものです。
今回は、仮想マシン VM-OPS から VM-01 に入り、疎通を確認しています。

全体ネットワーク構成図

① のネットワーク疎通について

仮想マシン VM-01 から仮想マシン VM-02 についての疎通を考えてみましょう。
まず、当初のネットワークの構成から、それぞれが所属する スポーク VNet #1 およびスポーク VNet #2 において、
ハブ (Azure Firewall) に対するユーザー定義ルートが設定されています。これによって、双方の VNet から通信は、ハブ VNet #1 の Azure Firewall を経由します。

VM-01からVM-02への疎通

ただし、上記にあったように、推移的なルーティングはサポートされていないため、
スポーク VNet #1 -> ハブ VNet #1 -> スポーク VNet #2 という通信は成り立ちません。
そこで、Azure Firewall ルールに以下のようなネットワーク ルールを設定することでその疎通が可能となります
(許可するプロトコルやポートは要件に合わせて要調整)。

設定項目 ルール1 ルール2
規則コレクションの種類 許可 許可
ソースの種類 IP アドレス IP アドレス
ソース IP アドレス 10.1.0.0/16 10.2.0.0/16
宛先の種類 IP addresses IP addresses
宛先 IP アドレス 10.2.0.0/16 10.1.0.0/16
プロトコル 任意 任意
ポート * *

設定後、疎通できることを確認します。

VM-01からVM-02への疎通確認

② のネットワーク疎通について

次に、仮想マシン VM-01 から仮想マシン VM-03 についての疎通を考えてみましょう (仮想マシン VM-04 も同じ考え方)。

VM-01からVM-03への疎通

先の場合と同様に、Azure Firewall のネットワーク ルールに対して、
スポーク VNet #3 のルールを設定します (カンマ区切りで複数の IP アドレスを指定することができます)。

設定項目 ルール3 ルール4
規則コレクションの種類 許可 許可
ソースの種類 IP アドレス IP アドレス
ソース IP アドレス 10.1.0.0/16,10.2.0.0/16 192.168.3.0/24,192.168.4.0/24
宛先の種類 IP addresses IP addresses
宛先 IP アドレス 192.168.3.0/24,192.168.4.0/24 10.1.0.0/16,10.2.0.0/16
プロトコル 任意 任意
ポート * *

ただ、このままでは Azure Firewall からインターネットへの疎通が試行されてしまうため、
スポーク VNet #3 とのルーティングを管理するハブ VNet #2 の Azure Fireweall (azfw2) に振り向けなくてななりません。

そのため、azfw1 がある AzureFirewallSubnet に対して、以下のユーザー定義ルートを関連付けます。
これによって、スポーク VNet #3 およびスポーク VNet #4 へのトラフィックが azfw2 に振り向けられます。

HubVNET1のUDR

また、戻りの通信のため、azfw2 のある AzureFirewallSubnet に対してもユーザー定義ルートが必要となります。
それはこちらになります。

HubVNET2のUDR

最後に、 azfw2 の Azure Firewall ルールに対して、スポーク VNet #1 およびスポーク VNet #2 のルールを設定します。

設定項目 ルール5 ルール6
規則コレクションの種類 許可 許可
ソースの種類 IP アドレス IP アドレス
ソース IP アドレス 10.1.0.0/16,10.2.0.0/16 192.168.3.0/24,192.168.4.0/24
宛先の種類 IP addresses IP addresses
宛先 IP アドレス 192.168.3.0/24,192.168.4.0/24 10.1.0.0/16,10.2.0.0/16
プロトコル 任意 任意
ポート * *

これで、スポーク VNet #1 からスポーク VNet #3 への疎通が可能となります。

VM-01からVM-03への疎通確認

まとめ

Hub & Spoke 構成において、Azure Firewall を使ってスポーク ネットワーク間の疎通を実現するための方法について確認しました。
これ以外にもスポーク ネットワーク間の必要な疎通が特定されているのであれば、そのスポーク ネットワーク間で VNet ピアリングを行う方が、
Azure Firewall を経由するよりも設定、性能面、コスト面でも最善かもしれませんので、併せて検討してみてください。

参考情報

サンプル Bicep テンプレート
param location1 string = 'japaneast'
param location2 string = 'japanwest'

param hubVnetName1 string = 'hubvnet1'
param hubVnetName2 string = 'hubvnet2'

param spokeVnetName1 string = 'spokevnet1'
param spokeVnetName2 string = 'spokevnet2'
param spokeVnetName3 string = 'spokevnet3'
param spokeVnetName4 string = 'spokevnet4'

param azfirewallName1 string = 'azfw1'
param azfirewallName2 string = 'azfw2'

@secure()
param adminUserName string

@secure()
param adminPassword string

// Define the hub virtual network #1
resource hubvnet1 'Microsoft.Network/virtualNetworks@2022-05-01' = {
  name: hubVnetName1
  location: location1
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.0.0.0/16'
      ]
    }
    subnets: [
      {
        name: 'AzureFirewallSubnet'
        properties: {
          addressPrefix: '10.0.0.0/26'
        }
      }
    ]
  }
  tags: {
    displayName: 'hubvnet1'
  }
}

// Define the subnet for the Azure Firewall in the hub virtual network #1
resource hubSubnet1 'Microsoft.Network/virtualNetworks/subnets@2022-05-01' = {
  parent: hubvnet1
  name: 'AzureFirewallSubnet'
  properties: {
    addressPrefix: '10.0.0.0/26'
    routeTable: {
      id: hubRouteTable1.id
    }
  }
}

// Define the hub virtual network #2
resource hubvnet2 'Microsoft.Network/virtualNetworks@2022-05-01' = {
  name: hubVnetName2
  location: location2
  properties: {
    addressSpace: {
      addressPrefixes: [
        '192.168.0.0/24'
      ]
    }
    subnets: [
      {
        name: 'AzureFirewallSubnet'
        properties: {
          addressPrefix: '192.168.0.0/26'
        }
      }
    ]
  }
  tags: {
    displayName: 'hubvnet2'
  }
}

// Define the subnet for the Azure Firewall in the hub virtual network #2
resource hubSubnet2 'Microsoft.Network/virtualNetworks/subnets@2022-05-01' = {
  parent: hubvnet2
  name: 'AzureFirewallSubnet'
  properties: {
    addressPrefix: '192.168.0.0/26'
    routeTable: {
      id: hubRouteTable2.id
    }
  }
}

// Define the spoke virtual network #1
resource spokevnet1 'Microsoft.Network/virtualNetworks@2022-05-01' = {
  name: spokeVnetName1
  location: location1
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.1.0.0/16'
      ]
    }
    subnets: [
      {
        name: 'default'
        properties: {
          addressPrefix: '10.1.0.0/24'
          routeTable: {
            id: spokeRouteTable1.id
          }
        }
      }
    ]
  }
  tags: {
    displayName: 'spokevnet1'
  }
}

// Define the spoke virtual network #2
resource spokevnet2 'Microsoft.Network/virtualNetworks@2022-05-01' = {
  name: spokeVnetName2
  location: location1
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.2.0.0/16'
      ]
    }
    subnets: [
      {
        name: 'default'
        properties: {
          addressPrefix: '10.2.0.0/24'
          routeTable: {
            id: spokeRouteTable1.id
          }
        }
      }
    ]
  }
  tags: {
    displayName: 'spokevnet2'
  }
}

// Define the spoke virtual network #3
resource spokevnet3 'Microsoft.Network/virtualNetworks@2022-05-01' = {
  name: spokeVnetName3
  location: location2
  properties: {
    addressSpace: {
      addressPrefixes: [
        '192.168.3.0/24'
      ]
    }
    subnets: [
      {
        name: 'default'
        properties: {
          addressPrefix: '192.168.3.0/24'
          routeTable: {
            id: spokeRouteTable2.id
          }
        }
      }
    ]
  }
  tags: {
    displayName: 'spokevnet3'
  }
}

// Define the spoke virtual network #4
resource spokevnet4 'Microsoft.Network/virtualNetworks@2022-05-01' = {
  name: spokeVnetName4
  location: location2
  properties: {
    addressSpace: {
      addressPrefixes: [
        '192.168.4.0/24'
      ]
    }
    subnets: [
      {
        name: 'default'
        properties: {
          addressPrefix: '192.168.4.0/24'
          routeTable: {
            id: spokeRouteTable2.id
          }
        }
      }
    ]
  }
  tags: {
    displayName: 'spokevnet4'
  }
}

// Define the OPS virtual network
resource opsvnet 'Microsoft.Network/virtualNetworks@2022-05-01' = {
  name: 'opsvnet'
  location: location1
  properties: {
    addressSpace: {
      addressPrefixes: [
        '172.1.0.0/24'
      ]
    }
    subnets: [
      {
        name: 'default'
        properties: {
          addressPrefix: '172.1.0.0/24'
        }
      }
    ]
  }
  tags: {
    displayName: 'opsvnet'
  }
}

// Define the hub-to-spoke virtual network peering #1
resource hubToSpoke1 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: hubvnet1
  name: 'hubToSpoke1'
  properties: {
    remoteVirtualNetwork: {
      id: spokevnet1.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the spoke virtual network #1-to-hub virtual network peering
resource spokeToHub1 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: spokevnet1
  name: 'spokeToHub1'
  properties: {
    remoteVirtualNetwork: {
      id: hubvnet1.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the hub-to-spoke virtual network peering #2
resource hubToSpoke2 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: hubvnet1
  name: 'hubToSpoke2'
  properties: {
    remoteVirtualNetwork: {
      id: spokevnet2.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the spoke virtual network #2-to-hub virtual network peering
resource spokeToHub2 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: spokevnet2
  name: 'spokeToHub2'
  properties: {
    remoteVirtualNetwork: {
      id: hubvnet1.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the hub-to-spoke virtual network peering #3
resource hubToSpoke3 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: hubvnet2
  name: 'hubToSpoke3'
  properties: {
    remoteVirtualNetwork: {
      id: spokevnet3.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the spoke virtual network #3-to-hub virtual network peering
resource spokeToHub3 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: spokevnet3
  name: 'spokeToHub3'
  properties: {
    remoteVirtualNetwork: {
      id: hubvnet2.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the hub-to-spoke virtual network peering #4
resource hubToSpoke4 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: hubvnet2
  name: 'hubToSpoke4'
  properties: {
    remoteVirtualNetwork: {
      id: spokevnet4.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the spoke virtual network #4-to-hub virtual network peering
resource spokeToHub4 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: spokevnet4
  name: 'spokeToHub4'
  properties: {
    remoteVirtualNetwork: {
      id: hubvnet2.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the hub-to-hub virtual network peering1
resource hubToHub1 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: hubvnet1
  name: 'hubToHub1'
  properties: {
    remoteVirtualNetwork: {
      id: hubvnet2.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the hub-to-hub virtual network peering2
resource hubToHub2 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: hubvnet2
  name: 'hubToHub2'
  properties: {
    remoteVirtualNetwork: {
      id: hubvnet1.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the ops-to-spoke virtual network peering #1
resource opsToSpoke1 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: opsvnet
  name: 'opsToSpoke1'
  properties: {
    remoteVirtualNetwork: {
      id: spokevnet1.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the spoke virtual network #1-to-ops virtual network peering
resource spokeToOps1 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: spokevnet1
  name: 'spokeToOps1'
  properties: {
    remoteVirtualNetwork: {
      id: opsvnet.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the ops-to-spoke virtual network peering #2
resource opsToSpoke2 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: opsvnet
  name: 'opsToSpoke2'
  properties: {
    remoteVirtualNetwork: {
      id: spokevnet2.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Deifne the spoke virtual network #2-to-ops virtual network peering
resource spokeToOps2 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: spokevnet2
  name: 'spokeToOps2'
  properties: {
    remoteVirtualNetwork: {
      id: opsvnet.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the ops-to-spoke virtual network peering #3
resource opsToSpoke3 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: opsvnet
  name: 'opsToSpoke3'
  properties: {
    remoteVirtualNetwork: {
      id: spokevnet3.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the spoke virtual network #3-to-ops virtual network peering
resource spokeToOps3 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: spokevnet3
  name: 'spokeToOps3'
  properties: {
    remoteVirtualNetwork: {
      id: opsvnet.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the ops-to-spoke virtual network peering #4
resource opsToSpoke4 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: opsvnet
  name: 'opsToSpoke4'
  properties: {
    remoteVirtualNetwork: {
      id: spokevnet4.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the spoke virtual network #4-to-ops virtual network peering
resource spokeToOps4 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-05-01' = {
  parent: spokevnet4
  name: 'spokeToOps4'
  properties: {
    remoteVirtualNetwork: {
      id: opsvnet.id
    }
    allowVirtualNetworkAccess: true
    allowForwardedTraffic: true
    allowGatewayTransit: false
    useRemoteGateways: false
  }
}

// Define the public IP address for the Azure Firewall in the hub virtual network #1
resource azurePublicIp1 'Microsoft.Network/publicIPAddresses@2022-05-01' = {
  name: 'azfwPublicIp1'
  location: location1
  sku: {
    name: 'Standard'
  }
  properties: {
    publicIPAllocationMethod: 'Static'
  }
  tags: {
    displayName: 'azfwPublicIp1'
  }
}

// Define the Azure Firewall in the hub virtual network #1
resource azureFirewall1 'Microsoft.Network/azureFirewalls@2022-05-01' = {
  name: azfirewallName1
  location: location1
  properties: {
    ipConfigurations: [
      {
        name: 'azureFirewallIpConfiguration'
        properties: {
          subnet: {
            id: '${hubvnet1.id}/subnets/AzureFirewallSubnet'
          }
          publicIPAddress: {
            id: azurePublicIp1.id
          }
        }
      }
    ]
    networkRuleCollections: [
      {
        name: 'hubspoketransition'
        properties: {
          action: {
            type: 'Allow'
          }
          priority: 100
          rules: [
            {
              name: 'towest'
              description: 'Allow outgoing traffic from Japan East.'
              sourceAddresses: [
                '10.1.0.0/16'
                '10.2.0.0/16'
              ]
              destinationAddresses: [
                '192.168.3.0/24'
                '192.168.4.0/24'
              ]
              destinationPorts: [
                '*'
              ]
              protocols: [
                'Any'
              ]
            }
            {
              name: 'fromwest'
              description: 'Allow incoming traffic from Japan West.'
              sourceAddresses: [
                '192.168.3.0/24'
                '192.168.4.0/24'
              ]
              destinationAddresses: [
                '10.1.0.0/16'
                '10.2.0.0/16'
              ]
              destinationPorts: [
                '*'
              ]
              protocols: [
                'Any'
              ]
            }
            {
              name: 'spoketospoke1'
              description: 'Allow inter-spoke communication.'
              sourceAddresses: [
                '10.1.0.0/16'
              ]
              destinationAddresses: [
                '10.2.0.0/16'
              ]
              destinationPorts: [
                '*'
              ]
              protocols: [
                'Any'
              ]
            }
            {
              name: 'spoketospoke2'
              description: 'Allow inter-spoke communication.'
              sourceAddresses: [
                '10.2.0.0/16'
              ]
              destinationAddresses: [
                '10.1.0.0/16'
              ]
              destinationPorts: [
                '*'
              ]
              protocols: [
                'Any'
              ]
            }
          ]
        }
      }
    ]
  }
}

// Deifne the public IP address for the Azure Firewall in the hub virtual network #2
resource azurePublicIp2 'Microsoft.Network/publicIPAddresses@2022-05-01' = {
  name: 'azfwPublicIp2'
  location: location2
  sku: {
    name: 'Standard'
  }
  properties: {
    publicIPAllocationMethod: 'Static'
  }
  tags: {
    displayName: 'azfwPublicIp2'
  }
}

// Define the Azure Firewall in the hub virtual network #2
resource azureFirewall2 'Microsoft.Network/azureFirewalls@2022-05-01' = {
  name: azfirewallName2
  location: location2
  properties: {
    ipConfigurations: [
      {
        name: 'azureFirewallIpConfiguration'
        properties: {
          subnet: {
            id: '${hubvnet2.id}/subnets/AzureFirewallSubnet'
          }
          publicIPAddress: {
            id: azurePublicIp2.id
          }
        }
      }
    ]
    networkRuleCollections: [
      {
        name: 'hubspoketransition'
        properties: {
          action: {
            type: 'Allow'
          }
          priority: 100
          rules: [
            {
              name: 'toeast'
              description: 'Allow incoming traffic from Japan West.'
              sourceAddresses: [
                '192.168.3.0/24'
                '192.168.4.0/24'
              ]
              destinationAddresses: [
                '10.1.0.0/16'
                '10.2.0.0/16'
              ]
              destinationPorts: [
                '*'
              ]
              protocols: [
                'Any'
              ]
            }
            {
              name: 'fromeast'
              description: 'Allow outgoing traffic from Japan East.'
              sourceAddresses: [
                '10.1.0.0/16'
                '10.2.0.0/16'
              ]
              destinationAddresses: [
                '192.168.3.0/24'
                '192.168.4.0/24'
              ]
              destinationPorts: [
                '*'
              ]
              protocols: [
                'Any'
              ]
            }
            {
              name: 'spoketospoke1'
              description: 'Allow inter-spoke communication.'
              sourceAddresses: [
                '192.168.3.0/24'
              ]
              destinationAddresses: [
                '192.168.4.0/24'
              ]
              destinationPorts: [
                '*'
              ]
              protocols: [
                'Any'
              ]
            }
            {
              name: 'spoketospoke2'
              description: 'Allow inter-spoke communication.'
              sourceAddresses: [
                '192.168.4.0/24'
              ]
              destinationAddresses: [
                '192.168.3.0/24'
              ]
              destinationPorts: [
                '*'
              ]
              protocols: [
                'Any'
              ]
            }
          ]
        }
      }
    ]
  }
}


// Define the route table for the hub virtual network #1
resource hubRouteTable1 'Microsoft.Network/routeTables@2022-05-01' = {
  name: 'hubRouteTable1'
  location: location1
  properties: {
    disableBgpRoutePropagation: false
    routes: [
      {
        name: 'hubToInternet'
        properties: {
          addressPrefix: '0.0.0.0/0'
          nextHopType: 'Internet'
        }
      }
      {
        name: 'toSpokeVnet3'
        properties: {
          addressPrefix: '192.168.3.0/24'
          nextHopType: 'VirtualAppliance'
          nextHopIpAddress: azureFirewall2.properties.ipConfigurations[0].properties.privateIPAddress
          
        }
      }
      {
        name: 'toSpokeVnet4'
        properties: {
          addressPrefix: '192.168.4.0/24'
          nextHopType: 'VirtualAppliance'
          nextHopIpAddress: azureFirewall2.properties.ipConfigurations[0].properties.privateIPAddress
        }
      }
    ]
  }
  tags: {
    displayName: 'hubRouteTable1'
  }
}

// Define the route table for the hub virtual network #2
resource hubRouteTable2 'Microsoft.Network/routeTables@2022-05-01' = {
  name: 'hubRouteTable2'
  location: location2
  properties: {
    disableBgpRoutePropagation: false
    routes: [
      {
        name: 'hubTointernet'
        properties: {
          addressPrefix: '0.0.0.0/0'
          nextHopType: 'Internet'
        }
      }
      {
        name: 'toSpokeVnet1'
        properties: {
          addressPrefix: '10.1.0.0/24'
          nextHopType: 'VirtualAppliance'
          nextHopIpAddress: azureFirewall1.properties.ipConfigurations[0].properties.privateIPAddress
        }
      }
      {
        name: 'toSpokeVnet2'
        properties: {
          addressPrefix: '10.2.0.0/24'
          nextHopType: 'VirtualAppliance'
          nextHopIpAddress: azureFirewall1.properties.ipConfigurations[0].properties.privateIPAddress
        }
      }
    ]
  }
  tags: {
    displayName: 'hubRouteTable2'
  }
}

// Define the route table toward Azure Fireweall #1
resource spokeRouteTable1 'Microsoft.Network/routeTables@2022-05-01' = {
  name: 'spokeRouteTable1'
  location: location1
  properties: {
    disableBgpRoutePropagation: false
    routes: [
      {
        name: 'toHubVnet1'
        properties: {
          addressPrefix: '0.0.0.0/0'
          nextHopType: 'VirtualAppliance'
          nextHopIpAddress: azureFirewall1.properties.ipConfigurations[0].properties.privateIPAddress
        }
      }
    ]
  }
  tags: {
    displayName: 'spokeRouteTable1'
  }
}

// Define the routae table toward Azure Firewall #2
resource spokeRouteTable2 'Microsoft.Network/routeTables@2022-05-01' = {
  name: 'spokeRouteTable2'
  location: location2
  properties: {
    disableBgpRoutePropagation: false
    routes: [
      {
        name: 'toHubVnet2'
        properties: {
          addressPrefix: '0.0.0.0/0'
          nextHopType: 'VirtualAppliance'
          nextHopIpAddress: azureFirewall2.properties.ipConfigurations[0].properties.privateIPAddress
        }
      }
    ]
  }
  tags: {
    displayName: 'spokeRouteTable2'
  }
}

// Define the NIC for vm01
resource vmnic01 'Microsoft.Network/networkInterfaces@2022-09-01' = {
  name: 'vmnic01'
  location: location1
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          subnet: {
            id: spokevnet1.properties.subnets[0].id
          }
          privateIPAllocationMethod: 'Dynamic'
        }
      }
    ]
  }
  tags: {
    displayName: 'vmnic01'
  }
}

// Define the NIC for vm02
resource vmnic02 'Microsoft.Network/networkInterfaces@2022-09-01' = {
  name: 'vmnic02'
  location: location1
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          subnet: {
            id: spokevnet2.properties.subnets[0].id
          }
          privateIPAllocationMethod: 'Dynamic'
        }
      }
    ]
  }
  tags: {
    displayName: 'vmnic02'
  }
}

// Define the NIC for vm03
resource vmnic03 'Microsoft.Network/networkInterfaces@2022-09-01' = {
  name: 'vmnic03'
  location: location2
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          subnet: {
            id: spokevnet3.properties.subnets[0].id
          }
          privateIPAllocationMethod: 'Dynamic'
        }
      }
    ]
  }
  tags: {
    displayName: 'vmnic03'
  }
}

// Define the NIC for opvm
resource opvmnic 'Microsoft.Network/networkInterfaces@2022-09-01' = {
  name: 'opvmnic'
  location: location1
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          subnet: {
            id: opsvnet.properties.subnets[0].id
          }
          privateIPAllocationMethod: 'Dynamic'
        }
      }
    ]
  }
  tags: {
    displayName: 'vmnic04'
  }
}

// Define vm01
resource vm01 'Microsoft.Compute/virtualMachines@2022-03-01' = {
  name: 'vm01'
  location: location1
  properties: {
    hardwareProfile: {
      vmSize: 'Standard_D2as_v4'
    }
    osProfile: {
      computerName: 'vm01'
      adminUsername: adminUserName
      adminPassword: adminPassword
    }
    storageProfile: {
      imageReference: {
        publisher: 'Canonical'
        offer: '0001-com-ubuntu-confidential-vm-jammy'
        sku: '22_04-lts-cvm'
        version: 'latest'
      }
      osDisk: {
        name: 'osdisk01'
        caching: 'ReadWrite'
        createOption: 'FromImage'
      }
    }
    networkProfile: {
      networkInterfaces: [
        {
          id: vmnic01.id
        }
      ]
    }
  }
}

// Define vm02
resource vm02 'Microsoft.Compute/virtualMachines@2022-03-01' = {
  name: 'vm02'
  location: location1
  properties: {
    hardwareProfile: {
      vmSize: 'Standard_D2as_v4'
    }
    osProfile: {
      computerName: 'vm02'
      adminUsername: adminUserName
      adminPassword: adminPassword
    }
    storageProfile: {
      imageReference: {
        publisher: 'Canonical'
        offer: '0001-com-ubuntu-confidential-vm-jammy'
        sku: '22_04-lts-cvm'
        version: 'latest'
      }
      osDisk: {
        name: 'osdisk02'
        caching: 'ReadWrite'
        createOption: 'FromImage'
      }
    }
    networkProfile: {
      networkInterfaces: [
        {
          id: vmnic02.id
        }
      ]
    }
  }
}

// Define vm03
resource vm03 'Microsoft.Compute/virtualMachines@2022-03-01' = {
  name: 'vm03'
  location: location2
  properties: {
    hardwareProfile: {
      vmSize: 'Standard_D2as_v4'
    }
    osProfile: {
      computerName: 'vm03'
      adminUsername: adminUserName
      adminPassword: adminPassword
    }
    storageProfile: {
      imageReference: {
        publisher: 'Canonical'
        offer: '0001-com-ubuntu-confidential-vm-jammy'
        sku: '22_04-lts-cvm'
        version: 'latest'
      }
      osDisk: {
        name: 'osdisk03'
        caching: 'ReadWrite'
        createOption: 'FromImage'
      }
    }
    networkProfile: {
      networkInterfaces: [
        {
          id: vmnic03.id
        }
      ]
    }
  }
}

// Define opsvm
resource opsvm 'Microsoft.Compute/virtualMachines@2022-03-01' = {
  name: 'opsvm'
  location: location1
  properties: {
    hardwareProfile: {
      vmSize: 'Standard_D2as_v4'
    }
    osProfile: {
      computerName: 'opsvm'
      adminUsername: adminUserName
      adminPassword: adminPassword
    }
    storageProfile: {
      imageReference: {
        publisher: 'Canonical'
        offer: '0001-com-ubuntu-confidential-vm-jammy'
        sku: '22_04-lts-cvm'
        version: 'latest'
      }
      osDisk: {
        name: 'osdisk04'
        caching: 'ReadWrite'
        createOption: 'FromImage'
      }
    }
    networkProfile: {
      networkInterfaces: [
        {
          id: opvmnic.id
        }
      ]
    }
  }
}
GitHubで編集を提案

Discussion