Início Rápido: criar um ponto de extremidade privado usando o Terraform

Neste início rápido, você usará o Terraform para criar um ponto de extremidade privado. O ponto de extremidade privado se conecta a um Banco de Dados SQL do Azure. O ponto de extremidade privado está associado a uma rede virtual e a uma zona DNS (Sistema de Nomes de Domínio) privada. A zona DNS privada resolve o endereço IP do ponto de extremidade privado. A rede virtual contém uma máquina virtual que você usa para testar a conexão do ponto de extremidade privado com a instância do Banco de Dados SQL.

O script gera uma senha aleatória para o SQL Server e uma chave SSH aleatória para a máquina virtual. Os nomes dos recursos criados são gerados quando o script é executado.

O Terraform permite a definição, a visualização e a implantação da infraestrutura de nuvem. Usando o Terraform, você cria arquivos de configuração usando sintaxe de HCL. A sintaxe da HCL permite que você especifique o provedor de nuvem, como o Azure, e os elementos que compõem sua infraestrutura de nuvem. Depois de criar os arquivos de configuração, você cria um plano de execução que permite visualizar as alterações de infraestrutura antes de serem implantadas. Depois de verificar as alterações, aplique o plano de execução para implantar a infraestrutura.

Diagrama dos recursos criados no início rápido do ponto de extremidade privado.

Pré-requisitos

Implementar o código do Terraform

  1. Crie um diretório no qual testar e executar o código de exemplo do Terraform e faça dele o diretório atual.

  2. Crie um arquivo chamado main.tf e insira o seguinte código:

    resource "random_pet" "prefix" {
      prefix = var.resource_group_name_prefix
      length = 1
    }
    
    # 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 = azapi_resource_action.ssh_public_key_gen.output.publicKey
      }
    
      boot_diagnostics {
        storage_account_uri = azurerm_storage_account.my_storage_account.primary_blob_endpoint
      }
    }
    
    # Create SQL server name
    resource "random_pet" "azurerm_mssql_server_name" {
      prefix = "sql"
    }
    
    # Random password for SQL server
    resource "random_password" "admin_password" {
      count       = var.admin_password == null ? 1 : 0
      length      = 20
      special     = true
      min_numeric = 1
      min_upper   = 1
      min_lower   = 1
      min_special = 1
    }
    
    locals {
      admin_password = try(random_password.admin_password[0].result, var.admin_password)
    }
    
    # Create SQL server
    resource "azurerm_mssql_server" "server" {
      name                         = random_pet.azurerm_mssql_server_name.id
      resource_group_name          = azurerm_resource_group.rg.name
      location                     = azurerm_resource_group.rg.location
      administrator_login          = var.admin_username
      administrator_login_password = local.admin_password
      version                      = "12.0"
    }
    
    # Create SQL database
    resource "azurerm_mssql_database" "db" {
      name      = var.sql_db_name
      server_id = azurerm_mssql_server.server.id
    }
    
    # Create private endpoint for SQL server
    resource "azurerm_private_endpoint" "my_terraform_endpoint" {
      name                = "private-endpoint-sql"
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
      subnet_id           = azurerm_subnet.my_terraform_subnet_1.id
    
      private_service_connection {
        name                           = "private-serviceconnection"
        private_connection_resource_id = azurerm_mssql_server.server.id
        subresource_names              = ["sqlServer"]
        is_manual_connection           = false
      }
    
      private_dns_zone_group {
        name                 = "dns-zone-group"
        private_dns_zone_ids = [azurerm_private_dns_zone.my_terraform_dns_zone.id]
      }
    }
    
    # Create private DNS zone
    resource "azurerm_private_dns_zone" "my_terraform_dns_zone" {
      name                = "privatelink.database.windows.net"
      resource_group_name = azurerm_resource_group.rg.name
    }
    
    # Create virtual network link
    resource "azurerm_private_dns_zone_virtual_network_link" "my_terraform_vnet_link" {
      name                  = "vnet-link"
      resource_group_name   = azurerm_resource_group.rg.name
      private_dns_zone_name = azurerm_private_dns_zone.my_terraform_dns_zone.name
      virtual_network_id    = azurerm_virtual_network.my_terraform_network.id
    }
    
  3. Crie um arquivo chamado outputs.tf e insira o seguinte código:

    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_name" {
      description = "The name of the created NAT gateway."
      value       = azurerm_nat_gateway.my_nat_gateway.name
    }
    
    output "sql_server_name" {
      value = azurerm_mssql_server.server.name
    }
    
    output "admin_password" {
      sensitive = true
      value     = local.admin_password
    }
    
  4. Crie um arquivo chamado provider.tf e insira o seguinte código:

    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 {
        resource_group {
          prevent_deletion_if_contains_resources = false
        }
      }
    }
    
  5. Crie um arquivo chamado ssh.tf e insira o seguinte código:

    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
    }
    
  6. Crie um arquivo chamado variables.tf e insira o seguinte código:

    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"
    }
    
    variable "sql_db_name" {
      type        = string
      description = "The name of the SQL Database."
      default     = "SampleDB"
    }
    
    variable "admin_username" {
      type        = string
      description = "The administrator username of the SQL logical server."
      default     = "azureadmin"
    }
    
    variable "admin_password" {
      type        = string
      description = "The administrator password of the SQL logical server."
      sensitive   = true
      default     = null
    }
    

