Attribute of custom module not available in terraform - terraform

I have created locally a custom module that just replicates the vault_identity_entity official module.
Here it is more or less
resource "vault_identity_entity" "this" {
name = var.name
policies = var.policies
metadata = var.metadata
disabled = var.disabled
external_policies = var.external_policies
}
In the corresponding outputs.tf file, I have this
output "entity" {
description = "The entity created"
value = vault_identity_entity.this
}
I am now trying to retrieve its id attribute as follows
module "identities_memberships" {
source = "../../../../path/to/identity_group_member_entity_ids"
for_each = {
for item in local.memberships: item.member_email => {
group = item.group
}
}
member_entity_ids = [module.identity_entities[each.key].id]
group_id = module.identity_groups[each.value.group].id
}
This fails as follows:
│ Error: Unsupported attribute
│
│ on main.tf line 101, in module "identities_memberships":
│ 101: member_entity_ids = module.identity_entities[each.key].id
│ ├────────────────
│ │ each.key is a string
│ │ module.identity_entities is a map of object
│
│ This object does not have an attribute named "id".
The vaule of local_memberships as printed when I set it in the outputs
+ local_memberships = [
+ {
+ group_name = "admins"
+ member_email = "john#gmail.com.com"
},
+ {
+ group_name = "admins"
+ member_email = "maria#gmail.com"
},
+ {
+ group_name = "operators"
+ member_email = "maria#gmail.com.com"
},
+ {
+ group_name = "viewers"
+ member_email = "adam#gmail.com.com"
},
]
and the module.identity_identities is the instantiation of the above module
module "identity_entities" {
source = "../../../../path/to/identity_entity"
for_each = local.member_groups
name = each.key
depends_on = [
module.identity_groups
]
}
where local.member_groups:
+ local_member_groups = {
+ "adam#gmail.com.com" = [
+ "viewers",
]
+ "john#gmail.com.com" = [
+ "admins",
]
+ "maria#gmail.com" = [
+ "admins",
]
+ "maria#gmail.com.com" = [
+ "operators",
]
}
Why can't I access the id attribute? What am I missing?
If I comment out the section that produces the error, the plan shows me it will create for example (among others) this:
# module.identity_entities["john#gmail.com"].vault_identity_entity.this will be created
+ resource "vault_identity_entity" "this" {
+ disabled = false
+ external_policies = false
+ id = (known after apply)
+ name = "john#gmail.com"
}
However when also hardcoding values as follows
module "identities_memberships" {
source = "../../../../modules-terraform/vault/identity_group_member_entity_ids"
for_each = {
for item in local.memberships : item.member_email => {
group = item.group
}
}
member_entity_ids = [module.identity_entities["john#gmail.com"].id]
group_id = module.identity_groups["admins"].id
depends_on = [
module.identity_entities
]
}
same error, (now for the other module as well)
│
│ on main.tf line 101, in module "identities_memberships":
│ 101: member_entity_ids = [module.identity_entities["john#gmail.com"].id]
│ ├────────────────
│ │ module.identity_entities["john#gmail.com"] is a object
│
│ This object does not have an attribute named "id".
╵
╷
│ Error: Unsupported attribute
│
│ on main.tf line 102, in module "identities_memberships":
│ 102: group_id = module.identity_groups["admins"].id
│ ├────────────────
│ │ module.identity_groups["admins"] is a object
│

To access id of the entity, you have to:
module.identity_entities["john#gmail.com"].entity.id

Related

Terraform dynamicly attach disks and network when vps is counted

