¿Por qué Terraform ha plantado cara a Cloudformation?

Antes de entrar en materia, conviene dejar algunas cosas claras: este post se basa en una opinión puramente personal. Esta opinión es el resultado de cierta experiencia trabajando con Cloudformation y Terraform (v0.11 por el momento).

Como con las religiones, equipos de fútbol y bebidas de cacao soluble, cada cual puede tener otra opinión perfectamente válida y respetable.  

Tanto Terraform como Cloudformation son herramientas que permiten gestionar infraestructuras como código. Ambas permiten crear y gestionar infraestructuras y recursos en la nube mediante archivos de código…

Sin embargo, tanto en concepción y diseño, como en el uso de las mismas, las diferencias son muy notables, y eso es lo que pretendemos analizar en este post. ¡Arrancamos!

Pongámonos en contexto: Terraform es una herramienta de Hashicorp y Cloudformation lo es de Amazon Web Services, pero a diferencia de Terraform, Cloudformation no es open source y sigue un roadmap de funcionalidades marcada por Amazon.

Terraform soporta múltiples proveedores

Cloudformation, al ser un servicio de Amazon Web Services, sirve para desplegar recursos en ese proveedor cloud únicamente.

Sin embargo, Terraform ha permitido desde el comienzo integrarse con distintos proveedores y esta es la razón más importante por la que Terraform ha conseguido plantar cara a Cloudformation.

Terraform es la navaja suiza de la Infraestructura como código y permite integrarse con prácticamente todos los proveedores de IaaS, contenedores e incluso de BBDD. La prueba, aquí.

Cada proveedor tiene su propia forma de crear sus recursos, eso sí. Así por ejemplo, para crear una instancia en AWS se haría con el recurso “aws_instance” y para una instancia en Google Cloud se usaría “google_compute_instance”.

Esto significa que para cada proveedor es necesario un código diferente para hacer prácticamente lo mismo, pero al menos usaremos un único lenguaje (HCL) y herramienta (Terraform).

Una comunidad más activa

La release 0.1.0 de Terraform es de 2014. Por aquella época ya hacía años que existían servicios como Cloudformation para AWS (2011 aprox.) o Heat para Openstack (2012 aprox.). Es normal, por lo tanto, que el nivel de madurez de Cloudformation en 2014 fuera mucho mayor que el de Terraform.

Sin embargo, el concepto de un único lenguaje (HCL) y una única herramienta (Terraform) para integrar el despliegue de recursos en la nube, independientemente del proveedor, supuso un hype tecnológico tal, que la comunidad de desarrollo de Terraform creció muchísimo tanto en número como en actividad.

Esto supuso una maduración y evolución muchísimo más rápida que la del resto de herramientas de Infraestructura como Código (Cloudformation incluida).

Esta es, por lo tanto, una de las grandes fortalezas de Terraform: su comunidad, la gente que hace que cada día se integre con más proveedores y servicios dentro de cada proveedor.

Terraform templates

Terraform templates permite el uso de plantillas con variables al estilo jinja2. Son archivos con extensión “tpl” y las variables se definen como sigue:

#!/bin/bash
echo "CONSUL_ADDRESS = ${consul_address}" > /tmp/iplist

Y para renderizar dicho archivo en un objeto de tipo dato de Terraform usamos “template_file” como sigue.

data "template_file" "init" {
  template = "${file("${path.module}/init.tpl")}"
  vars = {
    consul_address = "${aws_instance.consul.private_ip}"
  }
}

En la versión 0.12 de Terraform, aparece la función templatefile, además del datasource template_file que añade aún más versatilidad.

También podemos definir un directorio lleno de plantillas y usar el recurso  “template_dir” indicando otro directorio donde queremos que deje todas las plantillas renderizadas.

Esta funcionalidad es muy potente a la hora de definir archivos de configuración, definiciones de Swagger… para distintos entornos, y hoy por hoy no hemos encontrado algo parecido en cloudformation.

Terraform import versus Cloudformer

Otra funcionalidad de Terraform, en la que supera exageradamente a Cloudformation, es la importación de recursos ya existentes en la nube a código HCL. Terraform import funciona de la siguiente forma:

