Post MultiCloud - Terraform Remote State.
Configurando TRS en AWS y Azure
Objetivos
Configurar los recursos cloud necesarios en AWS y Azure, para trabajar con Terraform utilizando remote state.
Intro
Soy un gran fanático de Terraform, personalmente pienso que Terraform es lo mejor que le paso al mundo IaC, al menos hasta ahora, poder tener documentación viva de tu infraestructura y configuraciones, correrla, matarla, modificar, versionarla, iterarla etc es algo genial, y existen muchas herramientas de IaC que nos ofrecen esa posibilidad, pero Terraform nos añade un plus más, es extremadamente fácil de utilizar, se puede aprender lo básico en una tarde y comenzar a levantar recursos cloud de inmediato. Como deben saber si es que están leyendo este post, unos de los típicos problemas que se presentan al utilizar Terraform en un equipo de más de un cloud/devops eng, es el manejo del tfstate file, en donde Terraform almacena su current state, una solución, es almacenar dicho archivo en un storage que sea accesible para todo el team, de esta manera, nos evitaremos inconsistencias y errores con respecto a nuestro código y a nuestros recursos cloud ya levantados. Vamos a setear esto en dos de los Cloud Providers más utilizados actualmente: AWS y Azure.
Pre-requisitos:
- Conocimiento y experiencia utilizando Terraform.
- Conocimiento básico y una cuenta de Azure con acceso al portal y az CLI
- Conocimiento básico y cuenta AWS con acceso programático y también a la consola
TRS en Azure
Storage Account y Container con AZ CLI
Dentro de Azure, cualquier recurso cloud que creemos vive dentro de un Resource Group, y particularmente todo lo que se refiere a almacenamiento de tipo data object, pertenece a una Storage Account, desde la data de tu cloudshell hasta tus blobs, disks, queues etc., esta storage account nos ofrece un namespace único en Azure para nuestra info, dentro de esta storage account vamos a crear un container (no estoy hablando de Docker) que en pocas palabras sería algo así como un directorio, luego, toda esta información la vamos a pasar a Terraform para que guarde el tfstate dentro de este “directorio” en el cloud.
En primer lugar nos logueamos a Azure con:
$ az login --username TU-USERNAME --password TU-PASS
Si contamos con más de una suscripción, podemos listarlas y seleccionarlas con los siguientes comandos:
$ az account list
$ az account set --subscription 'NAME'
Creamos un RG por ej en eastus:
$ az group create --location eastus --name tfstaterg
Ahora creamos el SA y el Container dentro:
$ az storage account create --name tfstorage --resource-group tfstaterg --location eastus
$ az storage container create --name tfcont --account-name tfstorage
Podemos verificar todo el trabajo con:
$ az storage account keys list -g tfstaterg -n tfstorage
También podemos verificarlo desde Azure Portal y ver los recursos que acabamos de crear con AZ CLI.
Listo la parte de Azure, ahora solamente tenemos que pasar toda esta info a Don Terraform para que pueda tomar control de la storage account y escribir/leer el tfstate dentro del container.
Terraform + Azure.
En nuestro tf file, simplemente hay que referenciar y setear el tf backend como azurerm y pasarle la data necesaria para que Terraform se conecte:
terraform {
backend "azurerm" {
resource_group_name = "tfstaterg"
storage_account_name = "tfstorage"
container_name = "tfcont"
key = "dev.blog.tfstate"
}
}
Esto si ya estamos logueados con az cli, por supuesto si tuvieramos esto en un pipeline dentro de gitlab-ci, azure devops o jenkins, podemos también automatizar el login de varias maneras, una de ellas podría ser pasandole a Terraform el tenant y el subsc ID, de la siguiente manera:
provider "azurerm" {
tenant_id = "__tenantId__"
subscription_id = "__subscription_id__"
}
En el ejemplo anterior, estoy pasando esa info con env var, pero tranquilamente se lo podría definir como variables protegidas dentro de cualquier tool de CI/CD o incluso hacer que Terraform lea estos datos directamente desde un key vault algún paso previo dentro de algún pipeline por ejemplo.
Listo, ya tenemos configurado nuestro Terraform con Remote State hacia Azure Cloud.
TRS en AWS
Bucket S3, Policies y Versioning con AWS CLI
En AWS necesitamos simplemente un Bucket S3 para que Terraform pueda escribir/leer el tfstate remoto, opcionalmente se puede configurar el tf backend contra DynamoDB lo cual esta muy copado porque con esta opción tenemos state locking y chequeo de consistencia pero eso será para otro post, ahora vamos con S3.
Como siempre AWS nos provee de features geniales dentro de sus services, así que vamos a aprovechar una de ellas dentro del servicio S3, luego de crear el bucket y setear las policies, vamos a habilitar el versionado en nuestro bucket, para evitar tener problemas de capa 8 jeje, de esta forma podremos recuperar nuestro tfstate remoto ante cualquier borrado accidental o perdida masiva de datos.
Creamos nuestro bucket por ejemplo en el norte de Virginia:
$ aws s3api create-bucket --bucket tfbucket --region us-east-1
La Policy atachada al Role del User IAM con el que vamos a correr nuestro Terraform (ya sea desde nuestra shell o desde alguna tool de CI/CD) debe tener esta facha:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::tfbucket"
},
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::tfbucket/tf/dev.blog.tfstate"
}
]
}
Y como mencionamos anteriormente, prendemos el versionado en nuestro bucket:
$ aws s3api put-bucket-versioning --bucket tfbucket --versioning-configuration Status=Enabled
Recuerden que una vez que tengamos habilitado el versionado, no se lo puede quitar del todo, solo lo podemos suspender (Status=Suspended), lo cual equivale a un valor Null en el VersionID.
Listo la parte en el Cloud, ahora vamos a nuestros tf files.
Terraform + AWS
Ahora simplemente le decimos a Terraform que use S3 en el Backend Definition, de la siguiente manera:
terraform {
backend "s3" {
bucket = "tfbucket"
key = "tf/dev.blog.tfstate"
region = "us-east-1"
}
}
De nuevo, tenemos que loguearnos al cloud provider con anterioridad, esto lo podemos hacer desde nuestra laptop con aws cli, también inyectando los valores del set de keys desde algún servicio externo de almacenamiento de claves en un pipeline, o directamente sobre el vars file de terraform mediante env vars o hardcodeandolo (lo ultimo no lo recomiendo en prod jaja), por ejemplo algo así en el vars file, para probar anda de diez:
variable "access_key" {
default = "REEMPLAZAR"
description = "IAM Access Key"
}
variable "secret_key" {
default = "REEMPLAZAR"
description = "IAM Secret Key"
}
Listo, ya tenemos seteado nuestro Terraform con Remote State hacia AWS.