Issue with adding SES permissions to Terraform - amazon-web-services

I am very new to Terraform, so still finding my way around at the moment.
I am needing to add SES permissions to a Lambda function, for sending emails.
I thought it would be as simple as adding the DynamoDB permissions, but there seems to be a different format aws_ses_identity_policy instead of aws_iam_policy_attachment, and as a result, in the todo problem line, I can’t seem to just use .arn to link the policy to the Role.
Is there a different way of doing this? Am I looking at older versions of the library? Any help would be appreciated. Thanks.
### DynamoDB
…
resource "aws_iam_policy" "DynamoDBCrudPolicy" {
name = "DynamoDBCrudPolicy"
policy = data.aws_iam_policy_document.dynamodbPolicyDocument.json
}
### SES
data "aws_iam_policy_document" "sesPolicyDocument" {
statement {
actions = ["SES:SendEmail", "SES:SendRawEmail"]
resources = [aws_ses_domain_identity.SESPolicy.arn]
principals {
identifiers = ["*"]
type = "AWS"
}
}
}
resource "aws_ses_domain_identity" "SESPolicyDomainID" {
domain = "example.com"
}
resource "aws_ses_identity_policy" "SESPolicy" {
identity = aws_ses_domain_identity.SESPolicyDomainID.arn
name = "SESPolicy"
policy = data.aws_iam_policy_document.sesPolicyDocument.json
}
## Attach Policies to Role
resource "aws_iam_policy_attachment" "DynamoDBCrudPolicy_iam_policy_attachment" {
name = "DynamoDBCrudPolicy_iam_policy_attachment"
roles = [ aws_iam_role.DomainRole.name ]
policy_arn = aws_iam_policy.DynamoDBCrudPolicy.arn
}
resource "aws_iam_policy_attachment" "SES_iam_policy_attachment" {
name = "SESPolicy_iam_policy_attachment"
roles = [ aws_iam_role.DomainRole.name ]
# Todo problem here
policy_arn = aws_ses_identity_policy.SESPolicy.arn
}

Related

Defining proper IAM role for Cognito SMS using Terraform

I created a role with policy for Cognito to publish SNS. The problem with this when scanning via terraform security, is it complains of having an overly permissive (AVD-AWS-0057) since I'm using a wildcard in Resource: ["*"].
So, I made a change to this to only add the Cognito user pool ARN and SNS topic ARN, but still complain of the role not having an SNS publish permission. Where in fact, the action below indicates permission.
data "aws_iam_policy_document" "assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["cognito-idp.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "sts:ExternalId"
values = [aws_cognito_user_pool.uam_user_pool.arn]
}
}
}
resource "aws_iam_role" "iam_role" {
name = var.role_name
path = var.role_path
assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
tags = var.default_tags
}
data "aws_iam_policy_document" "role_policy" {
statement {
sid = "AllowSNSPublish"
effect = "Allow"
actions = ["sns:publish"]
resources = [
aws_sns_topic.topic.arn,
aws_cognito_user_pool.uam_user_pool.arn
]
}
}
resource "aws_iam_policy" "managed_policy" {
name = var.role_policy_name
policy = data.aws_iam_policy_document.role_policy.json
tags = var.default_tags
}
resource "aws_iam_role_policy_attachment" "managed_policy_attach" {
role = aws_iam_role.role.name
policy_arn = aws_iam_policy.managed_policy.arn
}
How do you properly set this up?
In your "assume_role_policy" you are referencing the ARN of the user_pool and checking this against the sts:ExternalId - you need to add the external-id here rather than the ARN.
data "aws_iam_policy_document" "assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["cognito-idp.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "sts:ExternalId"
values = ["<your-external-id>"]
}
}
}
You also don't need the ARN of the userpool in the "role_policy". This document is stating "whoever has this assigned to them can sns:publish to aws_sns_topic.topic.arn" - there is no need to specifc the userpool here. This is done at the point of attaching the policy to role/user/group (e.g. aws_iam_role.role.name).

Cannot create elasticSearch Domain using terraform

