快速入门:使用 Terraform 在 Azure 中配置专用 DNS 区域

在本快速入门中,你将使用 Terraform 在 Azure 中创建专用 DNS 区域、网络接口、Windows 虚拟机、专用 DNS A 记录、网络安全组和网络安全规则。

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

  • 创建具有唯一名称的 Azure 资源组。
  • 建立具有指定名称和地址的虚拟网络。
  • 在创建的虚拟网络中设置一个子网。
  • 创建专用 DNS 区域。
  • 为虚拟机生成随机密码。
  • 创建两个网络接口。
  • 创建两个 Windows 虚拟机,并附加网络接口。
  • 创建一个专用 DNS A 记录。
  • 创建一个网络安全组,并创建一个允许 ICMP 流量的网络安全规则。
  • 输出虚拟机的名称和管理员凭据。

先决条件

实现 Terraform 代码

注释

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

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

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

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

# Resource Group
resource "random_pet" "rg_name" {
  separator = "-"
}

resource "azurerm_resource_group" "rg" {
  location = var.resource_group_location
  name     = "${var.resource_group_name_prefix}-${random_pet.rg_name.id}"
}

# Random String for unique naming
resource "random_string" "name" {
  length  = 8
  special = false
  upper   = false
  lower   = true
  numeric = false
}

# Virtual Network
resource "azurerm_virtual_network" "vnet" {
  name                = "vnet-${random_string.name.result}"
  address_space       = var.address_space
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

# Subnet
resource "azurerm_subnet" "subnet" {
  name                 = "subnet-${random_string.name.result}"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = var.address_prefixes
}

# Private DNS Zone
resource "azurerm_private_dns_zone" "dns_zone" {
  name                = var.private_dns_zone_name
  resource_group_name = azurerm_resource_group.rg.name
}

# Private DNS Zone Virtual Network Link
resource "azurerm_private_dns_zone_virtual_network_link" "dsn_vnet_link" {
  name                  = "dns-vnet-link-${random_string.name.result}"
  resource_group_name   = azurerm_resource_group.rg.name
  private_dns_zone_name = azurerm_private_dns_zone.dns_zone.name
  virtual_network_id    = azurerm_virtual_network.vnet.id
}

# Random Passwords for VMs
resource "random_password" "vm1_admin_password" {
  length  = 16
  special = true
}

resource "random_password" "vm2_admin_password" {
  length  = 16
  special = true
}

# Network Interfaces
resource "azurerm_network_interface" "nic1" {
  name                = "nic1-${random_string.name.result}"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.subnet.id
    private_ip_address_allocation = "Dynamic"
  }
}

resource "azurerm_network_interface" "nic2" {
  name                = "nic2-${random_string.name.result}"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.subnet.id
    private_ip_address_allocation = "Dynamic"
  }
}

# Windows Virtual Machines
resource "azurerm_windows_virtual_machine" "vm1" {
  name                = "vm1-${random_string.name.result}"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  size                = "Standard_F2"
  admin_username      = var.admin_username
  admin_password      = random_password.vm1_admin_password.result
  network_interface_ids = [
    azurerm_network_interface.nic1.id,
  ]

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2019-Datacenter"
    version   = "latest"
  }

  # Make idempotent
  vm_agent_platform_updates_enabled = true
}

resource "azurerm_windows_virtual_machine" "vm2" {
  name                = "vm2-${random_string.name.result}"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  size                = "Standard_F2"
  admin_username      = var.admin_username
  admin_password      = random_password.vm1_admin_password.result
  network_interface_ids = [
    azurerm_network_interface.nic2.id,
  ]

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2019-Datacenter"
    version   = "latest"
  }

  # Make idempotent
  vm_agent_platform_updates_enabled = true
}

# Private DNS A Record
resource "azurerm_private_dns_a_record" "pdar" {
  name                = "test"
  zone_name           = azurerm_private_dns_zone.dns_zone.name
  resource_group_name = azurerm_resource_group.rg.name
  ttl                 = 300
  records             = [azurerm_windows_virtual_machine.vm1.private_ip_address]
}

# Network Security Group
resource "azurerm_network_security_group" "nsg" {
  name                = "nsg-${random_string.name.result}"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

# Network Security Rule
resource "azurerm_network_security_rule" "nsr_icmp" {
  name                        = "Allow-ICMP"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Icmp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.rg.name
  network_security_group_name = azurerm_network_security_group.nsg.name
}
  1. 创建名为 outputs.tf 的文件并插入以下代码:
output "resource_group_name" {
  value = azurerm_resource_group.rg.name
}

output "windows_virtual_machine_1_name" {
  value = azurerm_windows_virtual_machine.vm1.name
}

output "windows_virtual_machine_2_name" {
  value = azurerm_windows_virtual_machine.vm2.name
}

output "windows_virtual_machine_1_password" {
  value     = azurerm_windows_virtual_machine.vm1.admin_password
  sensitive = true
}

output "windows_virtual_machine_2_password" {
  value     = azurerm_windows_virtual_machine.vm2.admin_password
  sensitive = true
}

output "windows_virtual_machine_1_admin_username" {
  value     = azurerm_windows_virtual_machine.vm1.admin_username
  sensitive = true
}

output "windows_virtual_machine_2_admin_username" {
  value     = azurerm_windows_virtual_machine.vm2.admin_username
  sensitive = true
}
  1. 创建名为 providers.tf 的文件并插入以下代码:
terraform {
  required_version = ">=1.0"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~>3.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "~>3.0"
    }
  }
}

provider "azurerm" {
  features {}
  environment = "china"
}
  1. 创建名为 variables.tf 的文件并插入以下代码:
variable "resource_group_location" {
  type        = string
  default     = "chinanorth3"
  description = "Location of the resource group."
}

variable "resource_group_name_prefix" {
  type        = string
  default     = "rg"
  description = "Prefix of the resource group name that's combined with a random ID so name is unique in your Azure subscription."
}

variable "address_space" {
  type = list(string)
  default = ["10.0.0.0/16"]
  description = "The address space that is used the virtual network."
}

variable "address_prefixes" {
  type = list(string)
  default = ["10.0.2.0/24"]
  description = "The address prefixes to use for the subnet"
}

variable "private_dns_zone_name" {
  type = string
  default = "private.contoso.com"
  description = "The name of the Private DNS Zone. Must be a valid domain name. Changing this value forces a new resource to be created."
}

variable "admin_username" {
  type = string
  default = "adminuser"
  description = "The username for the Windows virtual machines."
}

初始化 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. 运行 az network private-dns zone list 来查看所有 DNS 区域并找到你的区域。
az network private-dns zone list --output table
  1. 运行 az network private-dns zone show 来查看与 DNS 区域关联的资源组。
az network private-dns zone show --name $dnsZoneName --resource-group $resourceGroupName

清理资源

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

  1. 运行 terraform plan 并指定 destroy 标志。
terraform plan -destroy -out main.destroy.tfplan

要点:

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

Azure 上的 Terraform 故障排除

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