Aprovisionamiento de una máquina virtual Linux mediante Terraform

Completado

Terraform implementa y controla una infraestructura de destino mediante archivos de configuración que describen el estado deseado de sus componentes. El formato básico de los archivos y su sintaxis general —expresada en el lenguaje de configuración de Hashicorp (HCL)— son iguales, independientemente de la elección de la nube. Sin embargo, las descripciones de componentes individuales dependen de la nube, según lo determinado por el proveedor de Terraform correspondiente.

Aunque hay varios proveedores de Terraform que admiten la administración de la infraestructura de Azure, AzureRM es de especial relevancia. El proveedor AzureRM facilita el aprovisionamiento y la configuración de recursos comunes de IaaS de Azure, como máquinas virtuales, cuentas de almacenamiento e interfaces de red. También hay proveedores adicionales que no son específicos de la nube que es posible que quiera incorporar en las implementaciones. Entre ellos figuran el proveedor aleatorio, que ayuda a evitar conflictos de nomenclatura de recursos mediante la generación de cadenas de caracteres pseudoaleatorios, y el proveedor tls, que simplifica la administración de claves asimétricas para proteger la autenticación de Linux.

Terraform está disponible como un único archivo binario que puede descargarse en el sitio web de Hashicorp. Este binario implementa la interfaz de la línea de comandos (CLI) de Terraform, que puede invocar desde una sesión de shell para inicializar Terraform y procesar archivos de configuración. Puede usar la CLI de Terraform desde cualquiera de los shells que admiten la CLI de Azure.

Nota:

Al usar Azure Cloud Shell, asegúrese de ejecutar la versión actual de Terraform siguiendo las instrucciones proporcionadas en Configuración de Terraform en Azure Cloud Shell con Bash.

Implementación de una máquina virtual Linux mediante Terraform

Terraform permite definir, previsualizar e implementar recursos en una infraestructura de nube específica del proveedor. El proceso de aprovisionamiento comienza con la creación de archivos de configuración que usan la sintaxis de HCL, lo que le permite designar el entorno de nube de destino (como Azure) y los recursos que componen la infraestructura en la nube. Una vez implementados todos los archivos de configuración pertinentes (normalmente dentro de la misma ubicación del sistema de archivos), puede generar un plan de ejecución que le permita obtener una vista previa de los cambios de infraestructura resultantes antes de la implementación real. Esto requiere que inicialice Terraform para descargar los módulos de proveedor necesarios para implementar recursos en la nube. Una vez que valide los cambios, podrá aplicar el plan de ejecución para implementar la infraestructura.

Nota:

La generación de un plan de ejecución es opcional, pero se recomienda hacerlo porque le permite identificar cualquier impacto de la implementación planeada sin incidir en el entorno de destino. Al implementar recursos de Azure de forma interactiva, Terraform admite la autenticación de la CLI de Azure de forma transparente mediante la reutilización de las credenciales para acceder a la suscripción de Azure de destino.

El proceso de aprovisionamiento de una máquina virtual de Azure que ejecuta Linux mediante Terraform normalmente implica la siguiente secuencia de pasos de alto nivel:

  • Identifique la imagen de máquina virtual adecuada.
  • Identifique el tamaño de máquina virtual adecuado.
  • Cree archivos de configuración que definan el recurso de máquina virtual de Azure con sus dependencias.
  • Inicialice Terraform.
  • Genere un plan de ejecución de Terraform.
  • Inicie una implementación de Terraform.

Para identificar la imagen y el tamaño adecuados de la máquina virtual, siga los pasos descritos en la unidad 4 de este módulo. Esta unidad se centra en tareas específicas de Terraform.

Creación de archivos de configuración

Nota:

Los nombres de archivo que elija para los archivos de Terraform son arbitrarios, aunque es recomendable elegir un nombre que refleje el contenido o el propósito del archivo. Debe usar ".tf" para la extensión de archivo.

Para implementar una máquina virtual Linux mediante Terraform, empiece por crear un directorio para hospedar los archivos de configuración. A continuación, cree un archivo denominado providers.tf que aplique la versión de Terraform y designe los proveedores en los que se basará al definir los recursos incluidos en la implementación. Este archivo debe tener el contenido mostrado en el siguiente fragmento de código:

