Managing Azure Firewall with Terraform

Learn how to set up and manage Azure Firewall using Terraform, including network rules, application rules, and threat intelligence

Managing Azure Firewall with Terraform

Azure Firewall is a managed, cloud-based network security service that protects your Azure Virtual Network resources. This guide demonstrates how to set up and manage Azure Firewall using Terraform.

Video Tutorial

Learn more about managing Azure Firewall with Terraform in this comprehensive video tutorial:

Prerequisites

  • Azure CLI configured with appropriate permissions
  • Terraform installed (version 1.0.0 or later)
  • Resource group created
  • Understanding of networking and security concepts

Project Structure

terraform-azure-firewall/
├── main.tf
├── variables.tf
├── outputs.tf
├── modules/
│   └── firewall/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── policies/
    └── rules.json

Firewall Configuration

Create modules/firewall/main.tf:

# Firewall Policy
resource "azurerm_firewall_policy" "main" {
  name                = "${var.project_name}-policy"
  resource_group_name = var.resource_group_name
  location            = var.location
  sku                 = "Premium"

  dns {
    proxy_enabled = true
    servers       = ["168.63.129.16"]
  }

  threat_intelligence_mode = "Alert"
  
  threat_intelligence_allowlist {
    ip_addresses = ["192.168.1.0/24"]
    fqdns        = ["*.microsoft.com"]
  }

  insights {
    enabled                            = true
    default_log_analytics_workspace_id = var.log_analytics_workspace_id
    retention_in_days                  = 30
  }

  intrusion_detection {
    mode = "Alert"
    signature_overrides {
      id    = "123456789"
      state = "Alert"
    }
    traffic_bypass {
      name                  = "bypass-rule"
      protocol             = "TCP"
      description          = "Test description"
      destination_ports    = ["80", "443"]
      destination_addresses = ["192.168.1.1"]
      source_addresses     = ["192.168.1.2"]
    }
  }

  identity {
    type = "SystemAssigned"
  }

  tags = var.tags
}

# Firewall
resource "azurerm_firewall" "main" {
  name                = "${var.project_name}-fw"
  location            = var.location
  resource_group_name = var.resource_group_name
  sku_name            = "AZFW_VNet"
  sku_tier            = "Premium"
  firewall_policy_id  = azurerm_firewall_policy.main.id
  dns_servers         = ["168.63.129.16"]
  zones               = ["1", "2", "3"]
  threat_intel_mode   = "Alert"

  ip_configuration {
    name                 = "configuration"
    subnet_id            = azurerm_subnet.firewall.id
    public_ip_address_id = azurerm_public_ip.firewall.id
  }

  management_ip_configuration {
    name                 = "management"
    subnet_id            = azurerm_subnet.management.id
    public_ip_address_id = azurerm_public_ip.management.id
  }

  tags = var.tags
}

# Network Rules
resource "azurerm_firewall_policy_rule_collection_group" "network" {
  name               = "network-rules"
  firewall_policy_id = azurerm_firewall_policy.main.id
  priority           = 100

  network_rule_collection {
    name     = "network-rules"
    priority = 100
    action   = "Allow"

    rule {
      name                  = "allow-rdp"
      protocols            = ["TCP"]
      source_addresses     = ["10.0.0.0/16"]
      destination_addresses = ["192.168.1.0/24"]
      destination_ports    = ["3389"]
    }

    rule {
      name                  = "allow-ssh"
      protocols            = ["TCP"]
      source_addresses     = ["10.0.0.0/16"]
      destination_addresses = ["192.168.1.0/24"]
      destination_ports    = ["22"]
    }
  }
}

# Application Rules
resource "azurerm_firewall_policy_rule_collection_group" "application" {
  name               = "application-rules"
  firewall_policy_id = azurerm_firewall_policy.main.id
  priority           = 200

  application_rule_collection {
    name     = "app-rules"
    priority = 100
    action   = "Allow"

    rule {
      name = "allow-microsoft"
      protocols {
        type = "Http"
        port = 80
      }
      protocols {
        type = "Https"
        port = 443
      }
      source_addresses  = ["10.0.0.0/16"]
      destination_fqdns = ["*.microsoft.com"]
    }

    rule {
      name = "allow-updates"
      protocols {
        type = "Https"
        port = 443
      }
      source_addresses  = ["10.0.0.0/16"]
      destination_fqdns = ["*.ubuntu.com"]
      terminate_tls     = true
      web_categories    = ["ComputersAndTechnology"]
    }
  }
}

