Ebook Terraform
Ebook Terraform
Ebook Terraform
Terraform.
La guía de buenas
Terraform. La guía de buenas prácticas.
prácticas.
1
Atribución 4.0 Internacional
términos de la licencia.
de forma tal que sugiera que usted o su uso tienen el apoyo del licenciante.
http://creativecommons.org/licenses/by/4.0/deed.es
Autores: Adrián Martín García y Juan Mas Aguilar.
— 04. Conclusiones 52
— Autores 54
Terraform. La guía de buenas prácticas.
4
Introducción:
Introducción.
6
Introducción.
7
— 01
Conceptos de
Terraform.
01 — Conceptos 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.
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.
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.
ami = “ami-a1b2c3d4”
instance_type = var.instance_type
variable “instance_type” {
type = string
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 {
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.
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.
14
02 — Organizando Terraform.
15
02 — Organizando Terraform.
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.
16
02 — Organizando Terraform.
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.
19
02 — Organizando Terraform.
• purpose (optional)
• 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.
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.
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)
README.md Documentación
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.
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 {
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 = {
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.
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.
.
├── 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.
.
├── 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.
• 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:
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:
• purpose (optional)
30
03 — Escribiendo Terraform.
• myproject-aws-dev-ec2-01-bastion
• myproject-gcp-pre-secretmanager-01-kubernetes
• myproject-az-pro-rgp-01-global
...
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.
variable “tf_tags” {
default = {}
type = “map”
variable “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.
...
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.
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)
users = [
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.
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.
35
03 — Escribiendo Terraform.
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.
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.
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 {
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.
Por ejemplo:
output “instance_name” {
value = var.name
output “instance_name” {
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
database.”
sensitive = true
Changes to Outputs:
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.
output “vpc” {
value = aws_vpc.main.id
output “public” {
value = aws_subnet.public.id
output “private” {
Terraform. La guía de buenas prácticas.
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.
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.
module “vpc” {
source = “terraform-aws-modules/vpc/aws”
version = “3.19.0”
name = “my_vpc”
cidr = “10.0.0.0/16”
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”
IMPLICITA
43
03 — Escribiendo Terraform.
DEPENDENCIA IMPLICITA
ingress_rules = [“https-443-tcp”]
module “explicit_security_group” {
source = “terraform-aws-modules/security-group/aws”
name = “my_sg”
vpc_id = “my_vpc”
ingress_cidr_blocks = [“10.0.0.0/16”]
ingress_rules = [“https-443-tcp”]
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.
# Main resources
## AWS VPC
## AWS Subnets
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:
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”
module “mymodule” {
source = “git::<URL>/myorg/myproject/myrepo?ref=v3.1.1”
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)
))
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.
• 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.
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: |-
{{ .Content }}
output-values:
enabled: false
from: “”
sort:
enabled: true
by: name
Comando state
Terraform. La guía de buenas prácticas.
50
— 04
Conclusiones.
04 — Conclusiones.
52
Terraform. La guía de buenas prácticas.
Autores:
53
Adrián
Martín García.
Cloud Engineer.
54
Juan
Mas Aguilar.
Cloud Engineer.
55
Think Big.