Draw a hub-and-spoke topology, peer the VNets, and prove segmentation with a route that is meant to be blocked.
Run one step, then check it
Read the step before copying. Bash commands belong in Git Bash on Windows or Azure Cloud Shell set to Bash; PowerShell uses different syntax. A JSON, YAML, Bicep or application-code example belongs in the named file, not directly at the terminal prompt. Follow any shell-specific instructions below.
Keep the same terminal open so earlier variables remain available. Copy one complete command block without the terminal's $ prompt. A backslash continues a command onto the next line. If Bash shows a lone >, it is waiting for the rest of a quote, command or file block. Finish the complete block; if the paste was incomplete, press Ctrl+C and copy it again. Do not paste the next step into an unfinished command.
A line such as RG="training-group" stores a value without printing it. Output captured with $(...) is also stored; a blank screen alone does not prove failure. A sleep deliberately waits, and -o none suppresses normal output. Wait for your normal prompt, then run the step's checkpoint. After an error, stop and keep the exact command and message; later steps cannot repair a missing resource. Never share passwords, access tokens or secret values in a screenshot.
Why one flat network is a liability
The instinct of a beginner is to put everything in one virtual network because it "just works" — everything can reach everything. That is precisely the problem: when a single workload is compromised, a flat network lets the blast spread to all of them. The enterprise answer is hub-and-spoke. A central hub holds shared services (a firewall, a gateway, DNS); each workload lives in its own spoke, peered to the hub but not to its siblings. Traffic between workloads is routed and inspected on purpose, never by accident. This lab builds that shape and then demonstrates the isolation — that two spokes genuinely cannot reach each other — without spinning up a single chargeable resource.
You will create a hub and two spokes with non-overlapping address spaces, peer each spoke to the hub, confirm the spokes are not peered to one another, and add an NSG that spells out the allowed and denied paths as explicit rules.
A flat network trusts everything once. Segmentation makes trust a decision.
You need a free Azure account, the Azure CLI (az), then be signed in with az login. First time? The 15-minute Set up your machine page covers the account, the installs (winget / brew / apt), and sign-in. Prefer zero installs? Run everything in Azure Cloud Shell (Bash), preinstalled and already signed in.
This page is the whole lab; a Bicep version of the same topology is in github.com/kloudcaptain/campux-labs under lab-hub-spoke-networking. Run the commands in Azure Cloud Shell (Bash). Virtual networks, peerings, and NSGs have no hourly charge — this lab is genuinely free, VMs are what would have cost money and we deliberately use none.
What this lab can and cannot show
Class 10 ended with one request: a web server asking a database for a connection on TCP 1433. Keep that request in mind. Here the "web" side is the hub, which holds shared services, and the "data" side is a spoke workload. You will build the networks and the rules that decide whether such a request is allowed.
The lab creates no virtual machines, so it costs nothing and no packet ever moves. Every checkpoint reads configuration: address spaces, peering state, NSG rules in priority order. That is real evidence about what Azure will permit. It is not evidence that a connection works, because a working connection also needs DNS, a listening service and a login. Testing a real request from inside a network comes in Class 14 Part A.
A hub and two spokes
Address spaces must not overlap or peering will refuse them. Give the hub 10.0.0.0/16 and the spokes their own /16s, each with a workload subnet.
# Windows/Git Bash: stop it mangling /subscriptions/... arguments (harmless on macOS/Linux) export MSYS_NO_PATHCONV=1 RG="campux-lab-net-rg" az group create -n "$RG" -l eastus # hub az network vnet create -g "$RG" -n hub-vnet --address-prefix 10.0.0.0/16 \ --subnet-name shared --subnet-prefix 10.0.1.0/24 # spoke 1 az network vnet create -g "$RG" -n spoke1-vnet --address-prefix 10.1.0.0/16 \ --subnet-name workload --subnet-prefix 10.1.1.0/24 # spoke 2 az network vnet create -g "$RG" -n spoke2-vnet --address-prefix 10.2.0.0/16 \ --subnet-name workload --subnet-prefix 10.2.1.0/24
Peer each spoke to the hub
Peering is directional, so each link needs two halves — spoke→hub and hub→spoke. Create all four.
for S in spoke1 spoke2; do
az network vnet peering create -g "$RG" -n "${S}-to-hub" \
--vnet-name "${S}-vnet" --remote-vnet hub-vnet --allow-vnet-access
az network vnet peering create -g "$RG" -n "hub-to-${S}" \
--vnet-name hub-vnet --remote-vnet "${S}-vnet" --allow-vnet-access
done
Connected — the state you want; Initiated means only one half of a pair exists, the other create command has not run yet or failed:
az network vnet peering list -g "$RG" --vnet-name hub-vnet \
--query "[].{name:name, state:peeringState}" -o tableProve the spokes are isolated
This is the segmentation payoff, read from configuration rather than from traffic. Azure VNet peering is non-transitive: spoke1 peers with the hub and spoke2 peers with the hub, but that does not connect spoke1 to spoke2. List spoke1's peerings and see that the hub is its only neighbour.
az network vnet peering list -g "$RG" --vnet-name spoke1-vnet \
--query "[].{name:name, remote:remoteVirtualNetwork.id}" -o table
hub-vnet — never spoke2-vnet. There is no route from spoke1 to spoke2, so traffic between the two workloads cannot flow. You have shown the isolation from the network's own configuration, which is how an auditor checks it first. You have not sent a packet: nothing in this lab can, because there are no machines. Write "configured, not tested" beside it in your notes.Make the intent explicit with an NSG
Non-transitive peering already isolates the spokes, but real landing zones state their rules out loud so intent survives future changes. Attach an NSG to spoke1's workload subnet that allows the hub's range and denies spoke2's range explicitly.
az network nsg create -g "$RG" -n spoke1-nsg az network nsg rule create -g "$RG" --nsg-name spoke1-nsg -n allow-hub \ --priority 100 --direction Inbound --access Allow --protocol '*' \ --source-address-prefixes 10.0.0.0/16 --destination-address-prefixes '*' \ --destination-port-ranges '*' az network nsg rule create -g "$RG" --nsg-name spoke1-nsg -n deny-spoke2 \ --priority 200 --direction Inbound --access Deny --protocol '*' \ --source-address-prefixes 10.2.0.0/16 --destination-address-prefixes '*' \ --destination-port-ranges '*' az network vnet subnet update -g "$RG" --vnet-name spoke1-vnet -n workload \ --network-security-group spoke1-nsg
az network nsg rule list -g "$RG" --nsg-name spoke1-nsg \
--query "sort_by([].{p:priority,name:name,access:access,src:sourceAddressPrefix}, &p)" -o table
Two layers now enforce the same intent: routing that offers no path, and a rule that would deny it even if a path appeared. Defence in depth, stated in code.Tear it down
Everything is in one resource group — delete it and confirm.
az group delete -n campux-lab-net-rg --yes
az group exists -n campux-lab-net-rg # -> false
What you can now honestly claim
You built a hub-and-spoke topology, peered spokes to a hub, demonstrated that non-transitive peering keeps the spokes isolated, and expressed that isolation as explicit NSG rules. That is the networking backbone of every Azure landing zone in the job descriptions — "segmentation and least-privilege connectivity between workloads" is this, exactly. The next step in a real design is putting a firewall or a gateway in the hub so spoke-to-spoke traffic can be forced through inspection when it is wanted; the shape you just built is what that inspection plugs into.
- Azure VNet peering is non-transitive by design: peering A-hub and B-hub never implies A-B. To allow controlled spoke-to-spoke traffic you route it through a network virtual appliance or Azure Firewall in the hub, using user-defined routes — which is why the hub exists at all.
- Virtual networks, subnets, peerings, and NSGs carry no hourly charge; you are billed for data processed by gateways and firewalls and for VM compute, none of which this lab creates. That is what makes a networking lab safe to leave running while you read — though tearing it down is still the habit to keep.