Malformed Policy Document: Has prohibited field Resource - amazon-web-services

I'm trying to create an IAM Role and IAM Policy using Terraform.
I'm getting this error:
│ Error: error creating IAM Role (asg-domain-join-policy): MalformedPolicyDocument: Has prohibited field Resource
status code: 400, request id: 53fa1ae0-f22f-4f2e-8aa6-1947421eae9b
with aws_iam_role.ad_join_role,
on iam.tf line 30, in resource "aws_iam_role" "ad_join_role":
30: resource "aws_iam_role" "ad_join_role" {
My current code for the IAM Role is the following:
resource "aws_iam_role" "ad_join_role" {
name = "asg-domain-join-policy"
assume_role_policy = data.aws_iam_policy_document.asg_domain_join_policy.json
permissions_boundary = "arn:aws:iam::${var.account_id}:policy/****"
}
The code for IAM Policy is the following:
data "aws_iam_policy_document" "asg_domain_join_policy" {
statement {
actions = [
"ssm:DescribeAssociation",
"ssm:GetDocument",
"ssm:ListAssociations",
"ssm:UpdateAssociationStatus",
"ssm:UpdateInstanceInformation",
"ssm:CreateAssociation",
]
effect = "Allow"
resources = ["ec2"]
}
}
I'm unsure why I'm getting that error.

The assume_role_policy can have a document which specifies only the AssumeRole action. What you have to do is split your policy to create separate ones for being able to assume a role, and being able to attach other permissions to the role.
For example:
# Allow EC2 instances to assume the role
data "aws_iam_policy_document" "asg_assume_role_policy" {
statement {
actions = [
"sts:AssumeRole"
]
effect = "Allow"
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}
# Create the policy which allows other actions for the EC2 instance
data "aws_iam_policy_document" "asg_domain_join_policy" {
statement {
actions = [
"ssm:DescribeAssociation",
"ssm:GetDocument",
"ssm:ListAssociations",
"ssm:UpdateAssociationStatus",
"ssm:UpdateInstanceInformation",
"ssm:CreateAssociation"
]
effect = "Allow"
resources = ["*"]
}
}
resource "aws_iam_role" "ad_join_role" {
name = "asg-domain-join-policy"
assume_role_policy = data.aws_iam_policy_document.asg_assume_role_policy.json
# Attach the policy
inline_policy {
policy = data.aws_iam_policy_document.asg_domain_join_policy.json
}
}
Some things to note in this example:
The second policy is attached as an inline policy. This is fine, if the policy is shorter, otherwise you may want to use aws_iam_policy_attachment
The resource type for the actions in the second policy is a wildcard ["*"]. If you want more granularity for your actions on the policy, you may want to check out this page to see which action allows what kind of resource type. Obviously, ["ec2"] is not a valid resource type.

Your resource block is using an incorrect reference. ec2 is not a resource. If you are referencing an instance you need to use aws_instance.my_ec2_instance, or if you want to allow all resources you can put "*".

Related

Policy document should not specify a principal - terraform aws_iam_policy_document

I have a an IAM policy which I have created and it seems to keep complaining that the policy document should not specify a principal. Am really unsure of what is wrong with my policy. This policy will be attached to my S3 Bucket which specifies only a certain group is allowed to do the following actions GetObject and ListBucket.
Error : MalformedPolicyDocument: Policy document should not specify a principal
My IAM Policy is as follows :
data "aws_iam_policy_document" "s3_admin_access" {
statement {
sid = "AllowGroupAAccess"
effect = "Allow"
actions = [
"s3:GetObject",
"s3:ListBucket"
]
resources = local.s3_etl_bucket_array
principals {
type = "AWS"
identifiers = [aws_iam_group.iam_group_team["admin-team"].arn]
}
}
statement {
sid = "DenyAllOtherUsers"
effect = "Deny"
actions = [
"s3:*"
]
resources = local.s3_etl_bucket_array
principals {
type = "AWS"
identifiers = ["*"]
}
condition {
test = "StringNotEquals"
variable = "aws:PrincipalArn"
values = [aws_iam_group.iam_group_team["admin-team"].arn]
}
}
}
resource "aws_iam_policy" "s3_admin_access" {
name = "${local.csi}-s3_admin_access"
path = "/"
policy = data.aws_iam_policy_document.s3_admin_access.json
}
The short answer is that groups cannot be used as a principal in a resource policy and the bucket policy is a type of resource policy [1]:
You cannot identify a user group as a principal in a policy (such as a resource-based policy) because groups relate to permissions, not authentication, and principals are authenticated IAM entities.
[1] https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html#Principal_specifying

Setting IAM Policy within Terraform

I am having troubles setting an IAM Policy following documentation on Terraform.
While trying to assign a this policy to my S3 Bucket using this documentation from databricks
the following error is being returned
Policy document should not specify a principal.
You may reproduce using the following code section
resource "aws_iam_policy" "databricks_bucket_policy" {
name = "databrick_bucket_policy"
path = "/"
description = "A policy for Databricks S3 Bucket"
# Terraform's "jsonencode" function converts a
# Terraform expression result to valid JSON syntax.
policy = jsonencode({
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Principal" : {
"AWS" : [
"arn:aws:iam::414351767826:role/unity-catalog-prod-UCMasterRole-14S5ZJVKOTYTL",
"arn:aws:iam::<YOUR_AWS_ACCOUNT_ID>:role/<THIS_ROLE_NAME>"
]
},
"Action" : "sts:AssumeRole",
"Condition" : {
"StringEquals" : {
"sts:ExternalId" : "<DATABRICKS_ACCOUNT_ID>"
}
}
}
]
})
}
I've tried following this terraform doc but it is not fully understood. I would appreciate if someone could clarify on how this can be done.
You are trying to create an aws_iam_policy resource and assign it to an S3 bucket, but you can only assign an aws_s3_bucket_policy to a bucket. An IAM policy is assigned to users or roles, so you never specify a principal in an IAM policy because it is directly assigned to the principal it is applied to.
By contrast a resource policy, such as the S3 bucket policy, is assigned to a resource, and you specify the principals that you are granting or denying access to the resource.
Now, looking at the Databricks documentation, it appears what they are giving you in step 3 of the documentation is a trust relationship. Step 4 has the IAM policy. They are also instructing you to create an IAM Role, not an S3 bucket policy.
It appears that what you are being instructed to do is create an IAM role that Databricks can assume, that gives Databricks access to the S3 bucket in your account. You are not being instructed to create an S3 bucket policy at all.
Your Terraform should look like this:
resource "aws_iam_role" "databricks_role" {
name = "databricks_role"
assume_role_policy = jsonencode(
# The JSON from Step 3 goes here
)
}
resource "aws_iam_policy" "databricks_role_policy" {
name = "databrick_role_policy"
path = "/"
description = "A policy for Databricks IAM Role"
policy = jsonencode(
# The JSON from Step 4 goes here
)
}
resource "aws_iam_role_policy_attachment" "databricks_role" {
role = aws_iam_role.databricks_role.name
policy_arn = aws_iam_policy.databricks_role_policy.arn
}

