Acabamos de terminar la serie sobre CI/CD, y en ella hemos hablado de lo importante que es mantener unas estrategias de trabajo que nos permitan la detección de errores rápidamente. Como colofón final, vimos como aprovisionar y desplegar en Azure Web Apps desde el Azure Pipelines (o desde otros sitios) y así despreocuparnos de esa parte utilizando las plantillas ARM.
Utilizar ARM es muy potente, pero quizás es algo tedioso de utilizar… ahí es donde entra terraform. ¿Y que es terraform podrás preguntarte? Pues es una herramienta multiplataforma hecha en Go que nos permite una gran abstracción sobre ARM, pero no solo funciona con ARM, admite una lista de proveedores bastante larga y que puedes consultar aquí.
Además de permitirnos trabajar de forma declarativa, sin tener que bajar al fango, aporta una gran ventaja respecto a usar ARM, y es que terraform mantiene el estado. Es decir, la herramienta es capaz de almacenar el estado de las cosas que ya están desplegadas, evitando volver a desplegarlas si no hay cambios. Pero hablar es fácil, y que yo te cuente que algo es mejor o peor no sirve de mucho, así que vamos a probarlo para ver lo sencillo que es.
Instalando terraform
Vamos a ir a su web y descargarnos el binario para nuestro sistema operativo. En la web nos indican que tenemos que añadir la ruta donde hayamos puesto el binario al PATH. (En mi caso, «C:/Terraform»):
Una vez que lo hayamos hecho, desde una terminal vamos a comprobar que todo esta bien ejecutando:
terraform version
Si todo está bien, debería devolvernos la versión actual. Si es así, ya tenemos terraform instalado. ¿A qué no ha sido tan difícil?
Aunque trabaja con ficheros *.tf que son texto plano, yo recomiendo utilizar Visual Studio Core con la extensión «Terraform«. Si tienes dudas sobre como instalar cualquiera de las dos, te dejo un enlace de CampusMVP donde explico cómo preparar un entorno de trabajo para .Net Core, al que solo hay que añadir la nueva extensión. Esto solo es una sugerencia, si prefieres utilizar cualquier otro entorno, o simplemente el block de notas y la terminal, todo funcionará igual de bien.
Creando nuestro código terraform
Para esta entrada, vamos a hacer un aprovisionamiento sencillo de una Azure Web App Windows, junto a su plan y su grupo de recursos desde terraform (para la siguiente vamos a hacer un escenario algo más complejo añadiendo un Sql Server a nuestra web). Para ello, vamos a crear un fichero al que llamaremos «main.tf» (el nombre no es importante). De hecho, terraform buscará todos los ficheros «*.tf» desde el nivel en el que se encuentre y unificará todos los archivos, por lo que es una buena idea separar los archivos por finalidades. En esta entrada vamos a utilizar un solo fichero para el código, pero una estructura más clara podría ser:
- webapp.tf para el código de la web.
- resourcegroup.tf para el código de grupo de recursos.
- variables.tf para las variables que vamos a necesitar.
Definiendo el proveedor
Lo primero que vamos a hacer, es definir el proveedor junto a las 4 variables que necesita (para utilizar las variables, se hace con el formato «${var.NOMBRE_VARIABLE}» ):
provider "azurerm" {
version = ">= 1.6.0"
subscription_id = "${var.ID_Suscripcion}"
client_id = "${var.ID_Aplicacion}"
client_secret = "${var.Password_Aplicacion}"
tenant_id = "${var.ID_Tenant}"
}
variable "ID_Suscripcion" {
description = "ID de la suscripción de Azure"
}
variable "ID_Aplicacion" {
description = "ID del la aplicación"
}
variable "Password_Aplicacion" {
description = "Secreto de la aplicación"
}
variable "ID_Tenant" {
description = "ID del directorio activo de Azure"
}
¿Y de dónde sacamos estos datos? Pues dos de ellas simplemente debemos consultarlas, para ello escribimos en la PowerShell:
az account list
Esto nos devolverá el ID de la suscripción y el directorio activo:
Para los dos campos de aplicación, lo que vamos a hacer es registrar una aplicación en nuestro directorio activo. Esto se puede hacer desde el portal de Azure, o desde la propia terminal ejecutando:
az ad sp create-for-rbac -n "NombreAplicacion"
Eso nos va a devolver los datos que nos faltan:
Si tienes problemas porque no se reconoce el comando «az», sigue este enlace para instalar Azure CLI.
Definiendo el Grupo de Recursos
Una vez que lo tenemos, vamos a añadir un grupo de recursos para nuestra Web App:
resource "azurerm_resource_group" "webapp" {
name = "${var.resource_group_name}"
location = "${var.location}"
}
variable "location" {
description = "Region donde queremos que se cree"
}
variable "resource_group_name" {
description = "Nombre del grupo de recursos"
}
Definiendo el Service Plan
Ahora vamos a añadir el service plan:
resource "azurerm_app_service_plan" "webserviceplan" {
name = "${var.service_plan_name}"
location = "${azurerm_resource_group.webapp.location}"
resource_group_name = "${azurerm_resource_group.webapp.name}"
kind = "${var.plan_settings["kind"]}"
sku {
tier = "${var.plan_settings["tier"]}"
size = "${var.plan_settings["size"]}"
capacity = "${var.plan_settings["capacity"]}"
}
}
variable "plan_settings" {
type = "map"
description = "Definimos el tipo de plan que vamos a utilizar"
default = {
kind = "Windows" # Linux or Windows
size = "S1"
capacity = 1
tier = "Standard"
}
}
variable "service_plan_name" {
description = "Nombre del service plan"
}
Una de las ventajas que podemos ver, es que terraform nos permite utilizar datos de los propios recursos desplegados utilizando el formato «${TIPO_DE_RECURSO.NOMBRE_RECURSO.DATO}». Por ejemplo, en:
location = "${azurerm_resource_group.webapp.location}"
Le estamos indicando que la localización del service plan, queremos que sea la misma que la del recurso «azurerm_resource_group» con el nombre «webapp». Trabajando de esta manera, terraform es capaz de calcular el grafo de dependencias para que todo se pueda aprovisionar sin problemas.
Definiendo la Web App
Por último, solo nos queda añadir el Web App:
resource "azurerm_app_service" "webapp" {
name = "${var.name}"
location = "${azurerm_resource_group.webapp.location}"
resource_group_name = "${azurerm_resource_group.webapp.name}"
app_service_plan_id = "${azurerm_app_service_plan.webserviceplan.id}"
}
variable "name" {
description = "Nombre de la Web App"
}
Hay muchos parámetros, que podríamos poner en el propio código, pero entonces no sería reutilizable… Terarform nos permite indicarle todos estos parámetros cuando vayamos a hacer el despliegue en el propio comando, pero como puedes intuir, es muy tedioso. Así que vamos a utilizar otra de las opciones que nos da terraform, que es cargar tener un segundo fichero de variables con sus valores.
Si usas git, hay que añadirlo en el .gitignore o nuestras claves se irán al repositorio.
Para poder tener este fichero, creamos un segundo fichero con el formato «*.auto.tfvars», por ejemplo, «variables.auto.tfvars», donde iremos rellenando los valores de esta forma:
ID_Suscripcion = "----"
ID_Aplicacion = "----"
Password_Aplicacion = "----"
ID_Tenant = "----"
name = "postterraformbasic"
service_plan_name = "planpostterraformbasic"
resource_group_name = "postterraform"
location = "westeurope"
A la vista está que este código es más fácil de seguir y mantener que si fuese ARM directamente, aunque hay algunas cosas que no están soportadas aun, y puede hacer falta incluir algo de ARM en algunos casos…
Desplegando con terraform
Una vez que tenemos todo el código, y las variables listas, vamos a desplegar, para ello vamos a inicializar la herramienta con:
terraform init
Una vez hecho esto, vamos a comprobar si existe algún error con:
terraform validate
En caso de que no exista ningún error, lo siguiente es calcular los cambios (aunque este paso se puede omitir):
terraform plan
La salida de este comando nos va a mostrar algo como esto:
Como salida, nos da las acciones que va a ejecutar en detalle, pero solo son a nivel informativo, para ejecutar el despliegue, utilizamos:
terraform apply
Esto nos devuelve algo parecido al comando plan, donde nos dice que es lo que va a hacer, pero ahora, nos pide que confirmemos escribiendo «yes» si queremos que despliegue:
Tras escribir «yes», se inicia el proceso. Una vez termine, la salida será algo asi:
Como terraform almacena el estado, si volvemos a intentar desplegar lo mismo, volviendo a ejecutar:
terraform apply
Obtenemos el resultado esperado, y nos dice que no hay ningún cambio pendiente:
Si por el contrario hubiésemos cambiado algo en el código, el resultado hubiese sido un cambio en nuestra infraestructura. Para terminar con las opciones que tenemos, existe una manera de eliminar los recursos desplegados:
terraform destroy
Volverá a pedirnos confirmación, y basta con que volvamos a escribir «yes»:
Conclusión
Aunque ha sido un ejemplo simple, se puede ver que terraform es una herramienta muy potente que nos quita mucho trabajo a la hora de desplegar infraestructura desde código al ser declarativo, persistir el estado y encima calcular las dependencias automáticamente. Existe una lista detallada de las opciones disponibles en la documentación oficial, a la cual recomiendo echarle un ojo.
En la próxima entrada vamos a ver un ejemplo más en profundidad creando una web en Linux conectada a un Sql Server, de modo que tengamos una infraestructura funcional, y además desplegaremos la web desde Azure Pipelines para poder comparar el proceso en el caso de ARM y en el caso de Terraform.
Si quieres ver el código completo, dejo un gist. También os dejo el enlace a un repo muy interesante de un workshop sobre terraform de un compañero.