Bats para testear scripts
Guía del comando tcpdump

Crear módulos de Terraform reutilizables

Modulos de Terraform

Existen muchos modulos de Terraform oficiales, pero también puedes construir propios para ajustarlos a tus necesidades.

Al implementar y gestionar Terraform a lo largo del tiempo en un solo archivo con todos tus recursos, pueden ocurrir varias cosas:

  • Complejidad: Un tema interesante en cualquier Código de Infraestructura (IaC), se busca reducir la complejidad tanto como sea posible.
  • Mantenibilidad: ¿Este código de Terraform todavía puede mantenerse? Intentar mapear valores y recursos cuando se utiliza un solo archivo grande.
  • Reusabilidad: ¿Implementando en múltiples entornos? ¿Puedo reutilizar lo que he creado anteriormente? Es posible, pero probablemente involucrará duplicación.

En relación con estas tres áreas clave, la escritura de módulos de Terraform reutilizables puede ayudar muchísimo al mantenimiento del código y su versatilidad.

módulos de Terraform
Referencia de imagen

¿Por qué crear módulos de Terraform?

  • Aumenta la mantenibilidad

Al implementar el mismo Terraform en múltiples entornos, con módulos, puedes aumentar la mantenibilidad de tu código de Terraform. Con un módulo, cuando lo actualizas, esa misma actualización se puede implementar en varios entornos sin tener que actualizar en varias áreas. (más sobre esto más adelante)

  • La navegación es más fácil

Crear módulos facilita la comprensión de lo que se está implementando, en lugar de un solo archivo o carpeta. Puedes filtrar y buscar dentro de un módulo. Por ejemplo, un módulo de Red Virtual de Azure contendrá todos los recursos necesarios para implementar una Red Virtual de Azure. Esto te permite navegar directamente al módulo si deseas realizar cambios en la Red Virtual de Azure.

  • Proporciona consistencia

Los módulos de Terraform pueden convertirse en una fuente de verdad, inclúyelos para proporcionar consistencia dentro de tu entorno. Actualizar el módulo puede ayudar a mantener todos los entornos sincronizados, pasando a mi próximo punto de reducir la desviación del entorno.

  • Reduce la desviación del entorno

Proporcionar consistencia realmente te permite reducir la desviación del entorno. Crear módulos de Terraform que todos los entornos utilicen como un recurso central ciertamente reducirá la desviación del entorno. La actualización del módulo puede tener efecto en todos los entornos, en lugar de modificar un recurso dentro de cada entorno.

  • Organización del código

Código más organizado, al usar módulos. Como se mencionó, tener una serie de módulos te permite navegar y organizar tu implementación de Terraform de manera mucho más fácil. Con el tiempo, la organización estructurada realmente se verá.

  • Comienza a implementar la estructura del código y definir las mejores prácticas

Como cualquier código/IaC, la estructuración se recomienda encarecidamente desde una perspectiva de desarrollo y también de soporte. Diseña tus módulos de Terraform con la estructura en mente. Mejores prácticas, recuérdalas al crear y definir módulos; con el tiempo, verás los beneficios de esto.

módulos de Terraform

Estructuración de tus módulos de Terraform

Los archivos y módulos de Terraform se pueden estructurar de muchas maneras. Aquí tienes algunos consejos para ayudarte a estructurar y crear tu módulo de Terraform.

  • Evita un solo archivo grande

Realmente, lo más importante en mi lista es esto: estructura tu(s) módulo(s) de Terraform, utilizando el potencial de la separación de preocupaciones, mantén archivos separados, incluyendo:

  • Variables
  • Recursos de Azure distintos: es posible que estés combinando un módulo con varios recursos, recomiendo dividirlos en archivos específicos para cada recurso.
  • Separa tus valores locales, si los estás utilizando mucho, muévelos a su propio archivo.
  • Cualquier recurso adicional de Terraform, potencialmente un archivo por base.

 

  • Planea desde el principio

Planifica tu implementación de Terraform desde el principio; piénsalo como mapear un viaje por carretera, ¿qué puntos quieres incluir y dónde? Será mucho más fácil trabajar hacia un plan; luego, incluso puedes verlo desde un formato ágil, dividiéndolo en varios epics e historias, etc.

  • Separación de preocupaciones

