├── .editorconfig ├── .github └── workflows │ ├── lock.yml │ ├── pr-title.yml │ ├── pre-commit.yml │ ├── release.yml │ └── stale-actions.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .releaserc.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── UPGRADE-3.0.md ├── examples ├── account-public-access │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── complete-legacy │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── complete │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── directory-bucket │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── notification │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── object │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── s3-analytics │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── s3-inventory │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── s3-replication │ ├── README.md │ ├── iam.tf │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf └── table-bucket │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── main.tf ├── modules ├── account-public-access │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── notification │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── object │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf └── table-bucket │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── outputs.tf ├── variables.tf ├── versions.tf └── wrappers ├── README.md ├── account-public-access ├── README.md ├── main.tf ├── outputs.tf ├── variables.tf └── versions.tf ├── main.tf ├── notification ├── README.md ├── main.tf ├── outputs.tf ├── variables.tf └── versions.tf ├── object ├── README.md ├── main.tf ├── outputs.tf ├── variables.tf └── versions.tf ├── outputs.tf ├── table-bucket ├── README.md ├── main.tf ├── outputs.tf ├── variables.tf └── versions.tf ├── variables.tf └── versions.tf /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | # Uses editorconfig to maintain consistent coding styles 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | # Unix-style newlines with a newline ending every file 8 | [*] 9 | charset = utf-8 10 | end_of_line = lf 11 | indent_size = 2 12 | indent_style = space 13 | insert_final_newline = true 14 | max_line_length = 80 15 | trim_trailing_whitespace = true 16 | 17 | [*.{tf,tfvars}] 18 | indent_size = 2 19 | indent_style = space 20 | 21 | [*.md] 22 | max_line_length = 0 23 | trim_trailing_whitespace = false 24 | 25 | [Makefile] 26 | tab_width = 2 27 | indent_style = tab 28 | 29 | [COMMIT_EDITMSG] 30 | max_line_length = 0 31 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | name: 'Lock Threads' 2 | 3 | on: 4 | schedule: 5 | - cron: '50 1 * * *' 6 | 7 | jobs: 8 | lock: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: dessant/lock-threads@v5 12 | with: 13 | github-token: ${{ secrets.GITHUB_TOKEN }} 14 | issue-comment: > 15 | I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. 16 | If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. 17 | issue-inactive-days: '30' 18 | pr-comment: > 19 | I'm going to lock this pull request because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. 20 | If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. 21 | pr-inactive-days: '30' 22 | -------------------------------------------------------------------------------- /.github/workflows/pr-title.yml: -------------------------------------------------------------------------------- 1 | name: 'Validate PR title' 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | jobs: 11 | main: 12 | name: Validate PR title 13 | runs-on: ubuntu-latest 14 | steps: 15 | # Please look up the latest version from 16 | # https://github.com/amannn/action-semantic-pull-request/releases 17 | - uses: amannn/action-semantic-pull-request@v5.5.3 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | with: 21 | # Configure which types are allowed. 22 | # Default: https://github.com/commitizen/conventional-commit-types 23 | types: | 24 | fix 25 | feat 26 | docs 27 | ci 28 | chore 29 | # Configure that a scope must always be provided. 30 | requireScope: false 31 | # Configure additional validation for the subject based on a regex. 32 | # This example ensures the subject starts with an uppercase character. 33 | subjectPattern: ^[A-Z].+$ 34 | # If `subjectPattern` is configured, you can use this property to override 35 | # the default error message that is shown when the pattern doesn't match. 36 | # The variables `subject` and `title` can be used within the message. 37 | subjectPatternError: | 38 | The subject "{subject}" found in the pull request title "{title}" 39 | didn't match the configured pattern. Please ensure that the subject 40 | starts with an uppercase character. 41 | # For work-in-progress PRs you can typically use draft pull requests 42 | # from Github. However, private repositories on the free plan don't have 43 | # this option and therefore this action allows you to opt-in to using the 44 | # special "[WIP]" prefix to indicate this state. This will avoid the 45 | # validation of the PR title and the pull request checks remain pending. 46 | # Note that a second check will be reported if this is enabled. 47 | wip: true 48 | # When using "Squash and merge" on a PR with only one commit, GitHub 49 | # will suggest using that commit message instead of the PR title for the 50 | # merge commit, and it's easy to commit this by mistake. Enable this option 51 | # to also validate the commit message for one commit PRs. 52 | validateSingleCommit: false 53 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: Pre-Commit 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - master 8 | 9 | env: 10 | TERRAFORM_DOCS_VERSION: v0.19.0 11 | TFLINT_VERSION: v0.53.0 12 | 13 | jobs: 14 | collectInputs: 15 | name: Collect workflow inputs 16 | runs-on: ubuntu-latest 17 | outputs: 18 | directories: ${{ steps.dirs.outputs.directories }} 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Get root directories 24 | id: dirs 25 | uses: clowdhaus/terraform-composite-actions/directories@v1.9.0 26 | 27 | preCommitMinVersions: 28 | name: Min TF pre-commit 29 | needs: collectInputs 30 | runs-on: ubuntu-latest 31 | strategy: 32 | matrix: 33 | directory: ${{ fromJson(needs.collectInputs.outputs.directories) }} 34 | steps: 35 | # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 36 | - name: Delete huge unnecessary tools folder 37 | run: | 38 | rm -rf /opt/hostedtoolcache/CodeQL 39 | rm -rf /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk 40 | rm -rf /opt/hostedtoolcache/Ruby 41 | rm -rf /opt/hostedtoolcache/go 42 | 43 | - name: Checkout 44 | uses: actions/checkout@v4 45 | 46 | - name: Terraform min/max versions 47 | id: minMax 48 | uses: clowdhaus/terraform-min-max@v1.3.1 49 | with: 50 | directory: ${{ matrix.directory }} 51 | 52 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }} 53 | # Run only validate pre-commit check on min version supported 54 | if: ${{ matrix.directory != '.' }} 55 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1 56 | with: 57 | terraform-version: ${{ steps.minMax.outputs.minVersion }} 58 | tflint-version: ${{ env.TFLINT_VERSION }} 59 | args: 'terraform_validate --color=always --show-diff-on-failure --files ${{ matrix.directory }}/*' 60 | 61 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }} 62 | # Run only validate pre-commit check on min version supported 63 | if: ${{ matrix.directory == '.' }} 64 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1 65 | with: 66 | terraform-version: ${{ steps.minMax.outputs.minVersion }} 67 | tflint-version: ${{ env.TFLINT_VERSION }} 68 | args: 'terraform_validate --color=always --show-diff-on-failure --files $(ls *.tf)' 69 | 70 | preCommitMaxVersion: 71 | name: Max TF pre-commit 72 | runs-on: ubuntu-latest 73 | needs: collectInputs 74 | steps: 75 | # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 76 | - name: Delete huge unnecessary tools folder 77 | run: | 78 | rm -rf /opt/hostedtoolcache/CodeQL 79 | rm -rf /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk 80 | rm -rf /opt/hostedtoolcache/Ruby 81 | rm -rf /opt/hostedtoolcache/go 82 | 83 | - name: Checkout 84 | uses: actions/checkout@v4 85 | with: 86 | ref: ${{ github.event.pull_request.head.ref }} 87 | repository: ${{github.event.pull_request.head.repo.full_name}} 88 | 89 | - name: Terraform min/max versions 90 | id: minMax 91 | uses: clowdhaus/terraform-min-max@v1.3.1 92 | 93 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }} 94 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1 95 | with: 96 | terraform-version: ${{ steps.minMax.outputs.maxVersion }} 97 | tflint-version: ${{ env.TFLINT_VERSION }} 98 | terraform-docs-version: ${{ env.TERRAFORM_DOCS_VERSION }} 99 | install-hcledit: true 100 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | - master 9 | paths: 10 | - '**/*.tpl' 11 | - '**/*.py' 12 | - '**/*.tf' 13 | - '.github/workflows/release.yml' 14 | 15 | jobs: 16 | release: 17 | name: Release 18 | runs-on: ubuntu-latest 19 | # Skip running release workflow on forks 20 | if: github.repository_owner == 'terraform-aws-modules' 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v4 24 | with: 25 | persist-credentials: false 26 | fetch-depth: 0 27 | 28 | - name: Release 29 | uses: cycjimmy/semantic-release-action@v4 30 | with: 31 | semantic_version: 23.0.2 32 | extra_plugins: | 33 | @semantic-release/changelog@6.0.3 34 | @semantic-release/git@10.0.1 35 | conventional-changelog-conventionalcommits@7.0.2 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }} 38 | -------------------------------------------------------------------------------- /.github/workflows/stale-actions.yaml: -------------------------------------------------------------------------------- 1 | name: 'Mark or close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v9 11 | with: 12 | repo-token: ${{ secrets.GITHUB_TOKEN }} 13 | # Staling issues and PR's 14 | days-before-stale: 30 15 | stale-issue-label: stale 16 | stale-pr-label: stale 17 | stale-issue-message: | 18 | This issue has been automatically marked as stale because it has been open 30 days 19 | with no activity. Remove stale label or comment or this issue will be closed in 10 days 20 | stale-pr-message: | 21 | This PR has been automatically marked as stale because it has been open 30 days 22 | with no activity. Remove stale label or comment or this PR will be closed in 10 days 23 | # Not stale if have this labels or part of milestone 24 | exempt-issue-labels: bug,wip,on-hold 25 | exempt-pr-labels: bug,wip,on-hold 26 | exempt-all-milestones: true 27 | # Close issue operations 28 | # Label will be automatically removed if the issues are no longer closed nor locked. 29 | days-before-close: 10 30 | delete-branch: true 31 | close-issue-message: This issue was automatically closed because of stale in 10 days 32 | close-pr-message: This PR was automatically closed because of stale in 10 days 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # Terraform lockfile 5 | .terraform.lock.hcl 6 | 7 | # .tfstate files 8 | *.tfstate 9 | *.tfstate.* 10 | 11 | # Crash log files 12 | crash.log 13 | 14 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as 15 | # password, private keys, and other secrets. These should not be part of version 16 | # control as they are data points which are potentially sensitive and subject 17 | # to change depending on the environment. 18 | *.tfvars 19 | 20 | # Ignore override files as they are usually used to override resources locally and so 21 | # are not checked in 22 | override.tf 23 | override.tf.json 24 | *_override.tf 25 | *_override.tf.json 26 | 27 | # Ignore CLI configuration files 28 | .terraformrc 29 | terraform.rc 30 | 31 | *.zip 32 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/antonbabenko/pre-commit-terraform 3 | rev: v1.98.1 4 | hooks: 5 | - id: terraform_fmt 6 | - id: terraform_wrapper_module_for_each 7 | - id: terraform_docs 8 | args: 9 | - '--args=--lockfile=false' 10 | - id: terraform_tflint 11 | args: 12 | - '--args=--only=terraform_deprecated_interpolation' 13 | - '--args=--only=terraform_deprecated_index' 14 | - '--args=--only=terraform_unused_declarations' 15 | - '--args=--only=terraform_comment_syntax' 16 | - '--args=--only=terraform_documented_outputs' 17 | - '--args=--only=terraform_documented_variables' 18 | - '--args=--only=terraform_typed_variables' 19 | - '--args=--only=terraform_module_pinned_source' 20 | - '--args=--only=terraform_naming_convention' 21 | - '--args=--only=terraform_required_version' 22 | - '--args=--only=terraform_required_providers' 23 | - '--args=--only=terraform_standard_module_structure' 24 | - '--args=--only=terraform_workspace_remote' 25 | - id: terraform_validate 26 | - repo: https://github.com/pre-commit/pre-commit-hooks 27 | rev: v5.0.0 28 | hooks: 29 | - id: check-merge-conflict 30 | - id: end-of-file-fixer 31 | - id: trailing-whitespace 32 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main", 4 | "master" 5 | ], 6 | "ci": false, 7 | "plugins": [ 8 | [ 9 | "@semantic-release/commit-analyzer", 10 | { 11 | "preset": "conventionalcommits" 12 | } 13 | ], 14 | [ 15 | "@semantic-release/release-notes-generator", 16 | { 17 | "preset": "conventionalcommits" 18 | } 19 | ], 20 | [ 21 | "@semantic-release/github", 22 | { 23 | "successComment": "This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:", 24 | "labels": false, 25 | "releasedLabels": false 26 | } 27 | ], 28 | [ 29 | "@semantic-release/changelog", 30 | { 31 | "changelogFile": "CHANGELOG.md", 32 | "changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file." 33 | } 34 | ], 35 | [ 36 | "@semantic-release/git", 37 | { 38 | "assets": [ 39 | "CHANGELOG.md" 40 | ], 41 | "message": "chore(release): version ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 42 | } 43 | ] 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /UPGRADE-3.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from v2.x to v3.x 2 | 3 | If you have any questions regarding this upgrade process, please consult the [`examples/`](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/examples) projects: 4 | 5 | If you find a bug, please open an issue with supporting configuration to reproduce. 6 | 7 | ## List of backwards incompatible changes 8 | 9 | - Terraform AWS provider minimum version is now `v4.5.0` in order to have forward compatibility with Terraform AWS provider `v4.x`. Using the latest version of `v4` is highly recommended, if possible. 10 | - If you are using AWS provider `v3.75` the latest supported version of this module is `v3.0.1` 11 | - Main group of changes is related to refactoring of `aws_s3_bucket` resource into several smaller resources. Read [`S3 bucket refactor` section in the official Terraform AWS Provider Version 4 Upgrade Guide](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/version-4-upgrade#s3-bucket-refactor) and [discussion around these changes](https://github.com/hashicorp/terraform-provider-aws/issues/23106). 12 | - `modules/object`: Changed resource type from `aws_bucket_s3_object` to `aws_s3_object`. After upgrade, on the next apply, Terraform will recreate the object. If you prefer to not have Terraform recreate the object, import the object using `aws_s3_object`. [Read more](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_object#import). 13 | 14 | ## Additional changes 15 | 16 | ### Added 17 | 18 | - None 19 | 20 | ### Modified 21 | 22 | - `acl` variable is set to `null` by default 23 | - In addition to pseudo-boolean values like "Enabled", "Disabled", "Suspended", it is now possible to specify `true` or `false` in all such arguments (e.g. `versioning = { enabled = true }`). 24 | 25 | ### Variable and output changes 26 | 27 | 1. Removed variables: 28 | 29 | - None 30 | 31 | 2. Renamed variables: 32 | 33 | - None 34 | 35 | 3. Added variables: 36 | 37 | - `owner` 38 | - `expected_bucket_owner` 39 | 40 | 4. Removed outputs: 41 | 42 | - None 43 | 44 | 5. Renamed outputs: 45 | 46 | `modules/object`: 47 | 48 | - `s3_bucket_object_id` -> `s3_object_id` 49 | - `s3_bucket_object_etag` -> `s3_object_etag` 50 | - `s3_bucket_object_version_id` -> `s3_object_version_id` 51 | 52 | 6. Added outputs: 53 | 54 | - None 55 | 56 | ## Upgrade Migrations 57 | 58 | The following examples demonstrate some of the changes that users can elect to make to avoid any potential disruptions when upgrading. 59 | 60 | ### Before 2.x Example 61 | 62 | See code in [`examples/complete-legacy`](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/examples/complete-legacy). 63 | 64 | ```hcl 65 | module "s3_bucket" { 66 | source = "terraform-aws-modules/s3-bucket/aws" 67 | version = "~> 2.0" 68 | 69 | bucket = "my-awesome-bucket" 70 | acl = "log-delivery-write" 71 | } 72 | 73 | terraform { 74 | required_providers { 75 | aws = "~> 3.69.0" # or anything lower than 3.75.0 76 | } 77 | } 78 | ``` 79 | 80 | ### After 3.x Example 81 | 82 | See code in [`examples/complete`](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/examples/complete). 83 | 84 | ```hcl 85 | module "s3_bucket" { 86 | source = "terraform-aws-modules/s3-bucket/aws" 87 | version = "~> 3.0" 88 | 89 | bucket = "my-awesome-bucket" 90 | acl = "log-delivery-write" 91 | } 92 | 93 | terraform { 94 | required_providers { 95 | aws = ">= 4.5" # or anything higher than 4.5.0 96 | } 97 | } 98 | ``` 99 | 100 | After the code is updated, you need run `terraform init -upgrade` to download newer AWS provider, and then import S3 bucket ACL using such command: 101 | 102 | ``` 103 | terraform import "module.s3_bucket.aws_s3_bucket_acl.this[0]" my-awesome-bucket,log-delivery-write 104 | ``` 105 | 106 | Where `log-delivery-write` is the value of `acl` argument in the module block above. 107 | 108 | Read more about [import in the official documentation for `aws_s3_bucket_acl`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl#import). 109 | 110 | #### Import existing resources (required during the migration from v2.x of this module) 111 | 112 | During the migration to v3.x of this module, several S3 resources will be created by this module. In order to guarantee the best experience and prevent data loss, you will need to import them into terraform state using commands like these: 113 | 114 | ```bash 115 | terraform import "module.s3_bucket.aws_s3_bucket_acl.this[0]" , 116 | terraform import "module.s3_bucket.aws_s3_bucket_logging.this[0]" 117 | terraform import "module.s3_bucket.aws_s3_bucket_website_configuration.this[0]" , 118 | terraform import "module.s3_bucket.aws_s3_bucket_versioning.this[0]" , 119 | terraform import "module.s3_bucket.aws_s3_bucket_server_side_encryption_configuration.this[0]" , 120 | terraform import "module.s3_bucket.aws_s3_bucket_request_payment_configuration.this[0]" , 121 | terraform import "module.s3_bucket.aws_s3_bucket_accelerate_configuration.this[0]" , 122 | terraform import "module.s3_bucket.aws_s3_bucket_policy.this[0]" 123 | terraform import "module.s3_bucket.aws_s3_bucket_ownership_controls.this[0]" 124 | terraform import "module.s3_bucket.aws_s3_bucket_cors_configuration.this[0]" , 125 | terraform import "module.s3_bucket.aws_s3_bucket_object_lock_configuration.this[0]" , 126 | terraform import "module.s3_bucket.aws_s3_bucket_public_access_block.this[0]" 127 | terraform import "module.s3_bucket.aws_s3_bucket_lifecycle_configuration.this[0]" , 128 | terraform import "module.s3_bucket.aws_s3_bucket_replication_configuration.this[0]" 129 | ``` 130 | 131 | Where `s3_bucket` is the name of your module definition, `bucket-name` is the name of the bucket, `acl` is the bucket ACL (e.g. `private`, `log-delivery-write`, etc), `` is your AWS account number (required only if `expected_bucket_owner` is set in the code). 132 | -------------------------------------------------------------------------------- /examples/account-public-access/README.md: -------------------------------------------------------------------------------- 1 | # S3 account-level Public Access Block 2 | 3 | Configuration in this directory creates S3 account-level Public Access Block. 4 | 5 | ## Usage 6 | 7 | To run this example you need to execute: 8 | 9 | ```bash 10 | $ terraform init 11 | $ terraform plan 12 | $ terraform apply 13 | ``` 14 | 15 | Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources. 16 | 17 | 18 | ## Requirements 19 | 20 | | Name | Version | 21 | |------|---------| 22 | | [terraform](#requirement\_terraform) | >= 1.0 | 23 | | [aws](#requirement\_aws) | >= 5.70 | 24 | | [random](#requirement\_random) | >= 2.0 | 25 | 26 | ## Providers 27 | 28 | No providers. 29 | 30 | ## Modules 31 | 32 | | Name | Source | Version | 33 | |------|--------|---------| 34 | | [account\_public\_access](#module\_account\_public\_access) | ../../modules/account-public-access | n/a | 35 | 36 | ## Resources 37 | 38 | No resources. 39 | 40 | ## Inputs 41 | 42 | No inputs. 43 | 44 | ## Outputs 45 | 46 | | Name | Description | 47 | |------|-------------| 48 | | [s3\_account\_public\_access\_block\_id](#output\_s3\_account\_public\_access\_block\_id) | AWS account ID | 49 | 50 | -------------------------------------------------------------------------------- /examples/account-public-access/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | 4 | # Make it faster by skipping something 5 | skip_metadata_api_check = true 6 | skip_region_validation = true 7 | skip_credentials_validation = true 8 | } 9 | 10 | locals { 11 | region = "eu-west-1" 12 | } 13 | 14 | module "account_public_access" { 15 | source = "../../modules/account-public-access" 16 | 17 | block_public_acls = true 18 | block_public_policy = true 19 | ignore_public_acls = true 20 | restrict_public_buckets = true 21 | } 22 | -------------------------------------------------------------------------------- /examples/account-public-access/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_account_public_access_block_id" { 2 | description = "AWS account ID" 3 | value = module.account_public_access.s3_account_public_access_block_id 4 | } 5 | -------------------------------------------------------------------------------- /examples/account-public-access/variables.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-s3-bucket/0d781fbb515b1f64c46bfba04153bef091d50fc5/examples/account-public-access/variables.tf -------------------------------------------------------------------------------- /examples/account-public-access/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.70" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 2.0" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/complete-legacy/README.md: -------------------------------------------------------------------------------- 1 | # Legacy - Complete S3 bucket with most of supported features enabled 2 | 3 | Configuration in this directory creates S3 bucket using previous (2.x) version of this module to test upgrade process. 4 | 5 | This configuration is similar to the code in [examples/complete](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/examples/complete) but not identical. 6 | 7 | ## Usage 8 | 9 | Once this configuration is created, you need to use the newer version of this module (e.g. `~> 3.0`), review/update arguments (see code in [examples/complete](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/examples/complete)) and import existing resources (see [UPGRADE-3.0.md](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/blob/master/UPGRADE-3.0.md) for more precise commands). 10 | 11 | 12 | ## Requirements 13 | 14 | | Name | Version | 15 | |------|---------| 16 | | [terraform](#requirement\_terraform) | >= 1.0 | 17 | | [aws](#requirement\_aws) | ~> 3.69.0 | 18 | | [random](#requirement\_random) | >= 2.0 | 19 | 20 | ## Providers 21 | 22 | | Name | Version | 23 | |------|---------| 24 | | [aws](#provider\_aws) | ~> 3.69.0 | 25 | | [random](#provider\_random) | >= 2.0 | 26 | 27 | ## Modules 28 | 29 | | Name | Source | Version | 30 | |------|--------|---------| 31 | | [log\_bucket](#module\_log\_bucket) | terraform-aws-modules/s3-bucket/aws | ~> 2.0 | 32 | | [s3\_bucket](#module\_s3\_bucket) | terraform-aws-modules/s3-bucket/aws | ~> 2.0 | 33 | 34 | ## Resources 35 | 36 | | Name | Type | 37 | |------|------| 38 | | [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 39 | | [aws_kms_key.objects](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | 40 | | [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | 41 | | [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 42 | 43 | ## Inputs 44 | 45 | No inputs. 46 | 47 | ## Outputs 48 | 49 | | Name | Description | 50 | |------|-------------| 51 | | [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | The ARN of the bucket. Will be of format arn:aws:s3:::bucketname. | 52 | | [s3\_bucket\_bucket\_domain\_name](#output\_s3\_bucket\_bucket\_domain\_name) | The bucket domain name. Will be of format bucketname.s3.amazonaws.com. | 53 | | [s3\_bucket\_bucket\_regional\_domain\_name](#output\_s3\_bucket\_bucket\_regional\_domain\_name) | The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL. | 54 | | [s3\_bucket\_hosted\_zone\_id](#output\_s3\_bucket\_hosted\_zone\_id) | The Route 53 Hosted Zone ID for this bucket's region. | 55 | | [s3\_bucket\_id](#output\_s3\_bucket\_id) | The name of the bucket. | 56 | | [s3\_bucket\_region](#output\_s3\_bucket\_region) | The AWS region this bucket resides in. | 57 | | [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. | 58 | | [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. | 59 | 60 | -------------------------------------------------------------------------------- /examples/complete-legacy/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | 4 | # Make it faster by skipping something 5 | skip_metadata_api_check = true 6 | skip_region_validation = true 7 | skip_credentials_validation = true 8 | } 9 | 10 | locals { 11 | bucket_name = "s3-bucket-${random_pet.this.id}" 12 | region = "eu-west-1" 13 | } 14 | 15 | resource "random_pet" "this" { 16 | length = 2 17 | } 18 | 19 | resource "aws_kms_key" "objects" { 20 | description = "KMS key is used to encrypt bucket objects" 21 | deletion_window_in_days = 7 22 | } 23 | 24 | resource "aws_iam_role" "this" { 25 | assume_role_policy = < 28 | ## Requirements 29 | 30 | | Name | Version | 31 | |------|---------| 32 | | [terraform](#requirement\_terraform) | >= 1.0 | 33 | | [aws](#requirement\_aws) | >= 5.83 | 34 | | [random](#requirement\_random) | >= 2.0 | 35 | 36 | ## Providers 37 | 38 | | Name | Version | 39 | |------|---------| 40 | | [aws](#provider\_aws) | >= 5.83 | 41 | | [random](#provider\_random) | >= 2.0 | 42 | 43 | ## Modules 44 | 45 | | Name | Source | Version | 46 | |------|--------|---------| 47 | | [cloudfront\_log\_bucket](#module\_cloudfront\_log\_bucket) | ../../ | n/a | 48 | | [log\_bucket](#module\_log\_bucket) | ../../ | n/a | 49 | | [s3\_bucket](#module\_s3\_bucket) | ../../ | n/a | 50 | | [simple\_bucket](#module\_simple\_bucket) | ../../ | n/a | 51 | 52 | ## Resources 53 | 54 | | Name | Type | 55 | |------|------| 56 | | [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 57 | | [aws_kms_key.objects](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | 58 | | [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | 59 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 60 | | [aws_canonical_user_id.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/canonical_user_id) | data source | 61 | | [aws_cloudfront_log_delivery_canonical_user_id.cloudfront](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_log_delivery_canonical_user_id) | data source | 62 | | [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 63 | 64 | ## Inputs 65 | 66 | No inputs. 67 | 68 | ## Outputs 69 | 70 | | Name | Description | 71 | |------|-------------| 72 | | [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | The ARN of the bucket. Will be of format arn:aws:s3:::bucketname. | 73 | | [s3\_bucket\_bucket\_domain\_name](#output\_s3\_bucket\_bucket\_domain\_name) | The bucket domain name. Will be of format bucketname.s3.amazonaws.com. | 74 | | [s3\_bucket\_bucket\_regional\_domain\_name](#output\_s3\_bucket\_bucket\_regional\_domain\_name) | The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL. | 75 | | [s3\_bucket\_hosted\_zone\_id](#output\_s3\_bucket\_hosted\_zone\_id) | The Route 53 Hosted Zone ID for this bucket's region. | 76 | | [s3\_bucket\_id](#output\_s3\_bucket\_id) | The name of the bucket. | 77 | | [s3\_bucket\_lifecycle\_configuration\_rules](#output\_s3\_bucket\_lifecycle\_configuration\_rules) | The lifecycle rules of the bucket, if the bucket is configured with lifecycle rules. If not, this will be an empty string. | 78 | | [s3\_bucket\_policy](#output\_s3\_bucket\_policy) | The policy of the bucket, if the bucket is configured with a policy. If not, this will be an empty string. | 79 | | [s3\_bucket\_region](#output\_s3\_bucket\_region) | The AWS region this bucket resides in. | 80 | | [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. | 81 | | [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. | 82 | 83 | -------------------------------------------------------------------------------- /examples/complete/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | 4 | # Make it faster by skipping something 5 | skip_metadata_api_check = true 6 | skip_region_validation = true 7 | skip_credentials_validation = true 8 | } 9 | 10 | locals { 11 | bucket_name = "s3-bucket-${random_pet.this.id}" 12 | region = "eu-west-1" 13 | } 14 | 15 | data "aws_caller_identity" "current" {} 16 | 17 | data "aws_canonical_user_id" "current" {} 18 | 19 | data "aws_cloudfront_log_delivery_canonical_user_id" "cloudfront" {} 20 | 21 | resource "random_pet" "this" { 22 | length = 2 23 | } 24 | 25 | resource "aws_kms_key" "objects" { 26 | description = "KMS key is used to encrypt bucket objects" 27 | deletion_window_in_days = 7 28 | } 29 | 30 | resource "aws_iam_role" "this" { 31 | assume_role_policy = < 18 | ## Requirements 19 | 20 | | Name | Version | 21 | |------|---------| 22 | | [terraform](#requirement\_terraform) | >= 1.0 | 23 | | [aws](#requirement\_aws) | >= 5.83 | 24 | | [random](#requirement\_random) | >= 2.0 | 25 | 26 | ## Providers 27 | 28 | | Name | Version | 29 | |------|---------| 30 | | [aws](#provider\_aws) | >= 5.83 | 31 | | [random](#provider\_random) | >= 2.0 | 32 | 33 | ## Modules 34 | 35 | | Name | Source | Version | 36 | |------|--------|---------| 37 | | [complete](#module\_complete) | ../../ | n/a | 38 | | [simple](#module\_simple) | ../../ | n/a | 39 | 40 | ## Resources 41 | 42 | | Name | Type | 43 | |------|------| 44 | | [aws_kms_key.objects](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | 45 | | [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | 46 | | [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | 47 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 48 | | [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 49 | 50 | ## Inputs 51 | 52 | No inputs. 53 | 54 | ## Outputs 55 | 56 | | Name | Description | 57 | |------|-------------| 58 | | [directory\_bucket\_arn](#output\_directory\_bucket\_arn) | ARN of the directory bucket. | 59 | | [directory\_bucket\_name](#output\_directory\_bucket\_name) | Name of the directory bucket. | 60 | 61 | -------------------------------------------------------------------------------- /examples/directory-bucket/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | region = "eu-west-1" 3 | } 4 | 5 | provider "aws" { 6 | region = local.region 7 | 8 | # Make it faster by skipping something 9 | skip_metadata_api_check = true 10 | skip_region_validation = true 11 | skip_credentials_validation = true 12 | } 13 | 14 | data "aws_caller_identity" "current" {} 15 | 16 | data "aws_availability_zones" "available" { 17 | state = "available" 18 | } 19 | 20 | module "simple" { 21 | source = "../../" 22 | 23 | is_directory_bucket = true 24 | bucket = random_pet.this.id 25 | # https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-express-Endpoints.html 26 | availability_zone_id = data.aws_availability_zones.available.zone_ids[1] 27 | } 28 | 29 | module "complete" { 30 | source = "../../" 31 | 32 | is_directory_bucket = true 33 | bucket = "${random_pet.this.id}-complete" 34 | # https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-express-Endpoints.html 35 | availability_zone_id = data.aws_availability_zones.available.zone_ids[1] 36 | server_side_encryption_configuration = { 37 | rule = { 38 | bucket_key_enabled = true # required for directory buckets 39 | apply_server_side_encryption_by_default = { 40 | kms_master_key_id = aws_kms_key.objects.arn 41 | sse_algorithm = "aws:kms" 42 | } 43 | } 44 | } 45 | lifecycle_rule = [ 46 | { 47 | id = "test" 48 | status = "Enabled" 49 | expiration = { 50 | days = 7 51 | } 52 | }, 53 | { 54 | id = "logs" 55 | status = "Enabled" 56 | expiration = { 57 | days = 5 58 | } 59 | filter = { 60 | prefix = "logs/" 61 | object_size_less_than = 10 62 | } 63 | }, 64 | { 65 | id = "other" 66 | status = "Enabled" 67 | expiration = { 68 | days = 2 69 | } 70 | filter = { 71 | prefix = "other/" 72 | } 73 | } 74 | ] 75 | attach_policy = true 76 | policy = data.aws_iam_policy_document.bucket_policy.json 77 | } 78 | 79 | resource "random_pet" "this" { 80 | length = 2 81 | } 82 | 83 | resource "aws_kms_key" "objects" { 84 | description = "KMS key is used to encrypt bucket objects" 85 | deletion_window_in_days = 7 86 | } 87 | 88 | data "aws_iam_policy_document" "bucket_policy" { 89 | 90 | statement { 91 | sid = "ReadWriteAccess" 92 | effect = "Allow" 93 | 94 | actions = [ 95 | "s3express:CreateSession", 96 | ] 97 | 98 | resources = [module.complete.s3_directory_bucket_arn] 99 | 100 | principals { 101 | identifiers = [data.aws_caller_identity.current.account_id] 102 | type = "AWS" 103 | } 104 | } 105 | 106 | statement { 107 | sid = "ReadOnlyAccess" 108 | effect = "Allow" 109 | 110 | actions = [ 111 | "s3express:CreateSession", 112 | ] 113 | 114 | resources = [module.complete.s3_directory_bucket_arn] 115 | 116 | principals { 117 | identifiers = [data.aws_caller_identity.current.account_id] 118 | type = "AWS" 119 | } 120 | 121 | condition { 122 | test = "StringEquals" 123 | values = ["ReadOnly"] 124 | variable = "s3express:SessionMode" 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /examples/directory-bucket/outputs.tf: -------------------------------------------------------------------------------- 1 | output "directory_bucket_name" { 2 | description = "Name of the directory bucket." 3 | value = module.complete.s3_directory_bucket_name 4 | } 5 | 6 | output "directory_bucket_arn" { 7 | description = "ARN of the directory bucket." 8 | value = module.complete.s3_directory_bucket_arn 9 | } 10 | -------------------------------------------------------------------------------- /examples/directory-bucket/variables.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-s3-bucket/0d781fbb515b1f64c46bfba04153bef091d50fc5/examples/directory-bucket/variables.tf -------------------------------------------------------------------------------- /examples/directory-bucket/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.83" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 2.0" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/notification/README.md: -------------------------------------------------------------------------------- 1 | # S3 bucket notifications to Lambda functions, SQS queues, and SNS topics 2 | 3 | Configuration in this directory creates S3 bucket notifications to all supported destinations. 4 | 5 | ## Usage 6 | 7 | To run this example you need to execute: 8 | 9 | ```bash 10 | $ terraform init 11 | $ terraform plan 12 | $ terraform apply 13 | ``` 14 | 15 | Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources. 16 | 17 | 18 | ## Requirements 19 | 20 | | Name | Version | 21 | |------|---------| 22 | | [terraform](#requirement\_terraform) | >= 1.0 | 23 | | [aws](#requirement\_aws) | >= 5.83 | 24 | | [null](#requirement\_null) | >= 2.0 | 25 | | [random](#requirement\_random) | >= 2.0 | 26 | 27 | ## Providers 28 | 29 | | Name | Version | 30 | |------|---------| 31 | | [aws](#provider\_aws) | >= 5.83 | 32 | | [null](#provider\_null) | >= 2.0 | 33 | | [random](#provider\_random) | >= 2.0 | 34 | 35 | ## Modules 36 | 37 | | Name | Source | Version | 38 | |------|--------|---------| 39 | | [all\_notifications](#module\_all\_notifications) | ../../modules/notification | n/a | 40 | | [lambda\_function1](#module\_lambda\_function1) | terraform-aws-modules/lambda/aws | ~> 3.0 | 41 | | [lambda\_function2](#module\_lambda\_function2) | terraform-aws-modules/lambda/aws | ~> 3.0 | 42 | | [s3\_bucket](#module\_s3\_bucket) | ../../ | n/a | 43 | | [sns\_topic1](#module\_sns\_topic1) | terraform-aws-modules/sns/aws | ~> 3.0 | 44 | | [sns\_topic2](#module\_sns\_topic2) | terraform-aws-modules/sns/aws | ~> 3.0 | 45 | 46 | ## Resources 47 | 48 | | Name | Type | 49 | |------|------| 50 | | [aws_sqs_queue.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue) | resource | 51 | | [aws_sqs_queue_policy.allow_external](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue_policy) | resource | 52 | | [null_resource.download_package](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | 53 | | [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | 54 | | [aws_iam_policy_document.sqs_external](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 55 | 56 | ## Inputs 57 | 58 | No inputs. 59 | 60 | ## Outputs 61 | 62 | | Name | Description | 63 | |------|-------------| 64 | | [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | The ARN of the bucket. Will be of format arn:aws:s3:::bucketname. | 65 | | [s3\_bucket\_bucket\_domain\_name](#output\_s3\_bucket\_bucket\_domain\_name) | The bucket domain name. Will be of format bucketname.s3.amazonaws.com. | 66 | | [s3\_bucket\_bucket\_regional\_domain\_name](#output\_s3\_bucket\_bucket\_regional\_domain\_name) | The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL. | 67 | | [s3\_bucket\_hosted\_zone\_id](#output\_s3\_bucket\_hosted\_zone\_id) | The Route 53 Hosted Zone ID for this bucket's region. | 68 | | [s3\_bucket\_id](#output\_s3\_bucket\_id) | The name of the bucket. | 69 | | [s3\_bucket\_region](#output\_s3\_bucket\_region) | The AWS region this bucket resides in. | 70 | | [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. | 71 | | [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. | 72 | 73 | -------------------------------------------------------------------------------- /examples/notification/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | 4 | # Make it faster by skipping something 5 | skip_metadata_api_check = true 6 | skip_region_validation = true 7 | skip_credentials_validation = true 8 | } 9 | 10 | locals { 11 | bucket_name = "s3-bucket-${random_pet.this.id}" 12 | region = "eu-west-1" 13 | } 14 | 15 | resource "random_pet" "this" { 16 | length = 2 17 | } 18 | 19 | module "s3_bucket" { 20 | source = "../../" 21 | 22 | bucket = local.bucket_name 23 | force_destroy = true 24 | } 25 | 26 | ############################################# 27 | # Using packaged function from Lambda module 28 | ############################################# 29 | 30 | locals { 31 | package_url = "https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-lambda/master/examples/fixtures/python3.8-zip/existing_package.zip" 32 | downloaded = "downloaded_package_${md5(local.package_url)}.zip" 33 | } 34 | 35 | resource "null_resource" "download_package" { 36 | triggers = { 37 | downloaded = local.downloaded 38 | } 39 | 40 | provisioner "local-exec" { 41 | command = "curl -L -o ${local.downloaded} ${local.package_url}" 42 | } 43 | } 44 | 45 | module "lambda_function1" { 46 | source = "terraform-aws-modules/lambda/aws" 47 | version = "~> 3.0" 48 | 49 | function_name = "${random_pet.this.id}-lambda1" 50 | handler = "index.lambda_handler" 51 | runtime = "python3.8" 52 | 53 | create_package = false 54 | local_existing_package = local.downloaded 55 | } 56 | 57 | module "lambda_function2" { 58 | source = "terraform-aws-modules/lambda/aws" 59 | version = "~> 3.0" 60 | 61 | function_name = "${random_pet.this.id}-lambda2" 62 | handler = "index.lambda_handler" 63 | runtime = "python3.8" 64 | 65 | create_package = false 66 | local_existing_package = local.downloaded 67 | } 68 | 69 | module "sns_topic1" { 70 | source = "terraform-aws-modules/sns/aws" 71 | version = "~> 3.0" 72 | 73 | name_prefix = "${random_pet.this.id}-2" 74 | } 75 | 76 | module "sns_topic2" { 77 | source = "terraform-aws-modules/sns/aws" 78 | version = "~> 3.0" 79 | 80 | name_prefix = "${random_pet.this.id}-2" 81 | } 82 | 83 | resource "aws_sqs_queue" "this" { 84 | count = 2 85 | name = "${random_pet.this.id}-${count.index}" 86 | } 87 | 88 | # SQS policy created outside of the module 89 | data "aws_iam_policy_document" "sqs_external" { 90 | statement { 91 | effect = "Allow" 92 | actions = ["sqs:SendMessage"] 93 | 94 | principals { 95 | type = "Service" 96 | identifiers = ["s3.amazonaws.com"] 97 | } 98 | 99 | resources = [aws_sqs_queue.this[0].arn] 100 | } 101 | } 102 | 103 | resource "aws_sqs_queue_policy" "allow_external" { 104 | queue_url = aws_sqs_queue.this[0].id 105 | policy = data.aws_iam_policy_document.sqs_external.json 106 | } 107 | 108 | module "all_notifications" { 109 | source = "../../modules/notification" 110 | 111 | bucket = module.s3_bucket.s3_bucket_id 112 | 113 | eventbridge = true 114 | 115 | # Common error - Error putting S3 notification configuration: InvalidArgument: Configuration is ambiguously defined. Cannot have overlapping suffixes in two rules if the prefixes are overlapping for the same event type. 116 | 117 | lambda_notifications = { 118 | lambda1 = { 119 | function_arn = module.lambda_function1.lambda_function_arn 120 | function_name = module.lambda_function1.lambda_function_name 121 | events = ["s3:ObjectCreated:Put"] 122 | filter_prefix = "prefix/" 123 | filter_suffix = ".json" 124 | } 125 | 126 | lambda2 = { 127 | function_arn = module.lambda_function2.lambda_function_arn 128 | function_name = module.lambda_function2.lambda_function_name 129 | events = ["s3:ObjectCreated:Post"] 130 | } 131 | } 132 | 133 | sqs_notifications = { 134 | sqs1 = { 135 | queue_arn = aws_sqs_queue.this[0].arn 136 | events = ["s3:ObjectCreated:Put"] 137 | filter_prefix = "prefix2/" 138 | filter_suffix = ".txt" 139 | 140 | # queue_id = aws_sqs_queue.this[0].id // optional 141 | } 142 | } 143 | 144 | sns_notifications = { 145 | sns1 = { 146 | topic_arn = module.sns_topic1.sns_topic_arn 147 | events = ["s3:ObjectRemoved:Delete"] 148 | filter_prefix = "prefix3/" 149 | filter_suffix = ".csv" 150 | } 151 | 152 | sns2 = { 153 | topic_arn = module.sns_topic2.sns_topic_arn 154 | events = ["s3:ObjectRemoved:DeleteMarkerCreated"] 155 | } 156 | } 157 | 158 | # Creation of policy is handled outside of the module 159 | create_sqs_policy = false 160 | } 161 | -------------------------------------------------------------------------------- /examples/notification/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_bucket_id" { 2 | description = "The name of the bucket." 3 | value = module.s3_bucket.s3_bucket_id 4 | } 5 | 6 | output "s3_bucket_arn" { 7 | description = "The ARN of the bucket. Will be of format arn:aws:s3:::bucketname." 8 | value = module.s3_bucket.s3_bucket_arn 9 | } 10 | 11 | output "s3_bucket_bucket_domain_name" { 12 | description = "The bucket domain name. Will be of format bucketname.s3.amazonaws.com." 13 | value = module.s3_bucket.s3_bucket_bucket_domain_name 14 | } 15 | 16 | output "s3_bucket_bucket_regional_domain_name" { 17 | description = "The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL." 18 | value = module.s3_bucket.s3_bucket_bucket_regional_domain_name 19 | } 20 | 21 | output "s3_bucket_hosted_zone_id" { 22 | description = "The Route 53 Hosted Zone ID for this bucket's region." 23 | value = module.s3_bucket.s3_bucket_hosted_zone_id 24 | } 25 | 26 | output "s3_bucket_region" { 27 | description = "The AWS region this bucket resides in." 28 | value = module.s3_bucket.s3_bucket_region 29 | } 30 | 31 | output "s3_bucket_website_endpoint" { 32 | description = "The website endpoint, if the bucket is configured with a website. If not, this will be an empty string." 33 | value = module.s3_bucket.s3_bucket_website_endpoint 34 | } 35 | 36 | output "s3_bucket_website_domain" { 37 | 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. " 38 | value = module.s3_bucket.s3_bucket_website_domain 39 | } 40 | -------------------------------------------------------------------------------- /examples/notification/variables.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-s3-bucket/0d781fbb515b1f64c46bfba04153bef091d50fc5/examples/notification/variables.tf -------------------------------------------------------------------------------- /examples/notification/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.83" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 2.0" 12 | } 13 | null = { 14 | source = "hashicorp/null" 15 | version = ">= 2.0" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/object/README.md: -------------------------------------------------------------------------------- 1 | # S3 bucket object 2 | 3 | Configuration in this directory creates S3 bucket objects with different configurations. 4 | 5 | ## Usage 6 | 7 | To run this example you need to execute: 8 | 9 | ```bash 10 | $ terraform init 11 | $ terraform plan 12 | $ terraform apply 13 | ``` 14 | 15 | Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources. 16 | 17 | 18 | ## Requirements 19 | 20 | | Name | Version | 21 | |------|---------| 22 | | [terraform](#requirement\_terraform) | >= 1.0 | 23 | | [aws](#requirement\_aws) | >= 5.83 | 24 | | [random](#requirement\_random) | >= 2.0 | 25 | 26 | ## Providers 27 | 28 | | Name | Version | 29 | |------|---------| 30 | | [aws](#provider\_aws) | >= 5.83 | 31 | | [random](#provider\_random) | >= 2.0 | 32 | 33 | ## Modules 34 | 35 | | Name | Source | Version | 36 | |------|--------|---------| 37 | | [object](#module\_object) | ../../modules/object | n/a | 38 | | [object\_complete](#module\_object\_complete) | ../../modules/object | n/a | 39 | | [object\_locked](#module\_object\_locked) | ../../modules/object | n/a | 40 | | [object\_with\_override\_default\_tags](#module\_object\_with\_override\_default\_tags) | ../../modules/object | n/a | 41 | | [s3\_bucket](#module\_s3\_bucket) | ../../ | n/a | 42 | | [s3\_bucket\_with\_object\_lock](#module\_s3\_bucket\_with\_object\_lock) | ../../ | n/a | 43 | 44 | ## Resources 45 | 46 | | Name | Type | 47 | |------|------| 48 | | [aws_kms_key.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | 49 | | [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | 50 | 51 | ## Inputs 52 | 53 | No inputs. 54 | 55 | ## Outputs 56 | 57 | | Name | Description | 58 | |------|-------------| 59 | | [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | The ARN of the bucket. Will be of format arn:aws:s3:::bucketname. | 60 | | [s3\_bucket\_id](#output\_s3\_bucket\_id) | The name of the bucket. | 61 | | [s3\_object\_etag](#output\_s3\_object\_etag) | The ETag generated for the object (an MD5 sum of the object content). | 62 | | [s3\_object\_id](#output\_s3\_object\_id) | The key of S3 object | 63 | | [s3\_object\_version\_id](#output\_s3\_object\_version\_id) | A unique version ID value for the object, if bucket versioning is enabled. | 64 | 65 | -------------------------------------------------------------------------------- /examples/object/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | 4 | # Make it faster by skipping something 5 | skip_metadata_api_check = true 6 | skip_region_validation = true 7 | skip_credentials_validation = true 8 | 9 | default_tags { 10 | tags = { 11 | Example = "object" 12 | } 13 | } 14 | } 15 | 16 | locals { 17 | region = "eu-west-1" 18 | } 19 | 20 | module "object" { 21 | source = "../../modules/object" 22 | 23 | bucket = module.s3_bucket.s3_bucket_id 24 | key = "${random_pet.this.id}-local" 25 | 26 | file_source = "README.md" 27 | # content = file("README.md") 28 | # content_base64 = filebase64("README.md") 29 | 30 | tags = { 31 | Sensitive = "not-really" 32 | } 33 | } 34 | 35 | module "object_complete" { 36 | source = "../../modules/object" 37 | 38 | bucket = module.s3_bucket.s3_bucket_id 39 | key = "${random_pet.this.id}-complete" 40 | 41 | content = jsonencode({ data : "value" }) 42 | 43 | # acl = "public-read" 44 | storage_class = "ONEZONE_IA" 45 | force_destroy = true 46 | 47 | cache_control = "public; max-age=1200" 48 | content_disposition = "attachment; filename=\"invoice.pdf\"" 49 | content_encoding = "gzip" 50 | content_language = "en-US" 51 | content_type = "application/json" 52 | 53 | website_redirect = "https://www.google.com/" 54 | metadata = { 55 | key = "value1" 56 | another-key = "value2" 57 | } 58 | 59 | server_side_encryption = "aws:kms" 60 | kms_key_id = aws_kms_key.this.arn 61 | } 62 | 63 | module "object_locked" { 64 | source = "../../modules/object" 65 | 66 | bucket = module.s3_bucket_with_object_lock.s3_bucket_id 67 | key = "${random_pet.this.id}-locked" 68 | 69 | content = "some-content-locked-by-governance" 70 | 71 | force_destroy = true 72 | 73 | object_lock_legal_hold_status = true # boolean or string ("ON" or "OFF") 74 | object_lock_mode = "GOVERNANCE" 75 | object_lock_retain_until_date = formatdate("YYYY-MM-DD'T'hh:00:00Z", timeadd(timestamp(), "1h")) # some time in the future 76 | } 77 | 78 | module "object_with_override_default_tags" { 79 | source = "../../modules/object" 80 | 81 | bucket = module.s3_bucket.s3_bucket_id 82 | key = "${random_pet.this.id}-local-override-default-tags" 83 | 84 | override_default_tags = true 85 | 86 | file_source = "README.md" 87 | 88 | tags = { 89 | Override = "true" 90 | } 91 | } 92 | 93 | ################## 94 | # Extra resources 95 | ################## 96 | resource "random_pet" "this" { 97 | length = 2 98 | } 99 | 100 | resource "aws_kms_key" "this" { 101 | description = "KMS key for S3 object" 102 | deletion_window_in_days = 7 103 | } 104 | 105 | ############# 106 | # S3 buckets 107 | ############# 108 | module "s3_bucket" { 109 | source = "../../" 110 | 111 | bucket = random_pet.this.id 112 | force_destroy = true 113 | } 114 | 115 | module "s3_bucket_with_object_lock" { 116 | source = "../../" 117 | 118 | bucket = "${random_pet.this.id}-with-object-lock" 119 | force_destroy = true 120 | 121 | object_lock_enabled = true 122 | object_lock_configuration = { 123 | rule = { 124 | default_retention = { 125 | mode = "GOVERNANCE" 126 | days = 1 127 | } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /examples/object/outputs.tf: -------------------------------------------------------------------------------- 1 | # S3 object 2 | output "s3_object_id" { 3 | description = "The key of S3 object" 4 | value = module.object.s3_object_id 5 | } 6 | 7 | output "s3_object_etag" { 8 | description = "The ETag generated for the object (an MD5 sum of the object content)." 9 | value = module.object.s3_object_etag 10 | } 11 | 12 | output "s3_object_version_id" { 13 | description = "A unique version ID value for the object, if bucket versioning is enabled." 14 | value = module.object.s3_object_version_id 15 | } 16 | 17 | # S3 bucket 18 | output "s3_bucket_id" { 19 | description = "The name of the bucket." 20 | value = module.s3_bucket.s3_bucket_id 21 | } 22 | 23 | output "s3_bucket_arn" { 24 | description = "The ARN of the bucket. Will be of format arn:aws:s3:::bucketname." 25 | value = module.s3_bucket.s3_bucket_arn 26 | } 27 | -------------------------------------------------------------------------------- /examples/object/variables.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-s3-bucket/0d781fbb515b1f64c46bfba04153bef091d50fc5/examples/object/variables.tf -------------------------------------------------------------------------------- /examples/object/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.83" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 2.0" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/s3-analytics/README.md: -------------------------------------------------------------------------------- 1 | # S3 bucket Analytics Configurations 2 | 3 | Configuration in this directory creates an S3 bucket with several analytics configurations including a different destination for analytics reports generated. 4 | 5 | Please check [complete example](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/examples/complete) to see all other features supported by this module. 6 | 7 | 8 | ## Requirements 9 | 10 | | Name | Version | 11 | |------|---------| 12 | | [terraform](#requirement\_terraform) | >= 1.0 | 13 | | [aws](#requirement\_aws) | >= 5.83 | 14 | | [random](#requirement\_random) | >= 2.0 | 15 | 16 | ## Providers 17 | 18 | | Name | Version | 19 | |------|---------| 20 | | [aws](#provider\_aws) | >= 5.83 | 21 | | [random](#provider\_random) | >= 2.0 | 22 | 23 | ## Modules 24 | 25 | | Name | Source | Version | 26 | |------|--------|---------| 27 | | [analytics\_and\_inventory\_destination\_bucket](#module\_analytics\_and\_inventory\_destination\_bucket) | ../../ | n/a | 28 | | [analytics\_configuration\_bucket](#module\_analytics\_configuration\_bucket) | ../../ | n/a | 29 | | [analytics\_destination\_bucket](#module\_analytics\_destination\_bucket) | ../../ | n/a | 30 | | [inventory\_source\_bucket](#module\_inventory\_source\_bucket) | ../.. | n/a | 31 | 32 | ## Resources 33 | 34 | | Name | Type | 35 | |------|------| 36 | | [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | 37 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 38 | 39 | ## Inputs 40 | 41 | No inputs. 42 | 43 | ## Outputs 44 | 45 | | Name | Description | 46 | |------|-------------| 47 | | [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | The ARN of the bucket. Will be of format arn:aws:s3:::bucketname. | 48 | | [s3\_bucket\_bucket\_domain\_name](#output\_s3\_bucket\_bucket\_domain\_name) | The bucket domain name. Will be of format bucketname.s3.amazonaws.com. | 49 | | [s3\_bucket\_bucket\_regional\_domain\_name](#output\_s3\_bucket\_bucket\_regional\_domain\_name) | The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL. | 50 | | [s3\_bucket\_hosted\_zone\_id](#output\_s3\_bucket\_hosted\_zone\_id) | The Route 53 Hosted Zone ID for this bucket's region. | 51 | | [s3\_bucket\_id](#output\_s3\_bucket\_id) | The name of the bucket. | 52 | | [s3\_bucket\_region](#output\_s3\_bucket\_region) | The AWS region this bucket resides in. | 53 | | [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. | 54 | | [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. | 55 | 56 | -------------------------------------------------------------------------------- /examples/s3-analytics/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | bucket_name = "s3-bucket-${random_pet.this.id}" 3 | region = "eu-west-1" 4 | } 5 | 6 | provider "aws" { 7 | region = local.region 8 | 9 | # Make it faster by skipping something 10 | skip_metadata_api_check = true 11 | skip_region_validation = true 12 | skip_credentials_validation = true 13 | } 14 | 15 | data "aws_caller_identity" "current" {} 16 | 17 | module "analytics_configuration_bucket" { 18 | source = "../../" 19 | 20 | bucket = local.bucket_name 21 | 22 | force_destroy = true 23 | 24 | attach_analytics_destination_policy = true 25 | attach_policy = true 26 | analytics_self_source_destination = true 27 | 28 | versioning = { 29 | status = true 30 | mfa_delete = false 31 | } 32 | 33 | analytics_configuration = { 34 | 35 | # No exporting 36 | prefix_documents = { 37 | filter = { 38 | prefix = "documents/" 39 | } 40 | } 41 | 42 | # Same source and destination bucket 43 | tags = { 44 | filter = { 45 | tags = { 46 | production = "true" 47 | } 48 | } 49 | storage_class_analysis = { 50 | output_schema_version = "V_1" 51 | } 52 | } 53 | 54 | # Different destination bucket 55 | all = { 56 | storage_class_analysis = { 57 | destination_bucket_arn = module.analytics_destination_bucket.s3_bucket_arn 58 | prefix = "analytics" 59 | } 60 | } 61 | 62 | # Different destination shared with inventory destination 63 | example = { 64 | storage_class_analysis = { 65 | destination_bucket_arn = module.analytics_and_inventory_destination_bucket.s3_bucket_arn 66 | } 67 | } 68 | } 69 | } 70 | 71 | resource "random_pet" "this" { 72 | length = 2 73 | } 74 | 75 | module "analytics_destination_bucket" { 76 | source = "../../" 77 | 78 | bucket = "analytics-destination-${random_pet.this.id}" 79 | force_destroy = true 80 | attach_policy = true 81 | attach_analytics_destination_policy = true 82 | analytics_source_bucket_arn = module.analytics_configuration_bucket.s3_bucket_arn 83 | analytics_source_account_id = data.aws_caller_identity.current.id 84 | } 85 | 86 | # Inventory configuration for shared destination example 87 | module "inventory_source_bucket" { 88 | source = "../.." 89 | 90 | bucket = "inventory-source-${random_pet.this.id}" 91 | 92 | force_destroy = true 93 | 94 | inventory_configuration = { 95 | destination_other = { 96 | included_object_versions = "All" 97 | destination = { 98 | bucket_arn = module.analytics_and_inventory_destination_bucket.s3_bucket_arn 99 | format = "CSV" 100 | encryption = { 101 | encryption_type = "sse_s3" 102 | } 103 | } 104 | frequency = "Daily" 105 | optional_fields = ["Size", "EncryptionStatus", "StorageClass", "ChecksumAlgorithm"] 106 | } 107 | } 108 | } 109 | 110 | # Example of using the same destination bucket for analytics and inventory 111 | module "analytics_and_inventory_destination_bucket" { 112 | source = "../../" 113 | 114 | bucket = "analytics-and-inventory-destination-${random_pet.this.id}" 115 | force_destroy = true 116 | attach_policy = true 117 | 118 | # Analytics bucket policy settings 119 | attach_analytics_destination_policy = true 120 | analytics_source_bucket_arn = module.analytics_configuration_bucket.s3_bucket_arn 121 | analytics_source_account_id = data.aws_caller_identity.current.id 122 | 123 | # Inventory bucket policy settings 124 | attach_inventory_destination_policy = true 125 | inventory_source_bucket_arn = module.inventory_source_bucket.s3_bucket_arn 126 | inventory_source_account_id = data.aws_caller_identity.current.id 127 | } 128 | -------------------------------------------------------------------------------- /examples/s3-analytics/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_bucket_id" { 2 | description = "The name of the bucket." 3 | value = module.analytics_configuration_bucket.s3_bucket_id 4 | } 5 | 6 | output "s3_bucket_arn" { 7 | description = "The ARN of the bucket. Will be of format arn:aws:s3:::bucketname." 8 | value = module.analytics_configuration_bucket.s3_bucket_arn 9 | } 10 | 11 | output "s3_bucket_bucket_domain_name" { 12 | description = "The bucket domain name. Will be of format bucketname.s3.amazonaws.com." 13 | value = module.analytics_configuration_bucket.s3_bucket_bucket_domain_name 14 | } 15 | 16 | output "s3_bucket_bucket_regional_domain_name" { 17 | description = "The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL." 18 | value = module.analytics_configuration_bucket.s3_bucket_bucket_regional_domain_name 19 | } 20 | 21 | output "s3_bucket_hosted_zone_id" { 22 | description = "The Route 53 Hosted Zone ID for this bucket's region." 23 | value = module.analytics_configuration_bucket.s3_bucket_hosted_zone_id 24 | } 25 | 26 | output "s3_bucket_region" { 27 | description = "The AWS region this bucket resides in." 28 | value = module.analytics_configuration_bucket.s3_bucket_region 29 | } 30 | 31 | output "s3_bucket_website_endpoint" { 32 | description = "The website endpoint, if the bucket is configured with a website. If not, this will be an empty string." 33 | value = module.analytics_configuration_bucket.s3_bucket_website_endpoint 34 | } 35 | 36 | output "s3_bucket_website_domain" { 37 | 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. " 38 | value = module.analytics_configuration_bucket.s3_bucket_website_domain 39 | } 40 | -------------------------------------------------------------------------------- /examples/s3-analytics/variables.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-s3-bucket/0d781fbb515b1f64c46bfba04153bef091d50fc5/examples/s3-analytics/variables.tf -------------------------------------------------------------------------------- /examples/s3-analytics/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.83" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 2.0" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/s3-inventory/README.md: -------------------------------------------------------------------------------- 1 | # S3 bucket with Inventory Configurations 2 | 3 | Configuration in this directory creates an S3 bucket with several inventory configurations including a different source and destination for inventory reports generated. 4 | 5 | Please check [complete example](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/examples/complete) to see all other features supported by this module. 6 | 7 | 8 | ## Requirements 9 | 10 | | Name | Version | 11 | |------|---------| 12 | | [terraform](#requirement\_terraform) | >= 1.0 | 13 | | [aws](#requirement\_aws) | >= 5.83 | 14 | | [random](#requirement\_random) | >= 2.0 | 15 | 16 | ## Providers 17 | 18 | | Name | Version | 19 | |------|---------| 20 | | [aws](#provider\_aws) | >= 5.83 | 21 | | [random](#provider\_random) | >= 2.0 | 22 | 23 | ## Modules 24 | 25 | | Name | Source | Version | 26 | |------|--------|---------| 27 | | [inventory\_destination\_bucket](#module\_inventory\_destination\_bucket) | ../../ | n/a | 28 | | [inventory\_source\_bucket](#module\_inventory\_source\_bucket) | ../../ | n/a | 29 | | [kms](#module\_kms) | terraform-aws-modules/kms/aws | ~> 2.0 | 30 | | [multi\_inventory\_configurations\_bucket](#module\_multi\_inventory\_configurations\_bucket) | ../../ | n/a | 31 | 32 | ## Resources 33 | 34 | | Name | Type | 35 | |------|------| 36 | | [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | 37 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 38 | 39 | ## Inputs 40 | 41 | No inputs. 42 | 43 | ## Outputs 44 | 45 | | Name | Description | 46 | |------|-------------| 47 | | [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | The ARN of the bucket. Will be of format arn:aws:s3:::bucketname. | 48 | | [s3\_bucket\_bucket\_domain\_name](#output\_s3\_bucket\_bucket\_domain\_name) | The bucket domain name. Will be of format bucketname.s3.amazonaws.com. | 49 | | [s3\_bucket\_bucket\_regional\_domain\_name](#output\_s3\_bucket\_bucket\_regional\_domain\_name) | The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL. | 50 | | [s3\_bucket\_hosted\_zone\_id](#output\_s3\_bucket\_hosted\_zone\_id) | The Route 53 Hosted Zone ID for this bucket's region. | 51 | | [s3\_bucket\_id](#output\_s3\_bucket\_id) | The name of the bucket. | 52 | | [s3\_bucket\_region](#output\_s3\_bucket\_region) | The AWS region this bucket resides in. | 53 | | [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. | 54 | | [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. | 55 | 56 | -------------------------------------------------------------------------------- /examples/s3-inventory/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | bucket_name = "s3-bucket-${random_pet.this.id}" 3 | region = "eu-west-1" 4 | } 5 | 6 | provider "aws" { 7 | region = local.region 8 | 9 | # Make it faster by skipping something 10 | skip_metadata_api_check = true 11 | skip_region_validation = true 12 | skip_credentials_validation = true 13 | } 14 | 15 | data "aws_caller_identity" "current" {} 16 | 17 | module "multi_inventory_configurations_bucket" { 18 | source = "../../" 19 | 20 | bucket = local.bucket_name 21 | 22 | force_destroy = true 23 | 24 | attach_policy = true 25 | attach_inventory_destination_policy = true 26 | inventory_self_source_destination = true 27 | 28 | versioning = { 29 | status = true 30 | mfa_delete = false 31 | } 32 | 33 | inventory_configuration = { 34 | 35 | # Same source and destination buckets 36 | daily = { 37 | included_object_versions = "Current" 38 | destination = { 39 | format = "CSV" 40 | encryption = { 41 | encryption_type = "sse_kms" 42 | kms_key_id = module.kms.key_arn 43 | } 44 | } 45 | filter = { 46 | prefix = "documents/" 47 | } 48 | frequency = "Daily" 49 | } 50 | 51 | weekly = { 52 | included_object_versions = "All" 53 | destination = { 54 | format = "CSV" 55 | } 56 | frequency = "Weekly" 57 | } 58 | 59 | # Different destination bucket 60 | destination_other = { 61 | included_object_versions = "All" 62 | destination = { 63 | bucket_arn = module.inventory_destination_bucket.s3_bucket_arn 64 | format = "Parquet" 65 | encryption = { 66 | encryption_type = "sse_s3" 67 | } 68 | } 69 | frequency = "Weekly" 70 | optional_fields = ["Size", "EncryptionStatus", "StorageClass", "ChecksumAlgorithm"] 71 | } 72 | 73 | # Different source bucket 74 | source_other = { 75 | included_object_versions = "Current" 76 | bucket = module.inventory_source_bucket.s3_bucket_id 77 | destination = { 78 | format = "ORC" 79 | encryption = { 80 | encryption_type = "sse_s3" 81 | } 82 | } 83 | frequency = "Daily" 84 | } 85 | } 86 | } 87 | 88 | resource "random_pet" "this" { 89 | length = 2 90 | } 91 | 92 | # https://docs.aws.amazon.com/AmazonS3/latest/userguide/configure-inventory.html#configure-inventory-kms-key-policy 93 | module "kms" { 94 | source = "terraform-aws-modules/kms/aws" 95 | version = "~> 2.0" 96 | 97 | description = "Key example for Inventory S3 destination encyrption" 98 | deletion_window_in_days = 7 99 | key_statements = [ 100 | { 101 | sid = "s3InventoryPolicy" 102 | actions = [ 103 | "kms:GenerateDataKey", 104 | ] 105 | resources = ["*"] 106 | 107 | principals = [ 108 | { 109 | type = "Service" 110 | identifiers = ["s3.amazonaws.com"] 111 | } 112 | ] 113 | 114 | conditions = [ 115 | { 116 | test = "StringEquals" 117 | variable = "aws:SourceAccount" 118 | values = [ 119 | data.aws_caller_identity.current.id, 120 | ] 121 | }, 122 | { 123 | test = "ArnLike" 124 | variable = "aws:SourceARN" 125 | values = [ 126 | module.inventory_source_bucket.s3_bucket_arn, 127 | module.multi_inventory_configurations_bucket.s3_bucket_arn 128 | ] 129 | } 130 | ] 131 | } 132 | ] 133 | } 134 | 135 | module "inventory_destination_bucket" { 136 | source = "../../" 137 | 138 | bucket = "inventory-destination-${random_pet.this.id}" 139 | force_destroy = true 140 | attach_policy = true 141 | attach_inventory_destination_policy = true 142 | inventory_source_bucket_arn = module.multi_inventory_configurations_bucket.s3_bucket_arn 143 | inventory_source_account_id = data.aws_caller_identity.current.id 144 | } 145 | 146 | module "inventory_source_bucket" { 147 | source = "../../" 148 | 149 | bucket = "inventory-source-${random_pet.this.id}" 150 | force_destroy = true 151 | } 152 | -------------------------------------------------------------------------------- /examples/s3-inventory/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_bucket_id" { 2 | description = "The name of the bucket." 3 | value = module.multi_inventory_configurations_bucket.s3_bucket_id 4 | } 5 | 6 | output "s3_bucket_arn" { 7 | description = "The ARN of the bucket. Will be of format arn:aws:s3:::bucketname." 8 | value = module.multi_inventory_configurations_bucket.s3_bucket_arn 9 | } 10 | 11 | output "s3_bucket_bucket_domain_name" { 12 | description = "The bucket domain name. Will be of format bucketname.s3.amazonaws.com." 13 | value = module.multi_inventory_configurations_bucket.s3_bucket_bucket_domain_name 14 | } 15 | 16 | output "s3_bucket_bucket_regional_domain_name" { 17 | description = "The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL." 18 | value = module.multi_inventory_configurations_bucket.s3_bucket_bucket_regional_domain_name 19 | } 20 | 21 | output "s3_bucket_hosted_zone_id" { 22 | description = "The Route 53 Hosted Zone ID for this bucket's region." 23 | value = module.multi_inventory_configurations_bucket.s3_bucket_hosted_zone_id 24 | } 25 | 26 | output "s3_bucket_region" { 27 | description = "The AWS region this bucket resides in." 28 | value = module.multi_inventory_configurations_bucket.s3_bucket_region 29 | } 30 | 31 | output "s3_bucket_website_endpoint" { 32 | description = "The website endpoint, if the bucket is configured with a website. If not, this will be an empty string." 33 | value = module.multi_inventory_configurations_bucket.s3_bucket_website_endpoint 34 | } 35 | 36 | output "s3_bucket_website_domain" { 37 | 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. " 38 | value = module.multi_inventory_configurations_bucket.s3_bucket_website_domain 39 | } 40 | -------------------------------------------------------------------------------- /examples/s3-inventory/variables.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-s3-bucket/0d781fbb515b1f64c46bfba04153bef091d50fc5/examples/s3-inventory/variables.tf -------------------------------------------------------------------------------- /examples/s3-inventory/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.83" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 2.0" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/s3-replication/README.md: -------------------------------------------------------------------------------- 1 | # S3 bucket with Cross-Region Replication (CRR) enabled 2 | 3 | Configuration in this directory creates S3 bucket in one region and configures CRR to another bucket in another region. 4 | 5 | Please check [complete example](https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/examples/complete) to see all other features supported by this module. 6 | 7 | ## Usage 8 | 9 | To run this example you need to execute: 10 | 11 | ```bash 12 | $ terraform init 13 | $ terraform plan 14 | $ terraform apply 15 | ``` 16 | 17 | Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources. 18 | 19 | 20 | ## Requirements 21 | 22 | | Name | Version | 23 | |------|---------| 24 | | [terraform](#requirement\_terraform) | >= 1.0 | 25 | | [aws](#requirement\_aws) | >= 5.83 | 26 | | [random](#requirement\_random) | >= 2.0 | 27 | 28 | ## Providers 29 | 30 | | Name | Version | 31 | |------|---------| 32 | | [aws](#provider\_aws) | >= 5.83 | 33 | | [aws.replica](#provider\_aws.replica) | >= 5.83 | 34 | | [random](#provider\_random) | >= 2.0 | 35 | 36 | ## Modules 37 | 38 | | Name | Source | Version | 39 | |------|--------|---------| 40 | | [replica\_bucket](#module\_replica\_bucket) | ../../ | n/a | 41 | | [s3\_bucket](#module\_s3\_bucket) | ../../ | n/a | 42 | 43 | ## Resources 44 | 45 | | Name | Type | 46 | |------|------| 47 | | [aws_iam_policy.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | 48 | | [aws_iam_policy_attachment.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) | resource | 49 | | [aws_iam_role.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 50 | | [aws_kms_key.replica](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | 51 | | [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | 52 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 53 | 54 | ## Inputs 55 | 56 | No inputs. 57 | 58 | ## Outputs 59 | 60 | | Name | Description | 61 | |------|-------------| 62 | | [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | The ARN of the bucket. Will be of format arn:aws:s3:::bucketname. | 63 | | [s3\_bucket\_bucket\_domain\_name](#output\_s3\_bucket\_bucket\_domain\_name) | The bucket domain name. Will be of format bucketname.s3.amazonaws.com. | 64 | | [s3\_bucket\_bucket\_regional\_domain\_name](#output\_s3\_bucket\_bucket\_regional\_domain\_name) | The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL. | 65 | | [s3\_bucket\_hosted\_zone\_id](#output\_s3\_bucket\_hosted\_zone\_id) | The Route 53 Hosted Zone ID for this bucket's region. | 66 | | [s3\_bucket\_id](#output\_s3\_bucket\_id) | The name of the bucket. | 67 | | [s3\_bucket\_region](#output\_s3\_bucket\_region) | The AWS region this bucket resides in. | 68 | | [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. | 69 | | [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. | 70 | 71 | -------------------------------------------------------------------------------- /examples/s3-replication/iam.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_role" "replication" { 2 | name = "s3-bucket-replication-${random_pet.this.id}" 3 | 4 | assume_role_policy = < 18 | ## Requirements 19 | 20 | | Name | Version | 21 | |------|---------| 22 | | [terraform](#requirement\_terraform) | >= 1.0 | 23 | | [aws](#requirement\_aws) | >= 5.98 | 24 | | [random](#requirement\_random) | >= 2.0 | 25 | 26 | ## Providers 27 | 28 | | Name | Version | 29 | |------|---------| 30 | | [aws](#provider\_aws) | >= 5.98 | 31 | | [random](#provider\_random) | >= 2.0 | 32 | 33 | ## Modules 34 | 35 | | Name | Source | Version | 36 | |------|--------|---------| 37 | | [kms](#module\_kms) | terraform-aws-modules/kms/aws | ~> 2.0 | 38 | | [table\_bucket](#module\_table\_bucket) | ../../modules/table-bucket | n/a | 39 | 40 | ## Resources 41 | 42 | | Name | Type | 43 | |------|------| 44 | | [aws_s3tables_namespace.namespace](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3tables_namespace) | resource | 45 | | [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | 46 | | [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 47 | | [aws_region.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | 48 | 49 | ## Inputs 50 | 51 | No inputs. 52 | 53 | ## Outputs 54 | 55 | | Name | Description | 56 | |------|-------------| 57 | | [owner\_account\_id](#output\_owner\_account\_id) | Account ID of the account that owns the table bucket. | 58 | | [s3\_table\_arns](#output\_s3\_table\_arns) | The table ARNs. | 59 | | [s3\_table\_bucket\_arn](#output\_s3\_table\_bucket\_arn) | ARN of the table bucket. | 60 | | [s3\_table\_bucket\_created\_at](#output\_s3\_table\_bucket\_created\_at) | Date and time when the bucket was created. | 61 | | [s3\_table\_created\_at](#output\_s3\_table\_created\_at) | Dates and times when the tables were created. | 62 | | [s3\_table\_created\_by](#output\_s3\_table\_created\_by) | Account IDs of the accounts that created the tables | 63 | | [s3\_table\_metadata\_locations](#output\_s3\_table\_metadata\_locations) | Locations of table metadata. | 64 | | [s3\_table\_modified\_at](#output\_s3\_table\_modified\_at) | Dates and times when the tables was last modified. | 65 | | [s3\_table\_modified\_by](#output\_s3\_table\_modified\_by) | Account IDs of the accounts that last modified the tables. | 66 | | [s3\_table\_owner\_account\_ids](#output\_s3\_table\_owner\_account\_ids) | Account IDs of the accounts that owns the tables. | 67 | | [s3\_table\_types](#output\_s3\_table\_types) | Types of the tables. One of customer or aws. | 68 | | [s3\_table\_version\_tokens](#output\_s3\_table\_version\_tokens) | Identifiers for the current version of table data. | 69 | | [s3\_table\_warehouse\_locations](#output\_s3\_table\_warehouse\_locations) | S3 URIs pointing to the S3 Bucket that contains the table data. | 70 | 71 | -------------------------------------------------------------------------------- /examples/table-bucket/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | 4 | # Make it faster by skipping something 5 | skip_metadata_api_check = true 6 | skip_region_validation = true 7 | skip_credentials_validation = true 8 | } 9 | 10 | locals { 11 | bucket_name = "s3-table-bucket-${random_pet.this.id}" 12 | } 13 | 14 | data "aws_caller_identity" "this" {} 15 | 16 | data "aws_region" "this" {} 17 | 18 | module "table_bucket" { 19 | source = "../../modules/table-bucket" 20 | 21 | table_bucket_name = local.bucket_name 22 | 23 | encryption_configuration = { 24 | kms_key_arn = module.kms.key_arn 25 | sse_algorithm = "aws:kms" 26 | } 27 | 28 | maintenance_configuration = { 29 | iceberg_unreferenced_file_removal = { 30 | status = "enabled" 31 | 32 | settings = { 33 | non_current_days = 7 34 | unreferenced_days = 3 35 | } 36 | } 37 | } 38 | 39 | create_table_bucket_policy = true 40 | table_bucket_policy_statements = [ 41 | { 42 | effect = "Allow" 43 | principals = [{ 44 | type = "AWS" 45 | identifiers = [data.aws_caller_identity.this.account_id] 46 | }] 47 | actions = [ 48 | "s3tables:GetTableData", 49 | "s3tables:GetTableMetadataLocation" 50 | ] 51 | } 52 | ] 53 | 54 | tables = { 55 | table1 = { 56 | format = "ICEBERG" 57 | namespace = aws_s3tables_namespace.namespace.namespace 58 | 59 | encryption_configuration = { 60 | kms_key_arn = module.kms.key_arn 61 | sse_algorithm = "aws:kms" 62 | } 63 | 64 | maintenance_configuration = { 65 | iceberg_compaction = { 66 | status = "enabled" 67 | settings = { 68 | target_file_size_mb = 64 69 | } 70 | } 71 | iceberg_snapshot_management = { 72 | status = "enabled" 73 | settings = { 74 | max_snapshot_age_hours = 40 75 | min_snapshots_to_keep = 3 76 | } 77 | } 78 | } 79 | 80 | create_table_policy = true 81 | policy_statements = [ 82 | { 83 | sid = "DeleteTable" 84 | effect = "Allow" 85 | principals = [{ 86 | type = "AWS" 87 | identifiers = [data.aws_caller_identity.this.account_id] 88 | }] 89 | actions = [ 90 | "s3tables:DeleteTable", 91 | "s3tables:UpdateTableMetadataLocation", 92 | "s3tables:PutTableData", 93 | "s3tables:GetTableMetadataLocation" 94 | ] 95 | } 96 | ] 97 | } 98 | table2 = { 99 | format = "ICEBERG" 100 | name = "table2" 101 | namespace = aws_s3tables_namespace.namespace.namespace 102 | } 103 | table3 = { 104 | format = "ICEBERG" 105 | namespace = aws_s3tables_namespace.namespace.namespace 106 | } 107 | } 108 | } 109 | 110 | resource "random_pet" "this" { 111 | length = 2 112 | } 113 | 114 | resource "aws_s3tables_namespace" "namespace" { 115 | namespace = "example_namespace" 116 | table_bucket_arn = module.table_bucket.s3_table_bucket_arn 117 | } 118 | 119 | module "kms" { 120 | source = "terraform-aws-modules/kms/aws" 121 | version = "~> 2.0" 122 | 123 | description = "Key example for s3 table buckets" 124 | deletion_window_in_days = 7 125 | 126 | # https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-kms-permissions.html 127 | key_statements = [ 128 | { 129 | sid = "s3TablesMaintenancePolicy" 130 | actions = [ 131 | "kms:GenerateDataKey", 132 | "kms:Decrypt" 133 | ] 134 | resources = ["*"] 135 | 136 | principals = [ 137 | { 138 | type = "Service" 139 | identifiers = ["maintenance.s3tables.amazonaws.com"] 140 | } 141 | ] 142 | 143 | conditions = [ 144 | { 145 | test = "StringEquals" 146 | variable = "aws:SourceAccount" 147 | values = [ 148 | data.aws_caller_identity.this.id, 149 | ] 150 | }, 151 | { 152 | test = "StringLike" 153 | variable = "kms:EncryptionContext:aws:s3:arn" 154 | values = [ 155 | "arn:aws:s3tables:${data.aws_region.this.name}:${data.aws_caller_identity.this.account_id}:bucket/${local.bucket_name}/table/*" 156 | ] 157 | } 158 | ] 159 | } 160 | ] 161 | } 162 | -------------------------------------------------------------------------------- /examples/table-bucket/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_table_bucket_arn" { 2 | description = "ARN of the table bucket." 3 | value = module.table_bucket.s3_table_bucket_arn 4 | } 5 | 6 | output "s3_table_bucket_created_at" { 7 | description = "Date and time when the bucket was created." 8 | value = module.table_bucket.s3_table_bucket_created_at 9 | } 10 | 11 | output "owner_account_id" { 12 | description = "Account ID of the account that owns the table bucket." 13 | value = module.table_bucket.s3_table_bucket_owner_account_id 14 | } 15 | 16 | output "s3_table_arns" { 17 | description = "The table ARNs." 18 | value = module.table_bucket.s3_table_arns 19 | } 20 | 21 | output "s3_table_created_at" { 22 | description = "Dates and times when the tables were created." 23 | value = module.table_bucket.s3_table_created_at 24 | } 25 | 26 | output "s3_table_created_by" { 27 | description = "Account IDs of the accounts that created the tables" 28 | value = module.table_bucket.s3_table_created_by 29 | } 30 | 31 | output "s3_table_metadata_locations" { 32 | description = "Locations of table metadata." 33 | value = module.table_bucket.s3_table_metadata_locations 34 | } 35 | 36 | output "s3_table_modified_at" { 37 | description = "Dates and times when the tables was last modified." 38 | value = module.table_bucket.s3_table_modified_at 39 | } 40 | 41 | output "s3_table_modified_by" { 42 | description = "Account IDs of the accounts that last modified the tables." 43 | value = module.table_bucket.s3_table_modified_by 44 | } 45 | 46 | output "s3_table_owner_account_ids" { 47 | description = "Account IDs of the accounts that owns the tables." 48 | value = module.table_bucket.s3_table_owner_account_ids 49 | } 50 | 51 | output "s3_table_types" { 52 | description = "Types of the tables. One of customer or aws." 53 | value = module.table_bucket.s3_table_types 54 | } 55 | 56 | output "s3_table_version_tokens" { 57 | description = "Identifiers for the current version of table data." 58 | value = module.table_bucket.s3_table_version_tokens 59 | } 60 | 61 | output "s3_table_warehouse_locations" { 62 | description = "S3 URIs pointing to the S3 Bucket that contains the table data." 63 | value = module.table_bucket.s3_table_warehouse_locations 64 | } 65 | -------------------------------------------------------------------------------- /examples/table-bucket/variables.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-s3-bucket/0d781fbb515b1f64c46bfba04153bef091d50fc5/examples/table-bucket/variables.tf -------------------------------------------------------------------------------- /examples/table-bucket/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.98" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 2.0" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/account-public-access/README.md: -------------------------------------------------------------------------------- 1 | # S3 account-level Public Access Block 2 | 3 | Manages S3 account-level Public Access Block configuration. 4 | 5 | ## Note 6 | 7 | Each AWS account may only have one S3 Public Access Block configuration. 8 | 9 | 10 | ## Requirements 11 | 12 | | Name | Version | 13 | |------|---------| 14 | | [terraform](#requirement\_terraform) | >= 1.0 | 15 | | [aws](#requirement\_aws) | >= 3.74 | 16 | 17 | ## Providers 18 | 19 | | Name | Version | 20 | |------|---------| 21 | | [aws](#provider\_aws) | >= 3.74 | 22 | 23 | ## Modules 24 | 25 | No modules. 26 | 27 | ## Resources 28 | 29 | | Name | Type | 30 | |------|------| 31 | | [aws_s3_account_public_access_block.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_account_public_access_block) | resource | 32 | 33 | ## Inputs 34 | 35 | | Name | Description | Type | Default | Required | 36 | |------|-------------|------|---------|:--------:| 37 | | [account\_id](#input\_account\_id) | AWS account ID | `string` | `null` | no | 38 | | [block\_public\_acls](#input\_block\_public\_acls) | Whether Amazon S3 should block public ACLs for buckets in this account. | `bool` | `false` | no | 39 | | [block\_public\_policy](#input\_block\_public\_policy) | Whether Amazon S3 should block public bucket policies for buckets in this account. | `bool` | `false` | no | 40 | | [create](#input\_create) | Whether to create this resource or not? | `bool` | `true` | no | 41 | | [ignore\_public\_acls](#input\_ignore\_public\_acls) | Whether Amazon S3 should ignore public ACLs for buckets in this account. | `bool` | `false` | no | 42 | | [restrict\_public\_buckets](#input\_restrict\_public\_buckets) | Whether Amazon S3 should restrict public bucket policies for buckets in this account. | `bool` | `false` | no | 43 | 44 | ## Outputs 45 | 46 | | Name | Description | 47 | |------|-------------| 48 | | [s3\_account\_public\_access\_block\_id](#output\_s3\_account\_public\_access\_block\_id) | AWS account ID | 49 | 50 | -------------------------------------------------------------------------------- /modules/account-public-access/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_s3_account_public_access_block" "this" { 2 | count = var.create ? 1 : 0 3 | 4 | account_id = var.account_id 5 | 6 | block_public_acls = var.block_public_acls 7 | block_public_policy = var.block_public_policy 8 | ignore_public_acls = var.ignore_public_acls 9 | restrict_public_buckets = var.restrict_public_buckets 10 | } 11 | -------------------------------------------------------------------------------- /modules/account-public-access/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_account_public_access_block_id" { 2 | description = "AWS account ID" 3 | value = try(aws_s3_account_public_access_block.this[0].id, "") 4 | } 5 | -------------------------------------------------------------------------------- /modules/account-public-access/variables.tf: -------------------------------------------------------------------------------- 1 | variable "create" { 2 | description = "Whether to create this resource or not?" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "account_id" { 8 | description = "AWS account ID" 9 | type = string 10 | default = null 11 | } 12 | 13 | variable "block_public_acls" { 14 | description = "Whether Amazon S3 should block public ACLs for buckets in this account." 15 | type = bool 16 | default = false 17 | } 18 | 19 | variable "block_public_policy" { 20 | description = "Whether Amazon S3 should block public bucket policies for buckets in this account." 21 | type = bool 22 | default = false 23 | } 24 | 25 | variable "ignore_public_acls" { 26 | description = "Whether Amazon S3 should ignore public ACLs for buckets in this account." 27 | type = bool 28 | default = false 29 | } 30 | 31 | variable "restrict_public_buckets" { 32 | description = "Whether Amazon S3 should restrict public bucket policies for buckets in this account." 33 | type = bool 34 | default = false 35 | } 36 | -------------------------------------------------------------------------------- /modules/account-public-access/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 3.74" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/notification/README.md: -------------------------------------------------------------------------------- 1 | # S3 bucket notification 2 | 3 | Creates S3 bucket notification resource with all supported types of deliveries: AWS Lambda, SQS Queue, SNS Topic. 4 | 5 | 6 | ## Requirements 7 | 8 | | Name | Version | 9 | |------|---------| 10 | | [terraform](#requirement\_terraform) | >= 1.0 | 11 | | [aws](#requirement\_aws) | >= 3.74 | 12 | 13 | ## Providers 14 | 15 | | Name | Version | 16 | |------|---------| 17 | | [aws](#provider\_aws) | >= 3.74 | 18 | 19 | ## Modules 20 | 21 | No modules. 22 | 23 | ## Resources 24 | 25 | | Name | Type | 26 | |------|------| 27 | | [aws_lambda_permission.allow](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource | 28 | | [aws_s3_bucket_notification.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_notification) | resource | 29 | | [aws_sns_topic_policy.allow](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_policy) | resource | 30 | | [aws_sqs_queue_policy.allow](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue_policy) | resource | 31 | | [aws_arn.queue](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | data source | 32 | | [aws_iam_policy_document.sns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 33 | | [aws_iam_policy_document.sqs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 34 | | [aws_partition.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | 35 | 36 | ## Inputs 37 | 38 | | Name | Description | Type | Default | Required | 39 | |------|-------------|------|---------|:--------:| 40 | | [bucket](#input\_bucket) | Name of S3 bucket to use | `string` | `""` | no | 41 | | [bucket\_arn](#input\_bucket\_arn) | ARN of S3 bucket to use in policies | `string` | `null` | no | 42 | | [create](#input\_create) | Whether to create this resource or not? | `bool` | `true` | no | 43 | | [create\_lambda\_permission](#input\_create\_lambda\_permission) | Whether to create Lambda permissions or not? | `bool` | `true` | no | 44 | | [create\_sns\_policy](#input\_create\_sns\_policy) | Whether to create a policy for SNS permissions or not? | `bool` | `true` | no | 45 | | [create\_sqs\_policy](#input\_create\_sqs\_policy) | Whether to create a policy for SQS permissions or not? | `bool` | `true` | no | 46 | | [eventbridge](#input\_eventbridge) | Whether to enable Amazon EventBridge notifications | `bool` | `null` | no | 47 | | [lambda\_notifications](#input\_lambda\_notifications) | Map of S3 bucket notifications to Lambda function | `any` | `{}` | no | 48 | | [sns\_notifications](#input\_sns\_notifications) | Map of S3 bucket notifications to SNS topic | `any` | `{}` | no | 49 | | [sqs\_notifications](#input\_sqs\_notifications) | Map of S3 bucket notifications to SQS queue | `any` | `{}` | no | 50 | 51 | ## Outputs 52 | 53 | | Name | Description | 54 | |------|-------------| 55 | | [s3\_bucket\_notification\_id](#output\_s3\_bucket\_notification\_id) | ID of S3 bucket | 56 | 57 | -------------------------------------------------------------------------------- /modules/notification/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_partition" "this" {} 2 | 3 | locals { 4 | bucket_arn = coalesce(var.bucket_arn, "arn:${data.aws_partition.this.partition}:s3:::${var.bucket}") 5 | 6 | # Convert from "arn:aws:sqs:eu-west-1:835367859851:bold-starling-0" into "https://sqs.eu-west-1.amazonaws.com/835367859851/bold-starling-0" if queue_id was not specified 7 | # queue_url used in aws_sqs_queue_policy is not the same as arn which is used in all other places 8 | queue_ids = { for k, v in var.sqs_notifications : k => format("https://%s.%s.amazonaws.com/%s/%s", data.aws_arn.queue[k].service, data.aws_arn.queue[k].region, data.aws_arn.queue[k].account, data.aws_arn.queue[k].resource) if try(v.queue_id, "") == "" } 9 | } 10 | 11 | resource "aws_s3_bucket_notification" "this" { 12 | count = var.create ? 1 : 0 13 | 14 | bucket = var.bucket 15 | 16 | eventbridge = var.eventbridge 17 | 18 | dynamic "lambda_function" { 19 | for_each = var.lambda_notifications 20 | 21 | content { 22 | id = try(lambda_function.value.id, lambda_function.key) 23 | lambda_function_arn = lambda_function.value.function_arn 24 | events = lambda_function.value.events 25 | filter_prefix = try(lambda_function.value.filter_prefix, null) 26 | filter_suffix = try(lambda_function.value.filter_suffix, null) 27 | } 28 | } 29 | 30 | dynamic "queue" { 31 | for_each = var.sqs_notifications 32 | 33 | content { 34 | id = try(queue.value.id, queue.key) 35 | queue_arn = queue.value.queue_arn 36 | events = queue.value.events 37 | filter_prefix = try(queue.value.filter_prefix, null) 38 | filter_suffix = try(queue.value.filter_suffix, null) 39 | } 40 | } 41 | 42 | dynamic "topic" { 43 | for_each = var.sns_notifications 44 | 45 | content { 46 | id = try(topic.value.id, topic.key) 47 | topic_arn = topic.value.topic_arn 48 | events = topic.value.events 49 | filter_prefix = try(topic.value.filter_prefix, null) 50 | filter_suffix = try(topic.value.filter_suffix, null) 51 | } 52 | } 53 | 54 | depends_on = [ 55 | aws_lambda_permission.allow, 56 | aws_sqs_queue_policy.allow, 57 | aws_sns_topic_policy.allow, 58 | ] 59 | } 60 | 61 | # Lambda 62 | resource "aws_lambda_permission" "allow" { 63 | for_each = { for k, v in var.lambda_notifications : k => v if var.create_lambda_permission } 64 | 65 | statement_id_prefix = "AllowLambdaS3BucketNotification-" 66 | action = "lambda:InvokeFunction" 67 | function_name = each.value.function_name 68 | qualifier = try(each.value.qualifier, null) 69 | principal = "s3.amazonaws.com" 70 | source_arn = local.bucket_arn 71 | source_account = try(each.value.source_account, null) 72 | } 73 | 74 | # SQS Queue 75 | data "aws_arn" "queue" { 76 | for_each = var.sqs_notifications 77 | 78 | arn = each.value.queue_arn 79 | } 80 | 81 | data "aws_iam_policy_document" "sqs" { 82 | for_each = { for k, v in var.sqs_notifications : k => v if var.create_sqs_policy } 83 | 84 | version = "2012-10-17" 85 | 86 | statement { 87 | sid = "AllowSQSS3BucketNotification" 88 | 89 | effect = "Allow" 90 | 91 | actions = [ 92 | "sqs:SendMessage", 93 | ] 94 | 95 | principals { 96 | type = "Service" 97 | identifiers = ["s3.amazonaws.com"] 98 | } 99 | 100 | resources = [each.value.queue_arn] 101 | 102 | condition { 103 | test = "ArnEquals" 104 | variable = "aws:SourceArn" 105 | values = [local.bucket_arn] 106 | } 107 | } 108 | } 109 | 110 | resource "aws_sqs_queue_policy" "allow" { 111 | for_each = { for k, v in var.sqs_notifications : k => v if var.create_sqs_policy } 112 | 113 | queue_url = try(each.value.queue_id, local.queue_ids[each.key], null) 114 | policy = data.aws_iam_policy_document.sqs[each.key].json 115 | } 116 | 117 | # SNS Topic 118 | data "aws_iam_policy_document" "sns" { 119 | for_each = { for k, v in var.sns_notifications : k => v if var.create_sns_policy } 120 | 121 | statement { 122 | sid = "AllowSNSS3BucketNotification" 123 | 124 | effect = "Allow" 125 | 126 | actions = [ 127 | "sns:Publish", 128 | ] 129 | 130 | principals { 131 | type = "Service" 132 | identifiers = ["s3.amazonaws.com"] 133 | } 134 | 135 | resources = [each.value.topic_arn] 136 | 137 | condition { 138 | test = "ArnEquals" 139 | variable = "aws:SourceArn" 140 | values = [local.bucket_arn] 141 | } 142 | } 143 | } 144 | 145 | resource "aws_sns_topic_policy" "allow" { 146 | for_each = { for k, v in var.sns_notifications : k => v if var.create_sns_policy } 147 | 148 | arn = each.value.topic_arn 149 | policy = data.aws_iam_policy_document.sns[each.key].json 150 | } 151 | -------------------------------------------------------------------------------- /modules/notification/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_bucket_notification_id" { 2 | description = "ID of S3 bucket" 3 | value = try(aws_s3_bucket_notification.this[0].id, "") 4 | } 5 | -------------------------------------------------------------------------------- /modules/notification/variables.tf: -------------------------------------------------------------------------------- 1 | variable "create" { 2 | description = "Whether to create this resource or not?" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "create_sns_policy" { 8 | description = "Whether to create a policy for SNS permissions or not?" 9 | type = bool 10 | default = true 11 | } 12 | 13 | variable "create_sqs_policy" { 14 | description = "Whether to create a policy for SQS permissions or not?" 15 | type = bool 16 | default = true 17 | } 18 | 19 | variable "create_lambda_permission" { 20 | description = "Whether to create Lambda permissions or not?" 21 | type = bool 22 | default = true 23 | } 24 | 25 | variable "bucket" { 26 | description = "Name of S3 bucket to use" 27 | type = string 28 | default = "" 29 | } 30 | 31 | variable "bucket_arn" { 32 | description = "ARN of S3 bucket to use in policies" 33 | type = string 34 | default = null 35 | } 36 | 37 | variable "eventbridge" { 38 | description = "Whether to enable Amazon EventBridge notifications" 39 | type = bool 40 | default = null 41 | } 42 | 43 | variable "lambda_notifications" { 44 | description = "Map of S3 bucket notifications to Lambda function" 45 | type = any 46 | default = {} 47 | } 48 | 49 | variable "sqs_notifications" { 50 | description = "Map of S3 bucket notifications to SQS queue" 51 | type = any 52 | default = {} 53 | } 54 | 55 | variable "sns_notifications" { 56 | description = "Map of S3 bucket notifications to SNS topic" 57 | type = any 58 | default = {} 59 | } 60 | -------------------------------------------------------------------------------- /modules/notification/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 3.74" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/object/README.md: -------------------------------------------------------------------------------- 1 | # S3 bucket object 2 | 3 | Creates S3 bucket objects with different configurations. 4 | 5 | 6 | ## Requirements 7 | 8 | | Name | Version | 9 | |------|---------| 10 | | [terraform](#requirement\_terraform) | >= 1.0 | 11 | | [aws](#requirement\_aws) | >= 5.24 | 12 | 13 | ## Providers 14 | 15 | | Name | Version | 16 | |------|---------| 17 | | [aws](#provider\_aws) | >= 5.24 | 18 | 19 | ## Modules 20 | 21 | No modules. 22 | 23 | ## Resources 24 | 25 | | Name | Type | 26 | |------|------| 27 | | [aws_s3_object.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | 28 | 29 | ## Inputs 30 | 31 | | Name | Description | Type | Default | Required | 32 | |------|-------------|------|---------|:--------:| 33 | | [acl](#input\_acl) | The canned ACL to apply. Valid values are private, public-read, public-read-write, aws-exec-read, authenticated-read, bucket-owner-read, and bucket-owner-full-control. Defaults to private. | `string` | `null` | no | 34 | | [bucket](#input\_bucket) | The name of the bucket to put the file in. Alternatively, an S3 access point ARN can be specified. | `string` | `""` | no | 35 | | [bucket\_key\_enabled](#input\_bucket\_key\_enabled) | Whether or not to use Amazon S3 Bucket Keys for SSE-KMS. | `bool` | `null` | no | 36 | | [cache\_control](#input\_cache\_control) | Specifies caching behavior along the request/reply chain. | `string` | `null` | no | 37 | | [content](#input\_content) | Literal string value to use as the object content, which will be uploaded as UTF-8-encoded text. | `string` | `null` | no | 38 | | [content\_base64](#input\_content\_base64) | Base64-encoded data that will be decoded and uploaded as raw bytes for the object content. This allows safely uploading non-UTF8 binary data, but is recommended only for small content such as the result of the gzipbase64 function with small text strings. For larger objects, use source to stream the content from a disk file. | `string` | `null` | no | 39 | | [content\_disposition](#input\_content\_disposition) | Specifies presentational information for the object. | `string` | `null` | no | 40 | | [content\_encoding](#input\_content\_encoding) | Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field. | `string` | `null` | no | 41 | | [content\_language](#input\_content\_language) | The language the content is in e.g. en-US or en-GB. | `string` | `null` | no | 42 | | [content\_type](#input\_content\_type) | A standard MIME type describing the format of the object data, e.g. application/octet-stream. All Valid MIME Types are valid for this input. | `string` | `null` | no | 43 | | [create](#input\_create) | Whether to create this resource or not? | `bool` | `true` | no | 44 | | [etag](#input\_etag) | Used to trigger updates. This attribute is not compatible with KMS encryption, kms\_key\_id or server\_side\_encryption = "aws:kms". | `string` | `null` | no | 45 | | [file\_source](#input\_file\_source) | The path to a file that will be read and uploaded as raw bytes for the object content. | `string` | `null` | no | 46 | | [force\_destroy](#input\_force\_destroy) | Allow the object to be deleted by removing any legal hold on any object version. Default is false. This value should be set to true only if the bucket has S3 object lock enabled. | `bool` | `false` | no | 47 | | [key](#input\_key) | The name of the object once it is in the bucket. | `string` | `""` | no | 48 | | [kms\_key\_id](#input\_kms\_key\_id) | Amazon Resource Name (ARN) of the KMS Key to use for object encryption. If the S3 Bucket has server-side encryption enabled, that value will automatically be used. If referencing the aws\_kms\_key resource, use the arn attribute. If referencing the aws\_kms\_alias data source or resource, use the target\_key\_arn attribute. Terraform will only perform drift detection if a configuration value is provided. | `string` | `null` | no | 49 | | [metadata](#input\_metadata) | A map of keys/values to provision metadata (will be automatically prefixed by x-amz-meta-, note that only lowercase label are currently supported by the AWS Go API). | `map(string)` | `{}` | no | 50 | | [object\_lock\_legal\_hold\_status](#input\_object\_lock\_legal\_hold\_status) | The legal hold status that you want to apply to the specified object. Valid values are ON and OFF. | `string` | `null` | no | 51 | | [object\_lock\_mode](#input\_object\_lock\_mode) | The object lock retention mode that you want to apply to this object. Valid values are GOVERNANCE and COMPLIANCE. | `string` | `null` | no | 52 | | [object\_lock\_retain\_until\_date](#input\_object\_lock\_retain\_until\_date) | The date and time, in RFC3339 format, when this object's object lock will expire. | `string` | `null` | no | 53 | | [override\_default\_tags](#input\_override\_default\_tags) | Ignore provider default\_tags. S3 objects support a maximum of 10 tags. | `bool` | `false` | no | 54 | | [server\_side\_encryption](#input\_server\_side\_encryption) | Specifies server-side encryption of the object in S3. Valid values are "AES256" and "aws:kms". | `string` | `null` | no | 55 | | [source\_hash](#input\_source\_hash) | Triggers updates like etag but useful to address etag encryption limitations. Set using filemd5("path/to/source") (Terraform 0.11.12 or later). (The value is only stored in state and not saved by AWS.) | `string` | `null` | no | 56 | | [storage\_class](#input\_storage\_class) | Specifies the desired Storage Class for the object. Can be either STANDARD, REDUCED\_REDUNDANCY, ONEZONE\_IA, INTELLIGENT\_TIERING, GLACIER, DEEP\_ARCHIVE, or STANDARD\_IA. Defaults to STANDARD. | `string` | `null` | no | 57 | | [tags](#input\_tags) | A map of tags to assign to the object. | `map(string)` | `{}` | no | 58 | | [website\_redirect](#input\_website\_redirect) | Specifies a target URL for website redirect. | `string` | `null` | no | 59 | 60 | ## Outputs 61 | 62 | | Name | Description | 63 | |------|-------------| 64 | | [s3\_object\_etag](#output\_s3\_object\_etag) | The ETag generated for the object (an MD5 sum of the object content). | 65 | | [s3\_object\_id](#output\_s3\_object\_id) | The key of S3 object | 66 | | [s3\_object\_version\_id](#output\_s3\_object\_version\_id) | A unique version ID value for the object, if bucket versioning is enabled. | 67 | 68 | -------------------------------------------------------------------------------- /modules/object/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_s3_object" "this" { 2 | count = var.create ? 1 : 0 3 | 4 | bucket = var.bucket 5 | key = var.key 6 | force_destroy = var.force_destroy 7 | 8 | acl = var.acl 9 | storage_class = try(upper(var.storage_class), var.storage_class) 10 | 11 | source = var.file_source 12 | content = var.content 13 | content_base64 = var.content_base64 14 | etag = var.etag 15 | 16 | cache_control = var.cache_control 17 | content_disposition = var.content_disposition 18 | content_encoding = var.content_encoding 19 | content_language = var.content_language 20 | content_type = var.content_type 21 | website_redirect = var.website_redirect 22 | metadata = var.metadata 23 | 24 | server_side_encryption = var.server_side_encryption 25 | kms_key_id = var.kms_key_id 26 | bucket_key_enabled = var.bucket_key_enabled 27 | 28 | object_lock_legal_hold_status = try(tobool(var.object_lock_legal_hold_status) ? "ON" : upper(var.object_lock_legal_hold_status), var.object_lock_legal_hold_status) 29 | object_lock_mode = try(upper(var.object_lock_mode), var.object_lock_mode) 30 | object_lock_retain_until_date = var.object_lock_retain_until_date 31 | 32 | source_hash = var.source_hash 33 | 34 | tags = var.tags 35 | 36 | dynamic "override_provider" { 37 | for_each = var.override_default_tags ? [true] : [] 38 | 39 | content { 40 | default_tags { 41 | tags = {} 42 | } 43 | } 44 | } 45 | 46 | lifecycle { 47 | ignore_changes = [object_lock_retain_until_date] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /modules/object/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_object_id" { 2 | description = "The key of S3 object" 3 | value = try(aws_s3_object.this[0].id, "") 4 | } 5 | 6 | output "s3_object_etag" { 7 | description = "The ETag generated for the object (an MD5 sum of the object content)." 8 | value = try(aws_s3_object.this[0].etag, "") 9 | } 10 | 11 | output "s3_object_version_id" { 12 | description = "A unique version ID value for the object, if bucket versioning is enabled." 13 | value = try(aws_s3_object.this[0].version_id, "") 14 | } 15 | -------------------------------------------------------------------------------- /modules/object/variables.tf: -------------------------------------------------------------------------------- 1 | variable "create" { 2 | description = "Whether to create this resource or not?" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "bucket" { 8 | description = "The name of the bucket to put the file in. Alternatively, an S3 access point ARN can be specified." 9 | type = string 10 | default = "" 11 | } 12 | 13 | variable "key" { 14 | description = "The name of the object once it is in the bucket." 15 | type = string 16 | default = "" 17 | } 18 | 19 | variable "file_source" { 20 | description = "The path to a file that will be read and uploaded as raw bytes for the object content." 21 | type = string 22 | default = null 23 | } 24 | 25 | variable "content" { 26 | description = "Literal string value to use as the object content, which will be uploaded as UTF-8-encoded text." 27 | type = string 28 | default = null 29 | } 30 | 31 | variable "content_base64" { 32 | description = "Base64-encoded data that will be decoded and uploaded as raw bytes for the object content. This allows safely uploading non-UTF8 binary data, but is recommended only for small content such as the result of the gzipbase64 function with small text strings. For larger objects, use source to stream the content from a disk file." 33 | type = string 34 | default = null 35 | } 36 | 37 | variable "acl" { 38 | description = "The canned ACL to apply. Valid values are private, public-read, public-read-write, aws-exec-read, authenticated-read, bucket-owner-read, and bucket-owner-full-control. Defaults to private." 39 | type = string 40 | default = null 41 | } 42 | 43 | variable "cache_control" { 44 | description = "Specifies caching behavior along the request/reply chain." 45 | type = string # map? 46 | default = null 47 | } 48 | 49 | variable "content_disposition" { 50 | description = "Specifies presentational information for the object." 51 | type = string # map? 52 | default = null 53 | } 54 | 55 | variable "content_encoding" { 56 | description = "Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field." 57 | type = string 58 | default = null 59 | } 60 | 61 | variable "content_language" { 62 | description = "The language the content is in e.g. en-US or en-GB." 63 | type = string 64 | default = null 65 | } 66 | 67 | variable "content_type" { 68 | description = "A standard MIME type describing the format of the object data, e.g. application/octet-stream. All Valid MIME Types are valid for this input." 69 | type = string 70 | default = null 71 | } 72 | 73 | variable "website_redirect" { 74 | description = "Specifies a target URL for website redirect." 75 | type = string 76 | default = null 77 | } 78 | 79 | variable "storage_class" { 80 | description = "Specifies the desired Storage Class for the object. Can be either STANDARD, REDUCED_REDUNDANCY, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE, or STANDARD_IA. Defaults to STANDARD." 81 | type = string 82 | default = null 83 | } 84 | 85 | variable "etag" { 86 | description = "Used to trigger updates. This attribute is not compatible with KMS encryption, kms_key_id or server_side_encryption = \"aws:kms\"." 87 | type = string 88 | default = null 89 | } 90 | 91 | variable "server_side_encryption" { 92 | description = "Specifies server-side encryption of the object in S3. Valid values are \"AES256\" and \"aws:kms\"." 93 | type = string 94 | default = null 95 | } 96 | 97 | variable "kms_key_id" { 98 | description = "Amazon Resource Name (ARN) of the KMS Key to use for object encryption. If the S3 Bucket has server-side encryption enabled, that value will automatically be used. If referencing the aws_kms_key resource, use the arn attribute. If referencing the aws_kms_alias data source or resource, use the target_key_arn attribute. Terraform will only perform drift detection if a configuration value is provided." 99 | type = string 100 | default = null 101 | } 102 | 103 | variable "bucket_key_enabled" { 104 | description = "Whether or not to use Amazon S3 Bucket Keys for SSE-KMS." 105 | type = bool 106 | default = null 107 | } 108 | 109 | variable "metadata" { 110 | description = "A map of keys/values to provision metadata (will be automatically prefixed by x-amz-meta-, note that only lowercase label are currently supported by the AWS Go API)." 111 | type = map(string) 112 | default = {} 113 | } 114 | 115 | variable "tags" { 116 | description = "A map of tags to assign to the object." 117 | type = map(string) 118 | default = {} 119 | } 120 | 121 | variable "force_destroy" { 122 | description = "Allow the object to be deleted by removing any legal hold on any object version. Default is false. This value should be set to true only if the bucket has S3 object lock enabled." 123 | type = bool 124 | default = false 125 | } 126 | 127 | variable "object_lock_legal_hold_status" { 128 | description = "The legal hold status that you want to apply to the specified object. Valid values are ON and OFF." 129 | type = string 130 | default = null 131 | } 132 | 133 | variable "object_lock_mode" { 134 | description = "The object lock retention mode that you want to apply to this object. Valid values are GOVERNANCE and COMPLIANCE." 135 | type = string 136 | default = null 137 | } 138 | 139 | variable "object_lock_retain_until_date" { 140 | description = "The date and time, in RFC3339 format, when this object's object lock will expire." 141 | type = string 142 | default = null 143 | } 144 | 145 | variable "source_hash" { 146 | description = "Triggers updates like etag but useful to address etag encryption limitations. Set using filemd5(\"path/to/source\") (Terraform 0.11.12 or later). (The value is only stored in state and not saved by AWS.)" 147 | type = string 148 | default = null 149 | } 150 | 151 | variable "override_default_tags" { 152 | description = "Ignore provider default_tags. S3 objects support a maximum of 10 tags." 153 | type = bool 154 | default = false 155 | } 156 | -------------------------------------------------------------------------------- /modules/object/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.24" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/table-bucket/README.md: -------------------------------------------------------------------------------- 1 | # S3 Table Bucket 2 | 3 | Creates S3 Table Bucket and Tables with various configurations. 4 | 5 | 6 | ## Requirements 7 | 8 | | Name | Version | 9 | |------|---------| 10 | | [terraform](#requirement\_terraform) | >= 1.0 | 11 | | [aws](#requirement\_aws) | >= 5.98 | 12 | 13 | ## Providers 14 | 15 | | Name | Version | 16 | |------|---------| 17 | | [aws](#provider\_aws) | >= 5.98 | 18 | 19 | ## Modules 20 | 21 | No modules. 22 | 23 | ## Resources 24 | 25 | | Name | Type | 26 | |------|------| 27 | | [aws_s3tables_table.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3tables_table) | resource | 28 | | [aws_s3tables_table_bucket.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3tables_table_bucket) | resource | 29 | | [aws_s3tables_table_bucket_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3tables_table_bucket_policy) | resource | 30 | | [aws_s3tables_table_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3tables_table_policy) | resource | 31 | | [aws_iam_policy_document.table_bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 32 | | [aws_iam_policy_document.table_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 33 | 34 | ## Inputs 35 | 36 | | Name | Description | Type | Default | Required | 37 | |------|-------------|------|---------|:--------:| 38 | | [create](#input\_create) | Whether to create s3 table resources | `bool` | `true` | no | 39 | | [create\_table\_bucket\_policy](#input\_create\_table\_bucket\_policy) | Whether to create s3 table bucket policy | `bool` | `false` | no | 40 | | [encryption\_configuration](#input\_encryption\_configuration) | Map of encryption configurations | `any` | `null` | no | 41 | | [maintenance\_configuration](#input\_maintenance\_configuration) | Map of table bucket maintenance configurations | `any` | `null` | no | 42 | | [table\_bucket\_name](#input\_table\_bucket\_name) | Name of the table bucket. Must be between 3 and 63 characters in length. Can consist of lowercase letters, numbers, and hyphens, and must begin and end with a lowercase letter or number | `string` | `null` | no | 43 | | [table\_bucket\_override\_policy\_documents](#input\_table\_bucket\_override\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no | 44 | | [table\_bucket\_policy](#input\_table\_bucket\_policy) | Amazon Web Services resource-based policy document in JSON format | `string` | `null` | no | 45 | | [table\_bucket\_policy\_statements](#input\_table\_bucket\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | `any` | `{}` | no | 46 | | [table\_bucket\_source\_policy\_documents](#input\_table\_bucket\_source\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no | 47 | | [tables](#input\_tables) | Map of table configurations | `any` | `{}` | no | 48 | 49 | ## Outputs 50 | 51 | | Name | Description | 52 | |------|-------------| 53 | | [s3\_table\_arns](#output\_s3\_table\_arns) | The table ARNs. | 54 | | [s3\_table\_bucket\_arn](#output\_s3\_table\_bucket\_arn) | ARN of the table bucket. | 55 | | [s3\_table\_bucket\_created\_at](#output\_s3\_table\_bucket\_created\_at) | Date and time when the bucket was created. | 56 | | [s3\_table\_bucket\_owner\_account\_id](#output\_s3\_table\_bucket\_owner\_account\_id) | Account ID of the account that owns the table bucket. | 57 | | [s3\_table\_created\_at](#output\_s3\_table\_created\_at) | Dates and times when the tables were created. | 58 | | [s3\_table\_created\_by](#output\_s3\_table\_created\_by) | Account IDs of the accounts that created the tables | 59 | | [s3\_table\_metadata\_locations](#output\_s3\_table\_metadata\_locations) | Locations of table metadata. | 60 | | [s3\_table\_modified\_at](#output\_s3\_table\_modified\_at) | Dates and times when the tables was last modified. | 61 | | [s3\_table\_modified\_by](#output\_s3\_table\_modified\_by) | Account IDs of the accounts that last modified the tables. | 62 | | [s3\_table\_owner\_account\_ids](#output\_s3\_table\_owner\_account\_ids) | Account IDs of the accounts that owns the tables. | 63 | | [s3\_table\_types](#output\_s3\_table\_types) | Types of the tables. One of customer or aws. | 64 | | [s3\_table\_version\_tokens](#output\_s3\_table\_version\_tokens) | Identifiers for the current version of table data. | 65 | | [s3\_table\_warehouse\_locations](#output\_s3\_table\_warehouse\_locations) | S3 URIs pointing to the S3 Bucket that contains the table data. | 66 | 67 | -------------------------------------------------------------------------------- /modules/table-bucket/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_s3tables_table_bucket" "this" { 2 | count = var.create ? 1 : 0 3 | 4 | name = var.table_bucket_name 5 | encryption_configuration = var.encryption_configuration 6 | maintenance_configuration = var.maintenance_configuration 7 | } 8 | 9 | resource "aws_s3tables_table_bucket_policy" "this" { 10 | count = var.create && var.create_table_bucket_policy ? 1 : 0 11 | 12 | resource_policy = var.table_bucket_policy != null ? var.table_bucket_policy : data.aws_iam_policy_document.table_bucket_policy[0].json 13 | table_bucket_arn = aws_s3tables_table_bucket.this[0].arn 14 | } 15 | 16 | data "aws_iam_policy_document" "table_bucket_policy" { 17 | count = var.create && var.create_table_bucket_policy && var.table_bucket_policy == null ? 1 : 0 18 | 19 | source_policy_documents = var.table_bucket_source_policy_documents 20 | override_policy_documents = var.table_bucket_override_policy_documents 21 | 22 | dynamic "statement" { 23 | for_each = var.table_bucket_policy_statements 24 | 25 | content { 26 | sid = try(statement.value.sid, null) 27 | actions = try(statement.value.actions, null) 28 | not_actions = try(statement.value.not_actions, null) 29 | effect = try(statement.value.effect, null) 30 | resources = try(statement.value.resources, ["${aws_s3tables_table_bucket.this[0].arn}/table/*"]) 31 | not_resources = try(statement.value.not_resources, null) 32 | 33 | dynamic "principals" { 34 | for_each = try(statement.value.principals, []) 35 | 36 | content { 37 | type = principals.value.type 38 | identifiers = principals.value.identifiers 39 | } 40 | } 41 | 42 | dynamic "not_principals" { 43 | for_each = try(statement.value.not_principals, []) 44 | 45 | content { 46 | type = not_principals.value.type 47 | identifiers = not_principals.value.identifiers 48 | } 49 | } 50 | 51 | dynamic "condition" { 52 | for_each = try(statement.value.conditions, []) 53 | 54 | content { 55 | test = condition.value.test 56 | values = condition.value.values 57 | variable = condition.value.variable 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | resource "aws_s3tables_table" "this" { 65 | for_each = { for k, v in var.tables : k => v if var.create } 66 | 67 | format = each.value.format 68 | name = try(each.value.table_name, each.key) 69 | namespace = each.value.namespace 70 | table_bucket_arn = aws_s3tables_table_bucket.this[0].arn 71 | encryption_configuration = try(each.value.encryption_configuration, null) 72 | maintenance_configuration = try(each.value.maintenance_configuration, null) 73 | } 74 | 75 | resource "aws_s3tables_table_policy" "this" { 76 | for_each = { for k, v in var.tables : k => v if var.create && try(v.create_table_policy, false) } 77 | 78 | name = aws_s3tables_table.this[each.key].name 79 | namespace = each.value.namespace 80 | resource_policy = data.aws_iam_policy_document.table_policy[each.key].json 81 | table_bucket_arn = aws_s3tables_table_bucket.this[0].arn 82 | } 83 | 84 | data "aws_iam_policy_document" "table_policy" { 85 | for_each = { for k, v in var.tables : k => v.policy_statements if var.create && try(v.create_table_policy, false) } 86 | 87 | dynamic "statement" { 88 | for_each = each.value 89 | 90 | content { 91 | sid = try(statement.value.sid, null) 92 | actions = try(statement.value.actions, null) 93 | not_actions = try(statement.value.not_actions, null) 94 | effect = try(statement.value.effect, null) 95 | resources = [aws_s3tables_table.this[each.key].arn] 96 | 97 | dynamic "principals" { 98 | for_each = try(statement.value.principals, []) 99 | 100 | content { 101 | type = principals.value.type 102 | identifiers = principals.value.identifiers 103 | } 104 | } 105 | 106 | dynamic "not_principals" { 107 | for_each = try(statement.value.not_principals, []) 108 | 109 | content { 110 | type = not_principals.value.type 111 | identifiers = not_principals.value.identifiers 112 | } 113 | } 114 | 115 | dynamic "condition" { 116 | for_each = try(statement.value.conditions, []) 117 | 118 | content { 119 | test = condition.value.test 120 | values = condition.value.values 121 | variable = condition.value.variable 122 | } 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /modules/table-bucket/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_table_bucket_arn" { 2 | description = "ARN of the table bucket." 3 | value = try(aws_s3tables_table_bucket.this[0].arn, null) 4 | } 5 | 6 | output "s3_table_bucket_created_at" { 7 | description = "Date and time when the bucket was created." 8 | value = try(aws_s3tables_table_bucket.this[0].created_at, null) 9 | } 10 | 11 | output "s3_table_bucket_owner_account_id" { 12 | description = "Account ID of the account that owns the table bucket." 13 | value = try(aws_s3tables_table_bucket.this[0].owner_account_id, null) 14 | } 15 | 16 | output "s3_table_arns" { 17 | description = "The table ARNs." 18 | value = { for k, v in aws_s3tables_table.this : k => v.arn } 19 | } 20 | 21 | output "s3_table_created_at" { 22 | description = "Dates and times when the tables were created." 23 | value = { for k, v in aws_s3tables_table.this : k => v.created_at } 24 | } 25 | 26 | output "s3_table_created_by" { 27 | description = "Account IDs of the accounts that created the tables" 28 | value = { for k, v in aws_s3tables_table.this : k => v.created_by } 29 | } 30 | 31 | output "s3_table_metadata_locations" { 32 | description = "Locations of table metadata." 33 | value = { for k, v in aws_s3tables_table.this : k => v.metadata_location } 34 | } 35 | 36 | output "s3_table_modified_at" { 37 | description = "Dates and times when the tables was last modified." 38 | value = { for k, v in aws_s3tables_table.this : k => v.modified_at } 39 | } 40 | 41 | output "s3_table_modified_by" { 42 | description = "Account IDs of the accounts that last modified the tables." 43 | value = { for k, v in aws_s3tables_table.this : k => v.modified_by } 44 | } 45 | 46 | output "s3_table_owner_account_ids" { 47 | description = "Account IDs of the accounts that owns the tables." 48 | value = { for k, v in aws_s3tables_table.this : k => v.owner_account_id } 49 | } 50 | 51 | output "s3_table_types" { 52 | description = "Types of the tables. One of customer or aws." 53 | value = { for k, v in aws_s3tables_table.this : k => v.type } 54 | } 55 | 56 | output "s3_table_version_tokens" { 57 | description = "Identifiers for the current version of table data." 58 | value = { for k, v in aws_s3tables_table.this : k => v.version_token } 59 | } 60 | 61 | output "s3_table_warehouse_locations" { 62 | description = "S3 URIs pointing to the S3 Bucket that contains the table data." 63 | value = { for k, v in aws_s3tables_table.this : k => v.warehouse_location } 64 | } 65 | -------------------------------------------------------------------------------- /modules/table-bucket/variables.tf: -------------------------------------------------------------------------------- 1 | variable "create" { 2 | description = "Whether to create s3 table resources" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "table_bucket_name" { 8 | description = "Name of the table bucket. Must be between 3 and 63 characters in length. Can consist of lowercase letters, numbers, and hyphens, and must begin and end with a lowercase letter or number" 9 | type = string 10 | default = null 11 | } 12 | 13 | variable "encryption_configuration" { 14 | description = "Map of encryption configurations" 15 | type = any 16 | default = null 17 | } 18 | 19 | variable "maintenance_configuration" { 20 | description = "Map of table bucket maintenance configurations" 21 | type = any 22 | default = null 23 | } 24 | 25 | variable "create_table_bucket_policy" { 26 | description = "Whether to create s3 table bucket policy" 27 | type = bool 28 | default = false 29 | } 30 | 31 | variable "table_bucket_policy" { 32 | description = "Amazon Web Services resource-based policy document in JSON format" 33 | type = string 34 | default = null 35 | } 36 | 37 | variable "table_bucket_source_policy_documents" { 38 | description = "List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s" 39 | type = list(string) 40 | default = [] 41 | } 42 | 43 | variable "table_bucket_override_policy_documents" { 44 | description = "List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid`" 45 | type = list(string) 46 | default = [] 47 | } 48 | 49 | variable "table_bucket_policy_statements" { 50 | description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" 51 | type = any 52 | default = {} 53 | } 54 | 55 | variable "tables" { 56 | description = "Map of table configurations" 57 | type = any 58 | default = {} 59 | } 60 | -------------------------------------------------------------------------------- /modules/table-bucket/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.98" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_bucket_id" { 2 | description = "The name of the bucket." 3 | value = try(aws_s3_bucket.this[0].id, aws_s3_directory_bucket.this[0].bucket, "") 4 | } 5 | 6 | output "s3_bucket_arn" { 7 | description = "The ARN of the bucket. Will be of format arn:aws:s3:::bucketname." 8 | value = try(aws_s3_bucket.this[0].arn, "") 9 | } 10 | 11 | output "s3_bucket_bucket_domain_name" { 12 | description = "The bucket domain name. Will be of format bucketname.s3.amazonaws.com." 13 | value = try(aws_s3_bucket.this[0].bucket_domain_name, "") 14 | } 15 | 16 | output "s3_bucket_bucket_regional_domain_name" { 17 | description = "The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL." 18 | value = try(aws_s3_bucket.this[0].bucket_regional_domain_name, "") 19 | } 20 | 21 | output "s3_bucket_hosted_zone_id" { 22 | description = "The Route 53 Hosted Zone ID for this bucket's region." 23 | value = try(aws_s3_bucket.this[0].hosted_zone_id, "") 24 | } 25 | 26 | output "s3_bucket_lifecycle_configuration_rules" { 27 | description = "The lifecycle rules of the bucket, if the bucket is configured with lifecycle rules. If not, this will be an empty string." 28 | value = try(aws_s3_bucket_lifecycle_configuration.this[0].rule, "") 29 | } 30 | 31 | output "s3_bucket_policy" { 32 | description = "The policy of the bucket, if the bucket is configured with a policy. If not, this will be an empty string." 33 | value = try(aws_s3_bucket_policy.this[0].policy, "") 34 | } 35 | 36 | output "s3_bucket_region" { 37 | description = "The AWS region this bucket resides in." 38 | value = try(aws_s3_bucket.this[0].region, "") 39 | } 40 | 41 | output "s3_bucket_website_endpoint" { 42 | description = "The website endpoint, if the bucket is configured with a website. If not, this will be an empty string." 43 | value = try(aws_s3_bucket_website_configuration.this[0].website_endpoint, "") 44 | } 45 | 46 | output "s3_bucket_website_domain" { 47 | 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." 48 | value = try(aws_s3_bucket_website_configuration.this[0].website_domain, "") 49 | } 50 | 51 | output "s3_directory_bucket_name" { 52 | description = "Name of the directory bucket." 53 | value = try(aws_s3_directory_bucket.this[0].bucket, null) 54 | } 55 | 56 | output "s3_directory_bucket_arn" { 57 | description = "ARN of the directory bucket." 58 | value = try(aws_s3_directory_bucket.this[0].arn, null) 59 | } 60 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.83" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /wrappers/README.md: -------------------------------------------------------------------------------- 1 | # Wrapper for the root module 2 | 3 | The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). 4 | 5 | You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. 6 | 7 | This wrapper does not implement any extra functionality. 8 | 9 | ## Usage with Terragrunt 10 | 11 | `terragrunt.hcl`: 12 | 13 | ```hcl 14 | terraform { 15 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" 16 | # Alternative source: 17 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" 18 | } 19 | 20 | inputs = { 21 | defaults = { # Default values 22 | create = true 23 | tags = { 24 | Terraform = "true" 25 | Environment = "dev" 26 | } 27 | } 28 | 29 | items = { 30 | my-item = { 31 | # omitted... can be any argument supported by the module 32 | } 33 | my-second-item = { 34 | # omitted... can be any argument supported by the module 35 | } 36 | # omitted... 37 | } 38 | } 39 | ``` 40 | 41 | ## Usage with Terraform 42 | 43 | ```hcl 44 | module "wrapper" { 45 | source = "terraform-aws-modules/s3-bucket/aws//wrappers" 46 | 47 | defaults = { # Default values 48 | create = true 49 | tags = { 50 | Terraform = "true" 51 | Environment = "dev" 52 | } 53 | } 54 | 55 | items = { 56 | my-item = { 57 | # omitted... can be any argument supported by the module 58 | } 59 | my-second-item = { 60 | # omitted... can be any argument supported by the module 61 | } 62 | # omitted... 63 | } 64 | } 65 | ``` 66 | 67 | ## Example: Manage multiple S3 buckets in one Terragrunt layer 68 | 69 | `eu-west-1/s3-buckets/terragrunt.hcl`: 70 | 71 | ```hcl 72 | terraform { 73 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" 74 | # Alternative source: 75 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" 76 | } 77 | 78 | inputs = { 79 | defaults = { 80 | force_destroy = true 81 | 82 | attach_elb_log_delivery_policy = true 83 | attach_lb_log_delivery_policy = true 84 | attach_deny_insecure_transport_policy = true 85 | attach_require_latest_tls_policy = true 86 | } 87 | 88 | items = { 89 | bucket1 = { 90 | bucket = "my-random-bucket-1" 91 | } 92 | bucket2 = { 93 | bucket = "my-random-bucket-2" 94 | tags = { 95 | Secure = "probably" 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | -------------------------------------------------------------------------------- /wrappers/account-public-access/README.md: -------------------------------------------------------------------------------- 1 | # Wrapper for module: `modules/account-public-access` 2 | 3 | The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). 4 | 5 | You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. 6 | 7 | This wrapper does not implement any extra functionality. 8 | 9 | ## Usage with Terragrunt 10 | 11 | `terragrunt.hcl`: 12 | 13 | ```hcl 14 | terraform { 15 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers/account-public-access" 16 | # Alternative source: 17 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers/account-public-access?ref=master" 18 | } 19 | 20 | inputs = { 21 | defaults = { # Default values 22 | create = true 23 | tags = { 24 | Terraform = "true" 25 | Environment = "dev" 26 | } 27 | } 28 | 29 | items = { 30 | my-item = { 31 | # omitted... can be any argument supported by the module 32 | } 33 | my-second-item = { 34 | # omitted... can be any argument supported by the module 35 | } 36 | # omitted... 37 | } 38 | } 39 | ``` 40 | 41 | ## Usage with Terraform 42 | 43 | ```hcl 44 | module "wrapper" { 45 | source = "terraform-aws-modules/s3-bucket/aws//wrappers/account-public-access" 46 | 47 | defaults = { # Default values 48 | create = true 49 | tags = { 50 | Terraform = "true" 51 | Environment = "dev" 52 | } 53 | } 54 | 55 | items = { 56 | my-item = { 57 | # omitted... can be any argument supported by the module 58 | } 59 | my-second-item = { 60 | # omitted... can be any argument supported by the module 61 | } 62 | # omitted... 63 | } 64 | } 65 | ``` 66 | 67 | ## Example: Manage multiple S3 buckets in one Terragrunt layer 68 | 69 | `eu-west-1/s3-buckets/terragrunt.hcl`: 70 | 71 | ```hcl 72 | terraform { 73 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" 74 | # Alternative source: 75 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" 76 | } 77 | 78 | inputs = { 79 | defaults = { 80 | force_destroy = true 81 | 82 | attach_elb_log_delivery_policy = true 83 | attach_lb_log_delivery_policy = true 84 | attach_deny_insecure_transport_policy = true 85 | attach_require_latest_tls_policy = true 86 | } 87 | 88 | items = { 89 | bucket1 = { 90 | bucket = "my-random-bucket-1" 91 | } 92 | bucket2 = { 93 | bucket = "my-random-bucket-2" 94 | tags = { 95 | Secure = "probably" 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | -------------------------------------------------------------------------------- /wrappers/account-public-access/main.tf: -------------------------------------------------------------------------------- 1 | module "wrapper" { 2 | source = "../../modules/account-public-access" 3 | 4 | for_each = var.items 5 | 6 | account_id = try(each.value.account_id, var.defaults.account_id, null) 7 | block_public_acls = try(each.value.block_public_acls, var.defaults.block_public_acls, false) 8 | block_public_policy = try(each.value.block_public_policy, var.defaults.block_public_policy, false) 9 | create = try(each.value.create, var.defaults.create, true) 10 | ignore_public_acls = try(each.value.ignore_public_acls, var.defaults.ignore_public_acls, false) 11 | restrict_public_buckets = try(each.value.restrict_public_buckets, var.defaults.restrict_public_buckets, false) 12 | } 13 | -------------------------------------------------------------------------------- /wrappers/account-public-access/outputs.tf: -------------------------------------------------------------------------------- 1 | output "wrapper" { 2 | description = "Map of outputs of a wrapper." 3 | value = module.wrapper 4 | # sensitive = false # No sensitive module output found 5 | } 6 | -------------------------------------------------------------------------------- /wrappers/account-public-access/variables.tf: -------------------------------------------------------------------------------- 1 | variable "defaults" { 2 | description = "Map of default values which will be used for each item." 3 | type = any 4 | default = {} 5 | } 6 | 7 | variable "items" { 8 | description = "Maps of items to create a wrapper from. Values are passed through to the module." 9 | type = any 10 | default = {} 11 | } 12 | -------------------------------------------------------------------------------- /wrappers/account-public-access/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 3.74" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /wrappers/main.tf: -------------------------------------------------------------------------------- 1 | module "wrapper" { 2 | source = "../" 3 | 4 | for_each = var.items 5 | 6 | acceleration_status = try(each.value.acceleration_status, var.defaults.acceleration_status, null) 7 | access_log_delivery_policy_source_accounts = try(each.value.access_log_delivery_policy_source_accounts, var.defaults.access_log_delivery_policy_source_accounts, []) 8 | access_log_delivery_policy_source_buckets = try(each.value.access_log_delivery_policy_source_buckets, var.defaults.access_log_delivery_policy_source_buckets, []) 9 | access_log_delivery_policy_source_organizations = try(each.value.access_log_delivery_policy_source_organizations, var.defaults.access_log_delivery_policy_source_organizations, []) 10 | acl = try(each.value.acl, var.defaults.acl, null) 11 | allowed_kms_key_arn = try(each.value.allowed_kms_key_arn, var.defaults.allowed_kms_key_arn, null) 12 | analytics_configuration = try(each.value.analytics_configuration, var.defaults.analytics_configuration, {}) 13 | analytics_self_source_destination = try(each.value.analytics_self_source_destination, var.defaults.analytics_self_source_destination, false) 14 | analytics_source_account_id = try(each.value.analytics_source_account_id, var.defaults.analytics_source_account_id, null) 15 | analytics_source_bucket_arn = try(each.value.analytics_source_bucket_arn, var.defaults.analytics_source_bucket_arn, null) 16 | attach_access_log_delivery_policy = try(each.value.attach_access_log_delivery_policy, var.defaults.attach_access_log_delivery_policy, false) 17 | attach_analytics_destination_policy = try(each.value.attach_analytics_destination_policy, var.defaults.attach_analytics_destination_policy, false) 18 | attach_deny_incorrect_encryption_headers = try(each.value.attach_deny_incorrect_encryption_headers, var.defaults.attach_deny_incorrect_encryption_headers, false) 19 | attach_deny_incorrect_kms_key_sse = try(each.value.attach_deny_incorrect_kms_key_sse, var.defaults.attach_deny_incorrect_kms_key_sse, false) 20 | attach_deny_insecure_transport_policy = try(each.value.attach_deny_insecure_transport_policy, var.defaults.attach_deny_insecure_transport_policy, false) 21 | attach_deny_ssec_encrypted_object_uploads = try(each.value.attach_deny_ssec_encrypted_object_uploads, var.defaults.attach_deny_ssec_encrypted_object_uploads, false) 22 | attach_deny_unencrypted_object_uploads = try(each.value.attach_deny_unencrypted_object_uploads, var.defaults.attach_deny_unencrypted_object_uploads, false) 23 | attach_elb_log_delivery_policy = try(each.value.attach_elb_log_delivery_policy, var.defaults.attach_elb_log_delivery_policy, false) 24 | attach_inventory_destination_policy = try(each.value.attach_inventory_destination_policy, var.defaults.attach_inventory_destination_policy, false) 25 | attach_lb_log_delivery_policy = try(each.value.attach_lb_log_delivery_policy, var.defaults.attach_lb_log_delivery_policy, false) 26 | attach_policy = try(each.value.attach_policy, var.defaults.attach_policy, false) 27 | attach_public_policy = try(each.value.attach_public_policy, var.defaults.attach_public_policy, true) 28 | attach_require_latest_tls_policy = try(each.value.attach_require_latest_tls_policy, var.defaults.attach_require_latest_tls_policy, false) 29 | attach_waf_log_delivery_policy = try(each.value.attach_waf_log_delivery_policy, var.defaults.attach_waf_log_delivery_policy, false) 30 | availability_zone_id = try(each.value.availability_zone_id, var.defaults.availability_zone_id, null) 31 | block_public_acls = try(each.value.block_public_acls, var.defaults.block_public_acls, true) 32 | block_public_policy = try(each.value.block_public_policy, var.defaults.block_public_policy, true) 33 | bucket = try(each.value.bucket, var.defaults.bucket, null) 34 | bucket_prefix = try(each.value.bucket_prefix, var.defaults.bucket_prefix, null) 35 | control_object_ownership = try(each.value.control_object_ownership, var.defaults.control_object_ownership, false) 36 | cors_rule = try(each.value.cors_rule, var.defaults.cors_rule, []) 37 | create_bucket = try(each.value.create_bucket, var.defaults.create_bucket, true) 38 | data_redundancy = try(each.value.data_redundancy, var.defaults.data_redundancy, null) 39 | expected_bucket_owner = try(each.value.expected_bucket_owner, var.defaults.expected_bucket_owner, null) 40 | force_destroy = try(each.value.force_destroy, var.defaults.force_destroy, false) 41 | grant = try(each.value.grant, var.defaults.grant, []) 42 | ignore_public_acls = try(each.value.ignore_public_acls, var.defaults.ignore_public_acls, true) 43 | intelligent_tiering = try(each.value.intelligent_tiering, var.defaults.intelligent_tiering, {}) 44 | inventory_configuration = try(each.value.inventory_configuration, var.defaults.inventory_configuration, {}) 45 | inventory_self_source_destination = try(each.value.inventory_self_source_destination, var.defaults.inventory_self_source_destination, false) 46 | inventory_source_account_id = try(each.value.inventory_source_account_id, var.defaults.inventory_source_account_id, null) 47 | inventory_source_bucket_arn = try(each.value.inventory_source_bucket_arn, var.defaults.inventory_source_bucket_arn, null) 48 | is_directory_bucket = try(each.value.is_directory_bucket, var.defaults.is_directory_bucket, false) 49 | lb_log_delivery_policy_source_organizations = try(each.value.lb_log_delivery_policy_source_organizations, var.defaults.lb_log_delivery_policy_source_organizations, []) 50 | lifecycle_rule = try(each.value.lifecycle_rule, var.defaults.lifecycle_rule, []) 51 | location_type = try(each.value.location_type, var.defaults.location_type, null) 52 | logging = try(each.value.logging, var.defaults.logging, {}) 53 | metric_configuration = try(each.value.metric_configuration, var.defaults.metric_configuration, []) 54 | object_lock_configuration = try(each.value.object_lock_configuration, var.defaults.object_lock_configuration, {}) 55 | object_lock_enabled = try(each.value.object_lock_enabled, var.defaults.object_lock_enabled, false) 56 | object_ownership = try(each.value.object_ownership, var.defaults.object_ownership, "BucketOwnerEnforced") 57 | owner = try(each.value.owner, var.defaults.owner, {}) 58 | policy = try(each.value.policy, var.defaults.policy, null) 59 | putin_khuylo = try(each.value.putin_khuylo, var.defaults.putin_khuylo, true) 60 | replication_configuration = try(each.value.replication_configuration, var.defaults.replication_configuration, {}) 61 | request_payer = try(each.value.request_payer, var.defaults.request_payer, null) 62 | restrict_public_buckets = try(each.value.restrict_public_buckets, var.defaults.restrict_public_buckets, true) 63 | server_side_encryption_configuration = try(each.value.server_side_encryption_configuration, var.defaults.server_side_encryption_configuration, {}) 64 | tags = try(each.value.tags, var.defaults.tags, {}) 65 | transition_default_minimum_object_size = try(each.value.transition_default_minimum_object_size, var.defaults.transition_default_minimum_object_size, null) 66 | type = try(each.value.type, var.defaults.type, "Directory") 67 | versioning = try(each.value.versioning, var.defaults.versioning, {}) 68 | website = try(each.value.website, var.defaults.website, {}) 69 | } 70 | -------------------------------------------------------------------------------- /wrappers/notification/README.md: -------------------------------------------------------------------------------- 1 | # Wrapper for module: `modules/notification` 2 | 3 | The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). 4 | 5 | You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. 6 | 7 | This wrapper does not implement any extra functionality. 8 | 9 | ## Usage with Terragrunt 10 | 11 | `terragrunt.hcl`: 12 | 13 | ```hcl 14 | terraform { 15 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers/notification" 16 | # Alternative source: 17 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers/notification?ref=master" 18 | } 19 | 20 | inputs = { 21 | defaults = { # Default values 22 | create = true 23 | tags = { 24 | Terraform = "true" 25 | Environment = "dev" 26 | } 27 | } 28 | 29 | items = { 30 | my-item = { 31 | # omitted... can be any argument supported by the module 32 | } 33 | my-second-item = { 34 | # omitted... can be any argument supported by the module 35 | } 36 | # omitted... 37 | } 38 | } 39 | ``` 40 | 41 | ## Usage with Terraform 42 | 43 | ```hcl 44 | module "wrapper" { 45 | source = "terraform-aws-modules/s3-bucket/aws//wrappers/notification" 46 | 47 | defaults = { # Default values 48 | create = true 49 | tags = { 50 | Terraform = "true" 51 | Environment = "dev" 52 | } 53 | } 54 | 55 | items = { 56 | my-item = { 57 | # omitted... can be any argument supported by the module 58 | } 59 | my-second-item = { 60 | # omitted... can be any argument supported by the module 61 | } 62 | # omitted... 63 | } 64 | } 65 | ``` 66 | 67 | ## Example: Manage multiple S3 buckets in one Terragrunt layer 68 | 69 | `eu-west-1/s3-buckets/terragrunt.hcl`: 70 | 71 | ```hcl 72 | terraform { 73 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" 74 | # Alternative source: 75 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" 76 | } 77 | 78 | inputs = { 79 | defaults = { 80 | force_destroy = true 81 | 82 | attach_elb_log_delivery_policy = true 83 | attach_lb_log_delivery_policy = true 84 | attach_deny_insecure_transport_policy = true 85 | attach_require_latest_tls_policy = true 86 | } 87 | 88 | items = { 89 | bucket1 = { 90 | bucket = "my-random-bucket-1" 91 | } 92 | bucket2 = { 93 | bucket = "my-random-bucket-2" 94 | tags = { 95 | Secure = "probably" 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | -------------------------------------------------------------------------------- /wrappers/notification/main.tf: -------------------------------------------------------------------------------- 1 | module "wrapper" { 2 | source = "../../modules/notification" 3 | 4 | for_each = var.items 5 | 6 | bucket = try(each.value.bucket, var.defaults.bucket, "") 7 | bucket_arn = try(each.value.bucket_arn, var.defaults.bucket_arn, null) 8 | create = try(each.value.create, var.defaults.create, true) 9 | create_lambda_permission = try(each.value.create_lambda_permission, var.defaults.create_lambda_permission, true) 10 | create_sns_policy = try(each.value.create_sns_policy, var.defaults.create_sns_policy, true) 11 | create_sqs_policy = try(each.value.create_sqs_policy, var.defaults.create_sqs_policy, true) 12 | eventbridge = try(each.value.eventbridge, var.defaults.eventbridge, null) 13 | lambda_notifications = try(each.value.lambda_notifications, var.defaults.lambda_notifications, {}) 14 | sns_notifications = try(each.value.sns_notifications, var.defaults.sns_notifications, {}) 15 | sqs_notifications = try(each.value.sqs_notifications, var.defaults.sqs_notifications, {}) 16 | } 17 | -------------------------------------------------------------------------------- /wrappers/notification/outputs.tf: -------------------------------------------------------------------------------- 1 | output "wrapper" { 2 | description = "Map of outputs of a wrapper." 3 | value = module.wrapper 4 | # sensitive = false # No sensitive module output found 5 | } 6 | -------------------------------------------------------------------------------- /wrappers/notification/variables.tf: -------------------------------------------------------------------------------- 1 | variable "defaults" { 2 | description = "Map of default values which will be used for each item." 3 | type = any 4 | default = {} 5 | } 6 | 7 | variable "items" { 8 | description = "Maps of items to create a wrapper from. Values are passed through to the module." 9 | type = any 10 | default = {} 11 | } 12 | -------------------------------------------------------------------------------- /wrappers/notification/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 3.74" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /wrappers/object/README.md: -------------------------------------------------------------------------------- 1 | # Wrapper for module: `modules/object` 2 | 3 | The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). 4 | 5 | You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. 6 | 7 | This wrapper does not implement any extra functionality. 8 | 9 | ## Usage with Terragrunt 10 | 11 | `terragrunt.hcl`: 12 | 13 | ```hcl 14 | terraform { 15 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers/object" 16 | # Alternative source: 17 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers/object?ref=master" 18 | } 19 | 20 | inputs = { 21 | defaults = { # Default values 22 | create = true 23 | tags = { 24 | Terraform = "true" 25 | Environment = "dev" 26 | } 27 | } 28 | 29 | items = { 30 | my-item = { 31 | # omitted... can be any argument supported by the module 32 | } 33 | my-second-item = { 34 | # omitted... can be any argument supported by the module 35 | } 36 | # omitted... 37 | } 38 | } 39 | ``` 40 | 41 | ## Usage with Terraform 42 | 43 | ```hcl 44 | module "wrapper" { 45 | source = "terraform-aws-modules/s3-bucket/aws//wrappers/object" 46 | 47 | defaults = { # Default values 48 | create = true 49 | tags = { 50 | Terraform = "true" 51 | Environment = "dev" 52 | } 53 | } 54 | 55 | items = { 56 | my-item = { 57 | # omitted... can be any argument supported by the module 58 | } 59 | my-second-item = { 60 | # omitted... can be any argument supported by the module 61 | } 62 | # omitted... 63 | } 64 | } 65 | ``` 66 | 67 | ## Example: Manage multiple S3 buckets in one Terragrunt layer 68 | 69 | `eu-west-1/s3-buckets/terragrunt.hcl`: 70 | 71 | ```hcl 72 | terraform { 73 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" 74 | # Alternative source: 75 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" 76 | } 77 | 78 | inputs = { 79 | defaults = { 80 | force_destroy = true 81 | 82 | attach_elb_log_delivery_policy = true 83 | attach_lb_log_delivery_policy = true 84 | attach_deny_insecure_transport_policy = true 85 | attach_require_latest_tls_policy = true 86 | } 87 | 88 | items = { 89 | bucket1 = { 90 | bucket = "my-random-bucket-1" 91 | } 92 | bucket2 = { 93 | bucket = "my-random-bucket-2" 94 | tags = { 95 | Secure = "probably" 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | -------------------------------------------------------------------------------- /wrappers/object/main.tf: -------------------------------------------------------------------------------- 1 | module "wrapper" { 2 | source = "../../modules/object" 3 | 4 | for_each = var.items 5 | 6 | acl = try(each.value.acl, var.defaults.acl, null) 7 | bucket = try(each.value.bucket, var.defaults.bucket, "") 8 | bucket_key_enabled = try(each.value.bucket_key_enabled, var.defaults.bucket_key_enabled, null) 9 | cache_control = try(each.value.cache_control, var.defaults.cache_control, null) 10 | content = try(each.value.content, var.defaults.content, null) 11 | content_base64 = try(each.value.content_base64, var.defaults.content_base64, null) 12 | content_disposition = try(each.value.content_disposition, var.defaults.content_disposition, null) 13 | content_encoding = try(each.value.content_encoding, var.defaults.content_encoding, null) 14 | content_language = try(each.value.content_language, var.defaults.content_language, null) 15 | content_type = try(each.value.content_type, var.defaults.content_type, null) 16 | create = try(each.value.create, var.defaults.create, true) 17 | etag = try(each.value.etag, var.defaults.etag, null) 18 | file_source = try(each.value.file_source, var.defaults.file_source, null) 19 | force_destroy = try(each.value.force_destroy, var.defaults.force_destroy, false) 20 | key = try(each.value.key, var.defaults.key, "") 21 | kms_key_id = try(each.value.kms_key_id, var.defaults.kms_key_id, null) 22 | metadata = try(each.value.metadata, var.defaults.metadata, {}) 23 | object_lock_legal_hold_status = try(each.value.object_lock_legal_hold_status, var.defaults.object_lock_legal_hold_status, null) 24 | object_lock_mode = try(each.value.object_lock_mode, var.defaults.object_lock_mode, null) 25 | object_lock_retain_until_date = try(each.value.object_lock_retain_until_date, var.defaults.object_lock_retain_until_date, null) 26 | override_default_tags = try(each.value.override_default_tags, var.defaults.override_default_tags, false) 27 | server_side_encryption = try(each.value.server_side_encryption, var.defaults.server_side_encryption, null) 28 | source_hash = try(each.value.source_hash, var.defaults.source_hash, null) 29 | storage_class = try(each.value.storage_class, var.defaults.storage_class, null) 30 | tags = try(each.value.tags, var.defaults.tags, {}) 31 | website_redirect = try(each.value.website_redirect, var.defaults.website_redirect, null) 32 | } 33 | -------------------------------------------------------------------------------- /wrappers/object/outputs.tf: -------------------------------------------------------------------------------- 1 | output "wrapper" { 2 | description = "Map of outputs of a wrapper." 3 | value = module.wrapper 4 | # sensitive = false # No sensitive module output found 5 | } 6 | -------------------------------------------------------------------------------- /wrappers/object/variables.tf: -------------------------------------------------------------------------------- 1 | variable "defaults" { 2 | description = "Map of default values which will be used for each item." 3 | type = any 4 | default = {} 5 | } 6 | 7 | variable "items" { 8 | description = "Maps of items to create a wrapper from. Values are passed through to the module." 9 | type = any 10 | default = {} 11 | } 12 | -------------------------------------------------------------------------------- /wrappers/object/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.24" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /wrappers/outputs.tf: -------------------------------------------------------------------------------- 1 | output "wrapper" { 2 | description = "Map of outputs of a wrapper." 3 | value = module.wrapper 4 | # sensitive = false # No sensitive module output found 5 | } 6 | -------------------------------------------------------------------------------- /wrappers/table-bucket/README.md: -------------------------------------------------------------------------------- 1 | # Wrapper for module: `modules/table-bucket` 2 | 3 | The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). 4 | 5 | You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. 6 | 7 | This wrapper does not implement any extra functionality. 8 | 9 | ## Usage with Terragrunt 10 | 11 | `terragrunt.hcl`: 12 | 13 | ```hcl 14 | terraform { 15 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers/table-bucket" 16 | # Alternative source: 17 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers/table-bucket?ref=master" 18 | } 19 | 20 | inputs = { 21 | defaults = { # Default values 22 | create = true 23 | tags = { 24 | Terraform = "true" 25 | Environment = "dev" 26 | } 27 | } 28 | 29 | items = { 30 | my-item = { 31 | # omitted... can be any argument supported by the module 32 | } 33 | my-second-item = { 34 | # omitted... can be any argument supported by the module 35 | } 36 | # omitted... 37 | } 38 | } 39 | ``` 40 | 41 | ## Usage with Terraform 42 | 43 | ```hcl 44 | module "wrapper" { 45 | source = "terraform-aws-modules/s3-bucket/aws//wrappers/table-bucket" 46 | 47 | defaults = { # Default values 48 | create = true 49 | tags = { 50 | Terraform = "true" 51 | Environment = "dev" 52 | } 53 | } 54 | 55 | items = { 56 | my-item = { 57 | # omitted... can be any argument supported by the module 58 | } 59 | my-second-item = { 60 | # omitted... can be any argument supported by the module 61 | } 62 | # omitted... 63 | } 64 | } 65 | ``` 66 | 67 | ## Example: Manage multiple S3 buckets in one Terragrunt layer 68 | 69 | `eu-west-1/s3-buckets/terragrunt.hcl`: 70 | 71 | ```hcl 72 | terraform { 73 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" 74 | # Alternative source: 75 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" 76 | } 77 | 78 | inputs = { 79 | defaults = { 80 | force_destroy = true 81 | 82 | attach_elb_log_delivery_policy = true 83 | attach_lb_log_delivery_policy = true 84 | attach_deny_insecure_transport_policy = true 85 | attach_require_latest_tls_policy = true 86 | } 87 | 88 | items = { 89 | bucket1 = { 90 | bucket = "my-random-bucket-1" 91 | } 92 | bucket2 = { 93 | bucket = "my-random-bucket-2" 94 | tags = { 95 | Secure = "probably" 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | -------------------------------------------------------------------------------- /wrappers/table-bucket/main.tf: -------------------------------------------------------------------------------- 1 | module "wrapper" { 2 | source = "../../modules/table-bucket" 3 | 4 | for_each = var.items 5 | 6 | create = try(each.value.create, var.defaults.create, true) 7 | create_table_bucket_policy = try(each.value.create_table_bucket_policy, var.defaults.create_table_bucket_policy, false) 8 | encryption_configuration = try(each.value.encryption_configuration, var.defaults.encryption_configuration, null) 9 | maintenance_configuration = try(each.value.maintenance_configuration, var.defaults.maintenance_configuration, null) 10 | table_bucket_name = try(each.value.table_bucket_name, var.defaults.table_bucket_name, null) 11 | table_bucket_override_policy_documents = try(each.value.table_bucket_override_policy_documents, var.defaults.table_bucket_override_policy_documents, []) 12 | table_bucket_policy = try(each.value.table_bucket_policy, var.defaults.table_bucket_policy, null) 13 | table_bucket_policy_statements = try(each.value.table_bucket_policy_statements, var.defaults.table_bucket_policy_statements, {}) 14 | table_bucket_source_policy_documents = try(each.value.table_bucket_source_policy_documents, var.defaults.table_bucket_source_policy_documents, []) 15 | tables = try(each.value.tables, var.defaults.tables, {}) 16 | } 17 | -------------------------------------------------------------------------------- /wrappers/table-bucket/outputs.tf: -------------------------------------------------------------------------------- 1 | output "wrapper" { 2 | description = "Map of outputs of a wrapper." 3 | value = module.wrapper 4 | # sensitive = false # No sensitive module output found 5 | } 6 | -------------------------------------------------------------------------------- /wrappers/table-bucket/variables.tf: -------------------------------------------------------------------------------- 1 | variable "defaults" { 2 | description = "Map of default values which will be used for each item." 3 | type = any 4 | default = {} 5 | } 6 | 7 | variable "items" { 8 | description = "Maps of items to create a wrapper from. Values are passed through to the module." 9 | type = any 10 | default = {} 11 | } 12 | -------------------------------------------------------------------------------- /wrappers/table-bucket/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.98" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /wrappers/variables.tf: -------------------------------------------------------------------------------- 1 | variable "defaults" { 2 | description = "Map of default values which will be used for each item." 3 | type = any 4 | default = {} 5 | } 6 | 7 | variable "items" { 8 | description = "Maps of items to create a wrapper from. Values are passed through to the module." 9 | type = any 10 | default = {} 11 | } 12 | -------------------------------------------------------------------------------- /wrappers/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.83" 8 | } 9 | } 10 | } 11 | --------------------------------------------------------------------------------