快速入門:使用 Terraform 建立 Azure NAT 閘道

使用 Terraform 開始使用 Azure NAT 閘道。 此 Terraform 檔案會部署虛擬網路、NAT 閘道資源和 Ubuntu 虛擬機。 Ubuntu 虛擬機器會部署到與 NAT 閘道資源相關聯的子網路。

此腳本也會產生隨機的 SSH 公鑰,並將它與虛擬機產生關聯,以進行安全存取。 公鑰會在文稿執行結束時輸出。

腳本除了 AzureRM 提供者之外,也會使用 Random 和 AzAPI 提供者。 隨機提供者可用來產生資源群組和 SSH 金鑰的唯一名稱。 AzAPI 提供者可用來產生 SSH 公鑰。

如同公鑰,腳本執行時會列印所建立資源群組、虛擬網路、子網和 NAT 閘道的名稱。

Terraform 可讓您定義、預覽和部署雲端基礎結構。 使用 Terraform 時,您可以使用 HCL 語法來建立設定檔。 HCL 語法可讓您指定雲端提供者 (例如 Azure) 和構成雲端基礎結構的元素。 建立設定檔之後,您可以建立執行計畫,讓您先預覽基礎結構變更,之後再部署。 驗證變更之後,您可以套用執行計畫來部署基礎結構。

nat 閘道快速入門中建立的資源圖表。

必要條件