I'm trying to create elasticsearch cluster using terraform.
Using terraform 0.11.13
Please can someone point out why I'm not able to create log groups? What is the Resource Access Policy? is it the same as the data "aws_iam_policy_document" I'm creating?
Note: I'm using elasticsearch_version = "7.9"
code:
resource "aws_cloudwatch_log_group" "search_test_log_group" {
name = "/aws/aes/domains/test-es7/index-logs"
}
resource "aws_elasticsearch_domain" "amp_search_test_es7" {
domain_name = "es7"
elasticsearch_version = "7.9"
.....
log_publishing_options {
cloudwatch_log_group_arn = "${aws_cloudwatch_log_group.search_test_log_group.arn}"
log_type = "INDEX_SLOW_LOGS"
enabled = true
}
access_policies = "${data.aws_iam_policy_document.elasticsearch_policy.json}"
}
data "aws_iam_policy_document" "elasticsearch_policy" {
version = "2012-10-17"
statement {
effect = "Allow"
principals {
identifiers = ["*"]
type = "AWS"
}
actions = ["es:*"]
resources = ["arn:aws:es:us-east-1:xxx:domain/test_es7/*"]
}
statement {
effect = "Allow"
principals {
identifiers = ["es.amazonaws.com"]
type = "Service"
}
actions = [
"logs:PutLogEvents",
"logs:PutLogEventsBatch",
"logs:CreateLogStream",
]
resources = ["arn:aws:logs:*"]
}
}
I'm getting this error
aws_elasticsearch_domain.test_es7: Error creating ElasticSearch domain: ValidationException: The Resource Access Policy specified for the CloudWatch Logs log group /aws/aes/domains/test-es7/index-logs does not grant sufficient permissions for Amazon Elasticsearch Service to create a log stream. Please check the Resource Access Policy.
For ElasticSearch (ES) to be able to write to CloudWatch (CW) Logs, you have to provide a resource-based policy on your CW logs.
This is achieved using aws_cloudwatch_log_resource_policy which is missing from your code.
In fact, TF docs have a ready to use example of how to do it for ES, thus you should be able to just copy and paste it.
ES access policies are different from CW log policies, as they determine who can do what on your ES domain. Thus, you would have to adjust that part of your code to meet your requirements.

How can i provision IAM Role in aws with terraform?

as i'm new with terraform, i'd like to ask your help once i got stuck for almost a day.
When trying to apply a IAC to deploy a Nginx service into a ECS(EC2 launch type) on aws i'm facing the following problem:
Error: Error creating IAM Role nginx-iam_role: MalformedPolicyDocument: Has prohibited field Resource status code: 400, request id: 0f1696f4-d86b-4ad1-ba3b-9453f3beff2b
I have already checked the documentation and the syntax is fine. What else could be wrong?
Following the snippet code creating the IAM infra:
provider "aws" {
region = "us-east-2"
}
data "aws_iam_policy_document" "nginx-doc-policy" {
statement {
sid = "1"
actions = [
"ec2:*"
]
resources = ["*"]
}
}
resource "aws_iam_role" "nginx-iam_role" {
name = "nginx-iam_role"
path = "/"
assume_role_policy = "${data.aws_iam_policy_document.nginx-doc-policy.json}"
}
resource "aws_iam_group_policy" "nginx-group-policy" {
name = "my_developer_policy"
group = "${aws_iam_group.nginx-iam-group.name}"
policy = "${data.aws_iam_policy_document.nginx-doc-policy.json}"
}
resource "aws_iam_group" "nginx-iam-group" {
name = "nginx-iam-group"
path = "/"
}
resource "aws_iam_user" "nginx-user" {
name = "nginx-user"
path = "/"
}
resource "aws_iam_user_group_membership" "nginx-membership" {
user = "${aws_iam_user.nginx-user.name}"
groups = ["${aws_iam_group.nginx-iam-group.name}"]
}
If you guys need the remaining code: https://github.com/atilasantos/iac-terraform-nginx.git
You are trying to use the aws_iam_policy_document.nginx-doc-policy policy as an assume_role_policy which does not work as an assume role policy needs to define a principal that you trust and want to grant access to assume the role you are creating.
An assume role policy could look like this is you want to grant access to the role to EC2 instances via instance profiles. At the end you can attach your initial role via a new resource as an inline policy to the role:
data "aws_iam_policy_document" "instance-assume-role-policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}
resource "aws_iam_role" "nginx-iam_role" {
name = "nginx-iam_role"
path = "/"
assume_role_policy = data.aws_iam_policy_document.instance-assume-role-policy.json
}
resource "aws_iam_role_policy" "role_policy" {
name = "role policy"
role = aws_iam_role.nginx-iam_role.id
policy = data.aws_iam_policy_document.nginx-doc-policy.json
}
Instead of attaching the policy as an inline policies you can also create an IAM Policy and attach it to the various iam resources. (e.g.: aws_iam_policy and aws_iam_role_policy_attachment for roles.)
We created a bunch of open-source IAM modules (and others) to make IAM handling easier: Find them here on github. But there are more modules out there that you can try.