How to attach iam policy to Lambda function for SNS publish access?

I am trying to allow my Lambda function permissions so that it can publish an SNS message.
I am creating my Lambda in Terraform:
resource "aws_lambda_function" "DynamoDB_SNS_Lambda" {
s3_bucket = var.bucket_id
s3_key = "s3key.zip"
handler = "index.handler"
runtime = "nodejs16.x"
role = aws_iam_role.lambda_iam_role.arn
function_name = "lambda_name"
}
I am then creating a new IAM role:
resource "aws_iam_role" "lambda_iam_role" {
name = "sns_lambda_iam_role"
assume_role_policy = data.aws_iam_policy_document.sns_lambda_policy.json
}
data "aws_iam_policy_document" "sns_lambda_policy" {
statement {
sid = ""
effect = "Allow"
actions = [
"sns:Publish",
]
resources = [
var.order_update_sns_topic_arn
]
}
}
I am then trying to attach a policy to that IAM role to allow SNS permissions.
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
role = aws_iam_role.lambda_iam_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonSNSRole"
}
When I run terraform apply, I get the following error:
module.Lambda.aws_iam_role.lambda_iam_role: Modifying... [id=sns_lambda_iam_role]
╷
│ Error: error updating IAM Role (sns_lambda_iam_role) assume role policy: MalformedPolicyDocument: Has prohibited field Resource
│ status code: 400, request id: 98a57a4c-1839-4a04-b903-e7deb409c71e
│
│ with module.Lambda.aws_iam_role.lambda_iam_role,
│ on modules/lambda/main.tf line 17, in resource "aws_iam_role" "lambda_iam_role":
│ 17: resource "aws_iam_role" "lambda_iam_role" {
Am I creating my aws_iam_policy_document correctly?
Am I correct in thinking I need to attach the policy to the role with aws_iam_role_policy_attachment?
This is incorrect assume policy. Assume policy tells only who/what can assume the role, not what permissions the role has. In your case, only lambda can assume the role. And the actual permissions can be set using inline_policy. For example:
resource "aws_iam_role" "lambda_iam_role" {
name = "sns_lambda_iam_role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "lambda.amazonaws.com"
}
},
]
})
inline_policy {
name = "policy-8675309"
policy = data.aws_iam_policy_document.sns_lambda_policy.json
}
}

