Terraform ARN ID of Subnet in IAM - amazon-web-services

I'm trying to create a custom IAM role with Terraform:
resource "aws_iam_role" "prod_role" {
name = "test_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:GetConsole*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "ec2:RunInstances",
"Resource": [
"arn:aws:ec2:${var.aws_region}:*:subnet/${aws_subnet.prod1.id}",
"arn:aws:ec2:${var.aws_region}:*:subnet/${aws_subnet.prod2.id}",
"arn:aws:ec2:${var.aws_region}:*:network-interface/*",
"arn:aws:ec2:${var.aws_region}:*:instance/*",
"arn:aws:ec2:${var.aws_region}:*:volume/*",
"arn:aws:ec2:${var.aws_region}::image/ami-*",
"arn:aws:ec2:${var.aws_region}:*:key-pair/*",
"arn:aws:ec2:${var.aws_region}:*:security-group/*"
]
}
]
}
EOF
}
My issue is that subnet ID's pulled into the policy are wrong. My ARN looke like this:
arn:aws:ec2:us-east-1:*:subnet/subnet-0bbg694edda209ab9
and it should look like this:
arn:aws:ec2:us-east-1:*:subnet/0bbg694edda209ab9
How do I get just the ID of the subnet (without the subnet-) for use with IAM?

This may not be the optimal solution however you could utilise the replace interpolation syntax for Terraform. Replacing "subnet-" with "". The result would look like so
arn:aws:ec2:${var.aws_region}:*:subnet/${replace(aws_subnet.prod1.id, "subnet-", "")}

Related

How do I enable a user to change their password on AWS using Terraform?

I have a group I defined in Terraform:
resource "aws_iam_group" "developers" {
name = "developers"
}
and I have some permissions added to it:
resource "aws_iam_group_policy_attachment" "cloudwatch_readonly_access" {
group = aws_iam_group.developers.name
policy_arn = arn:aws:iam::aws:policy/CloudWatchReadOnlyAccess
}
and I'd like to add the option for the user to change their password. The docs say that I need to do this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iam:GetAccountPasswordPolicy",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iam:ChangePassword",
"Resource": "arn:aws:iam::account-id-without-hyphens:user/${aws:username}"
}
]
}
but I'm not sure what would be the best course of action to do this. The docs say that I can just paste the JSON into the policy object:
resource "aws_iam_group_policy_attachment" "password_change" {
group = aws_iam_group.developers.name
policy = "{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iam:GetAccountPasswordPolicy",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iam:ChangePassword",
"Resource": "arn:aws:iam::account-id-without-hyphens:user/${aws:username}"
}
]
}"
}
but then I can't substitute the account-id from a variable (account-id-without-hyphens). How can I do this properly?
Typically, when using a multiline string in terraform, you would use the heredoc syntax:
resource "aws_iam_group_policy_attachment" "password_change" {
group = aws_iam_group.developers.name
policy = <<-EOT
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iam:GetAccountPasswordPolicy",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iam:ChangePassword",
"Resource": "arn:aws:iam::account-id-without-hyphens:user/$${aws:username}"
}
]
}
EOT
}
That syntax fully supports terraform interpolation, so you'll need to escape your IAM interpolation with a double $$. You can pull in your account name with a statement like:
data "aws_caller_identity" "current" {}
Which will let you get your account ID from ${data.aws_caller_identity.current.account_id}
Heredoc literals can get hairy if you have a lot of them with heavy use of interpolation. If you've got a lot of IAM policies to write, use the iam_policy_document data resource. That style is more visible to tools like tfsec, and lets you use features like dynamic blocks.

list/tuple of string in terraform not working in the IAM policy resource

I have a list of objects in an S3 bucket defined in locals:
locals {
s3_bucket_object_list = [
"Completed/",
"Error/",
"Processing/",
"Temp/"
]
s3_bucket_object_list_arn = [ for object in local.s3_bucket_object_list: "arn:aws:s3:::${var.bucket_name}/${object}*" ]
}
Here I needed the "arn:aws:s3:::" in the list to be used later in the IAM policy resource specification, so I used the for expression. And then I just used the local s3_bucket_object_list_arn in the following policy:
resource "aws_iam_policy" "s3" {
name = "s3-policy"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Effect": "Allow",
"Resource": "${local.s3_bucket_object_list_arn}"
}
]
}
EOF
}
I got the following error:
local.s3_bucket_object_list_arn is a tuple with # elements. Cannot
include the value in a string template: String required.
As far as I know, if it's a single resource, the resource should be enclosed with double quotes so that it's a string, but for multiple resources, lists can be used here, with each element being string. So I wonder why it's not working here in my case.
Your local.s3_bucket_object_list_arn is a list. To use it in an IAM policy, you need to convert it to valid json list. For this, jsonencode can be used:
resource "aws_iam_policy" "s3" {
name = "s3-policy"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Effect": "Allow",
"Resource": ${jsonencode(local.s3_bucket_object_list_arn)}
}
]
}
EOF
}

