How do you add a managed policy to a group in terraform? - amazon-web-services

I've got this so far:
data "aws_iam_policy" "config_role" {
arn = "arn:aws:iam::aws:policy/service_role/AWSConfigRole"
}
But I'm not sure how to attach this to a group.

You can use either the aws_iam_group_policy_attachment resource or the aws_iam_policy_attachment resource to attach a policy to a group.
As mentioned in the aws_iam_policy_attachment resource docs this resource creates an exclusive attachment of that policy to specified users, groups and roles and isn't normally what you want so I'd recommend the aws_iam_group_policy_attachment resource.
This might look something like this:
resource "aws_iam_group" "aws_config_group" {
name = "AWSConfigGroup"
path = "/"
}
resource "aws_iam_group_policy_attachment" "aws_config_attach" {
group = "${aws_iam_group.aws_config_group.name}"
policy_arn = "arn:aws:iam::aws:policy/service_role/AWSConfigRole"
}
Note that you don't actually need the aws_iam_policy data source here as you are already building the ARN to pass into the data source and that's all that's needed by the aws_iam_group_policy_attachment resource.

You add it later down in the file.
so something like
resource "aws_iam_group" "aws_config_group" {
name = "AWSConfigGroup"
path = "/"
}
resource "aws_iam_policy" "config_role" {
name = "AWSConfigRole"
arn = "arn:aws:iam::aws:policy/service_role/AWSConfigRole"
group = ['aws_config_group']
}

Related

How can I add multiple inline policy on AWS permission set using terraform?

I've created 2 policies and tried to attach as an inline policy on AWS SSO permission sets. However, it only applies either one of policy. How can I apply both policies as inline policy on SSO permission set?
resource "aws_iam_policy" "DenyAccess_nonUSRegions" {
name = "DenyAccess_nonUSRegions"
description = "DenyAccess_nonUSRegions"
policy = data.aws_iam_policy_document.DenyAccess_nonUSRegions.json
}
resource "aws_iam_policy" "role" {
name = "Deny_Specific_IAM_Actions"
description = "Deny_Specific_IAM_Actions"
policy = data.aws_iam_policy_document.Deny_Specific_IAM_Actions.json
}
resource "aws_ssoadmin_permission_set_inline_policy" "role" {
inline_policy = data.aws_iam_policy_document.Deny_Specific_IAM_Actions.json
instance_arn = aws_ssoadmin_permission_set.permission.instance_arn
permission_set_arn = aws_ssoadmin_permission_set.permission.arn
}
resource "aws_ssoadmin_permission_set_inline_policy" "DenyAccess_nonUSRegions" {
inline_policy = data.aws_iam_policy_document.DenyAccess_nonUSRegions.json
instance_arn = aws_ssoadmin_permission_set.permission.instance_arn
permission_set_arn = aws_ssoadmin_permission_set.permission.arn
}
In order to apply both policies as inline policies on an AWS SSO permission set, you can use the aws_ssoadmin_permission_set_inline_policy resource to create two separate inline policies, one for each of your existing policies.
You would need to update your Terraform configuration to create two aws_ssoadmin_permission_set_inline_policy resources, one for each of your existing policies.
For example, you can create the first inline policy using the aws_ssoadmin_permission_set_inline_policy resource, and reference the DenyAccess_nonUSRegions policy that you have created.
resource "aws_ssoadmin_permission_set_inline_policy"
"DenyAccess_nonUSRegions" {
inline_policy =
data.aws_iam_policy_document.DenyAccess_nonUSRegions.json
instance_arn =
aws_ssoadmin_permission_set.permission.instance_arn
permission_set_arn = aws_ssoadmin_permission_set.permission.arn
}
Then, you can create the second inline policy, using the aws_ssoadmin_permission_set_inline_policy resource, and reference the Deny_Specific_IAM_Actions policy that you have created.
resource "aws_ssoadmin_permission_set_inline_policy" "role" {
inline_policy =
data.aws_iam_policy_document.Deny_Specific_IAM_Actions.json
instance_arn =
aws_ssoadmin_permission_set.permission.instance_arn
permission_set_arn = aws_ssoadmin_permission_set.permission.arn
}
It's important to note that you should use different names for each aws_ssoadmin_permission_set_inline_policy resource , as they need to be unique across the same permission set.
With these two inline policies in place, both of your existing policies will be applied to the SSO permission set, and users assigned to that permission set will be subject to the restrictions defined in both policies.
You can have only one inline policy. So in your case the policies overwrite each other, and you end up with only one. So you either create a single inline policy combining the two that you have, or create two managed policies (not inline).

