How to Deploy the Sophos XG Firewall on Microsoft Azure using Powershell (Imperative method)

This is a repost from here

Why use this method when you can deploy through the Azure Portal?

The deployment of the Sophos XG firewall appliance through the new Azure Portal only allows up to two network interface cards. If you will like to have more than two networks connecting to the XG firewall, you’ll need to do a programmatic deployment. A programmatic deployment can be done using one of two methods – Imperative or Declarative. This document covers the Imperative method (using Powershell). The Declarative method (using ARM templates and Powershell) will be covered in a separate document.

Look out for other documentations and write-ups about configuring PaaS protection, HA clusters deployment, use with Azure Load Balancers and auto-scaling, integration with other Azure cloud services. 

NOTE: Microsoft Azure charges apply; For more information, see the Microsoft Azure Pricing Calculator –

Before You Begin

1. Install the latest version of Azure PowerShell.

2. Open Azure Powershell and log into your Azure account

  • Login-AzureRmAccount.
  • Enter your email address and password

3. Purchase a Sophos XG Firewall license or get a trial license.

4. Enable the Sophos XG firewall on Azure for programmatic deployment. The deployment will fail if this is not done!

  • The easiest way to do this is to deploy the XG firewall once through the Azure portal and then delete the deployment afterwards.


If you just want the script that you can customize, you can download from

1. Create the resource group for the deployment

Every resource in Azure lives within a resource group. First, we’ll need to create the resource group that we’ll deploy the various resources into. There is a best practice of using separate resource groups for different resource type but that is subject to your architecture on Azure. Notice that we’ll need to specify the location that our resource group will live in – this is true of every resource that you deploy to Azure.

# This is the Azure region where this workload will be deployed to. You can use the above code to verify the location that the Sophos publisher is available

$location = 'West Europe'

# Every resource in Azure lives in a resource group. Specify what you'll like this to be called
$rgName = "<resource group name>" 

# Deploy the new resource group
New-AzureRmResourceGroup -Name $rgName -Location $location

2. Create the storage account and the storage container (to store the VHDs)

Every IaaS deployment in Azure (including the XG appliance) uses a virtual hard drive (vhd). These vhds must be stored in a container that lives in a storage account. Next, we’ll need to create this storage account and the container.

# The virtual hard drives will be stored in a storage account. Specify the name for the account. This must be between 3 and 24 characters in length and use numbers and lower-case letters only.
$storageAccountName = "xgazurestore"

# Specify the name of the storage container where the VHDs will be stored
$storageContainerName = "vhds" 

# Create the locally redundant new storage account
New-AzureRmStorageAccount -ResourceGroupName $rgName -Name $storageAccountName -Type Standard_LRS -Location $location

# Set the newly created storage account as the subscription default
Set-AzureRmCurrentStorageAccount -ResourceGroupName $rgName -Name $storageAccountName

# Create a new container that we'll use to store our VHDs
New-AzureStorageContainer -Name $storageContainerName -Permission Off

3. Create the virtual network and subnets

Next, we will create the virtual network that our nodes will be deployed to and the subnets that they will connect to. If your Azure environment will be connecting via VPN or ExpressRoute to another datacenter, ensure that you have selected an address prefix that does not overlap with your other networks. Please note that this is customizable. The number of subnets that you want and what you call them is up to you but you must ensure that you select a VM size that can support the number of NICs required for the number of subnets that you want. Refer here for information about the VM sizes –

# Specify the name of the virtual network
$vnetName = "xgAzurevNet"

# Specify the address space of the virtual network. If you intend to connect this to an On-Prem DC, avoid using a conflicting address space
$vnetAddressSpace = ""

# This is the subnet that the WAN interface card of the XG firewall will connect to. Only the XG firewall will connect to this subnet. It must be within the address space specified above
$wanSubnetPrefix = ""

# These are the LAN subnets that the XG firewall will be protecting. They must be within the address space that was specified above
$lan1SubnetPrefix = ""
$lan2SubnetPrefix = ""
$lan3SubnetPrefix = "" 

