Skip to content

Commit 41e885e

Browse files
Merge pull request #311104 from asudbring/tsk544788-sfi-bastion
Update secure cloud network PowerShell tutorial with Bastion
2 parents bd404a3 + 0324711 commit 41e885e

1 file changed

Lines changed: 64 additions & 42 deletions

File tree

articles/firewall-manager/secure-cloud-network-powershell.md

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ services: firewall-manager
55
author: jomore
66
ms.topic: tutorial
77
ms.service: azure-firewall-manager
8-
ms.date: 10/22/2020
8+
ms.date: 01/29/2026
99
ms.author: duau
1010
ms.custom:
1111
- devx-track-azurepowershell
@@ -65,6 +65,8 @@ $Hub = New-AzVirtualHub -Name $HubName -ResourceGroupName $RG -VirtualWan $Vwan
6565
```azurepowershell-interactive
6666
# Create Virtual Network
6767
$Spoke1 = New-AzVirtualNetwork -Name "spoke1" -ResourceGroupName $RG -Location $Location -AddressPrefix "10.1.1.0/24"
68+
Add-AzVirtualNetworkSubnetConfig -Name "AzureBastionSubnet" -VirtualNetwork $Spoke1 -AddressPrefix "10.1.1.64/26"
69+
$Spoke1 | Set-AzVirtualNetwork
6870
$Spoke2 = New-AzVirtualNetwork -Name "spoke2" -ResourceGroupName $RG -Location $Location -AddressPrefix "10.1.2.0/24"
6971
# Connect Virtual Network to Virtual WAN
7072
$Spoke1Connection = New-AzVirtualHubVnetConnection -ResourceGroupName $RG -ParentResourceName $HubName -Name "spoke1" -RemoteVirtualNetwork $Spoke1 -EnableInternetSecurityFlag $True
@@ -179,53 +181,64 @@ Update-AzVHubRouteTable -ResourceGroupName <rgname> -ParentResourceName <hubname
179181
180182
## Test connectivity
181183
182-
Now that your secure hub is fully operational, you can test connectivity by deploying a virtual machine in each spoke virtual network connected to the hub:
184+
Now that your secure hub is fully operational, you can test connectivity by deploying a virtual machine in each spoke virtual network connected to the hub.
185+
186+
First, create SSH keys for authentication:
187+
188+
```azurepowershell-interactive
189+
# Generate SSH key pair for VM authentication
190+
ssh-keygen -t rsa -b 4096 -f ~/.ssh/vwan-lab-key -N ""
191+
$sshPublicKey = Get-Content ~/.ssh/vwan-lab-key.pub
192+
```
193+
194+
Now create the virtual machines without public IP addresses:
183195

184196
```azurepowershell-interactive
185197
# Create VMs in spokes for testing
186-
$VMLocalAdminUser = "lab-user"
187-
$VMLocalAdminSecurePassword = ConvertTo-SecureString -AsPlainText -Force
188-
$VMCredential = New-Object System.Management.Automation.PSCredential ($VMLocalAdminUser, $VMLocalAdminSecurePassword);
198+
$VMLocalAdminUser = "azureuser"
189199
$VMSize = "Standard_B2ms"
190200
# Spoke1
191201
$Spoke1 = Get-AzVirtualNetwork -ResourceGroupName $RG -Name "spoke1"
192202
Add-AzVirtualNetworkSubnetConfig -Name "vm" -VirtualNetwork $Spoke1 -AddressPrefix "10.1.1.0/26"
193203
$Spoke1 | Set-AzVirtualNetwork
194204
$VM1 = New-AzVM -Name "spoke1-vm" -ResourceGroupName $RG -Location $Location `
195-
-Image "UbuntuLTS" -credential $VMCredential `
196-
-VirtualNetworkName "spoke1" -SubnetName "vm" -PublicIpAddressName "spoke1-pip"
205+
-Image "Ubuntu2204" -Size $VMSize `
206+
-VirtualNetworkName "spoke1" -SubnetName "vm" `
207+
-PublicIpAddressName "" -OpenPorts 22,80 `
208+
-GenerateSshKey -SshKeyName "spoke1-ssh-key"
197209
$NIC1 = Get-AzNetworkInterface -ResourceId $($VM1.NetworkProfile.NetworkInterfaces[0].Id)
198210
$Spoke1VMPrivateIP = $NIC1.IpConfigurations[0].PrivateIpAddress
199-
$Spoke1VMPIP = $(Get-AzPublicIpAddress -ResourceGroupName $RG -Name "spoke1-pip")
200211
# Spoke2
201212
$Spoke2 = Get-AzVirtualNetwork -ResourceGroupName $RG -Name "spoke2"
202213
Add-AzVirtualNetworkSubnetConfig -Name "vm" -VirtualNetwork $Spoke2 -AddressPrefix "10.1.2.0/26"
203214
$Spoke2 | Set-AzVirtualNetwork
204215
$VM2 = New-AzVM -Name "spoke2-vm" -ResourceGroupName $RG -Location $Location `
205-
-Image "UbuntuLTS" -credential $VMCredential `
206-
-VirtualNetworkName "spoke2" -SubnetName "vm" -PublicIpAddressName "spoke2-pip"
216+
-Image "Ubuntu2204" -Size $VMSize `
217+
-VirtualNetworkName "spoke2" -SubnetName "vm" `
218+
-PublicIpAddressName "" -OpenPorts 22,80 `
219+
-GenerateSshKey -SshKeyName "spoke2-ssh-key"
207220
$NIC2 = Get-AzNetworkInterface -ResourceId $($VM2.NetworkProfile.NetworkInterfaces[0].Id)
208221
$Spoke2VMPrivateIP = $NIC2.IpConfigurations[0].PrivateIpAddress
209-
$Spoke2VMPIP = $(Get-AzPublicIpAddress -ResourceGroupName $RG -Name "spoke2-pip")
210222
```
211223

212-
By default, the firewall policy blocks all traffic. To allow access to your test virtual machines, you must configure DNAT (Destination Network Address Translation) rules. These rules will enable you to connect to the VMs through the Azure Firewall's public IP address:
224+
### Deploy Azure Bastion
225+
226+
Deploy Azure Bastion in the Spoke-01 virtual network to securely connect to the virtual machines without requiring public IP addresses or DNAT rules.
213227

214228
```azurepowershell-interactive
215-
# Adding DNAT rules for virtual machines in the spokes
216-
$AzFWPublicAddress = $AzFW.HubIPAddresses.PublicIPs.Addresses[0].Address
217-
$NATRuleSpoke1 = New-AzFirewallPolicyNatRule -Name "Spoke1SSH" -Protocol "TCP" `
218-
-SourceAddress "*" -DestinationAddress $AzFWPublicAddress -DestinationPort 10001 `
219-
-TranslatedAddress $Spoke1VMPrivateIP -TranslatedPort 22
220-
$NATRuleSpoke2 = New-AzFirewallPolicyNatRule -Name "Spoke2SSH" -Protocol "TCP" `
221-
-SourceAddress "*" -DestinationAddress $AzFWPublicAddress -DestinationPort 10002 `
222-
-TranslatedAddress $Spoke2VMPrivateIP -TranslatedPort 22
223-
$NATCollection = New-AzFirewallPolicyNatRuleCollection -Name "SSH" -Priority 100 `
224-
-Rule @($NATRuleSpoke1, $NATRuleSpoke2) -ActionType "Dnat"
225-
$NATGroup = New-AzFirewallPolicyRuleCollectionGroup -Name "NAT" -Priority 100 -RuleCollection $NATCollection -FirewallPolicyObject $FWPolicy
229+
# Deploy Azure Bastion for secure VM access
230+
$BastionPip = New-AzPublicIpAddress -ResourceGroupName $RG -Name "bastion-pip" `
231+
-Location $Location -AllocationMethod Static -Sku Standard
232+
$Spoke1 = Get-AzVirtualNetwork -ResourceGroupName $RG -Name "spoke1"
233+
$BastionSubnet = Get-AzVirtualNetworkSubnetConfig -Name "AzureBastionSubnet" -VirtualNetwork $Spoke1
234+
New-AzBastion -ResourceGroupName $RG -Name "spoke1-bastion" `
235+
-PublicIpAddress $BastionPip -VirtualNetwork $Spoke1 -Sku "Basic"
226236
```
227237