Terraform policy attachment to a role name in module

I'm trying to create data roles in three environments in AWS using Terraform.
One is an role in root account. This role can is used to login to AWS and can assume data roles in production and staging. This works fine. This is using a separate module.
I have problems when trying to create the roles in prod and staging from a module.
My module looks like this main.tf:
resource "aws_iam_role" "this" {
name = "${var.name}"
description = "${format("%s (managed by Terraform)", var.policy_description)}"
assume_role_policy = "${length(var.custom_principals) == 0 ? data.aws_iam_policy_document.assume_role.json : data.aws_iam_policy_document.assume_role_custom_principals.json}"
}
resource "aws_iam_policy" "this" {
name = "${var.name}"
description = "${format("%s (managed by Terraform)", var.policy_description)}"
policy = "${var.policy}"
}
data "aws_iam_policy_document" "assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::${var.account_id}:root"]
}
}
}
data "aws_iam_policy_document" "assume_role_custom_principals" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = [
"${var.custom_principals}",
]
}
}
}
resource "aws_iam_role_policy_attachment" "this" {
role = "${aws_iam_role.this.name}"
policy_arn = "${aws_iam_policy.this.arn}"
}
I also have the following in output.tf:
output "role_name" {
value = "${aws_iam_role.this.name}"
}
Next I try to use the module to create two roles in prod and staging.
main.tf:
module "data_role" {
source = "../tf_data_role"
account_id = "${var.account_id}"
name = "data"
policy_description = "Role for data engineers"
custom_principals = [
"arn:aws:iam::${var.master_account_id}:root",
]
policy = "${data.aws_iam_policy_document.data_access.json}"
}
Then I'm trying to attach a AWS policies like this:
resource "aws_iam_role_policy_attachment" "data_readonly_access" {
role = "${module.data_role.role_name}"
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
resource "aws_iam_role_policy_attachment" "data_redshift_full_access" {
role = "${module.data_role.role_name}"
policy_arn = "arn:aws:iam::aws:policy/AmazonRedshiftFullAccess"
}
The problem I encounter here is that when I try to run this module the above two policies are not attached in staging but in root account. How can I fix this to make it attach the policies in staging?
I'll assume from your question that staging is its own AWS account, separate from your root account. From the Terraform docs
You can define multiple configurations for the same provider in order to support multiple regions, multiple hosts, etc.
This also applies to creating resources in multiple AWS accounts. To create Terraform resources in two AWS accounts, follow these steps.
In your entrypoint main.tf, define aws providers for the accounts you'll be targeting:
# your normal provider targeting your root account
provider "aws" {
version = "1.40"
region = "us-east-1"
}
provider "aws" {
version = "1.40"
region = "us-east-1"
alias = "staging" # define custom alias
# either use an assumed role or allowed_account_ids to target another account
assume_role {
role_arn = "arn:aws:iam:STAGINGACCOUNTNUMBER:role/Staging"
}
}
(Note: the role arn must exist already and your current AWS credentials must have permission to assume it)
To use them in your module, call your module like this
module "data_role" {
source = "../tf_data_role"
providers = {
aws.staging = "aws.staging"
aws = "aws"
}
account_id = "${var.account_id}"
name = "data"
... remainder of module
}
and define the providers within your module like this
provider "aws" {
alias = "staging"
}
provider "aws" {}
Now when you are declaring resources within your module, you can dictate which AWS provider (and hence which account) to create the resources in, e.g
resource "aws_iam_role" "this" {
provider = "aws.staging" # this aws_iam_role will be created in your staging account
name = "${var.name}"
description = "${format("%s (managed by Terraform)", var.policy_description)}"
assume_role_policy = "${length(var.custom_principals) == 0 ? data.aws_iam_policy_document.assume_role.json : data.aws_iam_policy_document.assume_role_custom_principals.json}"
}
resource "aws_iam_policy" "this" {
# no explicit provider is set here so it will use the "default" (un-aliased) aws provider and create this aws_iam_policy in your root account
name = "${var.name}"
description = "${format("%s (managed by Terraform)", var.policy_description)}"
policy = "${var.policy}"
}

How to attach multiple IAM policies to IAM roles using Terraform?

I want to attach multiple IAM Policy ARNs to a single IAM Role.
One method is to create a new policy with privileges of all the policies (multiple policies).
But in AWS, we have some predefined IAM policies like AmazonEC2FullAccess, AmazomS3FullAccess, etc. I want to use a combination of these for my role.
I could not find a way to do so in the Terraform documentation.
As per documentation we can use aws_iam_role_policy_attachment to attach a policy to a role, but not multiple policies to a role as this is available via AWS console.
Please let me know if there is a method to do the same or is it still a feature to be added.
The Terraform version I use is v0.9.5
For Terraform versions >= 0.12 the cleanest way to add multiple policies is probably something like this:
resource "aws_iam_role_policy_attachment" "role-policy-attachment" {
for_each = toset([
"arn:aws:iam::aws:policy/AmazonEC2FullAccess",
"arn:aws:iam::aws:policy/AmazonS3FullAccess"
])
role = var.iam_role_name
policy_arn = each.value
}
As described in Pranshu Verma's answer, the list of policies can also be put into a variable.
Using for_each in favor of count has the advantage, that insertions to the list are properly recognized by terraform so that it would really only add one policy, while with count all policies after the insertion would be changed (this is described in detail in this blog post)
Thanks Krishna Kumar R for the hint.
A little more polished answer I reached from your answer.
# Define policy ARNs as list
variable "iam_policy_arn" {
description = "IAM Policy to be attached to role"
type = "list"
}
# Then parse through the list using count
resource "aws_iam_role_policy_attachment" "role-policy-attachment" {
role = "${var.iam_role_name}"
count = "${length(var.iam_policy_arn)}"
policy_arn = "${var.iam_policy_arn[count.index]}"
}
And finally the list of policies should be specified in *.tfvars file or in command line using -var, for example:
iam_policy_arn = [
"arn:aws:iam::aws:policy/AmazonEC2FullAccess", "arn:aws:iam::aws:policy/AmazonS3FullAccess"]
Did you try something like this:
resource "aws_iam_role" "iam_role_name" {
name = "iam_role_name"
}
resource "aws_iam_role_policy_attachment" "mgd_pol_1" {
name = "mgd_pol_attach_name"
role = "${aws_iam_role.iam_role_name.name}"
policy_arn = "${aws_iam_policy.mgd_pol_1.arn}"
}
resource "aws_iam_role_policy_attachment" "mgd_pol_2" {
name = "mgd_pol_attach_name"
role = "${aws_iam_role.iam_role_name.name}"
policy_arn = "${aws_iam_policy.mgd_pol_2.arn}"
}
Adding another option, which is similar to the excepted answer but instead of:
policy_arn = "${var.iam_policy_arn[count.index]}"
You can use the element function:
policy_arn = "${element(var.iam_policy_arn,count.index)}"
I think that in some cases (like a project with a large amount of code) this could be more readable.
In my case I added multiple statements in one policy document:
data "aws_iam_policy_document" "sns-and-sqs-policy" {
statement {
sid = "AllowToPublishToSns"
effect = "Allow"
actions = [
"sns:Publish",
]
resources = [
data.resource.arn,
]
}
statement {
sid = "AllowToSubscribeFromSqs"
effect = "Allow"
actions = [
"sqs:changeMessageVisibility*",
"sqs:SendMessage",
"sqs:ReceiveMessage",
"sqs:GetQueue*",
"sqs:DeleteMessage",
]
resources = [
data.resource.arn,
]
}
}
resource "aws_iam_policy" "sns-and-sqs" {
name = "sns-and-sqs-policy"
policy = data.aws_iam_policy_document.sns-and-sqs-policy.json
}
resource "aws_iam_role_policy_attachment" "sns-and-sqs-role" {
role = "role_name"
policy_arn = aws_iam_policy.sns-and-sqs.arn
}
simply combine your policies in one policy
1.Use a datasource with for loop to get all the policies
data "aws_iam_policy" "management_group_policy" {
for_each = toset(["Billing", "AmazonS3ReadOnlyAccess"])
name = each.value
}
2.Attach to role as so;
resource "aws_iam_role_policy_attachment" "dev_role_policy_attachment" {
for_each = data.aws_iam_policy.management_group_policy
role = aws_iam_role.role.name
policy_arn = each.value.arn
}
This is an example how i did it:
resource "aws_iam_group_policy_attachment" "policy_attach_example" {
for_each = aws_iam_policy.example
group = aws_iam_group.example.name
policy_arn = each.value["arn"]
}
So basically "aws_iam_policy.example" is a list of policies that i have made in the same way, with for_each
Hope that this help you, i know i come late but i had this simillar issue