Primero tenemos que saber qué tipo de recurso queremos importar (por ejemplo una lambda llamada “prueba_de_lambda”) y añadir la definición del recurso en nuestro archivo de código como sigue:

resource "aws_lambda_function" "example" {
}

A continuación importamos la lambda existente como sigue:

terraform import aws_lambda_function.example prueba_de_lambda

Solo con eso, la lambda pasa a ser un recurso gestionado por Terraform y está en su tfstate (o archivo de estado). Esto significa que, si a continuación borramos la parte de código que define el recurso “example”, la lambda se borraría de nuestra cuenta de AWS.

En Cloudformation, lo más parecido a esto que hay es Cloudformer. Es una funcionalidad que levanta una instancia con esta herramienta en tu cuenta de AWS, ejecuta un trabajo determinado que, como resultado, devuelve una plantilla (en JSON o YAML) con los recursos que existen en esa cuenta para la región que le indiquemos.

El problema es que lleva en beta años (algo habitual en otros proveedores cloud, pero nada normal en AWS). Además, los documentos resultantes no permiten la posterior gestión de esos recursos mediante la modificación del código. Su utilidad es más como de backup o listado de recursos existentes, pero poco más.

Gestión de parámetros y variables

Tanto Cloudformation como Terraform permiten el paso de parámetros al ejecutar la creación de los recursos con comandos del tipo:

terraform plan -var 'entorno=dev'

aws cloudformation create-stack --stack-name desarrollo --template-body file://./desarrollo.template --parameters ParameterKey=entorno,ParameterValue=dev ...

Sin embargo, Terraform permite también pasar los parámetros mediante un fichero de variables con la opción –var-file y archivos con extensión “tfvars”.

Así mismo, podemos usar variables locales definidas en archivos “.tf” directamente. Este abanico de opciones, facilita la gestión de variables según los entornos, por ejemplo.

Aunque, como contrapartida, obliga a asegurar esos ficheros en caso de que contengan variables que puedan comprometer la seguridad de nuestra plataforma.

Módulos de Terraform versus “Nested Stacks”

Tanto Cloudformation como Terraform tienen la posibilidad de agrupar o reutilizar código mediante el uso en el caso de Terraform de los módulos  y en Cloudformation de plantillas anidadas. Sin embargo, el objetivo de ambas opciones es ligeramente diferente:

En el caso de los módulos de Terraform, el objetivo es agrupar los recursos necesarios para crear una unidad funcional de código.

Por ejemplo, tiene sentido incluir en el mismo módulo la creación de una instancia, un grupo de autoescalado, un balanceador y los security groups correspondientes para habilitar el acceso por ssh desde ciertas ips a esa instancia (un ejemplo modelo de máquina bastión en alta disponibilidad).

De esta forma, tendríamos un módulo que nos daría toda esa funcionalidad y recursos desde un solo objeto de Terraform así:

module "bastion" {
    source = "../modules/aws-bastion"
    [...]
} 

Sin embargo, en el caso de Cloudformation y las stacks anidadas, el objetivo es la atomización del código en partes lo más sencillas y reutilizables posibles. Es decir, crear un modelo único de balanceador (por ejemplo) y reutilizar ese stack desde otra que necesitará múltiples stacks sencillas para crear la infraestructura requerida.

En definitiva, son dos formas de afrontar el mismo problema. Lo cierto es que, a la hora de trabajar, las stacks anidadas añaden bastante complejidad para lo que ofrecen.

Sin embargo, en Terraform tenemos módulos como el de VPC, que facilitan tremendamente el trabajo de creación del VPC y configuración de red de un proyecto, aportando a su vez una mayor la sencillez en el código, al sacar toda esa complejidad a un módulo.

Condicionales

Este es uno de los campos donde Terraform pierde estrepitosamente frente a Cloudformation. Básicamente no hay condicionales en Terraform y la única manera de poder o no ejecutar un código, viene basándose en las peripecias de los desarrolladores usando booleanos para simular dichas estructuras.

Aunque se ha prometido que en la versión 0.12 de Terraform se mejorarán estas estructuras, parece increíble que todavía no estén disponibles a estas alturas.

