AWS KMS and IAM association using terraform version 0.12 - amazon-web-services

Hi AWS and Terraform experts, I was kinda generating the KMS and IAM association that was built manually by our former colleague, I'm getting an issue to completing the copy of kms policy stated below:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::12345678912345:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "ALSKDJFHGNVBCMXJDH0987"
},
"Action": "kms:Decrypt",
"Resource": "*"
}
]
}
This ALSKDJFHGNVBCMXJDH0987 is an IAM Role which was I believe transformed by AWS console (not yet sure why)
{
"Effect": "Allow",
"Principal": {
"AWS": "ALSKDJFHGNVBCMXJDH0987"
},
"Action": "kms:Decrypt",
"Resource": "*"
}
I'm getting this error using the created terraform script I made:
Error: MalformedPolicyDocumentException: Policy contains a statement with one or more invalid principals.
status code: 400, request id: alsknldkj2-assd-3333-0sdc-askdjaksdjn2
on main.tf line 84, in resource "aws_kms_key" "secrets":
84: resource "aws_kms_key" "secrets" {
Is there something wrong with the sequence? or I'm missing anything?. Attached here is the terraform code I used:
data "template_file" "my-lambda-policy" {
template = "${file("policy/lambda.json")}"
vars = {
SWAG = var.AWS-SWAG
STUDENT-BELONGS = var.STUDENT
STUDENT-TEACHER = var.TEACHER
ROOM = var.CLASSROOM
}
}
resource "aws_iam_policy" "my-lambda-pol" {
name = "my-lambda-policy"
policy = data.template_file.my-lambda-policy.rendered
}
data "template_file" "my-my-lambda-pol2" {
template = "${file("policy/lambda2.json")}"
}
resource "aws_iam_policy" "my-lambda-pol2" {
name = "my-my-lambda-pol2"
policy = data.template_file.my-my-lambda-pol2.rendered
}
data "template_file" "my-lambda-to-my-kms-policy" {
template = "${file("policy/kms-lambda.json")}"
vars = {
SWAG = var.AWS-SWAG
KMS_KEY_ID = aws_kms_key.mysecret.id
}
}
resource "aws_iam_policy" "lambda-to-kms" {
name = "my-lambda-to-my-kms-policy"
policy = data.template_file.my-lambda-to-my-kms-policy.rendered
}
resource "aws_iam_role" "the-lambda-role" {
name = "{STUD_CHAIR}-${STU_SEAG}-${STUDENT-BELONGS}-${STUDENT-TEACHER}"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "my-lambda-policy_attachment" {
policy_arn = aws_iam_policy.my-lambda-pol.arn
role = aws_iam_role.the-lambda-role.name
}
resource "aws_iam_role_policy_attachment" "my-lambda-pol2_attachment" {
policy_arn = aws_iam_policy.my-lambda-pol2.arn
role = aws_iam_role.the-lambda-role.name
}
resource "aws_iam_role_policy_attachment" "kms-attachment" {
depends_on = [aws_kms_key.mysecret]
policy_arn = aws_iam_policy.lambda-to-kms.arn
role = aws_iam_role.the-lambda-role.name
}
data "template_file" "my-kms-policy" {
template = "${file("policy/my-kms-policy.json")}"
vars = {
STUD_CHAIR= "${var.CHAIR}"
STU_SWAG = "${l{var.SWAG}}"
STUDENT-BELONGS = "${var.STUDENT}"
STUDENT-TEACHER = "${var.TEACHER}"
ROOM = "${var.CLASSROOM}"
}
}
resource "aws_kms_key" "mysecret" {
description = "KMS Key for ${var.STUDENT}-${var.TEACHER}-key-${var.CLASSROOM}"
policy = data.template_file.my-kms-policy.rendered
depends_on = [aws_iam_role.the-lambda-role]
}
resource "aws_kms_alias" "mysecret" {
name = "alias/${var.STUDENT}-${var.TEACHER}-key-${var.CLASSROOM}"
depends_on = [aws_iam_role.the-lambda-role]
target_key_id = aws_kms_key.mysecret.key_id
}
this is what inside of my-kms-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${ROOM}:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${ROOM}:role/${STUD_CHAIR}-${STU_SEAG}-${STUDENT-BELONGS}-${STUDENT-TEACHER}"
},
"Action": "kms:Decrypt",
"Resource": "*"
}
]
}

Workaround
A workaround is to run terraform apply twice.
Reason
When recreating an IAM role the policy referencing this role needs to be updated for reasons described here:
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user.html
If your Principal element contains the ARN for a specific IAM role or user, then that ARN is transformed to a unique principal ID when the policy is saved. This helps mitigate the risk of someone escalating their permissions by removing and recreating the role or user. You don't normally see this ID in the console because there is also a reverse transformation back to the ARN when the trust policy is displayed. However, if you delete the role or user, then the principal ID appears in the console because AWS can no longer map it back to an ARN. Therefore, if you delete and recreate a user or role referenced in a trust policy's Principal element, you must edit the role to replace the ARN.
Running Terraform the first time will recreate the IAM role and that way break the policy. Running it the second time will correct the policy, by adding the newly created reference to the IAM role.

