Creating and Customizing PFSense VMs in Azure

This comprehensive guide demonstrates how to create custom Virtual Hard Disks (VHDs), configure them with the PFSense network appliance, and deploy them in Microsoft Azure. We'll cover the complete workflow from VM creation through image management and deployment.

Overview

In this tutorial, we'll:

  1. Create a custom VM and attach additional storage
  2. Configure network interfaces for PFSense
  3. Customize the operating system
  4. Upload the VHD to Azure Blob Storage
  5. Deploy VMs from custom images
  6. Manage VM deployment with PowerShell and Azure CLI

Part 1: Creating the VM

Step 1: Initial VM Creation

When creating a virtual machine in Azure:

  • Navigate to Virtual Machines and select "Create"
  • Configure basic settings (name, resource group, region)
  • Choose PFSense as the base image (or an appropriate variant)
  • Important: Specify the subnet using the private default network

Step 2: Storage and Network Configuration

Before starting the VM, we need to configure additional resources:

Add Additional Disk:

  • Create a new disk for extended storage
  • Attach it to the VM as additional storage
  • Use the proper disk attachment procedure before starting

Add Network Interface:

  • Add a second NIC (Network Interface Card) for external connectivity
  • This allows separate WAN/LAN interface configuration
  • Essential for PFSense router functionality

Part 2: Starting and Customizing the VM

Initial Boot and ISO Configuration

  1. Start the VM and connect via console
  2. Before reboot, eject the ISO image from the taskbar
  3. Ensure Boot Diagnostics is enabled for troubleshooting

Installing Required Software

Connect via PuTTY (SSH) and execute the following commands:

# Update packages
pkg upgrade -y

# Fix Perl symlink
ln -sf /usr/local/bin/perl /usr/bin/perl

# Install bash and git
pkg install bash git -y

# Search for Python packages
pkg search python
pkg search setuptools

# Install Python 2.7 (if using legacy setup)
pkg install python27-2.7.14_1 py27-setuptools-36.5.0

# Create Python symlink
ln -sf /usr/local/bin/python2.7 /usr/local/bin/python

# Install Azure WALinuxAgent
git clone https://github.com/Azure/WALinuxAgent.git
cd WALinuxAgent
python setup.py install

# Create WALinuxAgent symlink
ln -sf /usr/local/sbin/waagent /usr/local/bin/waagent

# Verify agent installation
waagent -version

Verify Azure Agent

Check that the WALinuxAgent is running properly:

# Check agent status
waagent -show-configuration

# The agent should be running and responsive

Network Configuration

Configure the network interfaces for proper DHCP operation:

  1. Set LAN interface to DHCP (IPv4)
  2. Set IPv6 to "None" for LAN
  3. Configure WAN interface settings as needed
  4. Apply configuration and reboot

Cleanup Before Capture

Before creating a VM image:

# Shutdown the VM
shutdown -p now

# Remove snapshots (via Azure Portal)
# This ensures a clean state for image capture

Part 3: Uploading VHD to Blob Storage

Azure PowerShell Setup

First, authenticate with Azure:

# Connect to Azure
Connect-AzureRmAccount

Create Storage Account

# Define variables
$ResourceGroupName = "RG_CORE_INFRA"
$pfresourcegroup = "RG_CORE_INFRA"
$StorageAccountName = "rgmouraddemov0"
$vnetname = "VNET_CORE_INFRA"
$location = "West Europe"

# Create storage account
New-AzureRmStorageAccount `
    -ResourceGroupName $ResourceGroupName `
    -Name $StorageAccountName `
    -Location $location `
    -SkuName "Standard_LRS" `
    -Kind "Storage"

Upload VHD to Blob Storage

# Define variables
$rgName = "RG_CORE_INFRA"
$urlOfUploadedImageVhd = "https://rgmouraddemov0.blob.core.windows.net/vhd/pfsenseappliancevhd.vhd"
$localVhdPath = "C:\pfsense_appliance_demo_vhd.vhd"

# Upload VHD (this may take considerable time)
Add-AzureRmVhd `
    -ResourceGroupName $rgName `
    -Destination $urlOfUploadedImageVhd `
    -LocalFilePath $localVhdPath

Note: VHD uploads can take 15-30+ minutes depending on size and connection speed. Be patient and keep the terminal active.

Part 4: Building VM from Blob

Deploy VM from VHD

This PowerShell script creates a VM with dual NICs and public IPs:

# Define variables
$ResourceGroupName = "RG_CORE_INFRA"
$pfresourcegroup = "RG_CORE_INFRA"
$StorageAccountName = "rgmouraddemov0"
$vnetname = "VNET_CORE_INFRA"
$location = "West Europe"

# Get network resources
$vnet = Get-AzureRmVirtualNetwork `
    -Name $vnetname `
    -ResourceGroupName $ResourceGroupName
$backendSubnet = Get-AzureRmVirtualNetworkSubnetConfig `
    -Name "default" `
    -VirtualNetwork $vnet

# Define VM parameters
$vmName = "ROUTER-PFSENSE"
$DiskNameOfVm = "pfsenseos"
$vmSize = "Standard_DS2_v2"

