Comprensión de las funciones, expresiones y metaargumentos de Terraform - AWS Guía prescriptiva

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Comprensión de las funciones, expresiones y metaargumentos de Terraform

Una crítica a las herramientas de IaC que utilizan archivos de configuración declarativos en lugar de lenguajes de programación comunes es que dificultan la implementación de una lógica programática personalizada. En las configuraciones de Terraform, este problema se resuelve mediante funciones, expresiones y metaargumentos.

Funciones

Una de las grandes ventajas de usar código para aprovisionar la infraestructura es la capacidad de almacenar flujos de trabajo comunes y reutilizarlos una y otra vez, con frecuencia utilizando argumentos diferentes cada vez. Las funciones de Terraform son similares a las funciones AWS CloudFormation intrínsecas, aunque su sintaxis es más parecida a la forma en que se denominan las funciones en los lenguajes de programación. Es posible que ya hayas visto algunas funciones de Terraform, como substr, concat, length y base64decode, en los ejemplos de esta guía. Al igual que CloudFormation las funciones intrínsecas, Terraform tiene una serie de funciones integradas que están disponibles para su uso en sus configuraciones. Por ejemplo, si un atributo de recurso concreto ocupa un objeto JSON muy grande y sería ineficiente pegarlo directamente en el archivo, podrías poner el objeto en un archivo.json y usar las funciones de Terraform para acceder a él. En el siguiente ejemplo, la file función devuelve el contenido del archivo en forma de cadena y, a continuación, lo convierte en un tipo de objeto. jsondecode

resource "example_resource" "example_resource_name" { json_object = jsondecode(file("/path/to/file.json")) }

Expressions

Terraform también admite expresiones condicionales, que son similares a CloudFormation condition las funciones, excepto que utilizan la sintaxis de operador ternario más tradicional. En el siguiente ejemplo, las dos expresiones devuelven exactamente el mismo resultado. El segundo ejemplo es lo que Terraform denomina expresión splat. El asterisco hace que Terraform recorra la lista y cree una nueva lista utilizando solo la id propiedad de cada elemento.

resource "example_resource" "example_resource_name" { boolean_value = var.value ? true : false numeric_value = var.value > 0 ? 1 : 0 string_value = var.value == "change_me" ? "New value" : var.value string_value_2 = var.value != "change_me" ? var.value : "New value" } There are two ways to express for loops in a Terraform configuration: resource "example_resource" "example_resource_name" { list_value = [for object in var.ids : object.id] list_value_2 = var.ids[*].id }

Metaargumentos

En el ejemplo de código anterior, list_value y se list_value_2 denominan argumentos. Es posible que ya estés familiarizado con algunos de estos metaargumentos. Terraform también tiene algunos metaargumentos, que actúan como argumentos pero con alguna funcionalidad adicional:

Otros metaargumentos permiten añadir funciones y expresiones directamente a un recurso. Por ejemplo, el metaargumento count es un mecanismo útil para crear varios recursos similares al mismo tiempo. En el siguiente ejemplo, se muestra cómo crear dos clústeres de Amazon Elastic Container Service (Amazon EKS) sin utilizar el count metaargumento.

resource "aws_eks_cluster" "example_0" { name = "example_0" role_arn = aws_iam_role.cluster_role.arn vpc_config { endpoint_private_access = true endpoint_public_access = true subnet_ids = var.subnet_ids[0] } } resource "aws_eks_cluster" "example_1" { name = "example_1" role_arn = aws_iam_role.cluster_role.arn vpc_config { endpoint_private_access = true endpoint_public_access = true subnet_ids = var.subnet_ids[1] } }

El siguiente ejemplo muestra cómo utilizar el count metaargumento para crear dos clústeres de Amazon EKS.

resource "aws_eks_cluster" "clusters" { count = 2 name = "cluster_${count.index}" role_arn = aws_iam_role.cluster_role.arn vpc_config { endpoint_private_access = true endpoint_public_access = true subnet_ids = var.subnet_ids[count.index] } }