# Specify the subnet configurations
$xgWanSubnet = New-AzureRmVirtualNetworkSubnetConfig -Name "xgWanSubnet" -AddressPrefix $wanSubnetPrefix
$xgLan1Subnet = New-AzureRmVirtualNetworkSubnetConfig -Name "xgLan1Subnet" -AddressPrefix $lan1SubnetPrefix
$xgLan2Subnet = New-AzureRmVirtualNetworkSubnetConfig -Name "xgLan2Subnet" -AddressPrefix $lan2SubnetPrefix
$xgLan3Subnet = New-AzureRmVirtualNetworkSubnetConfig -Name "xgLan3Subnet" -AddressPrefix $lan3SubnetPrefix

# Create the new virtual network with the above subnet configurations
New-AzureRmVirtualNetwork -Name $vnetName -ResourceGroupName $rgName -Location $location -AddressPrefix $vnetAddressSpace -Subnet $xgWanSubnet, $xgLan1Subnet, $xgLan2Subnet, $xgLan3Subnet

4. Create the network interfaces and the Public IP.

# Get information about the subnet IDs that the NICs will connect to
$vnet = Get-AzureRmVirtualNetwork -Name $vnetName -ResourceGroupName $rgName
$xgWanSubnetID = (Get-AzureRmVirtualNetworkSubnetConfig -Name $xgWanSubnet.Name -VirtualNetwork $vnet).Id
$xgLan1SubnetID = (Get-AzureRmVirtualNetworkSubnetConfig -Name $xgLan1Subnet.Name -VirtualNetwork $vnet).Id
$xgLan2SubnetID = (Get-AzureRmVirtualNetworkSubnetConfig -Name $xgLan2Subnet.Name -VirtualNetwork $vnet).Id
$xgLan3SubnetID = (Get-AzureRmVirtualNetworkSubnetConfig -Name $xgLan3Subnet.Name -VirtualNetwork $vnet).Id

# Specify the name of the public IP
$xgWanNicPublicIPName = "xgWanNicPIP"

# Create the public IP resource that will be linked to the WAN interface of the XG firewall
$xgWanNicPublicIP = New-AzureRmPublicIpAddress -ResourceGroupName $rgName -Location $location -Name $xgWanNicPublicIPName -AllocationMethod Static -DomainNameLabel $xgVmName

# Create the XG WAN network interface and assign the above public IP to it
$xgWanNic = New-AzureRmNetworkInterface -ResourceGroupName $rgName -Location $location -Name $xgWanNicName -PublicIpAddressId $xgWanNicPublicIP.Id -SubnetId $xgWanSubnetID -EnableIPForwarding

# Specify the names of the network interface cards of the XG firewall
$xgWanNicName = "xgWanNic"
$xgLan1NicName = "xgLan1Nic"
$xgLan2NicName = "xgLan2Nic"
$xgLan3NicName = "xgLan3Nic" 

# Specify the static IPs for the LAN interfaces of the XG firewall
$xgPrivateIPLan1 = ""
$xgPrivateIPLan2 = ""
$xgPrivateIPLan3 = ""

# Create the LAN network interfaces and assign them to their subnets
$xgLanNic1 = New-AzureRmNetworkInterface -ResourceGroupName $rgName -Location $location -Name $xgLan1NicName -SubnetId $xgLan1SubnetID -PrivateIpAddress $xgPrivateIPLan1 -EnableIPForwarding
$xgLanNic2 = New-AzureRmNetworkInterface -ResourceGroupName $rgName -Location $location -Name $xgLan2NicName -SubnetId $xgLan2SubnetID -PrivateIpAddress $xgPrivateIPLan2 -EnableIPForwarding
$xgLanNic3 = New-AzureRmNetworkInterface -ResourceGroupName $rgName -Location $location -Name $xgLan3NicName -SubnetId $xgLan3SubnetID -PrivateIpAddress $xgPrivateIPLan3 -EnableIPForwarding

5. Create the XG firewall on Azure and add the NICs that were created earlier to it.

This is where we’ll specify the size of the XG firewall VM, the Sku that we want to deploy, the disk and VM configurations

# Specify the name of your Sophos XG firewall VM
$xgVmName = "lbazurexg1" 