# Create public IPs
$pubipwan = New-AzureRmPublicIpAddress `
    -Name "PFPubIP-WAN" `
    -ResourceGroupName $pfresourcegroup `
    -Location $location `
    -AllocationMethod Dynamic

$pubiplanadmin = New-AzureRmPublicIpAddress `
    -Name "PFPubIP-LAN-ADMIN" `
    -ResourceGroupName $pfresourcegroup `
    -Location $location `
    -AllocationMethod Dynamic

# Create network interfaces
$nic1 = New-AzureRmNetworkInterface `
    -Name "PFIntNIC-LAN" `
    -ResourceGroupName $pfresourcegroup `
    -Location $location `
    -SubnetId $vnet.Subnets[0].Id `
    -PublicIpAddressId $pubiplanadmin.Id

$nic2 = New-AzureRmNetworkInterface `
    -Name "PFIntNIC-WAN" `
    -ResourceGroupName $pfresourcegroup `
    -Location $location `
    -SubnetId $vnet.Subnets[0].Id `
    -PublicIpAddressId $pubipwan.Id

# Create VM configuration
$vm = New-AzureRmVMConfig -VMName $vmName -VMSize $vmSize

# Attach OS disk from blob
$vm = Set-AzureRmVMOSDisk `
    -VM $vm `
    -Name $DiskNameOfVm `
    -VhdUri "https://rgmouraddemov0.blob.core.windows.net/vhd/pfsenseappliancevhdv1.vhd" `
    -CreateOption Attach `
    -Linux `
    -Caching ReadWrite

# Add network interfaces
$vm = Add-AzureRmVMNetworkInterface -VM $vm -Id $nic1.Id
$vm = Add-AzureRmVMNetworkInterface -VM $vm -Id $nic2.Id

# Configure boot diagnostics
Set-AzureRmVMBootDiagnostics `
    -VM $vm `
    -Enable `
    -ResourceGroupName $ResourceGroupName `
    -StorageAccountName $StorageAccountName

# Set primary NIC
$vm.NetworkProfile.NetworkInterfaces.Item(0).Primary = $true

# Create the VM
New-AzureRmVM `
    -ResourceGroupName $pfresourcegroup `
    -Location $location `
    -VM $vm

Verification

After deployment (wait 5 minutes for full initialization):

  1. Check the VM in the Azure Portal
  2. Verify both public IPs are assigned
  3. Access the PFSense web interface via the management public IP
  4. Configure routing and firewall rules as needed

Part 5: Creating and Managing Images

Create Image from VHD

# Define variables
$location = "west europe"
$imageName = "PFSENSE-IMAGE"
$ResourceGroupName = "RG_CORE_INFRA"
$osVhdUri = "https://rgmouraddemov0.blob.core.windows.net/vhd/pfsenseappliancevhd1.vhd"

# Create image configuration
$imageConfig = New-AzureRmImageConfig -Location $location

# Set OS disk properties
$imageConfig = Set-AzureRmImageOsDisk `
    -Image $imageConfig `
    -OsType Linux `
    -OsState Generalized `
    -BlobUri $osVhdUri

# Create the image
$image = New-AzureRmImage `
    -ImageName $imageName `
    -ResourceGroupName $ResourceGroupName `
    -Image $imageConfig

Part 6: Building VMs from Images

Method 1: Using Azure CLI (Recommended)

Azure CLI provides a more straightforward approach for multi-NIC deployments:

# Define credentials
AdminPassword="MySecurePassword123!"

# Create first network interface (DMZ/WAN)
az network nic create \
    --resource-group RG_CORE_INFRA \
    --name pf-sense-nva-1-nic-1 \
    --vnet-name vnet_core \
    --subnet subnet_DMZ \
    --ip-forwarding true \
    --private-ip-address 10.1.1.37

# Create second network interface (LAN/Core)
az network nic create \
    --resource-group RG_CORE_INFRA \
    --name pf-sense-nva-1-nic-2 \
    --vnet-name vnet_core \
    --subnet subnet_core \
    --ip-forwarding true \
    --private-ip-address 10.1.1.53

# Create VM from image with both NICs
az vm create \
    --resource-group RG_CORE_INFRA \
    --name pf-sense-nva-1 \
    --admin-username demo \
    --admin-password $AdminPassword \
    --nics pf-sense-nva-1-nic-2 pf-sense-nva-1-nic-1 \
    --image PFSENSE-IMAGE \
    --size Standard_DS2_v2 \
    --os-disk-size-gb 32

# Enable boot diagnostics
az vm boot-diagnostics enable \
    --resource-group RG_CORE_INFRA \
    --name pf-sense-nva-1 \
    --storage https://rgmouraddemov0.blob.core.windows.net

Method 2: Using PowerShell (Portal-Based Alternative)

While PowerShell is powerful, the Azure Portal is more user-friendly for creating VMs from images with multiple NICs:

  1. Navigate to the PFSENSE-IMAGE resource
  2. Click "Create VM"
  3. Configure settings interactively
  4. Select both NICs during network configuration
  5. Review and create

