diff --git a/.github/workflows/auto-readme.yml b/.github/workflows/auto-readme.yml
new file mode 100644
index 00000000..03232cd6
--- /dev/null
+++ b/.github/workflows/auto-readme.yml
@@ -0,0 +1,55 @@
+name: "auto-readme"
+on:
+ schedule:
+ # Example of job definition:
+ # .---------------- minute (0 - 59)
+ # | .------------- hour (0 - 23)
+ # | | .---------- day of month (1 - 31)
+ # | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
+ # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
+ # | | | | |
+ # * * * * * user-name command to be executed
+
+ # Update README.md nightly at 4am UTC
+ - cron: '0 4 * * *'
+
+jobs:
+ update:
+ if: github.event_name == 'schedule'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Update readme
+ shell: bash
+ id: update
+ env:
+ GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+ run: |
+ make init
+ make readme/build
+ # Ignore changes if they are only whitespace
+ git diff --ignore-all-space --ignore-blank-lines --quiet README.md && { git restore README.md; echo Ignoring whitespace-only changes in README; }
+
+ - name: Create Pull Request
+ # This action will not create or change a pull request if there are no changes to make.
+ # If a PR of the auto-update/readme branch is open, this action will just update it, not create a new PR.
+ uses: cloudposse/actions/github/create-pull-request@0.30.0
+ with:
+ token: ${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }}
+ commit-message: Update README.md and docs
+ title: Update README.md and docs
+ body: |-
+ ## what
+ This is an auto-generated PR that updates the README.md and docs
+
+ ## why
+ To have most recent changes of README.md and doc from origin templates
+
+ branch: auto-update/readme
+ base: main
+ delete-branch: true
+ labels: |
+ auto-update
+ no-release
+ readme
diff --git a/.github/workflows/validate-codeowners.yml b/.github/workflows/validate-codeowners.yml
index c5193b62..70f829e3 100644
--- a/.github/workflows/validate-codeowners.yml
+++ b/.github/workflows/validate-codeowners.yml
@@ -10,7 +10,7 @@ jobs:
steps:
- name: "Checkout source code at current commit"
uses: actions/checkout@v2
- - uses: mszostok/codeowners-validator@v0.5.0
+ - uses: mszostok/codeowners-validator@v0.7.1
if: github.event.pull_request.head.repo.full_name == github.repository
name: "Full check of CODEOWNERS"
with:
@@ -18,10 +18,12 @@ jobs:
# files so we can use the same CODEOWNERS file for Terraform and non-Terraform repos
# checks: "files,syntax,owners,duppatterns"
checks: "syntax,owners,duppatterns"
+ owner_checker_allow_unowned_patterns: "false"
# GitHub access token is required only if the `owners` check is enabled
github_access_token: "${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }}"
- - uses: mszostok/codeowners-validator@v0.5.0
+ - uses: mszostok/codeowners-validator@v0.7.1
if: github.event.pull_request.head.repo.full_name != github.repository
name: "Syntax check of CODEOWNERS"
with:
checks: "syntax,duppatterns"
+ owner_checker_allow_unowned_patterns: "false"
diff --git a/.gitignore b/.gitignore
index ce573e9d..41a3239b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,8 @@
# Build harness files
.build-harness
build-harness
+
+# Editor and merge files
+*.orig
+*.draft
+*~
diff --git a/Makefile b/Makefile
index 6002cb92..b9920609 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
SHELL := /bin/bash
-export TERRAFORM_VERSION = 0.12.3
+export TERRAFORM_VERSION = 1.1.6
# List of targets the `readme` target should call before generating the readme
export README_DEPS ?= docs/targets.md docs/terraform.md
diff --git a/README.md b/README.md
index c3f40345..80bb3d2c 100644
--- a/README.md
+++ b/README.md
@@ -212,15 +212,15 @@ Available targets:
| Name | Version |
|------|---------|
-| [terraform](#requirement\_terraform) | >= 0.13.0 |
-| [aws](#requirement\_aws) | >= 3.68.0 |
+| [terraform](#requirement\_terraform) | >= 1.0 |
+| [aws](#requirement\_aws) | >= 4.2.0 |
| [time](#requirement\_time) | >= 0.7 |
## Providers
| Name | Version |
|------|---------|
-| [aws](#provider\_aws) | >= 3.68.0 |
+| [aws](#provider\_aws) | >= 4.2.0 |
| [time](#provider\_time) | >= 0.7 |
## Modules
@@ -238,10 +238,21 @@ Available targets:
| [aws_iam_role.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_s3_bucket.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
+| [aws_s3_bucket_accelerate_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_accelerate_configuration) | resource |
+| [aws_s3_bucket_acl.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
+| [aws_s3_bucket_cors_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_cors_configuration) | resource |
+| [aws_s3_bucket_lifecycle_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource |
+| [aws_s3_bucket_logging.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource |
+| [aws_s3_bucket_object_lock_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_object_lock_configuration) | resource |
| [aws_s3_bucket_ownership_controls.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource |
| [aws_s3_bucket_policy.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_public_access_block.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
+| [aws_s3_bucket_replication_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_replication_configuration) | resource |
+| [aws_s3_bucket_server_side_encryption_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
+| [aws_s3_bucket_versioning.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
+| [aws_s3_bucket_website_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_website_configuration) | resource |
| [time_sleep.wait_for_aws_s3_bucket_settings](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource |
+| [aws_canonical_user_id.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/canonical_user_id) | data source |
| [aws_iam_policy_document.aggregated_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
@@ -253,6 +264,7 @@ Available targets:
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| [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\_grants](#input\_acl\_grants) | A list of policy grants for the bucket. Conflicts with `acl`. Set `acl` to `null` to use this. |
list(object({
id = string
type = string
permission = string
uri = string
}))
| `null` | 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 |
@@ -269,7 +281,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) | A boolean string 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 |
-| [grants](#input\_grants) | An ACL policy grant. Conflicts with `acl`. Set `acl` to `null` to use this. | list(object({
id = string
type = string
permissions = list(string)
uri = string
}))
| `null` | no |
+| [grants](#input\_grants) | DEPRECATED (replaced by `acl_grants`): A list of policy grants for the bucket. Conflicts with `acl`. Set `acl` to `null` to use this. | list(object({
id = string
type = string
permissions = list(string)
uri = string
}))
| `null` | no |
| [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no |
| [ignore\_public\_acls](#input\_ignore\_public\_acls) | Set to `false` to disable the ignoring of public access lists on the bucket | `bool` | `true` | no |
| [kms\_master\_key\_arn](#input\_kms\_master\_key\_arn) | The AWS KMS master key ARN used for the `SSE-KMS` encryption. This can only be used when you set the value of `sse_algorithm` as `aws:kms`. The default aws/s3 AWS KMS master key is used if this element is absent while the `sse_algorithm` is `aws:kms` | `string` | `""` | no |
@@ -277,12 +289,13 @@ Available targets:
| [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. | `list(string)` | `null` | no |
| [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper` and `none` (no transformation).
Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
Default value: `lower`. | `string` | `null` | no |
| [labels\_as\_tags](#input\_labels\_as\_tags) | Set of labels (ID elements) to include as tags in the `tags` output.
Default is to include all labels.
Tags with empty values will not be included in the `tags` output.
Set to `[]` to suppress all generated tags.
**Notes:**
The value of the `name` tag, if included, will be the `id`, not the `name`.
Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be
changed in later chained modules. Attempts to change it will be silently ignored. | `set(string)` | [
"default"
]
| no |
-| [lifecycle\_rules](#input\_lifecycle\_rules) | A list of lifecycle rules | list(object({
prefix = string
enabled = bool
tags = map(string)
enable_glacier_transition = bool
enable_deeparchive_transition = bool
enable_standard_ia_transition = bool
enable_current_object_expiration = bool
enable_noncurrent_version_expiration = bool
abort_incomplete_multipart_upload_days = number
noncurrent_version_glacier_transition_days = number
noncurrent_version_deeparchive_transition_days = number
noncurrent_version_expiration_days = number
standard_transition_days = number
glacier_transition_days = number
deeparchive_transition_days = number
expiration_days = number
}))
| [
{
"abort_incomplete_multipart_upload_days": 90,
"deeparchive_transition_days": 90,
"enable_current_object_expiration": true,
"enable_deeparchive_transition": false,
"enable_glacier_transition": true,
"enable_noncurrent_version_expiration": true,
"enable_standard_ia_transition": false,
"enabled": false,
"expiration_days": 90,
"glacier_transition_days": 60,
"noncurrent_version_deeparchive_transition_days": 60,
"noncurrent_version_expiration_days": 90,
"noncurrent_version_glacier_transition_days": 30,
"prefix": "",
"standard_transition_days": 30,
"tags": {}
}
]
| no |
+| [lifecycle\_configuration\_rules](#input\_lifecycle\_configuration\_rules) | A list of lifecycle rules | list(object({
id = string
prefix = string
enabled = bool
tags = map(string)
enable_glacier_transition = bool
enable_deeparchive_transition = bool
enable_standard_ia_transition = bool
enable_current_object_expiration = bool
enable_noncurrent_version_expiration = bool
abort_incomplete_multipart_upload_days = number
noncurrent_version_glacier_transition_days = number
noncurrent_version_deeparchive_transition_days = number
noncurrent_version_expiration_days = number
standard_transition_days = number
glacier_transition_days = number
deeparchive_transition_days = number
expiration_days = number
}))
| [
{
"abort_incomplete_multipart_upload_days": 90,
"deeparchive_transition_days": 90,
"enable_current_object_expiration": true,
"enable_deeparchive_transition": false,
"enable_glacier_transition": true,
"enable_noncurrent_version_expiration": true,
"enable_standard_ia_transition": false,
"enabled": false,
"expiration_days": 90,
"glacier_transition_days": 60,
"id": "noop",
"noncurrent_version_deeparchive_transition_days": 60,
"noncurrent_version_expiration_days": 90,
"noncurrent_version_glacier_transition_days": 30,
"prefix": "",
"standard_transition_days": 30,
"tags": {}
}
]
| no |
+| [lifecycle\_rules](#input\_lifecycle\_rules) | DEPRECATED: A list of lifecycle rules | list(object({
prefix = string
enabled = bool
tags = map(string)
enable_glacier_transition = bool
enable_deeparchive_transition = bool
enable_standard_ia_transition = bool
enable_current_object_expiration = bool
enable_noncurrent_version_expiration = bool
abort_incomplete_multipart_upload_days = number
noncurrent_version_glacier_transition_days = number
noncurrent_version_deeparchive_transition_days = number
noncurrent_version_expiration_days = number
standard_transition_days = number
glacier_transition_days = number
deeparchive_transition_days = number
expiration_days = number
}))
| `null` | no |
| [logging](#input\_logging) | Bucket access logging configuration. | object({
bucket_name = string
prefix = string
})
| `null` | no |
| [name](#input\_name) | ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
This is the only ID element not also included as a `tag`.
The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. | `string` | `null` | no |
| [namespace](#input\_namespace) | ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique | `string` | `null` | no |
| [object\_lock\_configuration](#input\_object\_lock\_configuration) | A configuration for S3 object locking. With S3 Object Lock, you can store objects using a `write once, read many` (WORM) model. Object Lock can help prevent objects from being deleted or overwritten for a fixed amount of time or indefinitely. | object({
mode = string # Valid values are GOVERNANCE and COMPLIANCE.
days = number
years = number
})
| `null` | no |
-| [policy](#input\_policy) | A valid bucket policy JSON document. Note that if the policy document is not specific enough (but still valid), Terraform may view the policy as constantly changing in a terraform plan. In this case, please make sure you use the verbose/specific version of the policy | `string` | `""` | no |
+| [policy](#input\_policy) | DEPRECATED: A valid bucket policy JSON document. Note that if the policy document is not specific enough (but still valid), Terraform may view the policy as constantly changing in a terraform plan. In this case, please make sure you use the verbose/specific version of the policy | `string` | `""` | no |
| [privileged\_principal\_actions](#input\_privileged\_principal\_actions) | List of actions to permit `privileged_principal_arns` to perform on bucket and bucket prefixes (see `privileged_principal_arns`) | `list(string)` | `[]` | no |
| [privileged\_principal\_arns](#input\_privileged\_principal\_arns) | (Optional) Map of IAM Principal ARNs to lists of S3 path prefixes to grant `privileged_principal_actions` permissions.
Resource list will include the bucket itself along with all the prefixes. Prefixes should not begin with '/'. | `map(list(string))` | `{}` | no |
| [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no |
@@ -293,6 +306,7 @@ Available targets:
| [s3\_replication\_enabled](#input\_s3\_replication\_enabled) | Set this to true and specify `s3_replication_rules` to enable replication. `versioning_enabled` must also be `true`. | `bool` | `false` | no |
| [s3\_replication\_rules](#input\_s3\_replication\_rules) | Specifies the replication rules for S3 bucket replication if enabled. You must also set s3\_replication\_enabled to true. | `list(any)` | `null` | no |
| [s3\_replication\_source\_roles](#input\_s3\_replication\_source\_roles) | 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). | `list(string)` | `[]` | no |
+| [source\_policy\_documents](#input\_source\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements defined in source\_policy\_documents or source\_json must have unique sids. Statements with the same sid from documents assigned to the override\_json and override\_policy\_documents arguments will override source statements. | `list(string)` | `[]` | no |
| [sse\_algorithm](#input\_sse\_algorithm) | The server-side encryption algorithm to use. Valid values are `AES256` and `aws:kms` | `string` | `"AES256"` | no |
| [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no |
| [tags](#input\_tags) | Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`).
Neither the tag keys nor the tag values will be modified by this module. | `map(string)` | `{}` | no |
diff --git a/docs/terraform.md b/docs/terraform.md
index c688ee51..63d2ea47 100644
--- a/docs/terraform.md
+++ b/docs/terraform.md
@@ -3,15 +3,15 @@
| Name | Version |
|------|---------|
-| [terraform](#requirement\_terraform) | >= 0.13.0 |
-| [aws](#requirement\_aws) | >= 3.68.0 |
+| [terraform](#requirement\_terraform) | >= 1.0 |
+| [aws](#requirement\_aws) | >= 4.2.0 |
| [time](#requirement\_time) | >= 0.7 |
## Providers
| Name | Version |
|------|---------|
-| [aws](#provider\_aws) | >= 3.68.0 |
+| [aws](#provider\_aws) | >= 4.2.0 |
| [time](#provider\_time) | >= 0.7 |
## Modules
@@ -29,10 +29,21 @@
| [aws_iam_role.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_s3_bucket.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
+| [aws_s3_bucket_accelerate_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_accelerate_configuration) | resource |
+| [aws_s3_bucket_acl.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
+| [aws_s3_bucket_cors_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_cors_configuration) | resource |
+| [aws_s3_bucket_lifecycle_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource |
+| [aws_s3_bucket_logging.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource |
+| [aws_s3_bucket_object_lock_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_object_lock_configuration) | resource |
| [aws_s3_bucket_ownership_controls.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource |
| [aws_s3_bucket_policy.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_public_access_block.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
+| [aws_s3_bucket_replication_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_replication_configuration) | resource |
+| [aws_s3_bucket_server_side_encryption_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
+| [aws_s3_bucket_versioning.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
+| [aws_s3_bucket_website_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_website_configuration) | resource |
| [time_sleep.wait_for_aws_s3_bucket_settings](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource |
+| [aws_canonical_user_id.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/canonical_user_id) | data source |
| [aws_iam_policy_document.aggregated_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
@@ -44,6 +55,7 @@
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| [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\_grants](#input\_acl\_grants) | A list of policy grants for the bucket. Conflicts with `acl`. Set `acl` to `null` to use this. | list(object({
id = string
type = string
permission = string
uri = string
}))
| `null` | 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 |
@@ -60,7 +72,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) | A boolean string 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 |
-| [grants](#input\_grants) | An ACL policy grant. Conflicts with `acl`. Set `acl` to `null` to use this. | list(object({
id = string
type = string
permissions = list(string)
uri = string
}))
| `null` | no |
+| [grants](#input\_grants) | DEPRECATED (replaced by `acl_grants`): A list of policy grants for the bucket. Conflicts with `acl`. Set `acl` to `null` to use this. | list(object({
id = string
type = string
permissions = list(string)
uri = string
}))
| `null` | no |
| [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no |
| [ignore\_public\_acls](#input\_ignore\_public\_acls) | Set to `false` to disable the ignoring of public access lists on the bucket | `bool` | `true` | no |
| [kms\_master\_key\_arn](#input\_kms\_master\_key\_arn) | The AWS KMS master key ARN used for the `SSE-KMS` encryption. This can only be used when you set the value of `sse_algorithm` as `aws:kms`. The default aws/s3 AWS KMS master key is used if this element is absent while the `sse_algorithm` is `aws:kms` | `string` | `""` | no |
@@ -68,12 +80,13 @@
| [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. | `list(string)` | `null` | no |
| [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper` and `none` (no transformation).
Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
Default value: `lower`. | `string` | `null` | no |
| [labels\_as\_tags](#input\_labels\_as\_tags) | Set of labels (ID elements) to include as tags in the `tags` output.
Default is to include all labels.
Tags with empty values will not be included in the `tags` output.
Set to `[]` to suppress all generated tags.
**Notes:**
The value of the `name` tag, if included, will be the `id`, not the `name`.
Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be
changed in later chained modules. Attempts to change it will be silently ignored. | `set(string)` | [
"default"
]
| no |
-| [lifecycle\_rules](#input\_lifecycle\_rules) | A list of lifecycle rules | list(object({
prefix = string
enabled = bool
tags = map(string)
enable_glacier_transition = bool
enable_deeparchive_transition = bool
enable_standard_ia_transition = bool
enable_current_object_expiration = bool
enable_noncurrent_version_expiration = bool
abort_incomplete_multipart_upload_days = number
noncurrent_version_glacier_transition_days = number
noncurrent_version_deeparchive_transition_days = number
noncurrent_version_expiration_days = number
standard_transition_days = number
glacier_transition_days = number
deeparchive_transition_days = number
expiration_days = number
}))
| [
{
"abort_incomplete_multipart_upload_days": 90,
"deeparchive_transition_days": 90,
"enable_current_object_expiration": true,
"enable_deeparchive_transition": false,
"enable_glacier_transition": true,
"enable_noncurrent_version_expiration": true,
"enable_standard_ia_transition": false,
"enabled": false,
"expiration_days": 90,
"glacier_transition_days": 60,
"noncurrent_version_deeparchive_transition_days": 60,
"noncurrent_version_expiration_days": 90,
"noncurrent_version_glacier_transition_days": 30,
"prefix": "",
"standard_transition_days": 30,
"tags": {}
}
]
| no |
+| [lifecycle\_configuration\_rules](#input\_lifecycle\_configuration\_rules) | A list of lifecycle rules | list(object({
id = string
prefix = string
enabled = bool
tags = map(string)
enable_glacier_transition = bool
enable_deeparchive_transition = bool
enable_standard_ia_transition = bool
enable_current_object_expiration = bool
enable_noncurrent_version_expiration = bool
abort_incomplete_multipart_upload_days = number
noncurrent_version_glacier_transition_days = number
noncurrent_version_deeparchive_transition_days = number
noncurrent_version_expiration_days = number
standard_transition_days = number
glacier_transition_days = number
deeparchive_transition_days = number
expiration_days = number
}))
| [
{
"abort_incomplete_multipart_upload_days": 90,
"deeparchive_transition_days": 90,
"enable_current_object_expiration": true,
"enable_deeparchive_transition": false,
"enable_glacier_transition": true,
"enable_noncurrent_version_expiration": true,
"enable_standard_ia_transition": false,
"enabled": false,
"expiration_days": 90,
"glacier_transition_days": 60,
"id": "noop",
"noncurrent_version_deeparchive_transition_days": 60,
"noncurrent_version_expiration_days": 90,
"noncurrent_version_glacier_transition_days": 30,
"prefix": "",
"standard_transition_days": 30,
"tags": {}
}
]
| no |
+| [lifecycle\_rules](#input\_lifecycle\_rules) | DEPRECATED: A list of lifecycle rules | list(object({
prefix = string
enabled = bool
tags = map(string)
enable_glacier_transition = bool
enable_deeparchive_transition = bool
enable_standard_ia_transition = bool
enable_current_object_expiration = bool
enable_noncurrent_version_expiration = bool
abort_incomplete_multipart_upload_days = number
noncurrent_version_glacier_transition_days = number
noncurrent_version_deeparchive_transition_days = number
noncurrent_version_expiration_days = number
standard_transition_days = number
glacier_transition_days = number
deeparchive_transition_days = number
expiration_days = number
}))
| `null` | no |
| [logging](#input\_logging) | Bucket access logging configuration. | object({
bucket_name = string
prefix = string
})
| `null` | no |
| [name](#input\_name) | ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
This is the only ID element not also included as a `tag`.
The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. | `string` | `null` | no |
| [namespace](#input\_namespace) | ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique | `string` | `null` | no |
| [object\_lock\_configuration](#input\_object\_lock\_configuration) | A configuration for S3 object locking. With S3 Object Lock, you can store objects using a `write once, read many` (WORM) model. Object Lock can help prevent objects from being deleted or overwritten for a fixed amount of time or indefinitely. | object({
mode = string # Valid values are GOVERNANCE and COMPLIANCE.
days = number
years = number
})
| `null` | no |
-| [policy](#input\_policy) | A valid bucket policy JSON document. Note that if the policy document is not specific enough (but still valid), Terraform may view the policy as constantly changing in a terraform plan. In this case, please make sure you use the verbose/specific version of the policy | `string` | `""` | no |
+| [policy](#input\_policy) | DEPRECATED: A valid bucket policy JSON document. Note that if the policy document is not specific enough (but still valid), Terraform may view the policy as constantly changing in a terraform plan. In this case, please make sure you use the verbose/specific version of the policy | `string` | `""` | no |
| [privileged\_principal\_actions](#input\_privileged\_principal\_actions) | List of actions to permit `privileged_principal_arns` to perform on bucket and bucket prefixes (see `privileged_principal_arns`) | `list(string)` | `[]` | no |
| [privileged\_principal\_arns](#input\_privileged\_principal\_arns) | (Optional) Map of IAM Principal ARNs to lists of S3 path prefixes to grant `privileged_principal_actions` permissions.
Resource list will include the bucket itself along with all the prefixes. Prefixes should not begin with '/'. | `map(list(string))` | `{}` | no |
| [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no |
@@ -84,6 +97,7 @@
| [s3\_replication\_enabled](#input\_s3\_replication\_enabled) | Set this to true and specify `s3_replication_rules` to enable replication. `versioning_enabled` must also be `true`. | `bool` | `false` | no |
| [s3\_replication\_rules](#input\_s3\_replication\_rules) | Specifies the replication rules for S3 bucket replication if enabled. You must also set s3\_replication\_enabled to true. | `list(any)` | `null` | no |
| [s3\_replication\_source\_roles](#input\_s3\_replication\_source\_roles) | 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). | `list(string)` | `[]` | no |
+| [source\_policy\_documents](#input\_source\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements defined in source\_policy\_documents or source\_json must have unique sids. Statements with the same sid from documents assigned to the override\_json and override\_policy\_documents arguments will override source statements. | `list(string)` | `[]` | no |
| [sse\_algorithm](#input\_sse\_algorithm) | The server-side encryption algorithm to use. Valid values are `AES256` and `aws:kms` | `string` | `"AES256"` | no |
| [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no |
| [tags](#input\_tags) | Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`).
Neither the tag keys nor the tag values will be modified by this module. | `map(string)` | `{}` | no |
diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf
index f080d322..3520e45f 100644
--- a/examples/complete/variables.tf
+++ b/examples/complete/variables.tf
@@ -18,8 +18,8 @@ variable "grants" {
variable "lifecycle_rules" {
type = list(object({
- enabled = bool
prefix = string
+ enabled = bool
tags = map(string)
enable_glacier_transition = bool
@@ -39,8 +39,8 @@ variable "lifecycle_rules" {
expiration_days = number
}))
default = [{
- enabled = false
prefix = ""
+ enabled = false
tags = {}
enable_glacier_transition = true
@@ -72,7 +72,13 @@ variable "s3_replication_enabled" {
variable "policy" {
type = string
default = ""
- description = "A valid bucket policy JSON document. Note that if the policy document is not specific enough (but still valid), Terraform may view the policy as constantly changing in a terraform plan. In this case, please make sure you use the verbose/specific version of the policy"
+ description = "DEPRECATED: Use source_policy_documents instead. A valid bucket policy JSON document. Note that if the policy document is not specific enough (but still valid), Terraform may view the policy as constantly changing in a terraform plan. In this case, please make sure you use the verbose/specific version of the policy"
+}
+
+variable "source_policy_documents" {
+ type = list(string)
+ default = null
+ description = "List of IAM policy documents that are merged together into the exported document. Statements defined in source_policy_documents or source_json must have unique sids. Statements with the same sid from documents assigned to the override_json and override_policy_documents arguments will override source statements."
}
variable "region" {
@@ -256,4 +262,4 @@ variable "bucket_key_enabled" {
For more information, see: https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-key.html
EOT
-}
\ No newline at end of file
+}
diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf
new file mode 100644
index 00000000..a5c83197
--- /dev/null
+++ b/examples/complete/versions.tf
@@ -0,0 +1,14 @@
+terraform {
+ required_version = ">= 0.13.0"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 4.0.0"
+ }
+ time = {
+ source = "hashicorp/time"
+ version = ">= 0.7"
+ }
+ }
+}
diff --git a/main.tf b/main.tf
index c0e6ce25..7c890fa6 100644
--- a/main.tf
+++ b/main.tf
@@ -1,8 +1,12 @@
locals {
- enabled = module.this.enabled
- bucket_name = var.bucket_name != null && var.bucket_name != "" ? var.bucket_name : module.this.id
- replication_enabled = local.enabled && var.s3_replication_enabled
- bucket_arn = "arn:${data.aws_partition.current.partition}:s3:::${join("", aws_s3_bucket.default.*.id)}"
+ enabled = module.this.enabled
+
+ replication_enabled = local.enabled && var.s3_replication_enabled
+ versioning_enabled = local.enabled && var.versioning_enabled
+ transfer_acceleration_enabled = local.enabled && var.transfer_acceleration_enabled
+
+ bucket_name = var.bucket_name != null && var.bucket_name != "" ? var.bucket_name : module.this.id
+ bucket_arn = "arn:${data.aws_partition.current.partition}:s3:::${join("", aws_s3_bucket.default.*.id)}"
# Deprecate `replication_rules` in favor of `s3_replication_rules` to keep all the replication related
# inputs grouped under s3_replica[tion]
@@ -17,126 +21,205 @@ resource "aws_s3_bucket" "default" {
#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
- count = local.enabled ? 1 : 0
- bucket = local.bucket_name
- acl = try(length(var.grants), 0) == 0 ? var.acl : null
- force_destroy = var.force_destroy
- tags = module.this.tags
- acceleration_status = var.transfer_acceleration_enabled ? "Enabled" : null
-
- dynamic "versioning" {
- for_each = var.versioning_enabled ? [true] : []
+ #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
+ force_destroy = var.force_destroy
+
+ dynamic "object_lock_configuration" {
+ for_each = var.object_lock_configuration != null ? [1] : []
+
content {
- enabled = true
+ object_lock_enabled = "Enabled"
}
}
- dynamic "lifecycle_rule" {
- for_each = var.lifecycle_rules
+ tags = module.this.tags
+}
+
+resource "aws_s3_bucket_accelerate_configuration" "default" {
+ count = local.transfer_acceleration_enabled ? 1 : 0
+ bucket = join("", aws_s3_bucket.default.*.id)
+ status = "Enabled"
+}
+
+resource "aws_s3_bucket_versioning" "default" {
+ count = local.versioning_enabled ? 1 : 0
+
+ bucket = join("", aws_s3_bucket.default.*.id)
+
+ versioning_configuration {
+ status = "Enabled"
+ }
+}
+
+resource "aws_s3_bucket_lifecycle_configuration" "default" {
+ count = local.enabled && length(local.lifecycle_configuration_rules) > 0 ? 1 : 0
+ bucket = join("", aws_s3_bucket.default.*.id)
+
+ dynamic "rule" {
+ for_each = local.lifecycle_configuration_rules
+
content {
- enabled = lifecycle_rule.value.enabled
- prefix = lifecycle_rule.value.prefix
- tags = lifecycle_rule.value.tags
- abort_incomplete_multipart_upload_days = lifecycle_rule.value.abort_incomplete_multipart_upload_days
+ id = rule.value.id
+ status = rule.value.enabled == true ? "Enabled" : "Disabled"
+
+ # Filter is always required due to https://github.com/hashicorp/terraform-provider-aws/issues/23299
+ filter {
+ dynamic "and" {
+ for_each = (try(length(rule.value.prefix), 0) + try(length(rule.value.tags), 0)) > 0 ? [1] : []
+ content {
+ prefix = rule.value.prefix == null ? "" : rule.value.prefix
+ tags = try(length(rule.value.tags), 0) > 0 ? rule.value.tags : {}
+ }
+ }
+ }
+
+ dynamic "abort_incomplete_multipart_upload" {
+ for_each = try(tonumber(rule.value.abort_incomplete_multipart_upload_days), null) != null ? [1] : []
+ content {
+ days_after_initiation = rule.value.abort_incomplete_multipart_upload_days
+ }
+ }
dynamic "noncurrent_version_expiration" {
- for_each = lifecycle_rule.value.enable_noncurrent_version_expiration ? [1] : []
+ for_each = rule.value.enable_noncurrent_version_expiration ? [1] : []
content {
- days = lifecycle_rule.value.noncurrent_version_expiration_days
+ noncurrent_days = rule.value.noncurrent_version_expiration_days
}
}
dynamic "noncurrent_version_transition" {
- for_each = lifecycle_rule.value.enable_glacier_transition ? [1] : []
+ for_each = rule.value.enable_glacier_transition ? [1] : []
content {
- days = lifecycle_rule.value.noncurrent_version_glacier_transition_days
- storage_class = "GLACIER"
+ noncurrent_days = rule.value.noncurrent_version_glacier_transition_days
+ storage_class = "GLACIER"
}
}
dynamic "noncurrent_version_transition" {
- for_each = lifecycle_rule.value.enable_deeparchive_transition ? [1] : []
+ for_each = rule.value.enable_deeparchive_transition ? [1] : []
content {
- days = lifecycle_rule.value.noncurrent_version_deeparchive_transition_days
- storage_class = "DEEP_ARCHIVE"
+ noncurrent_days = rule.value.noncurrent_version_deeparchive_transition_days
+ storage_class = "DEEP_ARCHIVE"
}
}
dynamic "transition" {
- for_each = lifecycle_rule.value.enable_glacier_transition ? [1] : []
+ for_each = rule.value.enable_glacier_transition ? [1] : []
content {
- days = lifecycle_rule.value.glacier_transition_days
+ days = rule.value.glacier_transition_days
storage_class = "GLACIER"
}
}
dynamic "transition" {
- for_each = lifecycle_rule.value.enable_deeparchive_transition ? [1] : []
+ for_each = rule.value.enable_deeparchive_transition ? [1] : []
content {
- days = lifecycle_rule.value.deeparchive_transition_days
+ days = rule.value.deeparchive_transition_days
storage_class = "DEEP_ARCHIVE"
}
}
-
-
dynamic "transition" {
- for_each = lifecycle_rule.value.enable_standard_ia_transition ? [1] : []
+ for_each = rule.value.enable_standard_ia_transition ? [1] : []
content {
- days = lifecycle_rule.value.standard_transition_days
+ days = rule.value.standard_transition_days
storage_class = "STANDARD_IA"
}
}
dynamic "expiration" {
- for_each = lifecycle_rule.value.enable_current_object_expiration ? [1] : []
+ for_each = rule.value.enable_current_object_expiration ? [1] : []
content {
- days = lifecycle_rule.value.expiration_days
+ days = rule.value.expiration_days
}
}
}
}
- dynamic "logging" {
- for_each = var.logging == null ? [] : [1]
- content {
- target_bucket = var.logging["bucket_name"]
- target_prefix = var.logging["prefix"]
+ depends_on = [
+ # versioning must be set before lifecycle configuration
+ aws_s3_bucket_versioning.default
+ ]
+}
+
+resource "aws_s3_bucket_logging" "default" {
+ count = local.enabled && var.logging != null ? 1 : 0
+ bucket = join("", aws_s3_bucket.default.*.id)
+
+ target_bucket = var.logging["bucket_name"]
+ target_prefix = var.logging["prefix"]
+}
+
+# https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-encryption.html
+# https://www.terraform.io/docs/providers/aws/r/s3_bucket.html#enable-default-server-side-encryption
+resource "aws_s3_bucket_server_side_encryption_configuration" "default" {
+ count = local.enabled ? 1 : 0
+ bucket = join("", aws_s3_bucket.default.*.id)
+
+ rule {
+ bucket_key_enabled = var.bucket_key_enabled
+
+ apply_server_side_encryption_by_default {
+ sse_algorithm = var.sse_algorithm
+ kms_master_key_id = var.kms_master_key_arn
}
}
+}
+
+resource "aws_s3_bucket_website_configuration" "default" {
+ for_each = local.enabled && var.website_inputs != null ? toset(var.website_inputs) : toset([])
+ bucket = join("", aws_s3_bucket.default.*.id)
+
+ index_document {
+ suffix = each.value.index_document
+ }
- # https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-encryption.html
- # https://www.terraform.io/docs/providers/aws/r/s3_bucket.html#enable-default-server-side-encryption
- server_side_encryption_configuration {
- rule {
- bucket_key_enabled = var.bucket_key_enabled
+ error_document {
+ key = each.value.error_document
+ }
- apply_server_side_encryption_by_default {
- sse_algorithm = var.sse_algorithm
- kms_master_key_id = var.kms_master_key_arn
- }
- }
+ redirect_all_requests_to {
+ host_name = each.value.redirect_all_requests_to
+ protocol = each.value.protocol
}
- dynamic "website" {
- for_each = var.website_inputs == null ? [] : var.website_inputs
+ dynamic "routing_rule" {
+ for_each = length(jsondecode(each.value.routing_rules)) > 0 ? jsondecode(each.value.routing_rules) : []
content {
- index_document = website.value.index_document
- error_document = website.value.error_document
- redirect_all_requests_to = website.value.redirect_all_requests_to
- routing_rules = website.value.routing_rules
+ dynamic "condition" {
+ for_each = routing_rule.value["Condition"]
+
+ content {
+ key_prefix_equals = lookup(condition.value, "KeyPrefixEquals")
+ }
+ }
+
+ dynamic "redirect" {
+ for_each = routing_rule.value["Redirect"]
+ content {
+ replace_key_prefix_with = lookup(redirect.value, "ReplaceKeyPrefixWith")
+ }
+ }
}
}
+}
+
+resource "aws_s3_bucket_cors_configuration" "default" {
+ count = local.enabled && var.cors_rule_inputs != null ? 1 : 0
+
+ bucket = join("", aws_s3_bucket.default.*.id)
dynamic "cors_rule" {
- for_each = var.cors_rule_inputs == null ? [] : var.cors_rule_inputs
+ for_each = var.cors_rule_inputs
content {
allowed_headers = cors_rule.value.allowed_headers
@@ -146,96 +229,142 @@ resource "aws_s3_bucket" "default" {
max_age_seconds = cors_rule.value.max_age_seconds
}
}
+}
- dynamic "grant" {
- for_each = try(length(var.grants), 0) == 0 || try(length(var.acl), 0) > 0 ? [] : var.grants
+data "aws_canonical_user_id" "default" {
+ count = local.enabled ? 1 : 0
+}
+
+resource "aws_s3_bucket_acl" "default" {
+ count = local.enabled ? 1 : 0
+ bucket = join("", aws_s3_bucket.default.*.id)
+
+ # Conflicts with access_control_policy so this is enabled if no grants
+ acl = try(length(local.acl_grants), 0) == 0 ? var.acl : null
+
+ dynamic "access_control_policy" {
+ for_each = try(length(local.acl_grants), 0) == 0 || try(length(var.acl), 0) > 0 ? [] : [1]
content {
- id = grant.value.id
- type = grant.value.type
- permissions = grant.value.permissions
- uri = grant.value.uri
+ dynamic "grant" {
+ for_each = local.acl_grants
+
+ content {
+ grantee {
+ id = grant.value.id
+ type = grant.value.type
+ uri = grant.value.uri
+ }
+ permission = grant.value.permission
+ }
+ }
+
+ owner {
+ id = join("", data.aws_canonical_user_id.default.*.id)
+ }
}
}
+}
- dynamic "replication_configuration" {
- for_each = local.replication_enabled ? [1] : []
+resource "aws_s3_bucket_replication_configuration" "default" {
+ count = local.replication_enabled ? 1 : 0
+
+ bucket = join("", aws_s3_bucket.default.*.id)
+ role = aws_iam_role.replication[0].arn
+
+ dynamic "rule" {
+ for_each = local.s3_replication_rules == null ? [] : local.s3_replication_rules
content {
- role = aws_iam_role.replication[0].arn
+ id = rule.value.id
+ priority = try(rule.value.priority, 0)
+
+ # `prefix` at this level is a V1 feature, replaced in V2 with the filter block.
+ # `prefix` conflicts with `filter`, and for multiple destinations, a filter block
+ # is required even if it empty, so we always implement `prefix` as a filter.
+ # OBSOLETE: prefix = try(rule.value.prefix, null)
+ status = try(rule.value.status, null)
+
+ # This is only relevant when "filter" is used
+ delete_marker_replication {
+ status = try(rule.value.delete_marker_replication_status, "Disabled")
+ }
- dynamic "rules" {
- for_each = local.s3_replication_rules == null ? [] : local.s3_replication_rules
+ destination {
+ # Prefer newer system of specifying bucket in rule, but maintain backward compatibility with
+ # s3_replica_bucket_arn to specify single destination for all rules
+ bucket = try(length(rule.value.destination_bucket), 0) > 0 ? rule.value.destination_bucket : var.s3_replica_bucket_arn
+ storage_class = try(rule.value.destination.storage_class, "STANDARD")
- content {
- id = rules.value.id
- priority = try(rules.value.priority, 0)
- # `prefix` at this level is a V1 feature, replaced in V2 with the filter block.
- # `prefix` conflicts with `filter`, and for multiple destinations, a filter block
- # is required even if it empty, so we always implement `prefix` as a filter.
- # OBSOLETE: prefix = try(rules.value.prefix, null)
- status = try(rules.value.status, null)
- # The `Delete marker replication` was disabled by default since empty filter created in Line 210, this needed to be "Enabled" to turn it on
- delete_marker_replication_status = try(rules.value.delete_marker_replication_status, null)
-
- destination {
- # Prefer newer system of specifying bucket in rule, but maintain backward compatibility with
- # s3_replica_bucket_arn to specify single destination for all rules
- bucket = try(length(rules.value.destination_bucket), 0) > 0 ? rules.value.destination_bucket : var.s3_replica_bucket_arn
- storage_class = try(rules.value.destination.storage_class, "STANDARD")
- replica_kms_key_id = try(rules.value.destination.replica_kms_key_id, null)
- account_id = try(rules.value.destination.account_id, null)
-
- # https://docs.aws.amazon.com/AmazonS3/latest/userguide/replication-walkthrough-5.html
- dynamic "metrics" {
- for_each = try(rules.value.destination.metrics.status, "") == "Enabled" ? [1] : []
-
- content {
- status = "Enabled"
- # Minutes can only have 15 as a valid value.
- minutes = 15
- }
- }
+ dynamic "encryption_configuration" {
+ for_each = try(rule.value.destination.replica_kms_key_id, null) != null ? [1] : []
- # This block is required when replication metrics are enabled.
- dynamic "replication_time" {
- for_each = try(rules.value.destination.metrics.status, "") == "Enabled" ? [1] : []
+ content {
+ replica_kms_key_id = try(rule.value.destination.replica_kms_key_id, null)
+ }
+ }
- content {
- status = "Enabled"
- # Minutes can only have 15 as a valid value.
- minutes = 15
- }
- }
+ account = try(rule.value.destination.account_id, null)
- dynamic "access_control_translation" {
- for_each = try(rules.value.destination.access_control_translation.owner, null) == null ? [] : [rules.value.destination.access_control_translation.owner]
+ # https://docs.aws.amazon.com/AmazonS3/latest/userguide/replication-walkthrough-5.html
+ dynamic "metrics" {
+ for_each = try(rule.value.destination.metrics.status, "") == "Enabled" ? [1] : []
- content {
- owner = access_control_translation.value
- }
+ content {
+ status = "Enabled"
+ event_threshold {
+ # Minutes can only have 15 as a valid value.
+ minutes = 15
}
}
+ }
- dynamic "source_selection_criteria" {
- for_each = try(rules.value.source_selection_criteria.sse_kms_encrypted_objects.enabled, null) == null ? [] : [rules.value.source_selection_criteria.sse_kms_encrypted_objects.enabled]
+ # This block is required when replication metrics are enabled.
+ dynamic "replication_time" {
+ for_each = try(rule.value.destination.metrics.status, "") == "Enabled" ? [1] : []
- content {
- sse_kms_encrypted_objects {
- enabled = source_selection_criteria.value
- }
+ content {
+ status = "Enabled"
+ time {
+ # Minutes can only have 15 as a valid value.
+ minutes = 15
}
}
+ }
+
+ dynamic "access_control_translation" {
+ for_each = try(rule.value.destination.access_control_translation.owner, null) == null ? [] : [rule.value.destination.access_control_translation.owner]
+
+ content {
+ owner = access_control_translation.value
+ }
+ }
+ }
- # Replication to multiple destination buckets requires that priority is specified in the rules object.
- # If the corresponding rule requires no filter, an empty configuration block filter {} must be specified.
- # See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket
- dynamic "filter" {
- for_each = try(rules.value.filter, null) == null ? [{ prefix = null, tags = {} }] : [rules.value.filter]
+ dynamic "source_selection_criteria" {
+ for_each = try(rule.value.source_selection_criteria.sse_kms_encrypted_objects.enabled, null) == null ? [] : [rule.value.source_selection_criteria.sse_kms_encrypted_objects.enabled]
+
+ content {
+ sse_kms_encrypted_objects {
+ status = source_selection_criteria.value
+ }
+ }
+ }
+
+ # Replication to multiple destination buckets requires that priority is specified in the rules object.
+ # If the corresponding rule requires no filter, an empty configuration block filter {} must be specified.
+ # See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket
+ dynamic "filter" {
+ for_each = try(rule.value.filter, null) == null ? [{ prefix = null, tags = {} }] : [rule.value.filter]
+
+ content {
+ prefix = try(filter.value.prefix, try(rule.value.prefix, null))
+ dynamic "tag" {
+ for_each = try(filter.value.tags, {})
content {
- prefix = try(filter.value.prefix, try(rules.value.prefix, null))
- tags = try(filter.value.tags, {})
+ key = tag.key
+ value = tag.value
}
}
}
@@ -243,17 +372,24 @@ resource "aws_s3_bucket" "default" {
}
}
- dynamic "object_lock_configuration" {
- for_each = var.object_lock_configuration != null ? [1] : []
- content {
- object_lock_enabled = "Enabled"
- rule {
- default_retention {
- mode = var.object_lock_configuration.mode
- days = var.object_lock_configuration.days
- years = var.object_lock_configuration.years
- }
- }
+ depends_on = [
+ # versioning must be set before replication
+ aws_s3_bucket_versioning.default
+ ]
+}
+
+resource "aws_s3_bucket_object_lock_configuration" "default" {
+ count = local.enabled && var.object_lock_configuration != null ? 1 : 0
+
+ bucket = join("", aws_s3_bucket.default.*.id)
+
+ object_lock_enabled = "Enabled"
+
+ rule {
+ default_retention {
+ mode = var.object_lock_configuration.mode
+ days = var.object_lock_configuration.days
+ years = var.object_lock_configuration.years
}
}
}
@@ -393,9 +529,10 @@ data "aws_iam_policy_document" "bucket_policy" {
}
data "aws_iam_policy_document" "aggregated_policy" {
- count = local.enabled ? 1 : 0
- source_json = var.policy
- override_json = join("", data.aws_iam_policy_document.bucket_policy.*.json)
+ count = local.enabled ? 1 : 0
+
+ source_policy_documents = compact(concat([var.policy], var.source_policy_documents))
+ override_policy_documents = data.aws_iam_policy_document.bucket_policy.*.json
}
resource "aws_s3_bucket_policy" "default" {
diff --git a/test/Makefile b/test/Makefile
index 17b2fe74..095a5b7c 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -33,11 +33,11 @@ clean:
all: module examples/complete
## Run basic sanity checks against the module itself
-module: export TESTS ?= installed lint get-modules module-pinning get-plugins provider-pinning validate terraform-docs input-descriptions output-descriptions
+module: export TESTS ?= installed lint module-pinning provider-pinning validate terraform-docs input-descriptions output-descriptions
module: deps
$(call RUN_TESTS, ../)
## Run tests against example
-examples/complete: export TESTS ?= installed lint get-modules get-plugins validate
+examples/complete: export TESTS ?= installed lint validate
examples/complete: deps
$(call RUN_TESTS, ../$@)
diff --git a/test/src/Makefile b/test/src/Makefile
index dd458292..d7ae5975 100644
--- a/test/src/Makefile
+++ b/test/src/Makefile
@@ -1,4 +1,4 @@
-export TF_CLI_ARGS_init ?= -get-plugins=true
+export TERRAFORM_VERSION ?= $(shell curl -s https://checkpoint-api.hashicorp.com/v1/check/terraform | jq -r -M '.current_version' | cut -d. -f1)
.DEFAULT_GOAL : all
@@ -14,16 +14,13 @@ init:
.PHONY : test
## Run tests
test: init
- # Verify we have valid AWS credential
- if command -v aws >/dev/null; then \
- (unset AWS_PROFILE; aws sts get-caller-identity >/dev/null); \
- fi
- go test -v -timeout 10m -run TestExamplesComplete
+ go mod download
+ go test -v -timeout 15m -run TestExamplesComplete
## Run tests in docker container
docker/test:
docker run --name terratest --rm -it -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN -e GITHUB_TOKEN \
- -e PATH="/usr/local/terraform/0.13/bin:/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
+ -e PATH="/usr/local/terraform/$(TERRAFORM_VERSION)/bin:/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
-v $(CURDIR)/../../:/module/ cloudposse/test-harness:latest -C /module/test/src test
.PHONY : clean
diff --git a/test/src/examples_complete_test.go b/test/src/examples_complete_test.go
index fc8d354f..0d843d90 100644
--- a/test/src/examples_complete_test.go
+++ b/test/src/examples_complete_test.go
@@ -3,24 +3,21 @@ package test
import (
"bytes"
"encoding/json"
- "math/rand"
- "strconv"
- "strings"
- "testing"
- "time"
-
"github.com/gruntwork-io/terratest/modules/aws"
+ "github.com/gruntwork-io/terratest/modules/random"
"github.com/gruntwork-io/terratest/modules/terraform"
test_structure "github.com/gruntwork-io/terratest/modules/test-structure"
"github.com/stretchr/testify/assert"
+ "strings"
+ "testing"
)
// Test the Terraform module in examples/complete using Terratest.
func TestExamplesComplete(t *testing.T) {
t.Parallel()
- rand.Seed(time.Now().UnixNano())
+ randID := strings.ToLower(random.UniqueId())
+ attributes := []string{randID}
- attributes := []string{strconv.Itoa(rand.Intn(100000))}
rootFolder := "../../"
terraformFolderRelativeToRoot := "examples/complete"
varFiles := []string{"fixtures.us-east-2.tfvars"}
@@ -62,9 +59,9 @@ func TestExamplesComplete(t *testing.T) {
// Test the Terraform module in examples/complete using Terratest for grants.
func TestExamplesCompleteWithGrants(t *testing.T) {
t.Parallel()
- rand.Seed(time.Now().UnixNano() + 1)
+ randID := strings.ToLower(random.UniqueId())
+ attributes := []string{randID}
- attributes := []string{strconv.Itoa(rand.Intn(100000))}
rootFolder := "../../"
terraformFolderRelativeToRoot := "examples/complete"
varFiles := []string{"grants.us-east-2.tfvars"}
@@ -99,9 +96,9 @@ func TestExamplesCompleteWithGrants(t *testing.T) {
// Test the Terraform module in examples/complete using Terratest for grants.
func TestExamplesCompleteWithObjectLock(t *testing.T) {
t.Parallel()
- rand.Seed(time.Now().UnixNano() + 2)
+ randID := strings.ToLower(random.UniqueId())
+ attributes := []string{randID}
- attributes := []string{strconv.Itoa(rand.Intn(100000))}
rootFolder := "../../"
terraformFolderRelativeToRoot := "examples/complete"
varFiles := []string{"object-lock.us-east-2.tfvars"}
@@ -134,9 +131,9 @@ func TestExamplesCompleteWithObjectLock(t *testing.T) {
func TestExamplesCompleteWithLifecycleRules(t *testing.T) {
t.Parallel()
- rand.Seed(time.Now().UnixNano() + 3)
+ randID := strings.ToLower(random.UniqueId())
+ attributes := []string{randID}
- attributes := []string{strconv.Itoa(rand.Intn(100000))}
rootFolder := "../../"
terraformFolderRelativeToRoot := "examples/complete"
varFiles := []string{"lifecycle.us-east-2.tfvars"}
@@ -170,9 +167,9 @@ func TestExamplesCompleteWithLifecycleRules(t *testing.T) {
func TestExamplesCompleteWithReplication(t *testing.T) {
t.Parallel()
- rand.Seed(time.Now().UnixNano() + 4)
+ randID := strings.ToLower(random.UniqueId())
+ attributes := []string{randID}
- attributes := []string{strconv.Itoa(rand.Intn(100000))}
rootFolder := "../../"
terraformFolderRelativeToRoot := "examples/complete"
varFiles := []string{"replication.us-east-2.tfvars"}
@@ -220,10 +217,10 @@ func TestExamplesCompleteWithReplication(t *testing.T) {
func TestExamplesCompleteWithPrivilegedPrincipals(t *testing.T) {
t.Parallel()
- rand.Seed(time.Now().UnixNano() + 5)
+ randID := strings.ToLower(random.UniqueId())
+ attributes := []string{randID}
awsRegion := "us-east-2"
- attributes := []string{strconv.Itoa(rand.Intn(100000))}
rootFolder := "../../"
terraformFolderRelativeToRoot := "examples/complete"
varFiles := []string{"privileged-principals.us-east-2.tfvars"}
@@ -328,9 +325,9 @@ func TestExamplesCompleteWithPrivilegedPrincipals(t *testing.T) {
func TestExamplesCompleteDisabled(t *testing.T) {
t.Parallel()
- rand.Seed(time.Now().UnixNano() + 6)
+ randID := strings.ToLower(random.UniqueId())
+ attributes := []string{randID}
- attributes := []string{strconv.Itoa(rand.Intn(100000))}
rootFolder := "../../"
terraformFolderRelativeToRoot := "examples/complete"
varFiles := []string{"replication.us-east-2.tfvars"}
diff --git a/variables-deprecated.tf b/variables-deprecated.tf
new file mode 100644
index 00000000..71a0772a
--- /dev/null
+++ b/variables-deprecated.tf
@@ -0,0 +1,78 @@
+variable "grants" {
+ type = list(object({
+ id = string
+ type = string
+ permissions = list(string)
+ uri = string
+ }))
+ default = null
+
+ description = "DEPRECATED (replaced by `acl_grants`): A list of policy grants for the bucket. Conflicts with `acl`. Set `acl` to `null` to use this."
+}
+
+locals {
+ acl_grants = var.grants == null ? var.acl_grants : flatten(
+ [
+ for g in var.grants : [
+ for p in g.permissions : {
+ id = g.id
+ type = g.type
+ permission = p
+ uri = g.uri
+ }
+ ]
+ ])
+}
+
+variable "lifecycle_rules" {
+ type = list(object({
+ prefix = string
+ enabled = bool
+ tags = map(string)
+
+ enable_glacier_transition = bool
+ enable_deeparchive_transition = bool
+ enable_standard_ia_transition = bool
+ enable_current_object_expiration = bool
+ enable_noncurrent_version_expiration = bool
+
+ abort_incomplete_multipart_upload_days = number
+ noncurrent_version_glacier_transition_days = number
+ noncurrent_version_deeparchive_transition_days = number
+ noncurrent_version_expiration_days = number
+
+ standard_transition_days = number
+ glacier_transition_days = number
+ deeparchive_transition_days = number
+ expiration_days = number
+ }))
+ default = null
+ description = "DEPRECATED: A list of lifecycle rules"
+}
+
+locals {
+ lifecycle_configuration_rules = var.lifecycle_rules == null ? var.lifecycle_configuration_rules : (
+ [for i, v in var.lifecycle_rules : {
+ id = "rule-${i + 1}"
+ prefix = v.prefix
+ enabled = v.enabled
+ tags = v.tags
+
+ enable_glacier_transition = v.enable_glacier_transition
+ enable_deeparchive_transition = v.enable_deeparchive_transition
+ enable_standard_ia_transition = v.enable_standard_ia_transition
+ enable_current_object_expiration = v.enable_current_object_expiration
+ enable_noncurrent_version_expiration = v.enable_noncurrent_version_expiration
+
+ abort_incomplete_multipart_upload_days = v.abort_incomplete_multipart_upload_days
+ noncurrent_version_glacier_transition_days = v.noncurrent_version_glacier_transition_days
+ noncurrent_version_deeparchive_transition_days = v.noncurrent_version_deeparchive_transition_days
+ noncurrent_version_expiration_days = v.noncurrent_version_expiration_days
+
+ standard_transition_days = v.standard_transition_days
+ glacier_transition_days = v.glacier_transition_days
+ deeparchive_transition_days = v.deeparchive_transition_days
+ expiration_days = v.expiration_days
+ }]
+ )
+}
\ No newline at end of file
diff --git a/variables.tf b/variables.tf
index 29b2e87d..4a6fde88 100644
--- a/variables.tf
+++ b/variables.tf
@@ -4,22 +4,28 @@ variable "acl" {
description = "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`."
}
-variable "grants" {
+variable "acl_grants" {
type = list(object({
- id = string
- type = string
- permissions = list(string)
- uri = string
+ id = string
+ type = string
+ permission = string
+ uri = string
}))
default = null
- description = "An ACL policy grant. Conflicts with `acl`. Set `acl` to `null` to use this."
+ description = "A list of policy grants for the bucket. Conflicts with `acl`. Set `acl` to `null` to use this."
}
variable "policy" {
type = string
default = ""
- description = "A valid bucket policy JSON document. Note that if the policy document is not specific enough (but still valid), Terraform may view the policy as constantly changing in a terraform plan. In this case, please make sure you use the verbose/specific version of the policy"
+ description = "DEPRECATED: A valid bucket policy JSON document. Note that if the policy document is not specific enough (but still valid), Terraform may view the policy as constantly changing in a terraform plan. In this case, please make sure you use the verbose/specific version of the policy"
+}
+
+variable "source_policy_documents" {
+ type = list(string)
+ default = []
+ description = "List of IAM policy documents that are merged together into the exported document. Statements defined in source_policy_documents or source_json must have unique sids. Statements with the same sid from documents assigned to the override_json and override_policy_documents arguments will override source statements."
}
variable "force_destroy" {
@@ -79,8 +85,9 @@ variable "allow_ssl_requests_only" {
description = "Set to `true` to require requests to use Secure Socket Layer (HTTPS/SSL). This will explicitly deny access to HTTP requests"
}
-variable "lifecycle_rules" {
+variable "lifecycle_configuration_rules" {
type = list(object({
+ id = string
prefix = string
enabled = bool
tags = map(string)
@@ -102,6 +109,7 @@ variable "lifecycle_rules" {
expiration_days = number
}))
default = [{
+ id = "noop"
enabled = false
prefix = ""
tags = {}
diff --git a/versions.tf b/versions.tf
index 749b0eb1..87da5770 100644
--- a/versions.tf
+++ b/versions.tf
@@ -1,10 +1,10 @@
terraform {
- required_version = ">= 0.13.0"
+ required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 3.68.0"
+ version = ">= 4.2.0"
}
time = {
source = "hashicorp/time"