Ebook Terraform

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 57

1ª edición

Terraform.
La guía de buenas
Terraform. La guía de buenas prácticas.

prácticas.

1
Atribución 4.0 Internacional

Usted es libre para:

Compartir, copiar y redistribuir el material en cualquier medio o formato.

Adaptar, remezclar, transformar y crear a partir del material para cualquier

propósito, incluso comercialmente.

El licenciante no puede revocar estas libertades en tanto usted siga los

términos de la licencia.

Bajo los siguientes términos:

Atribución Paradigma Digital. Usted debe darle crédito a esta obra de

manera adecuada, proporcionando un enlace a la licencia, e indicando si se

han realizado cambios. Puede hacerlo en cualquier forma razonable, pero no

de forma tal que sugiera que usted o su uso tienen el apoyo del licenciante.

No hay restricciones adicionales: Usted no puede aplicar términos legales ni

medidas tecnológicas que restrinjan legalmente a otros hacer cualquier uso

permitido por la licencia.

V.1.0 - Julio de 2023

http://creativecommons.org/licenses/by/4.0/deed.es
Autores: Adrián Martín García y Juan Mas Aguilar.

Ilustraciones: Marta Ruiz.

Maquetación: David Mota.


Contenido:
— Introducción 06

— 01. Conceptos de Terraform 09

— 02. Organizando Terraform 14

— 03. Escribiendo Terraform 29

— 04. Conclusiones 52

— Autores 54
Terraform. La guía de buenas prácticas.

4
Introducción:
Introducción.

La tendencia hacia la nube y la automatización de la infraestructura


sigue creciendo, y Terraform está muy bien posicionado para seguir
siendo relevante y útil en este contexto. La posibilidad de definir
la infraestructura como código permite una mayor eficiencia en
la creación y mantenimiento de la infraestructura, además de la
posibilidad de automatizar muchos procesos que anteriormente se
hacían de forma manual.

No obstante, los proyectos de infraestructura, como cualquier otro tipo


de proyecto, pueden escalar para adquirir grandes dimensiones. En
ese contexto, cuando ya comenzamos a tener plantillas de código con
muchos recursos, los equipos que las mantienen y las desarrollan pasan
a ser más grandes y el uso de los módulos tiende a la industrialización;
Terraform. La guía de buenas prácticas.

resulta necesario establecer un cómo, quién, cuándo, dónde y por qué.


Partiendo de esta base, inevitablemente surge la necesidad de disponer
de una guía de estilo y buenas prácticas para asegurar la calidad de lo
que se desarrolla.

6
Introducción.

Seguir unas buenas prácticas permite solucionar problemas que


pueden afectar negativamente al uso del código. Cuando hablamos
de buenas prácticas, nos referimos a un conjunto de directrices
recomendadas para escribir código limpio, legible y mantenible.
Son importantes para que sea fácil de entender y mantener por otros
miembros del equipo o para el propio desarrollador en el futuro, y es
de lo que vamos a hablar en este post. Es importante mencionar que
esta guía de estilo está basada en las recomendaciones de actores
importantes como Hashicorp, Google, Microsoft, etc; pero también en
nuestra experiencia en proyectos reales.
Terraform. La guía de buenas prácticas.

7
— 01

Conceptos de
Terraform.
01 — Conceptos de Terraform.

Antes de entrar en materia, es importante conocer los principales


elementos de Terraform.

Resources.
Es el elemento más importante y básico en el lenguaje de Terraform.
Cada definición describe uno o más objetos de infraestructura. Por
ejemplo, una zona DNS o una red virtual.

