Entendendo as funções, expressões e meta-argumentos do Terraform - AWS Orientação prescritiva

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Entendendo as funções, expressões e meta-argumentos do Terraform

Uma crítica às ferramentas de IaC que usam arquivos de configuração declarativos em vez de linguagens de programação comuns é que elas dificultam a implementação da lógica programática personalizada. Nas configurações do Terraform, esse problema é resolvido usando funções, expressões e meta-argumentos.

Funções

Uma das grandes vantagens de usar código para provisionar sua infraestrutura é a capacidade de armazenar fluxos de trabalho comuns e reutilizá-los repetidamente, geralmente transmitindo argumentos diferentes a cada vez. As funções do Terraform são semelhantes às funções AWS CloudFormation intrínsecas, embora sua sintaxe seja mais semelhante à forma como as funções são chamadas em linguagens programáticas. Talvez você já tenha notado algumas funções do Terraform, como substr, concat, length e base64decode, nos exemplos deste guia. Assim como CloudFormation as funções intrínsecas, o Terraform tem uma série de funções integradas que estão disponíveis para uso em suas configurações. Por exemplo, se um determinado atributo de recurso usa um objeto JSON muito grande que seria ineficiente para colar diretamente no arquivo, você poderia colocar o objeto em um arquivo.json e usar as funções do Terraform para acessá-lo. No exemplo a seguir, a file função retorna o conteúdo do arquivo na forma de string e, em seguida, a jsondecode função o converte em um tipo de objeto.

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

Expressões

O Terraform também permite expressões condicionais, que são semelhantes às CloudFormation condition funções, exceto pelo fato de usarem a sintaxe mais tradicional do operador ternário. No exemplo a seguir, as duas expressões retornam exatamente o mesmo resultado. O segundo exemplo é o que o Terraform chama de expressão splat. O asterisco faz com que o Terraform percorra a lista e crie uma nova lista usando apenas a id propriedade de cada item.

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 }

Meta-argumentos

No exemplo de código anterior, list_value e list_value_2 são chamados de argumentos. Talvez você já esteja familiarizado com alguns desses meta-argumentos. O Terraform também tem alguns meta-argumentos, que funcionam como argumentos, mas com algumas funcionalidades extras:

Outros meta-argumentos permitem que a funcionalidade de função e expressão seja adicionada diretamente a um recurso. Por exemplo, o meta-argumento count é um mecanismo útil para criar vários recursos semelhantes ao mesmo tempo. O exemplo a seguir demonstra como criar dois clusters do Amazon Elastic Container Service (Amazon EKS) sem usar o count meta-argumento.

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] } }

O exemplo a seguir demonstra como usar o count meta-argumento para criar dois clusters do 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 dar um nome de unidade a cada uma, você pode acessar o índice da lista dentro do bloco de recursos emcount.index. Mas e se você quiser criar vários recursos semelhantes que sejam um pouco mais complexos? É aí que entra o meta-argumento for_each. O for_each meta-argumento é muito semelhante acount, exceto que você passa uma lista ou um objeto em vez de um número. O Terraform cria um novo recurso para cada membro da lista ou objeto. É semelhante a se você definircount = length(list), exceto que você pode acessar o conteúdo da lista em vez do índice do loop.

Isso funciona tanto para uma lista de itens quanto para um único objeto. O exemplo a seguir criaria dois recursos que têm id-0 e id-1 como seus IDs.

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 }

O exemplo a seguir também criaria dois recursos, um para Sparky, o poodle, e outro para Fluffy, o chihuahua.

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

Assim como você pode acessar o índice do loop em count usando count.index, você pode acessar a chave e o valor de cada item em um loop for_each usando o objeto each. Como for_each itera tanto em listas quanto em objetos, o controle de cada chave e valor pode ser um pouco confuso. A tabela a seguir mostra as diferentes maneiras de usar o meta-argumento for_each e como você pode referenciar os valores em cada iteração.

Exemplo Tipo de for_each Primeira iteração Segunda iteração
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” } ]

 

Portanto, se var.animals fosse igual à linha E, você poderia criar um recurso por animal usando o código a seguir.

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

Como alternativa, você pode criar dois recursos por animal usando o código a seguir.

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 }