Terraform vs. CloudFormation: la guía definitiva

Publicado: 2022-03-11

Si, como yo, buscó en Internet para ayudarlo a elegir entre CloudFormation y Terraform como su próxima herramienta de infraestructura como código (IaC) sin encontrar una respuesta definitiva, compartí su dolor durante mucho tiempo. Ahora, tengo mucha experiencia con ambas herramientas y puedo tomar una decisión informada sobre cuál usar.

TL;DR

Para su proyecto IaC en AWS, elija CloudFormation porque:

  1. CloudFormation distingue entre código (es decir, plantillas) e instancias del código (es decir, pilas). En Terraform, no existe tal distinción. Más sobre esto en la siguiente sección.
  2. Terraform no maneja muy bien la administración básica de dependencias. Más sobre eso en una sección posterior.

Diferenciar entre código e instanciaciones

Una diferencia entre CloudFormation y Terraform es cómo el código y las instancias se relacionan entre sí dentro de cada servicio.

CloudFormation tiene el concepto de una pila , que es la instanciación de una plantilla. La misma plantilla puede ser instanciada hasta el infinito por un cliente determinado en una cuenta determinada, entre cuentas o por diferentes clientes.

Terraform no tiene tal concepto y requiere una relación uno a uno entre el código y su creación de instancias. Sería similar a duplicar el código fuente de un servidor web para cada servidor que desee ejecutar, o duplicar el código cada vez que necesite ejecutar una aplicación en lugar de ejecutar la versión compilada.

Este punto es bastante trivial en el caso de una configuración simple, pero rápidamente se convierte en un problema importante para las operaciones de mediana a gran escala. En Terraform, cada vez que necesite generar una nueva pila a partir del código existente, debe duplicar el código. Y copiar/pegar archivos de secuencias de comandos es una manera muy fácil de sabotearte y corromper recursos que no tenías la intención de tocar.

En realidad, Terraform no tiene un concepto de pilas como CloudFormation, lo que muestra claramente que Terraform se creó desde cero para tener una coincidencia uno a uno entre el código y los recursos que administra. Más tarde, esto se rectificó parcialmente con el concepto de entornos (que desde entonces han pasado a llamarse "áreas de trabajo"), pero la forma de usarlos hace que sea increíblemente fácil de implementar en un entorno no deseado. Esto se debe a que debe ejecutar terraform workspace select antes de la implementación, y si olvida este paso, se implementará en el área de trabajo previamente seleccionada, que puede ser o no la que desea.

En la práctica, es cierto que este problema se mitiga con el uso de módulos Terraform, pero incluso en el mejor de los casos, se necesitaría una cantidad significativa de código repetitivo. De hecho, este problema era tan grave que la gente necesitaba crear una herramienta envolvente alrededor de Terraform para abordar este problema: Terragrunt.

Gestión de estado y permisos

Otra diferencia importante entre CloudFormation y Terraform es cómo cada uno administra el estado y los permisos.

CloudFormation administra los estados de pila por usted y no le brinda ninguna opción. Pero los estados de pila de CloudFormation han sido sólidos en mi experiencia. Además, CloudFormation permite que los usuarios con menos privilegios administren pilas sin tener todos los permisos necesarios requeridos por la propia pila. Esto se debe a que CloudFormation puede obtener los permisos de un rol de servicio adjunto a la pila en lugar de los permisos del usuario que ejecuta la operación de la pila.

Terraform requiere que le proporcione algunos back-end para administrar estados. El valor predeterminado es un archivo local, que es totalmente insatisfactorio, dado:

  1. La solidez de su archivo de estado está completamente vinculada a la solidez de la máquina en la que está almacenado.
  2. Eso prácticamente hace que el trabajo en equipo sea imposible.

Por lo tanto, necesita un estado sólido y compartido, que en AWS generalmente se logra mediante el uso de un depósito S3 para almacenar el archivo de estado, acompañado de una tabla de DynamoDB para manejar la concurrencia.

Esto significa que debe crear un depósito de S3 y una tabla de DynamoDB manualmente para cada pila que desee crear una instancia, y también administrar los permisos manualmente para estos dos objetos para restringir el acceso de los usuarios con menos privilegios a los datos a los que no deberían tener acceso. Si solo tiene un par de pilas, eso no será un gran problema, pero si tiene que administrar 20 pilas, eso se vuelve muy engorroso.

Por cierto, cuando se utilizan espacios de trabajo de Terraform, no es posible tener una tabla de DynamoDB por espacio de trabajo. Esto significa que si desea que un usuario de IAM con permisos mínimos realice implementaciones, ese usuario podrá jugar con los bloqueos de todas las áreas de trabajo porque los permisos de DynamoDB no están detallados en el nivel de elemento.

