terraform nested dynamic block with nested map - dictionary

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.

Related

Inappropriate value for the attribute cidr_block string is required

Hi my terraform code is here
main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}
# Configure the AWS Provider
provider "aws" {
region = var.region
}
# DATA
data "aws_availability_zones" "available" {}
data "template_file" "public_cidrsubnet" {
count = var.subnet_count
template = "$${cidrsubnet(vpc_cidr,8,current_count)}"
vars = {
vpc_cidr = var.network_address_space
current_count = count.index
}
}
resource "aws_vpc" "tf-aws-vn" {
cidr_block = var.network_address_space
tags = local.common_tags
#name = var.name
}
resource "aws_subnet" "tf-aws-sn" {
count = length(data.aws_availability_zones.available.names)
vpc_id = aws_vpc.tf-aws-vn.id
cidr_block = [data.template_file.public_cidrsubnet[*].rendered]
availability_zone = slice(data.aws_availability_zones.available.names, 0, var.subnet_count)
tags = local.common_tags
}
variables.tf
variable "region" {
default = "us-east-1"
}
variable network_address_space {}
variable name {
type = string
}
variable "subnet_count" {
type = number
}
And finally!
terraform.tfvars
network_address_space = "10.0.0.0/16"
subnet_count = 2
I'm getting error like below:
Error: Incorrect attribute value type
on main.tf line 36, in resource "aws_subnet" "tf-aws-sn":
36: cidr_block = [data.template_file.public_cidrsubnet[*].rendered]
Inappropriate value for attribute "cidr_block": string required.
What is the issue?
I want to create n subnets for any address range I provide
My terraform version is 0.13.5
You are passing an array to the cidr_block which results in the given error. You need to pass a string to the cidr block.
cidr_block = data.template_file.public_cidrsubnet[count.index].rendered
You also need to change the * to the actual count. Otherwise you will get an error complaining that it is a tuple with 2 elements.
With your code the result of data.template_file.public_cidrsubnet[*].rendered is
cidr_output = [
"10.0.0.0/24",
"10.0.1.0/24",
]
Then the next issue you will face is with the count itself. You are using the total number of availability zones as count, but you want the count to be the subnet_count. You only have 2 subnets created in the public_cidrsubnet template, so you can't create a subnet in each availability zone.
Then you also need to pass the count.index to the slice for the availability_zone argument.
The correct aws_subnet resource that should work
resource "aws_subnet" "tf-aws-sn" {
count = var.subnet_count
vpc_id = aws_vpc.tf-aws-vn.id
cidr_block = data.template_file.public_cidrsubnet[count.index].rendered
availability_zone = slice(data.aws_availability_zones.available.names, 0, var.subnet_count)[count.index]
}

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
}

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"
}
}
}

Error: Inappropriate value for attribute "cidr_blocks": list of string required

While creating security groups, I keep getting the following error
Inappropriate value for attribute "cidr_blocks": list of string
required.
here is excerpt from main.tf
resource "aws_security_group" "sg_sagum" {
name = var.sg_sagum1
vpc_id = data.aws_vpc.vpcname.id
description = var.sg_sagum1
tags = {
Name = var.sg_sagum1
}
dynamic "ingress" {
for_each = [for s in var.sg_sagum_ports : {
from_port = s.from_port
to_port = s.to_port
desc = s.desc
cidrs = s.cidr
}]
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
cidr_blocks = ingress.value.cidrs
protocol = "tcp"
description = ingress.value.desc
}
}
}
variables.tf
variable "sg_sagum_ports" {
description = "Ports to be opened on SAGUM SG"
type = list(map(string))
default = []
}
terraform.tfvars
sg_sagum_ports = [
{ from_port = "9000",
to_port = "9000",
cidr = "10.22.9.11/32"
desc = "SAGBPMS"
}
]
Inappropriate value for attribute "cidr_blocks": list of string
required.
In terraform.tfvars you need to change the cidr from
cidr = "10.22.9.11/32"
to
cidr = ["10.22.9.11/32"]

Range of all linkAttributed String in MutableAttributedString in swift

I have a mutableAttributedString in which few strings are linkAttributed,I want to find the Range of all link attributed string. How to do that in swift3 ?
When user start type # in textview i show list of few name. If user select any row then following method gets called.
func didSelectMemberId(_ model: BaseModel) {
var fullName = ""
if model.entityType == ReceiverType.Active.rawValue{
fullName = model.name
}else{
fullName = AtMention + model.name + " " + model.name2
}
let attributedString = NSMutableAttributedString(string:fullName, attributes:[NSFontAttributeName:(appNeedsAutoResize ? (UIUtils.getFontForApproprieteField(.headlineWithoutBold).font) : UIFont.systemFont(ofSize: 14))])
attributedString.addAttribute(NSLinkAttributeName, value: "connectmention://\(model.entityId.stringValue())", range: NSRange(location: 0, length: fullName.length))
attributedString.append(NSAttributedString(string: emptySpaceStringByUC, attributes:[NSFontAttributeName:(appNeedsAutoResize ? (UIUtils.getFontForApproprieteField(.headlineWithoutBold).font) : UIFont.systemFont(ofSize: 14))]))
self.composeBar.textView.textStorage.insert(attributedString, at:self.composeBar.textView.selectedRange.location)
}
self.composeBar.textView.selectedRange = NSMakeRange(self.composeBar.textView.selectedRange.location+fullName.length, 0 )
To get the link proprty I am using the following method
func getlinkActionRange(attributeString: NSAttributedString) -> [MentionStruct] {
var arrMentions = [MentionStruct]()
_ = attributeString.enumerateAttribute(NSLinkAttributeName, in: NSRange.init(location: 0, length: attributeString.length), options: [], using: { (value, range, stop) in
if let url = value {
let occurrence = (attributeString.string as NSString).substring(with:range)
arrMentions.append(MentionStruct(link: url as! String, text: occurrence, range: range))
}
})
return arrMentions
}
If user type anything after inserting that name , that type string also coming.

Resources