228-
Next, configure example rules for your firewall policy. First, create a network rule to allow SSH traffic between the virtual networks. Then, add an application rule to permit Internet access only to the Fully Qualified Domain Name (FQDN) `ifconfig.co`, which returns the source IP address seen in the HTTP request:
238+
> [!NOTE]
239+
> Azure Bastion deployment can take approximately 10 minutes to complete.
240+
241+
By default, the firewall policy blocks all traffic. To allow access between spoke virtual machines and to the internet, you must configure firewall rules. First, create a network rule to allow SSH traffic between the virtual networks. Then, add an application rule to permit Internet access only to the Fully Qualified Domain Name (FQDN) `ifconfig.co`, which returns the source IP address seen in the HTTP request:
229242

230243
```azurepowershell-interactive
231244
# Add Network Rule
@@ -250,27 +263,40 @@ Get-AzEffectiveRouteTable -ResourceGroupName $RG -NetworkInterfaceName $NIC1.Nam
250263
Get-AzEffectiveRouteTable -ResourceGroupName $RG -NetworkInterfaceName $NIC2.Name | ft
251264
```
252265

253-
Generate traffic from one virtual machine to the other and verify that it is filtered by Azure Firewall. Use SSH to connect to the virtual machines—accept the SSH fingerprint and enter the password you set during VM creation. In this example, you will:
266+
Generate traffic from one virtual machine to the other and verify that it is filtered by Azure Firewall. Use Azure Bastion to connect to the virtual machines. In this example, you will:
254267