# NAT Rules
resource "azurerm_firewall_policy_rule_collection_group" "nat" {
  name               = "nat-rules"
  firewall_policy_id = azurerm_firewall_policy.main.id
  priority           = 300

  nat_rule_collection {
    name     = "nat-rules"
    priority = 100
    action   = "Dnat"

    rule {
      name                = "rdp-nat"
      protocols           = ["TCP"]
      source_addresses    = ["*"]
      destination_address = azurerm_public_ip.firewall.ip_address
      destination_ports   = ["3389"]
      translated_address  = "10.0.0.4"
      translated_port     = "3389"
    }
  }
}

# IDPS Policy
resource "azurerm_firewall_policy_rule_collection_group" "idps" {
  name               = "idps-rules"
  firewall_policy_id = azurerm_firewall_policy.main.id
  priority           = 400

  network_rule_collection {
    name     = "idps-network"
    priority = 100
    action   = "Allow"
    rule {
      name                  = "allow-http"
      protocols            = ["TCP"]
      source_addresses     = ["10.0.0.0/16"]
      destination_addresses = ["*"]
      destination_ports    = ["80", "443"]
    }
  }
}

Network Configuration

  1. Virtual Network Setup
resource "azurerm_virtual_network" "main" {
  name                = "${var.project_name}-vnet"
  address_space       = ["10.0.0.0/16"]
  location            = var.location
  resource_group_name = var.resource_group_name

  subnet {
    name           = "AzureFirewallSubnet"
    address_prefix = "10.0.1.0/24"
  }

  subnet {
    name           = "AzureFirewallManagementSubnet"
    address_prefix = "10.0.2.0/24"
  }

  tags = var.tags
}

resource "azurerm_public_ip" "firewall" {
  name                = "${var.project_name}-fw-pip"
  location            = var.location
  resource_group_name = var.resource_group_name
  allocation_method   = "Static"
  sku                 = "Standard"
  zones               = ["1", "2", "3"]

  tags = var.tags
}

resource "azurerm_public_ip" "management" {
  name                = "${var.project_name}-mgmt-pip"
  location            = var.location
  resource_group_name = var.resource_group_name
  allocation_method   = "Static"
  sku                 = "Standard"
  zones               = ["1", "2", "3"]

  tags = var.tags
}

Route Configuration

  1. Route Table
resource "azurerm_route_table" "main" {
  name                = "${var.project_name}-rt"
  location            = var.location
  resource_group_name = var.resource_group_name

  route {
    name                   = "ToInternet"
    address_prefix         = "0.0.0.0/0"
    next_hop_type         = "VirtualAppliance"
    next_hop_in_ip_address = azurerm_firewall.main.ip_configuration[0].private_ip_address
  }

  tags = var.tags
}

resource "azurerm_subnet_route_table_association" "main" {
  subnet_id      = azurerm_subnet.main.id
  route_table_id = azurerm_route_table.main.id
}

Monitoring Configuration

  1. Diagnostic Settings
resource "azurerm_monitor_diagnostic_setting" "firewall" {
  name                       = "${var.project_name}-diag"
  target_resource_id        = azurerm_firewall.main.id
  log_analytics_workspace_id = var.log_analytics_workspace_id

  log {
    category = "AzureFirewallApplicationRule"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }

  log {
    category = "AzureFirewallNetworkRule"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }

  metric {
    category = "AllMetrics"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }
}

resource "azurerm_monitor_metric_alert" "firewall" {
  name                = "${var.project_name}-health-alert"
  resource_group_name = var.resource_group_name
  scopes              = [azurerm_firewall.main.id]
  description         = "Alert when firewall health is degraded"

  criteria {
    metric_namespace = "Microsoft.Network/azureFirewalls"
    metric_name      = "FirewallHealth"
    aggregation      = "Average"
    operator         = "LessThan"
    threshold        = 100
  }

  action {
    action_group_id = var.action_group_id
  }
}

Best Practices

  1. Performance

    • Use Premium SKU
    • Enable zones
    • Configure DNS proxy
    • Optimize rules
  2. Security

    • Enable threat intelligence
    • Configure IDPS
    • Implement logging
    • Monitor traffic
  3. High Availability

    • Use zone redundancy
    • Configure failover
    • Monitor health
    • Implement backup
  4. Cost Optimization

    • Optimize rules
    • Use appropriate zones

Conclusion

You’ve learned how to set up and manage Azure Firewall using Terraform. This setup provides:

  • Network security
  • Application protection
  • Threat intelligence
  • Traffic monitoring

Remember to:

  • Monitor firewall health
  • Review security rules
  • Update policies
  • Maintain configurations