IAM policy for EBS volume with EC2 instance

I am trying to create an IAM role/policy to enable my EC2 instance to be able to list and attach EBS volumes (via scripts that call the aws cli). I want this policy to allow only listing/attaching of EBS volumes that have a specific tag.
I've noticed that the script is able to list/attach volumes when I set Resources: "*" and no Conditions in my policy below.
But as soon as I introduce the policy I have below, the AWS cli throws the following error:
./aws ec2 describe-volumes
An error occurred (UnauthorizedOperation) when calling the DescribeVolumes operation: You are not authorized to perform this operation.
Here is the IAM policy I have so far defined in terraform:
resource "aws_iam_role" "web_role" {
name = "web_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_instance_profile" "web_profile" {
name = "web_profile"
role = aws_iam_role.web_role.name
}
resource "aws_iam_role_policy" "web_disk_policy" {
name = "web_disk_policy"
role = aws_iam_role.web_role.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:AttachVolume",
"ec2:DetachVolume",
"ec2:DescribeVolumes"
],
"Resource": [
"arn:aws:ec2:*:*:instance/*",
"arn:aws:ec2:*:*:volume/*"
],
"Condition": {
"StringEquals": {
"ec2:ResourceTag/app": "web"
}
}
}
]
}
EOF
}
And my EC2 instances is created with the following:
resource "aws_instance" "web_vm" {
...
iam_instance_profile = aws_iam_instance_profile.web_profile.name
...
tags = {
app = "web"
}
}
And disk created with:
resource "aws_ebs_volume" "ebs-volume-1" {
availability_zone = "us-west-2a"
size = 10
tags = {
app = "web"
}
}
DescribeVolumes does not support aws:ResourceTag/${TagKey} condition, nor any other.
Most Describe/List type operations that target many resources aren't compatible with the conditional logic. DescribeVolumes does not work with conditions so split that off into a different statement.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:AttachVolume",
"ec2:DetachVolume"
],
"Resource": [
"arn:aws:ec2:*:*:instance/*",
"arn:aws:ec2:*:*:volume/*"
],
"Condition": {
"StringEquals": {
"ec2:ResourceTag/app": "web"
}
}
},
{
"Effect": "Allow",
"Action": "ec2:DescribeVolumes",
"Resource": "*"
}
]
}

How to create roles in terraform

I would like to create a aws_iam_role with terraform but after running terraform applyI get the following error message:
aws_iam_role.role: Error Updating IAM Role (edb_eb_role) Assume Role Policy: MalformedPolicyDocument: Has prohibited field Resource
That is my policy:
resource "aws_iam_role" "role" {
name = "edb_eb_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
},
{
"Action": [
"logs:*"
],
"Effect": "Allow",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": [
"*"
]
}
]
}
EOF
}
What did I wrong? I also tried to do it only with Principals but then I get the message that "Principals" is also not prohibited?
Assume_role_policy don't accept the aws policy json files. So the above code is not working. For detailed explanation of assume_role_policy in aws_iam_role, see this thread.
Update the code as shown below and execute.
variable policy_arn{
default = "arn:aws:iam::aws:policy/service-role/AWSLambdaRole"
}
resource "aws_iam_role" "edb_role" {
name = "edb_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": ["ec2.amazonaws.com" ]
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "test-attach" {
role = "${aws_iam_role.edb_role.name}"
policy_arn = "${var.policy_arn}"
}
output "role" {
value = "${aws_iam_role.edb_role.name}"
}
Here, we are using the AWSLambdaRole Policy present in Policies section of IAM.
Add multiple policies to a role using aws_iam_role_policy_attach
Use the default policies provided by aws as show above. Else to create a new policy, see the docs here

Terraform : AWS Redshift IAM role

Im trying to setup a redshift cluster and an IAM role that will have access to the cluster. I am using terraform for this. According the documentation I need to create a Service role and attach the AmazonS3ReadOnlyAccess policy to it. I have the following config in my terraform script:
resource "aws_iam_role" "my_admin_role" {
name = "my-role"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:Get*",
"s3:List*",
"redshift:*"
],
"Resource": "*"
}
]
}
EOF
}
However this gives me an error:
Errors:
* aws_iam_role.my_admin_role: "assume_role_policy": required field is not set
* aws_iam_role.my_admin_role: : invalid or unknown key: policy
How do I setup a service role for redshift ?
You mix the resources aws_iam_role and aws_iam_role_policy
Sample usage of resource aws_iam_role
resource "aws_iam_role" "test_role" {
name = "test_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
Sample usage of resource aws_iam_role_policy
resource "aws_iam_role_policy" "test_policy" {
name = "test_policy"
role = "${aws_iam_role.test_role.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ec2:Describe*"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
EOF
}