Define Lambda policy and assume role policy

I'm writing a Lambda function that should be triggered (after X amount of time) using a CloudWatch event. I have almost the configuration working, but I can't get the policy business from AWS right. This is what I have so far:
data "aws_iam_policy_document" "this" {
statement {
actions = ["sts:AssumeRole"]
effect = "Allow"
principals {
identifiers = ["lambda.amazonaws.com"]
type = "Service"
}
}
statement {
actions = [
"logs:CreateLogStream",
"logs:PutLogEvents",
]
effect = "Allow"
resources = ["arn:aws:logs:*:*:*"]
}
}
resource "aws_iam_role_policy_attachment" "this" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
role = aws_iam_role.this.name
}
resource "aws_iam_role" "this" {
assume_role_policy = data.aws_iam_policy_document.this.json
name = "AWSLambdaSpringCloudFunctionBasicExecutionRole"
}
I just want the Lambda to be able to write to CloudWatch and be triggered by the CloudWatch Event source...that's all, no more permissions. I'm creating the Log Group via Terraform, so no need for logs:CreateLogGroup.
When I do terraform apply (version 0.12.24) this error pops out:
aws_iam_role.this: Creating...
Error: Error creating IAM Role AWSLambdaSpringCloudFunctionBasicExecutionRole: MalformedPolicyDocument: Has prohibited field Resource
status code: 400, request id: 12ad676e-b98e-4c60-bedd-bf17487bd51d
Is there a way to declare the AWS Lambda policy and assume role policy together? If not, what are the recommended Terraform resources/data sources for doing this?
UPDATE: Complete Solution
As Oleksii pointed out, I was using the wrong resource to create the IAM policy:
data "aws_iam_policy_document" "assume_role" {
statement {
actions = ["sts:AssumeRole"]
effect = "Allow"
principals {
identifiers = ["lambda.amazonaws.com"]
type = "Service"
}
}
}
resource "aws_iam_role_policy_attachment" "assume_role" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
role = aws_iam_role.lambda.name
}
resource "aws_iam_role" "lambda" {
assume_role_policy = data.aws_iam_policy_document.assume_role.json
name = "Use Any Identifier/Name You Want Here For IAM Role"
}
data "aws_iam_policy_document" "logs" {
statement {
actions = [
"logs:CreateLogStream",
"logs:PutLogEvents",
]
effect = "Allow"
resources = ["arn:aws:logs:*:*:*"]
}
}
resource "aws_iam_policy_attachment" "logs" {
name = "Use Any Identifier/Name You Want Here For IAM Policy Logs"
policy_arn = aws_iam_policy.logs.arn
roles = [aws_iam_role.lambda.name]
}
resource "aws_iam_policy" "logs" {
name = "Use Any Identifier/Name You Want Here For IAM Policy Logs"
policy = data.aws_iam_policy_document.logs.json
}
You're trying to define multiple statements in assume_role_policy, which is not a regular policy, but a "trust relation" type of policy. It only describes who can assume role and under which conditions.
From my understanding (limited Terraform knowledge) you need to move second statement to its own policy and then attach it via aws_iam_role_policy_attachment

Terraform Failure configuring LB attributes