Related

Create parameterized resource policy on terraform

I want to create a resource policy for a Secrets Manager secret.
I am following the official example on the docs
resource "aws_secretsmanager_secret_policy" "this" {
count = var.create_resource_policy ? 1 : 0
secret_arn = aws_secretsmanager_secret.mysecret.arn
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnableAnotherAWSAccountToReadTheSecret",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "secretsmanager:GetSecretValue",
"Resource": "*"
}
]
}
POLICY
}
Is there a way to pass the following as variables in the policy document things as the principal(s), the action the resources etc?
I want to be able to pass those things as terraform variables.
Yes, you can use the built-in functions in terraform for that with interpolation syntax. For example, if you had a data source to get the account ID, you could do the following:
data "aws_caller_identity" "current" {}
resource "aws_secretsmanager_secret_policy" "this" {
count = var.create_resource_policy ? 1 : 0
secret_arn = aws_secretsmanager_secret.mysecret.arn
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnableAnotherAWSAccountToReadTheSecret",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${data.aws_caller_identity.current.id}:root"
},
"Action": "secretsmanager:GetSecretValue",
"Resource": "*"
}
]
}
POLICY
}
You could then do the same for any other property you want/need. However, I find it easier to use the built-in data source [1] for creating policies. So for example, the policy you have could be written in the following way:
data "aws_iam_policy_document" "secrets_manager_policy" {
statement {
sid = "EnableAnotherAWSAccountToReadTheSecret"
effect = "Allow"
principals {
identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.id}:root"]
type = "AWS"
}
actions = [
"secretsmanager:GetSecretValue"
]
resources = ["*"]
}
}
You could then tell actions argument to use a variable which would most preferably be a list(string) which would list all the actions necessary:
data "aws_iam_policy_document" "secrets_manager_policy" {
statement {
sid = "EnableAnotherAWSAccountToReadTheSecret"
effect = "Allow"
principals {
identifiers = "arn:aws:iam::${data.aws_caller_identity.current.id}:root"
type = "AWS"
}
actions = var.secrets_actions
resources = [ "*" ]
}
}
Then, you would only have to reference the output of the data source in the original resource:
resource "aws_secretsmanager_secret_policy" "this" {
count = var.create_resource_policy ? 1 : 0
secret_arn = aws_secretsmanager_secret.mysecret.arn
policy = data.aws_iam_policy_document.secrets_manager_policy.json
}
[1] https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document

Difference between aws_iam_policy and aws_iam_role_policy

I have a aws_iam_role which I want to add a policy to. Typically, I would create a policy with aws_iam_role and attach it to the role with aws_iam_role_policy_attachment.
However, I've seen some documentation which uses aws_iam_role_policy which, to my eyes, appears to do the same thing.
Am I correct or is there a subtle difference which I'm missing?
The difference is Managed policies and inline policies
When you create an aws_iam_policy, that is a managed policy and can be re-used.
When you create a aws_iam_role_policy that's an inline policy
For a given role, aws_iam_role_policy resource is incompatible with using the aws_iam_role resource inline_policy argument. When using that argument and this resource, both will attempt to manage the role's inline policies and Terraform will show a permanent difference.
Code to reproduce the above state
resource "aws_iam_role_policy" "test_policy" {
name = "test_policy"
role = aws_iam_role.test_role.id
# Terraform's "jsonencode" function converts a
# Terraform expression result to valid JSON syntax.
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"ec2:Describe*",
]
Effect = "Allow"
Resource = "*"
},
]
})
}
resource "aws_iam_role" "test_role" {
name = "test_role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "ec2.amazonaws.com"
}
},
]
})
}
resource "aws_iam_role" "role" {
name = "test-role1"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_policy" "policy" {
name = "test-policy"
description = "A test policy"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ec2:Describe*"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "test-attach" {
role = aws_iam_role.role.name
policy_arn = aws_iam_policy.policy.arn
}

Attempts at writing the Terraform code for AWS KMS

Goal :
I am trying to create the following things A terraform template to
create KMS keys This template should create the key and two IAM roles.
The roles would be one for admin functions and one that allows
encrypt/decrypt I have written the following code
Am I doing the correct thing to achieve my goal?
provider "aws"
{
access_key = "*****************"
secret_key = "4ZJaLh***********"
region = "us-east-1"
}
resource "aws_kms_key" "test_key" {
description = "KMS Test key"
}
resource "aws_kms_alias" "alias" {
name = "alias/test_key"
target_key_id = "${aws_kms_key.test_key.key_id}"
}
#IAM Role and Policy
resource "aws_iam_policy" "kms_user_policy" {
name = "KMS-User-Policy"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey",
"kms:ReEncryptTo",
"kms:DescribeKey",
"kms:ReEncryptFrom"
],
"Resource": "*"
}
]
}
EOF
}
resource "aws_iam_role" "kms_user_role" {
name = "kms_user_role"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_policy_attachment" "test-attach" {
name = "test-attachment"
roles = ["${aws_iam_role.kms_user_role.name}"]
policy_arn = "${aws_iam_policy.kms_user_policy.arn}"
}

