terraform .12 - add multiple inline policies - amazon-web-services

I am wondering if there is an easy way to add multiple json file policies
mypolicy1.json,
mypolicy2.json,
mypolicy3.json
as of now this is my code.. works great for one policy
variable "iam_policy_path" {
default = "./mypolicy.json"
}
resource "aws_iam_role_policy" "role" {
name = var.name
role = var.role
policy = file(var.iam_policy_path)
}
module "aws_iam_role_policy" {
source = "../modules/mypolicypolicy/"
name = "mypolicy"
role = module.myrole.myroleout
iam_policy_path = "new_policy_path.json"
}

One way would be to use for_each and having your iam_policy_path being a list of paths.
For example:
variable "iam_policy_path" {
default = ["./mypolicy.json", "./mypolicy2.json"]
}
resource "aws_iam_role_policy" "role" {
for_each = toset(var.iam_policy_path) # for each requires set.
name = var.name
role = var.role
policy = file(each.key)
}
Then when using the module:
module "aws_iam_role_policy" {
source = "../modules/mypolicypolicy/"
name = "mypolicy"
role = module.myrole.myroleout
iam_policy_path = ["new_policy_path.json", "new_policy_path2.json"]
}
Based on the extra info. The complete solution may require also using aws_iam_role_policy_attachment which attaches a managed policy to a role.

Related

Adding S3 bucket policy to multiple buckets with for_each Terraform module