實作 Terraform 程式碼

  1. 建立目錄,然後在目錄中測試並執行範例 Terraform 程式碼,且設為目前的目錄。

  2. 建立名為 main.tf 的檔案,並插入下列程式碼:

    # Resource Group
    resource "azurerm_resource_group" "rg" {
      location = var.resource_group_location
      name     = "${random_pet.prefix.id}-rg"
    }
    
    # Virtual Network
    resource "azurerm_virtual_network" "my_terraform_network" {
      name                = "${random_pet.prefix.id}-vnet"
      address_space       = ["10.0.0.0/16"]
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
    }
    
    # Subnet 1
    resource "azurerm_subnet" "my_terraform_subnet_1" {
      name                 = "subnet-1"
      resource_group_name  = azurerm_resource_group.rg.name
      virtual_network_name = azurerm_virtual_network.my_terraform_network.name
      address_prefixes     = ["10.0.0.0/24"]
    }
    
    # Public IP address for NAT gateway
    resource "azurerm_public_ip" "my_public_ip" {
      name                = "public-ip-nat"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
      allocation_method   = "Static"
      sku                 = "Standard"
    }
    
    # NAT Gateway
    resource "azurerm_nat_gateway" "my_nat_gateway" {
      name                = "nat-gateway"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
    }
    
    # Associate NAT Gateway with Public IP
    resource "azurerm_nat_gateway_public_ip_association" "example" {
      nat_gateway_id       = azurerm_nat_gateway.my_nat_gateway.id
      public_ip_address_id = azurerm_public_ip.my_public_ip.id
    }
    
    # Associate NAT Gateway with Subnet
    resource "azurerm_subnet_nat_gateway_association" "example" {
      subnet_id      = azurerm_subnet.my_terraform_subnet_1.id
      nat_gateway_id = azurerm_nat_gateway.my_nat_gateway.id
    }
    
    # Create public IP for virtual machine
    resource "azurerm_public_ip" "my_public_ip_vm" {
      name                = "public-ip-vm"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
      allocation_method   = "Static"
      sku                 = "Standard"
    }
    
    # Create Network Security Group and rule
    resource "azurerm_network_security_group" "my_terraform_nsg" {
      name                = "nsg-1"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
    
      security_rule {
        name                       = "SSH"
        priority                   = 1001
        direction                  = "Inbound"
        access                     = "Allow"
        protocol                   = "Tcp"
        source_port_range          = "*"
        destination_port_range     = "22"
        source_address_prefix      = "*"
        destination_address_prefix = "*"
      }
    }
    
    # Create network interface
    resource "azurerm_network_interface" "my_terraform_nic" {
      name                = "nic-1"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
    
      ip_configuration {
        name                          = "my_nic_configuration"
        subnet_id                     = azurerm_subnet.my_terraform_subnet_1.id
        private_ip_address_allocation = "Dynamic"
        public_ip_address_id          = azurerm_public_ip.my_public_ip_vm.id
      }
    }
    
    # Connect the security group to the network interface
    resource "azurerm_network_interface_security_group_association" "example" {
      network_interface_id      = azurerm_network_interface.my_terraform_nic.id
      network_security_group_id = azurerm_network_security_group.my_terraform_nsg.id
    }
    
    # Generate random text for a unique storage account name
    resource "random_id" "random_id" {
      keepers = {
        # Generate a new ID only when a new resource group is defined
        resource_group = azurerm_resource_group.rg.name
      }
    
      byte_length = 8
    }
    
    # Create storage account for boot diagnostics
    resource "azurerm_storage_account" "my_storage_account" {
      name                     = "diag${random_id.random_id.hex}"
      location                 = azurerm_resource_group.rg.location
      resource_group_name      = azurerm_resource_group.rg.name
      account_tier             = "Standard"
      account_replication_type = "LRS"
    }
    
    # Create virtual machine
    resource "azurerm_linux_virtual_machine" "my_terraform_vm" {
      name                  = "vm-1"
      location              = azurerm_resource_group.rg.location
      resource_group_name   = azurerm_resource_group.rg.name
      network_interface_ids = [azurerm_network_interface.my_terraform_nic.id]
      size                  = "Standard_DS1_v2"
    
      os_disk {
        name                 = "myOsDisk"
        caching              = "ReadWrite"
        storage_account_type = "Premium_LRS"
      }
    
      source_image_reference {
        publisher = "Canonical"
        offer     = "0001-com-ubuntu-server-jammy"
        sku       = "22_04-lts-gen2"
        version   = "latest"
      }
    
      computer_name  = "hostname"
      admin_username = var.username
    
      admin_ssh_key {
        username   = var.username
        public_key = jsondecode(azapi_resource_action.ssh_public_key_gen.output).publicKey
      }
    
      boot_diagnostics {
        storage_account_uri = azurerm_storage_account.my_storage_account.primary_blob_endpoint
      }
    }
    
    resource "random_pet" "prefix" {
      prefix = var.resource_group_name_prefix
      length = 1
    }
    
  3. 建立名為 outputs.tf 的檔案,並插入下列程式碼:

    output "resource_group_name" {
      description = "The name of the created resource group."
      value       = azurerm_resource_group.rg.name
    }
    
    output "virtual_network_name" {
      description = "The name of the created virtual network."
      value       = azurerm_virtual_network.my_terraform_network.name
    }
    
    output "subnet_name_1" {
      description = "The name of the created subnet 1."
      value       = azurerm_subnet.my_terraform_subnet_1.name
    }
    
    output "nat_gateway"{
      description = "The name of the created NAT gateway."
      value       = azurerm_nat_gateway.my_nat_gateway.id
    }
    
  4. 建立名為 providers.tf 的檔案,並插入下列程式碼:

    terraform {
      required_providers {
        azapi = {
          source  = "azure/azapi"
          version = "~>1.5"
        }
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "~>3.0"
        }
        random = {
          source  = "hashicorp/random"
          version = "~>3.0"
        }
      }
    }
    
    provider "azurerm" {
      features {}
    }
    
  5. 建立名為 ssh.tf 的檔案,並插入下列程式碼:

    resource "random_pet" "ssh_key_name" {
      prefix    = "ssh"
      separator = ""
    }
    
    resource "azapi_resource_action" "ssh_public_key_gen" {
      type        = "Microsoft.Compute/sshPublicKeys@2022-11-01"
      resource_id = azapi_resource.ssh_public_key.id
      action      = "generateKeyPair"
      method      = "POST"
    
      response_export_values = ["publicKey", "privateKey"]
    }
    
    resource "azapi_resource" "ssh_public_key" {
      type      = "Microsoft.Compute/sshPublicKeys@2022-11-01"
      name      = random_pet.ssh_key_name.id
      location  = azurerm_resource_group.rg.location
      parent_id = azurerm_resource_group.rg.id
    }
    
    output "key_data" {
      value = jsondecode(azapi_resource_action.ssh_public_key_gen.output).publicKey
    }
    
  6. 建立名為 variables.tf 的檔案,並插入下列程式碼:

    variable "resource_group_location" {
      type        = string
      default     = "eastus"
      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 "username" {
      type        = string
      description = "The username for the local account that will be created on the new VM."
      default     = "azureuser"
    }
    

初始化 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)
  1. 取得 NAT 閘道識別碼。
    nat_gateway=$(terraform output -raw nat_gateway)
  1. 執行 az network nat gateway show 以顯示 NAT 閘道的詳細數據。
az network nat gateway show \
    --resource-group $resource_group_name \
    --ids $nat_gateway

清除資源

當您不再需要透過 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 時的常見問題進行疑難排解

下一步