Is there a way to read the monitoring_role_arn from RDS into terraform.tfvars?

Instead of having to update this value manually each time, can I read this value directly into my terraform.tfvars file?
monitoring_role_arn = "arn:aws:iam::account:role/value"
you can use locals
Define in *.tf file
locals {
monitoring_role_arn = "arn:aws:iam::account:role/value"
}
in variables file you can refer as below
your_var = local.monitoring_role_arn
Role lookup option
Alternatively use the IAM Role lookup by the name given to the targeted role.
Ref: Data Source: aws_iam_role
To looup the resource by role name:
data "aws_iam_role" "monitoring_role_arn" {
name = "an_example_role_name" // This is the name of the role that appear in the AWS IAM Console
}
To get the ARN use the following line:
data.aws_iam_role.monitoring_role_arn.arn

terraform module - how to put policy in a variable

I have this snipit of code im using as my module.
What I am wondering is how can I make the policy
resource "aws_iam_role_policy" "role" {
name = var.name
role = var.role
policy = file("${path.module}/mypolicy.json")
}
here is my code I create my TF from:
module "aws_iam_role_policy" {
source = "../modules/mypolicypolicy/"
name = "mypolicy"
role = module.myrole.myroleout
}
What i want to know is the best way to reference 'policy' in my module, and the code I run to actually create the policy based off my module. I do not want to hard code the actual json in my module. How can I make this more reusable for later use for other policies?
What about passing the path to policy as a variable to your module?
In module:
variable "iam_policy_path" {
default = "./mypolicy.json"
}
resource "aws_iam_role_policy" "role" {
name = var.name
role = var.role
policy = file(var.iam_policy_path)
}
And then in the parent module you just provide new path if needed?
module "aws_iam_role_policy" {
source = "../modules/mypolicypolicy/"
name = "mypolicy"
role = module.myrole.myroleout
iam_policy_path = "new_policy_path.json"
}

Terraform - AWS IAM user with Programmatic access

