variable:
gateways:
gateway1:
routingConfig:
sslCertificates: [
"a",
"b"
],
## amended for brievity
gateway2:
routingConfig: {}
## amended for brievity
code attempt:
data "azurerm_key_vault_secret" "ssl_certificates" {
for_each = {
for k, v in local.gateways : v.routing_config.ssl_certificates
if v.routing_config != {}
}
name = each.value
key_vault_id = data.azurerm_key_vault.ssl_certificates.id
}
which doesnt work, it only creates a single iteration per gateway and attempts to insert tuple into the name:
each.value is tuple with 2 element
which is expected, if you think about it, but I cant figure out how do I do a loop inside a loop
for the gateway same data structure works, because I can do dynamic for ssl_certificates property, but I dont think I can do that here
final solution:
certificates_flat = merge([
for gtw, gtw_details in local.gateways : {
for ssl_cert in gtw_details.routing_config.ssl_certificates :
ssl_cert => ssl_cert
} if gtw_details.routing_config != {}
]...)
data "azurerm_key_vault_secret" "ssl_certificates" {
for_each = local.certificates_flat
name = each.value
key_vault_id = data.azurerm_key_vault.ssl_certificates.id
}
how do I do a loop inside a loop
You have to flatten your variable. For example as follows:
locals {
gateways_flat = merge([
for gtw, gtw_details in local.gateways: {
for ssl_cert in gtw_details.routing_config.ssl_certificates:
"${gtw}-${ssl_cert}" => {
ssl_certificate = ssl_cert
}
} if gtw_details.routing_config != {}
]...)
}
giving:
{
"gateway1-a" = {
"ssl_certificate" = "a"
}
"gateway1-b" = {
"ssl_certificate" = "b"
}
}
then
data "azurerm_key_vault_secret" "ssl_certificates" {
for_each = local.gateways_flat
name = each.value.ssl_certificate
key_vault_id = data.azurerm_key_vault.ssl_certificates.id
}
Related
I am having a azurerm_postgresql_flexible_server resource using count and azurerm_postgresql_flexible_server_configuration using fore each. Please find the below code.
config= [{
"name" = shared_preload_libraries,
"values" = ["EXAMPLE1", "EXAMPLE2"]
},
{
"name" = "azure.extensions"
"values" = ["EXAMPLE1", "EXAMPLE2", "EXAMPLE3"]
}]
locals {
flat_config = merge([
for single_config in var.config: {
for value in single_config.values:
"${single_config.name}-${value}" => {
"name" = single_config.name
"value" = value
}
}
]...)
}
Below is my for_each resource
resource "azurerm_postgresql_flexible_server_configuration" "example" {
for_each = local.flat_config
name = each.value.name
server_id = azurerm_postgresql_flexible_server.example.id
value = each.value.value
}
currently I am having two azurerm_postgresql_flexible_server resources. azurerm_postgresql_flexible_server.example[0] and azurerm_postgresql_flexible_server.example[1]. Could you please let me know if there is a possibility to include some alternative options like count?
Splat Expressions did not work.
If you want to apply your two azurerm_postgresql_flexible_server for each instance of azurerm_postgresql_flexible_server_configuration, you need one more extra level of flattening:
locals {
flat_config = merge(flatten([
for single_config in var.config: [
for value in single_config.values: {
for idx, server in azurerm_postgresql_flexible_server.example:
"${single_config.name}-${value}-${idx}" => {
"name" = single_config.name
"value" = value
"flexible_server" = server
}
}
]
])...)
}
then
resource "azurerm_postgresql_flexible_server_configuration" "example" {
for_each = local.flat_config
name = each.value.name
server_id = each.value.flexible_server.id
value = each.value.value
}
Is there a way to modify this output code to include the index number with the name? The current code below creates one output with Index '0', but each name should ideally be in its own Index (0, 1, 2, etc.)
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "3.1.0"
}
}
}
provider "azurerm" {
features {
}
}
variable "private_link_scopes" {
description = "A list of Azure private link scopes."
type = list(object({
name = string
resource_group_name = string
}))
}
locals {
rgs_map = {
for n in var.private_link_scopes : n.resource_group_name => {
name = n.resource_group_name
}
}
}
data "azurerm_resource_group" "rg" {
for_each = local.rgs_map
name = each.value.name
}
output "list" {
value = { for index, item in [var.private_link_scopes] : index => item[*].name }
}
It seems like there is a lot of structure change going on here. That may be due to other baggage or reasons that depend on it, but I think this could be simplified. I use locals in lieu of the variable, but I hope is help.
I'm not sure the splat operator there is what you want. That is the same as getting a list of all items' name attributes in that item value, but there would only be one per item so that seems strange.
locals {
scopes = [
{
name = "name_a"
rg_name = "rg_a"
},
{
name = "name_b"
rg_name = "rg_b"
}
]
}
data "azurerm_resource_group" "rg" {
# I don't see a reason to generate the intermediary map
# or to build another object in each map value. This is more
# simple. for_each with an object by name: rg_name
value = { for scope in local.scopes : scope.name => scope.rg_name }
name = each.value
}
output "list" {
# I don't see a need to use a splat expression here.
# You can just build a map with the index and the name
# here directly.
value = { for i, v in local.scopes : i => v.name }
}
In fact, if you don't need the resource group resources to be keyed by name, that can be simplified further to:
data "azurerm_resource_group" "rg" {
for_each = toset([for scope in local.scopes : scope.rg_name])
name = each.value
}
I have 2 services test1,test2 and for each service i have to create 6 vm's.This 6 vm's should be placed in 3 subnet id's which created in 3 different zones in a same region
In this services,test1 will be in private subnets and test2 will be in public subnets.So i have to pass that correct subnet id when creating ec2 instances
root module:
provider "aws" {
region = var.region
}
module "ecom-vpc" {
source = "./modules/vpc"
}
module "ecom-public-subnet" {
source = "./modules/subnets/public"
vpc-id = module.ecom-vpc.vpc-id
}
module "ecom-private-subnet" {
source = "./modules/subnets/private"
vpc-id = module.ecom-vpc.vpc-id
}
module "ecom-instances-sg" {
source = "./modules/sg"
vpc-id = module.ecom-vpc.vpc-id
}
module "ecom-vm-instances" {
source = "./modules/vm"
priv-subnet-ids = module.ecom-private-subnet.ecom_private_subnets
pub-subnet-ids = module.ecom-public-subnet.ecom_public_subnets
instances-sg = module.ecom-instances-sg.ecom-inst-sg
}
From child modules - vpc,subnets,ec2
variable "service-names" {
type = list(any)
default = ["ecom-app-TEST1","ecom-app-TEST2"]
}
variable "availability_zones" {
type = map
default = {
ap-south-1a = {
private_subnet = "10.0.1.0/24"
public_subnet = "10.0.4.0/24"
}
ap-south-1b = {
private_subnet = "10.0.2.0/24"
public_subnet = "10.0.5.0/24"
}
ap-south-1c = {
private_subnet = "10.0.3.0/24"
public_subnet = "10.0.6.0/24"
}
}
}
resource "aws_vpc" "ecom-vpc" {
cidr_block = var.ecom-cidr
}
output "vpc-id" {
value = aws_vpc.ecom-vpc.id
}
resource "aws_subnet" "ecom-private" {
for_each = var.availability_zones
vpc_id = var.vpc-id
cidr_block = each.value.private_subnet
availability_zone = each.key
map_public_ip_on_launch = false
tags = {
Name = "${split("-", each.key)[2]}"
Subnet_Type = "private"
}
}
output "ecom_private_subnets" {
value = aws_subnet.ecom-private
}
resource "aws_subnet" "ecom-public" {
for_each = var.availability_zones
vpc_id = var.vpc-id
cidr_block = each.value.public_subnet
availability_zone = each.key
map_public_ip_on_launch = true
tags = {
Name = "${split("-", each.key)[2]}"
Subnet_Type = "public"
}
depends_on = [aws_internet_gateway.igw
]
}
output "ecom_public_subnets" {
value = aws_subnet.ecom-public
}
I'm trying to achieve the same by creating a locals which combines service names,priv,public subnet id's,instance count(2).But the problem is i'm not able to make it because i'm not able to create a unique combination of keys
locals {
service_subnets = {
for pair in setproduct(var.service-names, values(var.priv-subnet-ids),values(var.pub-subnet-ids),range(var.instance_count)) :
"${pair[0]}:${pair[1].availability_zone}-${pair[3]}" => {
service_name = pair[0]
priv-subnet = pair[1]
pub-subnet = pair[2]
}
}
}
resource "aws_instance" "ecom-instances" {
for_each = local.service_subnets
ami = data.aws_ami.ecom.id
instance_type = "t3.micro"
tags = {
Name = each.value.service_name
Service = each.value.service_name
}
vpc_security_group_ids = [var.instances-sg[each.value.service_name].id]
subnet_id = "${split("-", each.value.service_name)[2] == "TEST1" ? each.value.pub-subnet.id : each.value.priv-subnet.id }"
}
I'm getting the below error.
Two different items produced the key "ecom-app-TEST1:ap-south-1c-1" in this 'for' expression. If duplicates are expected, use the ellipsis (...)
│ after the value expression to enable grouping by key.
If i add ... and change it as below then it is converted as tuple and i'm not sure how to get and pass the value from each.value in the aws_instance resource
locals {
service_subnets = {
for pair in setproduct(var.service-names, values(var.priv-subnet-ids),values(var.pub-subnet-ids),range(var.instance_count)) :
"${pair[0]}:${pair[1].availability_zone}-${pair[3]}" => {
service_name = pair[0]
priv-subnet = pair[1]
pub-subnet = pair[2]
}
... }
}
on modules/vm/main.tf line 60, in resource "aws_instance" "ecom-instances":
│ 60: subnet_id = "${split("-", each.value.service_name)[2] == "TEST1" ? each.value.pub-subnet.id : each.value.priv-subnet.id }"
│ ├────────────────
│ │ each.value is tuple with 3 elements
│
│ This value does not have any attributes.
Please Guide me
You could try adding the index to your for loop and making it part of your name, might help you avoid elipsis/tuple conversion.
locals {
service_subnets = {
for index, pair in setproduct(var.service-names, values(var.priv-subnet-ids),values(var.pub-subnet-ids),range(var.instance_count)) :
"${pair[0]}:${pair[1].availability_zone}-${pair[3]}=${index}" => {
service_name = pair[0]
priv-subnet = pair[1]
pub-subnet = pair[2]
}
}
}
I am trying to generate attributes dynamically in terraform 13. I've read through the docs but I can't seem to get this to work:
Given the following terraform:
#main.tf
locals {
secrets = {
secret1 = [
{
name = "user",
value = "secret"
},
{
name = "password",
value = "password123"
}
],
secret2 = [
{
name = "token",
value = "secret"
}
]
}
}
resource "kubernetes_secret" "secrets" {
for_each = local.secret
metadata {
name = each.key
}
data = {
[for name, value in each.value : name = value]
}
}
I would expect the following resources to be rendered:
resource "kubernetes_secret" "secrets[secret1]" {
metadata {
name = "secret1"
}
data = {
user = "secret"
password = "password123"
}
}
resource "kubernetes_secret" "secrets[secret2]" {
metadata {
name = "secret2"
}
data = {
token = "secret"
}
}
However I just get the following error:
Error: Invalid 'for' expression
on ../../main.tf line 96, in resource "kubernetes_secret" "secrets":
96: [for name, value in each.value : name = value]
Extra characters after the end of the 'for' expression.
Does anybody know how to make this work?
The correct syntax for generating a mapping using a for expression is the following:
data = {
for name, value in each.value : name => value
}
The above would actually be totally redundant, because it would produce the same value as each.value. However, because your local value has a list of objects with name and value attributes instead of maps from name to value, so to get a working result we'd either need to change the input to already be a map, like this:
locals {
secrets = {
secret1 = {
user = "secret"
password = "password123"
}
secret2 = {
token = "secret"
}
}
}
resource "kubernetes_secret" "secrets" {
for_each = local.secrets
metadata {
name = each.key
}
# each.value is already a map of a suitable shape
data = each.value
}
or, if the input being a list of objects is important for some reason, you can project from the list of objects to the mapping like this:
locals {
secrets = {
secret1 = [
{
name = "user",
value = "secret"
},
{
name = "password",
value = "password123"
}
],
secret2 = [
{
name = "token",
value = "secret"
}
]
}
}
resource "kubernetes_secret" "secrets" {
for_each = local.secrets
metadata {
name = each.key
}
data = {
for obj in each.value : obj.name => obj.value
}
}
Both of these should produce the same result, so which to choose will depend on what shape of local value data structure you find most readable or most convenient.
I'm hitting Error: Incorrect attribute value type when passing var_3 below to a tfe_variable of type HCL.
Is there a way to convert the decoded JSON variable to HCL?
My config.json:
{
"vars": {
"var_1": "foo",
"var_2": "bar",
"var_3": {
"default": "foo"
}
}
}
My terraform config:
variable "tfe_token" {}
provider "tfe" {
hostname = "app.terraform.io"
token = var.tfe_token
}
data "tfe_workspace" "this" {
name = "my-workspace-name"
organization = "my-org-name"
}
locals {
json_config = jsondecode(file("config.json"))
}
resource "tfe_variable" "workspace" {
for_each = local.json_config.vars
workspace_id = data.tfe_workspace.this.id
key = each.key
value = each.value
category = "terraform"
hcl = true
sensitive = false
}