I have some very simple code to do some testing with and I am running into some issues.
My overall goal is to have config stored in a .yaml file which I then use templatefile to replace some variables and finally it will be jsonencoded.
main.tf
locals {
test = yamldecode(templatefile("${path.module}/test.yaml",
{
admin_groups = ["123456-7890"]
}
))
}
output "test" {
value = jsonencode(local.test)
}
test.yaml
GITLAB_OMNIBUS_CONFIG: |
gitlab_rails['omniauth_providers'] = |
admin_groups: ['${admin_groups}']
When terraform plan however I am getting the following error message:
│ Error: Error in function call
│
│ on main.tf line 2, in locals:
│ 2: test = yamldecode(templatefile("${path.module}/test.yaml",
│ 3: {
│ 4: admin_groups = ["123456-7890"]
│ 5: }
│ 6: ))
│ ├────────────────
│ │ path.module is "."
│
│ Call to function "templatefile" failed: ./test.yaml:3,25-37: Invalid template interpolation value; Cannot include the given value in a string template: string required..
Is there some way to interpolate this list of strings that I am not doing?
Cheers for any help.
For list in yaml, you should iterate the values like:
test.yaml
GITLAB_OMNIBUS_CONFIG: |
gitlab_rails['omniauth_providers'] = |
admin_groups:
%{ for g in admin_groups ~}
- ${g}
%{ endfor ~}
Related
I have the following config.yaml file
entities:
admins:
someone#somewhere.com
viewers:
anotherone#somewhere.com
And I want to create vault entities based on the users below the yaml nodes admins and viewers
locals {
config = yamldecode(file("config.yaml"))
admins = keys(local.config["entities"]["admins"]...)
viewers = keys(local.config["entities"]["viewers"]...)
}
resource "vault_identity_entity" "admins" {
for_each = toset(local.admins)
name = each.key
policies = ["test"]
metadata = {
foo = "bar"
}
}
resource "vault_identity_entity" "viewers" {
for_each = toset(local.viewers)
name = each.key
policies = ["test"]
metadata = {
foo = "bar"
}
}
The code above fails with:
│ Error: Invalid expanding argument value
│
│ on ../../../../entities/main.tf line 3, in locals:
│ 3: admins = keys(local.config["entities"]["admins"]...)
│ ├────────────────
│ │ while calling keys(inputMap)
│ │ local.config["entities"]["admins"] is "someone#somewhere.com"
│
│ The expanding argument (indicated by ...) must be of a tuple, list, or set
│ type.
╵
╷
│ Error: Invalid index
│
│ on ../../../../entities/main.tf line 4, in locals:
│ 4: viewers = keys(local.config["entities"]["viewers"]...)
│ ├────────────────
│ │ local.config["entities"] is object with 2 attributes
│
│ The given key does not identify an element in this collection value.
How should I structure my yaml file?
It seems like you want the YAML to be a hash of a hash of a list of strings. You can restructure for that like:
entities:
admins:
- someone#somewhere.com
viewers:
- anotherone#somewhere.com
This will recast to map(map(list(string))) when yamldecode from a YAML format string to HCL2.
However, you are also attempting to convert the type with the ellipsis operator ... in your locals, and returning only the keys. I am unsure why you are doing that, and both should be removed:
admins = local.config["entities"]["admins"]
viewers = local.config["entities"]["viewers"]
Afterwards, you can convert to a set type with toset like you are doing already, and then leverage that within the for_each meta-argument as per usual:
for_each = toset(local.admins)
for_each = toset(local.viewers)
This will result in the desired behavior.
I have the following locals file. I need to get the child and parent names separately in for each in terraform.
locals:
{
l3_crm:
[
{ parent: "crm", child: ["crm-sap", "crm-sf"] },
{ parent: "fin", child: ["fin-mon"] },
]
}
For the following ou creation code in aws, parent_id needs the parent name from the locals and ou_name needs the corresponding child name iterated.
module "l3_crm" {
source = "./modules/ou"
for_each = { for idx, val in local.l3_crm : idx => val }
ou_name = [each.value.child]
parent_id = module.l2[each.key.parent].ou_ids[0]
depends_on = [module.l2]
ou_tags = var.l2_ou_tags
}
I get the following error:
│ Error: Unsupported attribute
│
│ on main.tf line 30, in module "l3_rnd":
│ 30: parent_id = module.l2[each.key.parent].ou_ids[0]
│ ├────────────────
│ │ each.key is a string, known only after apply
│
│ This value does not have any attributes.
╵
Let me know what I am doing wrong in for loop.
I tried this as well:
module "l3_rnd" {
source = "./modules/ou"
for_each = { for parent, child in local.l3_crm : parent => child }
ou_name = [each.value]
parent_id = module.l2[each.key].ou_ids[0]
depends_on = [module.l2]
ou_tags = var.l2_ou_tags
}
with the local.tf:
locals {
l3_crm = [
{ "rnd" : ["crm-sap", "crm-sf"] },
{ "trade" : ["fin-mon"] }
]
}
I get these errors:
╷
│ Error: Invalid value for module argument
│
│ on main.tf line 28, in module "l3_crm":
│ 28: ou_name = [each.value]
│
│ The given value is not suitable for child module variable "ou_name" defined
│ at modules\ou\variables.tf:1,1-19: element 0: string required.
╵
╷
│ Error: Invalid value for module argument
│
│ on main.tf line 28, in module "l3_crm":
│ 28: ou_name = [each.value]
│
│ The given value is not suitable for child module variable "ou_name" defined
│ at modules\ou\variables.tf:1,1-19: element 0: string required.
╵
╷
│ Error: Invalid index
│
│ on main.tf line 29, in module "l3_crm":
│ 29: parent_id = module.l2[each.key].ou_ids[0]
│ ├────────────────
│ │ each.key is "1"
│ │ module.l2 is object with 2 attributes
│
│ The given key does not identify an element in this collection value.
╵
╷
│ Error: Invalid index
│
│ on main.tf line 29, in module "l3_crm":
│ 29: parent_id = module.l2[each.key].ou_ids[0]
│ ├────────────────
│ │ each.key is "0"
│ │ module.l2 is object with 2 attributes
│
│ The given key does not identify an element in this collection value.
╵
time=2022-11-11T13:24:15Z level=error msg=Hit multiple errors:
Hit multiple errors:
exit status 1
With your current structure you can reconstruct the map in your meta-argument like:
for_each = { for l3_crm in local.l3_crm : l3_crm.parent => l3_crm.child }
to access the values of each key in the list element and reconstruct to a map of parent keys and child values.
You can also optimize the structure like:
l3_crm:
[
{ "crm" = ["crm-sap", "crm-sf"] },
{ "fin" = ["fin-mon"] },
]
and then:
for_each = { for parent, child in local.l3_crm : parent => child }
where you cannot simply convert to a set type with toset because set(map) is not allowed as an argument value type.
Either way the references are updated fully accordingly:
ou_name = [each.key]
parent_id = module.l2[each.value].ou_ids[0]
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.
I'm trying to create a ssm parameter in aws for dyanamo db table with name and arn so it can be referenced by another application. I've tried a few different things but can't figure out what I'm doing wrong here. Can anyone tell me what is wrong here?
Thank you
Here's my main file with the module.
main.tf
locals {
prefix = "/this/is/a/test"
}
module "test_table" {
source = "git#github.com:test/terraform-modules.git//dynamodb"
name = "dynamo-${local.environment}"
ssm_parameter_prefix = local.prefix
tags = {
Environment = local.environment
}
}
resource "aws_ssm_parameter" "table_name" {
provider = aws.east
name = "${local.prefix}/new/table-name"
type = "String"
value = module.test_table.name
}
resource "aws_ssm_parameter" "table_arn" {
provider = aws.east
name = "${local.prefix}/new/table-arn"
type = "String"
value = module.test_table.arn
}
Here is the output
outputs.tf
output "test_table" {
value = module.test_table
}
output "table_arn" {
value = module.test_table.arn
}
output "table_name" {
value = module.test_table.name
}
Terraform Error
│ Error: Unsupported attribute
│
│ on dynamo.tf line, in resource "aws_ssm_parameter" "table_name":
│ 118: value = module.test_table.name
│ ├────────────────
│ │ module.test_table is a object, known only after apply
│
│ This object does not have an attribute named "name".
╵
╷
│ Error: Unsupported attribute
│
│ on dynamo.tf line, in resource "aws_ssm_parameter" "table_arn":
│ 125: value = module.test_table.arn
│ ├────────────────
│ │ module.test_table is a object, known only after apply
│
│ This object does not have an attribute named "arn".
╵
The output blocks you showed declare output values named table_arn and table_name, but your references are to module.test_table.name and module_test.table.arn.
You'll need to either change the output value names to match the references, or change the references to match the output value names. Specifically, you'll need to either remove the table_ prefix from each of your output value names, or add table_ to the front of the references like module.test_table.table_name and module.test_table.table_arn.
I am trying to read the yaml file and assign value to local variable, below code giving `Invalid index' error. how to fix this error message?
YAML file server.yaml
vm:
- name: vmingd25
- system_cores: 4
Code block
locals {
vm_raw = yamldecode(file("server.yaml"))["vm"]
vm_name= local.vm_raw["name"]
vm_cpu = local.vm_raw["system_cores"]
}
Error message
╷
│ Error: Invalid index
│
│ on main.tf line 16, in locals:
│ 16: vm_name= local.vm_raw["name"]
│ ├────────────────
│ │ local.vm_raw is tuple with 10 elements
│
│ The given key does not identify an element in this collection value: a number is required.
╵
╷
│ Error: Invalid index
│
│ on main.tf line 17, in locals:
│ 17: vm_cpu = local.vm_raw["system_cores"]
│ ├────────────────
│ │ local.vm_raw is tuple with 10 elements
│
│ The given key does not identify an element in this collection value: a number is required.
Your YAML is equivalent to the following JSON:
{
"vm": [
{
"name": "vmingd25"
},
{
"system_cores": 4
}
]
}
As you can see the vm element is a list of objects because you are using the - character. This means you need to either:
Change you YAML to remove the - list definition. e.g.
vm:
name: vmingd25
system_cores: 4
This would turn the list into a dictionary so you could index with the keys as you have done in your question. OR
If you cannot change the YAML then you will need to index with an integer. This might work if your YAML never changes but is definitely not recommended.