Terraform: Unsupported attribute when referencing output variables - terraform

I have a question regarding terraform. I have the following setup:
rds/
main.tf
variables.tf
imports.tf
outputs.tf
database-service.tf
main.tf
variables.tf
imports.tf
outputs.tf
I have inside the rds folder my basic rds terraform scripts (as a module) which I want to use inside the database-service.tf file.
Inside the rds/outputs.tf are the following outputs defined:
output "foo-aurora-db-endpoint" {
description = "foo DB Endpoint"
value = aws_rds_cluster.foo_db_default.endpoint
}
output "foo-aurora-db" {
description = "foo DB"
value = aws_rds_cluster.foo_db_default
}
output "foo-aurora-db-port" {
description = "foo DB Port"
value = aws_rds_cluster.foo_db_default.port
}
These values are referenced inside the outputs.tf:
output "foo-aurora-db-endpoint" {
value = module.foo-db.foo-aurora-db-endpoint
}
output "foo-aurora-db-port" {
value = module.foo-db.foo-aurora-db-port
}
My database-service.tf looks like this:
module "foo-db" {
source = "./rds"
...
}
A snippet of my rds/main.tf file
resource "aws_rds_cluster" "foo_db_default" {
cluster_identifier = var.cluster_identifier
availability_zones = ["eu-central-1a", "eu-central-1b", "eu-central-1c"]
database_name = var.database_name
master_username = var.master_username
master_password = var.master_password
engine = var.database_engine
skip_final_snapshot = true
db_subnet_group_name = aws_db_subnet_group.foo_db_default.name
vpc_security_group_ids = [aws_security_group.foo_db_rds.id]
}
When I now run terraform plan or apply I get the following error
Error: Unsupported attribute
on outputs.tf line 35, in output "foo-aurora-db-endpoint":
35: value = module.foo-db.foo-aurora-db-endpoint
This value does not have any attributes.
What am I missing?
Edit: When referencing module.foo-db inside the outputs.tf I get the following output:
foo-aurora-db-endpoint = [
{
"foo-aurora-db-endpoint" = "<endpoint value>"
"foo-aurora-db-password" = "<password>"
"foo-aurora-db-port" = <port>
"foo-aurora-db-username" = "<username>"
},
]
But I can't figure out how to reference them.

Related

Combining/merging variables in terraform

