diff --git a/README.md b/README.md
index 6ec690a3..90c181c3 100644
--- a/README.md
+++ b/README.md
@@ -288,7 +288,7 @@ Available targets:
| Name | Source | Version |
|------|--------|---------|
-| [s3\_user](#module\_s3\_user) | cloudposse/iam-s3-user/aws | 1.0.0 |
+| [s3\_user](#module\_s3\_user) | cloudposse/iam-s3-user/aws | 1.1.0 |
| [this](#module\_this) | cloudposse/label/null | 0.25.0 |
## Resources
@@ -326,7 +326,7 @@ Available targets:
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| [access\_key\_enabled](#input\_access\_key\_enabled) | Set to `true` to create an IAM Access Key for the created IAM user | `bool` | `true` | no |
-| [acl](#input\_acl) | The [canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl) to apply.
We recommend `private` to avoid exposing sensitive information. Conflicts with `grants`. | `string` | `"private"` | no |
+| [acl](#input\_acl) | The [canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl) to apply.
Deprecated by AWS in favor of bucket policies.
Automatically disabled if `s3_object_ownership` is set to "BucketOwnerEnforced".
Defaults to "private" for backwards compatibility, but we recommend setting `s3_object_ownership` to "BucketOwnerEnforced" instead. | `string` | `"private"` | no |
| [additional\_tag\_map](#input\_additional\_tag\_map) | Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`.
This is for some rare cases where resources want additional configuration of tags
and therefore take a list of maps with tag key, value, and additional configuration. | `map(string)` | `{}` | no |
| [allow\_encrypted\_uploads\_only](#input\_allow\_encrypted\_uploads\_only) | Set to `true` to prevent uploads of unencrypted objects to S3 bucket | `bool` | `false` | no |
| [allow\_ssl\_requests\_only](#input\_allow\_ssl\_requests\_only) | Set to `true` to require requests to use Secure Socket Layer (HTTPS/SSL). This will explicitly deny access to HTTP requests | `bool` | `false` | no |
@@ -334,7 +334,7 @@ Available targets:
| [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no |
| [block\_public\_acls](#input\_block\_public\_acls) | Set to `false` to disable the blocking of new public access lists on the bucket | `bool` | `true` | no |
| [block\_public\_policy](#input\_block\_public\_policy) | Set to `false` to disable the blocking of new public policies on the bucket | `bool` | `true` | no |
-| [bucket\_key\_enabled](#input\_bucket\_key\_enabled) | Set this to true to use Amazon S3 Bucket Keys for SSE-KMS, which reduce the cost of AWS KMS requests.
For more information, see: https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-key.html | `bool` | `false` | no |
+| [bucket\_key\_enabled](#input\_bucket\_key\_enabled) | Set this to true to use Amazon S3 Bucket Keys for SSE-KMS, which may reduce the number of AWS KMS requests.
For more information, see: https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-key.html | `bool` | `false` | no |
| [bucket\_name](#input\_bucket\_name) | Bucket name. If provided, the bucket will be created with this name instead of generating the name from the context | `string` | `null` | no |
| [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes, tags, and additional\_tag\_map, which are merged. | `any` |
{| no | | [cors\_configuration](#input\_cors\_configuration) | Specifies the allowed headers, methods, origins and exposed headers when using CORS on this bucket |
"additional_tag_map": {},
"attributes": [],
"delimiter": null,
"descriptor_formats": {},
"enabled": true,
"environment": null,
"id_length_limit": null,
"label_key_case": null,
"label_order": [],
"label_value_case": null,
"labels_as_tags": [
"unset"
],
"name": null,
"namespace": null,
"regex_replace_chars": null,
"stage": null,
"tags": {},
"tenant": null
}
list(object({| `[]` | no | @@ -343,7 +343,7 @@ Available targets: | [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no | | [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | | [force\_destroy](#input\_force\_destroy) | When `true`, permits a non-empty S3 bucket to be deleted by first deleting all objects in the bucket.
allowed_headers = list(string)
allowed_methods = list(string)
allowed_origins = list(string)
expose_headers = list(string)
max_age_seconds = number
}))
list(object({| `[]` | no | +| [grants](#input\_grants) | A list of policy grants for the bucket, taking a list of permissions.
id = string
type = string
permissions = list(string)
uri = string
}))
list(object({| `[]` | no | | [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
id = string
type = string
permissions = list(string)
uri = string
}))
list(object({| `[]` | no | | [website\_redirect\_all\_requests\_to](#input\_website\_redirect\_all\_requests\_to) | If provided, all website requests will be redirected to the specified host name and protocol |
index_document = string
error_document = string
routing_rules = list(object({
condition = object({
http_error_code_returned_equals = string
key_prefix_equals = string
})
redirect = object({
host_name = string
http_redirect_code = string
protocol = string
replace_key_prefix_with = string
replace_key_with = string
})
}))
}))
list(object({| `[]` | no | diff --git a/docs/terraform.md b/docs/terraform.md index b29576ad..eadecb49 100644 --- a/docs/terraform.md +++ b/docs/terraform.md @@ -18,7 +18,7 @@ | Name | Source | Version | |------|--------|---------| -| [s3\_user](#module\_s3\_user) | cloudposse/iam-s3-user/aws | 1.0.0 | +| [s3\_user](#module\_s3\_user) | cloudposse/iam-s3-user/aws | 1.1.0 | | [this](#module\_this) | cloudposse/label/null | 0.25.0 | ## Resources @@ -56,7 +56,7 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [access\_key\_enabled](#input\_access\_key\_enabled) | Set to `true` to create an IAM Access Key for the created IAM user | `bool` | `true` | no | -| [acl](#input\_acl) | The [canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl) to apply.
host_name = string
protocol = string
}))
{| no | | [cors\_configuration](#input\_cors\_configuration) | Specifies the allowed headers, methods, origins and exposed headers when using CORS on this bucket |
"additional_tag_map": {},
"attributes": [],
"delimiter": null,
"descriptor_formats": {},
"enabled": true,
"environment": null,
"id_length_limit": null,
"label_key_case": null,
"label_order": [],
"label_value_case": null,
"labels_as_tags": [
"unset"
],
"name": null,
"namespace": null,
"regex_replace_chars": null,
"stage": null,
"tags": {},
"tenant": null
}
list(object({| `[]` | no | @@ -73,7 +73,7 @@ | [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no | | [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | | [force\_destroy](#input\_force\_destroy) | When `true`, permits a non-empty S3 bucket to be deleted by first deleting all objects in the bucket.
allowed_headers = list(string)
allowed_methods = list(string)
allowed_origins = list(string)
expose_headers = list(string)
max_age_seconds = number
}))
list(object({| `[]` | no | +| [grants](#input\_grants) | A list of policy grants for the bucket, taking a list of permissions.
id = string
type = string
permissions = list(string)
uri = string
}))
list(object({| `[]` | no | | [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
id = string
type = string
permissions = list(string)
uri = string
}))
list(object({| `[]` | no | | [website\_redirect\_all\_requests\_to](#input\_website\_redirect\_all\_requests\_to) | If provided, all website requests will be redirected to the specified host name and protocol |
index_document = string
error_document = string
routing_rules = list(object({
condition = object({
http_error_code_returned_equals = string
key_prefix_equals = string
})
redirect = object({
host_name = string
http_redirect_code = string
protocol = string
replace_key_prefix_with = string
replace_key_with = string
})
}))
}))
list(object({| `[]` | no | diff --git a/main.tf b/main.tf index 2e27cb29..f766fcbf 100644 --- a/main.tf +++ b/main.tf @@ -32,8 +32,8 @@ resource "aws_s3_bucket" "default" { #bridgecrew:skip=BC_AWS_S3_13:Skipping `Enable S3 Bucket Logging` because we do not have good defaults #bridgecrew:skip=CKV_AWS_52:Skipping `Ensure S3 bucket has MFA delete enabled` due to issue in terraform (https://github.com/hashicorp/terraform-provider-aws/issues/629). #bridgecrew:skip=BC_AWS_S3_16:Skipping `Ensure S3 bucket versioning is enabled` because dynamic blocks are not supported by checkov - #bridgecrew:skip=BC_AWS_S3_14:Skipping `Ensure all data stored in the S3 bucket is securely encrypted at rest` because variables are not understood - #bridgecrew:skip=BC_AWS_GENERAL_56:Skipping `Ensure that S3 buckets are encrypted with KMS by default` because we do not have good defaults + #bridgecrew:skip=BC_AWS_S3_14:Skipping `Ensure all data stored in the S3 bucket is securely encrypted at rest` because that is now enforced automatically by AWS + #bridgecrew:skip=BC_AWS_GENERAL_56:Skipping `Ensure that S3 buckets are encrypted with KMS by default` because we do not agree that this is required #bridgecrew:skip=BC_AWS_GENERAL_72:We do not agree that cross-region replication must be enabled count = local.enabled ? 1 : 0 bucket = local.bucket_name @@ -44,12 +44,14 @@ resource "aws_s3_bucket" "default" { tags = module.this.tags } +# Ensure the resource exists to track drift, even if the feature is disabled resource "aws_s3_bucket_accelerate_configuration" "default" { - count = local.transfer_acceleration_enabled ? 1 : 0 + count = local.enabled ? 1 : 0 bucket = join("", aws_s3_bucket.default.*.id) - status = "Enabled" + status = local.transfer_acceleration_enabled ? "Enabled" : "Suspended" } +# Ensure the resource exists to track drift, even if the feature is disabled resource "aws_s3_bucket_versioning" "default" { count = local.enabled ? 1 : 0 bucket = join("", aws_s3_bucket.default.*.id) @@ -156,7 +158,7 @@ resource "aws_s3_bucket_cors_configuration" "default" { } resource "aws_s3_bucket_acl" "default" { - count = local.enabled ? 1 : 0 + count = local.enabled && var.s3_object_ownership != "BucketOwnerEnforced" ? 1 : 0 bucket = join("", aws_s3_bucket.default.*.id) # Conflicts with access_control_policy so this is enabled if no grants @@ -184,6 +186,7 @@ resource "aws_s3_bucket_acl" "default" { } } } + depends_on = [aws_s3_bucket_ownership_controls.default] } resource "aws_s3_bucket_replication_configuration" "default" { @@ -316,7 +319,7 @@ resource "aws_s3_bucket_object_lock_configuration" "default" { module "s3_user" { source = "cloudposse/iam-s3-user/aws" - version = "1.0.0" + version = "1.1.0" enabled = local.enabled && var.user_enabled s3_actions = var.allowed_bucket_actions @@ -325,6 +328,7 @@ module "s3_user" { create_iam_access_key = var.access_key_enabled ssm_enabled = var.store_access_key_in_ssm ssm_base_path = var.ssm_base_path + permissions_boundary = var.user_permissions_boundary_arn context = module.this.context } diff --git a/replication.tf b/replication.tf index 04bcce3d..79a3447b 100644 --- a/replication.tf +++ b/replication.tf @@ -3,7 +3,7 @@ resource "aws_iam_role" "replication" { name = format("%s-replication", module.this.id) assume_role_policy = data.aws_iam_policy_document.replication_sts[0].json - permissions_boundary = var.s3_replication_permission_boundary_arn + permissions_boundary = var.s3_replication_permissions_boundary_arn } data "aws_iam_policy_document" "replication_sts" { diff --git a/variables.tf b/variables.tf index c3ffc9a0..17e6c476 100644 --- a/variables.tf +++ b/variables.tf @@ -3,7 +3,9 @@ variable "acl" { default = "private" description = <<-EOT The [canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl) to apply. - We recommend `private` to avoid exposing sensitive information. Conflicts with `grants`. + Deprecated by AWS in favor of bucket policies. + Automatically disabled if `s3_object_ownership` is set to "BucketOwnerEnforced". + Defaults to "private" for backwards compatibility, but we recommend setting `s3_object_ownership` to "BucketOwnerEnforced" instead. EOT } @@ -19,6 +21,8 @@ variable "grants" { description = <<-EOT A list of policy grants for the bucket, taking a list of permissions. Conflicts with `acl`. Set `acl` to `null` to use this. + Deprecated by AWS in favor of bucket policies. + Automatically disabled if `s3_object_ownership` is set to "BucketOwnerEnforced". EOT } @@ -77,6 +81,12 @@ variable "user_enabled" { description = "Set to `true` to create an IAM user with permission to access the bucket" } +variable "user_permissions_boundary_arn" { + type = string + default = null + description = "Permission boundary ARN for the IAM user created to access the bucket." +} + variable "access_key_enabled" { type = bool default = true @@ -280,10 +290,10 @@ variable "s3_replication_source_roles" { description = "Cross-account IAM Role ARNs that will be allowed to perform S3 replication to this bucket (for replication within the same AWS account, it's not necessary to adjust the bucket policy)." } -variable "s3_replication_permission_boundary_arn" { +variable "s3_replication_permissions_boundary_arn" { type = string default = null - description = "Permission boundary ARN of the IAM replication role. Defaults to null." + description = "Permissions boundary ARN for the created IAM replication role." } variable "bucket_name" { @@ -372,15 +382,18 @@ variable "transfer_acceleration_enabled" { variable "s3_object_ownership" { type = string default = "ObjectWriter" - description = "Specifies the S3 object ownership control. Valid values are `ObjectWriter`, `BucketOwnerPreferred`, and 'BucketOwnerEnforced'." + description = <<-EOT + Specifies the S3 object ownership control. + Valid values are `ObjectWriter`, `BucketOwnerPreferred`, and 'BucketOwnerEnforced'. + Defaults to "ObjectWriter" for backwards compatibility, but we recommend setting "BucketOwnerEnforced" instead. + EOT } variable "bucket_key_enabled" { type = bool default = false description = <<-EOT - Set this to true to use Amazon S3 Bucket Keys for SSE-KMS, which reduce the cost of AWS KMS requests. - + Set this to true to use Amazon S3 Bucket Keys for SSE-KMS, which may reduce the number of AWS KMS requests. For more information, see: https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-key.html EOT }
host_name = string
protocol = string
}))