Azure / Guide 5
Firewalls, private endpoints and admin access
Outbound control, Azure Firewall, WAF, DDoS protection, private endpoints, Bastion and just-in-time access.
NSGs filter by address and port, and that's all they do. They can't allow *.windowsupdate.com and block the rest of the internet, they don't inspect traffic, and they don't stop a storage account from being reachable from anywhere. This guide covers the layers around them: controlled outbound, Azure Firewall, web application firewalls, DDoS protection, private endpoints, and getting to servers without opening management ports.
What sits where
| Control | Protects against | Works at |
|---|---|---|
| NSG and ASG | Unwanted connections between addresses and ports | Subnet or NIC (layer 4) |
| Azure Firewall | Uncontrolled outbound, traffic between networks, known-bad destinations | Hub network, all traffic routed through it |
| Web application firewall (WAF) | Attacks on websites: SQL injection, cross-site scripting, bots | In front of web apps (Application Gateway or Front Door) |
| DDoS protection | Floods aimed at public IP addresses | Public IPs |
| Private endpoints | PaaS services reachable from the internet | The service's own network access |
| Bastion and just-in-time access | Open RDP and SSH | Admin access to VMs |
Control outbound traffic
For years, a VM with no public IP could still reach the internet through an address Azure assigned behind the scenes ("default outbound access"). That's going away. For virtual networks created with API versions released after 31 March 2026, subnets are private by default: VMs in them can't reach the internet, or Microsoft public endpoints, until you give them an explicit way out. Existing virtual networks don't change, unless you set their subnets to private yourself, which you should plan to do.
The explicit options:
- NAT gateway on the subnet: the simplest. Fixed public IPs that partners can allow-list, and no inbound exposure.
- Azure Firewall via a route table that sends
0.0.0.0/0to the firewall's private IP: when you need to control where servers can go, not only that they can. - Load balancer outbound rules, when the servers already sit behind a Standard load balancer.
Don't give servers public IPs just so they can download updates.
Azure Firewall
Azure Firewall is a managed, stateful firewall that usually sits in a hub virtual network, with spoke networks peered to it and a route table in each spoke sending traffic through it. Its main advantage over NSGs is application rules by name: allow *.ubuntu.com and the Windows Update FQDN tag, deny everything else.
| SKU | Throughput | Adds | Fits |
|---|---|---|---|
| Basic | Up to 250 Mbps | Network and application rules; threat intelligence in alert mode | Small environments |
| Standard | Up to 30 Gbps | Threat intelligence that blocks, DNS proxy, web categories | Most organisations |
| Premium | Up to 100 Gbps | TLS inspection, intrusion detection and prevention (IDPS), full-URL filtering | Regulated and sensitive workloads |
Practical points:
- Manage rules in a Firewall Policy, not classic rules on the firewall itself. A parent policy holds the organisation-wide rules; child policies hold each environment's.
- Rules are processed by type: DNAT first, then network rules, then application rules. A network rule that allows something wins before any application rule sees it.
- Send the firewall's logs to Log Analytics (resource-specific tables). A denied connection with no log is the hardest problem to troubleshoot.
- It's a significant fixed monthly cost. One shared firewall in a hub is normal; one per application usually isn't.
Web application firewall
Any website on the internet should sit behind a WAF. In Azure that's Application Gateway WAF (regional, inside your virtual network) or Azure Front Door Premium (global, at Microsoft's edge). Both use the managed rule set, which covers the OWASP top risks, plus bot protection and your own rules.
Start in Detection mode, review what would have been blocked for a week or two, add exclusions for the false positives, then switch to Prevention. A WAF left in Detection mode for good only writes logs. Then use an NSG so the web servers only accept traffic from the gateway's subnet (or, for Front Door, from the AzureFrontDoor.Backend service tag plus a check on the X-Azure-FDID header).
DDoS protection
Every public IP gets DDoS infrastructure protection at no cost; it protects Azure's platform, and is tuned for very large attacks. For internet-facing production services, add one of:
- DDoS Network Protection: a plan linked to virtual networks, covering the public IPs in them, with adaptive tuning, attack alerts and reports, and cost protection. Priced per plan, so it makes sense for many public IPs.
- DDoS IP Protection: the same core protection, billed per public IP. Cheaper for a handful of addresses.
Private endpoints for PaaS services
Storage accounts, Key Vaults, SQL databases and most other PaaS services have a public endpoint by default. NSGs don't help, because the service isn't in your network. Two ways to close it:
| Service endpoint | Private endpoint | |
|---|---|---|
| What it is | Lets a subnet reach the service's public endpoint over the Azure backbone; the service firewall then allows only that subnet | Gives the service a private IP address inside your subnet |
| Reachable from on-premises | No | Yes, over VPN or ExpressRoute |
| Public endpoint | Still exists, restricted by the service firewall | Can be turned off completely |
| Cost | Free | Per endpoint per hour, plus data processed |
| Use for | Test and low-risk services | Production and anything holding sensitive data |
DNS is where private endpoints go wrong. The service's normal name (for example stnotestest.blob.core.windows.net) has to resolve to the private IP. Link the matching private DNS zone (privatelink.blob.core.windows.net) to the virtual networks that use it, and forward on-premises DNS to Azure (a Private DNS Resolver). If the name still resolves to a public address, the traffic goes the public way, or fails once public access is turned off.
Once the private endpoint works, set Public network access to Disabled on the service. To find what's still open:
# PaaS resources that still accept traffic from public networks, across every subscription
Search-AzGraph -UseTenantScope -First 1000 -Query @"
Resources
| where type in~ ('microsoft.storage/storageaccounts', 'microsoft.keyvault/vaults', 'microsoft.sql/servers',
'microsoft.documentdb/databaseaccounts', 'microsoft.web/sites')
| extend publicAccess = tostring(properties.publicNetworkAccess)
| where publicAccess !~ 'Disabled'
| project name, type, resourceGroup, subscriptionId, publicAccess = iff(publicAccess == '', 'Enabled (default)', publicAccess)
| order by type asc, name asc
"@
"Enabled" isn't automatically wrong (a public website has to be public), but every row should have a reason.
Admin access without open ports
RDP and SSH should never be open to the internet. Two ways to manage servers without it:
Azure Bastion brokers RDP and SSH sessions over HTTPS, from the portal or your own client. VMs need no public IP and no inbound rule from the internet: allow 3389 and 22 from the Bastion subnet only. It needs its own subnet called AzureBastionSubnet, /26 or larger.
| SKU | Good for | Notable |
|---|---|---|
| Developer | Dev and test | Free, shared, one connection at a time, no peered networks. Not for production |
| Basic | Small environments | Portal connections only |
| Standard | Most production use | Native RDP and SSH clients, connect by IP address, shareable links, scaling |
| Premium | Regulated environments | Session recording and private-only deployment (no public IP on Bastion itself) |
One Bastion in the hub can reach VMs in peered spokes (all SKUs except Developer).
Just-in-time (JIT) VM access, part of Microsoft Defender for Servers Plan 2, keeps management ports closed with an NSG deny rule and opens them for a named person, from their IP address, for a set time, when they request it. Every request is logged. Use it for the cases Bastion doesn't cover, such as a vendor tool that needs a direct connection.