Effettuare il provisioning di una macchina virtuale Linux usando Terraform

Completato

Terraform implementa e controlla un'infrastruttura di destinazione usando i file di configurazione che descrivono lo stato desiderato dei relativi componenti. Il formato di base dei file e la relativa sintassi generale, espressa in HCL (Hashicorp Configuration Language) sono uguali indipendentemente dalla scelta del cloud. Tuttavia, le singole descrizioni dei componenti dipendono dal cloud, come determinato dal provider Terraform corrispondente.

Anche se esistono diversi provider Terraform che supportano la gestione dell'infrastruttura di Azure, AzureRM è particolarmente importante. Il provider AzureRM facilita il provisioning e la configurazione di risorse IaaS comuni di Azure, ad esempio macchine virtuali, account di archiviazione e interfacce di rete. Esistono anche altri provider non specifici del cloud che è possibile incorporare nelle distribuzioni. Questi includono il provider casuale, che consente di evitare conflitti di denominazione delle risorse generando stringhe di caratteri pseudo-casuali; e il provider tls, che semplifica la gestione delle chiavi asimmetriche per la protezione dell'autenticazione Linux.

Terraform è disponibile come singolo file binario che è possibile scaricare dal sito Web Hashicorp. Questo file binario implementa l'interfaccia della riga di comando di Terraform, che è quindi possibile richiamare da una sessione della shell per inizializzare Terraform ed elaborare i file di configurazione. È possibile usare l'interfaccia della riga di comando di Terraform da una delle shell che supportano l'interfaccia della riga di comando di Azure.

Nota

Quando si usa Azure Cloud Shell, assicurarsi di eseguire la versione corrente di Terraform seguendo le istruzioni fornite in Configurare Terraform in Azure Cloud Shell con Bash.

Distribuire una macchina virtuale Linux usando Terraform

Terraform consente di definire, visualizzare in anteprima e distribuire le risorse in un'infrastruttura cloud specifica del provider. Il processo di provisioning inizia con la creazione di file di configurazione che usano la sintassi HCL, che consente di designare l'ambiente cloud di destinazione, ad esempio Azure, e le risorse che costituiscono l'infrastruttura cloud. Dopo che tutti i file di configurazione pertinenti sono presenti (in genere all'interno dello stesso percorso del file system), è possibile generare un piano di esecuzione che consente di visualizzare in anteprima le modifiche dell'infrastruttura risultanti prima della distribuzione effettiva. A tale scopo è necessario inizializzare Terraform per scaricare i moduli del provider necessari per implementare le risorse cloud. Dopo aver convalidato le modifiche, è possibile applicare il piano di esecuzione per distribuire l'infrastruttura.

Nota

La generazione di un piano di esecuzione è facoltativa, ma è consigliabile farlo perché consente di identificare qualsiasi impatto dalla distribuzione pianificata senza influire sull'ambiente di destinazione. Quando si distribuiscono le risorse di Azure in modo interattivo, Terraform supporta l'autenticazione dell'interfaccia della riga di comando di Azure in modo trasparente riutilizzando le credenziali per accedere alla sottoscrizione di Azure di destinazione.

Il provisioning di una macchina virtuale di Azure che esegue Linux tramite Terraform prevede in genere questa sequenza di passaggi principali:

  • Identificare l'immagine di macchina virtuale appropriata.
  • Identificare le dimensioni di macchina virtuale appropriate.
  • Creare file di configurazione che definiscono la risorsa macchina virtuale di Azure con le relative dipendenze.
  • Inizializzare Terraform.
  • Generare un piano di esecuzione di Terraform.
  • Avviare una distribuzione di Terraform.

Per identificare l'immagine e le dimensioni appropriate per la macchina virtuale, seguire i passaggi descritti nell'Unità 4 di questo modulo. Questa unità illustra le attività specifiche di Terraform.

Creare i file di configurazione

Nota

I nomi file scelti per i file Terraform sono arbitrari, anche se è consigliabile scegliere un nome che rifletta il contenuto o lo scopo del file. È consigliabile usare ".tf" per l'estensione di file.

Per distribuire una macchina virtuale Linux usando Terraform, iniziare creando una directory per ospitare i file di configurazione. Creare quindi un file denominato providers.tf che impone la versione Terraform e designa i provider che verranno usati per la definizione delle risorse incluse nella distribuzione. Questo file deve includere il contenuto visualizzato nel frammento di codice seguente:

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 {}
}

Nella stessa directory creare un file denominato main.tf usando il codice seguente, che definisce la configurazione della macchina virtuale di Azure e le relative dipendenze:

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
  }
}

Nella stessa directory creare un altro file denominato variables.tf usando il codice seguente, che assegna il valore alle variabili visualizzate nel file 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"
}

Infine, creare un file denominato outputs.tf usando il codice seguente, che determina l'output che viene visualizzato a seguito dell'avvenuta distribuzione:

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
}

Inizializzare Terraform

Per inizializzare la distribuzione di Terraform, eseguire il comando seguente dal prompt della shell:

terraform init

Questo comando scarica i moduli di Azure necessari per effettuare il provisioning e gestire le risorse di Azure.

Generare un piano di esecuzione

Dopo l'inizializzazione, creare un piano di esecuzione eseguendo terraform plan. Il comando crea un piano di esecuzione ma non lo esegue. Determina invece le azioni necessarie per creare le risorse definite nei file di configurazione. Il parametro facoltativo -out consente di specificare un file di output per il piano, a cui è possibile fare riferimento durante la distribuzione effettiva. L'uso di questo file garantisce che il piano esaminato corrisponda al risultato esatto della distribuzione. Usare il comando seguente per generare un piano di esecuzione:

terraform plan -out <terraform_plan>.tfplan

Avviare una distribuzione

Quando si è pronti per applicare il piano di esecuzione all'ambiente Azure, eseguire terraform apply, incluso il nome del file generato nel passaggio precedente. Si avrà un'altra possibilità di esaminare il risultato previsto. Terraform richiede conferma per continuare, anche se è possibile eliminare la richiesta aggiungendo l'opzione -auto-approve . Usare il comando seguente per avviare la distribuzione:

terraform apply <terraform_plan>.tfplan

L'esecuzione della macchina virtuale di Azure inizierà poco dopo, in genere entro un paio di minuti. L'output terraform apply del comando include l'elenco di output, ma terraform sostituirà il valore di tls_private_key con l'etichetta <sensibile> :

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

Output:

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

Per usare la chiave privata generata automaticamente per autenticare la connessione SSH, archiviarla in un file e quindi impostare le autorizzazioni del file per assicurarsi che non sia accessibile da altri utenti. A questo scopo, eseguire i comandi seguenti:

terraform output -raw tls_private_key > id_rsa
chmod 600 id_rsa

A questo punto, sarà possibile connettersi alla macchina virtuale di Azure eseguendo il comando seguente (dopo aver sostituito il segnaposto <public_ip_address> con l'indirizzo IP identificato nell'output generato dal comando terraform apply):

ssh -i id_rsa azureuser@<public_ip_address>