I am working within terraform and I am trying to combine variables.
I have been able to do this previously in the format of
name = "${var.name}-${var.environment}"
or something like
domain = "${var.environment}.${var.domain}"
Now I am trying to accomplish something similar but my module that I would like to do this with is utilizing a for_each.
I am trying to specify a host using variables that would represent utility.environment.domain.
Module:
module "aws_alb_listener_rule" {
depends_on = [
module.aws_lb_target_group,
module.aws_alb_listener_https
]
source = "../../terraform/modules/aws_lb_listener_rule_https"
listener_rule = module.aws_alb_listener__https.lb_listener
for_each = var.target_group_listener_rule_values
listener_rule_host_header = "${each.value.host_header}.${var.environment}.${var.domain}"
#listener_rule_host_header = each.value["host_header"]
listener_rule_target_group = module.aws_lb_target_group[each.key].arn
listener_rule_action_type = each.value["action_type"]
Where the first host header is what I was hoping to include but the commented out host header is how I currently have it.
My variables:
variable "target_group_listener_rule_values" {
description = "Specify the target group and listener rule settings and it will create on a 1:1 ratio"
type = map(object({
/*--- Listener Rule ---*/
host_header = list(string)
#host_header = string
target_group = string
action_type = string
my tfvars
listener_rule_values = {
Utility1 = {
/*--- Listener Rule ---*/
"host_header" = ["utility"],
#"host_header" = ["utility.environment.domain"],
"action_type" = "forward",
},
Utility2 = {
/*--- Listener Rule ---*/
"host_header" = ["utility"],
#"host_header" = ["utility.environment.domain"],
"action_type" = "forward",
},
Where the first host header is what I was hoping to include but the commented out host header is how I currently have it.
Child module:
resource "aws_lb_listener_rule" "static" {
listener_arn = var.listener_rule
action {
type = var.listener_rule_action_type
target_group_arn = var.listener_rule_target_group
}
condition {
host_header {
values = var.listener_rule_host_header
}
}
}
What I would like to do is shift this over to use variables instead as I am trying to remove the environment from being entered anywhere in the config except for the "environment" variable.
listener_rule_host_header = "${each.value["host_header"]}.${var.environment}.${var.domain}"
the error I am seeing is
Error: Invalid template interpolation value
│
│ on main.tf line 181, in module "module":
│ 181: listener_rule_host_header = "${each.value["host_header"]}.${var.environment}.${var.domain}"
│ ├────────────────
│ │ each.value["host_header"] is list of string with 1 element
│
│ Cannot include the given value in a string template: string required.
you are very close and your syntax for interpolation is correct. What is not correct is the fact that your each.value["host_header"] is a list, so Terraform complains about that.
I don't know why you want a list, and You did not give your full code, so let me show you a working example with map, that I imagine you want to accomplish:
variable "environment" {
default = "stage"
}
variable "domain" {
default = "example.com"
}
variable "sites" {
type = map(any)
default = {
"utils" = {
"host_header" = "utility"
}
"main" = {
"host_header" = "www"
}
}
}
resource "local_file" "hosts" {
for_each = var.sites
filename = "${each.value.host_header}.${var.environment}.${var.domain}"
content = each.key
}
You can adjust your code based on this example, I hope.
Now, if you insist on using lists, this would look like this:
variable "sites_with_list" {
type = map(any)
default = {
"utils" = {
"host_headers" = ["utility"]
}
"main" = {
"host_headers" = ["www"]
}
}
}
resource "local_file" "hosts_list" {
for_each = var.sites_with_list
filename = "${each.value.host_headers[0]}.${var.environment}.${var.domain}"
content = each.key
}
I did not know how you want to utilize this, so I used a local_file just as an example that will work when copied to empty project.

Produce repeating blocks inside a terraform resource

I am fairly new to terraform and trying to create a google_compute_backend_service using terraform and there are multiple backend blocks inside the resource as shown below:
resource "google_compute_backend_service" "app-backend" {
log_config {
enable = "true"
sample_rate = "1"
}
name = "app-backend"
port_name = "http-34070"
project = "my-project"
protocol = "HTTP"
session_affinity = "NONE"
timeout_sec = "30"
backend {
group = "insatnce-group1"
}
backend {
group = "instance-group2"
}
backend {
group = "instance-group3"
}
health_checks = [google_compute_http_health_check.app-http-l7.name]
}
As seen in the code block above the backend block repeats multiple times. I want to make it dynamic so I do not have to write multiple blocks manually.
I tried the following:
Created a variable in the variables.tf file that contains all the instance groups:
variable "groups" {
type = list(object({
name = string
}))
default = [{ name = "instance-group1"},
{ name = "instance-group2"},
{ name = "instance-group3"}]
}
And modified my resource block to this:
resource "google_compute_backend_service" "app-backend" {
log_config {
enable = "true"
sample_rate = "1"
}
name = "app-backend"
port_name = "http-34070"
project = "my-project"
protocol = "HTTP"
session_affinity = "NONE"
timeout_sec = "30"
dynamic "backend" {
for_each = var.groups
iterator = item
group = item.value.name
}
health_checks = [google_compute_http_health_check.app-http-l7.name]
}
However, when I execute terraform plan I get the following error:
Error: Unsupported argument
│
│ on backend_service.tf line 15, in resource "google_compute_backend_service" "app-backend":
│ 15: group = item.value.name
│
│ An argument named "group" is not expected here.
Where am I going wrong? Is there a better way to achieve this?
You can check the dynamic blocks documentation for the syntax. Otherwise, you had the right idea.
dynamic "backend" {
for_each = var.groups
content {
group = backend.value.name
}
}
You can also simplify the variable structure to make this even easier.
variable "groups" {
type = set(string)
default = ["instance-group1", "instance-group2", "instance-group3"]
}
dynamic "backend" {
for_each = var.groups
content {
group = backend.value
}
}

Because data.zabbix_template.template has "for_each" set, its attributes must be accessed on specific instances

I'm tring to create a zabbix template with applications defined and trigger.
I can create the template, import my hosts and associate to it.
Now when I try to add the trigger to the template, I receive the error in the object.
this is my
data.tf
data "zabbix_hostgroup" "group" {
name = "Templates"
}
data "zabbix_template" "template" {
for_each = {
common_simple = { name = "Common Simple" }
common_snmp = { name = "Common SNMP" }
class_template = { name = var.class_names[var.class_id] }
}
name = each.value.name
}
data "zabbix_proxy" "proxy" {
for_each = {
for inst in var.instances :
"${inst.instance}.${inst.site}" => inst.site
}
#host = "zabpxy01.${each.value}.mysite.local"
host = "mon-proxy1.${each.value}.mtsite.local"
}
and this is my hosts.tf:
# create host group for specific to service
resource "zabbix_hostgroup" "hostgroup" {
name = var.class_names[var.class_id]
}
# create template
resource "zabbix_template" "template" {
host = var.class_id
name = var.class_names[var.class_id]
description = var.class_names[var.class_id]
groups = [
data.zabbix_hostgroup.group.id
]
}
# create application
resource "zabbix_application" "application" {
hostid = data.zabbix_template.template.id
name = var.class_names[var.class_id]
}
# create snmp disk_total item
resource "zabbix_item_snmp" "disk_total_item" {
hostid = data.zabbix_template.template.id
key = "snmp_disk_root_total"
name = "Disk / total"
valuetype = "unsigned"
delay = "1m"
snmp_oid="HOST-RESOURCES-MIB::hrStorageSize[\"index\", \"HOST-RESOURCES-MIB::hrStorageDescr\", \"/\"]"
depends_on = [
data.zabbix_template.template
]
}
# create snmp disk_used item
resource "zabbix_item_snmp" "disk_used_item" {
hostid = data.zabbix_template.template.id
key = "snmp_disk_root_used"
name = "Disk / used"
valuetype = "unsigned"
delay = "1m"
snmp_oid="HOST-RESOURCES-MIB::hrStorageUsed[\"index\", \"HOST-RESOURCES-MIB::hrStorageDescr\", \"/\"]"
depends_on = [
data.zabbix_template.template
]
}
# create trigger > 75%
resource "zabbix_trigger" "trigger" {
name = "Disk Usage 75%"
expression = "({${data.zabbix_template.template.host}:${zabbix_item_snmp.disk_used_item.key}.last()} / {${data.zabbix_template.template.host}:${zabbix_item_snmp.disk_total_item.key}.last()}) * 100 >= 75"
priority = "warn"
enabled = true
multiple = false
recovery_none = false
manual_close = false
}
# create hosts
resource "zabbix_host" "host" {
for_each = {
for inst in var.instances : "${var.class_id}${format("%02d", inst.instance)}.${inst.site}" => inst
}
host = var.ip_addresses[var.class_id][each.value.site][each.value.instance]["hostname"]
name = var.ip_addresses[var.class_id][each.value.site][each.value.instance]["hostname"]
enabled = false
proxyid = data.zabbix_proxy.proxy["${each.value.instance}.${each.value.site}"].id
groups = [
zabbix_hostgroup.hostgroup.id
]
templates = concat ([
data.zabbix_template.template["common_simple"].id,
data.zabbix_template.template["common_snmp"].id,
zabbix_template.template.id
])
# add SNMP interface
interface {
type = "snmp"
ip = var.ip_addresses[var.class_id][each.value.site][each.value.instance]["mgmt0"]
main = true
port = 161
}
# Add Zabbix Agent interface
interface {
type = "agent"
ip = var.ip_addresses[var.class_id][each.value.site][each.value.instance]["mgmt0"]
main = true
port = 10050
}
macro {
name = "{$INTERFACE_MONITOR}"
value = var.ip_addresses[var.class_id][each.value.site][each.value.instance]["mgmt0"]
}
macro {
name = "{$SNMP_COMMUNITY}"
value = var.ip_addresses[var.class_id][each.value.site][each.value.instance]["snmp"]
}
depends_on = [
zabbix_hostgroup.hostgroup,
data.zabbix_template.template,
data.zabbix_proxy.proxy,
]
}
output "class_template_id" {
value = zabbix_template.template.id
description = "Template ID of created class template for items"
}
When I run "Terraform plan" I receive the error:
Error: Missing resource instance key │ │ on hosts/hosts.tf line 26,
in resource "zabbix_application" "application": │ 26: hostid =
data.zabbix_template.template.id │ │ Because
data.zabbix_template.template has "for_each" set, its attributes must
be accessed on specific instances. │ │ For example, to correlate with
indices of a referring resource, use: │
data.zabbix_template.template[each.key]
Where is my error?
Thanks for the support
UPDATE
I tried to use
output "data_zabbix_template" {
value = data.zabbix_template.template
}
but I don't see any output when I run terraform plan
I tried to modify in:
hostid = data.zabbix_template.template.class_template.id
but I continue to receive the same error:
Error: Missing resource instance key on hosts/hosts.tf line 27, in
resource "zabbix_application" "application": 27: hostid =
data.zabbix_template.template.class_template.id Because
data.zabbix_template.template has "for_each" set, its attributes must
be accessed on specific instances.
For example, to correlate with indices of a referring resource, use:
data.zabbix_template.template[each.key]
Error: Unsupported attribute on hosts/hosts.tf line 27, in resource
"zabbix_application" "application": 27: hostid =
data.zabbix_template.template.class_template.id This object has no
argument, nested block, or exported attribute named "class_template".
UPDATE:
My script for each host taht I'll add, set two existing template ("Common Simple" and "Common SNMP") and create a new template as below:
# module.mytemplate-servers_host.zabbix_template.template will be created
+ resource "zabbix_template" "template" {
+ description = "mytemplate-servers"
+ groups = [
+ "1",
]
+ host = "mytemplate-servers"
+ id = (known after apply)
+ name = "mytemplate-servers"
}
Now my scope is to add on this template an application and set two items and one trigger
When you use for_each in a data source or resource, the output of that data source or resource is a map, where the keys in the map are the same as the keys in the for_each and the values are the regular output of that data/resource for the given input value with that key.
Try using:
output "data_zabbix_template" {
value = data.zabbix_template.template
}
And you'll see what I mean. The output will look something like:
data_zabbix_template = {
common_simple = {...}
common_snmp = {...}
class_template = {...}
}
So in order to use this data source (on the line where the error is being thrown), you need to do:
hostid = data.zabbix_template.template.common_simple.id
And replace common_simple in that line with whichever key in the for_each you want to use. You'll need to do this everywhere that you use data.zabbix_template.template.

Why am I getting an 'Incorrect attribute value type' error when I create a variable in Terraform?

I'm getting an Incorrect attribute value type when I try to create variables in TF.
I'm on this version of TF:
Terraform v0.14.4
+ provider registry.terraform.io/hashicorp/aws v2.70.0
This is the error that I'm getting:
λ terraform apply
Error: Incorrect attribute value type
on main.tf line 18, in resource "aws_instance" "example":
18: vpc_security_group_ids = var.vpc_security_group_ids["jf-master-pd"]
Inappropriate value for attribute "vpc_security_group_ids": set of string
required.
This is the code block in my main.tf code:
resource "aws_instance" "example" {
ami = var.amis["us-east-1"]
instance_type = "t2.micro"
vpc_security_group_ids = var.vpc_security_group_ids["jf-master-pd"]
subnet_id = var.subnet_id["jf-master-pd"]
key_name = "jf-timd-keypair"
}
These are the variables that I'm trying to set in the variables.tf file:
variable "vpc_security_group_ids" {
type = map(string)
default = {
"jf-master-pd" = "sg-0333d9eaaeb3ab1b0"
"jf-master-pd-gov" = "sg-7f051404"
}
}
variable "subnet_id" {
type = map(string)
default = {
"jf-master-pd" = "subnet-3ab1835d"
"jf-master-pd-gov" = "subnet-4dad6304"
}
}
The main.tf and variables.tf files are living in the same directory.
Why am I getting this error? How do I correct it?
I had to supply a list in the variable. That fixed the problem:
variable "vpc_security_group_ids" {
type = map(list(string))
default = {
"jf-master-pd" = ["sg-0333d9eaaeb3ab1b0"]
"jf-master-pd-gov" = ["sg-7f051404"]
}
}

Terraform Modules - Unable to Access variable from root

I am trying to pass a variable from the root module to a child module with the following syntax and i'm unable to do that:
└── Terraform
├── main.tf
├── variable.tf
└── module
├──main.tf
├── variable.tf
Terraform Version:
Terraform v0.11.11
+ provider.openstack v1.15.0
Terraform Configuration Files
/Terraform/main.tf:
provider "openstack" {
openstack_user_name = "${var.openstack_user_name}"
openstack_tenant_name = "${var.openstack_tenant_name}"
openstack_password = "${var.openstack_password}"
openstack_auth_url = "${var.openstack_auth_url}"
domain_name = "${var.domain_name}"
}
module "testMod" {
name = "${var.name}"
imageId = "${var.imageId}"
flavor_name = "${var.flavor_name}"
openstack_keypair = "${var.openstack_keypair}"
tenant_network_id = "${var.tenant_network_id}"
tenant_network = "${var.tenant_network}"
source = "./modules"
}
/Terraform/variable.tf:
variable "name" {default = "XXX"}
variable "imageId" {default = "11-22-33"}
variable "flavor_name"{default = "flavor"}
...
/Terraform/modules/main.tf:
resource "openstack_compute_instance_v2" "test" {
name = "${var.name}"
imageId = "${var.imageId}"
flavor_name = "${var.flavor_name}"
openstack_keypair = "${var.openstack_keypair}"
security_groups = ["default"]
network {
tenant_network_id = "${var.tenant_network_id}"
tenant_network = "${var.tenant_network}"
}
}
/Terraform/modules/variable.tf:
variable "name" {}
variable "imageId" {}
variable "flavor_name" {}
variable "openstack_keypair" {}
variable "tenant_network_id"{}
variable "tenant_network" {}
Actual Behavior
Error: module.testMod.openstack_compute_instance_v2.test: : invalid or unknown key: imageId
Steps to Reproduce
terraform init
terraform apply
Unsure what is going wrong here
The error is alerting you to the unknown keyimageId. This message is accurate as, in fact, the key should be image_id. You can check the Terraform openstack_compute_instance_v2 resource documentation and note the presence of the image_id argument.
Your code would then look like:
resource "openstack_compute_instance_v2" "test" {
name = "${var.name}"
image_id = "${var.imageId}"
flavor_name = "${var.flavor_name}"
openstack_keypair = "${var.openstack_keypair}"
security_groups = ["default"]
}
I have this requirement to access variables from root variable.tf inside module/myservice/main.tf
How can we achive that ? By using var.environmentdoes not work
e.g:
root/variable.tf
variable "environment" {
type = string
description = "Resource environment e.g dev, test or prod."
default = "dev"
}
How to access above variable var.environment in root/module/myservice/main.tf using ${var.environment} does not work.
root/module/myservice/main.tf
locals {
name = "${var.environment}.myInstance"
}
resource "aws_network_interface" "foo" {
name = local.name
.
.
.
}

Resources