I can't figure out how to dynamically connect disks and networks to virtual machines if they are counted
I expect i will pass variables like this:
module "vps-test" {
source = "../module"
count = 3
server_name = "vpstest"
server_image = "debian-11"
server_type = "cx21"
server_datacenter = "fsn1-dc14"
labels = { groups = "test_offline.test_vps" }
server_network = {
backend_network = {
subnet_id = (data.terraform_remote_state.htz_network.outputs.main-subnet-id)
ip = "" #DHCP
}
custom_network = {
subnet_id = "1867414"
ip = ""
}
}
volumes = {
firts_volume = {
name = "volume1"
size = "20"
}
second_volume = {
name = "volume1"
size = "20"
}
}
hetzner_cloud_token = var.hetzner_cloud_offline_main_api_token
cloud_init_file_path = "../module/scripts/user.yaml"
}
and the module will create 3 identical VMs, where each will have 2 disks and 2 networks
It's Hetznere cloud provider, here is my simple code:
resource "hcloud_server" "vps" {
count = var.server_count
name = var.server_count > 1 ? "${var.server_name}-${count.index}" : var.server_name
image = var.server_image
server_type = var.server_type
datacenter = var.server_datacenter
user_data = data.template_file.ansible_user_data.rendered
labels = var.labels
}
resource "hcloud_volume" "volume" {
for_each = var.volumes
name = tostring(each.value["name"])
size = tonumber(each.value["size"])
server_id = hcloud_server.vps.id
automount = true
format = var.volume_filesystem
}
resource "hcloud_server_network" "network" {
for_each = var.server_network
server_id = hcloud_server.vps.id
subnet_id = each.value["subnet_id"]
ip = tostring(each.value["ip"])
}
Errors:
│ Error: Missing resource instance key
│
│ on ../module/resource.tf line 15, in resource "hcloud_volume" "volume":
│ 15: server_id = hcloud_server.vps.id
│
│ Because hcloud_server.vps has "count" set, its attributes must be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│ hcloud_server.vps[count.index]
╵
╷
│ Error: Missing resource instance key
│
│ on ../module/resource.tf line 22, in resource "hcloud_server_network" "network":
│ 22: server_id = hcloud_server.vps.id
│
│ Because hcloud_server.vps has "count" set, its attributes must be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│ hcloud_server.vps[count.index]
if using recommends from error log
│ Error: Reference to "count" in non-counted context
│
│ on ../module/resource.tf line 15, in resource "hcloud_volume" "volume":
│ 15: server_id = hcloud_server.vps[count.index].id
│
│ The "count" object can only be used in "module", "resource", and "data" blocks, and only when the "count" argument is set.
╵
╷
│ Error: Reference to "count" in non-counted context
│
│ on ../module/resource.tf line 22, in resource "hcloud_server_network" "network":
│ 22: server_id = hcloud_server.vps[count.index].id
│
│ The "count" object can only be used in "module", "resource", and "data" blocks, and only when the "count" argument is set.
but if server_id = hcloud_server.vps[0].id (or any specific index) - working
where is the correct way
Since you are using both count and for_each you need to iterate over both of them. Basically it means you need a double for loop. One way to do it in TF is with the help of setproduct:
resource "hcloud_volume" "volume" {
for_each = {for idx, val in setproduct(range(var.server_count), keys(var.volumes)): idx => val}
name = tostring(var.volume[each.value[1]].name)
size = tonumber(var.volume[each.value[1]].size)
server_id = hcloud_server.vps[each.value[0]].id
automount = true
format = var.volume_filesystem
}
resource "hcloud_server_network" "network" {
for_each = {for idx, val in setproduct(range(var.server_count), keys(var.server_network)): idx => val}
server_id = hcloud_server.vps[each.value[0]].id
subnet_id = var.server_network[each.value[1]].subnet_id
ip = tostring(var.server_network[each.value[1]].ip)
}

How to define maps in template file for terraform to pick it up