Para asignar a cada uno un nombre de unidad, puede acceder al índice de la lista dentro del bloque de recursos en. count.index Pero, ¿qué pasa si quieres crear varios recursos similares que sean un poco más complejos? Ahí es donde entra en juego el metaargumento for_each. El for_each metaargumento es muy similar acount, excepto que se pasa una lista o un objeto en lugar de un número. Terraform crea un nuevo recurso para cada miembro de la lista u objeto. Es similar a si lo configurascount = length(list), excepto que puedes acceder al contenido de la lista en lugar del índice del bucle.

Esto funciona tanto para una lista de elementos como para un solo objeto. El siguiente ejemplo crearía dos recursos que tienen id-0 y id-1 como identificadores.

variable "ids" { default = [ { id = "id-0" }, { id = "id-1" }, ] } resource "example_resource" "example_resource_name" { # If your list fails, you might have to call "toset" on it to convert it to a set for_each = toset(var.ids) id = each.value }

En el ejemplo siguiente se crearían también dos recursos, uno para Sparky, el caniche, y otro para Fluffy, el chihuahua.

variable "dogs" { default = { poodle = "Sparky" chihuahua = "Fluffy" } } resource "example_resource" "example_resource_name" { for_each = var.dogs breed = each.key name = each.value }

Del mismo modo que puedes acceder al índice de bucles en count mediante count.index, puedes acceder a la clave y al valor de cada elemento de un bucle for_each utilizando cada objeto. Como for_each recorre listas y objetos, puede resultar un poco confuso realizar un seguimiento de cada clave y valor. En la siguiente tabla se muestran las distintas formas en que se puede utilizar el metaargumento for_each y cómo se puede hacer referencia a los valores en cada iteración.

Ejemplo for_each type Primera iteración Segunda iteración
A
[“poodle”, “chihuahua”]
each.key = "poodle" each.value = null
each.key = "chihuahua" each.value = null
B
[ { type = "poodle", name = "Sparky" }, { type = "chihuahua", name = "Fluffy" } ]
each.key = { type = “poodle”, name = “Sparky” } each.value = null
each.key = { type = “chihuahua”, name = “Fluffy” } each.value = null
C
{ poodle = “Sparky”, chihuahua = “Fluffy” }
each.key = “poodle” each.value = “Sparky”
each.key = “chihuahua” each.value = “Fluffy”
D
{ dogs = { poodle = “Sparky”, chihuahua = “Fluffy” }, cats = { persian = “Felix”, burmese = “Morris” } }
each.key = “dogs” each.value = { poodle = “Sparky”, chihuahua = “Fluffy” }
each.key = “cats” each.value = { persian = “Felix”, burmese = “Morris” }
E
{ dogs = [ { type = “poodle”, name = “Sparky” }, { type = “chihuahua”, name = “Fluffy” } ], cats = [ { type = “persian”, name = “Felix” }, { type = “burmese”, name = “Morris” } ] }
each.key = “dogs” each.value = [ { type = “poodle”, name = “Sparky” }, { type = “chihuahua”, name = “Fluffy” } ]
each.key = “cats” each.value = [ { type = “persian”, name = “Felix” }, { type = “burmese”, name = “Morris” } ]

 

Entonces, si var.animals fuera igual a la fila E, entonces podrías crear un recurso por animal usando el siguiente código.

resource "example_resource" "example_resource_name" { for_each = var.animals type = each.key breeds = each.value[*].type names = each.value[*].name }

Como alternativa, puedes crear dos recursos por animal mediante el siguiente código.

resource "example_resource" "example_resource_name" { for_each = var.animals.dogs type = "dogs" breeds = each.value.type names = each.value.name } resource "example_resource" "example_resource_name" { for_each = var.animals.cats type = "cats" breeds = each.value.type names = each.value.name }