你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

快速入门:部署具有可用性区域的 Azure 防火墙 - Terraform

本快速入门使用 Terraform 在三个可用性区域中部署 Azure 防火墙。

使用 Terraform 可以定义、预览和部署云基础结构。 使用 Terraform 时,请使用 HCL 语法来创建配置文件。 利用 HCL 语法,可指定 Azure 这样的云提供程序和构成云基础结构的元素。 创建配置文件后,请创建一个执行计划,利用该计划,可在部署基础结构更改之前先预览这些更改。 验证了更改后,请应用该执行计划以部署基础结构。

此 Terraform 配置创建带防火墙的测试网络环境。 网络有一个虚拟网络 (VNet),其中包含三个子网:AzureFirewallSubnetsubnet-serversubnet-jumpsubnet-serversubnet-jump 子网均包含一个单个、双核 Windows Server 虚拟机。

防火墙在 AzureFirewallSubnet 子网中,并配置有一个应用程序规则集合,其中包含允许访问 www.microsoft.com 的单个规则。

用户定义的一个路由,它引导来自 subnet-server 子网的网络流量穿过应用了防火墙规则的防火墙。

有关 Azure 防火墙的详细信息,请参阅使用 Azure 门户部署和配置 Azure 防火墙

在本文中,学习如何:

先决条件

实现 Terraform 代码

注意

本文中的示例代码位于 Azure Terraform GitHub 存储库中。 你可以查看包含当前和以前 Terraform 版本的测试结果的日志文件。

