Brewing the Basics: Zero Trust Networking in Azure

Because “trust but verify” is old hat. Now it’s “verify everything, every time.”

Back in the on‑prem days, network security was like making coffee with a single big percolator. You poured everything in, pressed a button, and assumed the end result would be drinkable. A big firewall at the edge, a DMZ for polish, and job done.

But Azure networking demands something more refined. Workloads scale elastically, PaaS services sneak into the mix, and identities wander across devices and geographies. That “one big brew” model loses flavour quickly.

This is where Zero Trust steps in. Instead of trusting the whole pot, you brew each cup individually, verifying every grain and every pour. And at the network layer, that refinement looks like segmentation: carving your infrastructure into deliberate, isolated segments where access is granted only when justified.

What is Zero Trust Networking?

Microsoft frames Zero Trust with three core ideas:

  • Verify explicitly → Authorise based on identity, policy, and context.
  • Least privilege access → Allow just enough, and only for as long as needed.
  • Assume breach → Design as if attackers are already inside the network.

When mapped into networking, one extra dimension gets special focus: segmentation. That means designing networks so that resources only talk to what they need, and nothing more. In other words, your SQL database shouldn’t chat to your web tier unless you’ve brewed in rules that say it can.

How it Works in Azure

A Zero Trust‑aligned Azure network typically combines:

  • Virtual Networks and Subnets → Provide isolation zones to enforce segmentation.
  • Network Security Groups (NSGs) → Control what can flow in/out of each subnet.
  • Azure Firewall or Firewall Policy → Centralised inspection and outbound control.
  • Private Link / Private Endpoints → Ensure PaaS services (SQL, Storage, Key Vault) are reachable only via trusted VNets, not the internet.
  • Azure Virtual Network Manager (AVNM) → Scale segmentation and security rules across environments.
  • Identity‑aware access → Use Just in Time (JIT), Conditional Access, Bastion integration to align network traffic to authenticated users.

Real-World Impact

Let’s say you’re running a classic 3‑tier app in Azure: Web, App, and Database. The easy (but risky) design would be to dump everything into a single flat VNet, where any tier can talk to any other without restriction. That’s perfect for attackers but painful for defenders.

By taking a Zero Trust view, you place each tier in its own spoke VNet, connect them via a secured hub, and attach NSGs to only allow the flows you intend. That design:

  • Prevents a compromised web VM in one spoke from freely reaching your data tier.
  • Enforces explicit, logged east‑west rules through the hub.
  • Scales cleanly with Azure Virtual Network Manager (AVNM) to apply consistent policies across many VNets.
  • Makes troubleshooting easier by having logs and flow records show exactly which segment boundary is passing or blocking traffic.

Architecture Overview

Here’s a high-level Zero Trust segmentation pattern in Azure:

flowchart TD subgraph HubVNet[Hub VNet] AFW[Azure Firewall] Bastion[Azure Bastion] end subgraph Spoke1[Web VNet] SubnetWeb[Web Subnet] WebVM["Web VM(s)"] NSGWeb[Web NSG] end subgraph Spoke2[App VNet] SubnetApp[App Subnet] AppVM["App VM(s)"] NSGApp[App NSG] end subgraph Spoke3[Data VNet] SQLpe[SQL Private Endpoint] NSGData[Data NSG] end Internet[(Internet)] Internet --> AFW AFW --> WebVM WebVM --> AppVM AppVM --> SQLpe Bastion --> WebVM Bastion --> AppVM

Notice each tier has its own VNet + subnet + NSG. Access is only permitted along the explicit paths (Web→App, App→SQL).

Implementation Examples

Azure Portal Steps

  1. Create the Hub VNet

    • Deploy a hub VNet that will host shared services (Azure Firewall, Bastion, DNS forwarders if needed).
  2. Create Spoke VNets

    • Web VNet → For your front‑end workloads.
    • App VNet → For application logic tier.
    • Data VNet → For databases, storage, or other sensitive back‑end systems.
  3. Peering

    • Peer each spoke VNet to the hub VNet (hub‑and‑spoke).
    • Disable spoke‑to‑spoke direct peering to force all east‑west traffic through the hub.
  4. Attach NSGs

    • WebNSG → Allow inbound HTTPS from internet, outbound only to App VNet.
    • AppNSG → Allow inbound traffic only from Web VNet, outbound only to Data VNet.
    • DataNSG → Allow inbound traffic only from App VNet.
  5. Apply Firewall Rules in Hub

    • Use Azure Firewall or Firewall Policy to allow/deny specific flows (e.g., Web→App, App→DB).
    • Log and monitor everything centrally from the hub.
  6. Private Endpoints for PaaS

    • Add Private Endpoints (e.g., SQL, Storage, Key Vault) inside the Data VNet.
    • Ensure DNS resolution points to the private endpoint IP, not public endpoints.
  7. Deploy Resources

    • Place VMs, App Services (via VNet Integration), and databases into their respective VNets.