Crea módulos que vayan a crear diferentes recursos o áreas de Azure, no crees un módulo enorme. Recomiendo, como se mencionó anteriormente, dividir en varias áreas o recursos.

  • Módulo de Red Hub: Incluye red virtual del centro, firewall de Azure, ingreso, etc.
  • Módulo de Máquina Virtual: Todos los recursos necesarios para crear y implementar una máquina virtual.

 

  • Simplicidad, sin necesidad de complejidad (la mayoría de las veces 🙂)

Mantén tus módulos simples; pero efectivos. Recuerda que al crear módulos, los estás creando para ser reutilizables y piensa en ello desde un aspecto de soporte también. Alguien puede llegar en el futuro y querer modificar lo que has creado.

  • Estructura de carpetas

Incluye una representación precisa de la estructura de carpetas dentro de tus implementaciones de módulos de Terraform, similar a la separación de preocupaciones, define componentes y entornos dentro de carpetas.

Ejemplo de módulo de Terraform

Terraform Root Module

En mi ejemplo de un módulo de Terraform, estaré implementando una Red Virtual de Azure y subredes relevantes. Este será un módulo simple, para referenciar la estructura de carpetas y la configuración. Repositorio de muestra aquí.

Estructura de carpetas:

terraform-module-example
    └── modules
       └── vnet
          └── vnet.tf
          └── outputs.tf
          └── variables.tf
    └── main.tf
    └── providers.tf
    └── variables.tf

Si profundizamos en modules/vnet

vnet.tf contiene los recursos necesarios para crear una Red Virtual de Azure y subredes relevantes, incluido el Grupo de Recursos

resource "azurerm_resource_group" "vnet_resource_group" {
  name     = "${var.name}-rg"
  location = var.location
   
  tags = {
    Environment = var.environment
  }
}
 
resource "azurerm_virtual_network" "virtual_network" {
  name                = var.name
  location            = var.location
  resource_group_name = azurerm_resource_group.vnet_resource_group.name
  address_space       = [var.network_address_space]
 
  tags = {
    Environment = var.environment
  }
}
 
resource "azurerm_subnet" "aks_subnet" {
  name                 = var.aks_subnet_address_name
  resource_group_name  = azurerm_resource_group.vnet_resource_group.name
  virtual_network_name = azurerm_virtual_network.virtual_network.name
  address_prefixes     = [var.aks_subnet_address_prefix]
}
 
resource "azurerm_subnet" "appgw_subnet" {
  name                 = var.appgw_subnet_address_name
  resource_group_name  = azurerm_resource_group.vnet_resource_group.name
  virtual_network_name = azurerm_virtual_network.virtual_network.name
  address_prefixes     = [var.appgw_subnet_address_prefix]
}

variables.tf contiene las variables relevantes que el módulo requiere:

variable "name" { }
 
variable "location" {
  default = "uksouth"
}
 
variable "network_address_space" { }
 
variable "aks_subnet_address_prefix" { }
 
variable "aks_subnet_address_name" { }
 
variable "appgw_subnet_address_prefix" { }
 
variable "appgw_subnet_address_name" { }
 
variable "environment" { }

outputs.tf contiene salidas que puedo devolver a mi configuración de Terraform.

output "aks_subnet_id" {
  value = azurerm_subnet.aks_subnet.id
}
 
output "appgw_subnet_id" {
  value = azurerm_subnet.appgw_subnet.id
}
 
output "vnet_id" {
  value = azurerm_virtual_network.virtual_network.id
}
 
output "vnet_name" {
  value = azurerm_virtual_network.virtual_network.name
}
 
output "resource_group" {
  value = azurerm_resource_group.vnet_resource_group.name
}
 
output "resource_group_id" {
  value = azurerm_resource_group.vnet_resource_group.id
}

Puedes hacer referencia a las salidas dentro de tu configuración principal de Terraform.

Mirando root/main.tf, conocido como el módulo raíz, puedes declarar el módulo secundario referenciado anteriormente:

module "vnet" {
  source                      = "./modules/vnet"
  name                        = var.vnet_name
  location                    = var.location
  network_address_space       = var.network_address_space
  aks_subnet_address_prefix   = var.aks_subnet_address_prefix
  aks_subnet_address_name     = var.aks_subnet_address_name
  appgw_subnet_address_prefix = var.appgw_subnet_address_prefix
  appgw_subnet_address_name   = var.appgw_subnet_address_name
  environment                 = var.environment
}

