Terraform: How to store a map in a single ssm-parameter and get back a value pair? - terraform

let's assume i have a map like this:
variable "test_parameters" {
type = map
default = {
"A" = "subnet-73e35d3e",
"B" = "subnet-7e00d503",
"C" = "subnet-d9d446b2",
}
}
What is the terraform-code
to store the values of the map in a single aws_ssm_parameter ?
get a single value from the parameter like: B = subnet-7e00d503 or B:subnet-7e00d503 ?
Many thanks for help ;)

You can store it as json, and then get json back.
resource "aws_ssm_parameter" "foo" {
name = "myparam"
type = "String"
value = jsonencode(var.test_parameters)
}
To read it:
data "aws_ssm_parameter" "foo" {
name = "myparam"
}
# to use
locals {
myparam_values = jsondecode(data.aws_ssm_parameter.foo.value)
}

Related

Failed to extract specific value from map object terraform

This is my main file
services_list = flatten([
reverse(values(var.host_svc)) ])
}
variable "host_svc" {
type = map(map(any))
}
output "aa" {
value = local.services_list
}
This is terraform tfvars file
host_svc = {
accounts = {
name = "accounts"
pod = "3"
cpu = "512M"
memory = "1024M"
}
analytics = {
name = "analytics"
pod = "3"
cpu = "512M"
memory = "1024M"
}
}
When i am output local.services_list i ma getting whole object. i just want to fetch just name of each map object like accounts,analytics.
i have tried with list data type it works but i want to do with map ojject because i have to use different object values
You can do that much easier than you are trying:
local {
services_list = [for k, v in var.host_svc : k]
}
Or even more easy using the built-in keys function [1]:
local {
services_list = keys(var.host_svc)
}
[1] https://developer.hashicorp.com/terraform/language/functions/keys
If I understood you correctly, you want to read the names from your map. If you want to read your map keys, just use keys(map) function:
variable "host_svc" {
type = map(map(any))
default = {
accounts_k = {
name = "accounts"
pod = "3"
cpu = "512M"
memory = "1024M"
}
analytics_k = {
name = "analytics"
pod = "3"
cpu = "512M"
memory = "1024M"
}
}
}
output "aa" {
value = keys(var.host_svc)
}
Which will output:
+ aa = [
+ "accounts_k",
+ "analytics_k",
]
I used _k suffix just to differentiate between the map keys and your name key inside the internal map
In case you want to read name attribute of that map you can do:
output "aa" {
value = [for svc in local.services_list : svc.name]
}
In case you don't want to have duplicates in that outputted list, use toset():
output "aa" {
value = toset([for svc in local.services_list : svc.name])
}

How do I get a specific key in a nested map in terraform, when running in a for_each/for loop?

I have two sets of locals defined as below:
locals {
env1 = {
a = {
}
b = {
}
}
}
locals {
env2 = {
x = {
}
y = {
}
}
}
Now, I have to run a loop for local.env1 so that loop runs for a & b. But I also need "x" from local.env2 for a resource attribute in the same resource block the loops runs.
There are 2 resource blocks and it would look something like this:
resource_block1
{
for_each = local.env1
scope = each.key
id = something["x"]
}
resource_block2
{
for_each = local.env1
scope = each.key
id = something["y"]
}
For "id" attribute in both the resource blocks I don't want to hardcode "x" or "y" respectively. I want to programmatically fill "x" and "y" for "id" attribute in the respective resource blocks.
I tried to create a map for env1 and env2 I was able to fetch "a" and "b" from local.env1 but I am unable to fetch/filter only "x" or only "y" from it.
Any ideas/solutions please? Thank you for checking.

I want to create a Terraform list of maps with some items in the list being conditional

How can I achieve the following:
I want to add map objects to a list based on whether they are passed as empty or not in the root module. So if this is passed in the variables
A = { foo = bar }
B = {}
C = { foo = bar }
then I would expect the final_list variable to be the one below
# main.tf
final_list = [{A}, {C}]
If there are alternate ways of achieving this (ie by using boolean flags), I'm ok with using those as well.
Probably you want filter usage like this:
locals {
a = {
foo = "bar"
}
b = {}
c = {
foo = "bar"
}
merge = [
for map in [local.a,local.b,local.c] :
map if map != {}
]
}
output "merge" {
value = local.merge
}

How can I split out an 'any' variable in terraform?

