Enterprise Hub-Spoke Network on Azure
Build a hub-spoke Azure network with Firewall, VPN Gateway, and Private Endpoints.
Introduction
30 Second Summary
Every organization needs a way to keep its internal systems separated and secure while still allowing controlled communication between teams. In the cloud, this separation is achieved through network architecture that mirrors the physical security of a building with a central reception desk controlling access to each floor.
In this project, you will design and deploy a complete enterprise hub-spoke network in Azure that covers all five AZ-700 exam domains in a single hands-on build.
What You'll Build
A fully functional hub-spoke network architecture in Azure with centralized security through Azure Firewall, hybrid connectivity via a VPN Gateway, application delivery through Application Gateway with WAF, and private access to PaaS services using Private Endpoints.
By the end of this project, you'll have:
- A production-grade hub-spoke network with Azure Firewall providing centralized traffic inspection and User-Defined Routes enforcing all traffic through the firewall.
- A working Point-to-Site VPN connection from your Windows machine to the Azure hub network using certificate-based authentication.
- A Private Endpoint with Private DNS Zone integration securing access to an Azure Storage Account from the spoke network.
- Secret Mission: Configure Azure Firewall rules to enable spoke-to-spoke communication and restrict outbound internet access to only Microsoft domains.
Are there any prerequisites?
You need an Azure account with credits (a $100 student account works perfectly), basic networking knowledge like IP addressing and CIDR notation, and familiarity with the Azure Portal.
This is an advanced project designed for learners preparing for the AZ-700 exam. Experience with Azure networking fundamentals will help you move through the steps confidently.
Design the Network Address Space and Create the Hub VNet
The hub VNet is the foundation of your entire enterprise network. It hosts centralized services that every spoke network depends on.
In this step, you'll create a resource group to hold all project resources, then build the hub VNet with carefully sized subnets for Azure Firewall, VPN Gateway, and Azure Bastion.
In this step, get ready to:
- Create a resource group for the project.
- Design and deploy the hub VNet with three dedicated subnets.
- Verify the network configuration in the Azure Portal.
Create the resource group
A resource group is a container that holds all related Azure resources together. Deleting the resource group at the end removes everything in one action.
- In the Azure Portal search bar at the top, type Resource groups.
- Select Resource groups from the search results.
- Click + Create.
- Select your Azure subscription from the Subscription dropdown.
- In the Resource group field, enter rg-az700-enterprise.
- In the Region dropdown, select East US.
- Click Review + Create.
- Click Create.
What is a resource group?
A resource group is a logical container in Azure. It groups related resources so you can deploy, monitor, and tear them down as a unit.
The region you pick for the resource group stores its metadata. Your actual resources inside can be in any region, but keeping them consistent simplifies things.
- Once the deployment completes, click Go to resource group in the notification banner.
You should see the empty rg-az700-enterprise resource group overview page.
Don't see the resource group?
Click Refresh at the top of the Resource groups list. It can take a few seconds to appear.
If you see a permissions error, make sure you're using a subscription with Owner or Contributor access.
Why can't I create my resource group?
Create the hub VNet with subnets
The hub VNet uses the address space 10.0.0.0/16, giving you 65,536 addresses to carve into subnets. Each subnet is sized specifically for the Azure service it will host.
- In the Azure Portal search bar, type Virtual networks.
- Select Virtual networks from the search results.
- Click + Create.
On the Basics tab:
- Select your Azure subscription.
- In Resource group, select rg-az700-enterprise.
- In Virtual network name, enter vnet-hub.
- In Region, select East US.
- Click Next to proceed to the Security tab.
- Click Next again to proceed to the IP Addresses tab.
On the IP Addresses tab, set up the address space and subnets:
- Confirm the IPv4 address space is set to 10.0.0.0/16. If a default value appears, delete it and enter 10.0.0.0/16.
- If a default subnet exists, select it and click Delete to remove it.
Now add the three required subnets. Click + Add a subnet for each one:
Subnet 1:
- In Subnet purpose, select Azure Firewall. This automatically sets the name to AzureFirewallSubnet.
- Set the Starting address to 10.0.1.0.
- Set the Subnet size to /26 (64 addresses).
- Click Add.
Subnet 2:
- Click + Add a subnet again.
- In Subnet purpose, select Virtual Network Gateway. This automatically sets the name to GatewaySubnet.
- Set the Starting address to 10.0.2.0.
- Set the Subnet size to /27 (32 addresses).
- Click Add.
Subnet 3:
- Click + Add a subnet again.
- In Subnet purpose, select Azure Bastion. This automatically sets the name to AzureBastionSubnet.
- Set the Starting address to 10.0.3.0.
- Set the Subnet size to /26 (64 addresses).
- Click Add.
- Click Review + create at the bottom of the screen.
- When validation passes, click Create.
Why these specific subnet sizes?
Azure Firewall requires a dedicated subnet named AzureFirewallSubnet with a minimum size of /26 (64 addresses). This is an Azure platform requirement.
The GatewaySubnet must be named exactly GatewaySubnet for VPN Gateway to deploy into it. A /27 (32 addresses) is sufficient for this project.
The AzureBastionSubnet hosts Azure Bastion for secure remote access. It also needs a minimum of /26.
Seeing a validation error?
If you see an overlap error, make sure you deleted the default subnet before adding the three new ones. Overlapping address ranges within the same VNet are not allowed.
If the subnet purpose dropdown does not show Azure Firewall or Virtual Network Gateway, ensure you selected a region that supports these services. East US supports all of them.
Help me fix my VNet creation error
Verify the hub VNet and subnet configuration
Now confirm that the VNet and all three subnets deployed correctly.
- Once the deployment finishes, click Go to resource to open the vnet-hub overview page.
- On the left menu, click Subnets under Settings.
You should see all three subnets listed with their address ranges:
- AzureFirewallSubnet with range 10.0.1.0/26.
- GatewaySubnet with range 10.0.2.0/27.
- AzureBastionSubnet with range 10.0.3.0/26.
What is the address space for?
The 10.0.0.0/16 address space gives the hub VNet a pool of 65,536 private IP addresses. The three subnets you created consume only a small portion of this space.
The remaining space is intentionally kept free. Azure best practice is to never allocate the entire VNet address space to subnets, so you have room to add more later.
Your hub network is in place. Next up, you'll create the two spoke VNets and connect them to the hub using VNet peering.
Create the Spoke VNets and Configure VNet Peering
In Step 1, you built the hub VNet with its three specialized subnets. Now you need the spoke networks that represent isolated workloads in your enterprise.
In a hub-spoke topology, each spoke VNet hosts a separate workload. Traffic between spokes is non-transitive by default. This means spokes cannot communicate with each other directly through the hub unless you explicitly route traffic through a central appliance like Azure Firewall. In this step, you will create two spoke VNets and peer them to the hub.
In this step, get ready to:
- Create the web spoke VNet with an Application Gateway subnet and a workload subnet.
- Create the data spoke VNet with a data subnet and a private endpoint subnet.
- Configure bidirectional VNet peering between the hub and each spoke.
Create the web spoke VNet
The first spoke VNet hosts the web tier. It needs two subnets: one dedicated to Application Gateway (which requires its own subnet) and one for the workload VMs.
- In the search box at the top of the Azure Portal, type Virtual network.
- Select Virtual networks in the search results.
- Select + Create.
- On the Basics tab, select your subscription.
- For Resource group, select rg-az700-enterprise.
- For Name, enter vnet-spoke-web.
- For Region, select East US.
- Select Next to proceed to the Security tab.
- Select Next to proceed to the IP Addresses tab.
- Delete the default address space and enter 10.1.0.0/16 as the address space.
- Select the default subnet and edit it. Set the Name to snet-appgw, Starting address to 10.1.1.0, and Subnet size to /24.
- Select Save.
- Select + Add a subnet. Set the Name to snet-workload, Starting address to 10.1.2.0, and Subnet size to /24.
- Select Add.
- Select Review + create, then select Create.
Why two subnets in the web spoke?
Application Gateway requires its own dedicated subnet with no other resources deployed in it. Separating the workload into snet-workload lets you apply different NSG rules and route tables to the VMs independently.
Wait for the deployment to complete before moving on.
Create the data spoke VNet
The second spoke VNet hosts data workloads and private connectivity to PaaS services. You will create it with a data subnet and a private endpoint subnet.
- Return to the Virtual networks page and select + Create.
- On the Basics tab, select your subscription.
- For Resource group, select rg-az700-enterprise.
- For Name, enter vnet-spoke-data.
- For Region, select East US.
- Select Next to proceed to the Security tab.
- Select Next to proceed to the IP Addresses tab.
- Delete the default address space and enter 10.2.0.0/16 as the address space.
- Select the default subnet and edit it. Set the Name to snet-data, Starting address to 10.2.1.0, and Subnet size to /24.
- Select Save.
- Select + Add a subnet. Set the Name to snet-pe, Starting address to 10.2.2.0, and Subnet size to /24.
- Select Add.
- Select Review + create, then select Create.
What is snet-pe for?
The snet-pe subnet will host Private Endpoints later in the project. Private Endpoints place a network interface into your VNet that connects to an Azure PaaS service over a private IP address, keeping traffic off the public internet.
Wait for the deployment to complete. You should now have three VNets visible in the Virtual networks list.
Don't see all three VNets?
Make sure you selected rg-az700-enterprise as the resource group for each VNet. If you accidentally created a VNet in a different resource group, delete it and recreate it in rg-az700-enterprise.
Also confirm the Region is set to East US for both spoke VNets.
Help me troubleshoot my missing VNets in the Azure Portal.
Configure bidirectional VNet peering
VNet peering connections are non-transitive. Each spoke must be explicitly peered to the hub. You will configure the peering from the hub side, which automatically creates the link on both ends in a single operation.
What is VNet peering?
VNet peering connects two virtual networks so they appear as one for connectivity purposes. Traffic between peered VNets stays on the Microsoft backbone and never touches the public internet.
For a hub-spoke topology, you enable Allow gateway transit on the hub side so spokes can use the hub's VPN Gateway. On the spoke side, you enable Use remote gateways so the spoke routes VPN traffic through the hub.
First, peer the hub to the web spoke.
- In the Virtual networks list, select vnet-hub.
- In the left menu under Settings, select Peerings.
- Select + Add.
- Under Remote virtual network summary, for Peering link name enter spoke-web-to-hub.
- For Virtual network, select vnet-spoke-web.
- Under Remote virtual network peering settings, check the box for Allow 'vnet-spoke-web' to receive forwarded traffic from 'vnet-hub'.
- Check the box for Enable 'vnet-spoke-web' to use 'vnet-hub's' remote gateway or route server.
- Under Local virtual network peering summary, for Peering link name enter hub-to-spoke-web.
- Under Local virtual network peering settings, check the box for Allow 'vnet-hub' to receive forwarded traffic from 'vnet-spoke-web'.
- Check the box for Allow gateway or route server in 'vnet-hub' to forward traffic to 'vnet-spoke-web'.
- Select Add.
What do these peering settings do?
- Allow forwarded traffic lets traffic that originates from outside the peered VNet (such as traffic forwarded by Azure Firewall) pass through the peering.
- Allow gateway transit (on the hub) makes the hub's VPN Gateway available to the spoke.
- Use remote gateways (on the spoke) tells the spoke to route gateway-bound traffic through the hub's gateway instead of requiring its own.
Now peer the hub to the data spoke using the same process.
- On the Peerings page for vnet-hub, select + Add again.
- Under Remote virtual network summary, for Peering link name enter spoke-data-to-hub.
- For Virtual network, select vnet-spoke-data.
- Under Remote virtual network peering settings, check the box for Allow 'vnet-spoke-data' to receive forwarded traffic from 'vnet-hub'.
- Check the box for Enable 'vnet-spoke-data' to use 'vnet-hub's' remote gateway or route server.
- Under Local virtual network peering summary, for Peering link name enter hub-to-spoke-data.
- Under Local virtual network peering settings, check the box for Allow 'vnet-hub' to receive forwarded traffic from 'vnet-spoke-data'.
- Check the box for Allow gateway or route server in 'vnet-hub' to forward traffic to 'vnet-spoke-data'.
- Select Add.
After a few seconds, select the Refresh button on the Peerings page. Both peerings should show a Peering status of Connected.
Peering status shows Initiated instead of Connected?
Both directions of a peering must be established for the status to change to Connected. If you see Initiated, the remote side was not created. Delete the peering and recreate it, ensuring you fill in both the Remote virtual network and Local virtual network sections before clicking Add.
Help me troubleshoot my VNet peering status stuck on Initiated.
Your spoke VNets are now connected to the hub. In the next step, you will deploy Azure Firewall in the hub and create route tables that force all spoke traffic through it for centralized inspection.
Deploy Azure Firewall and Configure UDRs
Your hub-spoke network has the right shape, but right now traffic can flow freely between spokes and out to the internet without any inspection. In a real enterprise, every packet leaving a workload subnet must pass through a centralized security appliance.
In this step, you will deploy Azure Firewall Basic into the hub VNet and create User-Defined Routes (UDRs) that force all outbound traffic from the spoke subnets through the firewall for inspection.
In this step, get ready to:
- Deploy Azure Firewall Basic into the hub VNet with a public IP.
- Create a route table with a default route pointing to the firewall.
- Associate the route table with spoke workload subnets to enforce centralized traffic flow.
Deploy Azure Firewall Basic into the hub VNet
Azure Firewall is a managed, cloud-based network security service that filters traffic at the network and application layers. The Basic SKU costs $0.395 per deployment hour and supports network rules, application rules, and DNAT rules.
- In the Azure Portal search bar at the top, type Firewall and select Firewalls from the results.
- Click + Create.
- On the Basics tab, enter the following values:
- Set Resource group to rg-az700-enterprise.
- Set Name to fw-hub.
- Set Region to East US.
- Set Firewall tier to Basic.
- Set Firewall management to Use a Firewall Policy to manage this firewall.
- For Firewall policy, click Add new. Name it fp-hub-basic, confirm the region is East US and the policy tier is Basic, then click OK.
- For Choose a virtual network, select Use existing and choose vnet-hub.
- For Public IP address, click Add new and enter pip-fw-hub as the name. Click OK.
- For Management public IP address, click Add new and enter pip-fw-hub-mgmt as the name. Click OK.
Why does Azure Firewall Basic need a management public IP?
Azure Firewall Basic always uses a dedicated management NIC and subnet (AzureFirewallManagementSubnet) to separate Microsoft's automatic update and health telemetry traffic from your customer traffic.
The portal will prompt you for this management subnet configuration. If the subnet does not already exist, the portal can create it for you inline during deployment.
- Click Review + create.
- Review the summary, then click Create.
How long does this deployment take?
Azure Firewall deployment typically takes 5 to 10 minutes. Wait for the deployment to complete before proceeding.
- Once the deployment finishes, click Go to resource.
- On the firewall Overview page, find and note down the Private IP address (it will be an IP from the 10.0.1.0/26 range, typically 10.0.1.4). You will need this IP in the next substep.
Write down the firewall private IP
Copy the private IP to a text file or notepad. You will use it as the next hop address in your route table.
Deployment failed?
If you see a deployment error, verify that your AzureFirewallSubnet is at least /26 (64 addresses). Azure Firewall requires this minimum size.
Also confirm you are deploying into the correct resource group and region (East US).
Help me troubleshoot my Azure Firewall deployment error in the rg-az700-enterprise resource group.
Create a route table with a default route to the firewall
A route table with a default route (0.0.0.0/0) pointing to the firewall ensures that any traffic leaving the spoke subnets goes through Azure Firewall for inspection before reaching the internet or other networks.
- In the Azure Portal search bar, type Route tables and select Route tables from the results.
- Click + Create.
- Set Resource group to rg-az700-enterprise.
- Set Region to East US.
- Set Name to rt-spoke-to-fw.
- Leave Propagate gateway routes set to Yes.
- Click Review + create, then click Create.
- After deployment completes, click Go to resource.
Now add the default route that forces traffic through the firewall.
- In the route table blade, expand Settings and select Routes.
- Click + Add.
- Set Route name to default-to-fw.
- Set Destination type to IP Addresses.
- Set Destination IP addresses/CIDR ranges to 0.0.0.0/0.
- Set Next hop type to Virtual appliance.
- Set Next hop address to the Azure Firewall private IP you noted earlier (e.g. 10.0.1.4).
- Click Add.
What does this route do?
The destination 0.0.0.0/0 is a catch-all that matches all traffic not covered by a more specific route. By setting the next hop to the firewall's private IP as a Virtual appliance, you are telling Azure to send all outbound traffic from associated subnets through the firewall.
This is how hub-spoke architectures achieve centralized security inspection. The firewall sees every packet and can apply rules before allowing or denying it.
Route not saving?
Double check that the next hop IP address exactly matches your firewall's private IP from the Overview page. A typo here will cause the route to fail silently.
My route table route is not saving or shows an error. Help me fix it.
Associate the route table with spoke workload subnets
A route table has no effect until you associate it with one or more subnets. You need to associate it with snet-workload in vnet-spoke-web and snet-data in vnet-spoke-data.
- In the rt-spoke-to-fw route table blade, expand Settings and select Subnets.
- Click + Associate.
- For Virtual network, select vnet-spoke-web.
- For Subnet, select snet-workload.
- Click OK.
Now associate the same route table with the data spoke subnet.
- Click + Associate again.
- For Virtual network, select vnet-spoke-data.
- For Subnet, select snet-data.
- Click OK.
You should now see both subnets listed under the Subnets section of your route table.
Why not associate with snet-appgw or snet-pe?
The Application Gateway subnet (snet-appgw) has its own routing requirements and should not have a default route through the firewall. The private endpoint subnet (snet-pe) handles Azure-internal traffic that does not leave the VNet, so forcing it through the firewall is unnecessary for this architecture.
Only the workload subnets where VMs generate outbound traffic need the forced route.
Subnet not appearing in the dropdown?
Ensure you are selecting the correct virtual network first. The subnet dropdown only shows subnets belonging to the selected VNet.
If the subnet already has a route table associated, you must disassociate it first before applying a new one.
My subnet is not showing up when I try to associate the route table. Help me figure out why.
All outbound traffic from your workload subnets now routes through Azure Firewall for inspection. Next up, you will lock down inbound access with Network Security Groups to enforce least-privilege rules on each subnet.
Deploy Network Security Groups with Least-Privilege Rules
Your firewall is now routing all spoke traffic through a central inspection point. However, Azure Firewall operates at the network perimeter. You still need subnet-level protection to enforce which traffic is even allowed to reach your workloads.
Network Security Groups add a second layer of defense directly on the subnet. They evaluate traffic using a five-tuple (source, destination, port, protocol, direction) and block anything not explicitly allowed. In this step, you will create two NSGs with least-privilege inbound rules and associate them with the workload subnets.
In this step, get ready to:
- Create nsg-workload with rules allowing only HTTP, HTTPS, and SSH from trusted sources.
- Create nsg-data with a rule allowing only port 443 from the workload subnet.
- Associate both NSGs with their respective subnets and verify the effective security rules.
Create the workload NSG with inbound rules
The workload subnet in spoke-web will host a web server. It needs to accept HTTP and HTTPS traffic from the Application Gateway subnet and SSH connections from VPN clients for management.
- In the Azure Portal search bar, type Network security group and select Network security groups from the results.
- Select + Create.
- On the Basics tab, select your subscription.
- For Resource group, select rg-az700-enterprise.
- For Name, enter nsg-workload.
- For Region, select East US.
- Select Review + create, then select Create.
Now add the first inbound rule to allow HTTP and HTTPS from the Application Gateway subnet.
- Once the NSG is created, select Go to resource.
- Under Settings, select Inbound security rules.
- Select + Add.
- For Source, select IP Addresses.
- For Source IP addresses/CIDR ranges, enter 10.1.1.0/24.
- For Source port ranges, leave the default *.
- For Destination, leave Any.
- For Service, leave Custom.
- For Destination port ranges, enter 80,443.
- For Protocol, select TCP.
- For Action, select Allow.
- For Priority, enter 100.
- For Name, enter Allow-HTTP-HTTPS-From-AppGw.
- Select Add.
What does this rule do?
This rule allows TCP traffic on ports 80 (HTTP) and 443 (HTTPS) only from the 10.1.1.0/24 subnet, which is snet-appgw where the Application Gateway will sit. No other source can reach these ports on your workload VMs.
Priority 100 means this rule is evaluated first. NSG rules use priority numbers between 100 and 4096, with lower numbers processed first.
Now add the second rule to allow SSH from the VPN client address pool.
- Select + Add again to create another inbound rule.
- For Source, select IP Addresses.
- For Source IP addresses/CIDR ranges, enter 172.16.0.0/24.
- For Source port ranges, leave *.
- For Destination, leave Any.
- For Service, leave Custom.
- For Destination port ranges, enter 22.
- For Protocol, select TCP.
- For Action, select Allow.
- For Priority, enter 110.
- For Name, enter Allow-SSH-From-VPN.
- Select Add.
Why 172.16.0.0/24 as the source?
This is the Point-to-Site VPN client address pool you will configure later in Step 5. When you connect via VPN, your machine gets an IP from this range. By restricting SSH to only this range, no other network can attempt SSH connections to your workload VMs.
You now have two explicit allow rules. Any traffic that does not match these rules hits the default DenyAllInBound rule at priority 65500, which Azure creates automatically in every NSG.
What is DenyAllInBound?
Azure automatically creates default rules in every NSG. The DenyAllInBound rule has priority 65500 and denies all inbound traffic that is not matched by any higher-priority rule. This is what enforces least-privilege. You only need to add explicit Allow rules for traffic you want to permit.
NSGs are also stateful. If an inbound request is allowed, the return traffic is automatically permitted without needing a separate outbound rule.
Create the data NSG with inbound rules
The data subnet in spoke-data only needs to accept secure HTTPS connections from the workload subnet. Everything else should be denied.
- In the Azure Portal search bar, type Network security group and select Network security groups.
- Select + Create.
- For Resource group, select rg-az700-enterprise.
- For Name, enter nsg-data.
- For Region, select East US.
- Select Review + create, then select Create.
- Select Go to resource.
Add the inbound rule for the data NSG.
- Under Settings, select Inbound security rules.
- Select + Add.
- For Source, select IP Addresses.
- For Source IP addresses/CIDR ranges, enter 10.1.2.0/24.
- For Source port ranges, leave *.
- For Destination, leave Any.
- For Service, leave Custom.
- For Destination port ranges, enter 443.
- For Protocol, select TCP.
- For Action, select Allow.
- For Priority, enter 100.
- For Name, enter Allow-443-From-Workload.
- Select Add.
Why restrict the data subnet so tightly?
The data subnet will host a Private Endpoint for Azure Storage. Only the web workload subnet (10.1.2.0/24) should be able to reach it. This is network segmentation in action. Even if an attacker compromises a different subnet, they cannot reach the data tier.
Associate NSGs with subnets and verify effective security rules
Creating the NSGs alone does not enforce them. You need to associate each NSG with its target subnet for the rules to take effect.
- In the Azure Portal search bar, type Network security group and select Network security groups.
- Select nsg-workload.
- Under Settings, select Subnets.
- Select + Associate.
- For Virtual network, select vnet-spoke-web.
- For Subnet, select snet-workload.
- Select OK.
Now repeat for the data NSG.
- Navigate back to Network security groups and select nsg-data.
- Under Settings, select Subnets.
- Select + Associate.
- For Virtual network, select vnet-spoke-data.
- For Subnet, select snet-data.
- Select OK.
Verify the rules are in place by inspecting the NSG's inbound rules list.
- Navigate back to Network security groups and select nsg-workload.
- Under Settings, select Inbound security rules.
- Confirm you see your two custom rules (Allow-HTTP-HTTPS-From-AppGw at priority 100 and Allow-SSH-From-VPN at priority 110) plus the three default rules from Azure.
- Navigate to nsg-data and confirm you see Allow-443-From-Workload at priority 100 plus the default rules.
What are the three default rules?
Every Azure NSG comes with three default inbound rules that cannot be deleted:
- AllowVNetInBound (priority 65000) allows all traffic within the virtual network.
- AllowAzureLoadBalancerInBound (priority 65001) allows health probes from Azure Load Balancer.
- DenyAllInBound (priority 65500) denies everything else.
Your custom rules have lower priority numbers, so they are evaluated before any default rule.
NSG not appearing under Subnets?
Make sure you selected the correct virtual network during association. nsg-workload should be associated with vnet-spoke-web and nsg-data with vnet-spoke-data. If you accidentally chose the hub VNet, dissociate it and try again.
Also confirm both NSGs are in the same region (East US) as the VNets. NSGs cannot be associated with resources in a different region.
help me troubleshoot my NSG subnet association
Both NSGs are now enforcing least-privilege inbound rules on your spoke subnets. Next up, you will deploy a VPN Gateway to enable secure remote access into the hub network.
Deploy VPN Gateway and Configure Point-to-Site VPN
Your hub network has centralized security through Azure Firewall, and your spokes are locked down with NSGs. The next piece of the enterprise puzzle is hybrid connectivity.
A VPN Gateway lets remote workers connect directly into your Azure network as if they were on-site. In this step, you will deploy a Point-to-Site (P2S) VPN so your Windows machine can tunnel into the hub VNet using certificate-based authentication.
In this step, get ready to:
- Deploy a VPN Gateway into the hub VNet's GatewaySubnet.
- Generate self-signed root and client certificates using PowerShell.
- Configure Point-to-Site VPN with certificate authentication on the gateway.
Deploy the VPN Gateway into GatewaySubnet
The VPN Gateway is a managed Azure resource that terminates encrypted VPN tunnels. It must live in the dedicated GatewaySubnet you created inside vnet-hub earlier.
- In the Azure Portal search bar, type virtual network gateway and select **Virtual network gateway** from the Marketplace results.
- Click + Create to open the Create virtual network gateway page.
- On the Basics tab, fill in the following values:
- Name: vpngw-hub.
- Region: East US.
- Gateway type: select VPN.
- SKU: select VpnGw1.
- Generation: select Generation1.
- Virtual network: select vnet-hub.
- Subnet: should auto-populate with GatewaySubnet (10.0.2.0/27).
- Under Public IP address, select Create new.
- Enter the name pip-vpngw-hub.
- Set Enable active-active mode to Disabled.
- Set Configure BGP to Disabled.
- Click Review + create, then click Create.
Why VpnGw1 instead of a higher SKU?
VpnGw1 supports up to 128 SSTP connections and 250 IKEv2/OpenVPN connections with 650 Mbps throughput. This is more than enough for a learning lab. It is also covered by the Azure free tier (750 hours per month for 12 months on new accounts), which helps protect your student credits.
VPN Gateway deployment takes 45 minutes or more
This is expected. Do not wait at this screen. Continue with the certificate generation steps below while the gateway deploys. You will return to configure Point-to-Site once the deployment completes.
Generate self-signed certificates using PowerShell
Azure P2S VPN with certificate authentication requires two certificates: a root certificate (whose public key you upload to Azure) and a client certificate (installed on the connecting machine). You will generate both on your Windows machine using PowerShell.
- Press the Windows key to open your search bar.
- Type PowerShell and select Run as administrator to open an elevated PowerShell console.
Generate the self-signed root certificate by running:
$params = @{
Type = 'Custom'
Subject = 'CN=P2SRootCert'
KeySpec = 'Signature'
KeyExportPolicy = 'Exportable'
KeyUsage = 'CertSign'
KeyUsageProperty = 'Sign'
KeyLength = 2048
HashAlgorithm = 'sha256'
NotAfter = (Get-Date).AddMonths(24)
CertStoreLocation = 'Cert:\CurrentUser\My'
}
$cert = New-SelfSignedCertificate @params
What does this command do?
- Creates a root certificate named P2SRootCert in your personal certificate store.
- The KeyUsage = 'CertSign' parameter marks this as a Certificate Authority so it can sign child certificates.
- The $cert variable holds a reference to the certificate. You need this to generate the client certificate next.
Keep the same PowerShell window open. Generate the client certificate by running:
$params = @{
Type = 'Custom'
Subject = 'CN=P2SChildCert'
DnsName = 'P2SChildCert'
KeySpec = 'Signature'
KeyExportPolicy = 'Exportable'
KeyLength = 2048
HashAlgorithm = 'sha256'
NotAfter = (Get-Date).AddMonths(18)
CertStoreLocation = 'Cert:\CurrentUser\My'
Signer = $cert
TextExtension = @(
'2.5.29.37={text}1.3.6.1.5.5.7.3.2')
}
New-SelfSignedCertificate @params
What does this command do?
- Creates a client certificate named P2SChildCert signed by the root certificate.
- The Signer = $cert parameter chains this certificate to the root you just created.
- The TextExtension value marks this as a client authentication certificate (OID 1.3.6.1.5.5.7.3.2).
- The client certificate is automatically installed in your personal certificate store.
Now you need to export the root certificate's public key so you can upload it to Azure.
- Press the Windows key, type certmgr.msc, and press Enter to open Manage User Certificates.
- Navigate to Personal > Certificates.
- Right-click P2SRootCert, select All Tasks, then Export.
- In the Certificate Export Wizard, click Next.
- Select No, do not export the private key, then click Next.
- Select Base-64 encoded X.509 (.CER), then click Next.
- Save the file to your Desktop as P2SRootCert.cer.
- Click Finish.
- Open P2SRootCert.cer with Notepad.
- Copy everything between -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- (do not include those header/footer lines). This is the public key data you will paste into Azure.
Keep Notepad open
You will paste this certificate data into the Azure Portal in the next substep. Keep Notepad open so you can copy it when needed.
Don't see P2SRootCert in certmgr?
Make sure you opened Manage User Certificates (Current User), not Manage Computer Certificates (Local Machine). The certificate was stored in Cert:\CurrentUser\My.
If you closed the PowerShell window and lost the $cert variable before generating the client certificate, re-run the root certificate command. It will create a new root certificate.
Help me troubleshoot my P2S certificate generation in PowerShell
Configure Point-to-Site VPN on the gateway
Once the VPN Gateway deployment finishes (check the Notifications bell icon in the Azure Portal for a success message), you can configure the P2S settings.
- In the Azure Portal, navigate to your newly created vpngw-hub virtual network gateway.
- In the left menu under Settings, click Point-to-site configuration.
- Click Configure now.
- In Address pool, enter 172.16.0.0/24. This is the IP range that VPN clients will receive when they connect.
- Under Tunnel type, select IKEv2 and SSTP (SSL).
- Under Authentication type, select Azure certificate.
- Scroll down to the Root certificates section.
- In the Name field, enter P2SRootCert.
- In the Public certificate data field, paste the Base64 certificate text you copied from Notepad (the content between BEGIN and END lines, without those lines).
- Click Save at the top of the page.
What is happening here?
You told the VPN Gateway to trust any client certificate signed by P2SRootCert. When a VPN client connects, Azure checks that the client certificate chains back to this root. The address pool 172.16.0.0/24 is the same range your nsg-workload SSH rule allows inbound, so VPN clients can SSH into workload VMs.
IKEv2 provides fast reconnection on network changes. SSTP works through firewalls that block IKEv2 (port 443). Together they cover most client scenarios on Windows.
Verify the configuration saved successfully.
- After saving, the page should display your address pool 172.16.0.0/24, tunnel type IKEv2 and SSTP(SSL), and the root certificate name P2SRootCert.
- Confirm these three values appear on the page.
Save button greyed out or validation error?
Ensure the address pool does not overlap with any existing VNet address space (172.16.0.0/24 is outside 10.x.x.x). Check that the certificate data you pasted does not include the -----BEGIN CERTIFICATE----- or -----END CERTIFICATE----- lines, and has no extra whitespace or line breaks.
Help me fix my P2S configuration in Azure Portal
Your VPN Gateway is live and configured for Point-to-Site access. Next up, you will deploy an Application Gateway with WAF to protect web traffic in the spoke network.
Deploy Application Gateway with WAF
Your network is locked down with centralized firewall inspection and least-privilege NSGs. Now you need a way for external users to reach a web application inside the spoke network safely.
In this step, you will deploy a web server VM behind an Application Gateway with WAF (Web Application Firewall). The Application Gateway handles Layer 7 load balancing and the WAF inspects every HTTP request for common attacks like SQL injection and cross-site scripting before traffic reaches your backend.
In this step, get ready to:
- Deploy a virtual machine running NGINX in the web spoke workload subnet.
- Deploy Application Gateway WAF v2 in the app gateway subnet with a WAF policy in Prevention mode.
- Test web application access through the Application Gateway public IP and confirm WAF inspection.
Deploy the backend web server VM
The Application Gateway needs a backend server to route traffic to. You will create a small Ubuntu VM in snet-workload and install NGINX on it automatically using a cloud-init custom data script during provisioning.
- In the Azure Portal search bar, type Virtual machines and select it.
- Click + Create and select Azure virtual machine.
On the Basics tab, configure:
- Set Resource group to rg-az700-enterprise.
- Set Virtual machine name to vm-web-01.
- Set Region to East US.
- Set Image to Ubuntu Server 22.04 LTS - x64 Gen2.
- Set Size to Standard_B1s (free-tier eligible).
- Set Authentication type to Password.
- Enter a username and password you will remember.
- Set Public inbound ports to None.
On the Networking tab, configure:
- Set Virtual network to vnet-spoke-web.
- Set Subnet to snet-workload (10.1.2.0/24).
- Set Public IP to None.
On the Advanced tab, scroll down to Custom data and paste the following cloud-init script:
#cloud-config
package_upgrade: true
packages:
- nginx
runcmd:
- systemctl start nginx
- systemctl enable nginx
What does this script do?
The #cloud-config header tells Azure to process this as a cloud-init configuration file on first boot.
It upgrades all packages, installs NGINX, then starts and enables the NGINX service so it runs automatically. This gives you a working web server without needing to SSH into the VM manually.
- Click Review + create, then click Create.
- Wait for the deployment to complete. This takes a few minutes.
Why no public IP on the VM?
In this architecture, external users never connect directly to the VM. All inbound traffic arrives through the Application Gateway, which has its own public IP. The VM only needs a private IP inside snet-workload.
For management access, you will use the Point-to-Site VPN you configured earlier.
VM deployment failed?
If you see a quota error, your subscription may not allow B1s VMs in East US. Try selecting a different size or request a quota increase under Subscription > Usage + quotas.
If the deployment times out, confirm that vnet-spoke-web and snet-workload exist in the same region (East US) as the VM.
Need help troubleshooting your VM deployment?
Deploy Application Gateway WAF v2
The Application Gateway sits in the dedicated snet-appgw subnet and inspects all inbound web traffic before routing it to your backend VM.
- In the Azure Portal search bar, type Application gateways and select it.
- Click + Create.
On the Basics tab, configure:
- Set Resource group to rg-az700-enterprise.
- Set Application gateway name to appgw-web.
- Set Region to East US.
- Set Tier to WAF V2.
- For WAF Policy, click Create new. Enter waf-policy-web as the policy name and click OK.
- Under Configure virtual network, select the existing vnet-spoke-web virtual network.
- Set Subnet to snet-appgw (10.1.1.0/24).
- Click Next: Frontends.
On the Frontends tab:
- Verify Frontend IP address type is set to Public.
- Click Add new for the public IP address, enter pip-appgw-web as the name, and click OK.
- Click Next: Backends.
On the Backends tab:
- Click Add a backend pool.
- Set Name to backend-pool-web.
- Under Backend targets, set Target type to Virtual machine.
- Select vm-web-01 and its network interface from the Target dropdown.
- Click Add.
- Click Next: Configuration.
On the Configuration tab:
- Click Add a routing rule.
- Set Rule name to rule-http.
- Set Priority to 100.
On the Listener tab within the routing rule:
- Set Listener name to listener-http.
- Set Frontend IP to Public IPv4.
- Set Protocol to HTTP and Port to 80.
- Click the Backend targets tab.
On the Backend targets tab within the routing rule:
- Set Backend target to backend-pool-web.
- For Backend settings, click Add new.
- Set Backend settings name to http-settings.
- Leave Backend protocol as HTTP and Backend port as 80.
- Click Add to save the backend settings.
- Click Add to save the routing rule.
- Click Next: Tags, then Next: Review + create.
- Review your settings, then click Create.
What is WAF Prevention mode?
When you created the WAF policy, it defaulted to Detection mode, which only logs malicious requests. You need to switch it to Prevention mode so the WAF actively blocks requests that match OWASP rules.
In Prevention mode, matching requests are blocked and logged in the WAF logs. In Detection mode, only logging occurs.
The Application Gateway deployment takes several minutes. While you wait, switch the WAF policy to Prevention mode.
- In the Azure Portal search bar, type Web Application Firewall policies and select it.
- Click waf-policy-web.
- On the overview page, click Switch to Prevention mode.
Application Gateway deployment failed?
Ensure snet-appgw does not have any NSG associated with it that blocks required ports. Application Gateway v2 requires inbound access on TCP ports 65200-65535 from the GatewayManager service tag.
Confirm the subnet snet-appgw is dedicated to the Application Gateway. No other resources (VMs, NICs) can reside in this subnet.
Help me troubleshoot my Application Gateway deployment failure.
Test access and verify WAF inspection
With both the VM and Application Gateway deployed, you can verify the end-to-end traffic flow. External users hit the Application Gateway public IP, WAF inspects the request, and if it passes, traffic routes to vm-web-01 on port 80.
- Navigate to Application gateways and select appgw-web.
- On the Overview page, copy the Frontend public IP address.
- Open a new browser tab and paste the IP address into the address bar.
- You should see the default NGINX welcome page ("Welcome to nginx!").
What does seeing the NGINX page confirm?
A successful NGINX page means the entire chain is working. The request entered through the Application Gateway public IP, passed the WAF inspection (no malicious patterns detected), routed through the backend pool to vm-web-01's private IP on port 80, and returned the NGINX default page.
The Application Gateway also configured a default health probe that checks HTTP 200-399 responses from your backend. A healthy backend status on the overview page confirms the probe is passing.
Now verify the WAF is actively inspecting traffic by sending a simulated attack:
- In your browser address bar, append a test SQL injection string to the Application Gateway IP. For example:
- http://<your-appgw-IP>/?id=1 OR 1=1.
- You should see a 403 Forbidden response. This confirms the WAF blocked the malicious request in Prevention mode.
Why did you get a 403?
The WAF policy uses OWASP Core Rule Set (CRS) managed rules by default. The SQL injection pattern OR 1=1 matches a rule in the CRS, so the WAF blocked the request and returned a 403 Forbidden response.
In Detection mode, this request would have been logged but still allowed through. Prevention mode actively blocks it, which is what you want in production.
- To check Backend health, return to appgw-web in the portal and select Backend health in the left menu.
- Confirm that vm-web-01 shows a status of Healthy.
Seeing a 502 Bad Gateway error?
A 502 means the Application Gateway cannot reach the backend VM. Check that vm-web-01 is running and that NGINX is active on port 80.
Verify that nsg-workload allows HTTP inbound from 10.1.1.0/24 (the Application Gateway subnet). The health probe originates from the gateway subnet.
Help me fix a 502 Bad Gateway error on my Application Gateway.
Your web application is now protected by Application Gateway WAF v2 in Prevention mode. Next up, you will secure access to Azure PaaS services using Private Endpoints and Private DNS Zones.
Configure Private Endpoint and Private DNS Zone
Your Application Gateway is delivering traffic to the web spoke, but the data spoke still needs secure access to Azure PaaS services. Right now, any connection to Azure Storage would travel over the public internet.
In this step, you will create a storage account with public access disabled, then connect it to your spoke network using a Private Endpoint. A Private DNS Zone will ensure that DNS queries for the storage account resolve to the private IP instead of the public endpoint.
In this step, get ready to:
- Create a storage account with public network access disabled.
- Create a Private Endpoint connecting the storage account to your data spoke.
- Set up a Private DNS Zone and link it to all three VNets for name resolution.
Create the Azure Storage Account
You need a storage account to serve as the PaaS resource that the data spoke will access privately. By disabling public network access at creation time, you ensure that only private endpoint connections can reach the storage data.
- In the Azure Portal search bar, type Storage accounts and select Storage accounts from the results.
- Click + Create.
- On the Basics tab, configure the following:
- Click Next until you reach the Networking tab.
- Under Public network access, select Disable.
- Click Review + create, then click Create.
- Wait for deployment to complete, then click Go to resource.
Why disable public network access?
Disabling public access forces all traffic to the storage account through the private endpoint. This is the enterprise-grade approach tested in the AZ-700 exam. The storage account becomes invisible to the public internet.
Storage account names must be globally unique
If staz700data is already taken, append your initials or a short number (e.g. staz700datajd01). Keep it all lowercase with no special characters.
You should now see the storage account overview page with a status of Succeeded.
Storage account creation failed?
If you see a naming conflict error, the name is already taken globally. Try a different name by appending random digits.
If deployment fails with a quota error, confirm you are deploying in East US and that your subscription has not exceeded its storage account limit (250 per region).
help me troubleshoot why my Azure Storage Account failed to create
Create the Private Endpoint
A Private Endpoint places a network interface with a private IP from your VNet directly into the storage account's data plane. Traffic from your spoke network reaches storage without ever touching the public internet.
- In the Azure Portal search bar, type Private endpoint and select Private endpoints.
- Click + Create.
- On the Basics tab, configure the following:
- Click Next: Resource.
- On the Resource tab, configure:
- Click Next: Virtual Network.
- On the Virtual Network tab, configure:
- Click Next: DNS.
- On the DNS tab, set Integrate with private DNS zone to No. You will create the Private DNS Zone manually in the next substep for full control over which VNets are linked.
- Click Next: Tags, then click Next: Review + create.
- Click Create.
What does the Private Endpoint do?
The private endpoint creates a network interface in snet-pe (10.2.2.0/24) with a private IP address. All blob traffic to staz700data now stays on the Azure backbone and never traverses the public internet.
The blob target subresource means this endpoint handles Azure Blob Storage traffic specifically. Other storage services (files, queues, tables) would each need their own private endpoint.
Once deployment completes, navigate to the private endpoint resource. Under Overview, confirm the Connection status shows Approved.
Connection status showing Pending?
If the connection status is Pending instead of Approved, navigate to your storage account, then go to Security + networking > Networking > Private endpoint connections. Select the pending connection and click Approve.
help me approve a pending private endpoint connection to my Azure Storage Account
Create the Private DNS Zone and link it to all VNets
Without DNS integration, clients would still resolve staz700data.blob.core.windows.net to a public IP. A Private DNS Zone with the correct zone name overrides this resolution so that all linked VNets receive the private endpoint IP instead.
- In the Azure Portal search bar, type Private DNS zones and select Private DNS zones.
- Click + Create.
- Select your subscription and resource group rg-az700-enterprise.
- For Name, enter privatelink.blob.core.windows.net.
- Click Review + create, then Create.
Why this specific zone name?
Azure requires a specific Private DNS Zone name for each storage service type. For Blob Storage, the zone must be named privatelink.blob.core.windows.net. When a client queries staz700data.blob.core.windows.net, Azure DNS chains through a CNAME to staz700data.privatelink.blob.core.windows.net, which your Private DNS Zone resolves to the private endpoint IP.
Now link the zone to all three VNets so that resources in any network can resolve the storage account to its private IP.
- Open your newly created privatelink.blob.core.windows.net zone.
- In the left menu under DNS Management, select Virtual network links.
- Click + Add to create the first link:
- Click + Add again for the second link:
- Click + Add once more for the third link:
Now add the DNS A record that maps the storage account to the private endpoint IP.
- In the privatelink.blob.core.windows.net zone, select Overview.
- Click + Record set.
- For Name, enter staz700data.
- For Type, select A.
- For IP address, enter the private IP of your pe-storage endpoint. Find this by navigating to the pe-storage resource and checking the Network interface section for its private IP (it will be in the 10.2.2.0/24 range).
- Click OK.
How to find the private endpoint IP
Navigate to pe-storage in the portal. Under Overview, look for the Network interface link. Click it, and on the NIC page you will see the Private IP address field with an address from 10.2.2.0/24 (for example 10.2.2.4).
Verify your setup by checking the Virtual network links page. You should see three links, all with a Link status of Completed.
Also confirm the A record is listed on the zone's Overview page with the correct private IP.
Link status stuck on In Progress?
Virtual network links can take a few minutes to become Completed depending on VNet size. Wait 2-3 minutes and refresh the page.
If a link fails, verify the VNet exists in the same subscription and that you have write permissions on both the VNet and the DNS zone.
help me troubleshoot why my Private DNS Zone virtual network link is not completing
Your Private Endpoint and DNS integration are complete. Any VM in a linked VNet that queries staz700data.blob.core.windows.net will now resolve to the private IP in snet-pe. In the next step, you will validate end-to-end connectivity across the entire architecture.
Validate End-to-End Connectivity
You have built a complete hub-spoke network with centralized security, hybrid connectivity, and private access to PaaS services. Now it is time to prove that every component works together.
In this step, you will connect to the hub network from your Windows machine over the Point-to-Site VPN, then validate that traffic flows correctly between spokes, through the firewall, and to private endpoints.
In this step, get ready to:
- Download and install the P2S VPN client profile from your VPN Gateway.
- Verify private connectivity from your VPN session to vm-web-01 through Azure Firewall.
- Confirm Private Endpoint DNS resolution and firewall-routed internet traffic from vm-web-01.
Download and install the VPN client profile
The VPN client profile is a configuration package that contains all the settings your Windows machine needs to establish a P2S connection to the hub VNet. You download it from the gateway's Point-to-site configuration page.
- In the Azure Portal, search for vpngw-hub and select your virtual network gateway.
- In the left menu under Settings, select Point-to-site configuration.
- At the top of the Point-to-site configuration page, click Download VPN client.
- Wait for the configuration package to generate. After a few minutes, your browser downloads a ZIP file named after your gateway.
- Extract the ZIP file to a folder on your machine.
What is inside the VPN client package?
Because you configured IKEv2 and SSTP as tunnel types with certificate authentication, the ZIP contains installer packages for the Windows native VPN client. For a 64-bit Windows machine, use the WindowsAmd64 folder. For 32-bit, use WindowsX86.
The package also contains a Generic folder with general configuration information like the gateway FQDN and routes.
- Open the extracted folder and double-click VpnClientSetupAmd64.exe (for 64-bit Windows) to install the VPN client configuration.
- If you see a SmartScreen popup, click More info, then Run anyway.
- Open Settings on your Windows machine, then select Network & Internet, then VPN.
- You should see a VPN connection named vnet-hub. Select it and click Connect.
- If prompted to select a certificate, choose the client certificate you generated earlier (signed by your self-signed root cert) and click OK.
- If prompted about elevated privileges, click Continue.
Once connected, the VPN status shows Connected. Your machine is now assigned an IP from the 172.16.0.0/24 address pool.
- Verify your VPN-assigned IP by opening a Command Prompt and running:
ipconfig
Look for a PPP adapter or VPN adapter with an IP address in the 172.16.0.x range. This confirms your P2S tunnel is active.
VPN connection failing?
Make sure your client certificate is installed in the Current User\Personal\Certificates store. If it is missing, double-click the .pfx file to install it.
Also confirm that the VPN Gateway deployment completed successfully in the portal (Status should show Succeeded). Gateway deployment takes 30-45 minutes.
Why can't I connect to my P2S VPN? The connection shows an error about certificates or the remote server not responding.
Verify connectivity to vm-web-01 through the firewall
With the VPN active, your machine can reach private IPs inside the hub VNet. Because VNet peering with gateway transit is enabled, you can also reach spoke VNets. Traffic from the VPN client to spoke subnets traverses Azure Firewall thanks to the UDRs you configured.
- Find the private IP of vm-web-01 by navigating to the VM in the Azure Portal and noting the Private IP address under Networking (it will be in the 10.1.2.0/24 range).
- From your local Command Prompt (while connected to VPN), test connectivity by running:
curl http://[[VM_PRIVATE_IP="private IP of vm-web-01 e.g. 10.1.2.4"]]
You should see the default NGINX welcome page HTML returned. This proves your VPN traffic reached vm-web-01 through the hub VNet and Azure Firewall.
Why does this work?
Your VPN client sends traffic to 10.1.2.x through the P2S tunnel into GatewaySubnet. The hub routes this to fw-hub via the UDR on snet-workload. The NSG on snet-workload allows SSH from 172.16.0.0/24 (your VPN pool) and HTTP/HTTPS from the AppGw subnet. Because you are connecting over HTTP from the VPN pool, the firewall must have a default allow rule or no deny rule for this traffic to pass through (you will configure explicit rules in the Secret Mission).
Connection timing out?
If curl times out, Azure Firewall Basic has no rules configured yet, which means it denies all traffic by default. You can temporarily add a network rule to allow traffic from 172.16.0.0/24 to 10.1.2.0/24 on port 80 in your firewall to test connectivity. The Secret Mission covers this configuration in full.
Traffic from my VPN client to vm-web-01 times out. How do I add a temporary firewall rule to allow it?
Verify Private Endpoint DNS resolution and firewall-routed traffic
The final validation confirms two things: your Private Endpoint DNS integration works correctly, and internet-bound traffic from vm-web-01 routes through Azure Firewall as intended.
- Connect to vm-web-01 via SSH from your VPN session (or use Azure Bastion if configured). From your local Command Prompt:
ssh [[VM_SSH_USER="your VM admin username"]]@[[VM_PRIVATE_IP="private IP of vm-web-01 e.g. 10.1.2.4"]]
- Once connected to the VM, verify that the storage account DNS name resolves to the Private Endpoint IP by running:
nslookup staz700data.blob.core.windows.net
You should see the DNS response resolve to a private IP in the 10.2.2.0/24 range (the snet-pe subnet where pe-storage lives). This confirms the Private DNS Zone integration is working correctly.
What does this DNS resolution prove?
Without Private DNS Zone integration, staz700data.blob.core.windows.net would resolve to a public IP. Your Private DNS Zone privatelink.blob.core.windows.net overrides this with an A record pointing to the Private Endpoint's private IP. Traffic to the storage account stays entirely within the Azure backbone network.
- Next, confirm internet-bound traffic routes through Azure Firewall. Enable diagnostic logging on the firewall to verify:
- In the Azure Portal, navigate to fw-hub.
- In the left menu under Monitoring, select Diagnostic settings.
- Click + Add diagnostic setting.
- Name it fw-hub-logs.
- Under Logs, check Azure Firewall Network Rule and Azure Firewall Application Rule.
- Under Destination details, check Send to Log Analytics workspace and create or select a workspace in East US.
- Click Save.
Why enable diagnostic settings?
Azure Firewall automatically generates resource logs, but they must be routed to a destination (like Log Analytics) before you can query them. Once enabled, every network and application rule hit is logged with source IP, destination IP, port, protocol, and action taken.
- Return to your SSH session on vm-web-01 and generate some outbound traffic by running:
curl -I https://www.microsoft.com
This request is routed through Azure Firewall because of the UDR on snet-workload (default route 0.0.0.0/0 pointing to the firewall's private IP).
- Wait 5-10 minutes for logs to appear, then verify in the Azure Portal. Navigate to fw-hub and select Logs under Monitoring.
- Run this query to check for firewall log entries:
AzureDiagnostics | where Category == "AzureFirewallNetworkRule" or Category == "AzureFirewallApplicationRule" | take 10
If you see log entries showing traffic from vm-web-01's IP as the source, this confirms internet-bound traffic is routing through Azure Firewall as designed.
Logs can take up to 15 minutes
Firewall diagnostic logs can take 5-30 minutes to appear in Log Analytics after the diagnostic setting is first created. If you see no results, wait a few more minutes and run the query again.
No log entries appearing?
Confirm that your diagnostic setting has Azure Firewall Network Rule and Azure Firewall Application Rule categories checked. Also verify that the Log Analytics workspace is in the same region (East US) as your firewall.
My Azure Firewall logs are not appearing in Log Analytics. What should I check?
Your enterprise hub-spoke network is fully validated. Every component is working together: VPN clients connect through the hub, traffic is inspected by Azure Firewall, spoke workloads communicate securely, and PaaS services are accessed privately through endpoints. Next, take on the Secret Mission to configure explicit firewall rules for spoke-to-spoke communication and controlled outbound access.
Secret mission
Configure Azure Firewall Rules for Spoke-to-Spoke and Controlled Outbound
Your hub-spoke network is deployed and validated, but the firewall has no rules yet. Traffic passes through it, but nothing is being inspected or filtered. In this secret mission, configure Azure Firewall with Network Rules for east-west spoke-to-spoke communication, Application Rules to restrict outbound internet access, and a DNAT rule to simulate external ingress through the firewall.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project uses multiple Azure resources that incur hourly charges while deployed.
Cost warning
Azure Firewall Basic costs $0.395/hr, Application Gateway WAF v2 costs $0.443/hr, and VPN Gateway VpnGw1 costs $0.19/hr. Combined, your paid resources cost approximately $1.03/hr while deployed.
If you have a new Azure account, the VPN Gateway may be covered under the free tier (750 hours/month for 12 months). The VM (B1s) is also free-tier eligible. Delete the resource group as soon as you finish to protect your credits.
Resources you used:
- Resource group rg-az700-enterprise (contains all Azure resources below).
- Azure Firewall Basic (fw-hub) with public IP pip-fw-hub.
- VPN Gateway (vpngw-hub) with Point-to-Site configuration.
- Application Gateway WAF v2 (appgw-web).
- Virtual machine (vm-web-01).
- Storage account (staz700data) with Private Endpoint.
- Three VNets (vnet-hub, vnet-spoke-web, vnet-spoke-data), NSGs, route table, and Private DNS Zone.
- Local: P2S VPN connection profile and self-signed certificates in your Windows certificate store.
Keep everything running
No action needed. Choose this if you are still actively testing connectivity or firewall rules and plan to continue within the next hour or two.
- All resources remain deployed and accessible.
- Your P2S VPN connection stays configured on your machine.
- Costs continue to accumulate at approximately $1.03/hr for the Firewall, Application Gateway, and VPN Gateway combined.
Pause - I'll come back to this later
Shut down the VM to save compute costs. The Firewall, VPN Gateway, and Application Gateway continue to incur charges while deployed because they cannot be paused without deletion.
- In the Azure Portal, navigate to vm-web-01.
- Click Stop to deallocate the VM.
- Confirm that the status changes to Stopped (deallocated).
- Disconnect your P2S VPN connection from Windows Settings.
What still costs money while paused
Azure Firewall Basic ($0.395/hr), Application Gateway WAF v2 ($0.443/hr), and VPN Gateway ($0.19/hr) all charge per deployment hour regardless of traffic. If you plan to be away for more than a few hours, deleting everything is the safer option for your credits.
Delete - I don't want to use this again
Remove all project resources and start fresh. This is the recommended option to protect your $100 student credits. You can always redeploy using this guide.
Delete all Azure resources by deleting the resource group:
- In the Azure Portal, search for Resource groups in the top search bar.
- Click rg-az700-enterprise.
- Click Delete resource group.
- Type rg-az700-enterprise to confirm.
- Click Delete.
Deletion takes a few minutes
Azure deletes resources in dependency order. The VPN Gateway and Firewall typically take the longest to deprovision. Wait for the notification confirming the resource group has been deleted.
Remove the VPN connection from your Windows machine:
- Open Settings on your Windows machine.
- Go to Network & Internet, then VPN.
- Find your Azure VPN connection, click it, and select Remove.
Remove the self-signed certificates from your certificate store:
- Open PowerShell and list your P2S certificates by running:
Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object { $_.Subject -like "*P2S*" }
- For each certificate shown, remove it using its thumbprint:
# Replace the thumbprint below with the one from Get-ChildItem output
Get-ChildItem -Path Cert:\CurrentUser\My\YOUR_THUMBPRINT | Remove-Item -DeleteKey
- Repeat for the root certificate in the Root store if you placed it there.
- Delete any downloaded VPN client profile files from your Downloads folder.
Nice Work!
Nice Work!
You just built an enterprise-grade hub-spoke network in Azure from scratch. This single deployment covers all five AZ-700 exam domains in a cohesive, production-representative architecture.
You've learned how to:
- Design and deploy a hub-spoke topology with VNet peering, Azure Firewall Basic, and User-Defined Routes to enforce centralized traffic inspection.
- Establish hybrid connectivity through a Point-to-Site VPN with self-signed certificate authentication and deploy Application Gateway WAF v2 for Layer 7 application delivery.
- Secure PaaS access using Private Endpoints with Private DNS Zone integration, ensuring storage traffic never traverses the public internet.
- Configured Azure Firewall rules for spoke-to-spoke communication and controlled outbound access.
Ready to quiz yourself?