# Specify the size of the Sophos XG firewall VM. Only sizes that support a minimum of 2 NICs are supported. Refer here - Any size that you select must be able to support the number of subnets that you want behind the XG firewall.
$xgVmSize = "Standard_D3"

# Specify the admin credentials for your firewall appliance. The username is just a placeholder. The webadmin username of the XG after deployment will still be "admin". Use a strong password.
$adminUsername = "azureadmin"
$adminPassword = "Pa$$w0rd123"

# Specify the SKU of the Sophos Firewall that you'll like to deploy. "BYOL" for Bring Your Own License or "PAYG" for Pay As You Go. This code was written during the preview so "PAYG" is not yet available (04/20/2016).
$xgSkuName = "byol" 

# Create the VM configuration
$xgVm = New-AzureRmVMConfig -VMName $xgVmName -VMSize $xgVmSize

# Create the credentials objects for the VM
$cred = New-Object pscredential $adminUsername, ($adminPassword | ConvertTo-SecureString -AsPlainText -Force)

# Set the operating system type to Linux and assign the credentials for the VM$
xgVm = Set-AzureRmVMOperatingSystem -VM $xgVm -Linux -ComputerName $xgVmName -Credential $cred

# Add the WAN NIC that was previous created to the VM:
$xgVm = Add-AzureRmVMNetworkInterface -VM $xgVm -Id $xgWanNic.Id -Primary

# Add the LAN NICs that were previously created to the VM:
$xgVm = Add-AzureRmVMNetworkInterface -VM $xgVm -Id $xgLanNic1.Id
$xgVm = Add-AzureRmVMNetworkInterface -VM $xgVm -Id $xgLanNic2.Id
$xgVm = Add-AzureRmVMNetworkInterface -VM $xgVm -Id $xgLanNic3.Id

# Set the Blob path for the OS and data disks and the Uri paths
$storageAcc = Get-AzureRmStorageAccount -ResourceGroupName $rgName -Name $storageAccountName$xgOsBlobPath = "vhds/lbazurexg1.vhd"
$xgDataBlobPath = "vhds/lbazurexg1data.vhd"$xgOsDiskUri = $storageAcc.PrimaryEndpoints.Blob.ToString() + $xgOsBlobPath$xgDataDiskUri = $storageAcc.PrimaryEndpoints.Blob.ToString() + $xgDataBlobPath

# Set the disk names, configurations and image configurations
$xgVmOsDiskName = $xgVmName + "_OsDisk"
$xgVmDataDiskName = $xgVmName + "_DataDisk"

$xgVm.Plan = @{'name'= $xgSkuName; 'publisher'= 'sophos'; 'product' = 'sophos-xg'}

$xgVm = Set-AzureRmVMSourceImage -VM $xgVm -PublisherName "sophos" -Offer "sophos-xg" -Skus $xgSkuName -Version "latest"
$xgVm = Set-AzureRmVMOSDisk -VM $xgVm -Name $xgVmOsDiskName -VhdUri $xgOsDiskUri -Caching ReadWrite -CreateOption FromImage
$xgVm = Add-AzureRmVMDataDisk -VM $xgVm -Name $xgVmDataDiskName -Caching ReadWrite -Lun 0 -VhdUri $xgDataDiskUri -DiskSizeInGB 100 -CreateOption FromImage

# Create the XG firewall VM
New-AzureRmVM -ResourceGroupName $rgName -Location $location -VM $xgVm

6. Create a custom Network Security Group (NSG) to allow all traffic from and to the XG WAN interface.

Please note that this has been set to allow all as this is a test environment. You can be more specific with which ports and sources that you want to allow. This is the equivalent of certain filtering rules being applied at your ISP side (it doesn’t mean access to your backend servers as the XG still by default not allow any traffic to the backend subnets)