255-
- Send five ICMP echo requests (pings) from the VM in spoke1 to the VM in spoke2.
256-
- Attempt a TCP connection on port 22 using the `nc` (netcat) utility with the `-vz` flags, which checks connectivity without sending data.
268+
- Connect to spoke1-vm using Azure Bastion through the Azure portal
269+
- Send five ICMP echo requests (pings) from the VM in spoke1 to the VM in spoke2
270+
- Attempt a TCP connection on port 22 using the `nc` (netcat) utility with the `-vz` flags, which checks connectivity without sending data
257271

258272
You should observe that the ping requests fail (blocked by the firewall), while the TCP connection on port 22 succeeds, as allowed by the previously configured network rule.
259273

260-
```azurepowershell-interactive
261-
# Connect to one VM and ping the other. It should not work, because the firewall should drop the traffic, since no rule for ICMP is configured
262-
ssh $AzFWPublicAddress -p 10001 -l $VMLocalAdminUser "ping $Spoke2VMPrivateIP -c 5"
263-
# Connect to one VM and send a TCP request on port 22 to the other. It should work, because the firewall is configured to allow SSH traffic (port 22)
264-
ssh $AzFWPublicAddress -p 10001 -l $VMLocalAdminUser "nc -vz $Spoke2VMPrivateIP 22"
274+
To test connectivity:
275+
276+
1. In the Azure portal, navigate to the **spoke1-vm** virtual machine.
277+
2. Select **Connect** > **Connect via Bastion**.
278+
3. Provide the username **azureuser** and upload the private key file generated earlier.
279+
4. Select **Connect** to open an SSH session.
280+
5. In the SSH session, run the following commands:
281+
282+
```bash
283+
# Ping should fail (blocked by firewall)
284+
ping $Spoke2VMPrivateIP -c 5
285+
# SSH connectivity check should succeed (allowed by firewall)
286+
nc -vz $Spoke2VMPrivateIP 22
265287
```
266288

267-
You can also test Internet access through the firewall. HTTP requests using the `curl` utility to the allowed FQDN (`ifconfig.co`) should succeed, while requests to other destinations (such as `bing.com`) should be blocked by the firewall policy:
289+
Replace `$Spoke2VMPrivateIP` with the actual private IP address of spoke2-vm (displayed in the PowerShell output).
268290

269-
```azurepowershell-interactive
291+
You can also test Internet access through the firewall. HTTP requests using the `curl` utility to the allowed FQDN (`ifconfig.co`) should succeed, while requests to other destinations (such as `bing.com`) should be blocked by the firewall policy.
292+
293+
From the same SSH session on spoke1-vm:
294+
295+
```bash
270296
# This HTTP request should succeed, since it is allowed in an app rule in the AzFW, and return the public IP of the FW
271-
ssh $AzFWPublicAddress -p 10001 -l $VMLocalAdminUser "curl -s4 ifconfig.co"
297+
curl -s4 ifconfig.co
272298
# This HTTP request should fail, since the FQDN bing.com is not in any app rule in the firewall policy
273-
ssh $AzFWPublicAddress -p 10001 -l $VMLocalAdminUser "curl -s4 bing.com"
299+
curl -s4 bing.com
274300
```
275301

