I created 2 buckets. One will create always and one will create when env is QA.
resource "aws_s3_bucket" "bucket_always" {
bucket_prefix = format("bucket.always")
acl = "private"
versioning {
enabled = true
}
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
}
}
}
}
resource "aws_s3_bucket" "bucket_conditional" {
count = var.name == "qa" ? 1 : 0
bucket_prefix = format("bucket.conditional")
acl = "private"
versioning {
enabled = true
}
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
}
}
}
}
Now how can I create an IAM and add both buckets arn? The below policy won't work because of conditional bucket
data "aws_iam_policy_document" "test_policy_document" {
statement {
actions = [
"s3:ListBucket",
]
resources = [
aws_s3_bucket.bucket_always.arn,
aws_s3_bucket.bucket_conditional.arn,
]
}
statement {
actions = [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
]
resources = [
"${aws_s3_bucket.bucket_always.arn}/*",
"${aws_s3_bucket.bucket_conditional.arn}/*",
]
}
}
Split the IAM statements up, and use dynamic blocks. You don't really need to iterate over a specific list of values here, just pass it a list with one value if you want to create the block, or an empty list if you don't want to create the block. For example:
data "aws_iam_policy_document" "test_policy_document" {
statement {
actions = [
"s3:ListBucket",
]
resources = [
aws_s3_bucket.bucket_always.arn
]
}
statement {
actions = [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
]
resources = [
"${aws_s3_bucket.bucket_always.arn}/*"
]
}
dynamic "statement" {
for_each = var.name == "qa" ? ["qa"] : []
content {
actions = [
"s3:ListBucket",
]
resources = [
aws_s3_bucket.bucket_conditional[0].arn
]
}
}
dynamic "statement" {
for_each = var.name == "qa" ? ["qa"] : []
content {
actions = [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
]
resources = [
"${aws_s3_bucket.bucket_conditional[0].arn}/*"
]
}
}
}
Related
So I have an issue, and need a bit of help. My knowledge of Terraform isn't all that great, and I need some advice on how to make this happen. I have a map variable with the following information in it:
In variables.tf:
variable "users" {
type = "map"
}
in the Terraform.tfvars:
users = {
"user1" = {
},
"user2" = {
instance = ["instance_size"]
},
"user3" = {
bucket = ["bucket1"]
},
"user4" = {
bucket = ["bucket1", "bucket2"]
},
"user5" = {
instance = ["instance_size"]
bucket = ["bucket1", "bucket2", "bucket3"]
}
And what I want to do is to take the bucket information out of that variable and apply a AWS policy kinda like so:
data "aws_iam_policy_document" "sm_s3_bucket" {
for_each = var.users
statement {
actions = [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket",
"s3:GetBucketLocation"
]
resources = formatlist("arn:aws:s3:::%s", each.value.bucket)
}
}
How do I go about getting only the bucket info for each user out of the variable and into the resource command? Thanks!
If the question is how to create a policy document for users who have bucket specified, it could be done for example like this:
for_each = {
for user, attr in var.users : user => attr if contains(keys(attr), "bucket")
}
I am trying to access all groups and create groups in the below terraform code. But I am facing error This object does not have an attribute named "groups". Is there any logic I am missing here in the resource "og" "example"
for_each=toset(flatten(local.instances[*].groups)). Thanks
locals {
instances = {
test1 = {
baseUrl = "url1"
subDomain = "sd1"
groups = [
"app1",
"app2",
],
}
test2 = {
baseUrl = "url2"
subDomain = "sd2"
groups = [
"t1",
"t2",
],
}
}
}
resource "og" "example" {
for_each = toset(flatten(local.instances[*].groups))
name = each.value
description = "${each.value}-access"
}
Your local variable is a map, not a list. So it should be:
for_each = toset(flatten(values(local.instances)[*].groups))
I will like to create a policy resource depending on how many projects are described. Also, the policy must be different between these projects, for example, I have this far:
projects = ["project1", "project2"]
projects_resources = ["project1/X", "project1/Y", "project2/X", "project2/Y"]
data source:
data "template_file" "project_policy" {
template = file("${path.module}/project_policy.tpl")
vars = {
projects_resources = join(",", var.projects_resources)
}
}
project_policy.tpl:
...
"Resource": [
%{ for projects_resources in slice(split(",", projects_resources), 0, length(split(",", projects_resources))-1) }
"arn:aws:...${projects_resources}}",
%{ endfor }
%{ for projects_resources in slice(split(",", projects_resources), length(split(",", projects_resources))-1, length(split(",", projects_resources))) }
"arn:aws:...${projects_resources}}"
%{ endfor }
policy resource:
resource "aws_iam_policy" "iam_policies_projects" {
count = length(var.projects)
name = "policy_${var.projects[count.index]}"
policy = data.template_file.policy_projects.rendered
}
Current Outcome:
policy_proyect1 and policy_proyect2 created with:
"Resource":
"arn:aws:...proyect1/X",
"arn:aws:...proyect1/Y",
"arn:aws:...proyect2/X",
"arn:aws:...proyect2/Y"
what I would like to achieve:
policy_proyect1 with:
"Resource":
"arn:aws:...proyect1/X",
"arn:aws:...proyect1/Y",
policy_proyect2 with:
"Resource":
"arn:aws:...proyect2/X",
"arn:aws:...proyect2/Y",
I don't know if there is a way to create 2 rendered files for each policy in which only the values that correspond to the projects are passed as arguments.
Thanks!
You can create new local map and then use that for the template:
variable "projects" {
default = ["project1", "project2"]
}
variable "projects_resources" {
default = ["project1/X", "project1/Y", "project2/X", "project2/Y", "project2/Z"]
}
locals {
proj_res_map = {for p in var.projects:
p => [for v in var.projects_resources : v if length(regexall("${p}.*", v)) > 0]
}
}
which gives:
proj_res_map = {
"project1" = [
"project1/X",
"project1/Y",
]
"project2" = [
"project2/X",
"project2/Y",
"project2/Z",
]
}
Then use it:
data "template_file" "project_policy" {
for_each = local.proj_res_map
template = file("${path.module}/project_policy.tpl")
vars = {
projects_resources = join(",", each.value)
}
}
Then iam_policies_projects probably will also need to be adjusted to account for for_each in template_file:
resource "aws_iam_policy" "iam_policies_projects" {
for_each = local.proj_res_map
name = "policy_${each.key}"
policy = data.template_file.policy_projects[each.key].rendered
}
I have below which creates avi gslbservice with a single pool created and attached to it. I would like to create a second pool created and attached to it. Can any one please guide?
I am new to terraform, I saw few tutorials on for_each fn. But not able to figure out, how to apply it for my need.
I have highlighted the block which create the gslb pool
resource "avi_gslbservice" "avi_gslbservice" {
name = "helloworldssl-gslb"
tenant_ref = data.avi_tenant.avi_tenant.id
domain_names = ["xxxxxxxxx"]
health_monitor_refs = [avi_healthmonitor.avi_healthmonitor_gslb.id]
enabled = true
pool_algorithm = "GSLB_SERVICE_ALGORITHM_GEO"
ttl = "30"
created_by = "xxxxxx"
description = "xxxxxx"
down_response {
type = "GSLB_SERVICE_DOWN_RESPONSE_ALL_RECORDS"
}
**groups {
priority = 10
members {
ip {
type = "V4"
addr = ""
}
fqdn = "xxxxxxxxxxxxxx"
vs_uuid = ""
cluster_uuid = ""
ratio = 1
enabled = true
}
name = "helloworldssl-gslb-pool1"
algorithm = "GSLB_ALGORITHM_TOPOLOGY"
}**
}
Edit Aug 8th 2021 - For now I have a work around of duplicating whole groups block two times.
Here is how you do it,
dynamic "groups" {
for_each = var.avi_gslbservice_groups
content {
dynamic "members" {
for_each = groups.value.avi_gslbservice_groups_ip
content {
ip {
type = "V4"
addr = ""
}
fqdn = members.value["host"]
vs_uuid = ""
cluster_uuid = ""
ratio = 1
enabled = members.value["enabled"]
}
}
name = groups.value["name"]
priority = groups.value["priority"]
algorithm = groups.value["algorithm"]
}
}
values will come from json file as below,
{
"avi_gslbservice_groups": [
{
"name": "us-east-1",
"priority": 7,
"algorithm": "GSLB_ALGORITHM_ROUND_ROBIN",
"avi_gslbservice_groups_ip": [
{
"host": "host1",
"enabled": "true"
},
{
"host": "host2",
"enabled": "false"
}
]
},
{
"name": "us-east-2",
"priority": 10,
"algorithm": "GSLB_ALGORITHM_TOPOLOGY",
"avi_gslbservice_groups_ip": [
{
"host": "host1",
"enabled": "true"
},
{
"host": "host2",
"enabled": "false"
}
]
}
]
}
I want to create a iam_policy_arn_list in terraform where the list consists of the "FullAccess" arns of existing AWS policies, and the arn of a policy that I create on the fly. (I'm trying to create a Lambda function that can read/write to only a specified bucket.) If I only use existing AWS policies, then the following ingredients in my setup work:
variable "iam_policy_arn_list" {
type = list(string)
description = "IAM Policies to be attached to role"
default = [
"arn:aws:iam::aws:policy/CloudWatchFullAccess",
"arn:aws:iam::aws:policy/AmazonSESFullAccess",
"arn:aws:iam::aws:policy/AmazonS3FullAccess"
]
}
resource "aws_iam_role_policy_attachment" "role-policy-attachment" {
role = "${var.prefix}${var.role_name}"
count = length(var.iam_policy_arn_list)
policy_arn = var.iam_policy_arn_list[count.index]
depends_on = [aws_iam_role.iam_for_lambda]
}
But now I want to remove "arn:aws:iam::aws:policy/AmazonS3FullAccess" and replace it with the arn of a policy that I create on the fly that lets the Lambda function only access a specified S3 bucket. Where I am stuck is how to end up with a list variable of the rough form:
variable "iam_policy_arn_list" {
type = list(string)
description = "IAM Policies to be attached to role"
default = [
"arn:aws:iam::aws:policy/CloudWatchFullAccess",
"arn:aws:iam::aws:policy/AmazonSESFullAccess",
arn_of_the_policy_I_create_on_the_fly
]
}
... because the concat function will not work when defining variables. I have tried using the concat function elsewhere, but nothing seems to work. E.g. I tried:
resource "aws_iam_policy" "specific_s3_bucket_policy" {
name = "my_name"
description = "Grant access to one specific S3 bucket"
policy = jsonencode({
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"s3:ListAllMyBuckets",
"s3:ListBucket"
],
"Resource" : "*"
},
{
"Effect" : "Allow",
"Action" : [
"s3:PutObject",
"s3:PutObjectAcl",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:DeleteObject"
],
"Resource" : "arn:aws:s3:::${var.S3_BUCKET_NAME}/*"
}
]
})
}
resource "aws_iam_role_policy_attachment" "role-policy-attachment" {
role = "${var.prefix}${var.role_name}"
count = length(var.iam_policy_arn_list)
policy_arn = concat(var.iam_policy_arn_list, [aws_iam_policy.specific_s3_bucket_policy.arn])[count.index]
depends_on = [aws_iam_role.iam_for_lambda]
}
... but this does not work. Suggestions?
Given the following iam_policy_arn_list:
variable "iam_policy_arn_list" {
type = list(string)
description = "IAM Policies to be attached to role"
default = [
"arn:aws:iam::aws:policy/CloudWatchFullAccess",
"arn:aws:iam::aws:policy/AmazonSESFullAccess",
]
}
Then create a local value like this:
locals {
combined_iam_policy_arn_list = concat(var.iam_policy_arn_list, [aws_iam_policy.specific_s3_bucket_policy.arn])
}
And then apply it like this:
resource "aws_iam_role_policy_attachment" "role-policy-attachment" {
role = "${var.prefix}${var.role_name}"
count = length(local.combined_iam_policy_arn_list)
policy_arn = local.combined_iam_policy_arn_list[count.index]
depends_on = [aws_iam_role.iam_for_lambda]
}