# Create security rules to allow access to and from the Internet on any port
$allowAllTrafficToXgInboundRule = New-AzureRmNetworkSecurityRuleConfig -Name allow_all_to_xg_inbound -Description "Allow all inbound traffic to the XG firewall WAN interface" -Access Allow -Protocol * -Direction Inbound -Priority 100 -SourceAddressPrefix Internet -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange *
$allowAllTrafficFromXgOutboundRule = New-AzureRmNetworkSecurityRuleConfig -Name allow_all_to_xg_outbound -Description "Allow all outbound traffic from the XG firewall WAN interface" -Access Allow -Protocol * -Direction Outbound -Priority 100 -SourceAddressPrefix Internet -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange *

# Add the rules created above to a new NSG named XgFirewallFrontEndNSG
$nsg = New-AzureRmNetworkSecurityGroup -ResourceGroupName $rgName -Location $location -Name "XgFirewallFrontEndNSG" -SecurityRules $allowAllTrafficToXgInboundRule,$allowAllTrafficFromXgOutboundRule

# Associate the NSG created above to the FrontEnd (WAN) subnet of the XG firewall
Set-AzureRmVirtualNetworkSubnetConfig -VirtualNetwork $vnet -Name $xgWanSubnet.Name -AddressPrefix $wanSubnetPrefix -NetworkSecurityGroup $nsgSet-AzureRmVirtualNetwork -VirtualNetwork $vnet

7. Create user defined route tables (one for each subnet) to route all LAN to WAN traffic and LAN to LAN traffic to the XG firewall.

By default, all routing takes place at the Azure fabric. For traffic to flow to go via our XG firewall appliance instead of via the Azure fabric, we’ll need to modify the default behaviour using User Defined Route (UDR) tables.

# Specify the names of the user defined route (UDR) tables that will be applied to the LAN subnets
$lan1RouteTableName = "Lan1SubnetRouteTable"
$lan2RouteTableName = "Lan2SubnetRouteTable"
$lan3RouteTableName = "Lan3SubnetRouteTable" 

# Create the one new UDR table for each LAN subnet
$lan1RouteTable = New-AzureRmRouteTable -ResourceGroupName $rgName -Location $location -Name $lan1RouteTableName
$lan2RouteTable = New-AzureRmRouteTable -ResourceGroupName $rgName -Location $location -Name $lan2RouteTableName
$lan3RouteTable = New-AzureRmRouteTable -ResourceGroupName $rgName -Location $location -Name $lan3RouteTableName

