Guía para la gestión de Terraform en AWS: Obsolescencia, compliance y buenas prácticas

En la segunda parte de este artículo hablamos sobre el CI/CD y autoservicios con Terraform. En este artículo continuaremos con los siguientes aspectos a tener en cuenta.

Obsolescencia

Uno de los aspectos más complicados en la gestión de la IaC en grandes entornos empresariales es la actualización de versiones en módulos/blueprints ya desplegados.

La problemática se da cuando los equipos de arquitectura liberan nuevas versiones de módulos/blueprints con mejoras importantes de seguridad o funcionales, pero esas nuevas versiones no se acaban implantando en proyectos ya desplegados.

Esto es debido a que para aplicar estos cambios se requieren acciones en los diferentes repositorios de código IaC para cambiar la versión. Realizar el cambio por el equipo de arquitectura en todos los repositorios es inabordable y si se delega en el propio equipo de cada proyecto no suele dar buenos resultados porque en muchas ocasiones no llega la información de las nuevas versiones y en otras no se le da prioridad porque no son requerimientos de negocio.

Para dar solución podemos adoptar varias alternativas, cada una de ellas con sus ventajas en inconvenientes:

  • Apuntar a versiones latest o rama main: La idea de esta estrategia es que siempre que invoquemos a un módulo/blueprint se apunte al tag/versión latest o la rama main (dependiendo de la política de branching) para que siempre que se aplique la IaC se coja la última versión del módulo/blueprint. Esta solución presenta la ventaja de que la actualización no requiere modificación de código y es obligatoria, sin embargo presenta muchas desventajas como que la actualización no es inmediata ya que requiere relanzar la IaC, que si hay incompatibilidad con una nueva versión no lo puedes solucionar y el principal que no se controla cuando se hace el cambio y podrías meterlo en producción antes que en entornos previos.
  • Facilitar la actualización: La idea de esta estrategia es que en el momento en que se liberen nuevas versiones de los módulos/blueprints se identifique en que repositorios de IaC se están usando (para los módulos) o cuantos despliegues hay de los mismos (para los blueprints). Esta identificación es sencilla si estamos usando un enfoque que utilice una Internal Developer Platform (a partir de ahora IdP), ya que esa información está en bbdd y tirar esas querys es relativamente sencillo. Para un enfoque usando repositorios de git esto se complica ya que no tenemos una forma efectiva de realizar esta búsqueda. Para poder efectuarla, en los pipelines mencionados en apartados anteriores deberíamos añadir un stage que lo que haga se persistir esta información en formato json dentro de algún otro repositorio de forma que se pueda consultar (o incluso en una base de datos). El siguiente paso sería modificar donde corresponda, si tenemos una estrategia sin repositorios live esto es sencillo ya que esa información se guarda en el repositorio de configuración. En el caso de utilizar un IdP esa info estará en la bbdd. Si utilizamos la estrategia de repositorios live esto se complica ya que hay que buscar en todos los ficheros donde se usan y cambiar la línea de código correspondiente. Lo siguiente sería generar un pull request a la rama correspondiente con los cambios realizados. De esta forma los equipos de desarrollo solo tienen que aceptar el pull request. Las ventajas de este método es la facilidad que se le da a los equipos de desarrollo para aplicar los cambios y la posibilidad de hacerlo de forma controlada. Una posibilidad adicional es que en los entornos de desarrollo se haga un commit en vez de un pull request para que se desplieguen y se prueben obligatoriamente los cambios y que sean más conscientes de la necesidad de alienar entornos.

En cualquier caso, es conveniente revisar periódicamente el estado de esta obsolescencia o automatizar la generación de reportes con esta información.

Compliance

Otro de los aspectos más complicados en la gestión de la IaC en grandes entornos empresariales es el compliance en cuanto a nomenclaturas y tags de recursos.

La problemática se da cuando muchos equipos generan recursos sin el uso de los módulos unitarios o cuando los módulos unitarios no tienen control sobre nomenclatura/tags de los recursos, porque, aunque haya documentación que defina la nomenclatura suele ocurrir que los equipos no conocen ese documento o que no se aplica bien.

Para gestionar estos temas es fundamental que existan módulos unitarios para la generación de recursos individuales y que no se creen recursos fuera de estos módulos unitarios (salvo excepciones).