Gestión de dependencias

En este punto, tanto CloudFormation como Terraform pueden ser un poco complicados. Si cambia el ID lógico (es decir, el nombre) de un recurso, ambos considerarán que se debe destruir el recurso antiguo y crear uno nuevo. Por lo tanto, generalmente es una mala idea cambiar la identificación lógica de los recursos en cualquiera de las herramientas, especialmente para las pilas anidadas en CloudFormation.

Como se mencionó en la primera sección, Terraform no maneja dependencias básicas. Desafortunadamente, los desarrolladores de Terraform no están prestando mucha atención al problema de larga data, a pesar de la aparente falta de soluciones.

Dado que la gestión adecuada de dependencias es absolutamente crítica para una herramienta IaC, tales problemas en Terraform ponen en duda su idoneidad tan pronto como se involucran operaciones críticas para el negocio, como la implementación en un entorno de producción. CloudFormation brinda una sensación mucho más profesional y AWS siempre está muy atento para asegurarse de que ofrece herramientas de nivel de producción a sus clientes. En todos los años que he estado usando CloudFormation, nunca me encontré con un problema con la administración de dependencias.

CloudFormation permite que una pila exporte algunas de sus variables de salida, que luego pueden ser reutilizadas por otras pilas. Para ser honesto, esta funcionalidad es limitada, ya que no podrá instanciar más de una pila por región. Esto se debe a que no puede exportar dos variables con el mismo nombre y las variables exportadas no tienen espacios de nombres.

Terraform no ofrece tales instalaciones, por lo que le quedan opciones menos deseables. Terraform le permite importar el estado de otra pila, pero eso le da acceso a toda la información en esa pila, incluidos los muchos secretos que se almacenan en el estado. Alternativamente, una pila puede exportar algunas variables en forma de un archivo JSON almacenado en un depósito S3, pero nuevamente, esta opción es más engorrosa: debe decidir qué depósito S3 usar y otorgarle los permisos apropiados, y escribir todos los código de plomería usted mismo tanto en el lado del escritor como en el del lector.

Una ventaja de Terraform es que tiene fuentes de datos. Por lo tanto, Terraform puede consultar recursos no administrados por Terraform. Sin embargo, en la práctica, esto tiene poca relevancia cuando desea escribir una plantilla genérica porque no supondrá nada sobre la cuenta de destino. El equivalente en CloudFormation es agregar más parámetros de plantilla, lo que implica repetición y posibilidad de errores; sin embargo, en mi experiencia, esto nunca ha sido un problema.

Volviendo al problema de la gestión de dependencias de Terraform, otro ejemplo es que obtiene un error cuando intenta actualizar la configuración de un balanceador de carga y obtiene lo siguiente:

 Error: Error deleting Target Group: ResourceInUse: Target group 'arn:aws:elasticloadbalancing:us-east-1:723207552760:targetgroup/strategy-api-default-us-east-1/14a4277881e84797' is currently in use by a listener or a rule status code: 400, request id: 833d8475-f702-4e01-aa3a-d6fa0a141905

El comportamiento esperado sería que Terraform detecte que el grupo de destino es una dependencia de algún otro recurso que no se está eliminando y, en consecuencia, no debería intentar eliminarlo, pero tampoco debería arrojar un error.

Operaciones

Aunque Terraform es una herramienta de línea de comandos, está muy claro que espera que un humano la ejecute, ya que es muy interactiva. Es posible ejecutarlo en modo por lotes (es decir, desde un script), pero esto requiere algunos argumentos de línea de comandos adicionales. El hecho de que Terraform haya sido desarrollado para ser ejecutado por humanos de forma predeterminada es bastante desconcertante, dado que el propósito de una herramienta IaC es la automatización.

Terraform es difícil de depurar. Los mensajes de error a menudo son muy básicos y no le permiten comprender lo que está fallando, en cuyo caso deberá ejecutar Terraform con TF_LOG=debug , lo que produce una gran cantidad de resultados para rastrear. Para complicar esto, Terraform a veces realiza llamadas API a AWS que fallan, pero la falla no es un problema con Terraform. Por el contrario, CloudFormation proporciona mensajes de error razonablemente claros con suficientes detalles para permitirle comprender dónde está el problema.

Un ejemplo de mensaje de error de Terraform:

 Error: error reading S3 bucket Public Access Block: NoSuchBucket: The specified bucket does not exist status code: 404, request id: 19AAE641F0B4AC7F, host id: rZkgloKqxP2/a2F6BYrrkcJthba/FQM/DaZnj8EQq/5FactUctdREq8L3Xb6DgJmyKcpImipv4s=

