diff --git a/aws-iam-role-s3-readonly/README.md b/aws-iam-role-s3-readonly/README.md new file mode 100644 index 00000000..534ec4e2 --- /dev/null +++ b/aws-iam-role-s3-readonly/README.md @@ -0,0 +1,51 @@ +# AWS IAM Role S3 Readonly + +This module will create a role which is granted readonly control over AWS S3 buckets. + + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [assume\_role\_policy](#module\_assume\_role\_policy) | ../aws-assume-role-policy | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy.s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_iam_policy_document.s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [env](#input\_env) | Env for tagging and naming. See [doc](../README.md#consistent-tagging) | `string` | n/a | yes | +| [iam\_path](#input\_iam\_path) | n/a | `string` | `"/"` | no | +| [owner](#input\_owner) | Owner for tagging and naming. See [doc](../README.md#consistent-tagging) | `string` | n/a | yes | +| [project](#input\_project) | Project for tagging and naming. See [doc](../README.md#consistent-tagging) | `string` | n/a | yes | +| [role\_name](#input\_role\_name) | Name of the role to create | `string` | n/a | yes | +| [s3\_bucket\_prefixes](#input\_s3\_bucket\_prefixes) | Limits role permissions to buckets with specific prefixes. Empty for all buckets. | `list(any)` |
[| no | +| [saml\_idp\_arns](#input\_saml\_idp\_arns) | The AWS SAML IDP arns to establish a trust relationship. Ignored if empty or not provided. | `set(string)` | `[]` | no | +| [service](#input\_service) | Service for tagging and naming. See [doc](../README.md#consistent-tagging) | `string` | n/a | yes | +| [source\_account\_ids](#input\_source\_account\_ids) | The source AWS account IDs to establish a trust relationship. Ignored if empty or not provided. | `set(string)` | `[]` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [role\_arn](#output\_role\_arn) | n/a | +| [role\_name](#output\_role\_name) | n/a | + diff --git a/aws-iam-role-s3-readonly/main.tf b/aws-iam-role-s3-readonly/main.tf new file mode 100644 index 00000000..b09377dc --- /dev/null +++ b/aws-iam-role-s3-readonly/main.tf @@ -0,0 +1,67 @@ +locals { + tags = { + env = var.env + owner = var.owner + service = var.service + project = var.project + } +} + +module "assume_role_policy" { + source = "../aws-assume-role-policy" + source_account_ids = var.source_account_ids + saml_idp_arns = var.saml_idp_arns + env = var.env + owner = var.owner + service = var.service + project = var.project +} + +resource "aws_iam_role" "role" { + name = var.role_name + assume_role_policy = module.assume_role_policy.json + tags = local.tags +} + +data "aws_iam_policy_document" "s3-bucket-readonly" { + statement { + sid = "GetFiles" + effect = "Allow" + actions = [ + "s3:GetObject", + "s3:GetObjectAcl", + "s3:GetObjectVersionAcl", + "s3:GetObjectVersion" + ] + resources = formatlist("arn:aws:s3:::%s*/*", var.s3_bucket_names) + } + statement { + sid = "ListBucket" + effect = "Allow" + actions = [ + "s3:ListBucket", + "s3:GetBucketLocation", + "s3:ListBucketMultipartUploads" + ] + resources = formatlist("arn:aws:s3:::%s*/*", var.s3_bucket_names) + } + statement { + sid = "ShowAllowedBuckets" + effect = "Allow" + actions = [ + "s3:ListAllMyBuckets" + ] + resources = toset(formatlist("arn:aws:s3:::%s", var.s3_bucket_names), formatlist("arn:aws:s3:::%s/*", var.s3_bucket_prefixes)) + } +} + +resource "aws_iam_policy" "s3" { + name = "${var.role_name}-s3" + description = "Provide access to s3 resources for a distribution ${var.role_name}" + policy = data.aws_iam_policy_document.s3-bucket-readonly.json +} + +resource "aws_iam_role_policy_attachment" "s3" { + role = aws_iam_role.role.name + policy_arn = aws_iam_policy.s3.arn +} diff --git a/aws-iam-role-s3-readonly/module_test.go b/aws-iam-role-s3-readonly/module_test.go new file mode 100644 index 00000000..b9d20d8d --- /dev/null +++ b/aws-iam-role-s3-readonly/module_test.go @@ -0,0 +1,42 @@ +package test + +import ( + "fmt" + "testing" + + "github.com/chanzuckerberg/go-misc/tftest" + "github.com/gruntwork-io/terratest/modules/random" + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/require" +) + +func TestAWSIAMRoleS3Readonly(t *testing.T) { + test := tftest.Test{ + Setup: func(t *testing.T) *terraform.Options { + curAcct := tftest.AWSCurrentAccountID(t) + + return tftest.Options( + tftest.IAMRegion, + + map[string]interface{}{ + "role_name": random.UniqueId(), + "iam_path": fmt.Sprintf("/%s/", random.UniqueId()), + "source_account_ids": []string{curAcct}, + }, + ) + }, + Validate: func(t *testing.T, options *terraform.Options) { + r := require.New(t) + region := options.EnvVars["AWS_DEFAULT_REGION"] + r.NotEmpty(region) + outputs := terraform.OutputAll(t, options) + r.NotEmpty(outputs) + roleName := outputs["role_name"].(string) + r.NotEmpty(roleName) + roleARN := outputs["role_arn"].(string) + r.NotEmpty(roleARN) + }, + } + + test.Run(t) +} diff --git a/aws-iam-role-s3-readonly/outputs.tf b/aws-iam-role-s3-readonly/outputs.tf new file mode 100644 index 00000000..9dc1b5c4 --- /dev/null +++ b/aws-iam-role-s3-readonly/outputs.tf @@ -0,0 +1,7 @@ +output "role_name" { + value = aws_iam_role.role.name +} + +output "role_arn" { + value = aws_iam_role.role.arn +} diff --git a/aws-iam-role-s3-readonly/variables.tf b/aws-iam-role-s3-readonly/variables.tf new file mode 100644 index 00000000..7642c237 --- /dev/null +++ b/aws-iam-role-s3-readonly/variables.tf @@ -0,0 +1,50 @@ +variable "role_name" { + type = string + description = "Name of the role to create" +} + +variable "s3_bucket_names" { + type = set(string) + description = "Limits role permissions to buckets with specific prefixes. Empty for all buckets." + + default = [ + "", + ] +} + +variable "iam_path" { + type = string + default = "/" +} + +variable "source_account_ids" { + type = set(string) + default = [] + description = "The source AWS account IDs to establish a trust relationship. Ignored if empty or not provided." +} + +variable "saml_idp_arns" { + type = set(string) + default = [] + description = "The AWS SAML IDP arns to establish a trust relationship. Ignored if empty or not provided." +} + +variable "project" { + type = string + description = "Project for tagging and naming. See [doc](../README.md#consistent-tagging)" +} + +variable "env" { + type = string + description = "Env for tagging and naming. See [doc](../README.md#consistent-tagging)" +} + +variable "service" { + type = string + description = "Service for tagging and naming. See [doc](../README.md#consistent-tagging)" +} + +variable "owner" { + type = string + description = "Owner for tagging and naming. See [doc](../README.md#consistent-tagging)" +}
""
]