Partiendo de lo anterior tenemos varias formas de lograr el compliance:

  • Definición en el módulo: La idea de esta estrategia es que en los módulos unitarios las nomenclaturas de los recursos no se deleguen al valor que se pase en una sola variable “name”, sino que se construyan en base a varias variables que conformen una estructura completa de nomenclatura como por ejemplo proyecto, identificador, entorno, etc. Del mismo modo se pueden definir los tags que debe tener cada recurso. Las ventajas de este método es unificación a nivel de recursos y una gestión sencilla del compliance. Las desventajas son que los tags no están unificados entre recursos de diferente tipología y que para cambiar la nomenclatura habría que sacar una nueva versión.
  • Definición en elementos externos: La idea de esta estrategia es que exista algún elemento externo (por ejemplo, AWS Parameter Store) donde se centralice por un lado las nomenclaturas de cada recurso y los tags globales que se deban aplicar. En los módulos unitarios se debe consultar a este elemento externo para saber la estructura de nomenclatura correspondiente a dicho recurso. Esta estructura debe estar basada en variables que vayan a existir en todos los repositorios de IaC. Este método presenta bastantes ventajas como la unificación de nomenclaturas a nivel de recursos, la estandarización de los tags obligatorios para todos los recursos, facilidad para cambiar la nomenclatura sin tocar código y centralización en un único lugar de todas estas configuraciones. Como desventaja podríamos indicar que no consigue que los cambios que apliques en la parte de configuración se actualicen automáticamente, pero eso es algo que no debería ser frecuente.

Integración/dependencias diferentes estructuras de IaC

Los repositorios de IaC deben tener un alcance limitado en número de recursos ya que, si creas demasiados recursos en una misma configuración, ésta se vuelve inmanejable. También hay que tener en cuenta el ámbito de responsabilidad de los recursos del repositorio de IaC, es decir, cada repositorio debería ser responsabilidad de un equipo, no de varios.

Sin embargo, tampoco debemos menospreciar la capacidad de dependencia de recursos conectados entre sí que nos da terraform, de modo que si un recurso cambia los recursos dependientes reciben esos cambios automáticamente.

En este punto una posible estrategia de segregación son repositorios cross/comunes y repositorios específicos, separados por ámbito de responsabilidad. Los repositorios cross o comunes crearán los recursos compartidos por las aplicaciones como elementos de red, balanceadores, etc. Los repositorios específicos crearán recursos para una arquitectura/aplicación concreta.

En este sentido los repositorios de tipo específico deberán hacer uso de los recursos creados por los repositorios de tipo común. Sin embargo, al haber separado los recursos en varios repositorios de IaC se pierde la capacidad de que, si un elemento común cambia, se actualicen los elementos específicos relacionados con el común. Es necesario realizar una integración.

Esta integración puede hacerse de 2 formas:

  • Variables: Los repositorios específicos tendrán variables para indicar los identificadores de los elementos comunes necesarios. Este método presenta la ventaja de que es sencillo, pero conlleva mucho “harcodeo” de información.
  • Almacén externo: Los repositorios comunes dejarán indicados en elementos externos los identificadores que puedan ser necesitados por otros repositorios. Los repositorios específicos leerán (mediante un “data”) la información necesaria de estos almacenes externos. Este método es algo más complejo de implementar, pero otorga la gran ventaja de que, si cambia un elemento común, no hay que tocar nada en los repositorios específicos y con un simple redespliegue quedaría todo conectado otra vez. Si tuviésemos un árbol de dependencias entre repositorios se podría incluso automatizar ese redespliegue de los repositorios específicos. Terraform Cloud tiene una capacidad específica para estos casos llamada “Terraform Stacks”. Respecto al almacén externo tenemos varias posibilidades, desde parámetros en AWS Parameter Store, hasta leer directamente del State de los repositorios comunes.

Buenas prácticas

Adicionalmente a todo lo que hemos comentado podemos añadir una serie de buenas prácticas que no hemos mencionado:

  • Utilizar una estructura de ficheros constante en todos los repositorios. Por ejemplo 01-variables.tf, 02-locals.tf, 03-providers.tf, 04-main.tf, 99-outputs.tf, etc.
  • Usar “.gitignore” para no commitear ficheros innecesarios como ficheros de state, módulos, etc.
  • Formatear ficheros de terraform.
  • Generar Readme de los repositorios.
  • Evitar hardcodear valores.
  • Hacer backup de los states.
  • Usar las built-in functions de terraform.
  • Solo interactuar con el cloud a través de terraform, es decir, no crear cosas a mano.
  • No incluir credenciales en el código.
  • Usar tflint.
  • Importar recursos si son necesarios.
  • Usar bucles en vez de repetir código.
  • Usar elementos “dynamic” en vez de repetir código.
  • Mantener actualizada la versión de terraform y los providers.
  • Ejecutar test donde se pueda.

Descubre las partes anteriores a este artículo:

Referencias

webinar AWS

Tags

Guía de posibilidades profesionales sobre AWS
He leído y acepto la política de privacidad
Acepto recibir emails sobre actividades de recruiting NTT DATA