El mensaje de error anterior muestra un mensaje de error claro que en realidad no refleja el problema subyacente (que en este caso era un problema de permisos).

Este mensaje de error también muestra cómo Terraform a veces puede pintarse a sí mismo en una esquina. Por ejemplo, si crea un depósito S3 y un recurso aws_s3_bucket_public_access_block en ese depósito, y si por alguna razón realiza algunos cambios en el código de Terraform que destruye ese depósito, por ejemplo, en el "cambio implica eliminar y crear" gotcha descrito anteriormente: Terraform se atascará al intentar cargar aws_s3_bucket_public_access_block pero fallará continuamente con el error anterior. El comportamiento correcto de Terraform sería reemplazar o eliminar aws_s3_bucket_public_access_block según corresponda.

Por último, no puede usar los scripts auxiliares de CloudFormation con Terraform. Esto podría ser una molestia, especialmente si espera usar cfn-signal, que le dice a CloudFormation que una instancia EC2 terminó de inicializarse y está lista para atender solicitudes.

Sintaxis, comunidad y reversión

En cuanto a la sintaxis, Terraform tiene una buena ventaja en comparación con CloudFormation: admite bucles. Pero en mi propia experiencia, esta función puede resultar un poco peligrosa. Por lo general, se usaría un bucle para crear una cantidad de recursos idénticos; sin embargo, cuando desee actualizar la pila con un recuento diferente, es posible que necesite vincular los recursos antiguos y nuevos (por ejemplo, utilizando zipmap() para combinar valores de dos matrices que ahora resultan ser de diferentes tamaños porque una matriz tiene el tamaño del bucle anterior y la otra tiene el tamaño del bucle nuevo). Es cierto que tal problema puede ocurrir sin bucles, pero sin bucles, el problema sería mucho más evidente para la persona que escribe el guión. El uso de bucles en tal caso ofusca el problema.

Si la sintaxis de Terraform o la sintaxis de CloudFormation es mejor es principalmente una cuestión de preferencias. CloudFormation inicialmente solo admitía JSON, pero las plantillas JSON son muy difíciles de leer. Afortunadamente, CloudFormation también admite YAML, que es mucho más fácil de leer y permite comentarios. Sin embargo, la sintaxis de CloudFormation tiende a ser bastante detallada.

La sintaxis de Terraform usa HCL, que es una especie de derivado de JSON y es bastante peculiar. Terraform ofrece más funciones que CloudFormation y, por lo general, son más fáciles de entender. Por lo tanto, se podría argumentar que Terraform tiene una ligera ventaja en este punto.

Otra ventaja de Terraform es su conjunto fácilmente disponible de módulos mantenidos por la comunidad, y esto simplifica la escritura de plantillas. Un problema podría ser que dichos módulos no sean lo suficientemente seguros para cumplir con los requisitos de una organización. Por lo tanto, para las organizaciones que requieren un alto nivel de seguridad, la revisión de estos módulos (así como de otras versiones que surjan) puede ser una necesidad.

En términos generales, los módulos de Terraform son mucho más flexibles que las pilas anidadas de CloudFormation. Una pila anidada de CloudFormation tiende a ocultar todo lo que hay debajo. Desde la pila anidada, una operación de actualización mostraría que la pila anidada se actualizará pero no muestra en detalle lo que sucederá dentro de la pila anidada.

Un punto final, que en realidad podría ser polémico, es que CloudFormation intenta revertir las implementaciones fallidas. Esta es una característica bastante interesante, pero lamentablemente puede llevar mucho tiempo (por ejemplo, CloudFormation podría tardar hasta tres horas en decidir que una implementación en Elastic Container Service ha fallado). Por el contrario, en caso de falla, Terraform simplemente se detiene donde sea que haya estado. Es discutible si una función de reversión es algo bueno o no, pero he llegado a apreciar el hecho de que una pila se mantiene en un estado de funcionamiento tanto como sea posible cuando una espera más larga resulta ser una compensación aceptable.

En defensa de Terraform frente a CloudFormation

Terraform tiene ventajas sobre CloudFormation. El más importante, en mi opinión, es que al aplicar una actualización, Terraform le muestra todos los cambios que está a punto de realizar, incluida la profundización en todos los módulos que está utilizando. Por el contrario, CloudFormation, cuando usa pilas anidadas, solo le muestra que la pila anidada necesita actualizarse, pero no proporciona una forma de profundizar en los detalles. Esto puede ser frustrante, ya que es muy importante conocer este tipo de información antes de presionar el botón "ir".