有关更多示例,请参阅演示如何使用 Terraform 管理 Azure 资源的文章和示例代码

  1. 创建用于测试和运行示例 Terraform 代码的目录,并将其设为当前目录。

  2. 创建名为 providers.tf 的文件并插入下列代码:

    terraform {
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "~>3.0"
        }
        random = {
          source  = "hashicorp/random"
          version = "~>3.0"
        }
      }
    }
    
    provider "azurerm" {
      features {}
    }
    
  3. 创建名为 main.tf 的文件并插入下列代码:

    resource "random_pet" "rg_name" {
      prefix = var.resource_group_name_prefix
    }
    
    resource "random_string" "storage_account_name" {
      length  = 8
      lower   = true
      numeric = false
      special = false
      upper   = false
    }
    
    resource "random_password" "password" {
      length      = 20
      min_lower   = 1
      min_upper   = 1
      min_numeric = 1
      min_special = 1
      special     = true
    }
    
    resource "azurerm_resource_group" "rg" {
      name     = random_pet.rg_name.id
      location = var.resource_group_location
    }
    
    resource "azurerm_public_ip" "pip_azfw" {
      name                = "pip-azfw"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
      allocation_method   = "Static"
      sku                 = "Standard"
      zones               = ["1", "2", "3"]
    }
    
    resource "azurerm_storage_account" "sa" {
      name                     = random_string.storage_account_name.result
      resource_group_name      = azurerm_resource_group.rg.name
      location                 = azurerm_resource_group.rg.location
      account_tier             = "Standard"
      account_replication_type = "LRS"
      account_kind             = "StorageV2"
    }
    
    resource "azurerm_virtual_network" "azfw_vnet" {
      name                = "azfw-vnet"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
      address_space       = ["10.10.0.0/16"]
    }
    
    resource "azurerm_subnet" "azfw_subnet" {
      name                 = "AzureFirewallSubnet"
      resource_group_name  = azurerm_resource_group.rg.name
      virtual_network_name = azurerm_virtual_network.azfw_vnet.name
      address_prefixes     = ["10.10.0.0/26"]
    }
    
    resource "azurerm_subnet" "server_subnet" {
      name                 = "subnet-server"
      resource_group_name  = azurerm_resource_group.rg.name
      virtual_network_name = azurerm_virtual_network.azfw_vnet.name
      address_prefixes     = ["10.10.1.0/24"]
    }
    
    resource "azurerm_subnet" "jump_subnet" {
      name                 = "subnet-jump"
      resource_group_name  = azurerm_resource_group.rg.name
      virtual_network_name = azurerm_virtual_network.azfw_vnet.name
      address_prefixes     = ["10.10.2.0/24"]
    }
    
    resource "azurerm_public_ip" "vm_jump_pip" {
      name                = "pip-jump"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
      allocation_method   = "Static"
      sku                 = "Standard"
    }
    
    resource "azurerm_network_interface" "vm_server_nic" {
      name                = "nic-server"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
    
      ip_configuration {
        name                          = "ipconfig-workload"
        subnet_id                     = azurerm_subnet.server_subnet.id
        private_ip_address_allocation = "Dynamic"
      }
    }
    
    resource "azurerm_network_interface" "vm_jump_nic" {
      name                = "nic-jump"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
    
      ip_configuration {
        name                          = "ipconfig-jump"
        subnet_id                     = azurerm_subnet.jump_subnet.id
        private_ip_address_allocation = "Dynamic"
        public_ip_address_id          = azurerm_public_ip.vm_jump_pip.id
      }
    }
    
    resource "azurerm_network_security_group" "vm_server_nsg" {
      name                = "nsg-server"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
    }
    
    resource "azurerm_network_security_group" "vm_jump_nsg" {
      name                = "nsg-jump"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
      security_rule {
        name                       = "Allow-TCP"
        priority                   = 1000
        direction                  = "Inbound"
        access                     = "Allow"
        protocol                   = "Tcp"
        source_port_range          = "*"
        destination_port_range     = "3389"
        source_address_prefix      = "*"
        destination_address_prefix = "*"
      }
    }
    
    resource "azurerm_network_interface_security_group_association" "vm_server_nsg_association" {
      network_interface_id      = azurerm_network_interface.vm_server_nic.id
      network_security_group_id = azurerm_network_security_group.vm_server_nsg.id
    }
    
    resource "azurerm_network_interface_security_group_association" "vm_jump_nsg_association" {
      network_interface_id      = azurerm_network_interface.vm_jump_nic.id
      network_security_group_id = azurerm_network_security_group.vm_jump_nsg.id
    }
    
    resource "azurerm_windows_virtual_machine" "vm_server" {
      name                  = "server-vm"
      resource_group_name   = azurerm_resource_group.rg.name
      location              = azurerm_resource_group.rg.location
      computer_name         = "server"
      size                  = var.virtual_machine_size
      admin_username        = var.admin_username
      admin_password        = random_password.password.result
      network_interface_ids = [azurerm_network_interface.vm_server_nic.id]
      os_disk {
        caching              = "ReadWrite"
        storage_account_type = "Standard_LRS"
        disk_size_gb         = "128"
      }
      source_image_reference {
        publisher = "MicrosoftWindowsServer"
        offer     = "WindowsServer"
        sku       = "2019-Datacenter"
        version   = "latest"
      }
      boot_diagnostics {
        storage_account_uri = azurerm_storage_account.sa.primary_blob_endpoint
      }
    }
    
    resource "azurerm_windows_virtual_machine" "vm_jump" {
      name                  = "jump-vm"
      resource_group_name   = azurerm_resource_group.rg.name
      location              = azurerm_resource_group.rg.location
      computer_name         = "jumpbox"
      size                  = var.virtual_machine_size
      admin_username        = var.admin_username
      admin_password        = random_password.password.result
      network_interface_ids = [azurerm_network_interface.vm_jump_nic.id]
      os_disk {
        caching              = "ReadWrite"
        storage_account_type = "Standard_LRS"
        disk_size_gb         = "128"
      }
      source_image_reference {
        publisher = "MicrosoftWindowsServer"
        offer     = "WindowsServer"
        sku       = "2019-Datacenter"
        version   = "latest"
      }
      boot_diagnostics {
        storage_account_uri = azurerm_storage_account.sa.primary_blob_endpoint
      }
    }
    
    resource "azurerm_firewall_policy" "azfw_policy" {
      name                     = "azfw-policy"
      resource_group_name      = azurerm_resource_group.rg.name
      location                 = azurerm_resource_group.rg.location
      sku                      = var.firewall_sku_tier
      threat_intelligence_mode = "Alert"
    }
    
    resource "azurerm_firewall_policy_rule_collection_group" "prcg" {
      name               = "prcg"
      firewall_policy_id = azurerm_firewall_policy.azfw_policy.id
      priority           = 300
      application_rule_collection {
        name     = "appRc1"
        priority = 101
        action   = "Allow"
        rule {
          name = "appRule1"
          protocols {
            type = "Http"
            port = 80
          }
          protocols {
            type = "Https"
            port = 443
          }
          destination_fqdns = ["www.microsoft.com"]
          source_addresses  = ["10.10.1.0/24"]
        }
      }
      network_rule_collection {
        name     = "netRc1"
        priority = 200
        action   = "Allow"
        rule {
          name                  = "netRule1"
          protocols             = ["TCP"]
          source_addresses      = ["10.10.1.0/24"]
          destination_addresses = ["*"]
          destination_ports     = ["8000", "8999"]
        }
      }
    }
    
    resource "azurerm_firewall" "fw" {
      name                = "azfw"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
      sku_name            = "AZFW_VNet"
      sku_tier            = var.firewall_sku_tier
      zones               = ["1", "2", "3"]
      ip_configuration {
        name                 = "azfw-ipconfig"
        subnet_id            = azurerm_subnet.azfw_subnet.id
        public_ip_address_id = azurerm_public_ip.pip_azfw.id
      }
      firewall_policy_id = azurerm_firewall_policy.azfw_policy.id
    }
    
    resource "azurerm_route_table" "rt" {
      name                          = "rt-azfw-eus"
      location                      = azurerm_resource_group.rg.location
      resource_group_name           = azurerm_resource_group.rg.name
      disable_bgp_route_propagation = false
      route {
        name                   = "azfwDefaultRoute"
        address_prefix         = "0.0.0.0/0"
        next_hop_type          = "VirtualAppliance"
        next_hop_in_ip_address = azurerm_firewall.fw.ip_configuration[0].private_ip_address
      }
    }
    
    resource "azurerm_subnet_route_table_association" "jump_subnet_rt_association" {
      subnet_id      = azurerm_subnet.server_subnet.id
      route_table_id = azurerm_route_table.rt.id
    }
    
  4. 创建名为 variables.tf 的文件并插入下列代码:

    variable "resource_group_location" {
      type        = string
      description = "Location for all resources."
      default     = "eastus"
    }
    
    variable "resource_group_name_prefix" {
      type        = string
      description = "Prefix for the Resource Group Name that's combined with a random id so name is unique in your Azure subcription."
      default     = "rg"
    }
    
    variable "firewall_sku_tier" {
      type        = string
      description = "Firewall SKU."
      default     = "Premium" # Valid values are Standard and Premium
      validation {
        condition     = contains(["Standard", "Premium"], var.firewall_sku_tier)
        error_message = "The SKU must be one of the following: Standard, Premium"
      }
    }
    
    variable "virtual_machine_size" {
      type        = string
      description = "Size of the virtual machine."
      default     = "Standard_D2_v3"
    }
    
    variable "admin_username" {
      type        = string
      description = "Value of the admin username."
      default     = "azureuser"
    }
    
  5. 创建名为 outputs.tf 的文件并插入下列代码:

    output "resource_group_name" {
      value = azurerm_resource_group.rg.name
    }
    
    output "firewall_name" {
      value = azurerm_firewall.fw.name
    }
    

