[NSX][IaC] Terraform para ingenieros de redes: Automatizando el despliegue del NSX Manager

[NSX][IaC] Terraform para ingenieros de redes: Automatizando el despliegue del NSX Manager

Como algunos sabéis, hace unos días me decidí a renovar mi home lab, cambiando mi viejo UCS C250 por un ligero y silencioso Intel NUC de 12ª Generación ¡con el que estoy contentísimo! Escribiré sobre ello muy pronto.

Una de las tareas a realizar durante el despliegue de éste, era la instalación de NSX, algo a lo que ya estoy acostumbrado a realizar de manera manual, necesitaba un nuevo reto, por lo que me puse a pensar y… ¡EUREKA! surgió la idea… ¿Y si intento automatizar el despliegue e implementación de NSX por completo con Terraform?

La idea de estas series serán, que todo aquel ingeniero de redes del mundo convencional que al igual que yo, quiera profundizar en temas de Automatización, IaC o NetDevOps partiendo desde cero pueda hacerlo de una forma sencilla, por lo que trataré de ser todo lo explicativo posible. También indicar que iré publicando a medida que vaya aprendiendo, por lo que yo también soy un novato… ¡Si veis cualquier error o tenéis sugerencia de mejora sobre mis pasos, serán bién recibidas! 😊

Antes de nada. ¿Qué es Terraform y que es IaC?

Si procedes de un mundo de redes convencional, imagino que la primera pregunta que te surgirá será ¿Qué es Terraform y para que sirve?

Terraform es una herramienta de código abierto desarrollada por Hashicorp que nos permite automatizar todo el despliegue y posterior gestión del ciclo de vida de nuestra infraestructura mediante código, es decir, podemos escribir mediante código cómo queremos que sea nuestra infraestructura y que parámetros queremos que contenga, y la herramienta se encargará del resto.

Por ejemplo, podemos definir cómo debe de ser nuestro fabric de red o nuestro fabric de servidores en plantillas para que la creación, modificación o decomisión de ésta se realice de manera automatizada con un click.

Para lograr esto, Terraform se apoya en los Providers, que son aquellos plugins desarrollados por los propios vendors, HashiCorp o por la comunidad y se encargan de transformar el código Terraform en acciones sobre la infraestructura. Para este tutorial en concreto, nos apoyaremos en el provider de vSphere. Pero podemos encontrar providers de muchos otros vendors como AWS, Azure, GCP, Arista, Cisco, HP, Dell, Oracle…

Paso 1: Preparando nuestro equipo para Terraform

En esta sección, instalaremos en Windows tanto Terraform como Visual Studio Code y la extensión de Terraform para simplificarnos el desarrollo de código. Si ya tienes instalado Terraform o utilizas otro sistema operativo, puedes saltarte esta sección.

1.1 Descargando e instalando Terraform

En primer lugar, iremos a la web de Terraform y pulsaremos en «Download» en la esquina superior derecha.

Escogeremos nuestra plataforma, y pulsaremos en «Download»

Se descargará un archivo Zip que contiene dentro el ejecutable de Terraform. Lo extraemos en la ruta donde queramos instalarlo. En mi caso ha sido «C:\Program Files\Terraform»

El siguente paso será añadir el directorio donde hemos descomprimido Terraform a la variable de entorno «Path» para que nuestro CMD pueda encontrarlo. Abrimos el menú de Windows y buscamos «System Variables»

Hacemos click en «Variables de Entorno» y editamos la variable de entorno «Path» para nuestro usuario

Añadimos la ruta donde hemos descomprimido Terraform y guardamos.

Para verificar el funcionamiento, abrimos un CMD de Windows y lanzamos «terraform«

Se nos devolverá un listado de los comandos disponibles, indicando que Terraform ha quedado correctamente instalado en nuestro sistema.

Paso 1.2 Descargando e instalando Visual Studio Code

Si bien esto es opcional, Visual Studio Code nos facillitará mucho el trabajo a la hora de trabajar con código ya que contiene vista de arbol de archivos así como editor visual.

Para descargarlo iremos a su página oficial y escogeremos nuestra plataforma. Ejecutamos el archivo resultante y seguimos el wizard.

