Nasazení minimální hub-spoke topologie v Azure - část 3

29.10.2026

Pokračování předchozího článku. K hub-spoke topologii přidáme management group hierarchii, Azure politiky a nahradíme stávající Linux router za Azure Firewall.

Slovník

Azure Landing Zone (ALZ) Ověřená a doporučená architektura pro řízení, zabezpečení a škálování Azure prostředí s více subskripcemi. Skládá se z platform landing zóny, která tvoří centrální základ se sdílenými službami, a z workload landing zón, kam se nasazují aplikace v rámci definovaných pravidel.
Hub-spoke topologie Doporučená síťová topologie v Azure, kde je centrální síť (hub) propojena s dalšími sítěmi (spokes). Alternativou je Virtual WAN.
Management Group (MG) Skupina, která sdružuje subskripce. Politiky a oprávnění přiřazené na management skupinu se automaticky dědí na všechny subskripce pod ní, což usnadňuje centrální správu.
Azure Policy Pravidla, která se přiřazují na management skupinu, subskripci nebo resource group. Politiky slouží k vynucení standardů nebo compliance.
Azure Firewall Spravovaná služba, která filtruje síťový provoz podle definovaných pravidel. V hub-spoke topologii se obvykle umisťuje do hubu a centrálně kontroluje provoz mezi spoky i do internetu.
User Defined Route (UDR) Směrovací pravidlo, které přepisuje výchozí směrování v Azure. Zapisuje se do route table, která se přiřazuje k subnetu. Pomocí UDR lze například směrovat veškerý provoz ze subnetu na firewall nebo router v hubu.
Privátní subnet Subnet s vypnutým přístupem do internetu (default outbound access). Prostředky v něm se do internetu dostanou jen přes explicitně nastavenou cestu, například NAT Gateway, veřejnou IP adresu nebo UDR směrující provoz na firewall či router v hubu.

Obsah

Úvod

V předchozím článku jsme vytvořili hub-spoke topologii s centrálním hubem a dvěma spokes (aplikacemi). Dále máme vytvořený Linux server, který funguje jako router, privátní DNS zónu pro překlad doménových jmen na IP adresy, Azure Bastion pro vzdálené přihlášení na virtuální servery a NSG pro filtrování síťového provozu na subnetech. Dnes přidáme management group hierarchii, která nám pomůže s centrální správou a využijeme ji k nastavení Azure politik pro spoke subskripce, které zařadíme do management skupiny Corp. Linux server v hubu nahradíme Azure Firewallem, přes který budeme routovat provoz do internetu, a zároveň přepneme spoke subnety do privátního režimu.

Příprava prostředí

V Azure portálu vytvoříme novou Management subskripci, kterou budeme potřebovat pro Log Analytics workspace, do kterého se budou ukládat logy z Azure Firewallu. Celkem musíme mít k dispozici tyto subskripce: Management, Connectivity , app1-dev , app2-dev. Dále musíme mít nainstalovaný Azure CLI a Bicep CLI. Veškeré příkazy v článku spouštíme z PowerShell terminálu.
winget install --exact --id "Microsoft.AzureCLI" 
az bicep upgrade
Stáhneme si projekt s deployment scripty.
git clone "https://github.com/Ninjanaut/azure-hub-spoke-bicep-template-03.git"
Oproti předchozí části projekt obsahuje několik změn, primárně jednodušší složkovou strukturu, centrální config a pomocné scripty.
config/
├── platform.json
└── platform.bicep

landing-zones/
├── main.bicep
├── app1-dev.bicepparam
└── app2-dev.bicepparam

platform/
├── connectivity/
│   ├── main.bicep
│   └── main.bicepparam
├── management/
│   ├── main.bicep
│   └── main.bicepparam
└── governance/
    └── mgmt-groups/
        └── corp/
            ├── main.bicep
            ├── main.bicepparam
            └── policy-definitions/
                └── Deny-Subnet-Without-Udr.alz_policy_definition.json
                
scripts/
├── setup-entra.ps1
├── setup-management-groups.ps1
├── deploy-governance.ps1
├── deploy.ps1
└── cleanup.ps1
V souboru platform.json musíme správně nastavit následující hodnoty
tenantId
sshPublicKeyPath
testUserUpn
management.subscriptionId
connectivity.subscriptionId
landingZones.app1-dev.subscriptionId
landingZones.app2-dev.subscriptionId
Vyplněné hodnoty se pak načítají v souboru platform.bicep a vstupují do deploymentu.

Deployment

Nejdříve připravíme uživatele a skupiny v Entra ID. Poté vytvoříme management group hierarchii a přiřadíme do ní subskripce. Dále nasadíme politiky (Azure Policy) a nakonec spustíme deployment do všech prostředí (management, hub, app1, app2). Skripty v sobě obsahují kontroly a je možné je spustit opakovaně, aniž by vznikaly duplicity.

# Přihlásíme se do Azure
az login --tenant "<tenant-id>"

# Vytvoříme testovacího uživatele a security groups, do kterých ho přidáme.
# ID vytvořených skupin se automaticky uloží do souboru config/platform.json.
./scripts/setup-entra.ps1

# Vytvoříme management group hierarchii a přiřadíme do ní subskripce.
./scripts/setup-management-groups.ps1   

# Na Corp management group skupině nastavíme politiky (Azure Policy).
./scripts/deploy-governance.ps1

# Nasazení do management, hub, app1 a app2 subskripcí.
./scripts/deploy.ps1

Oproti první a druhé části se navíc vytvoří následující resources:

VM router, včetně router subnetu, se nevytvoří, protože byl nahrazený Azure Firewallem.

Změny

Management group hierarchie

