I do have the default policy for the SQS like below. Referred the documentation - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue_policy
And if there is sns-subscription required, I would like to append the policy on top of the default policy.
Default policy is like below
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sqs:SendMessage*"
],
"Resource": [
"${aws_sqs_queue.queue.arn}"
]
}
]
}
Additional policy like below
{
"Sid": "topic-subscription-arn-test",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "SQS:SendMessage",
"Resource": ["${aws_sqs_queue.queue.arn}"],
"Condition": {
"ArnLike": {
"aws:SourceArn": "arn-test"
}
}
}
I just tried, but the policy getting override. Any thoughts how policy can be appended using Terraform? Thanks in advance.
Looking for somekind of thought for this approach? It may be more than 2 policies, so I am trying to append these policies.
I would strongly suggest using the aws_iam_policy_document data source [1] for building policies in Terraform instead of JSON. Since the SQS queue has an argument policy [2], the resource aws_sqs_queue_policy does not have to be used, but it can also be combined with the data source mentioned above. So there are two options:
Create a policy using data source and attach it by using the policy argument
Create a policy using data source and attach it to the aws_sqs_queue_policy
If you decide for the first option, here is how the code should look like:
data "aws_iam_policy_document" "sqs_policy" {
statement {
sid = "FirstSQSPolicy"
effect = "Allow"
actions = [
"sqs:SendMessage*"
]
resources = [
aws_sqs_queue.queue.arn
]
}
statement {
sid = "topic-subscription-arn-test"
effect = "Allow"
actions = [
"sqs:SendMessage"
]
resources = [
aws_sqs_queue.queue.arn
]
condition {
test = "ArnLike"
variable = "aws:SourceArn"
values = [
"arn-test"
]
}
}
}
resource "aws_sqs_queue" "terraform_queue" {
...
policy = data.aws_iam_policy_document.sqs_policy.json
}
For the second option, you can use the same data source and attach the JSON to the aws_sqs_queue_policy resource:
data "aws_iam_policy_document" "sqs_policy" {
statement {
sid = "FirstSQSPolicy"
effect = "Allow"
actions = [
"sqs:SendMessage*"
]
resources = [
aws_sqs_queue.queue.arn
]
}
statement {
sid = "topic-subscription-arn-test"
effect = "Allow"
actions = [
"sqs:SendMessage"
]
resources = [
aws_sqs_queue.queue.arn
]
condition {
test = "ArnLike"
variable = "aws:SourceArn"
values = [
"arn-test"
]
}
}
}
resource "aws_sqs_queue_policy" "sqs_queue_policy" {
queue_url = aws_sqs_queue.queue.id
policy = data.aws_iam_policy_document.sqs_policy.json
}
Using the data source for IAM policies you can add statements as you need them.
[1] https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document
[2] https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue#policy
Related
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
Im new at Terraform and im trying to create ecsTaskExcecutionRoles for each service i have, i create a module that allows to send a list of secrets, i want to make the inline policy that allows the access optional.
i tried putting inside the inline_policy something like:
count = length(var.secrets_arn_list) > 0 ? 1 : 0
but it's not possible use count in that place
data "aws_iam_policy_document" "ecs_tasks_execution_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs-tasks.amazonaws.com"]
}
}
}
resource "aws_iam_role" "ecs_tasks_execution_role" {
name = "TaskExecutionRole-${var.environment}-${var.project}"
assume_role_policy = "${data.aws_iam_policy_document.ecs_tasks_execution_role.json}"
inline_policy {
name = "SecretsManagerAccess-${var.project}-${var.environment}"
policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"secretsmanager:GetResourcePolicy",
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret",
"secretsmanager:ListSecretVersionIds"
],
"Resource": var.secrets_arn_list
}
]
})
}
tags = var.tags
}
resource "aws_iam_role_policy_attachment" "ecs_tasks_execution_role" {
role = "${aws_iam_role.ecs_tasks_execution_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
Someone knows how to solve it?
Yes, there is a way using dynamic [1] and for_each meta-argument [2]:
dynamic "inline_policy" {
for_each = length(var.secrets_arn_list) > 0 ? [1] : []
content {
name = "SecretsManagerAccess-${var.project}-${var.environment}"
policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"secretsmanager:GetResourcePolicy",
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret",
"secretsmanager:ListSecretVersionIds"
],
"Resource": var.secrets_arn_list
}
]
})
}
}
[1] https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks
[2] https://developer.hashicorp.com/terraform/language/meta-arguments/for_each
Either use a dynamic block, instead of count, or move the policy into a separate Terraform aws_iam_role_policy resource and put the count on that resource.
I am creating policy based on tags. The environment value differs for each resource, so I have to add them as multiple values. Unable to pass as list. Tried join,split and for loop. None of them works. Pls help. Below code just add the value as "beta,test" which will not work as expected
main.tf
locals{
workspaceValues = terraform.workspace == "dev" ? ["alpha", "dev"] : terraform.workspace == "test" ? ["beta", "test"] : ["prod", "staging"]
}
resource "aws_iam_group_policy" "inline_policy" {
name = "${terraform.workspace}_policy"
group = aws_iam_group.backend_admin.name
policy = templatefile("policy.tpl", { env = join(",", local.workspaceValues), region = "${data.aws_region.current.name}", account_id = "${data.aws_caller_identity.current.account_id}" })
}
policy.tpl:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*",
"Condition": {
"ForAllValues:StringLikeIfExists": {
"aws:ResourceTag/Environment": "${env}"
}
}
}
]
}
You can use jsonencode to properly format TF list as a list in json in your template:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*",
"Condition": {
"ForAllValues:StringLikeIfExists": {
"aws:ResourceTag/Environment": ${jsonencode(env)}
}
}
}
]
}
For that you would call the template as follows:
resource "aws_iam_group_policy" "inline_policy" {
name = "${terraform.workspace}_policy"
group = aws_iam_group.backend_admin.name
policy = templatefile("policy.tpl", {
env = local.workspaceValues
})
}
You are not using region nor account_id in your template, so there is no reason to pass them in.
An alternative solution to avoid this all together is to recreate the policy using the data component "aws_iam_policy_document" and then pass it to the aws_iam_policy resource like the following,
data "aws_iam_policy_document" "example" {
statement {
effect = "Allow"
actions = [
"ec2:*"
]
resources = [
"*"
]
condition {
test = "ForAllValues:StringLikeIfExists"
variable = "aws:ResourceTag/Environment"
values = local.workspaceValues
}
}
}
resource "aws_iam_policy" "example" {
name = "example_policy"
path = "/"
policy = data.aws_iam_policy_document.example.json
}
I created an AWS step function using Terraform. For now, the step function has only 1 lambda function for now:
resource "aws_iam_role_policy" "sfn_policy" {
policy = jsonencode(
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "*"
},
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction",
"lambda:InvokeAsync"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [ "states:StartExecution" ],
"Resource": "*"
}
]
}
)
role = aws_iam_role.processing_lambda_role.id
}
resource "aws_sfn_state_machine" "sfn_state_machine_zip_files" {
name = local.zip_files_step_function_name
role_arn = aws_iam_role.processing_lambda_role.arn
definition = <<EOF
{
"Comment": "Process Incoming Zip Files",
"StartAt": "ProcessIncomingZipFiles",
"States": {
"ProcessIncomingZipFiles": {
"Type": "Task",
"Resource": "${aws_lambda_function.process_zip_files_lambda.arn}",
"ResultPath": "$.Output",
"End": true
}
}
}
EOF
}
This is how the role is initially defined:
resource "aws_iam_role" "processing_lambda_role" {
name = local.name
path = "/service-role/"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
Action = "sts:AssumeRole"
}
]
})
}
Why do I get this error message even though the policies include the AssumeRole policy already. I also tried removing one of the sts:AssumeRolepolicies but the error was still there.
"Neither the global service principal states.amazonaws.com, nor the regional one is authorized to assume the provided role."
AWS docs Reference: https://aws.amazon.com/premiumsupport/knowledge-center/step-functions-iam-role-troubleshooting/
The role aws_iam_role.processing_lambda_role can be only assumed by a lambda function. So, your aws_sfn_state_machine.sfn_state_machine_zip_files can't assume this role. You have to change the Principal in the role from:
Principal = { Service = "lambda.amazonaws.com" }
into
Principal = { Service = "states.amazonaws.com" }
You still may have other issues, depending on what you want to do exactly. But your reported error is due to what I mentioned.
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
}