Create ROLE with Terraform in AWS with SAML provider attached

I'm currently trying to automate AWS account provisioning, and one the steps is to create IAM ROLE, with Identity provider(for federated user access).
I searched, and checked Terraform documentation, but cannot find any information about creating such role, or attaching provider to a role.
I can create both just fine, but they are independent.
here is portion of the code:
resource "aws_iam_saml_provider" "default" {
name = "ADFS-TEST"
saml_metadata_document = "${file("../../FederationMetadata.xml")}"
}
resource "aws_iam_role" "role" {
name = "test-Admins"
}
figured out. here is full block
resource "aws_iam_saml_provider" "test" {
name = "ADFS-TEST"
saml_metadata_document = "${file("../../FederationMetadata.xml")}"
}
resource "aws_iam_role" "role" {
name = "ADFStest-Admins"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Federated": "${aws_iam_saml_provider.test.arn}"
},
"Action": "sts:AssumeRoleWithSAML",
"Condition": {
"StringEquals": {
"SAML:aud": "https://signin.aws.amazon.com/saml"
}
}
}
]
}
EOF
}
resource "aws_iam_role_policy" "admins" {
name = "Admin-Policy"
#description = "A test policy"
role = "${aws_iam_role.role.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}
EOF
}
Thank you! It works for me.
I just change aws_iam_role_policy to use the aws_iam_role_policy_attachment:
resource "aws_iam_role_policy_attachment" "attach" {
role = "${aws_iam_role.role.name}"
policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
}

Terraform: Error creating IAM Role. MalformedPolicyDocument: Has prohibited field Resource

My TF code is giving me an error:
/*
* Policy: AmazonEC2ReadOnlyAccess
*/
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:Describe*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "elasticloadbalancing:Describe*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"cloudwatch:ListMetrics",
"cloudwatch:GetMetricStatistics",
"cloudwatch:Describe*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "autoscaling:Describe*",
"Resource": "*"
}
]
}
EOF
I copied and pasted the Policy from https://console.aws.amazon.com/iam/home?region=us-west-2#/policies/arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess$jsonEditor
* aws_iam_role.<role name>: Error creating IAM Role <role name>: MalformedPolicyDocument: Has prohibited field Resource
status code: 400, request id: <request id>
Not sure why it's saying Resource is prohibited.
Need to define assume_role_policy with sts:AssumeRole (Who can assume this role, ex: EC2 service).
Policy can be directly attached using aws_iam_role_policy_attachment instead of duplicating existing policy.
resource "aws_iam_role" "ec2_iam_role" {
name = "ec2_iam_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": [
"ec2.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "ec2-read-only-policy-attachment" {
role = "${aws_iam_role.ec2_iam_role.name}"
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"
}
I had faced similar issue when using role arn. When I tried using aws_iam_role_policy_attachment - I was getting error for role name having unsupported characters.
What worked for me for to create a aws_iam_role_policy as below:
resource "aws_iam_role_policy" "api-invoker" {
provider = <some provider>
role = aws_iam_role.api-invoker.id
policy = data.aws_iam_policy_document.execute-api.json
}
data "aws_iam_policy_document" "execute-api" {
statement {
sid = "all"
actions = [
"execute-api:*",
]
resources = [
"*"
]
}
}
I have faced the same issue while i am creating a policy to assume role from another AWS account. So, I have added another AWS account Id in the trusted entities then the problem is resolved.
#create i am user for account-1
resource "aws_iam_user" "user-1" {
name = "my-user"
tags = {
"Name" = "my-user"
}
}
# create policy for 2nd account
resource "aws_iam_policy" "prod_s3" {
provider = aws.aws02
name = "prod_s3"
description = "allow assuming prod_s3 role"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = "sts:AssumeRole",
Resource = "arn:aws:iam::940883708906:user/my-user"
}]
})
}
# Attach the policy
resource "aws_iam_user_policy_attachment" "prod_s3" {
provider = aws.aws02
user = aws_iam_user.user-1.name
policy_arn = aws_iam_policy.prod_s3.arn
}
# create assume role
resource "aws_iam_role" "prod_list_s3" {
provider = aws.aws02
name = "role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = "sts:AssumeRole",
Principal = { "AWS" : "arn:aws:iam::${data.aws_caller_identity.utils.account_id}:root" }
}]
})
}
# output arn
output "role-arn" {
value = aws_iam_role.prod_list_s3.arn
}
# create caller identity
data "aws_caller_identity" "utils" {
provider = aws.aws02
}
# create s3 full access for 2nd account and attach the file
resource "aws_iam_policy" "s3_all" {
provider = aws.aws02
name = "s3_all"
description = "allows listing all s3 buckets"
policy = file("role_permissions_policy.json")
}
# inside the file
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
]
}
**strong text**
# Attach the assume role
resource "aws_iam_policy_attachment" "s3-all-att" {
name = "list s3 buckets policy to role"
roles = ["${aws_iam_role.prod_list_s3.name}"]
policy_arn = aws_iam_policy.s3_all.arn
provider = aws.aws02