Best Practices

VHD Management

  • Naming Convention: Use descriptive names (e.g., pfsenseappliancevhdv1.vhd)
  • Version Control: Append version numbers to track iterations
  • Backup: Keep multiple versions before significant changes
  • Cleanup: Delete old VHDs to manage storage costs

VM Sizing

Scenario VM Size vCPUs RAM Cost
Development Standard_B2s 2 4GB Low
Small Production Standard_D2s_v3 2 8GB Medium
Production Standard_DS2_v2 2 7GB Medium-High
High-Performance Standard_D4s_v3 4 16GB High

Network Configuration

  • IP Forwarding: Enable on all NICs for routing functionality
  • Static IPs: Use private static IPs for consistent routing
  • NSG Rules: Configure Network Security Groups for traffic filtering
  • UDRs: Use User-Defined Routes for advanced routing

Monitoring and Troubleshooting

# Check disk usage
df -h

# Monitor network interfaces
netstat -i

# View system logs
tail -f /var/log/messages

# Check Azure agent logs
cat /var/log/waagent.log

Security Considerations

Before Deployment

  • [ ] Change default credentials
  • [ ] Disable unnecessary services
  • [ ] Configure firewall rules
  • [ ] Enable NTP synchronization
  • [ ] Set up backup retention policies

Ongoing Maintenance

  • [ ] Regularly update packages (pkg upgrade -y)
  • [ ] Monitor disk space and performance
  • [ ] Review access logs
  • [ ] Implement automated backups
  • [ ] Test disaster recovery procedures

Common Issues and Solutions

Issue: Boot Diagnostics Shows Errors

Solution: Enable boot diagnostics during VM creation. Use the serial console for troubleshooting:

# Check kernel logs
dmesg | tail -20

# Check system status
systemctl status waagent

Issue: Network Interface Not Available

Solution: Ensure IP forwarding is enabled and properly configured:

# Enable IP forwarding
sysctl -w net.ipv4.ip_forward=1

# Verify configuration
sysctl net.ipv4.ip_forward

Issue: Slow VHD Upload

Solution:

  • Use a high-speed connection
  • Consider uploading during off-peak hours
  • Split large VHDs if possible
  • Use Add-AzureRmVhd with parallel uploads for multiple files

Advanced Topics

Automating Deployments

Create reusable deployment scripts:

#!/bin/bash
# deploy-pfsense-vm.sh

RESOURCE_GROUP="RG_CORE_INFRA"
VM_NAME="pf-sense-nva-$1"
IMAGE_NAME="PFSENSE-IMAGE"
VNET_NAME="vnet_core"

# Create NICs
az network nic create \
    --resource-group $RESOURCE_GROUP \
    --name ${VM_NAME}-nic-1 \
    --vnet-name $VNET_NAME \
    --subnet subnet_DMZ \
    --ip-forwarding true

az network nic create \
    --resource-group $RESOURCE_GROUP \
    --name ${VM_NAME}-nic-2 \
    --vnet-name $VNET_NAME \
    --subnet subnet_core \
    --ip-forwarding true

# Create VM
az vm create \
    --resource-group $RESOURCE_GROUP \
    --name $VM_NAME \
    --nics ${VM_NAME}-nic-2 ${VM_NAME}-nic-1 \
    --image $IMAGE_NAME

Usage:

./deploy-pfsense-vm.sh 1  # Creates pf-sense-nva-1
./deploy-pfsense-vm.sh 2  # Creates pf-sense-nva-2

Custom Image Versions

Maintain multiple image versions:

# List all images
az image list --resource-group RG_CORE_INFRA

# Delete old image
az image delete \
    --resource-group RG_CORE_INFRA \
    --name PFSENSE-IMAGE-v1

# Create tagged versions
az image create \
    --name PFSENSE-IMAGE-2024-05 \
    --resource-group RG_CORE_INFRA \
    ...

Related Topics

  • Hub-and-Spoke Architecture: Use PFSense VMs as network hubs for complex topologies
  • User-Defined Routes (UDR): Route traffic through PFSense appliances
  • Network Security Groups (NSG): Complement PFSense rules with NSG policies
  • Azure Load Balancer: Distribute traffic across multiple PFSense instances

Conclusion

This guide provides a complete workflow for deploying custom-configured PFSense network appliances in Azure. The flexibility of using custom VHDs and images enables reproducible, scalable deployments for complex networking scenarios.

Key Takeaways:

  • Custom VHDs provide full control over OS configuration
  • Azure images enable rapid deployment of pre-configured systems
  • PowerShell and Azure CLI offer different levels of control and ease-of-use
  • Proper network configuration is essential for PFSense functionality
  • Automation scripts reduce deployment errors and time

Reference: Based on guidance from e-apostolidis.gr/microsoft/azure/custom-pfsense-on-azurerm-a-complete-guide/


PFSense provides enterprise-grade routing and firewalling capabilities. Combined with Azure's infrastructure flexibility, it creates powerful, customizable network topologies for complex cloud architectures.