I've followed the first answer on this post on StackOverflow but I obtain this error:
Failure configuring LB attributes: InvalidConfigurationRequest: Access Denied for bucket: myproject-log. Please check S3bucket permission status code: 400
This is my code:
s3_bucket
data "aws_elb_service_account" "main" {}
resource "aws_s3_bucket" "bucket_log" {
bucket = "${var.project}-log"
acl = "log-delivery-write"
policy = <<POLICY
{
"Id": "Policy",
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:PutObject"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::${var.project}-log/AWSLogs/*",
"Principal": {
"AWS": [
"${data.aws_elb_service_account.main.arn}"
]
}
}
]
}
POLICY
}
load balancer
resource "aws_lb" "vm_stage" {
name = "${var.project}-lb-stg"
internal = false
load_balancer_type = "application"
subnets = [aws_subnet.subnet_1.id, aws_subnet.subnet_2.id, aws_subnet.subnet_3.id]
security_groups = [aws_security_group.elb_project_stg.id]
access_logs {
bucket = aws_s3_bucket.bucket_log.id
prefix = "lb-stg"
enabled = true
}
tags = {
Name = "${var.project}-lb-stg"
}
}
Just going to drop this here since this cross applied to another question that was asked.
This took me awhile to figure out, but the S3 bucket has two requirements per the documentation:
The bucket must be located in the same Region as the load balancer.
Amazon S3-Managed Encryption Keys (SSE-S3) is required. No other encryption options are supported.
Source: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-access-logs.html
While it makes it seem like it's a permissions issue with the error message given it may actually be an issue with the bucket having the wrong encryption type. In my case the issue was that my bucket was unencrypted.
Updated the bucket to SSE-S3 encryption and I no longer received the error:
resource "aws_s3_bucket" "s3_access_logs_bucket" {
bucket = var.access_logs_bucket_name
acl = "private"
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
versioning {
enabled = true
}
}
And just because, here's the policy I used:
data "aws_elb_service_account" "main" {}
data "aws_iam_policy_document" "s3_lb_write" {
statement {
principals {
identifiers = ["${data.aws_elb_service_account.main.arn}"]
type = "AWS"
}
actions = ["s3:PutObject"]
resources = [
"${aws_s3_bucket.s3_access_logs_bucket.arn}/*"
]
}
}
resource "aws_s3_bucket_policy" "load_balancer_access_logs_bucket_policy" {
bucket = aws_s3_bucket.s3_access_logs_bucket.id
policy = data.aws_iam_policy_document.s3_lb_write.json
}
Official AWS Docs
https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-access-logs.html
Solution
Reference the docs above and change your bucket's iam policy to reflect what the documentation states. The logging is actually done by AWS and not your roles or IAM users. So you need to give ÅWS permission to do this. That's why the docs show statements in the policy that specify the delivery.logs.amazonaws.com principal. That principal is the AWS logging service. Even though your bucket is hosted on AWS, they don't give themselves access to your bucket by default. You have to explicitly grant access to AWS if you want their services to work.
As per this post, I was able to resolve this issue by disabling KMS and using SSE-S3 for bucket encryption. Also, there are additional permissions listed in the AWS docs.
I struggled with this as well the entire terraform bucket policy that worked for me is below.
data "aws_iam_policy_document" "elb_bucket_policy" {
statement {
effect = "Allow"
resources = [
"arn:aws:s3:::unique-bucket-name/${local.prefix}/AWSLogs/${local.application_account_id}/*",
]
actions = ["s3:PutObject"]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::${local.elb_account_id}:root"]
}
}
statement {
effect = "Allow"
resources = [
"arn:aws:s3:::unique-bucket-name/${local.prefix}/AWSLogs/${local.application_account_id}/*",
]
actions = ["s3:PutObject"]
principals {
type = "Service"
identifiers = ["logdelivery.elb.amazonaws.com"]
}
}
statement {
effect = "Allow"
resources = [
"arn:aws:s3:::unique-bucket-name/${local.prefix}/AWSLogs/${local.application_account_id}/*",
]
actions = ["s3:PutObject"]
principals {
type = "Service"
identifiers = ["logdelivery.elb.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "s3:x-amz-acl"
values = ["bucket-owner-full-control"]
}
}
}