# Create routes and add them to the UDR tables
Add-AzureRmRouteConfig -Name "route_all_to_xg_lan1" -AddressPrefix -RouteTable $lan1RouteTable -NextHopType VirtualAppliance -NextHopIpAddress $xgPrivateIPLan1 | `
    Set-AzureRmRouteTableAdd -AzureRmRouteConfig -Name "route_lan2_to_xg_lan1" -AddressPrefix $lan2SubnetPrefix -RouteTable $lan1RouteTable -NextHopType VirtualAppliance -NextHopIpAddress $xgPrivateIPLan1 | `
    Set-AzureRmRouteTableAdd -AzureRmRouteConfig -Name "route_lan3_to_xg_lan1" -AddressPrefix $lan3SubnetPrefix -RouteTable $lan1RouteTable -NextHopType VirtualAppliance -NextHopIpAddress $xgPrivateIPLan1 | `
    Set-AzureRmRouteTableAdd -AzureRmRouteConfig -Name "route_all_to_xg_lan2" -AddressPrefix -RouteTable $lan2RouteTable -NextHopType VirtualAppliance -NextHopIpAddress $xgPrivateIPLan2 | `
    Set-AzureRmRouteTableAdd -AzureRmRouteConfig -Name "route_lan1_to_xg_lan2" -AddressPrefix $lan1SubnetPrefix -RouteTable $lan2RouteTable -NextHopType VirtualAppliance -NextHopIpAddress $xgPrivateIPLan2 | `
    Set-AzureRmRouteTableAdd -AzureRmRouteConfig -Name "route_lan3_to_xg_lan2" -AddressPrefix $lan3SubnetPrefix -RouteTable $lan2RouteTable -NextHopType VirtualAppliance -NextHopIpAddress $xgPrivateIPLan2 | `
    Set-AzureRmRouteTableAdd -AzureRmRouteConfig -Name "route_all_to_xg_lan3" -AddressPrefix -RouteTable $lan3RouteTable -NextHopType VirtualAppliance -NextHopIpAddress $xgPrivateIPLan3 | `
    Set-AzureRmRouteTableAdd -AzureRmRouteConfig -Name "route_lan1_to_xg_lan3" -AddressPrefix $lan1SubnetPrefix -RouteTable $lan3RouteTable -NextHopType VirtualAppliance -NextHopIpAddress $xgPrivateIPLan3 | `
    Set-AzureRmRouteTableAdd -AzureRmRouteConfig -Name "route_lan2_to_xg_lan3" -AddressPrefix $lan2SubnetPrefix -RouteTable $lan3RouteTable -NextHopType VirtualAppliance -NextHopIpAddress $xgPrivateIPLan3 | `
# Associate the route table with the LAN subnets
$lan1SubnetConfig = Set-AzureRmVirtualNetworkSubnetConfig -VirtualNetwork $vnet -Name $xgLan1Subnet.Name -AddressPrefix $lan1SubnetPrefix -RouteTable $lan1RouteTable
$lan2SubnetConfig = Set-AzureRmVirtualNetworkSubnetConfig -VirtualNetwork $vnet -Name $xgLan2Subnet.Name -AddressPrefix $lan2SubnetPrefix -RouteTable $lan2RouteTable
$lan3SubnetConfig = Set-AzureRmVirtualNetworkSubnetConfig -VirtualNetwork $vnet -Name $xgLan3Subnet.Name -AddressPrefix $lan3SubnetPrefix -RouteTable $lan3RouteTableSet-AzureRmVirtualNetwork -VirtualNetwork $vnet

8. (OPTIONAL) Deploy one Windows server to each LAN subnet to test routing and traffic flow behaviour

This part is optional. The code block below will create one Windows Server 2016 VM into each LAN subnet for us to test LAN to WAN and LAN to LAN traffic flow and also if we want to test other awesome security features of the XG like Identity based filtering, Sophos heartbeat, WAF, IPS, e.t.c.

$srvNamePrefix = “lbazure”
$srvNumber = 1..3

foreach ($number in $srvNumber) {
   $vmName = $srvNamePrefix + $number + "vm"$nicName = $vmName + "LanNic"$lanName = "xgLan" + $number + "Subnet"
   $xgLanSubnetID = (Get-AzureRmVirtualNetworkSubnetConfig -Name $lanName -VirtualNetwork $vnet).Id
   $srvNic = New-AzureRmNetworkInterface -Name $nicName -ResourceGroupName $rgName -Location $location -SubnetId $xgLanSubnetID
   $srvVm = New-AzureRmVMConfig -VMName $vmName -VMSize $srvVmSize
   $srvVm = Set-AzureRmVMOperatingSystem -VM $srvVm -Windows -ComputerName $vmName -Credential $cred -ProvisionVMAgent -EnableAutoUpdate
   $srvBlobPath = "vhds/" + $vmName + ".vhd"
   $srvOsDiskUri = $storageAcc.PrimaryEndpoints.Blob.ToString() + $srvBlobPath
   $srvDiskName = $vmName

   # Define the image to use to provision the virtual machine.
   $srvVm = Set-AzureRmVMSourceImage -VM $srvVm -PublisherName MicrosoftWindowsServer -Offer WindowsServer -Skus Windows-Server-Technical-Preview -Version "latest"
   $srvVm = Add-AzureRmVMNetworkInterface -VM $srvVm -Id $srvNic.Id
   $srvVm = Set-AzureRmVMOSDisk -VM $srvVm -Name $srvDiskName -VhdUri $srvOsDiskUri -CreateOption fromImage
   New-AzureRmVM -ResourceGroupName $rgName -Location $location -VM $srvVm

9. Log into the XG firewall (https://publicIP:4444) and create firewall rules to allow traffic to and from the LAN. 

The public IP of the XG firewall that you just deployed can be obtained using the code below.

(Get-AzureRmPublicIpAddress -ResourceGroupName $rgName -Name $xgWanNicPublicIP).IpAddress