How to generate a map from two disjoint datastructures in Terraform? - dictionary

I have a list and a map like so:
locals {
traffic_rules = [
{
name = "eu-pool"
geo_mappings = ["GEO-EU"]
failover = [local.endpoint_pools.a, local.endpoint_pools.b, local.endpoint_pools.c]
},
{
name = "world-pool"
geo_mappings = ["WORLD"]
failover = [local.endpoint_pools.a]
},
]
endpoint_pools = {
a = [
{
fqdn = "endpoint_pool.a.com."
weight = 1
}]
b = [
{
fqdn = "endpoint_pool.b1.com."
weight = 1
},
{
fqdn = "endpoint_pool.b2.com."
weight = 1
}]
c = [
{
fqdn = "endpoint_pool.c.com."
weight = 1
}]
}
Basically, I want to iterate through traffic_rules and gather:
the index of failover[item] in traffic_rules when I iterate through it
the name of the traffic_rule, eg "eu-pool"
the string "a/b/c" from local.endpoint_pools.a/b/c (these are also keys in endpoint_pools map)
In the end, I need to be able to generate a flattened map like below:
{
“eu-pool-a” = {
endpoint_pool_name = "a"
priority = 1
top_profile_name = “eu-pool”,
bottom_profile_name = “bottom-profile-a”
}
“eu-pool-b” = {
endpoint_pool_name = "b"
priority = 2
top_profile_name = “eu-pool”,
bottom_profile_name = “bottom-profile-b”
}
“eu-pool-c” = {
endpoint_pool_name = "c"
priority = 3
top_profile_name = “eu-pool”,
bottom_profile_name = “bottom-profile-c”
}
“world-pool-a” = {
endpoint_pool_name = "a"
priority = 1
top_profile_name = “world-pool”,
bottom_profile_name = “bottom-profile-a”
}
}
I started off trying to flatten traffic_rules, then realized that TF is substituting local.endpoint_pools.a/b/c into its actual contents, so I am completely losing the context/string of a/b/c.

Like #MartinAtkins recommend, you should really concentrate on simplify your input data, rather then to try to hack up some complex TF expression.
Also your output data structure does not agree with your input data, as per my understanding, as you have two endpoints for endpoint_pools[b] yet they are not accounted for in your output data. I assume that you want to use both of them, or the first one only?
Anyway, you can first create a helper structure in locals which maps endpoint to pool name:
locals {
fqdn_to_pool_name = merge([
for pool_name,v in local.endpoint_pools:
{
for v1 in v:
"${v1.fqdn}" => {pool_name = pool_name}
}
]...)
}
Then you can generate your structure as follows (I assume you account for both endpoints):
output "test" {
value = merge(flatten([for rule in local.traffic_rules:
[for idx, failovers in rule.failover:
{ for idx2, failover in failovers:
"${rule.name}-${local.fqdn_to_pool_name[failover.fqdn].pool_name}" => {
endpoint_pool_name = local.fqdn_to_pool_name[failover.fqdn].pool_name
priority = idx + 1
top_profile_name = rule.name
bottom_profile_name = "bottom-profile-${local.fqdn_to_pool_name[failover.fqdn].pool_name}"
}...
}
]
])...)
}
which gives:
test = {
"eu-pool-a" = [
{
"bottom_profile_name" = "bottom-profile-a"
"endpoint_pool_name" = "a"
"priority" = 1
"top_profile_name" = "eu-pool"
},
]
"eu-pool-b" = [
{
"bottom_profile_name" = "bottom-profile-b"
"endpoint_pool_name" = "b"
"priority" = 2
"top_profile_name" = "eu-pool"
},
{
"bottom_profile_name" = "bottom-profile-b"
"endpoint_pool_name" = "b"
"priority" = 2
"top_profile_name" = "eu-pool"
},
]
"eu-pool-c" = [
{
"bottom_profile_name" = "bottom-profile-c"
"endpoint_pool_name" = "c"
"priority" = 3
"top_profile_name" = "eu-pool"
},
]
"world-pool-a" = [
{
"bottom_profile_name" = "bottom-profile-a"
"endpoint_pool_name" = "a"
"priority" = 1
"top_profile_name" = "world-pool"
},
]
}

Related

Is it possible to make tasklist automatically switch to different layout?

I am using the following code for my wibar tasklist:
s.mytasklist = awful.widget.tasklist
{
screen = s,
filter = awful.widget.tasklist.filter.allscreen,
buttons = tasklist_buttons,
layout = wibox.layout.fixed.horizontal(),
widget_template =
{
{
{
{
id = 'clienticon',
widget = awful.widget.clienticon,
},
margins = 0,
widget = wibox.container.margin,
},
{
id = 'text_role',
widget = wibox.widget.textbox,
},
layout = wibox.layout.fixed.horizontal,
},
id = 'background_role',
forced_width = 200,
forced_height = 60,
widget = wibox.container.background,
create_callback = function(self, c, index, objects)
self:get_children_by_id('clienticon')[1].client = c
end,
},
}
s.mytasklist_onlyminimised = awful.widget.tasklist
{
screen = s,
filter = awful.widget.tasklist.filter.minimizedcurrenttags,
buttons = tasklist_buttons,
style = {
shape_border_width = 1,
shape_border_color = '#333333',
shape = gears.shape.partially_rounded_rect,
},
}
Which makes the tasks on the tasklist have fixed width (as according to this answer)
My question is:
Is it possible to make the tasklist switch to wibox.layout.flex.horizontal when the tasklist is full of tasks?
While you could technically replace the tasklist from a client.connect_signal("tagged", function(c) if #c.screen.selected_tag:clients() >= some_number then <make_a_new_tasklist> end end) style code, that's pretty cheap and ugly.
So I think this would require to contribute a patch upstream to expose the taglist/tasklist constructor arguments as properties. The awful.widget.layoutlist already do it, but not the tag/tasklists.

Is it possible to update existing Dynamo DB table from Terraform

I am trying to create a terraform module with the help of which I can make an entry to existing Dynamo DB table.
I have got this code which create dynamo DB table
resource "aws_dynamodb_table" "basic-dynamodb-table" {
name = "GameScores"
billing_mode = "PROVISIONED"
read_capacity = 20
write_capacity = 20
hash_key = "UserId"
range_key = "GameTitle"
attribute {
name = "UserId"
type = "S"
}
attribute {
name = "GameTitle"
type = "S"
}
attribute {
name = "TopScore"
type = "N"
}
ttl {
attribute_name = "TimeToExist"
enabled = false
}
global_secondary_index {
name = "GameTitleIndex"
hash_key = "GameTitle"
range_key = "TopScore"
write_capacity = 10
read_capacity = 10
projection_type = "INCLUDE"
non_key_attributes = ["UserId"]
}
tags = {
Name = "dynamodb-table-1"
Environment = "production"
}
}
Is there any way I can make changes in existing dynamo db table.
For adding entries to a table you can take a look at the aws_dynamodb_table_item resource. Here is an example that you can use to add an entry to your table:
resource "aws_dynamodb_table_item" "item1" {
table_name = aws_dynamodb_table.basic-dynamodb-table.name
hash_key = aws_dynamodb_table.basic-dynamodb-table.hash_key
range_key = aws_dynamodb_table.basic-dynamodb-table.range_key
item = <<ITEM
{
"UserId": {"S": "user"},
"GameTitle": {"S": "gamex"},
"TopScore": {"N": "42"}
}
ITEM
}

terraform nested dynamic block with nested map

I'm trying to get tf 0.12.x new dynamic feature to work with a nested map, config is below.
As you can see below (simplified for this) I'm defining all the variables and adding variable required_resource_access which contains a map.
I was hoping to use new dynamic feature to create read this map in a nested dyanmic block.
variable prefix {
description = "Prefix to applied to all top level resources"
default = "abx"
}
variable suffix {
description = "Suffix to applied to all valid top level resources, usually this is 2 letter region code such as we (westeurope), ne (northeurope)."
default = "we"
}
variable env {
description = "3 letter environment code appied to all top level resources"
default = "dev"
}
variable location {
description = "Where to create all resources in Azure"
default = "westeurope"
}
variable available_to_other_tenants {
default = false
}
variable oauth2_allow_implicit_flow {
default = true
}
variable public_client {
default = false
}
# other option is native
variable application_type {
default = "webapp/api"
}
variable required_resource_access {
type = list(object({
resource_app_id = string
resource_access = object({
id = string
type = string
})
}))
default = [{
resource_app_id = "00000003-0000-0000-c000-000000000000"
resource_access = {
id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61"
type = "Role"
}
}]
}
variable reply_urls {
default = []
}
variable group_membership_claims {
default = "All"
}
resource "azuread_application" "bootstrap" {
name = "${var.prefix}-${var.env}-spn"
homepage = "http://${var.prefix}-${var.env}-spn"
identifier_uris = ["http://${var.prefix}-${var.env}-spn"]
reply_urls = var.reply_urls
available_to_other_tenants = var.available_to_other_tenants
oauth2_allow_implicit_flow = var.oauth2_allow_implicit_flow
type = var.application_type
group_membership_claims = var.group_membership_claims
dynamic "required_resource_access" {
for_each = var.required_resource_access
content {
resource_app_id = required_resource_access.value["resource_app_id"]
dynamic "resource_access" {
for_each = required_resource_access.value["resource_access"]
content {
id = resource_access.value["id"]
type = resource_access.value["type"]
}
}
}
}
}
But for reasons beyond my knowledge it keeps giving me this error (notice it's priting it twice as well), I've tried a few other options but this is the closest I managed to get where it would at least give me a meaningful error.
------------------------------------------------------------------------
Error: Invalid index
on pe_kubernetes.tf line 24, in resource "azuread_application" "bootstrap":
24: id = resource_access.value["id"]
|----------------
| resource_access.value is "7ab1d382-f21e-4acd-a863-ba3e13f7da61"
This value does not have any indices.
Error: Invalid index
on pe_kubernetes.tf line 24, in resource "azuread_application" "bootstrap":
24: id = resource_access.value["id"]
|----------------
| resource_access.value is "Role"
This value does not have any indices.
Error: Invalid index
on pe_kubernetes.tf line 25, in resource "azuread_application" "bootstrap":
25: type = resource_access.value["type"]
|----------------
| resource_access.value is "7ab1d382-f21e-4acd-a863-ba3e13f7da61"
This value does not have any indices.
Error: Invalid index
on pe_kubernetes.tf line 25, in resource "azuread_application" "bootstrap":
25: type = resource_access.value["type"]
|----------------
| resource_access.value is "Role"
This value does not have any indices.
Spent the best part of 2 days on this with no luck so any help or pointers would be much appreciated!
I had some time to test my comment...
If I change the resource_access to a list it works.
See code below:
variable required_resource_access {
type = list(object({
resource_app_id = string
resource_access = list(object({
id = string
type = string
}))
}))
default = [{
resource_app_id = "00000003-0000-0000-c000-000000000000"
resource_access = [{
id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61"
type = "Role"
}]
}]
}
resource "azuread_application" "bootstrap" {
name = "test"
type = "webapp/api"
group_membership_claims = "All"
dynamic "required_resource_access" {
for_each = var.required_resource_access
content {
resource_app_id = required_resource_access.value["resource_app_id"]
dynamic "resource_access" {
for_each = required_resource_access.value["resource_access"]
content {
id = resource_access.value["id"]
type = resource_access.value["type"]
}
}
}
}
}
And the plan shows:
Terraform will perform the following actions:
# azuread_application.bootstrap will be created
+ resource "azuread_application" "bootstrap" {
+ application_id = (known after apply)
+ available_to_other_tenants = false
+ group_membership_claims = "All"
+ homepage = (known after apply)
+ id = (known after apply)
+ identifier_uris = (known after apply)
+ name = "test"
+ oauth2_allow_implicit_flow = true
+ object_id = (known after apply)
+ owners = (known after apply)
+ public_client = (known after apply)
+ reply_urls = (known after apply)
+ type = "webapp/api"
+ oauth2_permissions {
+ admin_consent_description = (known after apply)
...
}
+ required_resource_access {
+ resource_app_id = "00000003-0000-0000-c000-000000000000"
+ resource_access {
+ id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61"
+ type = "Role"
}
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
I removed a lot of your variables an some of the optional Arguments for azuread_application to keep the code as small as possible, but the same principle applies to your code, use lists on for_each or it will loop on the object properties.

Dynamically importing secondary indexes in terraform for dynamodb

I'm trying to import a couple of dynamodb tables to terraform. I'm stuck on how to dynamically handle global secondary indexes between environments.
I have a module and two state files for each environment.
How can i dynamically enter these variables using count , that change between environments,
For example in the below example there are 4 indexes but for a particular index in prod account the read capacity and write capacity changes, whereas all other variables remain constant.
ie last-index has different read and write capacity values for both prod and nonprod
How can it be implemented in terraform?
Module:
locals {
name = ["xxx-index","xxx-index","xxx-index","xxx-index","last-index"]
write_capacity = [ 5,5,5,5,5]
read_capacity = [ 5,5,5,5,5]
range_key = ["xxx","xxx","xxx","xxx","xxx"]
}
global_secondary_index {
count = "${length(local.name)}"
name = "${element(local.name, count.index)}"
write_capacity = "${element(local.write_capacity, count.index)"
read_capacity = "${element(local.read_capacity, count.index)"
hash_key = "userId"
range_key = "${element(local.range_key,count.index)}"
projection_type = "ALL"
}
Terraform -version Terraform v0.11.13
+ provider.aws v2.25.0
There is no reasonable answer to this question for Terraform 0.11. It lacks the primitives required to describe the transform you are looking for, and it doesn't support dynamically generating nested blocks.
The closest supported thing in Terraform 0.11 would be to fix the number of indices as constant but still vary the individual parts, like this:
resource "aws_dynamodb_table" "example" {
# ...
global_secondary_index {
name = "${local.name[0]}"
write_capacity = "${local.write_capacity[0]}"
read_capacity = "${local.read_capacity[0]}"
range_key = "${local.range_key[0]}"
hash_key = "userId"
projection_type = "ALL"
}
global_secondary_index {
name = "${local.name[1]}"
write_capacity = "${local.write_capacity[1]}"
read_capacity = "${local.read_capacity[1]}"
range_key = "${local.range_key[1]}"
hash_key = "userId"
projection_type = "ALL"
}
global_secondary_index {
name = "${local.name[2]}"
write_capacity = "${local.write_capacity[2]}"
read_capacity = "${local.read_capacity[2]}"
range_key = "${local.range_key[2]}"
hash_key = "userId"
projection_type = "ALL"
}
global_secondary_index {
name = "${local.name[3]}"
write_capacity = "${local.write_capacity[3]}"
read_capacity = "${local.read_capacity[3]}"
range_key = "${local.range_key[3]}"
hash_key = "userId"
projection_type = "ALL"
}
global_secondary_index {
name = "${local.name[4]}"
write_capacity = "${local.write_capacity[4]}"
read_capacity = "${local.read_capacity[4]}"
range_key = "${local.range_key[4]}"
hash_key = "userId"
projection_type = "ALL"
}
}
The new Terraform 0.12 feature that was added to deal with this use-case is dynamic blocks, which allow producing zero or more blocks of a particular type based on a collection value.
For example:
locals {
indices = {
"xxx-index" = {
write_capacity = 5
read_capacity = 5
range_key = "xxx"
},
"last-index" = {
write_capacity = 5
read_capacity = 5
range_key = "xxx"
},
}
}
resource "aws_dynamodb_table" "example" {
# ...
dynamic "global_secondary_index" {
for_each = local.indices
content {
name = global_secondary_index.key
write_capacity = global_secondary_index.value.write_capacity
read_capacity = global_secondary_index.value.read_capacity
range_key = global_secondary_index.value.range_key
hash_key = "userId"
projection_type = "ALL"
}
}
}

BFM v4.2.0 How to add DepartureDateTime

how do you add the departure date time in the request for C#? here is part of the code
refOrgDest = new BFMV430.OTA_AirLowFareSearchRQOriginDestinationInformation
{
OriginLocation = new BFMV430.OriginDestinationInformationTypeOriginLocation { LocationCode
= refItin.Origin },
DestinationLocation = new BFMV430.OriginDestinationInformationTypeDestinationLocation { LocationCode = refItin.Destination },
RPH = (i + 1).ToString(),
TPA_Extensions = new BFMV430.OTA_AirLowFareSearchRQOriginDestinationInformationTPA_Extensions
{
CabinPref = new BFMV430.CabinPrefType
{
Cabin = (BFMV430.CabinType)Enum.Parse(typeof(BFMV430.CabinType), refResBookType, true),
PreferLevel = BFMV430.PreferLevelType.Preferred,
CabinSpecified = true,
}
},
ItemElementName = BFMV430.ItemChoiceType.DepartureDateTime,
Item = new BFMV430.TravelDateTimeType
{
DepartureWindow = "00002359",
},
};
refOrgDestList.Add(refOrgDest);
also I'm getting this mismatch error when running it
Value of ItemElementName mismatches the type of System.String; you need to set it to prjWService.BFMV430.ItemChoiceType.#DepartureDateTime.
any help would be great.
thanks in advance
Here is a piece of code that could help you, the interesting part is about the date format expected by Sabre for all transaction related to search flight/availability, it means (BMF, ADVShopping, AirLowFare and etc is gonna to use the same date format). (2019-04-14T00:00:00)
__ItemOriginDestination = new AdvacedAirShopping.OTA_AirLowFareSearchRQOriginDestinationInformation
{
RPH = "1",
ItemElementName = AdvacedAirShopping.ItemChoiceType.DepartureDateTime,
Item = _FlightSearch.DepartureDateTime, //"2019-04-14T00:00:00" Here is the format expected by Sabre
OriginLocation = new AdvacedAirShopping.OriginDestinationInformationTypeOriginLocation
{
LocationCode = _FlightSearch.Origin
},
DestinationLocation = new AdvacedAirShopping.OriginDestinationInformationTypeDestinationLocation
{
LocationCode = _FlightSearch.Destination
}
};

Resources