Hello
I would like to create a list of objects like that:
locals {
bucket_mapping = {
"dev" = "test1"
"ltiq" = "test2", "test3"
"prod" = "test4","test5"
}
}
I want to call each line when , the env is called , Its working when Each line contain one element ,
I use this
${lookup(local.bucket_mapping, var.environment)}
but when I want multiple elements not working anymore:
I have this error , when I run the terraform:
Expected an attribute value, introduced by an equals sign ("=").
Thanks for the help
Hi @dassay75,
The nested lists will need brackets [ ]
to tell Terraform that you are intending to write a list value. Without those brackets, Terraform thinks the first comma ,
is the end of the map element and so it expects the next token to be a map key.
locals {
bucket_mapping = {
"dev" = ["test1"]
"ltiq" = ["test2", "test3"]
"prod" = ["test4", "test5"]
}
}
Hi @apparentlymart
Thanks a lot for you answer , but when I made that , I have this error:
│ local.bucket_mapping is object with 3 attributes
│ var.environment is "dev"`
│
│ Cannot include the given value in a string template: string required.
Thanks
Hi @dassay75,
This seems like a correct error: you can only interpolate single strings into a string template.
To make use of local.bucket_mapping
in a template like this, you’ll need to decide what the result should be in the case where there is more than one value in the list. If you can show an example of what you intend the resulting string to be when var.environment
is set to either "ltiq"
or "prod"
then I can hopefully make some suggestions about how you could achieve that result.
Hi @apparentlymart
Thanks again to take the time to answer me , My purpose is the following one:
locals {
bucket_mapping = {
“dev” = [“arn:aws:s3:::bucket-log”]
“ltiq” = [“arn:aws:s3:::bucket-log-2”, “arn:aws:s3:::bucket-log-3”]
“prod” = [“arn:aws:s3:::bucket-log-4”, “arn:aws:s3:::bucket-log-6”]
}
}
In this example , I put the arn of the bucket , but in the real case , I use terraform remote state to have it.
My point is to pupulate a iam role in AWS with the arn of the bucket depending of the environment.
When I have “dev” , I want terraform to replace this value in the Ressource part in the iam role.
When I have ltiq , is to put the name of the 2 buckets in the ressource location on the IAM role .
In the dev , I have just one bucket that is the reason why I have one .
the goal is just to take all the element of the list depending the env and put it in the iam role correctly.
I hope my explanation is clear if you have more interrogation , do not hesistate.
Thanks
Hi @dassay75,
Can you also show the full part of the code where Terraform reported the error you shared previously, about including a non-string value in the template? I’d like to see what that template is generating so I can see what might be an appropriate way to represent more than one ARN at the same time.
Hi @apparentlymart
Sorry for the delay of the answer , this is the template that I want to generate thanks to the variables:
resource "aws_iam_role" "replication-role" {
name = "ReplicationBucketRole"
assume_role_policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "s3.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
POLICY
}
resource "aws_iam_policy" "replication-policy" {
name = "ReplicationBucketPolicy"
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:ListBucket",
"s3:GetReplicationConfiguration"
],
"Effect": "Allow",
"Resource": [
"${local.bucket_id}"
]
},
{
"Action": [
"s3:GetObjectVersionForReplication",
"s3:GetObjectVersionAcl",
"s3:GetObjectVersionTagging"
],
"Effect": "Allow",
"Resource": [
"${local.bucket_id}/*"
]
},
{
"Action": [
"s3:ReplicateObject",
"s3:ReplicateDelete",
"s3:ReplicateTags",
"s3:ObjectOwnerOverrideToBucketOwner"
],
"Effect": "Allow",
"Resource": [
"${lookup(local.bucket_mapping, var.environment)}/*"
]
},
{
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Effect": "Allow",
"Resource": [
"${data.aws_kms_key.s3.arn}",
],
"Condition": {
"StringLike": {
"kms:ViaService": "s3.eu-west-1.amazonaws.com",
"kms:EncryptionContext:aws:s3:arn": "${local.bucket_id}"
}
}
},
{
"Action": [
"kms:Encrypt",
"kms:GenerateDataKey"
],
"Effect": "Allow",
"Resource": ["${lookup(local.kms_mapping, var.environment)}"],
"Condition": {
"StringLike": {
"kms:ViaService": "s3.eu-west-1.amazonaws.com",
"kms:EncryptionContext:aws:s3:arn": "${lookup(local.bucket_mapping, var.environment)}"
}
}
}
]
}
POLICY
}
resource "aws_iam_role_policy_attachment" "replication-attachment" {
#count = var.environment == "prod" || var.environment == "ltiq" ? 1 : 0
role = aws_iam_role.replication-role.name
policy_arn = aws_iam_policy.replication-policy.arn
}
Hi @dassay75,
From reading the documentation on IAM policy conditions I can see that the StringLike
object can accept both individual strings and arrays of strings as valid values, and so I suppose the correct way to represent what you want to describe here would be for property "kms:EncryptionContext:aws:s3:arn"
to take a JSON array of the bucket ARNs.
Building complicated IAM policies using template interpolation is often a frustrating experience, because IAM is expecting valid JSON but it’s hard to produce correct JSON grammar using only string concatenation. Instead, I would suggest using Terraform’s jsonencode
function to let Terraform itself produce the JSON string based on a data structure you can construct using normal Terraform expressions.
resource "aws_iam_policy" "replication-policy" {
name = "ReplicationBucketPolicy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:ListBucket",
"s3:GetReplicationConfiguration"
]
Effect = "Allow",
Resource = local.bucket_id
},
{
Action = [
"s3:ReplicateObject",
"s3:ReplicateDelete",
"s3:ReplicateTags",
"s3:ObjectOwnerOverrideToBucketOwner"
]
Effect = "Allow"
Resource = ["${local.bucket_id}/*"]
},
{
Action = [
"s3:ReplicateObject",
"s3:ReplicateDelete",
"s3:ReplicateTags",
"s3:ObjectOwnerOverrideToBucketOwner"
]
Effect = "Allow"
Resource = [
for arn in local.bucket_mapping[var.environment] :
"${arn}/*"
]
},
{
Action = [
"kms:Decrypt",
"kms:GenerateDataKey",
]
Effect = "Allow"
Resource = [data.aws_kms_key.s3.arn]
Condition = {
StringLike = {
"kms:ViaService" = "s3.eu-west-1.amazonaws.com"
"kms:EncryptionContext:aws:s3:arn" = local.bucket_id
}
}
},
{
Action = [
"kms:Decrypt",
"kms:GenerateDataKey",
]
Effect = "Allow"
Resource = local.kms_mapping[var.environment]
Condition = {
StringLike = {
"kms:ViaService" = "s3.eu-west-1.amazonaws.com"
"kms:EncryptionContext:aws:s3:arn" = local.bucket_mapping[var.environment]
}
}
},
]
})
}
Notice that this argument to jsonencode
is written in normal Terraform expression syntax, rather than as JSON. This expression will produce a data structure which matches the expected shape of an IAM policy and then the jsonencode
function will serialize that data structure as JSON to meet the provider’s expectations.
Using Terraform’s normal expression syntax instead of a string template means that you can safely include lists of strings directly, without any need for any extra encoding first; jsonencode
knows that a Terraform list corresponds with a JSON array and so it will automatically produce a JSON array when needed.
Hi @apparentlymart
Its working perfectly , thanks a lot for your help on that topic.