My code is as follows, so the custom_parameter fails to be decoded, I'm not sure how I can define this maps in template file, How can I define the maps variable in template file.
Invalid template interpolation value; Cannot include the given value in a
│ string template: string required..
main.tf looks like this
resource "google_dataflow_flex_template_job" "dataflow_jobs_static" {
provider = google-beta
for_each = {
for k, v in var.dataflows : k => v
if v.type == "static"
}
parameters = merge(
yamldecode(templatefile("df/${each.key}/config.yaml", {
tf_host_project = var.host_project
tf_dataflow_subnet = var.dataflow_subnet
tf_airflow_project = local.airflow_project
tf_common_project = "np-common"
tf_dataeng_project = local.dataeng_project
tf_domain = var.domain
tf_use_case = var.use_case
tf_env = var.env
tf_region = lookup(local.regions, each.value.region, "northamerica-northeast1")
tf_short_region = each.value.region
tf_dataflow_job = each.key
tf_dataflow_job_img_tag = each.value.active_img_tag
tf_metadata_json = indent(6, file("df/${each.key}/metadata.json"))
tf_sdk_language = each.value.sdk_language
tf_custom_parameters = each.value.custom_parameters[*]
}))
)
}
terraform.tfvars looks like this
dataflows = {
"lastflow" = {
type = "static"
region = "nane1"
sdk_language = "JAVA"
active_img_tag = "0.2"
custom_parameters = {
bootstrapServers = "abc,gv"
}
},
vars.tf
variable "dataflows" {
type = map(object({
type = string
region = string
sdk_language = string
active_img_tag = string
custom_parameters = map(string)
}))
default = {}
}
config.yaml
custom_var: ${tf_custom_parameters}
Also my metadata json file looks like this
{
"name": "Streaming Beam PubSub to Kafka Testing",
"description": "An Apache Beam streaming pipeline that reads JSON encoded messages from Pub/Sub, uses Beam to transform the message data, and writes the results to a Kafka",
"parameters": [
{
"name": "custom_var",
"isOptional": true
}
]
}
Error
Error: Error in function call
│
│ on dataflow.tf line 60, in resource "google_dataflow_flex_template_job" "dataflow_jobs_static":
│ ├────────────────
│ │ each.key is "lastflow"
│ │ each.value.active_img_tag is "0.2"
│ │ each.value.custom_parameters is map of string with 1 element
│ │ each.value.region is "nane1"
│ │ each.value.sdk_language is "JAVA"
│ │ local.airflow_project is "-01"
│ │ local.dataeng_project is "-02"
│ │ local.regions is object with 2 attributes
│ │ var.common_project_index is "01"
│ │ var.dataflow_subnet is "dev-01"
│ │ var.domain is "datapltf"
│ │ var.env is "npe"
│ │ var.host_project is "rod-01"
│ │ var.use_case is "featcusto"
│
│ Call to function "templatefile" failed: df/lastflow/config.yaml:2,15-35:
│ Invalid template interpolation value; Cannot include the given value in a
│ string template: string required..
Hi I fixed this by referencing to the foll. doc
https://www.terraform.io/language/functions/templatefile#maps
following that, my solution is as follows,
the config.yaml was changed to be like this
%{ for key, value in tf_custom_parameters }
${key}: ${value}
%{ endfor ~}
And the metadata.json file changed to be as follows
{
"name": "Streaming Beam PubSub to Kafka Testing",
"description": "An Apache Beam streaming pipeline that reads JSON encoded messages from Pub/Sub, uses Beam to transform the message data, and writes the results to a Kafka",
"parameters": [
{
"name": "bootstrapServers",
"label": "test label.",
"isOptional": true,
"helpText": "test help Text"
}
]
}
And the one change in main.tf file was this
.........
.........
tf_custom_parameters = each.value.custom_parameters
.........
this the solution that helped.

Use attribute KEY and VALUE of a map inside a list element

I have defined a List called data, inside the list, i use a map with a key and a value. I now need to access the KEY and VALUE inside each element of the data list.
locals {
data = [
{ "secret1" = 1 },
{ "secret2" = 1 },
{ "secret3" = 1 },
{ "secret4" = 1 },
{ "secret5" = 1 }
]
}
The goal is to use the KEY and VALUE inside a google secret resource,
the value should then be used inside secret and version attribute. Something like this:
data "google_secret_manager_secret_version" "secret_datas" {
count = length(local.data)
secret = local.data[count.index].key
project = "myproject"
version = local.data[count.index].value
}
My Current Error Message
│ Error: Unsupported attribute
│
│ on dependabot.tf line 38, in data "google_secret_manager_secret_version" "secret_data":
│ 38: version = local.data[count.index].value
│ ├────────────────
│ │ count.index is 1
│ │ local.data is tuple with 5 elements
│
│ This object does not have an attribute named "value".
This would be much easier with the modern for_each meta-argument. After optimizing the structure of the data in the locals:
locals {
data = {
"secret1" = 1,
"secret2" = 1,
"secret3" = 1,
"secret4" = 1,
"secret5" = 1
}
}
we can easily use it in the resource.
data "google_secret_manager_secret_version" "secret_datas" {
for_each = local.data
secret = each.key
project = "myproject"
version = each.value
}

how to use list(string) in terraform

I'm trying to use list(string) for azure event grid event types. I used to pass all the inputs using tfvars file. and use locals to get data from it.
Below is how the tfvars looks like.
grid_configuration = {
grid1 = {
name = "testgridsiai"
rg-name = "sai-india"
location = "uksouth"
is_storage_grid = true
source_storage_account = "apmapplicationstorages"
topic_subscription = [
{
is_sink_queue = true
is_sink_eventhub = true
storage_account_name = "apmapplicationstorages"
storage_account_queue_name = "asset-data"
storage_account_queue_name_subscription_name = "store"
event_hub_name = "input"
event_hub_namespace_name = "SIAI-EH-NEW-APMS"
event_hub_subscription_name = "event-test"
event_types = ["Microsoft.Storage.BlobCreated","Microsoft.Storage.BlobDeleted"]
}
]
}
}
and below is the terraform configuration
locals {
grid_topics = { for e in var.grid_configuration : e.name => e }
subscriptions = { for hc in flatten([for h in var.grid_configuration :
[for c in h.topic_subscription : {
is_sink_queue = c.is_sink_queue
is_sink_eventhub = c.is_sink_eventhub
storage_account_name = c.storage_account_name
storage_account_queue_name = c.storage_account_queue_name
event_hub_name = c.event_hub_name
grid_name = h.name
location = h.location
rg-name = h.rg-name
storage_account_queue_name_subscription_name = c.storage_account_queue_name_subscription_name
event_hub_namespace_name = c.event_hub_namespace_name
event_hub_subscription_name = c.event_hub_subscription_name
event_types = c.event_types
}]]) : format("%s.%s.%s.%s.%s.%s.%s.%s.%s.%s.%s", hc.is_sink_queue, hc.is_sink_eventhub, hc.storage_account_name, hc.storage_account_queue_name, hc.event_hub_name, hc.grid_name, hc.rg-name, hc.location, hc.event_hub_namespace_name, hc.event_hub_subscription_name, hc.event_types) => hc }
}
resource "azurerm_eventgrid_system_topic_event_subscription" "example" {
for_each = { for k, v in local.subscriptions : k => v if v.is_sink_queue }
name = each.value.storage_account_queue_name_subscription_name
system_topic = each.value.grid_name
resource_group_name = each.value.rg-name
storage_queue_endpoint {
storage_account_id = data.azurerm_storage_account.example[each.key].id
queue_name = each.value.storage_account_queue_name
}
included_event_types = [each.value.event_types]
depends_on = [azurerm_eventgrid_system_topic.example]
}
and below is the error
│ Error: Error in function call
│
│ on event-grid/main.tf line 18, in locals:
│ 18: }]]) : format("%s.%s.%s.%s.%s.%s.%s.%s.%s.%s.%s", hc.is_sink_queue, hc.is_sink_eventhub, hc.storage_account_name, hc.storage_account_queue_name, hc.event_hub_name, hc.grid_name, hc.rg-name, hc.location, hc.event_hub_namespace_name, hc.event_hub_subscription_name, hc.event_types) => hc }
│ ├────────────────
│ │ hc.event_hub_name is "input"
│ │ hc.event_hub_namespace_name is "SIAI-EH-NEW-APMS"
│ │ hc.event_hub_subscription_name is "event-test2"
│ │ hc.event_types is list of string with 2 elements
│ │ hc.grid_name is "testgridsiai"
│ │ hc.is_sink_eventhub is true
│ │ hc.is_sink_queue is true
│ │ hc.location is "uksouth"
│ │ hc.rg-name is "sai-india"
│ │ hc.storage_account_name is "apmapplicationstorages"
│ │ hc.storage_account_queue_name is "channel-data"
│
│ Call to function "format" failed: unsupported value for "%s" at 30: string required.
I understood that I needed to use formatlist() instead of format(). Can someone throw some light on it.
Just add ...:
format("%s.%s.%s.%s.%s.%s.%s.%s.%s.%s.%s", hc.is_sink_queue, hc.is_sink_eventhub, hc.storage_account_name, hc.storage_account_queue_name, hc.event_hub_name, hc.grid_name, hc.rg-name, hc.location, hc.event_hub_namespace_name, hc.event_hub_subscription_name, hc.event_types...)

