Feb 12 2022
Recurso Compartido de Terraform con el Ejemplo: Claves SSH
Terraform automatiza la creación de infraestructura en el cloud, comúnmente conocida como infraestructura como código. Necesitamos crear una máquina virtual, que debe contener las claves SSH de 3 administradores. El objetivo será hacer que este recurso compartido de Terraform sea reutilizable por otros módulos. Este ejemplo en Cloud IBM se basa en el plugin de IBM para Terraform, pero el método sigue siendo válido para otros proveedores de cloud, por supuesto.
No puse la creación de la VPC, subredes y grupos de seguridad para ganar legibilidad.
Recursos en un solo módulo
Comencemos por crear los archivos ssh.tf que contienen el código que crea las claves del administrador y vm.tf el código de creación del servidor en el mismo directorio. Las claves se dan como parámetros a la máquina virtual.
resource "ibm_is_ssh_key" "user1_sshkey" {
name = "user1"
public_key = "ssh-rsa AAAAB3[...]k+XR=="
}
resource "ibm_is_ssh_key" "user2_sshkey" {
name = "user2"
public_key = "ssh-rsa AAAAB3[...]Zo9R=="
}
resource "ibm_is_ssh_key" "user3_sshkey" {
name = "user3"
public_key = "ssh-rsa AAAAB3[...]67GqV="
}
resource "ibm_is_instance" "server1" {
name = "server1"
image = var.image
profile = var.profile
vpc = ibm_is_vpc.vpc.id
zone = var.zone1
primary_network_interface {
subnet = ibm_is_subnet.subnet1.id
security_groups = [ibm_is_vpc.vpc.default_security_group]
}
keys = [
ibm_is_ssh_key.user1_sshkey.id,
ibm_is_ssh_key.user2_sshkey.id,
ibm_is_ssh_key.user3_sshkey.id
]
}
El código es simple pero tiene un problema importante:
Las claves SSH no son reutilizables en otro módulo de Terraform. Si copiamos/pegamos este código para crear una segunda VM, un error indicará que las claves ya existen. Además, agregar una clave requiere modificar los 2 archivos de Terraform.
Recursos comunes de Terraform
Por lo tanto, es necesario crear las claves SSH en un módulo Terraform independiente y hacerlas accesibles desde los otros módulos. Esto se puede lograr exportando los id de las claves utilizando los valores output. Los outputs permiten que las variables estén disponibles en la línea de comandos o en otros módulos de Terraform para su reutilización.
Muevamos la declaración de las claves a un nuevo directorio de Terraform al que agregamos una salida output ssh_keys que devuelve una matriz de sus ID, ya que esto es lo que las máquinas virtuales esperan como parámetro.
resource "ibm_is_ssh_key" "user1_sshkey" {
name = "user1"
public_key = "ssh-rsa AAAAB3[...]k+XR=="
}
resource "ibm_is_ssh_key" "user2_sshkey" {
name = "user2"
public_key = "ssh-rsa AAAAB3[...]Zo9R=="
}
resource "ibm_is_ssh_key" "user3_sshkey" {
name = "user3"
public_key = "ssh-rsa AAAAB3[...]67GqV="
}
output "ssh_keys" {
value = [
ibm_is_ssh_key.user1_sshkey.id,
ibm_is_ssh_key.user2_sshkey.id,
ibm_is_ssh_key.user3_sshkey.id
]
}
Después de ejecutar terraform apply, podemos mostrar los valores de salida con terraform output:
$ terraform output
ssh_keys = [
"r010-3e98b94b-9518-4e11-9ac4-a014120344dc",
"r010-b271dce5-4744-48c3-9001-a620e99563d9",
"r010-9358c6ab-0eed-4de7-a4a0-4ba20b2c04c9",
]
Es exactamente lo que queríamos. Todo lo que queda es recuperar el contenido de la salida en forma de data lookup para usarlo en el módulo VM.
data "terraform_remote_state" "ssh_keys" {
backend = "local"
config = {
path = "../ssh_keys/terraform.tfstate"
}
}
resource "ibm_is_instance" "server1" {
name = "server1"
image = var.image
profile = var.profile
primary_network_interface {
subnet = ibm_is_subnet.subnet1.id
security_groups = [ibm_is_vpc.vpc.default_security_group]
}
vpc = ibm_is_vpc.vpc.id
zone = var.zone1
keys = data.terraform_remote_state.ssh_keys.outputs.ssh_keys
}
Es mucho mejor, podemos administrar las claves SSH independientemente de otros módulos de Terraform y reutilizarlas a voluntad. El path del data lookup es la ruta relativa al directorio que contiene el archivo ssh.tf.
Variables de Lista
No está mal pero podríamos hacer más elegante la creación de recursos compartidos (aquí las claves SSH).
De hecho, agregar una nueva clave se realiza en 2 lugares: crear un recurso de Terraform y agregarlo a los valores devueltos en la salida. Lo cual es restrictivo y genera errores.De hecho, agregar una nueva clave se realiza en 2 lugares: crear un recurso de Terraform y agregarlo a los valores devueltos en la salida. Lo cual es restrictivo y puede generar errores.
Además, sigue siendo bastante difícil de leer y sería más claro separar valores y código.
Para eso almacenaremos las claves en una tabla de tipo map en un archivo terraform.tfvars, que se cargará automáticamente.
ssh_keys = {
"user1" = "ssh-rsa AAAAB3[...]k+XR=="
"user2" = "ssh-rsa AAAAB3[...]Zo9R=="
"user3" = "ssh-rsa AAAAB3[...]67GqV="
}
En ssh.tf, luego recorreremos esta matriz de clave/valor para crear los recursos y exportarlos en el output.
# Définition du tableau
variable "ssh_keys" {
type = map(string)
}
resource "ibm_is_ssh_key" "keys" {
for_each = var.ssh_keys
name = each.key
public_key = each.value
}
output "ssh_keys" {
value = values(ibm_is_ssh_key.keys)[*].id
}
Recuperar valores es un poco complejo. Empecé generando valores de output (ibm_is_ssh_key.keys) para analizar la estructura y así recuperar los id.
Al final, un nuevo recurso compartido (una clave SSH en nuestro caso) se realiza simplemente agregándolo a una matriz, en un archivo que contiene solo variables. En un lugar. Cualquiera puede hacer esto sin siquiera leer o entender el código.