Dynamically AWS IAM policy document with principals - amazon-s3

I am creating a dynamic AWS IAM policy document "FROM" static to "TO" dynamic but principals part gives "An argument named "principals" is not expected here"
If I delete "principals" from the aws_iam_policy_document it works. Any suggestion would be helpful.
FROM
data "aws_iam_policy_document" "bucket_policy" {
statement {
principals {
type = "AWS"
identifiers = [
"arn:aws:iam::sdfsdfsdeploy",
"arn:aws:iam::sdfsdfsdeploy/OrganizationAccountAccessRole"
]
}
actions = [
"s3:GetObject",
"s3:PutObject"
]
resources = formatlist("arn:aws:s3:::%s/*", var.bucket_name)
}
}
TO
this code in source = "../../modules/s3/main.tf"
data "aws_iam_policy_document" "bucket_policy" {
dynamic "statement" {
for_each = var.policies_list
iterator = role
content {
effect = lookup(role.value, "effect", null)
principals = lookup(role.value, "principals", null)
actions = lookup(role.value, "actions", null)
resources = lookup(role.value, "resources", null)
}
}
}
module "s3_test" {
source = "../../modules/s3"
region = var.region
policies_list = [
{
effect = "Allow"
principals = {
type = "AWS"
identifiers = [
"arn:aws:iam::3ssdfsdfy",
"arn:aws:iam::3ssdfsdfy:role/OrganizationAccountAccessRole"
]
}
actions = [
"s3:GetObject",
"s3:PutObject"
]
resources = formatlist("arn:aws:s3:::%s/*", "teskjkjsdkfkjskdjhkjfhkjhskjdf")
}
]
}

Found it.
variable "policies_list" {
description = "nested block: s3_aws_iam_policy_document"
type = set(object(
{
actions = list(string)
effect = string
principals = set(object(
{
type = string
identifiers = list(string)
}
))
resources = list(string)
}
))
default = []
}
data "aws_iam_policy_document" "bucket_policy" {
dynamic "statement" {
for_each = var. policies_list
iterator = role
content {
effect = lookup(role.value, "effect", null)
actions = lookup(role.value, "actions", null)
dynamic "principals" {
for_each = role.value.principals
content {
type = principals.value["type"]
identifiers = principals.value["identifiers"]
}
}
resources = lookup(role.value, "resources", null)
}
}
}
based on
https://github.com/niveklabs/tfwriter/blob/1ea629ed386bbe6a8f21617a430dae19ba536a98/google-beta/r/google_storage_bucket.md

Related

Write a dynamic Terraform block for a load balancer listener rule