I'm trying to get multiple values out of an 'any' type variable. I'm new to terraform and open to recommendations. Specifically for this example, I'd like to know how I can output the 'bucket_name' value in my outputs.
variable "replica_config" {
type = any
default = {
role = "role_name"
rules = [
{
id = "full-s3-replication"
status = true
priority = 10
delete_marker_replication = false
destination = {
bucket = "bucket_name"
storage_class = "STANDARD"
replica_kms_key_id = "key_id"
account_id = "account_id"
replication_time = {
status = "Enabled"
minutes = 15
}
}
}
]
}
}
Current Output:
output "output4" {
value = flatten(var.replica_config["rules"])
}
Since you you have a list for rules, you can use a splat expression as such:
output "output4" {
value = var.replica_config.rules[*].destination.bucket
}
Keep in mind, the output of this expression will also be a list. If you want a single item instead of a list, you can use an index.
For example:
output "output4" {
value = var.replica_config.rules[0].destination.bucket
}

Produce single item maps procedurally from nested map in terraform

I have a nested map variable of account name and ID by OU, like:
variable "aws_accounts" {
type = map(map(any))
default = {
first_ou = {
first_account = "111111111"
second_account = "222222222"
}
second_ou = {
third_account = "333333333"
fourth_account = "444444444"
}
}
}
This is great for passing a map of account_name to account_id as a sub-variable to do things by ou and the modules in question are constructed to accept a map input.
I would like to also render a local so that I can also reference single accounts but get a map value for them without having to maintain a separate list of variables, like
local.first_account = {
first_account = "111111111"
}
local.second_account = {
second_account = "222222222"
}
local.third_account = {
third_account = "33333333"
}
etc.
I have tried various techniques but without success:
I cannot work out how to refer to each map in the array iteratively- most documentation seems to be based on lists and when I try to do a for_each I get
The "each" object can be used only in "module" or "resource" blocks, and only when the "for_each" argument is set.
Based on your example, it seems like you want to take your two-level map and turn it into a single-level map where the keys are the account names and the "OU names" are just discarded.
Here's one way to achieve that:
locals {
account_ids = merge(values(var.aws_accounts)...)
}
This first uses values to take the values from the top-level map, producing a list of maps.
It then uses merge to take all of the elements from each of the maps and combine them into a single new map. I used the ... symbol to tell Terraform that it should treat each element of the list as a separate argument to merge, rather than just passing the whole list as a single argument.
After merging these together you could potentially split them apart again, creating a separate map each, using a for expression.
locals {
account_maps = tomap({
for k, id in local.account_ids :
k => { (k) = id }
})
}
Maybe not exactly what you're looking for, but could be helpful:
locals {
accounts = merge(var.aws_accounts["first_ou"], var.aws_accounts["second_ou"])
}
If you need to do this in a more dynamic way:
locals {
accounts = zipmap(
flatten([for item in var.aws_accounts : keys(item)]),
flatten([for item in var.aws_accounts : values(item)])
)
}
Now you can access each account with local.accounts["first_account"] etc.
OK So with help from Martin Atkins and Bryan Heden I have found an answer to this. It isn't exactly pretty but it does work:
variable "aws_accounts" {
type = map(map(any))
default = {
first_ou = {
first_account = "111111111"
second_account = "222222222"
}
second_ou = {
third_account = "333333333"
fourth_account = "444444444"
}
}
}
locals {
# gives single map from nested map
account_ids = merge(values(var.aws_accounts)...)
# gives separate structured map for each key
single_accounts_maps = {
for account, id in local.account_ids :
account => {
account = account
id = id
}
}
# gives map where values = keys plus values
single_accounts_maps_joined = zipmap(
flatten([for item in var.aws_accounts : keys(item)]),
[for item in local.single_accounts_maps :
join(" = ", values(item))]
)
# gives nested map by key = {key = "value"}
single_accounts_maps_keys_values = {
for item in local.single_accounts_maps_joined :
(split(" = ", item)[0]) => {
(split(" = ", item)[0]) = (split(" = ", item)[1])
}
}
}
Output that I wanted:
terraform console
> local.single_accounts_maps_keys_values
{
"first_account" = {
"first_account" = "111111111"
}
"fourth_account" = {
"fourth_account" = "444444444"
}
"second_account" = {
"second_account" = "222222222"
}
"third_account" = {
"third_account" = "333333333"
}
}
After the discussion with Martin Atkins and his subsequent edits below, I am recommending his answer instead as simpler, more legible and more graceful, although the tomap() nesting appears to be unneeded, i.e. do
locals {
account_ids = merge(values(var.aws_accounts)...)
account_maps = {
for k, id in local.account_ids :
k => { (k) = id }
}
}

Resources