Terraform missing map element - terraform

Attempts to map the result from the SQL query. This seems correct to me, but terraform returns an error.
It does not return any blank result for IP and server name.
Is there a bug somewhere? It seems correct to me.
I have main.tf
/*
* admin database
*/
module "sql-servers-admindb" {
source = "../terraform-modules/terraform-data-db"
dbtype = "mysql"
connection = {
mysql = {
host = local.mysql-admin_credentials.dbhost
user = local.mysql-admin_credentials.dbuser
pass = local.mysql-admin_credentials.dbpass
dbase = local.mysql-admin_credentials.dbname
}
sqlite = null
}
debugfile = "debug.json"
query = {
sqlquery = <<EOT
SELECT sd.serwer_id, sd.ip_local, sd.host_mysql
FROM serwer_panel sd
JOIN serwer_serwer_grupa ssg ON ssg.serwer_id = sd.serwer_id
JOIN serwer_grupa sg ON ssg.serwer_grupa_id = sg.id
EOT
}
}
module "terraform-sql" {
source = "../terraform-modules/terraform-sql"
depends_on = [module.sql-servers-admindb]
for_each = { for sql in module.sql-servers-admindb.result.sqlquery : sql.serwer_id => sql }
general = {
zone = "sql.${var.dns_local_name[local.environment]}."
ptr_networks = "${var.dns_local_reverse_allowed[local.environment]}"
}
}
terraform-modules/terraform-sql/main.tf
// IP address and A record
module "sql-nsrecord_a" {
source = "../terraform-modules/terraform-generic-nsrecord
zone = "{var.general.zone}."
name = "${var.sql.serwer_id}."
type = "A"
records = [var.sql.local_ip]
ptr_networks = var.general.ptr_networks
}
/*
* General
*/
variable "general" {
type = object({
zone = string
ptr_networks = list(any)
})
default = {
zone = ""
ptr_networks = []
}
}
/*
* SQL records
*/
variable "sql" {
type = map(any)
default = {}
}
Example debug.json
Array
(
[sqlquery] => Array
(
[0] => Array
(
[serwer_id] => s12
[ip_local] => 127.0.0.1
[host_mysql] =>
)
Return error:
│ Error: Missing map element
│
│ on ../terraform-modules/terraform-sql/main.tf line 9, in module "sql-nsrecord_a":
│ 9: name = "${var.sql.serwer_id}."
│ ├────────────────
│ │ var.sql is empty map of dynamic
│
│ This map does not have an element with the key "serwer_id".
╵
╷
│ Error: Missing map element
│
│ on ../terraform-modules/terraform-sql/main.tf line 11, in module "sql-nsrecord_a":
│ 11: records = [var.sql.local_ip]
│ ├────────────────
│ │ var.sql is empty map of dynamic
│
I am asking for advice on how to improve it.

You don't seem to be passing the variable sql to the terraform-sql module and thus its default value is used:
default = {}
Hence an empty map. No wonder it doesn't contain some entry.
You need to amend your
module "terraform-sql" {
...
}
to pass the sql argument too.

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

Duplicate key in for loop terraform

I am trying to create cloudwatch dashboards for ALB with multiple ALB and Target groups.
But I am getting an error while passing same value to the for loop "AWS/ApplicationELB" so is there any by which I can remove this duplicate in for loop.
data.tf
dashboards = [
{
my-dashboard-name = "Cloudwatch-Dashboard-ALB-test3"
aws-region = "us-east-1"
targets = ["app/test3/e8586bb7d49cf35b"]
target_groups = ["targetgroup/test-tg-2/f2e0260797ce83e8","targetgroup/test4/2dd3e8ae3bf2cb94"]
metrics = ["RequestCountPerTarget", "NewConnectionCount", "TargetResponseTime", "HTTPCode_Target_2XX_Count","RuleEvaluations", "HTTPCode_ELB_4XX_Count", "HTTPCode_Target_3XX_Count", "HTTP_Redirect_Count", "ActiveConnectionCount", "ProcessedBytes", "HTTPCode_ELB_3XX_Count", "RequestCount", "HTTPCode_ELB_5XX_Count", "HTTPCode_ELB_504_Count", "ConsumedLCUs", "HTTPCode_ELB_503_Count", "HTTPCode_ELB_502_Count", "HTTP_Fixed_Response_Count", "HTTPCode_Target_4XX_Count", "UnHealthyHostCount", "HealthyHostCount"]
aws-namespace = "AWS/ApplicationELB"
dim = "LoadBalancer"
service_names = ""
stat = "Average"
period = 300
},
{
my-dashboard-name = "Cloudwatch-Dashboard-ALB-test"
aws-region = "us-east-1"
targets = ["app/test2/eb397187e673ccc3"]
target_groups = ["targetgroup/test-tg/62fdb81766299e0e"]
metrics = ["RequestCountPerTarget", "NewConnectionCount", "TargetResponseTime", "HTTPCode_Target_2XX_Count","RuleEvaluations", "HTTPCode_ELB_4XX_Count", "HTTPCode_Target_3XX_Count", "HTTP_Redirect_Count", "ActiveConnectionCount", "ProcessedBytes", "HTTPCode_ELB_3XX_Count", "RequestCount", "HTTPCode_ELB_5XX_Count", "HTTPCode_ELB_504_Count", "ConsumedLCUs", "HTTPCode_ELB_503_Count", "HTTPCode_ELB_502_Count", "HTTP_Fixed_Response_Count", "HTTPCode_Target_4XX_Count", "UnHealthyHostCount", "HealthyHostCount"]
aws-namespace = "AWS/ApplicationELB"
dim = "LoadBalancer"
service_names = ""
stat = "Average"
period = 300
},
]
}
cloudwatch.tf
module "create-dashboard" {
source = "../"
for_each = { for service in local.dashboards : service.aws-namespace => service}
dashboard-name = each.value.my-dashboard-name
aws-region = each.value.aws-region
targets = each.value.targets
metrics = each.value.metrics
aws-namespace = each.value.aws-namespace
dim = each.value.dim
service_names = each.value.service_names
target_groups = each.value.target_groups
cluster_name = ["a-cluster"]
stat = each.value.stat
period = each.value.period
}
Error
╷
│ Error: Duplicate object key
│
│ on cloudwatch-v1.tf line 10, in module "create-dashboard":
│ 10: for_each = { for idx, service in local.dashboards : service.aws-namespace => service}
│ ├────────────────
│ │ service.aws-namespace is "AWS/ApplicationELB"
│
│ Two different items produced the key "AWS/ApplicationELB" in this 'for' expression. If duplicates are expected, use the ellipsis (...) after the value
│ expression to enable grouping by key
It would probably be more transparent for your config and state if the module declared name key was synced with the my-dashboard-name object key, and that would also fix your issue:
module "create-dashboard" {
source = "../"
for_each = { for service in local.dashboards : service.my-dashboard-name => service}
...
}

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
}