I'm working with aws via terraform.
I'm trying to create an IAM user with Access type of "Programmatic access".
With the AWS management console this is quite simple:
When trying with Terraform (reference to docs) it seems that only the following arguments are supported:
name
path
permissions_boundary
force_destroy
tags
Maybe this should be configured via a policy?
Any help will be appreciated.
(*) Related question with different scenario.
You can use aws_iam_access_key (https://www.terraform.io/docs/providers/aws/r/iam_access_key.html) terraform resource to create Access keys for the user and that should imply that user has Programmatic Access.
Hope this helps.
The aws_iam_user resource needs to also have an aws_iam_access_key resource created for it.
The iam-user module has a comprehensive example of using it.
You could also use that module straight from the registry and let that do everything for you.
If you dont want to encrypt and just looking for Access key & Secret key into plain text you can use this
main.tf
resource "aws_iam_access_key" "sagemaker" {
user = aws_iam_user.user.name
}
resource "aws_iam_user" "user" {
name = "user-name"
path = "/"
}
data "aws_iam_policy" "sagemaker_policy" {
arn = "arn:aws:iam::aws:policy/AmazonSageMakerFullAccess"
}
resource "aws_iam_policy_attachment" "attach-policy" {
name = "sagemaker-policy-attachment"
users = [aws_iam_user.user.name]
policy_arn = data.aws_iam_policy.sagemaker_policy.arn
}
output.tf
output "secret_key" {
value = aws_iam_access_key.user.secret
}
output "access_key" {
value = aws_iam_access_key.user.id
}
you will get the Access key and secret key into the plain text you can directly use it.

How to add lifecycle rules to an S3 bucket using terraform?

I am using Terraform to create a bucket in S3 and I want to add "folders" and lifecycle rules to it.
I can create the bucket (using an "aws_s3_bucket" resource).
I can create the bucket and define my lifecycle rules within the same "aws_s3_bucket" resource, ie. at creation time.
I can add "folders" to the bucket (I know they aren't really folders, but they are presented to the client systems as if they were... :-) ), using an "aws_s3_bucket_object" resource, ie. after bucket creation.
All good...
But I want to be able to add lifecycle rules AFTER I've created the bucket, but I get an error telling me the bucket already exists. (Actually I want to be able to subsequently add folders and corresponding lifecycle rules as and when required.)
Now, I can add lifecycle rules to an existing bucket in the AWS GUI, so I know it is a reasonable thing to want to do.
But is there a way of doing it with Terraform?
Am I missing something?
resource "aws_s3_bucket" "bucket" {
bucket = "${replace(var.tags["Name"],"/_/","-")}"
region = "${var.aws_region}"
#tags = "${merge(var.tags, map("Name", "${var.tags["Name"]}"))}"
tags = "${merge(var.tags, map("Name", "${replace(var.tags["Name"],"/_/","-")}"))}"
}
resource "aws_s3_bucket" "bucket_quarterly" {
bucket = "${aws_s3_bucket.bucket.id}"
#region = "${var.aws_region}"
lifecycle_rule {
id = "quarterly_retention"
prefix = "quarterly/"
enabled = true
expiration {
days = 92
}
}
}
resource "aws_s3_bucket" "bucket_permanent" {
bucket = "${aws_s3_bucket.bucket.id}"
#region = "${var.aws_region}"
lifecycle_rule {
id = "permanent_retention"
enabled = true
prefix = "permanent/"
transition {
days = 1
storage_class = "GLACIER"
}
}
}
resource "aws_s3_bucket_object" "quarterly" {
bucket = "${aws_s3_bucket.bucket.id}"
#bucket = "${var.bucket_id}"
acl = "private"
key = "quarterly"
source = "/dev/null"
}
resource "aws_s3_bucket_object" "permanent" {
bucket = "${aws_s3_bucket.bucket.id}"
#bucket = "${var.bucket_id}"
acl = "private"
key = "permanent"
source = "/dev/null"
}
I expect to have a bucket with 2 lifecycle rules, but I get the following error:
Error: Error applying plan:
2 error(s) occurred:
* module.s3.aws_s3_bucket.bucket_quarterly: 1 error(s) occurred:
* aws_s3_bucket.bucket_quarterly: Error creating S3 bucket: BucketAlreadyOwnedByYou: Your previous request to create the named bucket succeeded and you already own it.
status code: 409, request id: EFE9C62B25341478, host id: hcsCNracNrpTJZ4QdU0AV2wNm/FqhYSEY4KieQ+zSHNsj6AUR69XvPF+0BiW4ZOpfgIoqwFoXkI=
* module.s3.aws_s3_bucket.bucket_permanent: 1 error(s) occurred:
* aws_s3_bucket.bucket_permanent: Error creating S3 bucket: BucketAlreadyOwnedByYou: Your previous request to create the named bucket succeeded and you already own it.
status code: 409, request id: 7DE1B1A36138A614, host id: 8jB6l7d6Hc6CZFgQSLQRMJg4wtvnrSL6Yp5R4RScq+GtuMW+6rkN39bcTUwQhzxeI7jRStgLXSc=
Terraform does not automatically rollback in the face of errors.
Instead, your Terraform state file has been partially updated with
any resources that successfully completed. Please address the error
above and apply again to incrementally change your infrastructure.
Lets first break down whats happening and how we can overcome this issue. Each time you define a resource "aws_s3_bucket", terraform will attempt to create a bucket with the parameters specified. If you want to attach a lifecycle policy to a bucket, do it where you define the bucket, e.g.:
resource "aws_s3_bucket" "quarterly" {
bucket = "quarterly_bucket_name"
#bucket = "${var.bucket_id}"
acl = "private"
lifecycle_rule {
id = "quarterly_retention"
prefix = "folder/"
enabled = true
expiration {
days = 92
}
}
}
resource "aws_s3_bucket" "permanent" {
bucket = "perm_bucket_name"
acl = "private"
lifecycle_rule {
id = "permanent_retention"
enabled = true
prefix = "permanent/"
transition {
days = 1
storage_class = "GLACIER"
}
}
}
A bucket can have multiple lifecycle_rule blocks on it.
If you want to define the lifecycle rules as external blocks, you can do it in this way:
// example of what the variable would look like:
variable "lifecycle_rules" {
type = "list"
default = []
}
// example of what the assignment would look like:
lifecycle_rules = [{
id = "cleanup"
prefix = ""
enabled = true
expiration = [{
days = 1
}]
}, {...}, {...} etc...]
// example what the usage would look like
resource "aws_s3_bucket" "quarterly" {
bucket = "quarterly_bucket_name"
#bucket = "${var.bucket_id}"
acl = "private"
source = "/dev/null"
lifecycle_rule = [ "${var.lifecycle_rules}" ]
}
Note: the implementation above of having an external lifecycle policy isn't really the best way to do it, but the only way. You pretty much trick terraform into accepting the list of maps, which happens to be the same type as lifecycle_rule, so it works. Ideally, Terraform should have it's own resource block for lifecycle rules, but it doesn't.
Edit: why have separate resource blocks when we now have dynamic blocks! Woohoo
As far as I am aware, you cannot make a lifecycle policy separately.
Someone raised a PR for a resource to be created to allow you to do so, but looks like it is still open: https://github.com/terraform-providers/terraform-provider-aws/issues/6188
As for your error, I believe the reason you're getting the error is because:
resource "aws_s3_bucket" "bucket"
Creates a bucket with a particular name.
resource "aws_s3_bucket" "bucket_quarterly"
References bucket = "${aws_s3_bucket.bucket.id}" and therefore tries to create a bucket with the same name as the previous resource (which cannot be done as names are unique).
resource "aws_s3_bucket" "bucket_permanent"
Similarly, this resource references bucket = "${aws_s3_bucket.bucket.id}" and therefore tries to create a bucket with the same name as the first resource (which cannot be done as names are unique).
You mentioned I expect to have a bucket with 2 lifecycle rules but in your above code you are creating 3 separate s3 buckets (one without a lifecycle, and 2 with a lifecycle) and two objects (folders) that are being placed into the s3 bucket without a lifecycle policy.
Thanks for the info (I like the idea of the list to separate the rules from the resource).
The issue was that I didn't appreciate that you could define lifecycle rules within the resource AND change them subsequently, so I was trying to figure out how to define them separately...
All that's required is to specify them in the resource and do terraform apply, then you can edit it and add/amend/remove lifecycle_rules items and just do terraform apply again to apply the changes.
source "aws_s3_bucket" "my_s3_bucket" {
bucket = local.s3_bucket_name
}
resource "aws_s3_bucket_acl" "my_s3_bucket_acl" {
bucket = aws_s3_bucket.my_s3_bucket.arn
lifecycle {
prevent_destroy = true
}
}
resource "aws_s3_bucket_versioning" "my_s3_bucket_versioning" {
bucket = aws_s3_bucket.my_s3_bucket.arn
versioning_configuration {
status = true
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "my_s3-bucket_encryption" {
bucket = aws_s3_bucket.my_s3_bucket.arn
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_lifecycle_configuration" "my_s3_bucket_lifecycle_config" {
bucket = aws_s3_bucket.my_s3_bucket.arn
rule {
id = "dev_lifecycle_7_days"
status = true
abort_incomplete_multipart_upload {
days_after_initiation = 30
}
noncurrent_version_expiration {
noncurrent_days = 1
}
transition {
storage_class = "STANDARD_IA"
days = 30
}
expiration {
days = 30
}
}
}