Terraform Essentials V: Cómo importar infraestructura existente a Terraform
En este artículo, que es el último de la serie Terraform Essentials, veremos como importar infraestructura existente a Terraform.
Bienvenidos al último capítulo de esta serie que llamé Terraform Essentials. Si te perdiste los artículos anteriores, te los dejo aquí:
Arrancamos
"Nacho, muy lindo todo esto de Terraform, aprendí a desplegar Infraestructura, ningún problema, todo divino, pero, yo ya tengo mi infra desplegada, ¿cómo puedo importarla a Terraform?
Esta es una buena pregunta Mike...
En serio, es una excelente pregunta. Porque esta bien cuando desde un principio montamos nuestra infra desde cero y con Terraform, ya que desde ahí, modificar, romper o reducir, es sumamente sencillo, el desafío viene cuando tenemos recursos ya desplegados de manera manual o usando otras herramientas.
La importación de recursos en Terraform, aún es un tanto limitada, porque la misma permite solamente escribir un "tfstate" y replicar eso a otro ambiente o re deployar sobre el mismo, pero, lo que aún no hace es generar archivos "tf" en base a eso, por ahora, lo debemos hacer nosotros... a mano!.
Para este ejemplo, voy a importar la configuración de un contenedor muy sencillo que tengo corriendo en Docker. Algo a tener en cuenta es que ese contenedor puede estar corriendo o no, para Terraform, es indiferente.
Vamos a generar un archivo que se llamado container.tf que va a tener el siguiente contenido:
# Contenedor
resource "docker_container" "nginx" {
name = "nginx"
image = "nginx:latest"
}
Luego, veamos el contenedor que tengo corriendo.
2c6ede4ffa8a nginx:latest "nginx -g 'daemon of…" 7 seconds ago Up 6 seconds 80/tcp nginx
Ahora, lo que debemos ejecutar es el comando "terraform import", en el mismo le vamos a indicar que recurso vamos a importar, en este caso, un contenedor y vamos a especificar el nombre del mismo, este dato, debe coincidir con el del contenedor:
terraform import docker_container.nginx $(docker inspect -f {{.ID}} nginx)
Terraform nos responderá con algo como esto:
docker_container.nginx: Importing from ID "2c6ede4ffa8ab824e5f9b2df44eaf2b29620c91c37aea5cae6e8bc367c917586"...
docker_container.nginx: Import prepared!
Prepared docker_container for import
docker_container.nginx: Refreshing state... [id=2c6ede4ffa8ab824e5f9b2df44eaf2b29620c91c37aea5cae6e8bc367c917586]
Import successful!
The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
Y si inspeccionamos, nuestro archivo terraform.tfstate, se verá así:
{
"version": 4,
"terraform_version": "0.12.24",
"serial": 1,
"lineage": "8b83edb5-1c52-da0a-c243-871eb8eeb337",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "docker_container",
"name": "nginx",
"provider": "provider.docker",
"instances": [
{
"schema_version": 2,
"attributes": {
"attach": null,
"bridge": "",
"capabilities": [],
"command": [
"nginx",
"-g",
"daemon off;"
],
"container_logs": null,
"cpu_set": "",
"cpu_shares": 0,
"destroy_grace_seconds": null,
"devices": [],
"dns": [],
"dns_opts": [],
"dns_search": [],
"domainname": "",
"entrypoint": [],
"env": [
"NGINX_VERSION=1.17.10",
"NJS_VERSION=0.3.9",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"PKG_RELEASE=1~buster"
],
"exit_code": null,
"gateway": "172.17.0.1",
"group_add": [],
"healthcheck": [],
"host": [],
"hostname": "2c6ede4ffa8a",
"id": "2c6ede4ffa8ab824e5f9b2df44eaf2b29620c91c37aea5cae6e8bc367c917586",
"image": "sha256:602e111c06b6934013578ad80554a074049c59441d9bcd963cb4a7feccede7a5",
"ip_address": "172.17.0.2",
"ip_prefix_length": 16,
"ipc_mode": "private",
"labels": [
{
"label": "maintainer",
"value": "NGINX Docker Maintainers \u003cdocker-maint@nginx.com\u003e"
}
],
"links": [],
"log_driver": "json-file",
"log_opts": {},
"logs": null,
"max_retry_count": 0,
"memory": 0,
"memory_swap": 0,
"mounts": [],
"must_run": null,
"name": "nginx",
"network_alias": null,
"network_data": [
{
"gateway": "172.17.0.1",
"ip_address": "172.17.0.2",
"ip_prefix_length": 16,
"network_name": "bridge"
}
],
"network_mode": "default",
"networks": null,
"networks_advanced": [],
"pid_mode": "",
"ports": [],
"privileged": false,
"publish_all_ports": false,
"read_only": false,
"restart": "no",
"rm": false,
"shm_size": 64,
"start": null,
"sysctls": {},
"tmpfs": {},
"ulimit": [],
"upload": [],
"user": "",
"userns_mode": "",
"volumes": [],
"working_dir": ""
},
"private": "eyJzY2hlbWFfdmVyc2lvbiI6IjIifQ=="
}
]
}
]
}
A partir de acá, como comenté más arriba, podríamos re-deployar o hacerlo en otro ambiente con esa configuración, aunque también, podríamos editar el archivo container.tf y por ejemplo, exponerle el puerto 80 a ese contenedor.
# Contenedor
resource "docker_container" "nginx" {
name = "nginx"
image = "nginx:latest"
ports {
internal = "80"
external = "80"
}
}
Ejecutamos...
terraform apply
Nos devuelve lo siguiente:
docker_container.nginx: Refreshing state... [id=2c6ede4ffa8ab824e5f9b2df44eaf2b29620c91c37aea5cae6e8bc367c917586]
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# docker_container.nginx must be replaced
-/+ resource "docker_container" "nginx" {
+ attach = false
+ bridge = (known after apply)
~ command = [
- "nginx",
- "-g",
- "daemon off;",
] -> (known after apply)
+ container_logs = (known after apply)
- cpu_shares = 0 -> null
- dns = [] -> null
- dns_opts = [] -> null
- dns_search = [] -> null
~ entrypoint = [] -> (known after apply)
~ env = [
- "NGINX_VERSION=1.17.10",
- "NJS_VERSION=0.3.9",
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
- "PKG_RELEASE=1~buster",
] -> (known after apply)
+ exit_code = (known after apply)
~ gateway = "172.17.0.1" -> (known after apply)
- group_add = [] -> null
~ hostname = "2c6ede4ffa8a" -> (known after apply)
~ id = "2c6ede4ffa8ab824e5f9b2df44eaf2b29620c91c37aea5cae6e8bc367c917586" -> (known after apply)
~ image = "sha256:602e111c06b6934013578ad80554a074049c59441d9bcd963cb4a7feccede7a5" -> "nginx:latest" # forces replacement
~ ip_address = "172.17.0.2" -> (known after apply)
~ ip_prefix_length = 16 -> (known after apply)
~ ipc_mode = "private" -> (known after apply)
- links = [] -> null
log_driver = "json-file"
- log_opts = {} -> null
+ logs = false
- max_retry_count = 0 -> null
- memory = 0 -> null
- memory_swap = 0 -> null
+ must_run = true
name = "nginx"
~ network_data = [
- {
- gateway = "172.17.0.1"
- ip_address = "172.17.0.2"
- ip_prefix_length = 16
- network_name = "bridge"
},
] -> (known after apply)
- network_mode = "default" -> null
- privileged = false -> null
- publish_all_ports = false -> null
read_only = false
restart = "no"
rm = false
~ shm_size = 64 -> (known after apply)
+ start = true
- sysctls = {} -> null
- tmpfs = {} -> null
- labels {
- label = "maintainer" -> null
- value = "NGINX Docker Maintainers <docker-maint@nginx.com>" -> null
}
+ labels {
+ label = (known after apply)
+ value = (known after apply)
}
+ ports { # forces replacement
+ external = 80 # forces replacement
+ internal = 80 # forces replacement
+ ip = "0.0.0.0" # forces replacement
+ protocol = "tcp" # forces replacement
}
}
Plan: 1 to add, 0 to change, 1 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
Tipeamos "yes" como está acá arriba y pum:
docker_container.nginx: Destroying... [id=2c6ede4ffa8ab824e5f9b2df44eaf2b29620c91c37aea5cae6e8bc367c917586]
docker_container.nginx: Destruction complete after 0s
docker_container.nginx: Creating...
docker_container.nginx: Creation complete after 1s [id=204f58c382d45bbb0c881aeeba53c8d437013d81d4203c61c60b2baccbefb2b5]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
Nuestro contenedor, nuevo, con el puerto 80 expuesto:
204f58c382d4 nginx:latest "nginx -g 'daemon of…" 20 seconds ago Up 19 seconds 0.0.0.0:80->80/tcp nginx
Para ir cerrando
Como siempre digo, no parece ser complicado pero en este caso puede ser tedioso, porque yo importe un solo contenedor a Terraform, imaginen tenerlo que hacerlo con cientos de VMs o Contenedores con puertos, volumenes, labels, etc. Complicado. Espero que pronto Terraform sea capaz de escribir por sí solo archivos de configuración.
También, les recuerdo el Gitlab del proyecto, donde esta el contenido de los archivos usados en los cinco artículos:
Espero, que esta serie les haya servido de algo, si así fue, por favor, dejame un comentario en Twitter.
Estén atentos, porque más adelante, haré una serie llamada Terraform Advanced, donde veremos cosas muy concretas que están por fuera de la común pero que nos ayudarán a tener un mejor dominio de esta grandiosa herramienta.
Por ultimo, quiero invitarlos a que estén atentos al tag de Ansible, ya que en breve haré una serie llamada Ansible Essentials.