Azure Landing Zone Networking: Hub-Spoke s Firewallem
Po nasazení základů Landing Zone z prvního dílu přijde fáze, která rozhodne o tom, jestli budete mít infrastrukturu pod kontrolou, nebo jestli za půl roku budete zjišťovat, proč osm týmů provozuje osm různých firewallů s osmi různými konfiguracemi DNS. V českém enterprise prostředí, kde se navíc řeší NIS2 a požadavky na viditelnost síťového provozu, je centralizovaná hub-spoke architektura prakticky nutnost.
Náročnost: 3-5 dní na kompletní hub-spoke nasazení včetně pravidel firewallu Náklady: cca 850 $/měsíc (Azure Firewall Basic: 650 $, VPN Gateway: 140 $, Private DNS: 25 $, peering: cca 35 $) Předpoklady: Dokončený základ Landing Zone (1. díl), Azure subscription s rolí Contributor na connectivity subscription, VS Code s Bicep rozšířením
Co se změnilo v roce 2025
Tři zásadní novinky mění pravidla hry pro hub-spoke nasazení.
Azure Firewall Basic SKU je v GA. Dříve byla nejlevnější varianta Standard za zhruba 912 $/měsíc. Basic vychází na přibližně 650 $ a nabízí stejné L3/L4 filtrování -- jen s limitem propustnosti 250 Mbps a bez IDPS signatur. Pro české středně velké firmy s jednotkami spoke VNetů to většinou bohatě stačí.
Azure Virtual Network Manager je obecně dostupný. Končí éra ručního peeringu přes Bicep smyčky. AVNM umožňuje definovat hub-spoke nebo mesh topologie deklarativně a automaticky spravuje peering. Nové spoke VNety se díky dynamickému členství skupin (na základě tagů) propeerují samy.
Private DNS Resolver nahrazuje conditional forwardery. Provozovat BIND nebo Windows DNS servery v hub VNetu pro on-premises DNS rezoluci už nemá smysl. Azure DNS Private Resolver poskytuje inbound a outbound endpointy přímo v hub VNetu za zlomek provozní náročnosti.
Proč na tom záleží
Bez sdíleného hubu si každý aplikační tým řeší síťování po svém. Viděl jsem to u jednoho výrobního podniku v Brně, kde šest vývojových týmů provozovalo infrastrukturu nezávisle:
| Přístup | Měsíční náklady | Provozní režie |
|---|---|---|
| Sdílený hub-spoke (1 firewall) | cca 850 $ | Centrální tým spravuje pravidla |
| Firewall na každý spoke (6 týmů) | cca 5 500 $ | Každý tým spravuje svá pravidla |
| Bez firewallu (přímý internet) | cca 50 $ | Nulová viditelnost, neprošlo auditem |
Varianta s per-spoke firewally měla navíc šest různých sad pravidel, šest různých log konfigurací a šest týmů, které DNS řešily každý jinak. Když auditor NIS2 požadoval přehled veškerého egress provozu za 90 dní, trvalo tři dny jen shromáždění logů.
Hub-spoke toto centralizuje: jeden firewall, jedna DNS konfigurace, jedna sada route tabulek, jedno místo kam se podívat, když něco nefunguje.
Implementace: Hub-Spoke v Bicep modulech
Architektura se skládá ze čtyř Bicep modulů: hub VNet, spoke VNet(y), Azure Firewall s policy a Private DNS zóny.
Hub VNet modul
// modules/network/hub-vnet.bicep
// Nasazení hub virtuální sítě s požadovanými subnety
@description('Azure region pro hub nasazení')
param location string = 'westeurope'
@description('Identifikátor prostředí')
param environmentType string = 'prod'
@description('Adresní prostor hub VNetu')
param hubAddressPrefix string = '10.0.0.0/16'
resource hubVnet 'Microsoft.Network/virtualNetworks@2023-11-01' = {
name: 'vnet-hub-${location}-001'
location: location
properties: {
addressSpace: {
addressPrefixes: [hubAddressPrefix]
}
subnets: [
{
name: 'AzureFirewallSubnet'
properties: {
addressPrefix: '10.0.1.0/26'
}
}
{
name: 'GatewaySubnet'
properties: {
addressPrefix: '10.0.2.0/27'
}
}
{
name: 'AzureBastionSubnet'
properties: {
addressPrefix: '10.0.3.0/26'
}
}
{
name: 'snet-dns-inbound-${location}'
properties: {
addressPrefix: '10.0.4.0/28'
delegations: [
{
name: 'Microsoft.Network.dnsResolvers'
properties: {
serviceName: 'Microsoft.Network/dnsResolvers'
}
}
]
}
}
]
}
}
output hubVnetId string = hubVnet.id
output hubVnetName string = hubVnet.name
output firewallSubnetId string = hubVnet.properties.subnets[0].idNázev AzureFirewallSubnet je povinný -- Azure odmítne jakýkoliv jiný název subnetu pro firewall. Totéž platí pro GatewaySubnet. Při prvním nasazení Landing Zone jsem si tím prošel: pojmenoval jsem subnet snet-firewall a dostal jsem chybu, která neukazovala na název subnetu, ale na neplatnou subnet konfiguraci.
Spoke VNet modul s peeringem
// modules/network/spoke-vnet.bicep
// Nasazení spoke VNetu a konfigurace obousměrného peeringu s hubem
@description('Identifikátor spoke, např. app01, data01')
param spokeName string
@description('Resource ID hub VNetu pro peering')
param hubVnetId string
@description('Název hub VNetu pro referenci peeringu')
param hubVnetName string
param location string = 'westeurope'
param spokeAddressPrefix string
resource spokeVnet 'Microsoft.Network/virtualNetworks@2023-11-01' = {
name: 'vnet-spoke-${spokeName}-${location}-001'
location: location
properties: {
addressSpace: {
addressPrefixes: [spokeAddressPrefix]
}
subnets: [
{
name: 'snet-default-${spokeName}'
properties: {
addressPrefix: spokeAddressPrefix
routeTable: {
id: spokeRouteTable.id
}
}
}
]
}
}
// Směrování veškerého provozu přes hub firewall
resource spokeRouteTable 'Microsoft.Network/routeTables@2023-11-01' = {
name: 'rt-spoke-${spokeName}-${location}'
location: location
properties: {
disableBgpRoutePropagation: true
routes: [
{
name: 'route-to-firewall'
properties: {
addressPrefix: '0.0.0.0/0'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: '10.0.1.4' // Privátní IP Azure Firewallu
}
}
]
}
}
// Spoke -> Hub peering
resource spokeToHub 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2023-11-01' = {
parent: spokeVnet
name: 'peer-${spokeName}-to-hub'
properties: {
remoteVirtualNetwork: {
id: hubVnetId
}
allowForwardedTraffic: true
allowGatewayTransit: false
useRemoteGateways: true
}
}
output spokeVnetId string = spokeVnet.idTo disableBgpRoutePropagation: true na route tabulce je něco, co zapomenete přesně jednou. Bez toho BGP routy z VPN Gateway přepíšou vaše user-defined routy a provoz obejde firewall. Logy firewallu budou ukazovat nulový provoz, zatímco všechno běží -- až do bezpečnostního auditu.
Azure Firewall s Policy
// modules/network/firewall.bicep
// Nasazení Azure Firewall Basic s kolekcemi aplikačních a síťových pravidel
param location string = 'westeurope'
param firewallSubnetId string
param skuTier string = 'Basic'
resource fwPublicIp 'Microsoft.Network/publicIPAddresses@2023-11-01' = {
name: 'pip-fw-hub-${location}-001'
location: location
sku: {
name: 'Standard'
}
properties: {
publicIPAllocationMethod: 'Static'
}
}
resource fwPolicy 'Microsoft.Network/firewallPolicies@2023-11-01' = {
name: 'fwpol-hub-${location}-001'
location: location
properties: {
sku: {
tier: skuTier
}
threatIntelMode: 'Alert'
}
}
resource networkRuleCollection 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2023-11-01' = {
parent: fwPolicy
name: 'rcg-network-baseline'
properties: {
priority: 200
ruleCollections: [
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
name: 'rc-allow-spoke-to-spoke'
priority: 210
action: {
type: 'Allow'
}
rules: [
{
ruleType: 'NetworkRule'
name: 'allow-rfc1918'
sourceAddresses: ['10.0.0.0/8']
destinationAddresses: ['10.0.0.0/8']
destinationPorts: ['*']
ipProtocols: ['TCP', 'UDP']
}
]
}
]
}
}
resource appRuleCollection 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2023-11-01' = {
parent: fwPolicy
name: 'rcg-application-baseline'
dependsOn: [networkRuleCollection]
properties: {
priority: 300
ruleCollections: [
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
name: 'rc-allow-azure-management'
priority: 310
action: {
type: 'Allow'
}
rules: [
{
ruleType: 'ApplicationRule'
name: 'allow-azure-management'
sourceAddresses: ['10.0.0.0/8']
protocols: [
{
protocolType: 'Https'
port: 443
}
]
targetFqdns: [
'management.azure.com'
'login.microsoftonline.com'
'*.blob.core.windows.net'
]
}
]
}
]
}
}
resource firewall 'Microsoft.Network/azureFirewalls@2023-11-01' = {
name: 'fw-hub-${location}-001'
location: location
properties: {
sku: {
name: 'AZFW_VNet'
tier: skuTier
}
firewallPolicy: {
id: fwPolicy.id
}
ipConfigurations: [
{
name: 'fw-ipconfig'
properties: {
subnet: {
id: firewallSubnetId
}
publicIPAddress: {
id: fwPublicIp.id
}
}
}
]
}
}
output firewallPrivateIp string = firewall.properties.ipConfigurations[0].properties.privateIPAddressDůležitý detail: dependsOn: [networkRuleCollection] na kolekcí aplikačních pravidel. Firewall policy rule collection groups se nasazují paralelně a Azure vrátí conflict error, pokud se dvě kolekce pokusí zapsat současně. V malém testovacím nasazení se to neprojeví, ale v CI/CD pipeline to padá pravidelně.
Výsledky z praxe
Po nasazení hub-spoke topologie pro středně velkou českou SaaS firmu se čtyřmi spoke VNety (aplikace, data, sdílené služby, staging) vypadá výstup z Azure CLI takto:
$ az network firewall show \
--name fw-hub-westeurope-001 \
--resource-group rg-connectivity-prod \
--query '{name:name, sku:sku.tier, provisioningState:provisioningState, privateIp:ipConfigurations[0].privateIPAddress, threatIntelMode:threatIntelMode}' \
--output table
Name Sku ProvisioningState PrivateIp ThreatIntelMode
------------------------ ----- ------------------- ----------- -----------------
fw-hub-westeurope-001 Basic Succeeded 10.0.1.4 Alert
Měsíční účet za celý hub networking stack se ustálil na:
| Zdroj | Měsíční náklady |
|---|---|
| Azure Firewall Basic | 648,00 $ |
| VPN Gateway (VpnGw1) | 138,70 $ |
| Private DNS Zones (4 zóny) | 24,00 $ |
| VNet Peering (4 spoke, cca 500 GB) | 35,00 $ |
| Public IP (Standard, statická) | 3,65 $ |
| Celkem | 849,35 $ |
Nejzákeřnější problém, na který jsme narazili: chyba SubnetNotInSameVnet při zakládání peeringu. Bicep deployment se pokusil vytvořit peering dříve, než se subnet spoke VNetu plně zprovoznil. Řešením bylo přidat explicitní dependsOn na peering resource odkazující na spoke VNet -- implicitní závislost přes parent: spokeVnet nestačila, protože subnety jsou inline resources.
Klíčové poznatky
- Začněte s Azure Firewall Basic, pokud nepotřebujete IDPS nebo TLS inspekci. SKU lze upgradovat bez nutnosti redeploymentu.
- Vždy vypněte BGP route propagation na spoke route tabulkách. Bez toho BGP routy z VPN Gateway přepíšou vaše UDR a provoz tiše obejde firewall.
- Používejte Private DNS Resolver místo DNS serverů. Provozní režie údržby BIND nebo Windows DNS forwarderů v hub VNetu se už nevyplatí.
- Pro NIS2 compliance je centrální firewall s jednotnými logy zásadní -- auditní stopa egress provozu z jednoho místa výrazně zjednodušuje reporting.
- Hub rozpočtujte jako sdílený platformní náklad, ne jako účet pro jednotlivé aplikační týmy. Jakmile začnete firewall náklady dělit per-spoke, týmy budou hledat cesty jak hub obejít.
Se základy z prvního dílu a funkční hub-spoke sítí máte infrastrukturu pod kontrolou. Dalším krokem je governance vrstva -- Azure Policy a compliance dashboardy, které pokryjeme v třetím dílu série. Pokud chcete urychlit cestu ke cloudové infrastruktuře, náš konzultační tým pro cloudovou architekturu pokrývá celý životní cyklus Landing Zone nasazení.
O autorovi

