How to transform a map to map in Terraform - functional-programming

I have a map in Terraform, values of which I want to transform by prepending them with a string, resulting in another map.
variable "my_map" {
type = map(string)
}
locals {
my_new_map = [for key, value in var.my_map: { key = "prefix/${value}"}]
}
But local.my_new_map is a tuple instead of a map. What am I missing for the result to be a map?

You must use the map syntax with { and } :
variable "my_map" {
type = map(string)
}
locals {
my_new_map = {for key, value in var.my_map: key => "prefix/${value}"}
}

Related

Terraform setproduct with inner loop

I'm trying to create a CloudWatch alarm that will cycle through instances defined in data.tf and for each on of these to cycle through the volume id's
data.tf
data "aws_instances" "instance_cloudwatch" {
instance_tags = {
Type = var.type
}
}
data "aws_ebs_volumes" "cw_volumes" {
tags = {
Name = var.name
}
}
data "aws_ebs_volume" "cw_volume" {
for_each = toset(data.aws_ebs_volumes.cw_volumes.ids)
filter {
name = "volume-id"
values = [each.value]
}
}
In the resource I created
locals {
vol_map = {
for pair in setproduct(data.aws_instances.instance_cloudwatch.ids,data.aws_ebs_volume.cw_volume.*.id) : "${pair[0]}-${pair[1]}" => {
id = pair[0]
vol = pair[1]
}
}
}
And then I try to use these pairs in the alarm dimensions
resource "aws_cloudwatch_metric_alarm" "some_alarm" {
for_each = local.vol_map
...
dimensions = {
InstanceId = each.value.id
VolumeId = each.value.vol
}
When I run terraform apply I get this error
Error: Unsupported attribute
for pair in setproduct(data.aws_instances.instance_cloudwatch.ids,data.aws_ebs_volume.cw_volume..id) : "${pair[0]}-${pair[1]}" => {*
This object does not have an attribute named "id" I tried volume_id and got the same error
The issue is that you can't use the .*. syntax (in data.aws_instances.instance_cloudwatch.ids,data.aws_ebs_volume.cw_volume.*.id) on a resource that you created with for_each. The .*. syntax only works when you use count. This is because it only works with arrays/lists, and for_each creates a map.
Try values(data.aws_instances.instance_cloudwatch.ids,data.aws_ebs_volume.cw_volume).*.id. This will get the data.aws_ebs_volume.cw_volume values as a list instead of a map, so you can then use .*. on them.

rewrite data null_data_source to locals - rewrite count to for_each

data "aws_iam_role" "extra_iam_role" {
count = length(var.s3_permission_extra_roles)
name = var.s3_permission_extra_roles[count.index]
}
data "null_data_source" "extra_iam" {
count = length(var.s3_permission_extra_roles)
inputs = {
role_id = "${data.aws_iam_role.extra_iam_role[count.index].unique_id}:*"
}
}
I want to rewrite that code like this:
data "aws_iam_role" "extra_iam_role" {
for_each = var.s3_permission_extra_roles
name = each.value
}
# is that correct ?
data "null_data_source" "extra_iam" {
for_each = var.s3_permission_extra_roles
inputs = {
role_id = data.aws_iam_role.extra_iam_role????????
#what code should be in previous line
}
}
i am clueless here....please help
i want to get rid of that count.index because that cant be used by locals...
i am not sure how to rewrite that count.index into for_each similarity
#kovokilla I believe 'var.s3_permission_extra_roles' is of type set and in that case, in your first code you should do something similar to below
data "aws_iam_role" "extra_iam_role" {
for_each = toset(var.s3_permission_extra_roles)
name = each.value
}
and for second block, you can write something similar to below
data "null_data_source" "extra_iam" {
for_each = toset(var.s3_permission_extra_roles)
inputs = {
role_id = data.aws_iam_role.extra_iam_role[each.key].unique_id
}
}
Remember to define your 's3_permission_extra_roles' as type set in your variables.tf file
variable "s3_permission_extra_roles" {
description = "s3 permission roles"
default = ["role1","role2"]
type = set(string)
}

Is there any way to access individual values of a map of <key, value> pairs in karate if we do not know the value of key?

Assume that i have the following structure
Content : {
"Key1" : {
JSON_OBJECT1
}
"Key2" : {
JSON_OBJECT#
}
}
I need a way to evaluate the schema for all the JSON objects in the content structure and i do not know the value of key at runtime here, is there any way in karate to achieve the same?
Came up with a generic function to transform my Map to object of values and that fixed my issue.
"""
* def getObjects =
"""
function(array) {
var objects = []
for (var propName in array) {
var a = array[propName];
objects.push(a);
}
return objects;
}
"""

type check dynamic properties flowtype

I am trying to add dynamic properties to an object and have flow type check them:
my function would be like this:
function defineStuff(obj:MyType, keys:string[]):??? {
keys.forEach(function(key) {
Object.defineProperty(obj, key, {get:function(){....}});
obj["Add"+key] = function(value) {....};
obj["Remove"+key] = function(value) {....};
}
return obj;
}
I would like to be able to do stuff like this;
var obj : MyType = fetchMyObj();
defineStuff(obj, ["Thing", "OtherThing"]);
var thing = obj.Thing;
obj.AddOtherThing(10);
all dynamic properties type would be number
is there a syntax for doing this in flow? (i.e. how to fill the ???)
This should work for dictionaries.
type MyType = {[key: string]: number};
// ...
function defineStuff(obj: MyType, keys:string[]): MyType {
keys.forEach(function(key) {
Object.defineProperty(obj, key, {get:function(){....}});
// you can't have functions here, because you say that all values should be numbers
// obj["Add"+key] = function(value) {....};
// obj["Remove"+key] = function(value) {....};
}
return obj;
}
// ...
See docs

dart, a nice way of replacing keys and values in maps?

I have a Map and I want to go through both the values and keys and replace any occurrences of particular objects which meet some set criteria with some other objects, so if I find a key that meets a specific criteria.
I want to swap that key for a different object that still points at the same value object in the map, similarly if I find a value that I want to replace I want the original key to point at the replacement value.
Here is some code that works for a simplified example but it looks quite ugly, is there a nicer way of achieving this, meaning a method which doesn't require that you extract every key and value you want to replace and then write the replacements back in.
It would be nicer to be able to just iterate of the map once rather than iterating over the keys, and then iterating over all the keys and values to be replaced?
void main(){
//replace all values of instance A with an Object and all keys starting with "t" with the same key minus the "t"
var m = {
"one": new A(),
"two": new A(),
"three": new B(),
"four": new B()
};
mapKeyAndValueSwapper(m,
keyMeetsCondition: (k) => k.startsWith("t"),
valueMeetsCondition: (v) => v is A,
keyReplacer: (k) => k.replaceFirst("t", ""),
valueReplacer: (v) => new Object());
print(m);
}
mapKeyAndValueSwapper(Map m, {bool keyMeetsCondition(key), bool valueMeetsCondition(value), dynamic keyReplacer(key), dynamic valueReplacer(value)}){
var keysToReplace = [];
var keysToReplaceValuesFor = [];
var keys = m.keys;
keys.forEach((k){
if(keyMeetsCondition != null){
if(keyMeetsCondition(k)){
keysToReplace.add(k);
}
}
if(valueMeetsCondition != null){
if(valueMeetsCondition(m[k])){
keysToReplaceValuesFor.add(k);
}
}
});
keysToReplaceValuesFor.forEach((k){
m[k] = valueReplacer(m[k]);
});
keysToReplace.forEach((k){
m[keyReplacer(k)] = m.remove(k);
});
}
class A{}
class B{}
I think this does the same:
Map newMap = {};
m.forEach((k, v) {
var key = k;
var value = v;
if(m[k] is A) {
value = new Object();
}
if(k.startsWith('t')) {
key = k.replaceFirst('t', '');
}
newMap[key]=value;
});
You can create an extension on Map that creates a list from the mapEntries and then manipulate that list to update the key (using the index to maintain order), then clear the map and rebuild it from your updated list.
extension MapX<K, V> on Map<K, V> {
bool updateKey({required K currentKey, required K newKey}) {
if (containsKey(currentKey) && !containsKey(newKey)) {
final value = this[currentKey] as V;
final index = keys.toList().indexWhere((k) => k == currentKey);
final mapEntriesList = entries.toList();
mapEntriesList.removeAt(index);
mapEntriesList.insert(index, MapEntry<K,V>(newKey, value));
clear();
addEntries(mapEntriesList);
return true;
} else {
return false;
}
}
}

Resources