Invalid Index on data lookup on multiple vault auth backend - terraform

I am writing a terraform module creates a single entity with multiple aliases. I'm an unable to lookup aliases auth backend. am I missing something. Any help greatly appreciated.
data "vault_auth_backend" "b" {
provider = vault.this
for_each = {
for alias in var.entity.aliases :
alias.type => alias
}
path = each.value.auth_path
}
resource "vault_identity_entity_alias" "alias" {
provider = vault.this
for_each = {
for alias in var.entity.aliases :
alias.name => alias
}
name = each.key
mount_accessor = lookup(data.vault_auth_backend.b[each.key], "accessor", null)
canonical_id = vault_identity_entity.entity.id
}
Terraform Plan output:
Error: Invalid index
on .terraform/modules/vault_dba_entity/main.tf line 31, in resource "vault_identity_entity_alias" "alias":
31: mount_accessor = lookup(data.vault_auth_backend.b[each.key], "accessor", null)
|----------------
| data.vault_auth_backend.b is object with 2 attributes
| each.key is "ldap-team-foo"
The given key does not identify an element in this collection value.
Error: Invalid index
on .terraform/modules/vault_dba_entity/main.tf line 31, in resource "vault_identity_entity_alias" "alias":
31: mount_accessor = lookup(data.vault_auth_backend.b[each.key], "accessor", null)
|----------------
| data.vault_auth_backend.b is object with 2 attributes
| each.key is "aws-team-foo"

Your for_each blocks are not the same: in the vault_auth_backend you are using the type of the alias as the key, while in the vault_identity_entity_alias you are using its name. Then you try to look up in the vault_auth_backend using the name, which won't work because that uses type for its key.
Change the vault_auth_backend to use alias.name => alias instead of alias.type => alias.

Related

Terraform plan err: Unsupported argument