Sin embargo, Cloudformation dispone de funciones que simulan los And, Equals, If, Not y Or lógicos. ¡Punto a favor de Cloudformation!

Terraform workspace

Ya hemos hablado en este mismo blog sobre lo útil que es esta funcionalidad de Terraform para gestionar distintos entornos con un código común y distintos parámetros de entrada.  

Pues bien, como en Cloudformation no disponemos de archivos de variables, conseguir algo parecido requiere más imaginación y resulta especialmente farragoso.

A continuación recogemos algunas opciones que podrían suponer una funcionalidad parecida a  la que ofrece el comando “Terraform workspace” con Cloudformation.

Por un lado, podríamos recoger como valor por defecto de los parámetros de entrada el correspondiente para cada entorno, pero esto supondría duplicar el código para cada entorno (tener un archivo con el código y distintos parámetros de entrada por entorno).

"NombreEntorno": {
      "Description": "Entorno a desplegar",
      "Type": "String",
      "MinLength": "9",
      "MaxLength": "18",
      "Default": "dev"
    }

También podríamos tener un script que defina la creación o actualización de cada entorno con sus parámetros de entrada, e incluso que los parámetros de entrada se basaran en variables de entorno que cargaramos dependiendo del entorno justo antes de la ejecución.

#!/bin/bash
export ENTORNO = dev

aws cloudformation create-stack --stack-name $ENTORNO --template-body file://./codigo.template --parameters ParameterKey=entorno,ParameterValue=$ENTORNO

En definitiva, la funcionalidad de gestión de workspaces de Terraform es bastante fácil de usar y útil a la hora de simplificar el código y gestionar múltiples entornos a la vez con el mismo código. ¡Otro punto para Terraform!

Gestión del estado

Este punto es bastante confuso pues trata de una funcionalidad de Terraform que no está disponible en Cloudformation, pero que posiblemente es mejor que no esté disponible.

Por un lado, en Cloudformation, una vez creados los recursos del stack, vemos un listado de los mismos, pero no podemos hacer nada más con ellos.

Por otro lado, en Terraform, como resultado de la creación de los recursos obtenemos un archivo .tfstate con el listado y características de los recursos creados.

Terraform necesita tener localizado dicho archivo para poder saber qué se creó durante el último “Terraform apply” y si hay modificaciones entre eso y el código actual.

Es decir, claramente, muchas cosas pueden salir mal, especialmente si tenemos dicho tfstate en local, si no está protegido, si no tenemos backup, si lo tocan múltiples personas a la vez…

Digamos que es una funcionalidad envenenada, que aporta un riesgo mayor que las funcionalidades que permite. Sería más seguro si simplemente no fuera editable dicho archivo por el usuario.

De hecho, lo más recomendable es que este archivo se encuentre en algún almacenamiento con protección ante borrado, versionado, backup y cifrado.  

JSON y YAML versus HCL

Este punto está el último de forma intencionada porque realmente es un tema de gusto personal. Hay auténticos fans del Hashicorp Configuration Language, mientras que las personas normales prefieren YAML o JSON.

Lo cierto es que Terraform no usa un lenguaje tan estandarizado como pueden ser JSON y YAML. Como se indicó más arriba, muchos de los puntos de este post se basan en opiniones personales y esta es una de ellas: YAML gana por goleada a HCL, y si encima se puede elegir entre YAML y JSON y transformar automáticamente las plantillas de un lenguaje al otro, Cloudformation en este caso se lleva la palma.  

Conclusión

En definitiva, en Paradigma tenemos claro que una cosa es la herramienta y otra el fin mismo del uso de la misma: nuestro objetivo con el uso de estas herramientas es conseguir infraestructuras robustas, con despliegues predecibles y fácilmente manejables mediante código.

Sin embargo, tener claras las virtudes y defectos de cada una de ellas es fundamental para conseguir este fin de una manera más eficiente y sencilla.  

Esperamos, por lo tanto, que este artículo sea útil para aclarar virtudes y defectos, con el fin de elegir con mayor claridad la herramienta más adecuada en cada momento.

Escribe un comentario