Una vez instalado Visual Studio Code, lo abriremos y pulsaremos sobre «Open Folder», escogeremos la carpeta donde trabajaremos con nuestros archivos de Terraform:

Cuando tengamos la carpeta escogida, pincharemos en el menú de la izquierda sobre el icono de Extensiones, buscaremos la extensión oficial de Terraform y pulsaremos en Install.

Con esto ¡Tendremos nuestro entorno listo para empezar a trabajar con Terraform»

Paso 2: Creando los archivos necesarios y programando nuestra infraestructura

Lo siguiente, será preparar los archivos necesarios para que Terraform funcione, para ello vamos a crear 4 archivos distintos, que son los básicos en cualquier despliegue:

  • providers.tf – En este archivo, definiremos que providers queremos utilizar para este proyecto de Terraform, en nuestro caso utilizaremos el de vSphere. Es necesario para poder inicializar Terraform. 
  • main.tf – Este archivo es el principal del proyecto, contiene toda la configuración y las las acciones a realizar. 
  • variables.tf – En este archivo se definirán las variables a utilizar para el proyecto, así como tenemos la posibilidad de añadir un valor por defecto en caso de que no se especifiquen en el archivo que viene a continuación.
  • terraform.tfvars  – En este archivo se darán valores a las variables definidas anteriormente. Este sería el único archivo que modificaríamos por ejemplo para desplegar exactamente la misma infraestructura en un entorno distinto con IPs distintas, servidores DNS distintos…

Crearemos estos 4 archivos con Visual Studio Code, haciendo click sobre el espacio de trabajo y pulsando en «New File»

A continuación, podemos empezar a crear código. Para esto me he apoyado principalmente en la siguiente documentación:

2.1 El archivo providers.tf

En primer lugar, debemos añadir nuestro provider al archivo providers.tf

Para ello, vamos a la página oficial del provider (link) y pulsamos en la esquina superior izquierda sobre «Use Provider»

Copiamos el contenido en nuestro archivo providers.tf y añadimos las siguientes líneas bajo «# Configuration Options»

  user                 = var.vsphere_user # Here we indicate that the vSphere User will be read from a variable
  password             = var.vsphere_password # Here we indicate that the vSphere password will be read from a variable
  vsphere_server       = var.vsphere_server # Here we indicate that the vSphere server will be read from a variable
  allow_unverified_ssl = true # true if we use self-signed certificates / false if we use trusted certificates

El archivo debería quedar de la siguiente manera:

terraform {
  required_providers {
    vsphere = {
      source = "hashicorp/vsphere"
      version = "2.3.1"
    }
  }
}

provider "vsphere" {
  user                 = var.vsphere_user ## Here we declare the variable for the vSphere user
  password             = var.vsphere_password ## Here we declare the variable for the vSphere password
  vsphere_server       = var.vsphere_server ## Here we declare the variable for the vSphere server
  allow_unverified_ssl = true # true if we use self-signed certificates / false if we use trusted certificates
}
2.2 El archivo variables.tf

El siguiente paso será definir todas las variables que utilizaremos en nuestro archivo main.tf.

Para saber cuales son, por un lado deberemos leer la documentación del provider para conocer que campos se nos pedirán. En la documentación del provider de vSphere, vemos que para implementar una OVA (link), son necesarios los siguientes parámetros:

  • vsphere_server: Servidor vCenter
  • vsphere_user: Usuario del vCenter
  • vsphere_password: Contraseña del vCenter
  • deployment_option: Tamaño del despliegue, si no se indica se escogerá la opción por defecto
  • disk_provisioning: Thin o thick
  • datacenter: El datacenter donde desplegaremos nuestra OVA
  • cluster: El cluster donde desplegaremos nuestra OVA
  • vsphere_host: Será el host ESX donde desplegaremos nuestra OVA
  • datastore: El datastore donde desplegaremos nuestra OVA
  • vm_name: El nombre de la VM
  • vsphere_network: La red que asignaremos a la VM
  • local_ovf_path: Ubicación de la OVA en nuestro disco duro local