Result: Each tier lives in its own segmented VNet, only talks to the tier it needs, and all traffic is filtered centrally by the hub.

Bicep Example

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
param location string = resourceGroup().location

// Hub VNet – shared services like Azure Firewall, Bastion, DNS
resource hubVnet 'Microsoft.Network/virtualNetworks@2024-07-01' = {
  name: 'hub-vnet'
  location: location
  properties: {
    addressSpace: { addressPrefixes: ['10.0.0.0/16'] }
    subnets: [
      {
        name: 'AzureFirewallSubnet' // reserved name for firewall
        properties: { addressPrefix: '10.0.0.0/24' }
      }
      {
        name: 'BastionSubnet' // reserved name for Bastion
        properties: { addressPrefix: '10.0.1.0/27' }
      }
    ]
  }
}

// Web Tier Spoke VNet
resource webVnet 'Microsoft.Network/virtualNetworks@2024-07-01' = {
  name: 'web-vnet'
  location: location
  properties: {
    addressSpace: { addressPrefixes: ['10.1.0.0/16'] }
  }
}

// App Tier Spoke VNet
resource appVnet 'Microsoft.Network/virtualNetworks@2024-07-01' = {
  name: 'app-vnet'
  location: location
  properties: {
    addressSpace: { addressPrefixes: ['10.2.0.0/16'] }
  }
}

// Data Tier Spoke VNet
resource dataVnet 'Microsoft.Network/virtualNetworks@2024-07-01' = {
  name: 'data-vnet'
  location: location
  properties: {
    addressSpace: { addressPrefixes: ['10.3.0.0/16'] }
  }
}

// Example NSGs for spoke segmentation
resource webNsg 'Microsoft.Network/networkSecurityGroups@2024-07-01' = {
  name: 'web-nsg'
  location: location
  properties: {
    securityRules: [
      {
        name: 'Allow-HTTPS-In'
        properties: {
          direction: 'Inbound'
          priority: 100
          access: 'Allow'
          protocol: 'Tcp'
          sourcePortRange: '*'
          destinationPortRange: '443'
          sourceAddressPrefix: '*'
          destinationAddressPrefix: '*'
        }
      }
      {
        name: 'Allow-Out-AppVnet'
        properties: {
          direction: 'Outbound'
          priority: 200
          access: 'Allow'
          protocol: '*'
          sourcePortRange: '*'
          destinationPortRange: '*'
          sourceAddressPrefix: '*'
          destinationAddressPrefix: appVnet.properties.addressSpace.addressPrefixes[0]
        }
      }
    ]
  }
}

resource appNsg 'Microsoft.Network/networkSecurityGroups@2024-07-01' = {
  name: 'app-nsg'
  location: location
  properties: {
    securityRules: [
      {
        name: 'Allow-In-WebVnet'
        properties: {
          direction: 'Inbound'
          priority: 100
          access: 'Allow'
          protocol: '*'
          sourcePortRange: '*'
          destinationPortRange: '*'
          sourceAddressPrefix: webVnet.properties.addressSpace.addressPrefixes[0]
          destinationAddressPrefix: '*'
        }
      }
      {
        name: 'Allow-Out-DataVnet'
        properties: {
          direction: 'Outbound'
          priority: 200
          access: 'Allow'
          protocol: '*'
          sourcePortRange: '*'
          destinationPortRange: '*'
          sourceAddressPrefix: '*'
          destinationAddressPrefix: dataVnet.properties.addressSpace.addressPrefixes[0]
        }
      }
    ]
  }
}