Martin Rylko
Senior Cloud Architect & DevOps Engineer
Více než 14 let v IT – od on-premises datacenter a Hyper-V clusteringu po cloudovou infrastrukturu v Microsoft Azure. Specializuji se na Landing Zones, IaC automatizaci, Kubernetes a bezpečnostní compliance.
Nejcastejsi dotazy
Kolik stojí Azure Firewall v hub-spoke Landing Zone?▾
Kdy je lepší hub-spoke a kdy Azure Virtual WAN?▾
Existují limity na VNet peering v Azure?▾
Jak řešit DNS překlad mezi spokes v hub-spoke topologii?▾
Mohlo by vás zajímat
Azure Landing Zone v Bicepu: Enterprise nasazení
Nasaďte enterprise-ready Azure Landing Zone pomocí Bicep modulů. Hub-spoke síťová architektura, governance politiky a integrace do CI/CD pipeline.
ČístAzure Landing Zone Governance: Policy ve velkém
Azure Policy governance pro Landing Zones ve velkém měřítku. Vlastní definice politik, přiřazení iniciativ, compliance dashboardy a nákladové guardrails.
ČístBicep CI/CD: GitHub Actions pipeline pro Azure
Produkční deployment pipeline pro Bicep v GitHub Actions. What-if náhledy, schvalování prostředí, OIDC autentizace a rollback strategie.
Číst