Azure Firewall e NSGs na Landing Zone

Neste artigo você vai configurar o Azure Firewall e NSGs na Landing Zone Hub-Spoke criada no artigo anterior, adicionando uma camada de segurança centralizada no Hub e regras de segmentação por camada no Spoke. Vamos implantar o Firewall com Firewall Policy, criar User-Defined Routes para forçar todo o tráfego do Spoke pelo Firewall, e definir Network Security Groups específicos para frontend, backend, data e AKS — tudo com Terraform em Brazil South e East US.
- 📖 Art. 01: Planejamento de DR Azure e Landing Zone
- 📖 Art. 02: Hub-Spoke Landing Zone com Terraform
- ⚙️ Art. 03 (este): Azure Firewall e NSGs na Landing Zone
- 🔒 Art. 04: Azure Bastion na Landing Zone sem IP Público
- 🔒 Art. 05: Azure Front Door e Traffic Manager para Failover
- 🔒 Art. 06: DNS Privado Multi-Região no Azure
- 🔒 Art. 07: Azure Site Recovery para VMs Multi-Região
- 🔒 Art. 08: Azure Storage com Geo-Replicação para DR
- 🔒 Art. 09: AKS Multi-Região com Failover no Azure
- 🔒 Art. 10: Velero no AKS para Backup e Restore Cross-Region
- 🔒 Art. 11: Runbooks de Failover no Azure Automation
- 🔒 Art. 12: Simular um DR no Azure sem Impacto em Produção
- 🔒 Art. 13: Monitoramento do DR no Azure com Azure Monitor
Sumário
- Azure Firewall vs NSG — quando usar cada um
- Arquitetura do lab
- Pré-requisitos
- Variáveis de ambiente
- Passo 1 — Azure Firewall Policy e Azure Firewall
- Passo 2 — User-Defined Routes (UDR)
- Passo 3 — NSGs por camada
- Passo 4 — Executar o Terraform
- Verificar a configuração
- Troubleshooting
- Limpeza dos recursos
- Próximos passos
Azure Firewall vs NSG — quando usar cada um
Os azure firewall e nsgs na landing zone têm papéis complementares e não concorrentes. Entender a diferença é fundamental para não criar brechas de segurança nem bloquear tráfego legítimo.
| Característica | Azure Firewall | NSG |
|---|---|---|
| Camada OSI | L3–L7 (FQDN, TLS inspection) | L3–L4 (IP e porta) |
| Estado | Stateful | Stateless (avalia cada pacote) |
| Escopo | Centralizado no Hub — protege múltiplas VNets | Por subnet ou NIC |
| Regras FQDN | Sim (ex: *.windowsupdate.com) |
Não — apenas IPs |
| Custo | Alto (instância fixa + dados processados) | Gratuito |
| Posição na Landing Zone | Hub — AzureFirewallSubnet | Spoke — cada subnet do workload |
Portanto, a estratégia usada neste artigo é combinar os azure firewall e nsgs na landing zone em duas camadas: o Firewall inspeciona e filtra todo o tráfego Norte-Sul (Spoke → Internet) e Leste-Oeste entre Spokes, enquanto os NSGs impõem microssegmentação dentro do próprio Spoke, limitando a comunicação entre as camadas de aplicação.
Arquitetura do lab
Os recursos de Azure Firewall e NSGs na Landing Zone complementam a Hub-Spoke do Art. 02. O fluxo de tráfego após a configuração é:fego após a configuração é: workload no Spoke → UDR força para o Firewall no Hub → Firewall inspeciona → Internet (ou outra VNet). Os NSGs atuam antes do tráfego chegar ao Firewall, como primeira linha de defesa na subnet.
| Recurso | Região | Descrição |
|---|---|---|
| afwp-drlab-brazilsouth | Brazil South | Firewall Policy com regras de rede e aplicação |
| afwp-drlab-eastus | East US | Firewall Policy com regras de rede e aplicação |
| afw-drlab-brazilsouth | Brazil South | Azure Firewall SKU Standard na AzureFirewallSubnet |
| afw-drlab-eastus | East US | Azure Firewall SKU Standard na AzureFirewallSubnet |
| pip-afw-drlab-brazilsouth | Brazil South | IP público estático do Firewall primário |
| pip-afw-drlab-eastus | East US | IP público estático do Firewall secundário |
| rt-spoke-brazilsouth | Brazil South | Route Table com default route → Firewall |
| rt-spoke-eastus | East US | Route Table com default route → Firewall |
| nsg-frontend / backend / data / aks | BRS + EUS | NSG por camada com regras de microssegmentação |
Regras NSG por camada
| NSG | Regra | Origem | Porta | Ação |
|---|---|---|---|---|
| nsg-frontend | allow-https-inbound | Internet | 443 | Allow |
| nsg-frontend | allow-http-inbound | Internet | 80 | Allow |
| nsg-frontend | deny-all-inbound | * | * | Deny (p.4096) |
| nsg-backend | allow-frontend-inbound | 10.2.1.0/24 | 8080 | Allow |
| nsg-backend | deny-internet-inbound | Internet | * | Deny (p.200) |
| nsg-data | allow-backend-sql | 10.2.2.0/24 | 1433 | Allow |
| nsg-data | deny-all-inbound | * | * | Deny (p.4096) |
| nsg-aks | allow-lb-inbound | AzureLoadBalancer | 443 | Allow |
Pré-requisitos
Segurança: Os comandos deste artigo utilizam variáveis de ambiente para credenciais. Nunca insira IDs de subscription, senhas ou chaves diretamente nos comandos. Use um arquivo .env local (não versionado) ou o Azure Key Vault para armazenar segredos.
- Hub-Spoke Landing Zone do Art. 02 provisionada (4 VNets + subnets + peerings)
- Terraform ≥ 1.5 instalado —
terraform -version - Azure CLI autenticado —
az account show - IP privado do Firewall anotado após o deploy (será usado na Route Table)
O custo do Azure Firewall e NSGs na Landing Zone deve ser considerado: o Azure Firewall SKU Standard custa aproximadamente USD 1,25/hora por instância — por isso, neste lab, recomenda-se destruir o Fir lab, recomenda-se destruir o Firewall ao final de cada sessão de testes e recriá-lo quando necessário. O custo dos demais recursos deste artigo é negligenciável.
Variáveis de ambiente
No Azure Firewall e NSGs na Landing Zone, além da subscription_id, o módulo recebe o IP privado do Firewall para configurar as rotas da UDR. O IP privado éara configurar as rotas da UDR. O IP privado é atribuído automaticamente pelo Azure na AzureFirewallSubnet (normalmente o primeiro IP disponível, como 10.0.0.4):
export TF_VAR_subscription_id="<SUBSCRIPTION_ID>"
# Obter o IP privado do Firewall após o apply do passo 1
export TF_VAR_firewall_private_ip_primary="10.0.0.4" # Hub Brazil South
export TF_VAR_firewall_private_ip_secondary="10.1.0.4" # Hub East US
Para confirmar o IP privado após criar o Firewall, use o comando abaixo. O valor real pode variar conforme a subnet:
az network firewall show \
--name afw-drlab-brazilsouth \
--resource-group rg-drlab-network-brazilsouth \
--query "ipConfigurations[0].privateIPAddress" \
-o tsv
Passo 1 — Azure Firewall Policy e Azure Firewall
A Firewall Policy é o modelo moderno de gerenciamento de regras — substitui as coleções clássicas diretamente no recurso do Firewall. Ela permite reutilizar a mesma política em múltiplos Firewalls e herdar regras de uma política pai. Neste lab, cada região tem sua própria política para facilitar a gestão independente:
# Buscar recursos do Art. 02 via data sources
data "azurerm_resource_group" "this" {
name = "rg-drlab-network-brazilsouth"
}
data "azurerm_subnet" "firewall" {
name = "AzureFirewallSubnet"
virtual_network_name = "vnet-hub-drlab-brazilsouth"
resource_group_name = data.azurerm_resource_group.this.name
}
# IP Público — obrigatório para o Azure Firewall Standard
resource "azurerm_public_ip" "firewall" {
name = "pip-afw-drlab-brazilsouth"
resource_group_name = data.azurerm_resource_group.this.name
location = data.azurerm_resource_group.this.location
allocation_method = "Static"
sku = "Standard"
tags = var.tags
}
# Firewall Policy — gerencia Rule Collection Groups
resource "azurerm_firewall_policy" "this" {
name = "afwp-drlab-brazilsouth"
resource_group_name = data.azurerm_resource_group.this.name
location = data.azurerm_resource_group.this.location
sku = "Standard"
tags = var.tags
}
# Azure Firewall — associado à Policy e à subnet
resource "azurerm_firewall" "this" {
name = "afw-drlab-brazilsouth"
resource_group_name = data.azurerm_resource_group.this.name
location = data.azurerm_resource_group.this.location
sku_name = "AZFW_VNet"
sku_tier = "Standard"
firewall_policy_id = azurerm_firewall_policy.this.id
tags = var.tags
ip_configuration {
name = "ipconfig1"
subnet_id = data.azurerm_subnet.firewall.id
public_ip_address_id = azurerm_public_ip.firewall.id
}
}
Observe que o Firewall usa sku_name = "AZFW_VNet", que é o modo de implantação dentro de uma VNet (não o modo de hub Virtual WAN). O deploy do Firewall leva entre 5 e 10 minutos — é o recurso mais lento desta série. Além disso, o par East US segue o mesmo padrão, substituindo os nomes e o address space.
Passo 2 — User-Defined Routes (UDR)
Sem as User-Defined Routes, o Azure usa o roteamento padrão e o tráfego do Spoke vai direto para a Internet, sem passar pelo Firewall. A Route Table força o tráfego de saída (0.0.0.0/0) para o IP privado do Firewall como Virtual Appliance:
resource "azurerm_route_table" "spoke" {
name = "rt-spoke-brazilsouth"
resource_group_name = data.azurerm_resource_group.this.name
location = data.azurerm_resource_group.this.location
bgp_route_propagation_enabled = false
tags = var.tags
}
resource "azurerm_route" "default_to_firewall" {
name = "route-default-to-firewall"
resource_group_name = data.azurerm_resource_group.this.name
route_table_name = azurerm_route_table.spoke.name
address_prefix = "0.0.0.0/0"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = var.firewall_private_ip
}
# Associar a Route Table às quatro subnets do Spoke
resource "azurerm_subnet_route_table_association" "frontend" {
subnet_id = data.azurerm_subnet.frontend.id
route_table_id = azurerm_route_table.spoke.id
}
resource "azurerm_subnet_route_table_association" "backend" {
subnet_id = data.azurerm_subnet.backend.id
route_table_id = azurerm_route_table.spoke.id
}
resource "azurerm_subnet_route_table_association" "data_tier" {
subnet_id = data.azurerm_subnet.data_tier.id
route_table_id = azurerm_route_table.spoke.id
}
resource "azurerm_subnet_route_table_association" "aks" {
subnet_id = data.azurerm_subnet.aks.id
route_table_id = azurerm_route_table.spoke.id
}
O parâmetro bgp_route_propagation_enabled = false é importante: ele impede que rotas aprendidas via BGP (de um VPN Gateway ou ExpressRoute) sobrescrevam a rota forçada para o Firewall. Sem ele, um failover de conectividade on-premises poderia bypassar o Firewall inadvertidamente.
Passo 3 — NSGs por camada
Cada subnet do Spoke recebe um NSG dedicado com regras que seguem o princípio de menor privilégio: apenas o tráfego explicitamente necessário para aquela camada é permitido. Dessa forma, mesmo que o Firewall seja contornado (o que não deveria acontecer), os NSGs garantem uma segunda camada de proteção.
NSG — Frontend (porta 443 e 80 da Internet)
resource "azurerm_network_security_group" "frontend" {
name = "nsg-frontend-brazilsouth"
resource_group_name = data.azurerm_resource_group.workload.name
location = data.azurerm_resource_group.workload.location
tags = var.tags
}
resource "azurerm_network_security_rule" "allow_https" {
name = "allow-https-inbound"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "Internet"
destination_address_prefix = "VirtualNetwork"
resource_group_name = data.azurerm_resource_group.workload.name
network_security_group_name = azurerm_network_security_group.frontend.name
}
resource "azurerm_network_security_rule" "allow_http" {
name = "allow-http-inbound"
priority = 110
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "Internet"
destination_address_prefix = "VirtualNetwork"
resource_group_name = data.azurerm_resource_group.workload.name
network_security_group_name = azurerm_network_security_group.frontend.name
}
resource "azurerm_network_security_rule" "deny_all_frontend" {
name = "deny-all-inbound"
priority = 4096
direction = "Inbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
resource_group_name = data.azurerm_resource_group.workload.name
network_security_group_name = azurerm_network_security_group.frontend.name
}
resource "azurerm_subnet_network_security_group_association" "frontend" {
subnet_id = data.azurerm_subnet.frontend.id
network_security_group_id = azurerm_network_security_group.frontend.id
}
NSG — Backend (apenas Frontend pode acessar, porta 8080)
resource "azurerm_network_security_group" "backend" {
name = "nsg-backend-brazilsouth"
resource_group_name = data.azurerm_resource_group.workload.name
location = data.azurerm_resource_group.workload.location
tags = var.tags
}
resource "azurerm_network_security_rule" "allow_frontend" {
name = "allow-frontend-inbound"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "8080"
source_address_prefix = "10.2.1.0/24" # snet-frontend
destination_address_prefix = "VirtualNetwork"
resource_group_name = data.azurerm_resource_group.workload.name
network_security_group_name = azurerm_network_security_group.backend.name
}
resource "azurerm_network_security_rule" "deny_internet_backend" {
name = "deny-internet-inbound"
priority = 200
direction = "Inbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "Internet"
destination_address_prefix = "*"
resource_group_name = data.azurerm_resource_group.workload.name
network_security_group_name = azurerm_network_security_group.backend.name
}
NSG — Data (apenas Backend via SQL, porta 1433)
resource "azurerm_network_security_group" "data" {
name = "nsg-data-brazilsouth"
resource_group_name = data.azurerm_resource_group.workload.name
location = data.azurerm_resource_group.workload.location
tags = var.tags
}
resource "azurerm_network_security_rule" "allow_backend_sql" {
name = "allow-backend-sql"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "1433"
source_address_prefix = "10.2.2.0/24" # snet-backend
destination_address_prefix = "VirtualNetwork"
resource_group_name = data.azurerm_resource_group.workload.name
network_security_group_name = azurerm_network_security_group.data.name
}
resource "azurerm_network_security_rule" "deny_all_data" {
name = "deny-all-inbound"
priority = 4096
direction = "Inbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
resource_group_name = data.azurerm_resource_group.workload.name
network_security_group_name = azurerm_network_security_group.data.name
}
NSG — AKS (Azure Load Balancer na porta 443)
resource "azurerm_network_security_group" "aks" {
name = "nsg-aks-brazilsouth"
resource_group_name = data.azurerm_resource_group.workload.name
location = data.azurerm_resource_group.workload.location
tags = var.tags
}
resource "azurerm_network_security_rule" "allow_lb_aks" {
name = "allow-lb-inbound"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "AzureLoadBalancer"
destination_address_prefix = "VirtualNetwork"
resource_group_name = data.azurerm_resource_group.workload.name
network_security_group_name = azurerm_network_security_group.aks.name
}
resource "azurerm_subnet_network_security_group_association" "aks" {
subnet_id = data.azurerm_subnet.aks.id
network_security_group_id = azurerm_network_security_group.aks.id
}
O NSG do AKS usa a Service Tag AzureLoadBalancer em vez de um range de IPs — dessa forma, o Azure atualiza automaticamente os IPs dos load balancers internos sem necessidade de manutenção manual das regras. Além disso, o AKS exige regras específicas para comunicação entre nós; para simplificar o lab, as regras de egresso ficam no nível do Firewall Policy.
Passo 4 — Executar o Terraform
A ordem de execução é importante: o Firewall deve ser criado antes da Route Table, pois a UDR precisa do IP privado do Firewall como next hop. Em seguida, os NSGs podem ser criados em qualquer ordem:
export TF_VAR_subscription_id="<SUBSCRIPTION_ID>"
# 1. Criar o Firewall (5-10 minutos)
cd scripts/art-03-firewall-nsg/afw-blog-castilho-brazilsouth/
terraform init && terraform apply -auto-approve
cd ../afw-blog-castilho-eastus/
terraform init && terraform apply -auto-approve
# 2. Obter IPs privados dos Firewalls
FW_IP_BRS=$(az network firewall show \
--name afw-drlab-brazilsouth \
--resource-group rg-drlab-network-brazilsouth \
--query "ipConfigurations[0].privateIPAddress" -o tsv)
FW_IP_EUS=$(az network firewall show \
--name afw-drlab-eastus \
--resource-group rg-drlab-network-eastus \
--query "ipConfigurations[0].privateIPAddress" -o tsv)
# 3. Criar as Route Tables
export TF_VAR_firewall_private_ip="$FW_IP_BRS"
cd ../rt-spoke-brazilsouth/
terraform init && terraform apply -auto-approve
export TF_VAR_firewall_private_ip="$FW_IP_EUS"
cd ../rt-spoke-eastus/
terraform init && terraform apply -auto-approve
# 4. Criar os NSGs (em paralelo ou sequencial)
for nsg in nsg-frontend-brazilsouth nsg-backend-brazilsouth nsg-data-brazilsouth nsg-aks-brazilsouth \
nsg-frontend-eastus nsg-backend-eastus nsg-data-eastus nsg-aks-eastus; do
cd ../$nsg/ && terraform init && terraform apply -auto-approve && cd ..
done
Verificar a configuração
Após o deploy, confirme que os azure firewall e nsgs na landing zone estão ativos e que as rotas foram associadas corretamente às subnets do Spoke:
# Verificar estado do Firewall
az network firewall show \
--name afw-drlab-brazilsouth \
--resource-group rg-drlab-network-brazilsouth \
--query "{nome:name, estado:provisioningState, ip:ipConfigurations[0].privateIPAddress}" \
-o table
# Verificar routes da Route Table
az network route-table route list \
--route-table-name rt-spoke-brazilsouth \
--resource-group rg-drlab-workload-brazilsouth \
--query "[].{nome:name, prefixo:addressPrefix, nextHop:nextHopIpAddress}" \
-o table
# Verificar NSGs das subnets do Spoke
az network vnet subnet show \
--vnet-name vnet-spoke-drlab-brazilsouth \
--resource-group rg-drlab-workload-brazilsouth \
--name snet-frontend \
--query "networkSecurityGroup.id" \
-o tsv
O resultado esperado para a Route Table deve mostrar uma rota 0.0.0.0/0 com next hop VirtualAppliance e o IP privado do Firewall (ex: 10.0.0.4). Se a rota não aparecer associada à subnet, verificar se o azurerm_subnet_route_table_association foi aplicado sem erros.
Troubleshooting
| Erro | Causa | Solução |
|---|---|---|
AzureFirewallSubnetAddressPrefixTooSmall |
Subnet menor que /26 | A AzureFirewallSubnet deve ter pelo menos /26 — verificar o Art. 02 |
| Tráfego bloqueado mesmo com regra Allow no NSG | A Route Table redireciona o tráfego para o Firewall antes do NSG ser avaliado | Adicionar também a regra na Firewall Policy (Application ou Network Rule Collection) |
FirewallPrivateIPNotFound na Route Table |
IP privado incorreto em TF_VAR_firewall_private_ip |
Consultar o IP real com az network firewall show --query ipConfigurations[0].privateIPAddress |
| NSG criado mas subnet não associada | azurerm_subnet_network_security_group_association não aplicado |
Executar terraform apply novamente no módulo do NSG |
| Deploy do Firewall timeout (>15 min) | Provisioning lento em Brazil South | Aguardar — o Azure Firewall pode levar até 20 minutos; re-executar terraform apply se necessário |
Limpeza dos recursos
O Azure Firewall é o recurso mais caro desta série. Para evitar cobranças durante o desenvolvimento, destrua-o ao final de cada sessão. Os NSGs e Route Tables podem permanecer — eles têm custo zero e o Azure simplesmente os ignora enquanto não há Firewall respondendo no next hop:
export TF_VAR_subscription_id="<SUBSCRIPTION_ID>"
# Destruir os Firewalls (maior economia)
cd scripts/art-03-firewall-nsg/afw-blog-castilho-brazilsouth/
terraform destroy -auto-approve
cd ../afw-blog-castilho-eastus/
terraform destroy -auto-approve
# Opcional: destruir também NSGs e Route Tables
for recurso in nsg-frontend-brazilsouth nsg-backend-brazilsouth nsg-data-brazilsouth nsg-aks-brazilsouth \
rt-spoke-brazilsouth rt-spoke-eastus; do
cd ../$recurso/ && terraform destroy -auto-approve && cd ..
done
Próximos passos
Com os azure firewall e nsgs na landing zone configurados, a Landing Zone está protegida contra tráfego não autorizado tanto no perímetro (Firewall) quanto entre as camadas de aplicação (NSGs). No próximo artigo, vamos adicionar o Azure Bastion em cada Hub, permitindo acesso SSH e RDP às VMs do Spoke sem expor nenhum IP público — complementando a estratégia de segurança em profundidade desta Landing Zone.
- 📖 Art. 01: Planejamento de DR Azure e Landing Zone
- 📖 Art. 02: Hub-Spoke Landing Zone com Terraform
- ⚙️ Art. 03 (este): Azure Firewall e NSGs na Landing Zone
- 🔒 Art. 04: Azure Bastion na Landing Zone sem IP Público
- 🔒 Art. 05: Azure Front Door e Traffic Manager para Failover
- 🔒 Art. 06: DNS Privado Multi-Região no Azure
- 🔒 Art. 07: Azure Site Recovery para VMs Multi-Região
- 🔒 Art. 08: Azure Storage com Geo-Replicação para DR
- 🔒 Art. 09: AKS Multi-Região com Failover no Azure
- 🔒 Art. 10: Velero no AKS para Backup e Restore Cross-Region
- 🔒 Art. 11: Runbooks de Failover no Azure Automation
- 🔒 Art. 12: Simular um DR no Azure sem Impacto em Produção
- 🔒 Art. 13: Monitoramento do DR no Azure com Azure Monitor
Interessado em saber mais sobre artigos relacionados ao Microsoft Azure CLIQUE AQUI
🚀 Vamos nos conectar?
Não perca nenhuma oportunidade! Cadastre-se nas minhas redes e no canal do YouTube para receber conteúdos de TI, Cloud, Azure, Kubernetes e DevOps em primeira mão.
Dica: No Facebook, todos os artigos do blog são publicados automaticamente. Vale a pena curtir!
💬 Dúvidas ou Problemas?
Com o intuito de ajudar a comunidade, caso você tenha dúvidas ou encontre problemas na execução dos comandos deste artigo, deixe um comentário abaixo. Responderei o mais breve possível!
Muito obrigado pela visita e até o próximo post!
Jefferson Castilho Especialista em Cloud & DevOps.Este guia técnico é exclusivo do Blog do Castilho. Explore nossa para mais conteúdos sobre IA e Cloud.