初始化 Terraform

运行 terraform init,将 Terraform 部署进行初始化。 此命令将下载管理 Azure 资源所需的 Azure 提供程序。

terraform init -upgrade

要点:

  • 参数 -upgrade 可将必要的提供程序插件升级到符合配置版本约束的最新版本。

创建 Terraform 执行计划

运行 terraform plan 以创建执行计划。

terraform plan -out main.tfplan

要点:

  • terraform plan 命令将创建一个执行计划,但不会执行它。 它会确定创建配置文件中指定的配置需要执行哪些操作。 此模式允许你在对实际资源进行任何更改之前验证执行计划是否符合预期。
  • 使用可选 -out 参数可以为计划指定输出文件。 使用 -out 参数可以确保所查看的计划与所应用的计划完全一致。

应用 Terraform 执行计划

运行 terraform apply,将执行计划应用到云基础结构。

terraform apply main.tfplan

要点:

  • 示例 terraform apply 命令假设你先前运行了 terraform plan -out main.tfplan
  • 如果为 -out 参数指定了不同的文件名,请在对 terraform apply 的调用中使用该相同文件名。
  • 如果未使用 -out 参数,请调用不带任何参数的 terraform apply

验证结果

  1. 获取 Azure 资源组名称。

    resource_group_name=$(terraform output -raw resource_group_name)
    
  2. 获取防火墙名称。

    firewall_name=$(terraform output -raw firewall_name)
    
  3. 运行 az network firewall showJMESPath 查询来显示防火墙的可用性区域。

    az network firewall show --name $firewall_name --resource-group $resource_group_name --query "{Zones:zones"}
    

清理资源

不再需要通过 Terraform 创建的资源时,请执行以下步骤:

  1. 运行 terraform plan 并指定 destroy 标志。

    terraform plan -destroy -out main.destroy.tfplan
    

    要点:

    • terraform plan 命令将创建一个执行计划,但不会执行它。 它会确定创建配置文件中指定的配置需要执行哪些操作。 此模式允许你在对实际资源进行任何更改之前验证执行计划是否符合预期。
    • 使用可选 -out 参数可以为计划指定输出文件。 使用 -out 参数可以确保所查看的计划与所应用的计划完全一致。
  2. 运行 terraform apply 以应用执行计划。

    terraform apply main.destroy.tfplan
    

Azure 上的 Terraform 故障排除

排查在 Azure 上使用 Terraform 时遇到的常见问题

后续步骤

接下来,可以监视 Azure 防火墙日志。