Inicializar Terraform

Execute terraform init para inicializar a implantação do Terraform. Esse comando baixa o provedor do Azure necessário para gerenciar seus recursos do Azure.

terraform init -upgrade

Pontos principais:

  • O parâmetro -upgrade atualiza os plug-ins do provedor necessários para a versão mais recente que esteja em conformidade com as restrições de versão da configuração.

Criar um plano de execução Terraform

Execute o comando terraform plan para criar um plano de execução.

terraform plan -out main.tfplan

Pontos principais:

  • O comando terraform plan cria um plano de execução, mas não o executa. Em vez disso, ele determina quais ações são necessárias para criar a configuração especificada em seus arquivos de configuração. Esse padrão permite que você verifique se o plano de execução corresponde às suas expectativas antes de fazer qualquer alteração nos recursos reais.
  • O parâmetro opcional -out permite que você especifique um arquivo de saída para o plano. Usar o parâmetro -out garante que o plano que você examinou seja exatamente o que é aplicado.

Aplicar um plano de execução do Terraform

Execute terraform apply para aplicar o plano de execução à sua infraestrutura de nuvem.

terraform apply main.tfplan

Pontos principais:

  • O exemplo de comando do terraform apply pressupõe que você executou o terraform plan -out main.tfplan anteriormente.
  • Se você especificou um nome de arquivo diferente para o parâmetro -out, use esse mesmo nome de arquivo na chamada para terraform apply.
  • Se você não usou o parâmetro -out, chame terraform apply sem nenhum parâmetro.

Verifique os resultados

  1. Obtenha o nome do grupo de recursos do Azure.

    resource_group_name=$(terraform output -raw resource_group_name)
    
  2. Obtenha o nome do SQL Server.

    sql_server=$(terraform output -raw sql_server)
    
  3. Execute az sql server show para exibir os detalhes sobre o ponto de extremidade privado do SQL Server.

    az sql server show \
        --resource-group $resource_group_name \
        --name $sql_server --query privateEndpointConnections \
        --output tsv
    

Limpar os recursos

Quando você não precisar mais dos recursos criados por meio o Terraform, execute as seguintes etapas:

  1. Execute terraform plan e especifique o sinalizador destroy.

    terraform plan -destroy -out main.destroy.tfplan
    

    Pontos principais:

    • O comando terraform plan cria um plano de execução, mas não o executa. Em vez disso, ele determina quais ações são necessárias para criar a configuração especificada em seus arquivos de configuração. Esse padrão permite que você verifique se o plano de execução corresponde às suas expectativas antes de fazer qualquer alteração nos recursos reais.
    • O parâmetro opcional -out permite que você especifique um arquivo de saída para o plano. Usar o parâmetro -out garante que o plano que você examinou seja exatamente o que é aplicado.
  2. Execute a aplicação do Terraform para aplicar o plano de execução.

    terraform apply main.destroy.tfplan
    

Solucionar problemas do Terraform no Azure

Solucionar problemas comuns ao usar o Terraform no Azure.

Próximas etapas