Por otro lado, debemos especificar las vAPP options, estas son específicas a la OVA que queremos desplegar, y en el caso de NSX Manager indicaré cuales son a continuación, pero dejo el siguiente procedimiento por si para algún otro despliegue de otra OVA queremos saber cuales son estos valores:

Si abrimos nuestra OVA con un archivador como 7zip, dentro encontraremos un archivo con formato OVF:

Si abrimos éste archivo con un editor como Notepad++, y buscamos por la cadena «ovf:userConfigurable=»true», veremos todas las variables de vAPP disponibles.

¡OJO! No todas estas variables son configurables por el usuario, pues algunas están para uso interno, deberemos revisar cuidadosamente cuales son las que debemos usar.

Las variables que utilizaremos para la OVA del NSX Manager son:

  • nsx_role: Rol del manager (NSX Manager o NSX Global Manager)
  • nsx_grub_passwd: Contraseña del menú de GRUB
  • nsx_grub_menu_timeout: Timeout del menú de GRUB
  • nsx_passwd_0: Contraseña de root
  • nsx_cli_passwd_0: Contraseña de admin
  • nsx_cli_audit_passwd_0: Contraseña de audit
  • nsx_cli_username: Usuario de admin
  • nsx_cli_audit_username: Usuario de audit
  • nsx_hostname: Hostname del manager
  • nsx_ip_0: IP de gestión
  • nsx_netmask_0: Máscara de gestión
  • nsx_gateway_0: Default GW
  • nsx_dns1_0: Servidor DNS
  • nsx_domain_0: Dominio
  • nsx_ntp_0: Servidor NTP
  • nsx_isSSHEnabled: true para habilitar SSH por defecto, false para deshabilitarlo
  • nsx_allowSSHRootLogin: true para permitir el acceso por SSH con el usuario root, false para deshabilitar

Una vez conocemos las variables a declarar, las introducimos en el archivo «variables.tf» con la siguiente estructura:

variable "datacenter" {}
variable "cluster" {}

En caso de querer añadir un valor por defecto para alguna variable, podemos hacerlo siguiendo ésta sintaxis:

variable "datacenter" {default = "sd-juan-lab"}
variable "cluster" {default = "sd-juan-lab}

Dejo como ejemplo, el resultado de mi archivo variables.tf

# vSphere Infrastructure Details
variable "datacenter" {}
variable "cluster" {}
variable "datastore" {}
variable "vsphere_host" {}
variable "vsphere_server" {default = "vcenter.sd-juan.lab"}
 
# vCenter Credential Variables
variable "vsphere_user" {}
variable "vsphere_password" {}

#NSX OVA Deployment Variables
variable "vm_name" { default = "nsxmgr01" }
variable "vsphere_network" { default = "VM Network" }
variable "disk_provisioning" { default = "thin" }
variable "deployment_option" { default = "small" } ## "extra_small", "small", "medium", "large"
variable "local_ovf_path" {default = "C:\\Users\\Juan\\Downloads\\nsx-unified-appliance-4.0.1.1.0.20598732.ova"}
variable "nsx_role"  { default = "NSX Manager" } ## "NSX Manager" or "NSX Global Manager"
variable "nsx_grub_passwd" { default = "VMware1!VMware1!" }
variable "nsx_grub_menu_timeout" { default = "5" }
variable "nsx_passwd_0" { default = "VMware1!VMware1!" }
variable "nsx_cli_passwd_0" { default = "VMware1!VMware1!" }
variable "nsx_cli_audit_passwd_0" { default = "VMware1!VMware1!" }
variable "nsx_cli_username" { default = "admin" }
variable "nsx_cli_audit_username" { default = "audit" }
variable "nsx_hostname" {}
variable "nsx_ip_0" {}
variable "nsx_netmask_0" {}
variable "nsx_gateway_0" {}
variable "nsx_dns1_0" { default = "192.168.20.2" }
variable "nsx_domain_0" { default = "sd-juan.lab" }
variable "nsx_ntp_0" { default = "192.168.20.2" }
variable "nsx_isSSHEnabled" { default = "True" }
variable "nsx_allowSSHRootLogin" { default = "False" }
2.3 El archivo main.tf

Este archivo, es el principal del despliegue, donde se incluyen todas las funciones a realizar así como las fuentes de obtención de datos.

De aquí es principalmente interesante entender las siguientes partes:

  • data: Son las fuentes de obtención de datos, aquí indicamos al archivo, que variable debe de leer para cada función que se le requiera.
  • resource «vsphere_virtual_machine» «vmFromLocalOvf»: Es la parte del código que le indica al provider que debe de desplegar una VM dentro del vCenter, dentro se incluyen las funciones hijas, como las propiedades del vAPP o la configuración de la OVF
  • lifecycle: En este apartado podríamos incluir características específicas del ciclo de vida de esta VM, como por ejemplo que cada vez que se haga un cambio, se ignore si se ha cambiado la password en la configuración.

Para más información sobre cómo funciona este provider podemos consultar la documentación del mismo (link)

## vSphere DC data source
data "vsphere_datacenter" "datacenter" {
  name = var.datacenter
}

# vSphere datastore data source
data "vsphere_datastore" "datastore" {
  name          = var.datastore
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

# vSphere cluster data source
data "vsphere_compute_cluster" "cluster" {
  name          = var.cluster
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

# vSphere resouce pool, leave by default if you have no resource pools
data "vsphere_resource_pool" "default" {
  name          = format("%s%s", data.vsphere_compute_cluster.cluster.name, "/Resources")
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

# vSphere host to deploy the OVA data source
data "vsphere_host" "host" {
  name          = var.vsphere_host
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

# vSphere network to connect the VM data source
data "vsphere_network" "network" {
  name          = var.vsphere_network
  datacenter_id = data.vsphere_datacenter.datacenter.id
}

## Local OVF/OVA Source
data "vsphere_ovf_vm_template" "ovfLocal" {
  name              = var.vm_name
  disk_provisioning = var.disk_provisioning
  resource_pool_id  = data.vsphere_resource_pool.default.id
  datastore_id      = data.vsphere_datastore.datastore.id
  host_system_id    = data.vsphere_host.host.id
  local_ovf_path    = var.local_ovf_path
  ovf_network_map = {
    "VM Network" : data.vsphere_network.network.id
  }
}

## Deployment of VM from Local OVF
resource "vsphere_virtual_machine" "vmFromLocalOvf" {
  name                 = var.vm_name
  datacenter_id        = data.vsphere_datacenter.datacenter.id
  datastore_id         = data.vsphere_datastore.datastore.id
  host_system_id       = data.vsphere_host.host.id
  resource_pool_id     = data.vsphere_resource_pool.default.id
  num_cpus             = data.vsphere_ovf_vm_template.ovfLocal.num_cpus
  num_cores_per_socket = data.vsphere_ovf_vm_template.ovfLocal.num_cores_per_socket
  memory               = data.vsphere_ovf_vm_template.ovfLocal.memory
  guest_id             = data.vsphere_ovf_vm_template.ovfLocal.guest_id
  scsi_type            = data.vsphere_ovf_vm_template.ovfLocal.scsi_type
  nested_hv_enabled    = data.vsphere_ovf_vm_template.ovfLocal.nested_hv_enabled
  dynamic "network_interface" {
    for_each = data.vsphere_ovf_vm_template.ovfLocal.ovf_network_map
    content {
      network_id = network_interface.value
    }
  }
  wait_for_guest_net_timeout = 0
  wait_for_guest_ip_timeout  = 0

  ovf_deploy {
    allow_unverified_ssl_cert = true ## true if we use self-signed certificates / false if we use trusted certificates
    local_ovf_path            = data.vsphere_ovf_vm_template.ovfLocal.local_ovf_path
    disk_provisioning         = data.vsphere_ovf_vm_template.ovfLocal.disk_provisioning
    ovf_network_map           = data.vsphere_ovf_vm_template.ovfLocal.ovf_network_map
    deployment_option         = var.deployment_option
  }

  vapp {
    properties = {
      "nsx_role"               = var.nsx_role,
      "nsx_grub_passwd"  = var.nsx_grub_passwd
      "nsx_grub_menu_timeout"  = var.nsx_grub_menu_timeout
      "nsx_passwd_0"  = var.nsx_passwd_0
      "nsx_cli_passwd_0"  = var.nsx_cli_passwd_0
      "nsx_cli_audit_passwd_0"  = var.nsx_cli_audit_passwd_0
      "nsx_cli_username"  = var.nsx_cli_username
      "nsx_cli_audit_username"  = var.nsx_cli_audit_username
      "nsx_hostname"  = var.nsx_hostname
      "nsx_ip_0"  = var.nsx_ip_0
      "nsx_netmask_0"  = var.nsx_netmask_0
      "nsx_gateway_0"  = var.nsx_gateway_0
      "nsx_dns1_0"  = var.nsx_dns1_0
      "nsx_domain_0"  = var.nsx_domain_0
      "nsx_ntp_0"  = var.nsx_ntp_0
      "nsx_isSSHEnabled"  = var.nsx_isSSHEnabled
      "nsx_allowSSHRootLogin"  = var.nsx_allowSSHRootLogin


    }
  }

  lifecycle {
    ignore_changes = [
      annotation,
      disk[0].io_share_count,
      disk[1].io_share_count,
      disk[2].io_share_count,
      vapp[0].properties,
    ]
  }
}
2.4 El archivo terraform.tfvars

En este archivo, definiremos el valor de todas las variables que declaramos en el archivo «variables.tf». Aquellas que no se indiquen aquí tomarán el valor por defecto especificado en el anterior.

Utilizaremos el siguiente formato:

datacenter = "sd-juan-lab"
cluster = "sd-juan-lab-cl1"
# vSphere Infrastructure Details
datacenter     = "sd-juan-lab"
cluster     = "sd-juan-lab-cl1"
datastore     = "datastore1"
vsphere_host     = "esx1.sd-juan.lab"
 
# vCenter Credential Variables
vsphere_user     = "administrator@vsphere.local"
vsphere_password     = #PASSWORD#

#NSX OVA Deployment Variables
vm_name     = "nsxmgr"
vsphere_network     = "MGMT-VM"
local_ovf_path     = "C:\\Users\\Juan\\Downloads\\nsx-unified-appliance-4.0.1.1.0.20598732.ova"
nsx_hostname     = "nsxmgr.sd-juan.lab"
nsx_ip_0     = "192.168.10.11"
nsx_netmask_0     = "24"
nsx_gateway_0     = "192.168.10.1"
nsx_allowSSHRootLogin      = "False"

Paso 3: Iniciando Terraform y aplicando nuestra configuración

¡Ya casi lo tenemos!

El último paso consistirá inicializar Terraform, crear un plan con la configuración actual y aplicarlo.

3.1 Inicializando Terraform

Una vez que tenemos todos nuestros archivos preparados y guardados, abrimos un CMD de Windows y hacemos CD hacia nuestra carpeta de trabajo de Terraform (la que teníamos abierta en Visual Studio Code) y lanzamos el comando «terraform init»

Si todo ha ido bien, veremos una pantalla como la siguiente, indicando que terraform se ha inicializado correctamente.

3.2 Creando un plan

El siguiente paso, será crear un plan en base a los archivos que hemos creado antes, algo así como «compilar» nuestra infraestructura para que Terraform verifique si está todo correcto, busque cambios con respecto a la infraestructura existente y esté preparado para trabajar.

Para ello, lanzaremos el comando «terraform plan -out #NOMBREDELPLAN#«

Una vez mas, si todo va bien, veremos una pantalla similar a ésta, donde tendremos un resumen de la configuración a aplicar

3.3 Aplicando el plan

Si todo está correcto, procederemos a aplicar el plan con el comando «terraform apply #NOMBREDELPLAN#

Terraform comenzará a crear la VM con los parámetros indicados. Podemos observar que esto es así en el panel de tareas de nuestro vCenter:

Cuando termine, ¡nuestra VM estará creada y lista para usarse!

Como siempre, ¡Espero que hayáis disfrutado leyéndome y que os sirva de utilidad! ¡Nos leemos pronto!

Deja una respuesta

Tu dirección de correo electrónico no será publicada.