resource “aws_instance” “web” {

ami = “ami-a1b2c3d4”

instance_type = “t2.micro”

Data source.
Estos elementos permiten acceder a información fuera de Terraform,
definida en otro estado de Terraform, en la propia herramienta, la api
del proveedor o modificada por funciones auxiliares.

data “aws_ami” “web” {

most_recent = true

owners = [“self”]

tags {
Terraform. La guía de buenas prácticas.

name = “state”

values = [“available”]

9
01 — Conceptos de Terraform.

Variable.
Las variables permiten personalizar aspectos de los resources de
Terraform sin alterar el propio código fuente, aportando un nivel de
abstracción superior y facilitando la generalización del código.

resource “aws_instance” “web” {

ami = “ami-a1b2c3d4”

instance_type = var.instance_type

variable “instance_type” {

type = string

description = “Instance type to use for the instance”

default = “t2.micro”

Outputs.
Permiten exponer información sobre la infraestructura en la línea de
comando y posibilita el consumo de esta información desde otras
configuraciones de Terraform.

output “instance_ip_addr” {
Terraform. La guía de buenas prácticas.

value = aws_instance.web.private_ip

10
01 — Conceptos de Terraform.

Locals.
Las variables locales o locals son un tipo de variable computada que
nos permitirá almacenar valores intermedios, que generamos a partir
de las variables de entrada, y los atributos de los elementos que
hayamos creado. Existen solo en el contexto de la ejecución y son
incluidos en el cómputo de dependencias de Terraform.

locals {

instance_ids = concat(aws_instance.web.*.id, aws_instance.

bbdd.*.id)

Providers.
Terraform se basa en complementos llamados providers que
interactúan con proveedores de infraestructura en la nube, proveedores
de SaaS y otras APIs.

La configuración de Terraform debe declarar qué providers requiere


para que puedan ser instalados y usados.

provider “aws” {...}


Terraform. La guía de buenas prácticas.

Terraform se basa en complementos llamados


providers que interactúan con proveedores
de infraestructura en la nube, proveedores de
SaaS y otras APIs.

11
01 — Conceptos de Terraform.

Módulos.
Los módulos son piezas reutilizables de código de Terraform que
permiten a los usuarios definir y compartir recursos y configuraciones
complejas. Los módulos se pueden utilizar para crear abstracciones de
infraestructura y para construir plantillas reutilizables de configuración.

Providers.
Es un fichero de registro de la infraestructura que ha sido creada o
modificada mediante Terraform. El estado se almacena en un archivo
local o remoto y se utiliza para determinar si se deben realizar cambios
en la infraestructura.
Terraform. La guía de buenas prácticas.

12
— 02

Organizando
Terraform.
02 — Organizando Terraform.

Cuando comenzamos un proyecto en el que gestionamos nuestra


infraestructura con Terraform, nos enfrentamos al duro síndrome de la
página en blanco:

• ¿Organizamos el código por stacks? ¿Carpetas? ¿Por servicio?

• ¿Copiamos ejemplos de Internet? ¿O copiamos y pegamos código


de un proyecto anterior?

• Vamos a ser un equipo grande, ¿cómo organizamos el repositorio?


¿Y si somos más de dos personas las que tocamos al mismo
tiempo?

• ¿Cómo gestionamos entornos? ¿Hay promoción? ¿GitOps?

Cualquier código puede escribirse de mil maneras, y se vuelve aún más


complejo cuando el código es mantenido por varias personas.
Terraform. La guía de buenas prácticas.

14
02 — Organizando Terraform.

¿Dónde almacenar el código?


Lo más seguro y útil es tenerlo almacenado en algún sistema de control
de versiones. Existen múltiples servicios como Bitbucket, Gitlab o
Github, incluso los proveedores de nube tienen los suyos propios como
AWS CodeCommit, Azure Repos o Google Cloud Source Repositories.

Almacenarlo en estos sistemas tiene muchos beneficios como


preservar el historial, permitir revisiones sencillas entre los miembros
del equipo o poder aplicar estrategias de ramificación (GitFlow) y
operación (GitOps).

Adicionalmente, tener un repositorio de código como fuente de verdad


de la infraestructura desplegada sirve a dos propósitos adicionales
como bonus: auditoría y documentación.
Terraform. La guía de buenas prácticas.

15
02 — Organizando Terraform.

Ficheros de estado (tfstate).


Todos hemos oído alguna vez frases como: “el fichero tfstate está en
una máquina remota” o “lo tiene una persona en su equipo”.

Es un fichero crítico que no debe almacenarse en cualquier lugar. La


recomendación es utilizar un backend remoto, como AWS S3, Azure
Storage o Google Cloud Storage. Utilizar un backend remoto aporta
beneficios como tener un control de versiones sobre el fichero y
permitir la colaboración entre varios miembros de un equipo.

Datos sensibles.
Algunos recursos necesitan valores sensibles, como podría ser la
contraseña de una base de datos. No obstante, todos los datos de tu
código de Terraform se definen en claro en el fichero tfstate, incluso
cuando añades la propiedad sensitive a Outputs o Variables.

Por ello, suele recomendarse el uso de servicios externos gestionados


como Google Secret Manager, AWS Secret Manager o Azure Key Vault,
y acceder a los valores a través de Data Sources. Sin embargo, ¿cómo
crearemos estos valores? Las opciones pueden ser múltiples:

• Manual. Sabiendo las implicaciones que conlleva la gestión de


recursos de este modo.
Terraform. La guía de buenas prácticas.

• Automatizado. Herramientas como Terraform, Ansible o la propia


CLI. Si utilizamos Terraform, serán almacenados en claro en el
fichero tfstate. Además, los valores también estarán en claro
en el repositorio de versionado en cualquier otra opción de
automatización.

16
02 — Organizando Terraform.

Existe un provider de Terraform para SOPS. Este provider permite


encriptar ficheros con AWS KMS, GCP KMS o Azure Key Vault, de tal
forma que el fichero sólo puede ser desencriptado por quienes tengan
permisos para acceder a las claves de encriptación de los servicios.
Por otro lado, este provider no impide que los datos se definan en claro
en el fichero tfstate, pero combinarlo junto con la encriptación del
backend remoto sigue pareciendo la mejor opción.

Algunos backends soportan locking, que es un sistema para evitar la


edición simultánea por varios usuarios. Además, almacenarlo en estos
servicios aporta un extra de seguridad sobre quién puede acceder
a dicho fichero y, por lo tanto, quién puede crear, editar o destruir
código. Adicionalmente, la mayoría de estos backends suelen disponer
de cifrado en reposo, por lo que añadimos más capas de seguridad que
protegen el estado.

Suele recomendarse el uso de servicios


externos gestionados como Google Secret
Manager, AWS Secret Manager o Azure Key
Vault, y acceder a los valores a través de
Data Sources.
Terraform. La guía de buenas prácticas.

Cifrado.
La encriptación de los backends es algo muy recomendable.
Normalmente se encuentran cifrados en reposo, pero es posible añadir
una capa adicional a través de AWS KMS o GCP KMS.

17
02 — Organizando Terraform.

Organización de repositorios.
Este punto suele ser conflictivo: ¿cómo almacenar los módulos? Las
recomendaciones dicen que, si un módulo no va a ser compartido ni va
a tener un ciclo de vida independiente, lo mejor es que se almacene en
un directorio modules junto al resto del código.

-- REPOSITORY/

-- modules/

-- my-module/

-- main.tf

-- variables.tf

-- outputs.tf

-- provider.tf

-- README.md

-- environments/

-- dev/

-- backend.tf

-- main.tf

-- qa/

-- backend.tf

-- main.tf
Terraform. La guía de buenas prácticas.

-- prod/

-- backend.tf

-- main.tf

18
02 — Organizando Terraform.

Sin embargo, nosotros lo enfocamos de otro modo y lo justificamos a


continuación:

Podemos diferenciar entre dos tipos de módulos según su uso:

• Módulo pensado para ser utilizado de forma generalista e


industrializada, con un ciclo de vida propio. Este tipo de
módulos deben tener un versionado independiente y, por ello,
recomendamos que tengan su propio repositorio/subrepositorio.

• Módulo que resuelve algo muy concreto del proyecto y que no


va a ser compartido. Para este tipo de módulos no es necesario
aplicar un versionado, pues su ciclo de vida y el del código que
lo llama son iguales. Normalmente, son módulos que se crean
para simplificar grandes plantillas en conjuntos funcionales.
Básicamente, nos permiten crear un nivel de abstracción superior
para facilitar la legibilidad y el mantenimiento del código.

Una vez aclarado lo anterior, podremos pensar en la organización de


los repositorios:

• Un repositorio para cada módulo que sea independiente.

• Un repositorio para cada entorno (donde se inicializan los módulos


y se aplica el código). Esto también es matizable, y puede ser
entendido también como un único “repositorio de entorno” que
Terraform. La guía de buenas prácticas.

hace uso de Terraform Workspaces o Terragrunt para desplegar


diferentes versiones.

Realmente no existe una convención estándar en este punto. Las


buenas prácticas apelan más, en este caso, al ajuste del modelo en
varios factores: el día a día del equipo, su forma de trabajar, seguridad,
etc. Simplemente piensa qué resulta más idóneo para tu caso.

19
02 — Organizando Terraform.

Al mismo tiempo, independientemente de los repositorios que crees,


es importante darles una nomenclatura concreta. No será lo mismo
un repositorio para un módulo que un repositorio para un entorno que
consumirá los módulos. Definir una convención de nombres garantiza
un buen orden, para ello podemos obtener los nombres a través de los
valores que consideremos, por ejemplo:

• project/product myproject, myproduct

• area aws, gcp, az, ado, ibm

• type tfmodule, tfenv

• environment dev, pre, pro

• purpose (optional)

Aplicando esta convención, el nombre de los repositorios podrían


verse así:

• myproject-aws-tfmodule-dev-rds

• myproject-az-tfenv-pro

• myproject-gcp-tfmodule-pre-storage
Terraform. La guía de buenas prácticas.

20
02 — Organizando Terraform.

Organización del código.


Con los repositorios definidos, centramos nuestros esfuerzos en el
punto de entrada de nuestro Terraform. Analizar previamente cómo
vas a organizar tu código, evitará que crezca de forma incontrolada,
volviéndose algo difícilmente manejable.

Directorios
Una buena opción es dividir el código en directorios según su orden de
lanzamiento. Si tu intención es tenerlo todo como código, no podrás
crear una AWS EC2 Instance sin antes haber creado una AWS VPC.

├── 000-vpc

└── 010-ec2-instances

No los definas con números consecutivos para dejar espacios libres con
la finalidad de poder añadir nuevos recursos en un futuro.

Analizar previamente cómo vas a


organizar tu código, evitará que crezca
Terraform. La guía de buenas prácticas.

de forma incontrolada, volviéndose algo


difícilmente manejable.

21
02 — Organizando Terraform.

Ficheros
Durante la creación de los ficheros que componen un directorio,
seguiremos el siguiente criterio:

Fichero Propósito

Configuración de Terraform
terraform_config.tf
(Backends, Providers y versión)

data.tf Recursos de tipo Data Source

locals.tf Recursos de tipo Locals

main.tf Recursos de tipo Resources

ouputs.tf Recursos de tipo Ouputs

README.md Documentación

En algún momento puede que sea necesario definir otro tipo de


ficheros distintos de la extensión .tf. Por ello, es importante tener en
cuenta dónde deben almacenarse en el caso de que ocurra.

Scripts
Terraform puede ejecutar scripts con local-exec. Esta función no se
recomienda, excepto cuando Terraform no admita el comportamiento
Terraform. La guía de buenas prácticas.

deseado. Los scripts deberían estar ubicados en el directorio scripts/.

Files
Los ficheros estáticos a los que Terraform hace referencia a través de la
función file() deben estar en el directorio files/.

22
02 — Organizando Terraform.

Templates
Cuando necesitamos hacer uso de la función templatefile(), usar la
extensión .tftpl y almacenarlos en el directorio templates/.

Documentación
Si es necesario añadir documentación adicional, añade ficheros .md al
directorio docs/.

Ejemplos
Si necesita añadir algún tipo de fichero de ejemplo, cree un directorio
por cada ejemplo y un fichero README.md bajo examples/.
Terraform. La guía de buenas prácticas.

23
02 — Organizando Terraform.

Configuración de Terraform.
Versión de Terraform
La versión requerida garantiza que todos los miembros del equipo
y automatizaciones utilicen una versión o un rango de versiones de
Terraform. Esta configuración se define en el fichero terraform_config.tf.

terraform {

required_version = “>= 1.3.8, < 1.4.0”

Como complemento, puedes utilizar la utilidad tfenv para gestionar las


versiones de Terraform mediante un fichero .terraform-version.

1.3.9

Providers
Los providers se definen en el fichero terraform_config.tf en la
inicialización del módulo, nunca dentro del módulo. Es recomendable
definir una versión para evitar problemas de compatibilidad.

terraform {
Terraform. La guía de buenas prácticas.

required_providers {

aws = {

version = “~> 2.13.0”

24
02 — Organizando Terraform.

Backend
El backend es la configuración de dónde se almacena el estado de
nuestro código. Como hemos comentado antes, lo recomendable es el
uso de sistemas remotos de almacenamiento. Esta configuración debe
estar en el fichero terraform_config.tf.

Si utilizamos la estructura de definición de directorios por orden de


lanzamiento, tendremos tantos tfstates como directorios. Defínelos con
el mismo nombre que el directorio en el que se encuentran.

terraform {

backend “s3” {

bucket = “mybucket”

key = “terraform/000-vpc.tfstate”

region = “us-east-1”

terraform {

backend “s3” {

bucket = “mybucket”

key = “terraform/010-ec2-instances.tfstate”

region = “us-east-1”
Terraform. La guía de buenas prácticas.

25
02 — Organizando Terraform.

Ficheros tfvars
Los ficheros .tfvars contienen el valor inicializado de estas variables y
nos van a permitir reutilizar el mismo módulo o plantilla de Terraform
para diferentes casuísticas. Por ejemplo, podríamos tener un fichero
.tfvars para el entorno de desarrollo y otro para el entorno de
producción.

Es importante indicar que, cuando realizamos un plan/apply, Terraform


utilizará el fichero terraform.tfvars por defecto si no definimos el tfvars
de forma explícita con el flag -var-file. Si quisiéramos dividir este fichero
(y si hay muchas variables se recomienda), debemos definir el resto de
ficheros como .auto.tfvars.

Nuevamente, el uso del fichero tfvars se relaciona con la


recomendación del uso del atributo default. Existen ocasiones en
las que es interesante inicializar variables en tfvars aunque tengan el
mismo valor que el de por defecto por un simple tema de legibilidad.

Ejemplo con .tfvars independientes:

.
├── data.tf
├── locals.tf
├── main.tf
├── outputs.tf
Terraform. La guía de buenas prácticas.

├── README.md
├── variables.tf
├── terraform_config.tf
└── workspaces
├── pro_europe.tfvars
└── staging_europe.tfvars

26
02 — Organizando Terraform.

Ejemplo con varios .tfvars para facilitar el mantenimiento:

.
├── data.tf
├── locals.tf
├── main.tf
├── outputs.tf
├── README.md
├── variables.tf
├── terraform_config.tf
├── firewall_rules.auto.tfvars
└── vnet.tfvars

Revisiones de código
Ejecutar terraform plan es una operación que permite hacer una
comparativa entre el código y el estado de la infraestructura escrito
en el fichero tfstate. Al ejecutarlo, mostrará por pantalla los cambios
que se realizarán. Una buena práctica es guardar el resultado de la
ejecución en un fichero, validar los cambios con los miembros del
equipo y ejecutar terraform apply del fichero.

Existen herramientas que automatizan estas acciones:

• Atlantis
Terraform. La guía de buenas prácticas.

• env0

• Scalr

• Spacelift

27
— 03

Escribiendo
Terraform.
03 — Escribiendo Terraform.

Ficheros de recursos.
Normalmente, todos los recursos deben estar en el fichero main.tf. Sin
embargo, es muy recomendable separar por grupos lógicos cuando el
código comienza a crecer demasiado: vpc.tf, instances.tf o dns.tf.

Identificador de recursos.
La convención de los identificadores de recursos es algo que tampoco
se suele respetar, y hay dos recomendaciones principales:

• Utilizar un nombre de identificador con guiones bajos. Permite


delimitar varias palabras y garantizar la coherencia con el nombre
del tipo de recursos. Por ejemplo:

resource “aws_instance” “web-server” {...}

resource “aws_instance” “webServer” {...}

resource “aws_instance” “web_server” {...}

• No añadir información del tipo de recurso en el identificador del


recurso. Por ejemplo:
Terraform. La guía de buenas prácticas.

resource “aws_instance” “web_server_instance” {...}

resource “aws_instance” “web_server” {...}

29
03 — Escribiendo Terraform.

Puede que encuentres recursos con nombres this o main. Puede ser
una buena práctica cuando no se van a añadir nuevos recursos del
mismo tipo, pero la mejor opción es crear un nombre más concreto por
si fuesen necesarios nuevos elementos del mismo tipo en un futuro. Es
cierto que el recurso se podría renombrar con terraform state mv, sin
embargo, si este recurso contiene un for_each, quizás la tarea comienza
a complicarse.

Nombre de recursos.
El nombre de los recursos en el proveedor de nube también es otro de
los temas a tratar. Vemos multitud de recursos que no siguen ningún
patrón o coherencia, y esto puede dificultar el seguimiento y la gestión
de recursos. Del mismo modo que, durante la definición de nombres
de repositorios, es recomendable pensar cómo queremos llamar a
nuestros recursos definiendo una convención:

• project/product myproject, myproduct

• area aws, gcp az, ado, ibm

• environment dev, pre, pro

• resource-type ec2, rgp (Resource Group), sg


(Security Group) o vpc
Terraform. La guía de buenas prácticas.

• sequence 01, 02, 03

• purpose (optional)

30
03 — Escribiendo Terraform.

Los nombres de los recursos en el proveedor podrían verse así:

• myproject-aws-dev-ec2-01-bastion

• myproject-gcp-pre-secretmanager-01-kubernetes

• myproject-az-pro-rgp-01-global

O mejor aún, podemos utilizar un módulo de naming, como el que


existe para Azure, y obtener el nombre de los recursos formateado a
través de un módulo. Podemos incluso añadir validaciones para definir
qué valores están permitidos.

Vemos multitud de recursos que no siguen


ningún patrón o coherencia, y esto puede
dificultar el seguimiento y la gestión de
recursos.

Proteger recursos con estado.


Es conveniente proteger algunos recursos para evitar su destrucción,
como por ejemplo una base de datos.

resource “aws_db_instance” “marketing” {


Terraform. La guía de buenas prácticas.

...

lifecycle {

prevent_destroy = true

31
03 — Escribiendo Terraform.

Tags.
Definir etiquetas es muy útil para la identificación de recursos
gestionados con Terraform, visualizar costes, o realizar acciones por
etiquetas con servicios como AWS SSM. Puedes incluso definir varias
etiquetas y mergearlas en la definición del recurso.

Como referencia, algunos providers de Terraform como el de AWS,


permiten la definición de Tags por defecto, que se aplican de forma
automática sobre todos los recursos desplegados.

variable “tf_tags” {

description = “Terraform tags.”

default = {}

type = “map”

variable “rds_tags” {

description = “AWS RDS Tags.”

default = {}

type = “map”

tf_tags = {

“managed_by” = “Terraform”
Terraform. La guía de buenas prácticas.

rds_tags = {

“team” = “marketing”,

“cost_center” = “eu”,

“backup_plan” = “daily”

32
03 — Escribiendo Terraform.

resource “aws_db_instance” “marketing” {

...

tags = merge(tf_tags, rds_tags)

Algunos providers de Terraform como el


de AWS, permiten la definición de Tags por
defecto, que se aplican de forma automática
sobre todos los recursos desplegados.

Count.
Utiliza la propiedad count para crear recursos de forma condicional
o clones idénticos de un recurso. No se debe usar para recursos
iguales que tengan funcionalidades distintas (ej. máquinas virtuales
con estado).

variable “readers” {

type = list

default = []

}
Terraform. La guía de buenas prácticas.

resource “resource_type” “reference_name” {

count = length(var.readers) == 0 ? 0 : 1

...
}

33
03 — Escribiendo Terraform.

For_each.
Si deseas crear varias copias de un recurso, utiliza for_each.

variable “users” {

type = list(string)

description = “User list”

users = [

[email protected]”,

[email protected]

resource “resource_type” “resource_name” {

for_each = toset(var.users)

username = each.value

}
Terraform. La guía de buenas prácticas.

34
03 — Escribiendo Terraform.

Dynamic blocks.
Algunos tipos de recursos incluyen bloques anidados repetibles
entre sus argumentos, puedes construirlos dinámicamente con
dynamic blocks.

resource “resource_type” “resource_name” {

name = “tf-test-name”

dynamic “setting” {

for_each = var.settings

content {

namespace = setting.value[“namespace”]

name = setting.value[“name”]

value = setting.value[“value”]

Variables.
Las variables se definen en el fichero variables.tf. Proporciona
nombres descriptivos que sean relevantes para su propósito.

Es importante tener muy claro cuál va a ser el uso que se le quiere


Terraform. La guía de buenas prácticas.

dar a la variable, y debemos darle mucha importancia a estas


definiciones. Al final, cuando hablamos de una variable, estamos
hablando de un dato de entrada cuyo valor, a priori, no conocemos.

35
03 — Escribiendo Terraform.

Adicionalmente, debemos tener siempre en cuenta que el fichero


de variables y sus definiciones son la puerta de entrada a nuestro
módulo o plantilla de Terraform, por lo que debemos definir las
variables de forma que un usuario futuro pueda entender y, sobre
todo, comprender la forma de trabajar con él.

Por ello, las variables deberían estar totalmente definidas, tanto en


concepto como en forma. A continuación analizaremos los atributos
que podemos utilizar para este fin.

Description.
Es recomendable establecer una descripción literal en alto nivel.
Vital para el mantenimiento del código y la legibilidad del mismo.
Además, como ya veremos más adelante, nos ayudará a generar
documentación que aporte valor, por lo que se recomienda que se
redacte utilizando sintaxis de Markdown.

Type.
Terraform implementa tipos primitivos (booleanos, cadenas de
caracteres, enteros) y tipos complejos (mapas, objetos y listas).
Definir correctamente el tipo de una variable nos va a permitir
tener mucho nivel de certeza a la hora de aplicar posteriormente
transformaciones, bucles, estructuras de control, etc.
Terraform. La guía de buenas prácticas.

El tipo any es un placeholder genérico y, normalmente, debería


evitarse su uso si la variable se puede definir de una forma más
estricta (variables fuertemente tipadas).

36
03 — Escribiendo Terraform.

Default.
El valor por defecto que tendrá la variable en caso de que no sea
indicada en el momento de la ejecución. Es importante indicar que,
actualmente, se recomienda separar estrictamente la definición de
variables del fichero variables.tf de la inicialización de las variables
del fichero terraform.tfvars. Por lo tanto, el campo default debería ser
utilizado únicamente en aquellas variables que no sean obligatorias o
que no tengan un valor sujeto a cambios relevantes.

En este punto deberíamos colocarnos en el lugar de un usuario


que utilice por primera vez nuestro código para ejecutarlo. Si
inicializamos todas las variables con un valor default, conseguiremos
que ese usuario pueda ejecutar el código de manera inmediata.
Pero, probablemente, estará desplegando recursos con una serie de
presunciones que no aparecen en el fichero .tfvars y, por lo tanto,
se corre el riesgo de desconocerlas. En algunos casos esto es un
comportamiento deseable, dado que pretendemos entregar un
código funcional y entendemos que, en caso de querer modificarlo,
el usuario leerá y entenderá el código.

Esto no siempre es así. Por ello, si nuestro módulo parametriza algún


valor que podamos considerar importante (ej. la versión de un cluster
de Kubernetes), deberíamos dejarlo sin inicializar, de forma que el
usuario esté obligado a revisar esos valores antes del lanzamiento.
Terraform. La guía de buenas prácticas.

Realmente la buena práctica en este punto radica no tanto


en introducir un valor por defecto o no, sino en instrumentar
las variables de forma ajustada al uso que se vaya a hacer
posteriormente del módulo y el público al que esté dirigido.

37
03 — Escribiendo Terraform.

Validation.
El bloque de validación nos va a permitir definir una serie de reglas
que nuestras variables deben cumplir para ser consideradas válidas.
Por ejemplo, podríamos definir que un valor tiene que pertenecer a
un enumerado, que un entero debe estar entre ciertos valores o que
una cadena debe cumplir con una expresión regular determinada
(muy útil para construir nombres).

variable “env” {

description = “Environment.”

type = string

default = “pre”

validation {

condition = contains([“pre”, “pro”], var.env)

error_message = “The environment must be one of the

following: `pre` or `pro`.”

Locals.
El uso de variables locales se recomienda para mejorar la legibilidad
y reducir la complejidad del código (guardar el resultado de
transformaciones complejas, renombrar una referencia poco legible
Terraform. La guía de buenas prácticas.

a una variable que sea más explícita, calcular una expresión una sola
vez para poder usarla en varios puntos del código, etc).

38
03 — Escribiendo Terraform.

Outputs.
Los outputs serán consistentes y entendibles fuera de su alcance.
Deben definirse claramente los atributos y valores que devuelve, así
como una descripción, por muy obvio que parezca.

Tener una convención de nombres para outputs, al igual que para


el resto de recursos, es una buena práctica. Por ejemplo: {nombre}_
{atributo}. Incluso tener en cuenta si devuelve más de un valor para
añadirlo en plural o singular.

Los outputs siempre deben obtener los valores de los recursos y


nunca de variables.

Por ejemplo:

output “instance_name” {

description = “Name of instance”

value = var.name

output “instance_name” {

description = “Name of instance”

value = aws_instance.web.name

}
Terraform. La guía de buenas prácticas.

39
03 — Escribiendo Terraform.

Sensitive.
Si se exponen datos sensibles en Outputs, es recomendable
marcarlo como sensitive. Terraform ocultará los valores durante las
ejecuciones terraform plan y terraform apply.

output “db_password” {

value = aws_db_instance.db.password

description = “The password for logging in to the

database.”

sensitive = true

Changes to Outputs:

+ db_password = (sensitive value)

Data sources
Del mismo modo que en el resto de elementos, utiliza nombres
claros y descriptivos.
Terraform. La guía de buenas prácticas.

40
03 — Escribiendo Terraform.

Ordenación de recursos
Este apartado consiste en una declaración ordenada de elementos.
Cuanto más ordenado esté, mejor será su entendimiento para todos.

Dentro de cada fichero, es importante definir un orden de elementos


que garantice la coherencia y la relación entre ellos. Un ejemplo sería
el de definir primero la VPC y, posteriormente, las Subnets.

resource “aws_vpc” “main” {...}

resource “aws_subnet” “public” {...}

resource “aws_subnet” “private” {...}

Pero del mismo modo ocurre en otros ficheros.

output “vpc” {

description = “Show AWS VPC ID”

value = aws_vpc.main.id

output “public” {

description = “Show AWS Public Subnet Id”

value = aws_subnet.public.id

output “private” {
Terraform. La guía de buenas prácticas.

description = “Show AWS Private Subnet Id”

value = aws_subnet.private.id

41
03 — Escribiendo Terraform.

Dependencias
Cuando Terraform genera un plan de cambios, también genera un
grafo de dependencias entre los recursos que hemos especificado
(es posible verlo mediante el comando terraform graph). Estas
dependencias se pueden definir de dos formas: implícitas o
explícitas.

Esta es la forma más sencilla y limpia de


definir dependencias, dejando el código lo
más plano posible y sin introducir elementos
“artificiales”.

Las dependencias implícitas son dependencias que el propio


Terrraform es capaz de inferir de forma autónoma al procesar el
código que hemos desarrollado. Esta es la forma más sencilla y
limpia de definir dependencias, dejando el código lo más plano
posible y sin introducir elementos “artificiales”. Imaginemos un
security group que necesita de una vpc para existir, y así queda
reflejado en el campo vpc_id del recurso aws_security_group
mediante la referencia al identificador devuelto por el recurso aws_
vpc. Esto último es muy importante. Si en vez de usar la referencia
del valor devuelto por el recurso, usáramos la misma variable que
Terraform. La guía de buenas prácticas.

hemos utilizado para darle nombre a la vpc, Terraform no detectaría


la dependencia, ya que no procesa los valores literales.

Por otro lado, las dependencias explícitas son aquellas que nosotros
le indicamos a Terraform de forma literal. Por ejemplo, si nuestra
instancia EC2 ejecuta un startup script que lee un fichero de S3.
Realmente no existe dependencia de infraestructura entre la
42
03 — Escribiendo Terraform.

EC2 y el S3 para que Terraform pueda inferirla (lo que sí ocurría


con el ejemplo anterior), por lo que debemos informarle que tal
dependencia existe con la directriz depends_on.

Las buenas prácticas siempre apuntan a no definir dependencias


explícitas si no es estrictamente necesario. A continuación, vemos
un ejemplo en el que se usa una dependencia explícita de forma
errónea, dado que puede definirse de la misma manera de forma
implícita:

module “vpc” {

source = “terraform-aws-modules/vpc/aws”

version = “3.19.0”

name = “my_vpc”

cidr = “10.0.0.0/16”

azs = [eu-west-1a, eu-west1b, eu-west-1c]

private_subnets = []

public_subnets = []

module “implicit_security_group” {

source = “terraform-aws-modules/security-group/aws”
Terraform. La guía de buenas prácticas.

name = “my_sg”

description = “Demo security group”

vpc_id = module.vpc.vpc_id #### DEPENDENCIA

IMPLICITA

43
03 — Escribiendo Terraform.

ingress_cidr_blocks = [module.vpc.vpc_cidr] ####

DEPENDENCIA IMPLICITA

ingress_rules = [“https-443-tcp”]

module “explicit_security_group” {

source = “terraform-aws-modules/security-group/aws”

name = “my_sg”

description = “Demo security group”

vpc_id = “my_vpc”

ingress_cidr_blocks = [“10.0.0.0/16”]

ingress_rules = [“https-443-tcp”]

depends_on = [ module.vpc ] #### DEPENDENCIA EXPLÍCITA

Las dependencias bien definidas evitarán condiciones de carrera,


permiten aprovechar al máximo la paralelización de la ejecución
y aseguran el orden del despliegue de forma que podemos evitar
muchos errores.
Terraform. La guía de buenas prácticas.

44
03 — Escribiendo Terraform.

Comentarios
Añade comentarios que ayuden a entender el código. Aunque
Terraform es autodescriptivo, puede utilizarse para marcar conjuntos
de recursos y anidaciones. Hay múltiples formas de establecer
comentarios.

• # comentario en una sola línea.

• // comentario en una sola línea.

• /*y*/ comentario en varias líneas.

# Main resources

## AWS VPC

resource “aws_vpc” “main” {...}

## AWS Subnets

### Public Subnet

resource “aws_subnet” “public” {...}

### Private Subnet

resource “aws_subnet” “private” {...}


Terraform. La guía de buenas prácticas.

Puedes tener la tentación de comentar código porque actualmente


no se está utilizando y subirlo al repositorio. Evita añadir código para
no dificultar el entendimiento y lectura. Adicionalmente, tampoco hay
que abusar de los comentarios, es importante que el código sea lo más
autoexplicativo posible mediante nombres de variables significativos y
recursos sencillos de leer. En el término medio está la virtud.
45
03 — Escribiendo Terraform.

Módulos
Durante la escritura de módulos, es posible aplicar la gran mayoría
de buenas prácticas definidas en este documento. Sin embargo, es
conveniente hacer algunos apuntes adicionales:

No debe haber referencias a recursos de tipo Data Source que no


estén parametrizados, salvo que sean referentes a datos sobre la
conexión con el proveedor (por ejemplo, la identidad que invoca
el Terraform). Es decir, si se necesita obtener el identificador de
un recurso a partir del nombre, se solicita directamente el nombre
como variable (eliminando directamente la necesidad de usar el
Data Source) o se parametriza el identificador para mantener la
generalidad.

No se deben realizar llamadas a otros módulos desde un módulo.


Esto introduce complejidad en el mantenimiento y puede ocasionar
problemas si ambos módulos tienen ritmos de versionado distintos.
En caso de querer enlazar dos módulos distintos, se debe realizar
la primera llamada al módulo desde el main.tf principal y, con los
outputs que devuelva, invocar al segundo módulo. Por tanto, los
módulos deben ser atómicos.

No debe haber referencias a recursos de tipo


Data Source que no estén parametrizados,
Terraform. La guía de buenas prácticas.

salvo que sean referentes a datos sobre la


conexión con el proveedor.
Idealmente, y en la medida de lo posible, también se debería
incluir algún ejemplo de invocación y una descripción general en la
documentación.

46
03 — Escribiendo Terraform.

Versiones
Después de lo que comentamos al principio del documento
sobre los dos tipos de módulos, veremos cómo establecer una
política de versionado. Esto nos permite, por un lado, mantener la
retrocompatibilidad cuando se introducen nuevos cambios y, por
otro, la posibilidad de aportar flexibilidad en el uso de módulos
(versiones de Terraform, del provider, etc). Los módulos publicados
en un registry oficial siempre van asociados a una versión en
concreto. Dicha versión se puede especificar en la llamada al
módulo:

module “mymodule” {

source = “azure/mymodule”

version = “3.1.1”

No obstante, los módulos privados o publicados en repositorios git


deben incluir la versión en el parámetro ref de la invocación HTTP:

module “mymodule” {

source = “git::<URL>/myorg/myproject/myrepo?ref=v3.1.1”

Puede establecerse una política de versionado de módulos basada


Terraform. La guía de buenas prácticas.

en los principios del Semantic Versioning.

47
03 — Escribiendo Terraform.

Formatear código
El comando terraform fmt se utiliza para dar formato a los ficheros de
configuración de Terraform.

Por ejemplo:

variable “budget” {

type = list(object(

name = string

subscription_id = string

amount = number

contact_emails = list(string)

))

description = “Azure subscription budgets”

variable “budget” {

type = list(object(

name = string

subscription_id = string
Terraform. La guía de buenas prácticas.

amount = number

contact_emails = list(string)

))
description = “Azure subscription budgets”

48
03 — Escribiendo Terraform.

Validación de código
Para la validación de código existen múltiples opciones, como las
incluidas en el propio binario de Terraform con el comando validate.

Otras opciones externas serían:

• Tflint

• Tfsec

• OPA

• Terratest

• Kics

Documentación
Acompaña tu código con documentación. Existen herramientas
como terraform-docs para la generación del contenido de los
ficheros README.md.

Es posible la definición de un fichero .terraform-docs.yml para crear


la documentación bajo la configuración declarada en el fichero.

formatter: “”
Terraform. La guía de buenas prácticas.

version: “”

header-from: main.tf

footer-from: “”

recursive:

enabled: false

path: modules

49
03 — Escribiendo Terraform.

sections:

hide: []

show: []

content: “”

output:

file: “”

mode: inject

template: |-

<!-- BEGIN_TF_DOCS -->

{{ .Content }}

<!-- END_TF_DOCS -->

output-values:

enabled: false

from: “”

sort:

enabled: true

by: name

Comando state
Terraform. La guía de buenas prácticas.

El formato de los ficheros tfstate es JSON, pero se desaconseja la


edición directa de estos ficheros. El comando terraform state permite
realizar operaciones como listar, renombrar o eliminar recursos.

50
— 04

Conclusiones.
04 — Conclusiones.

Esta guía pretende dar algunas recomendaciones para poder crear


proyectos de Terraform que puedan ser utilizados en proyectos grandes
y pequeños, centrado sobre todo en la operación y sencillez para los
equipos que desarrollan el código y los que lo operan. Y, de esta forma,
permitir que un equipo sea verdaderamente ágil en el desarrollo.

Al final, estas buenas prácticas no dejan de ser recomendaciones


y lo más importante es aplicar el sentido común y adaptarlas a tu
caso particular. Las combinaciones son enormes y en cada casa se
trabaja de una manera distinta pero, al final, la gran mayoría de estas
recomendaciones son bastante estándar en la industria.

Dicho lo cual: happy coding ;)


Terraform. La guía de buenas prácticas.

52
Terraform. La guía de buenas prácticas.

Autores:
53
Adrián
Martín García.
Cloud Engineer.

Siempre he sido bastante curioso con la tecnología,


me encanta aprender y la evolución constante de la
tecnología es mi principal motivación. Durante los
últimos años, me he centrado en infraestructuras
Cloud, automatización y contenedores.
Terraform. La guía de buenas prácticas.

54
Juan
Mas Aguilar.
Cloud Engineer.

Curioso, motivado y con más preguntas que


respuestas en el vasto mundo de la arquitectura de
sistemas. Disfruto investigando nuevas formas de
hacer las cosas con el objetivo de hacer la vida fácil a
todas las personas que usan las plataformas.
Terraform. La guía de buenas prácticas.

55
Think Big.

V.1.0 - Julio de 2023

[email protected]

También podría gustarte