Separate structure for variables.tf gets "Object, known only after apply"

I have recently separated terraform files from it's variable file as per below structure
(root)
| main.tf
| users.tf
| roles.tf
├── Configuration (folder)
├──── azure-pipelines.yml
├──── .gitignore
├──── variables.tf
and since then I am getting bellow error messages
│ Error: Unsupported attribute
│
│ on main.tf line 77, in resource "azurerm_key_vault_secret" "primary_account_storage_access_key":
│ 77: name = "${module.variables.storage-account_name}-access-key"
│ ├────────────────
│ │ module.variables is a object, known only after apply
│
│ This object does not have an attribute named "storage-account_name".
╵
╷
│ Error: Unsupported attribute
│
│ on main.tf line 78, in resource "azurerm_key_vault_secret" "primary_account_storage_access_key":
│ 78: value = module.variables.storage-access_key
│ ├────────────────
│ │ module.variables is a object, known only after apply
│
│ This object does not have an attribute named "storage-access_key".
╵
This is how problematic resource "primary_account_storage_access_key" is defined
resource "azurerm_key_vault_secret" "primary_account_storage_access_key" {
depends_on = [azurerm_key_vault_access_policy.terraform_sp_access]
key_vault_id = data.azurerm_key_vault.azvault.id
name = "${module.variables.storage-account_name}-access-key"
value = module.variables.storage-access_key
}
Module is defined as below:
module "variables" {
source = "./Configuration"
}
I have no issue with utilizing the same module in other resources placed in the same file (main.tf)
terraform {
backend "azurerm" {
resource_group_name = module.variables.storage-resource_group_name
storage_account_name = module.variables.storage-storage_account_name
container_name = module.variables.storage-container_name
key = module.variables.storage-key
}
}
Found articles here on the site focusing on the error suggesting
"splat" operator with "toset" / "one" function, however that did not help:
name = "${toset(module.variables[*].storage-account_name)}-access-key"
value = toset(module.variables[*].storage-access_key)
name = "${one(module.variables[*].storage-account_name)}-access-key"
value = one(module.variables[*].storage-access_key)
Content of variables.tf what child module variables uses:
variable "storage-resource_group_name" {
type = string
default = "Reporting-HFM-integration-rg"
}
variable "storage-account_name" {
type = string
default = "reportinghfmintegration"
}
variable "storage-container_name" {
type = string
default = "tfstate-blob"
}
variable "storage-key" {
type = string
default = "terraform.tfstate"
}
variable "storage-access_key" {
type = string
default = "u3K..."
}
variable "keyVault-name" {
type = string
default = "se-dataplat-dwvault-prod"
}
variable "keyVault-resource_group_name" {
type = string
default = "AzureDataPlatform-dwtools-prod-rg"
}
variable "keyVault-id" {
type = string
default = "/subscriptions/23a89ca1-9743-4b3b-b5ff-41cea9985deb/resourceGroups/..."
}

Add default timestamp function as a default value using terraform chanzuckerberg snowflake provider

I am trying to create a table and for the column I want to give the default value as current timestamp. What am I doing wrong here?
Ref -
https://registry.terraform.io/providers/chanzuckerberg/snowflake/latest/docs/resources/table
resource "snowflake_table" "snow_events_table" {
database = "SNOW"
schema = "PUBLIC"
name = "SNOW_EVENTS"
comment = "Events from S3 are transformed into this table"
cluster_by = ["org_id"]
change_tracking = false
column {
name = "_date_created"
type = "TIMESTAMP_NTZ(9)"
nullable = false
default {
expression = "CURRENT_TIMESTAMP()"
}
}
}
Error is
Error: Unsupported block type │ │ on ../../modules/snowplow-s3-storage/tables.tf line 21, in resource "snowflake_table" "snow_events_table": │ 21: default { │ │ Blocks of type "default" are not expected here.

Resources