276302
To confirm that the firewall is dropping packets as expected, review the logs sent to Azure Monitor. Since Azure Firewall is configured to send diagnostic logs to Azure Monitor, you can use Kusto Query Language (KQL) to query and analyze the relevant log entries:
@@ -297,7 +323,6 @@ $(Invoke-AzOperationalInsightsQuery -Workspace $LogWS -Query $LogQuery).Results
297323

298324
In the previous command you should see different entries:
299325

300-
* Your SSH connection being DNAT'ed
301326
* Dropped ICMP packets between the VMs in the spokes (10.1.1.4 and 10.1.2.4)
302327
* Allowed SSH connections between the VMs in the spokes
303328

@@ -306,17 +331,14 @@ Here a sample output produced by the command above:
306331
```
307332
TimeGenerated Protocol SourceIP SourcePort TargetIP TargetPort Action NatDestination Resource
308333
------------- -------- -------- ---------- -------- ---------- ------ -------------- --------
309-
2020-10-04T20:53:02.41Z TCP 109.125.122.99 62281 51.105.224.44 10001 DNAT'ed 10.1.1.4:22 AZFW1
310334
2020-10-04T20:53:07.045Z TCP 10.1.1.4 35932 10.1.2.4 22 Allow N/A AZFW1
311-
2020-10-04T20:53:50.119Z TCP 109.125.122.99 62293 51.105.224.44 10001 DNAT'ed 10.1.2.4:22 AZFW1
312-
2020-10-04T20:52:47.475Z TCP 109.125.122.99 62273 51.105.224.44 10001 DNAT'ed 10.1.2.4:22 AZFW1
313-
2020-10-04T20:51:04.682Z TCP 109.125.122.99 62200 51.105.224.44 10001 DNAT'ed 10.1.2.4:22 AZFW1
335+
2020-10-04T20:52:47.475Z TCP 10.1.1.4 53748 10.1.2.4 22 Allow N/A AZFW1
336+
2020-10-04T20:51:04.682Z ICMP Type=8 10.1.1.4 N/A 10.1.2.4 N/A Deny N/A AZFW1
314337
2020-10-04T20:51:17.031Z ICMP Type=8 10.1.1.4 N/A 10.1.2.4 N/A Deny N/A AZFW1
315338
2020-10-04T20:51:18.049Z ICMP Type=8 10.1.1.4 N/A 10.1.2.4 N/A Deny N/A AZFW1
316339
2020-10-04T20:51:19.075Z ICMP Type=8 10.1.1.4 N/A 10.1.2.4 N/A Deny N/A AZFW1
317340
2020-10-04T20:51:20.097Z ICMP Type=8 10.1.1.4 N/A 10.1.2.4 N/A Deny N/A AZFW1
318341
2020-10-04T20:51:21.121Z ICMP Type=8 10.1.1.4 N/A 10.1.2.4 N/A Deny N/A AZFW1
319-
2020-10-04T20:52:52.356Z TCP 10.1.1.4 53748 10.1.2.4 22 Allow N/A AZFW1
320342
```
321343

322344
If you want to see the logs for the application rules (describing allowed and denied HTTP connections) or change the way that the logs are displayed, you can try with other KQL queries. You can find some examples in [Azure Monitor logs for Azure Firewall](../firewall/firewall-workbook.md).

0 commit comments

Comments
 (0)