Dynamic block of lifecycle_rule not working in terraform 1.0.6

The following block within a google_cloud_storage resource
dynamic "lifecycle_rule" {
for_each = var.bucket_lifecycle_rule
content {
condition {
age = lifecycle_rule.age
with_state = lifecycle_rule.with_state
}
action {
type = lifecycle_rule.type
storage_class = lifecycle_rule.storage_class
}
}
}
with the following variable declaration
variable "bucket_lifecycle_rule" {
description = "Lifecycle rules"
type = list(object({
age = string
with_state = string
type = string
storage_class = string
}))
default = [
{
age = 30
with_state = "ANY"
type = "SetStorageClass"
storage_class = "COLDLINE"
},
{
age = 120
with_state = "ANY"
type = "Delete"
storage_class = ""
},
]
}
errors out as follows:
│ Error: Unsupported attribute
│
│ on main.tf line 18, in resource "google_storage_bucket" "my_bucket":
│ 18: age = lifecycle_rule.age
│
│ This object does not have an attribute named "age".
╵
╷
│ Error: Unsupported attribute
│
│ on main.tf line 19, in resource "google_storage_bucket" "my_bucket":
│ 19: with_state = lifecycle_rule.with_state
│
│ This object does not have an attribute named "with_state".
╵
╷
│ Error: Unsupported attribute
│
│ on main.tf line 22, in resource "google_storage_bucket" "my_bucket":
│ 22: type = lifecycle_rule.type
│
│ This object does not have an attribute named "type".
╵
╷
│ Error: Unsupported attribute
│
│ on main.tf line 23, in resource "google_storage_bucket" "my_bucket":
│ 23: storage_class = lifecycle_rule.storage_class
│
│ This object does not have an attribute named "storage_class".
why is that?
You have to reference the properties from your objects with value:
dynamic "lifecycle_rule" {
for_each = var.bucket_lifecycle_rule
content {
condition {
age = lifecycle_rule.value.age
with_state = lifecycle_rule.value.with_state
}
action {
type = lifecycle_rule.value.type
storage_class = lifecycle_rule.value.storage_class
}
}
}
In the Terraform docs there is a note about this:
The iterator object has two attributes:
key is the map key or list element index for the current element. If
the for_each expression produces a set value then key is identical to value and should not be used.
value is the value of the current element.

Resources