Skript setup-management-groups.ps1 vytvořil hierarchii management skupin podle referenční architektury a přiřadil do ní subskripce. Corp skupina obsahuje aplikace, které nejsou přímo vystavené do internetu ani do něj nemají přímý přístup. Pokud by aplikace měla komunikovat s internetem přímo, a ne třeba přes platformní firewall nebo aplikační gateway, patřila by podle ALZ do Online skupiny.

Azure Policy

Skript deploy-governance.ps1 nastavil na Corp management skupině tři politiky. První dvě jsou built-in a třetí má definici v souboru Deny-Subnet-Without-Udr.alz_policy_definition.json. Všechny politiky mají efekt Deny a režim vynucení Policy enforcement: Default (enforce). Po přiřazení tedy začnou platit a ihned blokovat případné nepovolené operace.

  1. Deny the creation of public IP (built-in)
  2. Deny network interfaces having a public IP (built-in)
  3. Subnets should have a User Defined Route (custom)
Abychom mohli vidět, které stávající resources jsou a nejsou v souladu s nastavenými politikami, skript na subskripcích zaregistroval resource providera Microsoft.PolicyInsights. Na úrovni management skupiny si pak můžeme zobrazit následující report.
Pokud bychom chtěli například vytvořit veřejnou IP adresu, tak tomu politika zabrání s následující chybou.

Azure Firewall

Původní Linux server, který fungoval jako router, jsme nahradili službou Azure Firewall. Skript deploy.ps1 vytvořil Azure Firewall v tieru Basic. Ten kromě subnetu AzureFirewallSubnet vyžaduje také AzureFirewallManagementSubnet a druhou veřejnou IP adresu pro management provoz. Tiery Standard a Premium dedikovaný management subnet ani druhou veřejnou IP adresu nepotřebují. Abychom vyzkoušeli, zda nám vše funguje, tak se přihlásíme pod testovacím uživatelem do Azure portálu a pomocí Bastion služby se přihlásíme k virtuálnímu serveru aplikace 1 (10.0.4.4) a pingneme DNS název virtuálního serveru aplikace 2 (10.0.8.4).
Dále vyzkoušíme přístup do internetu. Webová služba https://api.ipify.org (172.67.74.152) vrací veřejnou IP adresu, ze které požadavek přišel. Ta by měla být shodná s naší veřejnou IP adresou pip-afw-hub-shared-swedencentral-001.
curl -s -m 10 "https://api.ipify.org"
Po několika minutách si můžeme provoz zobrazit i ve firewall logu, který se ukládá do Log Analytics workspace v Management subskripci.
$workspaceId = az monitor log-analytics workspace show `
  -g "rg-management-shared" `
  -n "log-management-shared-swedencentral-001" `
  --subscription "<management-subscription-id>" `
  --query customerId `
  -o tsv

az monitor log-analytics query `
  -w $workspaceId `
  --analytics-query "AZFWNetworkRule | where TimeGenerated > ago(30m) | project TimeGenerated, SourceIp, DestinationIp, Protocol, Action, Rule" `
  -o table

# Result
Action    DestinationIp    Protocol     Rule                  SourceIp    TableName      TimeGenerated
--------  ---------------  -----------  --------------------  ----------  -------------  ---------------------------
Allow     10.0.8.4         ICMP Type=8  allow-spoke-to-spoke  10.0.4.4    PrimaryResult  2026-10-29T14:43:12.538532Z
Allow     172.67.74.152    TCP          allow-spoke-internet  10.0.4.4    PrimaryResult  2026-10-29T14:44:33.089767Z

Privátní subnet a UDR

Všechny spoke subnety jsou nově nastavené jako privátní, pomocí vlastnosti defaultOutboundAccess: false v souboru landing-zones/main.bicep.
Aby se virtuální server mohl připojit do internetu, musí mít subnet nastavenou výchozí routu (0.0.0.0/0) na firewall. Firewall odchozí provoz kontroluje a překládá ho na svou veřejnou IP adresu (SNAT).

Dealokace a smazání

Vzhledem k provozním nákladům můžeme Firewall dealokovat.
$fw = Get-AzFirewall -ResourceGroupName "rg-hub-shared" -Name "afw-hub-shared-swedencentral-001"
$fw.Deallocate()
Set-AzFirewall -AzureFirewall $fw

# Kontrola, dealokovaný fw by měl vrátit "0"
# (Get-AzFirewall -ResourceGroupName rg-hub-shared -Name afw-hub-shared-swedencentral-001).IpConfigurations.Count
Můžeme smazat i službu bastion, pokud jsme jí nasadili pomocí parametru deployBastion = true v souboru platform/connectivity/main.bicepparam.
az network bastion delete -g "rg-hub-shared" -n "bas-hub-shared-swedencentral-001" --subscription "<hub-subscription-id>"
az network public-ip delete -g "rg-hub-shared" -n "pip-bas-hub-shared-swedencentral-001" --subscription "<hub-subscription-id>"
Dealokujeme virtuální servery.
az vm deallocate -g "rg-spoke-app1-dev" -n "vm-conntest-app1-dev-swedencentral-001" --subscription "<app1-subscription-id>"
az vm deallocate -g "rg-spoke-app2-dev" -n "vm-conntest-app2-dev-swedencentral-001" --subscription "<app2-subscription-id>"
Případně můžeme smazat všechny vytvořené resources příkazem
./scripts/cleanup.ps1

Závěr

Vytvořili jsme management group hierarchii, abychom mohli prostředí centrálně spravovat. Na skupině Corp jsme nastavili Azure politiky. Spoke subnety jsme přepnuli do privátního režimu, aktualizovali směrování a centrální Linux server, který fungoval jako router, jsme nahradili Azure Firewallem. Příště se podíváme na VPN Gateway 🙌.

Odkazy