I'm new to dynamic blocks and am having some trouble writing rules to listeners on a load balancer that was created using for_each.
Below are the resources I created:
resource "aws_lb_listener" "app_listener_forward" {
for_each = toset(var.app_listener_ports)
load_balancer_arn = aws_lb.app_alb.arn
port = each.value
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS-1-2-Ext-2018-06"
certificate_arn = var.ssl_cert
default_action {
type = "forward"
forward {
dynamic "target_group" {
for_each = aws_lb_target_group.app_tg
content {
arn = target_group.value["arn"]
}
}
stickiness {
enabled = true
duration = 86400
}
}
}
}
resource "aws_lb_listener_rule" "app_https_listener_rule" {
for_each = toset(var.app_listener_ports)
listener_arn = aws_lb_listener.app_listener_forward[each.value].arn
action {
type = "forward"
forward {
dynamic "target_group" {
for_each = aws_lb_target_group.app_tg
content {
arn = target_group.value["arn"]
}
}
}
}
dynamic "condition" {
for_each = var.images
path_pattern {
content {
values = condition.value["paths"]
}
}
}
}
resource "aws_lb_target_group" "app_tg" {
for_each = var.images
name = each.key
port = each.value.port
protocol = "HTTP"
target_type = "ip"
vpc_id = aws_vpc.app_vpc.id
health_check {
interval = 130
timeout = 120
healthy_threshold = 10
unhealthy_threshold = 10
}
stickiness {
type = "lb_cookie"
cookie_duration = 86400
}
}
Below are how the variables are defined:
variable "images" {
type = map(object({
app_port = number
paths = set(string)
}))
{
"app-one" = {
app_port = 3000
paths = [
"/appOne",
"/appOne/*"
]
}
"app-two" = {
app_port = 4000
paths = [
"/appTwo",
"/appTwo/*"
]
}
}
variable "app_listener_ports" {
type = list(string)
default = [
80, 443, 22, 7999, 8999
]
}
Upon executing, I am getting an error dealing with the path_pattern being unexpected:
Error: Unsupported block type
│
│ on alb.tf line 78, in resource "aws_lb_listener_rule" "app_https_listener_rule":
│ 78: path_pattern {
│
│ Blocks of type "path_pattern" are not expected here.
I've tried a few ways to get this dynamic block but am having some difficulty. Any advice would be appreciated.
Thank you!
Try it like this:
dynamic "condition" {
for_each = var.images
content {
path_pattern {
values = condition.value.paths
}
}
}
And change the type of paths from set(string) to list(string).
This is also completely acceptable:
dynamic "condition" {
for_each = var.images
content {
path_pattern {
values = condition.value["paths"]
}
}
}
However, in my opinion here it's better to not use a dynamic block for the condition to maintain readability and maintenance.
condition {
path_pattern {
values = [
"/appOne",
"/appOne/*" ## can also use variables if you prefer !!
]
}
}
I have already answered your original post related to the problem which you had after fixing the dynamic syntax.
Post URL: Error when creating dynamic terraform rule for alb listener rule

Terraform Object Lock Configuration: AccessDenied

I have this terraform script that works perfectly fine for the whole s3 module but it cannot create the Object lock configuration resource and returns the message :
error creating S3 bucket (bucket-name) Object lock configuration: AccessDenied: AccessDenied
Status code 403, request id: ..., host id: ...
Desite the message, the S3 bucket is actually created, but I still get this error, maybe there is something missing in the policy ?
Here is my code.
module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
version = "3.4.0"
bucket = local.bucket_name
...
object_lock_enabled = true
attach_policy = true
policy = data.aws_iam_policy_document.voucher_s3_bucket.json
versioning = {
status = var.status
mfa_delete = var.mfa_delete
}
server_side_encryption_configuration = {
rule = {
apply_server_side_encryption_by_default = {
kms_master_key_id = aws_kms_key.voucher_s3_bucket.arn
sse_algorithm = "aws:kms"
}
}
}
}
data "aws_iam_policy_document" "s3_bucket_kms_key" {
statement {
sid = "AllowPutRoles"
effect = "Allow"
actions = ["kms:GenerateDataKey"]
principals {
identifiers = local.put_object_roles #we can use event_gateway iam_role for now
type = "AWS"
}
resources = ["*"]
}
statement {
sid = "AllowAdmin"
effect = "Allow"
actions = [
"kms:*",
]
principals {
identifiers = [data.aws_iam_role.admin_role.arn, data.aws_iam_role.default_role.arn, data.aws_iam_role.automation_role.arn]
type = "AWS"
}
resources = ["*"]
}
}
resource "aws_kms_key" "s3_bucket" {
tags = {
"s3_bucket" = local.bucket_name
}
enable_key_rotation = true
policy = data.aws_iam_policy_document.voucher_s3_bucket_kms_key.json
}
resource "aws_s3_bucket_object_lock_configuration" "s3_bucket_object_lock_configuration" {
bucket = local.bucket_name
rule {
default_retention {
mode = "GOVERNANCE"
years = 10
}
}
}
data "aws_iam_policy_document" "voucher_s3_bucket" {
statement {
sid = "DenyNoKMSEncryption"
effect = "Deny"
actions = ["s3:PutObject"]
principals {
identifiers = ["*"]
type = "*"
}
resources = ["${module.voucher_s3_bucket.s3_bucket_arn}/*"]
condition {
test = "StringNotEqualsIfExists"
values = ["aws:kms"]
variable = "s3:x-amz-server-side-encryption"
}
condition {
test = "Null"
values = ["false"]
variable = "s3:x-amz-server-side-encryption"
}
}
statement {
sid = "DenyWrongKMSKey"
effect = "Deny"
actions = ["s3:PutObject"]
principals {
identifiers = ["*"]
type = "*"
}
resources = ["${module.s3_bucket.s3_bucket_arn}/*"]
condition {
test = "StringNotEquals"
values = [aws_kms_key.voucher_s3_bucket.arn]
variable = "s3:x-amz-server-side-encryption-aws-kms-key-id"
}
}
statement {
sid = "AllowAdminDefault"
effect = "Allow"
actions = ["s3:*"]
principals {
identifiers = [data.aws_iam_role.admin_role.arn, data.aws_iam_role.default_role.arn]
type = "AWS"
}
resources = [
"${module.voucher_s3_bucket.s3_bucket_arn}/*",
module.voucher_s3_bucket.s3_bucket_arn,
]
}
statement {
sid = "DenyDeleteActions"
effect = "Deny"
actions = ["s3:DeleteBucket", "s3:DeleteObject", "s3:DeleteObjectVersion", "s3:PutBucketObjectLockConfiguration"]
principals {
identifiers = ["*"]
type = "AWS"
}
resources = [
"${module.s3_bucket.s3_bucket_arn}/*",
module.s3_bucket.s3_bucket_arn,
]
}
}

Terraform - Manipulate local variables

I am trying to setup some aws_ssoadmin_managed_policy_attachments (there will end up being a lot) so ideally I just want to be able to update a local variable and then use for_each to go through said variable and churn out a bunch of resources the other side.
I currently have something like the following:
locals {
roles = {
admin = {
//managed_policy_name = toset(["AdministratorAccess", "AWSSupportAccess"])
managed_policy_name = "AWSSupportAccess"
perm_set_name = "admin"
}
auditor = { ...
}
}
There will be a bunch more roles within this. I use the below to transform it to something more usable by the for_each.
managed_policy_map = [for keys, managed_policy_names in local.roles : {
for managed_policy_name in managed_policy_names :
format("%s-%s", keys, managed_policy_name) => { "managed_policy_name" : managed_policy_name }
}]
perm_set_to_managed_policy_map = merge(local.managed_policy_map...)
Output from this:
a = [{
"admin-AWSSupportAccess" = {
"managed_policy_name" = "AWSSupportAccess"
"perm_set_name" = "admin"
}
"admin-admin" = {
"managed_policy_name" = "admin"
"perm_set_name" = "admin"
}
"auditor-ReadOnlyAccess" = {
"managed_policy_name" = "ReadOnlyAccess"
"perm_set_name" = "auditor"
}
"auditor-auditor" = {
"managed_policy_name" = "auditor"
"perm_set_name" = "auditor"
}
"auditor-auditor-permission-set" = {
"managed_policy_name" = "auditor-permission-set"
"perm_set_name" = "auditor"
}
},]
Now, ideally, I would like to use the commented managed_policy_name which uses a list (or set to avoid dupes) //managed_policy_name = toset(["AdministratorAccess", "AWSSupportAccess"]) and cycle through that to end up with something like.
a = [{
"admin-AdministratorAccess" = {
"managed_policy_name" = "AdministratorAccess"
"perm_set_name" = "admin"
}
"admin-AWSSupportAccess" = {
"managed_policy_name" = "AWSSupportAccess"
"perm_set_name" = "admin"
}
"auditor-ReadOnlyAccess" = {
"managed_policy_name" = "ReadOnlyAccess"
"perm_set_name" = "auditor"
} ...
Is this doable? My assumption is that it will be fairly complicated or I'm missing some Terraform function that makes it easy. Any help would be greatly appreciated.
You can simplify your variable structure:
locals {
roles = {
admin = toset(["AdministratorAccess", "AWSSupportAccess"])
auditor = ...
}
}
and then simplify your for expression:
managed_policy_map = [for role, managed_policy_names in local.roles : {
for policy in managed_policy_names : "${role}-${policy}" => {
"managed_policy_name" = policy
"perm_set_name" = role
}
}]
to easily achieve the same output structure with the set type instead of the string type:
[
{
"admin-AWSSupportAccess" = {
"managed_policy_name" = "AWSSupportAccess"
"perm_set_name" = "admin"
}
"admin-AdministratorAccess" = {
"managed_policy_name" = "AdministratorAccess"
"perm_set_name" = "admin"
}
},
]
I would also recommend simplifying the output structure for easier use in the for_each meta-argument according to the intention stated in the question.

terraform azurerm_data_factory_pipeline assing type to the variables

in terraform documentation i found the follow example:
resource "azurerm_data_factory_pipeline" "test" {
name = .....
resource_group_name = ...
data_factory_id = ...
variables = {
"bob" = "item1"
}
but I need to create a boolean variable, in the portal Azure I have the type field.
how can I set the variable like this:
"variables": {
"END": {
"type": "Boolean",
"defaultValue": false
}
}
Based on your question, if you are asking how to create a variable of type boolean in Terraform, that is done like this:
variable "END" {
type = bool
description = "End variable."
default = false
}
You can reference that variable then in the resource definition:
resource "azurerm_data_factory_pipeline" "test" {
name = .....
resource_group_name = ...
data_factory_id = ...
variables = {
"END" = var.END
}
}
Or alternatively you can set it without defining the Terraform variable like this:
resource "azurerm_data_factory_pipeline" "test" {
name = .....
resource_group_name = ...
data_factory_id = ...
variables = {
"END" = false
}
}

Dynamic block with optional data

let say we have this locals:
locals = {
schemas = [
{
name = "is_cool"
attribute_data_type = "Boolean"
mutable = true
required = false
},
{
name = "firstname"
attribute_data_type = "String"
mutable = true
required = false
min_length = 1
max_length = 256
}
]
}
What I would like to achieve is to use dynamic to build schemas and when the schema is a string I would like to add the string_attribute_constraints block.
This is what I did so far but it adds an empty string_attribute_constraints block when the schema is Boolean
dynamic "schema" {
for_each = var.schemas
content {
name = schema.value.name
attribute_data_type = schema.value.attribute_data_type
mutable = schema.value.mutable
required = schema.value.required
string_attribute_constraints {
min_length = lookup(schema.value, "min_length", null)
max_length = lookup(schema.value, "max_length", null)
}
}
}
terraform plan:
+ schema {
+ attribute_data_type = "Boolean"
+ mutable = true
+ name = "is_cool"
+ required = false
+ string_attribute_constraints {}
}
You can use a second nested dynamic block to tell Terraform how many string_attribute_constraints blocks to generate based on your rule:
dynamic "schema" {
for_each = var.schemas
content {
name = schema.value.name
attribute_data_type = schema.value.attribute_data_type
mutable = schema.value.mutable
required = schema.value.required
dynamic "string_attribute_constraints" {
for_each = schema.attribute_data_type == "String" ? [1] : []
content {
min_length = lookup(schema.value, "min_length", null)
max_length = lookup(schema.value, "max_length", null)
}
}
}
}
This works by making the for_each for the nested dynamic be an empty list in the case where we don't want to generate any blocks, and making it a single-element list in the case where we do. Since we need no references to string_attribute_constraints.key or string_attribute_constraints.value inside the block, we can set the value of the single element to anything, and so I just set it to 1 as an arbitrary placeholder.
dynamic "schema" {
for_each = local.my_schema
content {
name = schema.value.name
attribute_data_type = schema.value.attribute_data_type
mutable = schema.value.mutable
required = schema.value.required
dynamic "string_attribute_constraints" {
for_each = schema.value.attribute_data_type == "String" ? [1] : []
content {
min_length = lookup(schema.value, "min_length", 0)
max_length = lookup(schema.value, "max_length", 2048)
}
}
dynamic "number_attribute_constraints" {
for_each = schema.value.attribute_data_type == "Number" ? [1] : []
content {
min_value = lookup(schema.value, "min_value", 0)
max_value = lookup(schema.value, "max_value", 2048)
}
}
}
}