diff --git a/README.md b/README.md index 0e4e2ef8..95c66643 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ These features of S3 bucket configurations are supported: - ELB log delivery bucket policy - ALB/NLB log delivery bucket policy - Account-level Public Access Block +- S3 Directory Bucket ## Usage @@ -123,6 +124,7 @@ Users of Terragrunt can achieve similar results by using modules provided in the - [S3 Analytics](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/examples/s3-analytics) - S3 bucket Analytics Configurations. - [S3 Inventory](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/examples/s3-inventory) - S3 bucket Inventory configuration. - [S3 Account-level Public Access Block](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/examples/account-public-access) - Manage S3 account-level Public Access Block. +- [S3 Directory Bucket](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/examples/directory-bucket) - S3 Directory Bucket configuration. ## Requirements @@ -130,13 +132,13 @@ Users of Terragrunt can achieve similar results by using modules provided in the | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 5.70 | +| [aws](#requirement\_aws) | >= 5.83 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.70 | +| [aws](#provider\_aws) | >= 5.83 | ## Modules @@ -165,6 +167,7 @@ No modules. | [aws_s3_bucket_server_side_encryption_configuration.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_versioning.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | | [aws_s3_bucket_website_configuration.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_website_configuration) | resource | +| [aws_s3_directory_bucket.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_directory_bucket) | resource | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | | [aws_canonical_user_id.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/canonical_user_id) | data source | | [aws_iam_policy_document.access_log_delivery](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | @@ -207,6 +210,7 @@ No modules. | [attach\_policy](#input\_attach\_policy) | Controls if S3 bucket should have bucket policy attached (set to `true` to use value of `policy` as bucket policy) | `bool` | `false` | no | | [attach\_public\_policy](#input\_attach\_public\_policy) | Controls if a user defined public bucket policy will be attached (set to `false` to allow upstream to apply defaults to the bucket) | `bool` | `true` | no | | [attach\_require\_latest\_tls\_policy](#input\_attach\_require\_latest\_tls\_policy) | Controls if S3 bucket should require the latest version of TLS | `bool` | `false` | no | +| [availability\_zone\_id](#input\_availability\_zone\_id) | Availability Zone ID or Local Zone ID | `string` | `null` | no | | [block\_public\_acls](#input\_block\_public\_acls) | Whether Amazon S3 should block public ACLs for this bucket. | `bool` | `true` | no | | [block\_public\_policy](#input\_block\_public\_policy) | Whether Amazon S3 should block public bucket policies for this bucket. | `bool` | `true` | no | | [bucket](#input\_bucket) | (Optional, Forces new resource) The name of the bucket. If omitted, Terraform will assign a random, unique name. | `string` | `null` | no | @@ -214,6 +218,7 @@ No modules. | [control\_object\_ownership](#input\_control\_object\_ownership) | Whether to manage S3 Bucket Ownership Controls on this bucket. | `bool` | `false` | no | | [cors\_rule](#input\_cors\_rule) | List of maps containing rules for Cross-Origin Resource Sharing. | `any` | `[]` | no | | [create\_bucket](#input\_create\_bucket) | Controls if S3 bucket should be created | `bool` | `true` | no | +| [data\_redundancy](#input\_data\_redundancy) | Data redundancy. Valid values: `SingleAvailabilityZone` | `string` | `null` | no | | [expected\_bucket\_owner](#input\_expected\_bucket\_owner) | The account ID of the expected bucket owner | `string` | `null` | no | | [force\_destroy](#input\_force\_destroy) | (Optional, Default:false ) A boolean that indicates all objects should be deleted from the bucket so that the bucket can be destroyed without error. These objects are not recoverable. | `bool` | `false` | no | | [grant](#input\_grant) | An ACL policy grant. Conflicts with `acl` | `any` | `[]` | no | @@ -223,7 +228,9 @@ No modules. | [inventory\_self\_source\_destination](#input\_inventory\_self\_source\_destination) | Whether or not the inventory source bucket is also the destination bucket. | `bool` | `false` | no | | [inventory\_source\_account\_id](#input\_inventory\_source\_account\_id) | The inventory source account id. | `string` | `null` | no | | [inventory\_source\_bucket\_arn](#input\_inventory\_source\_bucket\_arn) | The inventory source bucket ARN. | `string` | `null` | no | +| [is\_directory\_bucket](#input\_is\_directory\_bucket) | If the s3 bucket created is a directory bucket | `bool` | `false` | no | | [lifecycle\_rule](#input\_lifecycle\_rule) | List of maps containing configuration of object lifecycle management. | `any` | `[]` | no | +| [location\_type](#input\_location\_type) | Location type. Valid values: `AvailabilityZone` or `LocalZone` | `string` | `null` | no | | [logging](#input\_logging) | Map containing access bucket logging configuration. | `any` | `{}` | no | | [metric\_configuration](#input\_metric\_configuration) | Map containing bucket metric configuration. | `any` | `[]` | no | | [object\_lock\_configuration](#input\_object\_lock\_configuration) | Map containing S3 object locking configuration. | `any` | `{}` | no | @@ -238,6 +245,7 @@ No modules. | [server\_side\_encryption\_configuration](#input\_server\_side\_encryption\_configuration) | Map containing server-side encryption configuration. | `any` | `{}` | no | | [tags](#input\_tags) | (Optional) A mapping of tags to assign to the bucket. | `map(string)` | `{}` | no | | [transition\_default\_minimum\_object\_size](#input\_transition\_default\_minimum\_object\_size) | The default minimum object size behavior applied to the lifecycle configuration. Valid values: all\_storage\_classes\_128K (default), varies\_by\_storage\_class | `string` | `null` | no | +| [type](#input\_type) | Bucket type. Valid values: `Directory` | `string` | `"Directory"` | no | | [versioning](#input\_versioning) | Map containing versioning configuration. | `map(string)` | `{}` | no | | [website](#input\_website) | Map containing static web-site hosting or redirect configuration. | `any` | `{}` | no | @@ -255,6 +263,8 @@ No modules. | [s3\_bucket\_region](#output\_s3\_bucket\_region) | The AWS region this bucket resides in. | | [s3\_bucket\_website\_domain](#output\_s3\_bucket\_website\_domain) | The domain of the website endpoint, if the bucket is configured with a website. If not, this will be an empty string. This is used to create Route 53 alias records. | | [s3\_bucket\_website\_endpoint](#output\_s3\_bucket\_website\_endpoint) | The website endpoint, if the bucket is configured with a website. If not, this will be an empty string. | +| [s3\_directory\_bucket\_arn](#output\_s3\_directory\_bucket\_arn) | ARN of the directory bucket. | +| [s3\_directory\_bucket\_name](#output\_s3\_directory\_bucket\_name) | Name of the directory bucket. | ## Authors diff --git a/examples/complete/README.md b/examples/complete/README.md index b0783ff9..079f346f 100644 --- a/examples/complete/README.md +++ b/examples/complete/README.md @@ -30,14 +30,14 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 5.70 | +| [aws](#requirement\_aws) | >= 5.83 | | [random](#requirement\_random) | >= 2.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.70 | +| [aws](#provider\_aws) | >= 5.83 | | [random](#provider\_random) | >= 2.0 | ## Modules diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf index 6d9488f3..9ef2d5d8 100644 --- a/examples/complete/versions.tf +++ b/examples/complete/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.70" + version = ">= 5.83" } random = { source = "hashicorp/random" diff --git a/examples/directory-bucket/README.md b/examples/directory-bucket/README.md new file mode 100644 index 00000000..1ce36553 --- /dev/null +++ b/examples/directory-bucket/README.md @@ -0,0 +1,60 @@ +# S3 directory bucket + +Configuration in this directory creates S3 directory bucket and related resources. + +## Usage + +To run this example you need to execute: + +```bash +$ terraform init +$ terraform plan +$ terraform apply +``` + +Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 5.83 | +| [random](#requirement\_random) | >= 2.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 5.83 | +| [random](#provider\_random) | >= 2.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [complete](#module\_complete) | ../../ | n/a | +| [simple](#module\_simple) | ../../ | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_kms_key.objects](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | +| [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | +| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [directory\_bucket\_arn](#output\_directory\_bucket\_arn) | ARN of the directory bucket. | +| [directory\_bucket\_name](#output\_directory\_bucket\_name) | Name of the directory bucket. | + diff --git a/examples/directory-bucket/main.tf b/examples/directory-bucket/main.tf new file mode 100644 index 00000000..75c6e756 --- /dev/null +++ b/examples/directory-bucket/main.tf @@ -0,0 +1,127 @@ +locals { + region = "eu-west-1" +} + +provider "aws" { + region = local.region + + # Make it faster by skipping something + skip_metadata_api_check = true + skip_region_validation = true + skip_credentials_validation = true +} + +data "aws_caller_identity" "current" {} + +data "aws_availability_zones" "available" { + state = "available" +} + +module "simple" { + source = "../../" + + is_directory_bucket = true + bucket = random_pet.this.id + # https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-express-Endpoints.html + availability_zone_id = data.aws_availability_zones.available.zone_ids[1] +} + +module "complete" { + source = "../../" + + is_directory_bucket = true + bucket = "${random_pet.this.id}-complete" + # https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-express-Endpoints.html + availability_zone_id = data.aws_availability_zones.available.zone_ids[1] + server_side_encryption_configuration = { + rule = { + bucket_key_enabled = true # required for directory buckets + apply_server_side_encryption_by_default = { + kms_master_key_id = aws_kms_key.objects.arn + sse_algorithm = "aws:kms" + } + } + } + lifecycle_rule = [ + { + id = "test" + status = "Enabled" + expiration = { + days = 7 + } + }, + { + id = "logs" + status = "Enabled" + expiration = { + days = 5 + } + filter = { + prefix = "logs/" + object_size_less_than = 10 + } + }, + { + id = "other" + status = "Enabled" + expiration = { + days = 2 + } + filter = { + prefix = "other/" + } + } + ] + attach_policy = true + policy = data.aws_iam_policy_document.bucket_policy.json +} + +resource "random_pet" "this" { + length = 2 +} + +resource "aws_kms_key" "objects" { + description = "KMS key is used to encrypt bucket objects" + deletion_window_in_days = 7 +} + +data "aws_iam_policy_document" "bucket_policy" { + + statement { + sid = "ReadWriteAccess" + effect = "Allow" + + actions = [ + "s3express:CreateSession", + ] + + resources = [module.complete.s3_directory_bucket_arn] + + principals { + identifiers = [data.aws_caller_identity.current.account_id] + type = "AWS" + } + } + + statement { + sid = "ReadOnlyAccess" + effect = "Allow" + + actions = [ + "s3express:CreateSession", + ] + + resources = [module.complete.s3_directory_bucket_arn] + + principals { + identifiers = [data.aws_caller_identity.current.account_id] + type = "AWS" + } + + condition { + test = "StringEquals" + values = ["ReadOnly"] + variable = "s3express:SessionMode" + } + } +} diff --git a/examples/directory-bucket/outputs.tf b/examples/directory-bucket/outputs.tf new file mode 100644 index 00000000..eae34c94 --- /dev/null +++ b/examples/directory-bucket/outputs.tf @@ -0,0 +1,9 @@ +output "directory_bucket_name" { + description = "Name of the directory bucket." + value = module.complete.s3_directory_bucket_name +} + +output "directory_bucket_arn" { + description = "ARN of the directory bucket." + value = module.complete.s3_directory_bucket_arn +} diff --git a/examples/directory-bucket/variables.tf b/examples/directory-bucket/variables.tf new file mode 100644 index 00000000..e69de29b diff --git a/examples/directory-bucket/versions.tf b/examples/directory-bucket/versions.tf new file mode 100644 index 00000000..9ef2d5d8 --- /dev/null +++ b/examples/directory-bucket/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.83" + } + random = { + source = "hashicorp/random" + version = ">= 2.0" + } + } +} diff --git a/examples/notification/README.md b/examples/notification/README.md index 63f52d98..8c331142 100644 --- a/examples/notification/README.md +++ b/examples/notification/README.md @@ -20,7 +20,7 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 5.70 | +| [aws](#requirement\_aws) | >= 5.83 | | [null](#requirement\_null) | >= 2.0 | | [random](#requirement\_random) | >= 2.0 | @@ -28,7 +28,7 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.70 | +| [aws](#provider\_aws) | >= 5.83 | | [null](#provider\_null) | >= 2.0 | | [random](#provider\_random) | >= 2.0 | diff --git a/examples/notification/versions.tf b/examples/notification/versions.tf index 7fd34151..bd90e261 100644 --- a/examples/notification/versions.tf +++ b/examples/notification/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.70" + version = ">= 5.83" } random = { source = "hashicorp/random" diff --git a/examples/object/README.md b/examples/object/README.md index 3a968001..f694e65d 100644 --- a/examples/object/README.md +++ b/examples/object/README.md @@ -20,14 +20,14 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 5.70 | +| [aws](#requirement\_aws) | >= 5.83 | | [random](#requirement\_random) | >= 2.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.70 | +| [aws](#provider\_aws) | >= 5.83 | | [random](#provider\_random) | >= 2.0 | ## Modules diff --git a/examples/object/versions.tf b/examples/object/versions.tf index 6d9488f3..9ef2d5d8 100644 --- a/examples/object/versions.tf +++ b/examples/object/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.70" + version = ">= 5.83" } random = { source = "hashicorp/random" diff --git a/examples/s3-analytics/README.md b/examples/s3-analytics/README.md index a64874e7..c2148d9e 100644 --- a/examples/s3-analytics/README.md +++ b/examples/s3-analytics/README.md @@ -10,14 +10,14 @@ Please check [complete example](https://github.com/terraform-aws-modules/terrafo | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 5.70 | +| [aws](#requirement\_aws) | >= 5.83 | | [random](#requirement\_random) | >= 2.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.70 | +| [aws](#provider\_aws) | >= 5.83 | | [random](#provider\_random) | >= 2.0 | ## Modules diff --git a/examples/s3-analytics/versions.tf b/examples/s3-analytics/versions.tf index 6d9488f3..9ef2d5d8 100644 --- a/examples/s3-analytics/versions.tf +++ b/examples/s3-analytics/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.70" + version = ">= 5.83" } random = { source = "hashicorp/random" diff --git a/examples/s3-inventory/README.md b/examples/s3-inventory/README.md index e3a72de5..fb67fb8d 100644 --- a/examples/s3-inventory/README.md +++ b/examples/s3-inventory/README.md @@ -10,14 +10,14 @@ Please check [complete example](https://github.com/terraform-aws-modules/terrafo | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 5.70 | +| [aws](#requirement\_aws) | >= 5.83 | | [random](#requirement\_random) | >= 2.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.70 | +| [aws](#provider\_aws) | >= 5.83 | | [random](#provider\_random) | >= 2.0 | ## Modules diff --git a/examples/s3-inventory/versions.tf b/examples/s3-inventory/versions.tf index 6d9488f3..9ef2d5d8 100644 --- a/examples/s3-inventory/versions.tf +++ b/examples/s3-inventory/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.70" + version = ">= 5.83" } random = { source = "hashicorp/random" diff --git a/examples/s3-replication/README.md b/examples/s3-replication/README.md index d256ca94..e85b5c03 100644 --- a/examples/s3-replication/README.md +++ b/examples/s3-replication/README.md @@ -22,15 +22,15 @@ Note that this example may create resources which cost money. Run `terraform des | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 5.70 | +| [aws](#requirement\_aws) | >= 5.83 | | [random](#requirement\_random) | >= 2.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.70 | -| [aws.replica](#provider\_aws.replica) | >= 5.70 | +| [aws](#provider\_aws) | >= 5.83 | +| [aws.replica](#provider\_aws.replica) | >= 5.83 | | [random](#provider\_random) | >= 2.0 | ## Modules diff --git a/examples/s3-replication/versions.tf b/examples/s3-replication/versions.tf index 6d9488f3..9ef2d5d8 100644 --- a/examples/s3-replication/versions.tf +++ b/examples/s3-replication/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.70" + version = ">= 5.83" } random = { source = "hashicorp/random" diff --git a/main.tf b/main.tf index ce49ab09..667c0b21 100644 --- a/main.tf +++ b/main.tf @@ -23,7 +23,7 @@ locals { } resource "aws_s3_bucket" "this" { - count = local.create_bucket ? 1 : 0 + count = local.create_bucket && !var.is_directory_bucket ? 1 : 0 bucket = var.bucket bucket_prefix = var.bucket_prefix @@ -33,8 +33,22 @@ resource "aws_s3_bucket" "this" { tags = var.tags } +resource "aws_s3_directory_bucket" "this" { + count = local.create_bucket && var.is_directory_bucket ? 1 : 0 + + bucket = "${var.bucket}--${var.availability_zone_id}--x-s3" + data_redundancy = var.data_redundancy + force_destroy = var.force_destroy + type = var.type + + location { + name = var.availability_zone_id + type = var.location_type + } +} + resource "aws_s3_bucket_logging" "this" { - count = local.create_bucket && length(keys(var.logging)) > 0 ? 1 : 0 + count = local.create_bucket && length(keys(var.logging)) > 0 && !var.is_directory_bucket ? 1 : 0 bucket = aws_s3_bucket.this[0].id @@ -63,7 +77,7 @@ resource "aws_s3_bucket_logging" "this" { } resource "aws_s3_bucket_acl" "this" { - count = local.create_bucket && local.create_bucket_acl ? 1 : 0 + count = local.create_bucket && local.create_bucket_acl && !var.is_directory_bucket ? 1 : 0 bucket = aws_s3_bucket.this[0].id expected_bucket_owner = var.expected_bucket_owner @@ -102,7 +116,7 @@ resource "aws_s3_bucket_acl" "this" { } resource "aws_s3_bucket_website_configuration" "this" { - count = local.create_bucket && length(keys(var.website)) > 0 ? 1 : 0 + count = local.create_bucket && length(keys(var.website)) > 0 && !var.is_directory_bucket ? 1 : 0 bucket = aws_s3_bucket.this[0].id expected_bucket_owner = var.expected_bucket_owner @@ -157,7 +171,7 @@ resource "aws_s3_bucket_website_configuration" "this" { } resource "aws_s3_bucket_versioning" "this" { - count = local.create_bucket && length(keys(var.versioning)) > 0 ? 1 : 0 + count = local.create_bucket && length(keys(var.versioning)) > 0 && !var.is_directory_bucket ? 1 : 0 bucket = aws_s3_bucket.this[0].id expected_bucket_owner = var.expected_bucket_owner @@ -175,7 +189,7 @@ resource "aws_s3_bucket_versioning" "this" { resource "aws_s3_bucket_server_side_encryption_configuration" "this" { count = local.create_bucket && length(keys(var.server_side_encryption_configuration)) > 0 ? 1 : 0 - bucket = aws_s3_bucket.this[0].id + bucket = var.is_directory_bucket ? aws_s3_directory_bucket.this[0].bucket : aws_s3_bucket.this[0].id expected_bucket_owner = var.expected_bucket_owner dynamic "rule" { @@ -197,7 +211,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "this" { } resource "aws_s3_bucket_accelerate_configuration" "this" { - count = local.create_bucket && var.acceleration_status != null ? 1 : 0 + count = local.create_bucket && var.acceleration_status != null && !var.is_directory_bucket ? 1 : 0 bucket = aws_s3_bucket.this[0].id expected_bucket_owner = var.expected_bucket_owner @@ -207,7 +221,7 @@ resource "aws_s3_bucket_accelerate_configuration" "this" { } resource "aws_s3_bucket_request_payment_configuration" "this" { - count = local.create_bucket && var.request_payer != null ? 1 : 0 + count = local.create_bucket && var.request_payer != null && !var.is_directory_bucket ? 1 : 0 bucket = aws_s3_bucket.this[0].id expected_bucket_owner = var.expected_bucket_owner @@ -217,7 +231,7 @@ resource "aws_s3_bucket_request_payment_configuration" "this" { } resource "aws_s3_bucket_cors_configuration" "this" { - count = local.create_bucket && length(local.cors_rules) > 0 ? 1 : 0 + count = local.create_bucket && length(local.cors_rules) > 0 && !var.is_directory_bucket ? 1 : 0 bucket = aws_s3_bucket.this[0].id expected_bucket_owner = var.expected_bucket_owner @@ -239,7 +253,7 @@ resource "aws_s3_bucket_cors_configuration" "this" { resource "aws_s3_bucket_lifecycle_configuration" "this" { count = local.create_bucket && length(local.lifecycle_rules) > 0 ? 1 : 0 - bucket = aws_s3_bucket.this[0].id + bucket = var.is_directory_bucket ? aws_s3_directory_bucket.this[0].bucket : aws_s3_bucket.this[0].id expected_bucket_owner = var.expected_bucket_owner transition_default_minimum_object_size = var.transition_default_minimum_object_size @@ -369,7 +383,7 @@ resource "aws_s3_bucket_object_lock_configuration" "this" { } resource "aws_s3_bucket_replication_configuration" "this" { - count = local.create_bucket && length(keys(var.replication_configuration)) > 0 ? 1 : 0 + count = local.create_bucket && length(keys(var.replication_configuration)) > 0 && !var.is_directory_bucket ? 1 : 0 bucket = aws_s3_bucket.this[0].id role = var.replication_configuration["role"] @@ -540,7 +554,7 @@ resource "aws_s3_bucket_policy" "this" { # to prevent "A conflicting conditional operation is currently in progress against this resource." # Ref: https://github.com/hashicorp/terraform-provider-aws/issues/7628 - bucket = aws_s3_bucket.this[0].id + bucket = var.is_directory_bucket ? aws_s3_directory_bucket.this[0].bucket : aws_s3_bucket.this[0].id policy = data.aws_iam_policy_document.combined[0].json depends_on = [ @@ -600,7 +614,7 @@ locals { } data "aws_iam_policy_document" "elb_log_delivery" { - count = local.create_bucket && var.attach_elb_log_delivery_policy ? 1 : 0 + count = local.create_bucket && var.attach_elb_log_delivery_policy && !var.is_directory_bucket ? 1 : 0 # Policy for AWS Regions created before August 2022 (e.g. US East (N. Virginia), Asia Pacific (Singapore), Asia Pacific (Sydney), Asia Pacific (Tokyo), Europe (Ireland)) dynamic "statement" { @@ -649,7 +663,7 @@ data "aws_iam_policy_document" "elb_log_delivery" { # ALB/NLB data "aws_iam_policy_document" "lb_log_delivery" { - count = local.create_bucket && var.attach_lb_log_delivery_policy ? 1 : 0 + count = local.create_bucket && var.attach_lb_log_delivery_policy && !var.is_directory_bucket ? 1 : 0 statement { sid = "AWSLogDeliveryWrite" @@ -702,7 +716,7 @@ data "aws_iam_policy_document" "lb_log_delivery" { # https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-ownership-migrating-acls-prerequisites.html#object-ownership-server-access-logs # https://docs.aws.amazon.com/AmazonS3/latest/userguide/enable-server-access-logging.html#grant-log-delivery-permissions-general data "aws_iam_policy_document" "access_log_delivery" { - count = local.create_bucket && var.attach_access_log_delivery_policy ? 1 : 0 + count = local.create_bucket && var.attach_access_log_delivery_policy && !var.is_directory_bucket ? 1 : 0 statement { sid = "AWSAccessLogDeliveryWrite" @@ -764,7 +778,7 @@ data "aws_iam_policy_document" "access_log_delivery" { } data "aws_iam_policy_document" "deny_insecure_transport" { - count = local.create_bucket && var.attach_deny_insecure_transport_policy ? 1 : 0 + count = local.create_bucket && var.attach_deny_insecure_transport_policy && !var.is_directory_bucket ? 1 : 0 statement { sid = "denyInsecureTransport" @@ -795,7 +809,7 @@ data "aws_iam_policy_document" "deny_insecure_transport" { } data "aws_iam_policy_document" "require_latest_tls" { - count = local.create_bucket && var.attach_require_latest_tls_policy ? 1 : 0 + count = local.create_bucket && var.attach_require_latest_tls_policy && !var.is_directory_bucket ? 1 : 0 statement { sid = "denyOutdatedTLS" @@ -826,7 +840,7 @@ data "aws_iam_policy_document" "require_latest_tls" { } data "aws_iam_policy_document" "deny_incorrect_encryption_headers" { - count = local.create_bucket && var.attach_deny_incorrect_encryption_headers ? 1 : 0 + count = local.create_bucket && var.attach_deny_incorrect_encryption_headers && !var.is_directory_bucket ? 1 : 0 statement { sid = "denyIncorrectEncryptionHeaders" @@ -854,7 +868,7 @@ data "aws_iam_policy_document" "deny_incorrect_encryption_headers" { } data "aws_iam_policy_document" "deny_incorrect_kms_key_sse" { - count = local.create_bucket && var.attach_deny_incorrect_kms_key_sse ? 1 : 0 + count = local.create_bucket && var.attach_deny_incorrect_kms_key_sse && !var.is_directory_bucket ? 1 : 0 statement { sid = "denyIncorrectKmsKeySse" @@ -882,7 +896,7 @@ data "aws_iam_policy_document" "deny_incorrect_kms_key_sse" { } data "aws_iam_policy_document" "deny_unencrypted_object_uploads" { - count = local.create_bucket && var.attach_deny_unencrypted_object_uploads ? 1 : 0 + count = local.create_bucket && var.attach_deny_unencrypted_object_uploads && !var.is_directory_bucket ? 1 : 0 statement { sid = "denyUnencryptedObjectUploads" @@ -910,7 +924,7 @@ data "aws_iam_policy_document" "deny_unencrypted_object_uploads" { } data "aws_iam_policy_document" "deny_ssec_encrypted_object_uploads" { - count = local.create_bucket && var.attach_deny_ssec_encrypted_object_uploads ? 1 : 0 + count = local.create_bucket && var.attach_deny_ssec_encrypted_object_uploads && !var.is_directory_bucket ? 1 : 0 statement { sid = "denySSECEncryptedObjectUploads" @@ -938,7 +952,7 @@ data "aws_iam_policy_document" "deny_ssec_encrypted_object_uploads" { } resource "aws_s3_bucket_public_access_block" "this" { - count = local.create_bucket && var.attach_public_policy ? 1 : 0 + count = local.create_bucket && var.attach_public_policy && !var.is_directory_bucket ? 1 : 0 bucket = aws_s3_bucket.this[0].id @@ -949,7 +963,7 @@ resource "aws_s3_bucket_public_access_block" "this" { } resource "aws_s3_bucket_ownership_controls" "this" { - count = local.create_bucket && var.control_object_ownership ? 1 : 0 + count = local.create_bucket && var.control_object_ownership && !var.is_directory_bucket ? 1 : 0 bucket = local.attach_policy ? aws_s3_bucket_policy.this[0].id : aws_s3_bucket.this[0].id @@ -966,7 +980,7 @@ resource "aws_s3_bucket_ownership_controls" "this" { } resource "aws_s3_bucket_intelligent_tiering_configuration" "this" { - for_each = { for k, v in local.intelligent_tiering : k => v if local.create_bucket } + for_each = { for k, v in local.intelligent_tiering : k => v if local.create_bucket && !var.is_directory_bucket } name = each.key bucket = aws_s3_bucket.this[0].id @@ -994,7 +1008,7 @@ resource "aws_s3_bucket_intelligent_tiering_configuration" "this" { } resource "aws_s3_bucket_metric" "this" { - for_each = { for k, v in local.metric_configuration : k => v if local.create_bucket } + for_each = { for k, v in local.metric_configuration : k => v if local.create_bucket && !var.is_directory_bucket } name = each.value.name bucket = aws_s3_bucket.this[0].id @@ -1009,7 +1023,7 @@ resource "aws_s3_bucket_metric" "this" { } resource "aws_s3_bucket_inventory" "this" { - for_each = { for k, v in var.inventory_configuration : k => v if local.create_bucket } + for_each = { for k, v in var.inventory_configuration : k => v if local.create_bucket && !var.is_directory_bucket } name = each.key bucket = try(each.value.bucket, aws_s3_bucket.this[0].id) @@ -1064,7 +1078,7 @@ resource "aws_s3_bucket_inventory" "this" { # Inventory and analytics destination bucket requires a bucket policy to allow source to PutObjects # https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html#example-bucket-policies-use-case-9 data "aws_iam_policy_document" "inventory_and_analytics_destination_policy" { - count = local.create_bucket && var.attach_inventory_destination_policy || var.attach_analytics_destination_policy ? 1 : 0 + count = local.create_bucket && !var.is_directory_bucket && var.attach_inventory_destination_policy || var.attach_analytics_destination_policy ? 1 : 0 statement { sid = "destinationInventoryAndAnalyticsPolicy" @@ -1110,7 +1124,7 @@ data "aws_iam_policy_document" "inventory_and_analytics_destination_policy" { } resource "aws_s3_bucket_analytics_configuration" "this" { - for_each = { for k, v in var.analytics_configuration : k => v if local.create_bucket } + for_each = { for k, v in var.analytics_configuration : k => v if local.create_bucket && !var.is_directory_bucket } bucket = aws_s3_bucket.this[0].id name = each.key diff --git a/outputs.tf b/outputs.tf index e46a9a2f..58a2d0ab 100644 --- a/outputs.tf +++ b/outputs.tf @@ -47,3 +47,13 @@ output "s3_bucket_website_domain" { description = "The domain of the website endpoint, if the bucket is configured with a website. If not, this will be an empty string. This is used to create Route 53 alias records." value = try(aws_s3_bucket_website_configuration.this[0].website_domain, "") } + +output "s3_directory_bucket_name" { + description = "Name of the directory bucket." + value = try(aws_s3_directory_bucket.this[0].bucket, null) +} + +output "s3_directory_bucket_arn" { + description = "ARN of the directory bucket." + value = try(aws_s3_directory_bucket.this[0].arn, null) +} diff --git a/variables.tf b/variables.tf index 4d3aa95c..912ae4ba 100644 --- a/variables.tf +++ b/variables.tf @@ -322,6 +322,37 @@ variable "object_ownership" { default = "BucketOwnerEnforced" } +# Directory Bucket +variable "is_directory_bucket" { + description = "If the s3 bucket created is a directory bucket" + type = bool + default = false +} + +variable "data_redundancy" { + description = "Data redundancy. Valid values: `SingleAvailabilityZone`" + type = string + default = null +} + +variable "type" { + description = "Bucket type. Valid values: `Directory`" + type = string + default = "Directory" +} + +variable "availability_zone_id" { + description = "Availability Zone ID or Local Zone ID" + type = string + default = null +} + +variable "location_type" { + description = "Location type. Valid values: `AvailabilityZone` or `LocalZone`" + type = string + default = null +} + variable "putin_khuylo" { description = "Do you agree that Putin doesn't respect Ukrainian sovereignty and territorial integrity? More info: https://en.wikipedia.org/wiki/Putin_khuylo!" type = bool diff --git a/versions.tf b/versions.tf index b5d776ce..e0d68841 100644 --- a/versions.tf +++ b/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.70" + version = ">= 5.83" } } } diff --git a/wrappers/main.tf b/wrappers/main.tf index d207d50e..881a23f9 100644 --- a/wrappers/main.tf +++ b/wrappers/main.tf @@ -25,6 +25,7 @@ module "wrapper" { attach_policy = try(each.value.attach_policy, var.defaults.attach_policy, false) attach_public_policy = try(each.value.attach_public_policy, var.defaults.attach_public_policy, true) attach_require_latest_tls_policy = try(each.value.attach_require_latest_tls_policy, var.defaults.attach_require_latest_tls_policy, false) + availability_zone_id = try(each.value.availability_zone_id, var.defaults.availability_zone_id, null) block_public_acls = try(each.value.block_public_acls, var.defaults.block_public_acls, true) block_public_policy = try(each.value.block_public_policy, var.defaults.block_public_policy, true) bucket = try(each.value.bucket, var.defaults.bucket, null) @@ -32,6 +33,7 @@ module "wrapper" { control_object_ownership = try(each.value.control_object_ownership, var.defaults.control_object_ownership, false) cors_rule = try(each.value.cors_rule, var.defaults.cors_rule, []) create_bucket = try(each.value.create_bucket, var.defaults.create_bucket, true) + data_redundancy = try(each.value.data_redundancy, var.defaults.data_redundancy, null) expected_bucket_owner = try(each.value.expected_bucket_owner, var.defaults.expected_bucket_owner, null) force_destroy = try(each.value.force_destroy, var.defaults.force_destroy, false) grant = try(each.value.grant, var.defaults.grant, []) @@ -41,7 +43,9 @@ module "wrapper" { inventory_self_source_destination = try(each.value.inventory_self_source_destination, var.defaults.inventory_self_source_destination, false) inventory_source_account_id = try(each.value.inventory_source_account_id, var.defaults.inventory_source_account_id, null) inventory_source_bucket_arn = try(each.value.inventory_source_bucket_arn, var.defaults.inventory_source_bucket_arn, null) + is_directory_bucket = try(each.value.is_directory_bucket, var.defaults.is_directory_bucket, false) lifecycle_rule = try(each.value.lifecycle_rule, var.defaults.lifecycle_rule, []) + location_type = try(each.value.location_type, var.defaults.location_type, null) logging = try(each.value.logging, var.defaults.logging, {}) metric_configuration = try(each.value.metric_configuration, var.defaults.metric_configuration, []) object_lock_configuration = try(each.value.object_lock_configuration, var.defaults.object_lock_configuration, {}) @@ -56,6 +60,7 @@ module "wrapper" { server_side_encryption_configuration = try(each.value.server_side_encryption_configuration, var.defaults.server_side_encryption_configuration, {}) tags = try(each.value.tags, var.defaults.tags, {}) transition_default_minimum_object_size = try(each.value.transition_default_minimum_object_size, var.defaults.transition_default_minimum_object_size, null) + type = try(each.value.type, var.defaults.type, "Directory") versioning = try(each.value.versioning, var.defaults.versioning, {}) website = try(each.value.website, var.defaults.website, {}) } diff --git a/wrappers/versions.tf b/wrappers/versions.tf index b5d776ce..e0d68841 100644 --- a/wrappers/versions.tf +++ b/wrappers/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.70" + version = ">= 5.83" } } }