Terraform Modules - Unable to Access variable from root - terraform

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

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

Terraform - azurerm_frontdoor_custom_https_configuration - 'the given key does not identify an element in this collection value'

this code has worked before, all I'm trying to do is add new frontend endpoints, routing rules, backend pools
I've tried only sharing the code snippets that I think are relevant but let me know if there's some key info you need missing
This one has stumped me for a couple days now and no matter what I've tried I cannot seem to make sense of the error. Its like its indexing out of the variable or searching for something that isn't there but there are something like 6 already there and now I'm adding another.
I'm worried that this front door code has not been ran in awhile and something has gotten screwed up in state. Especially given all the alerts on the accompanying TF docs for this resource - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/frontdoor_custom_https_configuration
Its been quite awhile but the AzureRM version has gone through several updates - possibly from previous to 2.58 to now past 2.58. I guess I also don't know how to verify/look at the state file and ensure its correct - even looking at the 2.58 upgrade notes its just confusing.
Ideas?
The error
on ..\modules\frontdoor\main.tf line 129, in resource "azurerm_frontdoor_custom_https_configuration" "https_config":
129: frontend_endpoint_id = azurerm_frontdoor.main.frontend_endpoints[each.value]
|----------------
| azurerm_frontdoor.main.frontend_endpoints is map of string with 8 elements
| each.value is "www-sell-dev-contoso-com"
The given key does not identify an element in this collection value.
main.tf
provider "azurerm" {
features {}
}
terraform {
backend "azurerm" {
}
}
#the outputs.tf on this module output things like the frontdoor_endpoints
#the outputs.tf with main.tf also output similar values
module "coreInfraFrontDoor" {
source = "../modules/frontdoor"
resource_group_name = module.coreInfraResourceGroup.resource_group_name
frontdoor_name = "fd-infra-${terraform.workspace}-001"
enforce_backend_pools_certificate_name_check = lookup(var.enforce_backend_pools_certificate_name_check, terraform.workspace)
log_analytics_workspace_id = module.coreInfraLogAnalytics.log_analytics_workspace_id
tags = local.common_tags
health_probes = lookup(var.health_probes, terraform.workspace)
routing_rules = lookup(var.routing_rules, terraform.workspace)
backend_pools = lookup(var.backend_pools, terraform.workspace)
frontend_endpoints = lookup(var.frontend_endpoints, terraform.workspace)
prestage_frontend_endpoints = lookup(var.prestage_frontend_endpoints, terraform.workspace)
frontdoor_firewall_policy_name = "fdfwp${terraform.workspace}001"
frontdoor_firewall_prestage_policy_name = "fdfwp${terraform.workspace}prestage"
mode = lookup(var.mode, terraform.workspace)
ip_whitelist_enable = lookup(var.ip_whitelist_enable, terraform.workspace)
ip_whitelist = lookup(var.ip_whitelist, terraform.workspace)
key_vault_id = module.coreInfraKeyVault.id
}
module main.tf
resource "azurerm_frontdoor" "main" {
name = var.frontdoor_name
location = "global"
resource_group_name = var.resource_group_name
enforce_backend_pools_certificate_name_check = var.enforce_backend_pools_certificate_name_check
tags = var.tags
dynamic "routing_rule {#stuff is here obv}
dynamic "backend_pool {#also here}
#i think this is because there was an issue/needs to be some default value for the first endpoint?
frontend_endpoint {
name = var.frontdoor_name
host_name = "${var.frontdoor_name}.azurefd.net"
web_application_firewall_policy_link_id = azurerm_frontdoor_firewall_policy.main.id
}
#now the dynamic ones from vars
dynamic "frontend_endpoint" {
for_each = var.frontend_endpoints
content {
name = frontend_endpoint.value.name
host_name = frontend_endpoint.value.host_name
session_affinity_enabled = lookup(frontend_endpoint.value, "session_affinity_enabled", false)
web_application_firewall_policy_link_id = azurerm_frontdoor_firewall_policy.main.id
}
}
versions.tf
terraform {
required_version = "~> 0.14.7"
required_providers {
azurerm = "~>2.72.0"
}
}
variables.tf
variable "frontend_endpoints" {
type = map(any)
description = "List of frontend (custom) endpoints. This is in addition to the <frontend_name>.azurefd.net endpoint that this module creates by default."
default = {
dev = [
{
name = "dev-search-contoso-com"
host_name = "dev.search.contoso.com"
},
{
name = "dev-cool-contoso-com"
host_name = "dev.cool.contoso.com"
},
########################
#this is new below
########################
{
name = "dev-sell-contoso-com"
host_name = "dev.sell.contoso.com"
}
]
prod = [ #you get the idea ]
}

Terraform: Unsupported attribute when referencing output variables

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.

Terraform - Using merge function makes the argument not excepted

Terraform Version
Terraform v0.12.18
+ provider.aws v2.42.0
+ provider.grafana v1.5.0
+ provider.helm v0.10.4
+ provider.kubernetes v1.10.0
+ provider.local v1.4.0
+ provider.null v2.1.2
+ provider.ovh v0.5.0
+ provider.random v2.2.1
+ provider.template v2.1.2
+ provider.vault v2.7.0
Terraform Configuration Files
# mymodule/service.k8s.tf
resource "kubernetes_service" "wordpress" {
metadata {
labels = merge(var.deployment_config.labels, {
app = "myname
engine = "wordpress"
tier = "frontend"
})
}
...
}
# mymodule/variables.tf
variable "deployment_config" {
description = "The deployment configuration."
type = object({
labels = map(string)
})
}
# modules.tf
module "my-module" {
source = "./mymodule"
deployment_config = {
labels = {
environment = "production"
}
}
}
Error
An argument named "labels" is not expected here.
Expected Behavior
The metadata.labels argument of the resource "kubernetes_service" (and other kubernetes resources) attempt to receive a map(). This should simply create the resource with theses labels (of both maps):
labels = {
app = "myname"
engine = "wordpress"
tier = "frontend"
environment = "production"
}
Actual Behavior
Without the use of the merge() function by simply putting a single map is working.
But when the merge() function is used Terraform say that the argument is not excepted.
Steps to Reproduce
Have a module with an argument of type map(string) (in this case kubernetes_service with the argument metadata.labels) which merge a variable of type map(string) with an other one map(string).
Pass to this module the variable of type map(string).
Then, terraform apply
Question
Why using merge() function makes the argument not excepted ?
Thanks in advance for your help !
My guess is that in mymodule/service.k8s.tf, there is a missing end quotation for the map key app's value. When I tried to run a terraform apply with the missing end quotation, I received a Error: Invalid multi-line string. Perhaps that was the issue you received ?
This seems to work for me.
$ tree
.
├── main.tf
└── mymodule
└── main.tf
1 directory, 2 files
$ cat main.tf
module "mymodule" {
source = "./mymodule"
deployment_config = {
labels = {
environment = "production"
}
}
}
output "mymodule" {
value = module.mymodule
}
$ cat mymodule/main.tf
variable "deployment_config" {
description = "The deployment configuration."
type = object({
labels = map(string)
})
}
locals {
labels = merge(var.deployment_config.labels, {
app = "myname"
engine = "wordpress"
tier = "frontend"
})
}
output "deployment_config" {
value = local.labels
}
$ ls
main.tf mymodule/
$ terraform version
Terraform v0.12.18
$ terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
mymodule = {
"deployment_config" = {
"app" = "myname"
"engine" = "wordpress"
"environment" = "production"
"tier" = "frontend"
}
}

Resources