I have the following module which works fine.
Module
resource "aws_s3_bucket" "buckets" {
bucket = var.s3_buckets
}
resource "aws_s3_bucket_acl" "buckets" {
bucket = var.s3_buckets
acl = "private"
}
Root module
module "s3_buckets" {
source = "./modules/s3"
for_each = toset([
"bucket-test1-${var.my_env}",
"bucket-test2-${var.my_env}",
])
s3_buckets = each.value
}
I would like to add get the following policy to all the buckets in the list. Obviously the count option below does not work.
data "aws_iam_policy_document" "buckets" {
count = length(var.s3_buckets)
statement {
sid = "AllowSSlRequestsOnly"
actions = ["s3:*"]
effect = "Deny"
condition {
test = "Bool"
variable = "aws:SecureTransport"
values = ["false"]
}
principals {
type = "*"
identifiers = ["*"]
}
resources = ["arn:aws:s3:::${var.s3_buckets}"]
}
}
resource "aws_s3_bucket_policy" "buckets" {
bucket = var.s3_buckets
policy = data.aws_iam_policy_document.buckets[count.index].json
}
I'm thinking that I need another for_each and use a resource for the IAM policy, I have seen an example such as below, but in the current form I'm providing a string instead a set of strings. Any ideas?
resource "aws_s3_bucket_policy" "buckets" {
for_each = var.s3_buckets
bucket = each.key
policy = jsonencode({
Version = "2012-10-17"
Id = "AllowSSlRequestsOnly",
Statement = [
{
Sid = "AllowSSlRequestsOnly"
Effect = "Deny"
Principal = "*"
Action = "s3:*"
Resource = each.value.arn
Condition = {
Bool = {
"aws:SecureTransport": "false"
}
}
}
]
})
}
If you are adding the policy inside the module (which you probably are, otherwise it doesn't make much sense to attach the policies outside, since you have full control) - then why do you need to mingle with count() at all?
Create the policy and attach to the bucket like:
data "aws_iam_policy_document" "buckets" {
statement {
sid = "AllowSSlRequestsOnly"
actions = ["s3:*"]
effect = "Deny"
condition {
test = "Bool"
variable = "aws:SecureTransport"
values = ["false"]
}
principals {
type = "*"
identifiers = ["*"]
}
resources = ["arn:aws:s3:::${var.s3_buckets}"]
}
}
resource "aws_s3_bucket_policy" "buckets" {
bucket = var.s3_buckets
policy = data.aws_iam_policy_document.buckets.json
}
Couple more points:
It's a singular bucket that you are passing to the module, yet the variable is named s3_buckets, it is confusing.
Using var.s3_buckets for all dependent resources is not the best practice. Create the bucket with var.s3.buckets, after which use the outputs of the resource. This and examples of policies reside here: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket
good luck ☺️

How to refer parent resource in child resource when using terraform map (and for_each)

I have the following code which works fine.
resource "aws_ses_email_identity" "main_from_email" {
email = "some#email.com"
}
data "aws_iam_policy_document" "main_from_email_policy_document" {
statement {
actions = ["SES:SendEmail", "SES:SendRawEmail"]
resources = [aws_ses_email_identity.main_from_email.arn]
principals {
identifiers = ["*"]
type = "AWS"
}
}
}
resource "aws_ses_identity_policy" "email_notif_policy" {
identity = aws_ses_email_identity.main_from_email.arn
name = "${local.namespace}-ses_main_from_email_policy"
policy = data.aws_iam_policy_document.main_from_email_policy_document.json
}
The above code is working fine. But the email is hard coded. I would like to have the (email) resources to be created based on some configuration provided in terraform.tfvars as follows:
clientemails = {
"client1" = { "email" = "client1#gmail.com" }
"client2" = { "email" = "client2#gmail.com" }
}
I modified main resource as follows:
resource "aws_ses_email_identity" "main_from_email_map" {
for_each = var.clientemails
email = each.value.email
}
But, I don't know how I can modify "aws_iam_policy_document" and "aws_ses_identity_policy" to follow "aws_ses_email_identity".
How do I modify my terraform script to honor "clientemails" configuration?
This should be relatively straight forward to do:
resource "aws_ses_email_identity" "main_from_email_map" {
for_each = var.clientemails
email = each.value.email
}
data "aws_iam_policy_document" "main_from_email_policy_document" {
statement {
actions = ["SES:SendEmail", "SES:SendRawEmail"]
resources = values(aws_ses_email_identity.main_from_email_map)[*].arn
principals {
identifiers = ["*"]
type = "AWS"
}
}
}
resource "aws_ses_identity_policy" "email_notif_policy" {
for_each = aws_ses_email_identity.main_from_email_map
identity = each.value.arn
name = "${local.namespace}-ses_main_from_email_policy"
policy = data.aws_iam_policy_document.main_from_email_policy_document.json
}
Here, there will be a single policy which will allow all the same actions for all the SES email identity ARNs. To achieve this, since the SES email identity resource was created using for_each meta-argument, the values built-in function [1] was used to fetch all the values of the ARN attribute for all the keys (hence the [*] part).
There could be a way to modify the data source with for_each if needed (i.e., to create a policy per client email):
data "aws_iam_policy_document" "main_from_email_policy_document" {
for_each = aws_ses_email_identity.main_from_email_map
statement {
actions = ["SES:SendEmail", "SES:SendRawEmail"]
resources = [each.value.arn]
principals {
identifiers = ["*"]
type = "AWS"
}
}
}
Then, you would also have to fix the SES identity policy:
resource "aws_ses_identity_policy" "email_notif_policy" {
for_each = aws_ses_email_identity.main_from_email_map
identity = each.value.arn
name = "${local.namespace}-ses_main_from_email_policy"
policy = data.aws_iam_policy_document.main_from_email_policy_document[each.key].json
}
The code where for_each meta-argument is used with the resources (e.g., aws_ses_email_identity.main_from_email_map) is called for_each chaining [2].
[1] https://developer.hashicorp.com/terraform/language/functions/values
[2] https://developer.hashicorp.com/terraform/language/meta-arguments/for_each#chaining-for_each-between-resources

How to create multiple policies and policy attachments for each iam group terraform

So I have multiple IAM groups which I am looping through as follows :
resource "aws_iam_group" "all_iam_groups" {
for_each = var.iam_user_groups
name = "${local.csi}-${each.key}"
path = "/"
}
This will create multiple IAM groups. Now for each IAM groups I will have an IAM Policy which I will attatch. Rather than having to do it manually and create multiple resources what is the best approach to take here. So far I have been doing it as follows :
resource "aws_iam_policy" "finance_read_only" {
name = "${local.csi}-finance-read-only"
path = "/"
policy = data.aws_iam_policy_document.finance_read_only.json
}
resource "aws_iam_policy" "security_read_only" {
name = "${local.csi}-security-read-only"
path = "/"
policy = data.aws_iam_policy_document.security_read_only.json
}
resource "aws_iam_group_policy_attachment" "security_read_only" {
group = aws_iam_group.security_team.name
policy_arn = aws_iam_policy.security_read_only.arn
}
resource "aws_iam_group_policy_attachment" "finance_read_only" {
group = aws_iam_group.finance_team.name
policy_arn = aws_iam_policy.finance_read_only.arn
}
For example purposes I have added in the group names but I know i can use a for-each loop to go through the names but am not sure how I would be able to look through the IAM Policies and policy attatchments.
I do understand that I will have multiple aws_iam_policy_document which is fine.
There's a direct relationship between the following resources: aws_iam_policy , the data source for aws_iam_policy_document, the aws_iam_group_policy_attachment and the aws_iam_group resource.
My suggestion is to create a variable of a list of objects type, where you define different attributes that are needed for the resources required.
Example:
variable "policies" {
type = list(object({
name = string
statement = object
}))
default = [
{
name = "finance-read-only"
statement = {
sid = "XXX"
actions = ["ec2:XXX"]
resources = ["XXX"]
}
},
{
name = "security-read-only"
statement = {
sid = "XXX"
actions = ["ec2:XXX"]
resources = ["XXX"]
}
}
]
}
data "aws_iam_policy_document" "this" {
for_each = { for policy in var.policies : policy.name => policy }
statement = each.value.statement
}
resource "aws_iam_policy" "this" {
for_each = { for policy in var.policies : policy.name => policy }
name = format("${local.csi}-%s", each.key)
path = "/"
policy = data.aws_iam_policy_document.this[each.key].json
}
resource "aws_iam_group" "this" {
for_each = { for policy in var.policies : policy.name => policy }
name = each.key
}
resource "aws_iam_group_policy_attachment" "security_read_only" {
for_each = { for policy in var.policies : policy.name => policy }
group = aws_iam_group.this[each.key].name
policy_arn = aws_iam_policy.this[each.key].arn
}

Get a list of created resources in terraform

I am creating AWS ECR repositories via terraform
resource "aws_ecr_repository" "repo1" {
name = "repo1"
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = true
}
}
resource "aws_ecr_repository" "repo2" {
name = "repo2"
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = true
}
}
Now I want to attach a policy to all ECR repositories.
Question is, is there a dynamic way to create a list of all the resources (of type ECR) created using the terraform script? If yes then we can have a for_each on that list and attach a policy.
Or is there any better way to do it?
P.S. I know I can attach policy by writing the following for each. I want to avoid duplication and avoid a case where policy is not attached if the block is missed by someone
resource "aws_ecr_lifecycle_policy" "insights_repository_policy" {
repository = aws_ecr_repository.insights_repository.name
policy = local.ecr_cleanup_policy
}
Edit: Question 2
There are some accounts I want to give access to. If I use list of repositories to create and then I want to assign policies for each account then it would make nested for loops. Is there a cleaner solution for that?
local {
accounts = {test=account_id_123, prod=account_id_456}
}
resource "aws_ecr_repository_policy" "access-permission" {
for_each = local.accounts
policy = <<POLICY
...
POLICY
repository = aws_ecr_repository.repo_template.name
}
Not in your form. It would be better if you used for_each or count. For example:
variable "repos" {
default = ["repo1", "repo2"]
}
resource "aws_ecr_repository" "repo" {
for_each = to_set(var.repos)
name = each.key
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = true
}
}
then you can do:
resource "aws_ecr_lifecycle_policy" "insights_repository_policy" {
for_each = aws_ecr_repository.repo
repository = each.value.name
policy = local.ecr_cleanup_policy
}