I have a resource like this repo/dynamo/main.tf:
resource "aws_dynamodb_table" "infra_locks" {
name = "infra-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
And I refer above file as a module follow github repo
Tf file where I refer to above file repo/example/main.tf:
provider "aws" {
region = var.region
}
module "dynamodb_table" {
source = "../dynamodb_table"
name = "my-table"
}
I have terraform init success but fail when run terraform plan
Error: Unsupported argument
on main.tf line 14, in module "dynamodb_table":
14: name = "my-table"
An argument named "name" is not expected here.
How can i fix this?
Tks in advance
As #luk2302 has said:
resource "aws_dynamodb_table" "infra_locks" {}
The above resource exactly has name attribute but it 's not a variable so we can not assign anything to it.
There is a confused right here.
I have fixed by change a little bit: repo/dynamo/main.tf
resource "aws_dynamodb_table" "infra_locks" {
name = var.name
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
repo/dynamo/variable.tf
variable "name" {
description = "dynamo table name"
default = null
}
And final, we can configure your dynamo_table 's name.

Terraform error with dynamic block in for_each

I'm trying to instantiate an azure storage_share map using the azuremrm resource storage_share. By design, I need to be able to instantiate more than one storage share with the same block; each of those shares may or may not have an "acl" section.
I was thinking of solving this issue using a for_each in conjuction with a dynamic block, as in the related SE question:
Main.tf
resource "azurerm_storage_share" "storage_share" {
for_each = var.storage_share_map
name = each.key
storage_account_name = azurerm_storage_account.sa.name
quota = each.value.quota
dynamic "acl" {
for_each = each.value.acl
content {
id = acl.value.id
access_policy {
permissions = acl.value.access_policy.permissions
start = acl.value.access_policy.start
expiry = acl.value.access_policy.expiry
}
}
}
The variable would be defined as:
variable "storage_share_map" {
type = map(object({
quota = number,
acl = object({
id = string,
access_policy = object({
expiry = string,
permissions = string,
start = string
})
}),
}))
default = {}
}
and later parametrized in my tests as:
storage_share_map = {
my-share-2 = {
quota = 123,
acl = {
id = "a-id",
access_policy = {
expiry = "ISO8061 UTC TIME"
permissions = "rwdl"
start = "ISO8601 UTC TIME"
},
},
}
However, when testing, terraform returns the following output:
Error: Unsupported attribute
on .terraform\modules\sa\main.tf line 83, in resource "azurerm_storage_share" "storage_share":
83: id = acl.value.id
|----------------
| acl.value is object with 3 attributes
This object does not have an attribute named "id".
Error: Unsupported attribute
on .terraform\modules\sa\main.tf line 83, in resource "azurerm_storage_share" "storage_share":
83: id = acl.value.id
|----------------
| acl.value is "a-id"
This value does not have any attributes.
Error: Unsupported attribute
on .terraform\modules\sa\main.tf line 86, in resource "azurerm_storage_share" "storage_share":
86: permissions = acl.value.access_policy.permissions
|----------------
| acl.value is object with 3 attributes
This object does not have an attribute named "access_policy".
Error: Unsupported attribute
on .terraform\modules\sa\main.tf line 86, in resource "azurerm_storage_share" "storage_share":
86: permissions = acl.value.access_policy.permissions
|----------------
| acl.value is "a-id"
This value does not have any attributes.
As I understand it, the issue here is that the for_each inside the dynamic block is either malformed or misbehaving: acl.value appears to be both valued as the string "a-id" and carrying three attribute (?).
Terraform version 0.12.26
Azurerm version 2.26.0
Any insight would be appreciated.
Related question:
Dynamic block with for_each inside a resource created with a for_each
By iterating in the dynamic block with for_each = each.value.acl, you are iterating over the values in the object type. It appears you really want to iterate over the acl themselves. You would need to adjust your type to:
variable "storage_share_map" {
type = map(object({
quota = number,
acl = list(object({
...
}))
})),
}
You can tell from the error messages that currently it is iterating over id and then access_policy, and failing to find the two requested attributes for each, which is why you have 2*2=4 errors.
You can adjust your input correspondingly to:
storage_share_map = {
my-share-2 = {
quota = 123,
acl = [{
id = "a-id",
access_policy = {
expiry = "ISO8061 UTC TIME"
permissions = "rwdl"
start = "ISO8601 UTC TIME"
},
}],
}
and this will achieve the behavior you desire.
Note that Terraform 0.12 has issues sometimes with nested object type specifications, so omitting the acl with [] may result in crashing under certain circumstances.
Please use square brackets for each.value.acl.
Azure storage share block should look like:
resource "azurerm_storage_share" "storage_share" {
for_each = var.storage_share_map
name = each.key
storage_account_name = azurerm_storage_account.sa.name
quota = each.value.quota
dynamic "acl" {
for_each = [each.value.acl]
content {
id = acl.value.id
access_policy {
permissions = acl.value.access_policy.permissions
start = acl.value.access_policy.start
expiry = acl.value.access_policy.expiry
}
}
}
}

How perform nested for loop on list(object) variable in Terraform

I'm writing a terraform module accepts a list of entities and each entity associates with a list of aliases. I'm having an issue to access alias object and pass in each.key. Any help greatly appreciated.
resource "vault_identity_entity_alias" "alias" {
provider = vault.this
for_each = [
for entity in var.entities : {
for alias in entity.aliases :
alias.name => alias
}
]
name = each.key
mount_accessor = lookup(vault_auth_backend.b[each.key], "accessor", null)
canonical_id = vault_identity_entity.entity[each.value.entity].id
}
Variable definition
variable "entities" {
description = "A collection of entities where each entity is associated with a list aliases "
type = list(object({
name = string
policies = list(string)
metadata = map(string)
aliases = list(object({
name = string
entity = string
auth_path = string
type = string
}))
}))
}
Terraform output
Error: Invalid for_each argument
on .terraform/modules/vault_dba_entity/main.tf line 9, in resource "vault_auth_backend" "b":
9: for_each = [
10: for entity in var.entities : {
11: for alias in entity.aliases :
12: alias.name => alias
13: }
14: ]
The given "for_each" argument value is unsuitable: the "for_each" argument
must be a map, or set of strings, and you have provided a value of type tuple.
As error message says, for_each accepts map or set, thus you have to convert array of objects into a map. Common way of doing it is creating flat array of objects first and then convert it into a map. It can be done by using flatten function. For better readability I put it into a local variable, but it also can be done inline:
locals {
entities = flatten([
for entity in var.entities: [
for alias in entity.aliases: {
entity_name = entity.name
alias_name = alias.name
alias_entity = alias.entity
}
]
])
}
Variable local.entities would contain list of objects, for example:
[
{
"alias_name" = "alias1"
"entity_name" = "object1"
"alias_entity" = "entity alias1"
},
{
"alias_name" = "alias2"
"entity_name" = "object1"
"alias_entity" = "entity alias2"
},
]
Now it's easy to convert it into a map. We just need to pick up unique key to use as an index. Based on your question, alias name should be unique, so it could be done like this:
resource "vault_identity_entity_alias" "alias" {
provider = vault.this
for_each = {
for item in local.entities: item.alias_name => item
}
name = each.key
mount_accessor = lookup(vault_auth_backend.b[each.key], "accessor", null)
# Note, we reference alias_entity, because it was defined with this name
# in local variable.
canonical_id = vault_identity_entity.entity[each.value.alias_entity].id
}

Unable to loop over list using for_each

I want to reserve an IP and then use it. If I create a separate google_compute_address block for each IP, it works well. But since I want to make the code as dry and optimized as possible, I am learning how to loop and use for_each
My main.tf looks like this
module "nat" {
source = "../../modules/nat"
reserved_ips = [
{
name = "gke-frontend-prod-lb"
ip = "10.238.232.10"
},
{
name = "gke-frontend-test-lb"
ip = "10.238.232.11"
}
]
}
As you can see, I would like to form a list of reserved IPs having name and IP.
Now lets look at my module
My variables.tf looks like
variable "reserved_ips" {
type = list(object({
name = string
ip = string
}))
description = <<EOF
Reserved IPs.
EOF
}
And the main.tf of my module looks like
locals {
ips = {
# for_each needs transform to map
for ip in var.reserved_ips : "${ip.name}" => "${ip.ip}"
}
}
resource "google_compute_address" "gke-frontend" {
for_each = local.ips
name = "${each.value.name}"
subnetwork = "mysubnet"
address_type = "INTERNAL"
address = "${each.value.ip}"
}
But running the code gives me
Error: Unsupported attribute
on ../../modules/nat/main.tf line 11, in resource "google_compute_address" "gke-frontend":
11: name = "${each.value.name}"
|----------------
| each.value is "10.238.232.10"
This value does not have any attributes.
Error: Unsupported attribute
on ../../modules/nat/main.tf line 11, in resource "google_compute_address" "gke-frontend":
11: name = "${each.value.name}"
|----------------
| each.value is "10.238.232.11"
This value does not have any attributes.
Error: Unsupported attribute
on ../../modules/nat/main.tf line 14, in resource "google_compute_address" "gke-frontend":
14: address = "${each.value.ip}"
|----------------
| each.value is "10.238.232.10"
This value does not have any attributes.
Error: Unsupported attribute
on ../../modules/nat/main.tf line 14, in resource "google_compute_address" "gke-frontend":
14: address = "${each.value.ip}"
|----------------
| each.value is "10.238.232.11"
This value does not have any attributes.
Im confused as to what am I missing here exactly.
The issue is that your ips local converts the list to a map(string) (i.e. a map with string values)
locals {
ips = {
# for_each needs transform to map
for ip in var.reserved_ips : "${ip.name}" => "${ip.ip}"
}
}
Notice that on the right-side of => you have "${ip.ip}".
When for_each loops over a map it assigns each.key to each key (a string) and each.value to each corresponding value in the map (in this case "${ip.ip} is also a string).
So, I think what you want in this case is something like the following
# ...
name = each.key
# ...
address = each.value
# ...

Referencing resource instances created by "for_each" in Terraform

I'm testing the "for_each" resource attribute now available in Terraform 12.6 but can't manage to reference created instances in other resources.
azure.tf
variable "customers" {
type = map(object({name=string}))
}
resource "azurerm_storage_account" "provisioning-datalake" {
for_each = var.customers
name = "mydatalake${each.key}"
resource_group_name = "${azurerm_resource_group.provisioning-group.name}"
location = "${azurerm_databricks_workspace.databricks.location}"
account_kind = "StorageV2"
account_tier = "Standard"
account_replication_type = "GRS"
is_hns_enabled = true
enable_advanced_threat_protection = true
tags = {
environment = var.environment
customer = each.value.name
}
}
resource "azurerm_key_vault_secret" "key-vault-datalake-secret" {
for_each = var.customers
name = "mydatalake-shared-key-${each.key}"
value = azurerm_storage_account.provisioning-datalake[each.key].primary_access_key
key_vault_id = azurerm_key_vault.key-vault.id
tags = {
environment = var.environment
customer = each.value.name
}
}
build.tfvars
environment = "Build"
customers = {
dev = {
name = "Development"
},
int = {
name = "Integration"
},
stg = {
name = "Staging"
}
}
I expect "keyvault-datalake-secret" entries to be created with the matching keys of the generated "provisioning-datalake" resources.
But when I run terraform plan --var-file=build.tfvars, I get the following error:
Error: Invalid index
on azure.tf line 45, in resource "azurerm_key_vault_secret" "key-vault-datalake-secret":
45: value = azurerm_storage_account.provisioning-datalake[each.key].primary_access_key
|----------------
| azurerm_storage_account.provisioning-datalake is object with 52 attributes
| each.key is "stg"
The given key does not identify an element in this collection value.
Error: Invalid index
on azure.tf line 45, in resource "azurerm_key_vault_secret" "key-vault-datalake-secret":
45: value = azurerm_storage_account.provisioning-datalake[each.key].primary_access_key
|----------------
| azurerm_storage_account.provisioning-datalake is object with 52 attributes
| each.key is "int"
The given key does not identify an element in this collection value.
Error: Invalid index
on azure.tf line 45, in resource "azurerm_key_vault_secret" "key-vault-datalake-secret":
45: value = azurerm_storage_account.provisioning-datalake[each.key].primary_access_key
|----------------
| azurerm_storage_account.provisioning-datalake is object with 52 attributes
| each.key is "dev"
The given key does not identify an element in this collection value.
Bug corrected in Terraform 0.12.7

Resources