resource dataNsg 'Microsoft.Network/networkSecurityGroups@2024-07-01' = {
  name: 'data-nsg'
  location: location
  properties: {
    securityRules: [
      {
        name: 'Allow-In-AppVnet'
        properties: {
          direction: 'Inbound'
          priority: 100
          access: 'Allow'
          protocol: 'Tcp'
          sourcePortRange: '*'
          destinationPortRange: '1433'
          sourceAddressPrefix: appVnet.properties.addressSpace.addressPrefixes[0]
          destinationAddressPrefix: '*'
        }
      }
    ]
  }
}

// Add Subnets and Associated NSGs
resource webSubnetAndNSG 'Microsoft.Network/virtualNetworks/subnets@2024-07-01' = {
  name: 'web-subnet'
  parent: webVnet
  properties: {
    addressPrefix: '10.1.1.0/24'
    networkSecurityGroup: {
      id: webNsg.id
    }
  }
}

resource appSubnetNsgAssociation 'Microsoft.Network/virtualNetworks/subnets@2024-07-01' = {
  name: 'app-subnet'
  parent: appVnet
  properties: {
    addressPrefix: '10.2.1.0/24'
    networkSecurityGroup: {
      id: appNsg.id
    }
  }
}

resource dataSubnetNsgAssociation 'Microsoft.Network/virtualNetworks/subnets@2024-07-01' = {
  name: 'data-subnet'
  parent: dataVnet
  properties: {
    addressPrefix: '10.3.1.0/24'
    networkSecurityGroup: {
      id: dataNsg.id
    }
  }
}

// Hub-Spoke VNet Peerings
resource hubToWebPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2024-07-01' = {
  name: 'hub-to-web'
  parent: hubVnet
  properties: {
    remoteVirtualNetwork: { id: webVnet.id }
    allowForwardedTraffic: true
    allowVirtualNetworkAccess: true
  }
}

resource webToHubPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2024-07-01' = {
  name: 'web-to-hub'
  parent: webVnet
  properties: {
    remoteVirtualNetwork: { id: hubVnet.id }
    allowForwardedTraffic: true
    allowVirtualNetworkAccess: true
  }
}

resource hubToAppPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2024-07-01' = {
  name: 'hub-to-app'
  parent: hubVnet
  properties: {
    remoteVirtualNetwork: { id: appVnet.id }
    allowForwardedTraffic: true
    allowVirtualNetworkAccess: true
  }
}

resource appToHubPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2024-07-01' = {
  name: 'app-to-hub'
  parent: appVnet
  properties: {
    remoteVirtualNetwork: { id: hubVnet.id }
    allowForwardedTraffic: true
    allowVirtualNetworkAccess: true
  }
}

resource hubToDataPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2024-07-01' = {
  name: 'hub-to-data'
  parent: hubVnet
  properties: {
    remoteVirtualNetwork: { id: dataVnet.id }
    allowForwardedTraffic: true
    allowVirtualNetworkAccess: true
  }
}

resource dataToHubPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2024-07-01' = {
  name: 'data-to-hub'
  parent: dataVnet
  properties: {
    remoteVirtualNetwork: { id: hubVnet.id }
    allowForwardedTraffic: true
    allowVirtualNetworkAccess: true
  }
}

That’s segmentation in action: each subnet is fenced off, with NSGs acting as gates.

Gotchas & Edge Cases

  • Flat VNets = flat risk. If you don’t segment, lateral movement gets too easy.
  • NSGs aren’t inspection engines — use Firewall when you need advanced filtering, FQDNs, or logging at scale.
  • Over‑segmentation can slow development. Strike balance: enough to contain blasts, not so much that your developers revolt.

Best Practices

  • Segment by function/tier (Web, App, Data) as a baseline.
  • Consider landing zones in an enterprise environment where segmentation often matches business units, environments or even functions.
  • Deny by default in every NSG.
  • Use AVNM to deploy and audit segmentation policies consistently across subscriptions. (Check out my blog post around AVNM Security Admin Rules in the Real World )
  • Combine segmentation + Private Endpoints to fully cut off internet exposure.
🍺
Brewed Insight: Think of segmentation as the espresso shot in your Zero Trust latte. Without it, you just have warm milk (a flat network with skimpy controls). But layer those shots the right way, and you build strong, structured flavour — traffic flows only where intended, and every sip is deliberate.

Learn More