How can I attach a generic policy to more than 1 bucket, while having the bucket arn dynamically?

I am learning terraform and currently attempting to attach a policy to a created bucket. More specifically, the policy I want to attach has the same permissions/structure but the only difference is the resources section. I will illustrate with an example:
Let's say I create an s3 bucket like:
module "happy_bucket" {
source = "outer space"
name = "happy-bucket"
}
And another bucket like:
module "sad_bucket" {
source = "outer space"
name = "sad-bucket"
}
And now I have a policy that looks like:
data "aws_iam_policy_document" "some_policy" {
statement {
effect = "Allow"
resources = [module.some_bucket.bucket_arn]
actions = ["s3:GetObject", "s3:GetObjectVersion"]
}
}
And now I would like to attach "some_policy" to both "sad-bucket" and "happy-bucket". But I want to do that without having to repeat myself by creating the policy two times (because I need the .bucket_arn to be based on the bucket). In other words, I want to create one generic policy, and attach it to the 2 buckets I created (while picking up the arn dynamically).
Bucket policies require principals, so you need to add that to your some_policy. Having said that, if you want to keep using aws_iam_policy_document you use for_each to iterate over your buckets.
For example, if your module is:
variable "name" {}
resource "aws_s3_bucket" "b" {
bucket = var.name
}
output "bucket_arn" {
value = aws_s3_bucket.b.arn
}
output "bucket_name" {
value = aws_s3_bucket.b.id
}
then in parent module, you can:
module "happy_bucket" {
source = "./modules/buckets"
name = "happy-bucket-231123124ff"
}
module "sad_bucket" {
source = "./modules/buckets"
name = "sad-bucket-231123124ff"
}
locals {
my_buckets = {for bucket in [module.happy_bucket, module.sad_bucket]:
bucket.bucket_name => bucket}
}
data "aws_iam_policy_document" "some_policy" {
for_each = local.my_buckets
statement {
effect = "Allow"
resources = ["${each.value.bucket_arn}/*"]
actions = ["s3:GetObject", "s3:GetObjectVersion"]
principals {
type = "AWS"
identifiers = ["*"]
}
}
}
resource "aws_s3_bucket_policy" "bucket_policie" {
for_each = local.my_buckets
bucket = each.key
policy = data.aws_iam_policy_document.some_policy[each.key].json
}