快速入門:建立 Azure 防火牆和 IP 群組 - Terraform
在本快速入門中,您會使用 Terraform 部署 Azure 防火牆以及用在網路規則和應用程式規則中的範例 IP 群組。 IP 群組是最上層資源,可讓您定義 IP 位址、範圍和子網定義,並將其群組為單一物件。 IP 群組非常適合用來管理 Azure 防火牆規則中的 IP 位址。 您可手動輸入 IP 位址,或從檔案進行匯入。
Terraform 可讓您定義、預覽和部署雲端基礎結構。 使用 Terraform 時,您可以使用 HCL 語法來建立設定檔。 HCL 語法可讓您指定雲端提供者 (例如 Azure) 和構成雲端基礎結構的元素。 建立設定檔之後,您可以建立執行計畫,讓您先預覽基礎結構變更,之後再部署。 驗證變更之後,您可以套用執行計畫來部署基礎結構。
在本文中,您將學會如何:
- 使用 random_pet 建立隨機值 (以用於資源群組名稱)
- 使用 azurerm_resource_group 建立 Azure 資源群組
- 使用 random_password 建立 Windows VM 的隨機密碼
- 使用 random_string 建立隨機值 (以用作儲存體名稱)
- 使用 azurerm_public_ip 建立 Azure 公用 IP
- 使用 azurerm_storage_account 建立儲存體帳戶
- 使用 azurerm_firewall_policy 建立 Azure 防火牆原則
- 使用 azurerm_firewall_policy_rule_collection_group 建立 Azure 防火牆原則規則集合群組
- 使用 azurerm_firewall 建立 Azure 防火牆
- 使用 azurerm_ip_group 建立 Azure IP 群組
- 使用 azurerm_virtual_network 建立 Azure 虛擬網路
- 使用 azurerm_subnet 建立三個 Azure 子網路
- 使用 azurerm_network_interface 建立網路介面
- 使用 azurerm_network_security_group 建立網路安全性群組 (以包含網路安全性規則的清單)
- 使用 azurerm_network_interface_security_group_association 建立網路介面與網路安全性群組之間的關聯
- 使用 azurerm_linux_virtual_machine 建立 Azure Linux 虛擬機器
- 使用 azurerm_route_table 建立路由表
- 使用 azurerm_subnet_route_table_association 建立路由表與子網路之間的關聯
- 建立 AzAPI 資源 azapi_resource。
- 建立 AzAPI 資源,以使用 azapi_resource_action 產生 SSH 金鑰組。
必要條件
實作 Terraform 程式碼
注意
本文中的範例程式碼位於 Azure Terraform GitHub 存放庫 (英文)。 您可以檢視記錄檔,內含目前和舊版 Terraform 的測試結果 (英文)。
建立目錄,然後在目錄中測試範例 Terraform 程式碼,並將其設為目前的目錄。
建立名為
providers.tf
的檔案,並插入下列程式碼:terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "~>3.0" } random = { source = "hashicorp/random" version = "~>3.0" } azapi = { source = "azure/azapi" version = "~>1.5" } } } provider "azurerm" { features {} }
建立名為
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 = azapi_resource_action.ssh_public_key_gen.output.publicKey }
建立名為
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" } 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_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 = "app-rule-collection-1" priority = 101 action = "Allow" rule { name = "someAppRule" protocols { type = "Https" port = 443 } destination_fqdns = ["*bing.com"] source_ip_groups = [azurerm_ip_group.ip_group_1.id] } } network_rule_collection { name = "net-rule-collection-1" priority = 200 action = "Allow" rule { name = "someNetRule" protocols = ["TCP", "UDP", "ICMP"] source_ip_groups = [azurerm_ip_group.ip_group_1.id] destination_ip_groups = [azurerm_ip_group.ip_group_2.id] destination_ports = ["90"] } } } 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 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_ip_group" "ip_group_1" { name = "ip-group_1" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location cidrs = ["13.73.64.64/26", "13.73.208.128/25", "52.126.194.0/23"] } resource "azurerm_ip_group" "ip_group_2" { name = "ip_group_2" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location cidrs = ["12.0.0.0/24", "13.9.0.0/24"] } 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-SSH" priority = 1000 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "22" 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_linux_virtual_machine" "vm_server" { name = "server-vm" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location size = var.virtual_machine_size admin_username = var.admin_username admin_ssh_key { username = var.admin_username public_key = azapi_resource_action.ssh_public_key_gen.output.publicKey } network_interface_ids = [azurerm_network_interface.vm_server_nic.id] os_disk { caching = "ReadWrite" storage_account_type = "Standard_LRS" } source_image_reference { publisher = "Canonical" offer = "UbuntuServer" sku = "18.04-LTS" version = "latest" } boot_diagnostics { storage_account_uri = azurerm_storage_account.sa.primary_blob_endpoint } } resource "azurerm_linux_virtual_machine" "vm_jump" { name = "jump-vm" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location size = var.virtual_machine_size network_interface_ids = [azurerm_network_interface.vm_jump_nic.id] admin_username = var.admin_username os_disk { caching = "ReadWrite" storage_account_type = "Standard_LRS" } admin_ssh_key { username = var.admin_username public_key = azapi_resource_action.ssh_public_key_gen.output.publicKey } source_image_reference { publisher = "Canonical" offer = "UbuntuServer" sku = "18.04-LTS" version = "latest" } boot_diagnostics { storage_account_uri = azurerm_storage_account.sa.primary_blob_endpoint } computer_name = "JumpBox" } 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" "server_subnet_rt_association" { subnet_id = azurerm_subnet.server_subnet.id route_table_id = azurerm_route_table.rt.id }
建立名為
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" }
建立名為
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
,不需要使用參數。
驗證結果
取得 Azure 資源群組名稱。
resource_group_name=$(terraform output -raw resource_group_name)
執行 az network ip-group list 以顯示兩個新的 IP 群組。
az network ip-group list --resource-group $resource_group_name
清除資源
當您不再需要透過 Terraform 建立的資源時,請執行下列步驟:
執行 terraform plan 並指定
destroy
旗標。terraform plan -destroy -out main.destroy.tfplan
重點︰
terraform plan
命令會建立執行計畫,但不會執行。 相反地,其會決定要在您指定的設定檔中建立設定所需的動作。 此模式可讓您在對實際資源進行任何變更之前,先確認執行方案是否符合您的預期。- 選用的
-out
參數可讓您指定計畫的輸出檔。 使用-out
參數可確保您所檢閱的方案就是所套用的方案。
執行 terraform apply 以套用執行方案。
terraform apply main.destroy.tfplan
對 Azure 上的 Terraform 進行疑難排解
針對在 Azure 上使用 Terraform 時的常見問題進行疑難排解