Tanto CloudFormation como Terraform admiten extensiones. En CloudFormation, es posible administrar los llamados "recursos personalizados" mediante el uso de una función AWS Lambda de su propia creación como back-end. Para Terraform, las extensiones son mucho más fáciles de escribir y forman parte del código. Entonces hay una ventaja para Terraform en este caso.

Terraform puede manejar muchos proveedores de nube. Esto coloca a Terraform en una posición de poder unificar una implementación dada entre múltiples plataformas en la nube. Por ejemplo, supongamos que tiene una única carga de trabajo distribuida entre AWS y Google Cloud Platform (GCP). Normalmente, la parte de AWS de la carga de trabajo se implementaría mediante CloudFormation y la parte de GCP mediante Cloud Deployment Manager de GCP. Con Terraform, podría usar un solo script para implementar y administrar ambas pilas en sus respectivas plataformas en la nube. De esta manera, solo tiene que implementar una pila en lugar de dos.

No argumentos a favor de Terraform frente a CloudFormation

Hay bastantes no-argumentos que siguen circulando por Internet. La más importante es que debido a que Terraform es multinube, puede usar una herramienta para implementar todos sus proyectos, sin importar en qué plataforma de nube se realicen. Técnicamente, esto es cierto, pero no es la gran ventaja que puede parecer, especialmente cuando se administran proyectos típicos de una sola nube. La realidad es que existe una correspondencia casi uno a uno entre los recursos declarados en (por ejemplo) CloudFormation y los mismos recursos declarados en un script de Terraform. Dado que debe conocer los detalles de los recursos específicos de la nube de cualquier manera, la diferencia se reduce a la sintaxis, que no es el mayor problema en la gestión de implementaciones.

Algunos argumentan que al usar Terraform, uno puede evitar el bloqueo de proveedores. Este argumento no se sostiene en el sentido de que al usar Terraform, HashiCorp (el creador de Terraform) lo bloquea, de la misma manera que al usar CloudFormation, AWS lo bloquea, y así sucesivamente para otras aplicaciones en la nube. plataformas

El hecho de que los módulos de Terraform sean más fáciles de usar es para mí de menor importancia. En primer lugar, creo que AWS quiere evitar deliberadamente alojar un repositorio único para las plantillas de CloudFormation basadas en la comunidad debido a la responsabilidad percibida por los agujeros de seguridad creados por los usuarios y las infracciones de varios programas de cumplimiento.

A un nivel más personal, entiendo completamente los beneficios de usar bibliotecas en el caso del desarrollo de software, ya que esas bibliotecas pueden ejecutar fácilmente decenas de miles de líneas de código. En el caso de IaC, sin embargo, el tamaño del código suele ser mucho menor, y dichos módulos suelen tener unas pocas docenas de líneas. Usar copiar/pegar en realidad no es tan mala idea en el sentido de que evita problemas para mantener la compatibilidad y delegar su seguridad a personas desconocidas.

Muchos desarrolladores e ingenieros de DevOps desaprueban el uso de copiar/pegar, y hay buenas razones detrás de esto. Sin embargo, mi punto de vista es que usar copiar/pegar para fragmentos de código le permite adaptarlo fácilmente a sus necesidades, y no hay necesidad de crear una biblioteca y dedicar mucho tiempo a hacerlo genérico. El dolor de mantener esos fragmentos de código suele ser muy bajo, a menos que su código se duplique, digamos, en una docena o más de plantillas. En tal caso, apropiarse del código y usarlo como pilas anidadas tiene sentido, y los beneficios de no repetirse son probablemente mayores que la molestia de no poder ver lo que se actualizará dentro de la pila anidada cuando realiza una actualización. operación.

Conclusión de CloudFormation frente a Terraform

Con CloudFormation, AWS quiere brindar a sus clientes una herramienta sólida que funcionará según lo previsto en todo momento. El equipo de Terraform también lo hace, por supuesto, pero parece que un aspecto crucial de sus herramientas, la gestión de dependencias, lamentablemente no es una prioridad.

Terraform podría tener un lugar en su proyecto, especialmente si tiene una arquitectura de múltiples nubes, en cuyo caso los scripts de Terraform son una forma de unificar la gestión de recursos entre los diversos proveedores de nube que está utilizando. Pero aún podría evitar las desventajas de Terraform en este caso si solo usa Terraform para administrar pilas ya implementadas usando sus respectivas herramientas de IaC específicas de la nube.

La sensación general de Terraform frente a CloudFormation es que CloudFormation, aunque imperfecto, es más profesional y confiable, y definitivamente lo recomendaría para cualquier proyecto que no sea específicamente de varias nubes.