providers.tf declara los proveedores de Terraform e información del backend.

provider "azurerm" {
    version = "~> 2.0"
    features {}
}
 
terraform {
    backend "azurerm" {
      resource_group_name   = "platopsacad-tf-rg"  
      storage_account_name   = "platopsacadazuredevops"
      container_name         = "terraform.tfstate`"
    }
}

data "azurerm_client_config" "current" {}

variables.tf nuevamente, variables, pero ahora para todo el entorno. Al agregar más módulos, incluirías más variables en este archivo.

variable "location" {
  type        = string
  description = "Location of Resources"
}
 
variable "vnet_name" {
  type        = string
  description = "Virtual Network Name"
}
 
variable "network_address_space" {
  type        = string
  description = "Virtual Network Address Space"
}
 
variable "aks_subnet_address_prefix" {
  type        = string
  description = "AKS Subnet Address Prefix"
}
 
variable "aks_subnet_address_name" {
  type        = string
  description = "AKS Subnet Name"
}
 
variable "appgw_subnet_address_prefix" {
  type        = string
  description = "AppGW Subnet Address Prefix"
}
 
variable "appgw_subnet_address_name" {
  type        = string
  description = "AppGW Subnet Name"
}
 
variable "environment" {
  type        = string
  description = "Environment"
}

Módulos de Terraform a escala

Anteriormente, mostré el desglose de cómo referenciar un módulo, ahora veamos cómo implementar varios módulos. En este ejemplo, solo tocaré la estructura de carpetas.

Consulta aquí para ver una configuración de repositorio de muestra adicional de esto.

En mi ejemplo, voy a implementar en Azure:

  • Log Analytics
  • Red Virtual
  • Azure Kubernetes Service (AKS)
  • Azure Container Registry (ACR)
  • Permisos de roles (IAM)
  • Application Insights

Ampliemos, tenemos una configuración básica: observa la estructura de carpetas, ¿notas que se está reutilizando?

También consulta environments/environments/development.tfvars y environments/environments/production.tfvars; usar .tfvars es realmente bueno, puedes usarlos para personalizar el nombre de las variables por entorno(s).

terraform-full
    └── environments
        └── development.tfvars
        └── production.tfvars
    └── modules
       └── acr
          └── acr.tf
          └── outputs.tf
          └── variables.tf
       └── aks
          └── vnet.tf
          └── outputs.tf
          └── variables.tf
       └── appinsights
          └── appinsights.tf
          └── variables.tf
       └── keyvault
          └── keyvault.tf
          └── variables.tf
       └── log-analytics
          └── la.tf
          └── outputs.tf
          └── variables.tf
       └── vnet
          └── vnet.tf
          └── outputs.tf
          └── variables.tf
    └── main.tf
    └── providers.tf
    └── variables.tf

¡La reutilización es clave! Ten en cuenta que, en mi ejemplo, he estructurado los módulos de Terraform en un modelo de repositorio único para mostrar la estructura de carpetas. Siéntete libre de tener repositorios separados por módulo 🙂

Tomando un pequeño fragmento de root/main.tf observa la referencia a las salidas del módulo module.acr.resource_group_id y module.aks.kubelet_object_id. Como se mencionó anteriormente, al mostrar las salidas dentro de un módulo, puedes hacer referencia a esto.

module "acr" {
  source      = "./modules/acr"
  name        = var.acr_name
  location    = var.location
  environment = var.environment
}
 
resource "azurerm_role_assignment" "aks-acr-rg" {
  scope                = module.acr.resource_group_id
  role_definition_name = "Acrpull"
  principal_id         = module.aks.kubelet_object_id
 
  depends_on = [
    module.aks,
    module.acr
  ]
}

¡Fantástico! 🙂

Algunas lecturas adicionales que te recomiendo revisar:

Resumen de módulos Terraform [ENG]
Construir y usar un módulo local
Buenas prácticas en Terraform
Herramientas de Terraform

Más apuntes

Invítame a un café con bitcoins:
1QESjZDPxWtZ9sj3v5tvgfFn3ks13AxWVZ

Bitcoins para café
También puedes invitarme a algo para mojar...

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Rellena este campo
Rellena este campo
Por favor, introduce una dirección de correo electrónico válida.
Tienes que aprobar los términos para continuar