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"
}
}
}