terraform {
  required_version = ">=0.12"

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

provider "azurerm" {
  features {}
}

En el mismo directorio, cree un archivo denominado main.tf con el código siguiente, que define la configuración de la máquina virtual de Azure y sus dependencias:

resource "random_pet" "rg_name" {
  prefix = var.resource_group_name_prefix
}

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

# Create virtual network
resource "azurerm_virtual_network" "terraform_network" {
  name                = "lnx-tf-vnet"
  address_space       = ["10.1.0.0/16"]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

# Create subnet
resource "azurerm_subnet" "terraform_subnet" {
  name                 = "subnet0"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.terraform_network.name
  address_prefixes     = ["10.1.0.0/24"]
}

# Create public IPs
resource "azurerm_public_ip" "terraform_public_ip" {
  name                = "lnx-tf-pip"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  allocation_method   = "Dynamic"
}

# Create Network Security Group and rule
resource "azurerm_network_security_group" "terraform_nsg" {
  name                = "lnx-tf-nsg"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  security_rule {
    name                       = "ssh"
    priority                   = 300
    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" "terraform_nic" {
  name                = "lnx-tf-nic"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                          = "nic_configuration"
    subnet_id                     = azurerm_subnet.terraform_subnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.terraform_public_ip.id
  }
}

# Connect the security group to the network interface
resource "azurerm_network_interface_security_group_association" "lnx-tf-nic-nsg" {
  network_interface_id      = azurerm_network_interface.terraform_nic.id
  network_security_group_id = azurerm_network_security_group.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" "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 (and display) an SSH key
resource "tls_private_key" "lnx-tf-ssh" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

# Create virtual machine
resource "azurerm_linux_virtual_machine" "lnx-tf-vm" {
  name                  = "lnx-tf-vm"
  location              = azurerm_resource_group.rg.location
  resource_group_name   = azurerm_resource_group.rg.name
  network_interface_ids = [azurerm_network_interface.terraform_nic.id]
  size                  = "Standard_F4s"

  os_disk {
    name                 = "lnx-tf-vm-osdisk"
    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                   = "lnx-tf-vm"
  admin_username                  = "azureuser"
  disable_password_authentication = true

  admin_ssh_key {
    username   = "azureuser"
    public_key = tls_private_key.lnx-tf-ssh.public_key_openssh
  }

  boot_diagnostics {
    storage_account_uri = azurerm_storage_account.storage_account.primary_blob_endpoint
  }
}

En el mismo directorio, cree otro archivo denominado variables.tf con el código siguiente, que asigna el valor a las variables que aparecen en el archivo main.tf :

variable "resource_group_location" {
  default     = "eastus"
  description = "Location of the resource group"
}

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

Por último, cree un archivo denominado outputs.tf con el código siguiente, que determina la salida que se muestra después de una implementación correcta:

output "resource_group_name" {
  value = azurerm_resource_group.rg.name
}

output "public_ip_address" {
  value = azurerm_linux_virtual_machine.lnx-tf-vm.public_ip_address
}

output "tls_private_key" {
  value     = tls_private_key.lnx-tf-ssh.private_key_pem
  sensitive = true
}

Inicialización de Terraform

Para inicializar la implementación de Terraform, ejecute el siguiente comando desde el símbolo del sistema del shell:

terraform init

Este comando descarga los módulos de Azure necesarios para aprovisionar y administrar los recursos de Azure.

Generación del plan de ejecución

Después de la inicialización, cree un plan de ejecución mediante la ejecución del plan de Terraform. El comando crea un plan de ejecución, pero no lo ejecuta. En su lugar, determina qué acciones son necesarias para crear los recursos definidos en los archivos de configuración. El parámetro opcional -out permite especificar un archivo de salida para el plan, al que puede hacer referencia durante la implementación real. El uso de este archivo garantiza que el plan que revise coincida con el resultado exacto de la implementación. Use el siguiente comando para generar un plan de ejecución:

terraform plan -out <terraform_plan>.tfplan

Inicio de una implementación

Cuando esté listo para aplicar el plan de ejecución a su entorno de Azure, ejecute terraform apply, incluido el nombre del archivo que generó en el paso anterior. Tendrá otra oportunidad de revisar el resultado esperado. Terraform le pide confirmación para continuar, aunque puede eliminar el mensaje agregando el modificador -auto-approve. Use el siguiente comando para iniciar la implementación:

terraform apply <terraform_plan>.tfplan

La máquina virtual de Azure comenzará a ejecutarse en breve, normalmente en un par de minutos. La salida del comando terraform apply incluye la lista de salidas, pero terraform reemplazará el valor de tls_private_key por la etiqueta <confidencial>:

Apply complete! Resources: 12 added, 0 changed, 0 destroyed.

Salidas:

public_ip_address = "74.235.10.136"
resource_group_name = "rg-flexible-shark"
tls_private_key = <sensitive>

Para usar la clave privada generada automáticamente para autenticar la conexión SSH, almacénela en un archivo y, a continuación, establezca los permisos del archivo para asegurarse de que otros usuarios no puedan acceder a él. Para ello, ejecute los siguientes comandos:

terraform output -raw tls_private_key > id_rsa
chmod 600 id_rsa

En este momento, podrá conectarse a la máquina virtual de Azure mediante la ejecución del siguiente comando (después de reemplazar el marcador de posición <public_ip_address> por la dirección IP que identificó en la salida generada por terraform apply):

ssh -i id_rsa azureuser@<public_ip_address>