├── .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 ├── examples ├── README.md └── complete │ ├── README.md │ ├── function.py │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── main.tf ├── outputs.tf ├── variables.tf ├── versions.tf └── wrappers ├── README.md ├── main.tf ├── outputs.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 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # terraform lockfile 9 | .terraform.lock.hcl 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 | # 19 | *.tfvars 20 | 21 | # Ignore override files as they are usually used to override resources locally and so 22 | # are not checked in 23 | override.tf 24 | override.tf.json 25 | *_override.tf 26 | *_override.tf.json 27 | 28 | # Include override files you do wish to add to version control using negated pattern 29 | # 30 | # !example_override.tf 31 | 32 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 33 | # example: *tfplan* 34 | 35 | # Ignore CLI configuration files 36 | .terraformrc 37 | terraform.rc 38 | 39 | # Lambda 40 | builds/ 41 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/antonbabenko/pre-commit-terraform 3 | rev: v1.96.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 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [1.3.1](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/compare/v1.3.0...v1.3.1) (2024-10-11) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * Update CI workflow versions to latest ([#11](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/issues/11)) ([85977d1](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/commit/85977d132b8491281266ca412cee3e9ce7f2b457)) 11 | 12 | ## [1.3.0](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/compare/v1.2.0...v1.3.0) (2024-09-19) 13 | 14 | 15 | ### Features 16 | 17 | * Add `secret_name` to outputs ([#9](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/issues/9)) ([e3d07ad](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/commit/e3d07ad1e3da4984cffb1423689afcb2e8033986)) 18 | 19 | ## [1.2.0](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/compare/v1.1.2...v1.2.0) (2024-09-16) 20 | 21 | 22 | ### Features 23 | 24 | * Add `secret_string` and `secret_binary` to outputs ([#8](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/issues/8)) ([0b36ebb](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/commit/0b36ebbf308226b6d516b6a386b10bec3520333e)) 25 | 26 | ## [1.1.2](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/compare/v1.1.1...v1.1.2) (2024-03-06) 27 | 28 | 29 | ### Bug Fixes 30 | 31 | * Update CI workflow versions to remove deprecated runtime warnings ([#5](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/issues/5)) ([4a88afe](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/commit/4a88afe383b2b2fb4d4c2c9c0802ad407e60462b)) 32 | 33 | ### [1.1.1](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/compare/v1.1.0...v1.1.1) (2023-08-10) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * Updated variable types from any to map(any) ([#3](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/issues/3)) ([90ea91f](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/commit/90ea91fa2f23bce2777a47c98f4bdb8aa4c65b65)) 39 | 40 | ## [1.1.0](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/compare/v1.0.0...v1.1.0) (2023-07-15) 41 | 42 | 43 | ### Features 44 | 45 | * Add support for random password generation and Terragrunt wrapper ([#1](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/issues/1)) ([3e80d9b](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/commit/3e80d9b643baa9fd30fc0cd42016dd725e3fa625)) 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Secrets Manager Terraform module 2 | 3 | Terraform module which creates AWS Secrets Manager resources. 4 | 5 | [![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) 6 | 7 | ## Usage 8 | 9 | See [`examples`](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/tree/master/examples) directory for working examples to reference: 10 | 11 | ### Standard 12 | 13 | ```hcl 14 | module "secrets_manager" { 15 | source = "terraform-aws-modules/secrets-manager/aws" 16 | 17 | # Secret 18 | name_prefix = "example" 19 | description = "Example Secrets Manager secret" 20 | recovery_window_in_days = 30 21 | 22 | # Policy 23 | create_policy = true 24 | block_public_policy = true 25 | policy_statements = { 26 | read = { 27 | sid = "AllowAccountRead" 28 | principals = [{ 29 | type = "AWS" 30 | identifiers = ["arn:aws:iam::1234567890:root"] 31 | }] 32 | actions = ["secretsmanager:GetSecretValue"] 33 | resources = ["*"] 34 | } 35 | } 36 | 37 | # Version 38 | create_random_password = true 39 | random_password_length = 64 40 | random_password_override_special = "!@#$%^&*()_+" 41 | 42 | tags = { 43 | Environment = "Development" 44 | Project = "Example" 45 | } 46 | } 47 | ``` 48 | 49 | ### w/ Rotation 50 | 51 | ```hcl 52 | module "secrets_manager" { 53 | source = "terraform-aws-modules/secrets-manager/aws" 54 | 55 | # Secret 56 | name_prefix = "rotated-example" 57 | description = "Rotated example Secrets Manager secret" 58 | recovery_window_in_days = 7 59 | 60 | # Policy 61 | create_policy = true 62 | block_public_policy = true 63 | policy_statements = { 64 | lambda = { 65 | sid = "LambdaReadWrite" 66 | principals = [{ 67 | type = "AWS" 68 | identifiers = ["arn:aws:iam:1234567890:role/lambda-function"] 69 | }] 70 | actions = [ 71 | "secretsmanager:DescribeSecret", 72 | "secretsmanager:GetSecretValue", 73 | "secretsmanager:PutSecretValue", 74 | "secretsmanager:UpdateSecretVersionStage", 75 | ] 76 | resources = ["*"] 77 | } 78 | read = { 79 | sid = "AllowAccountRead" 80 | principals = [{ 81 | type = "AWS" 82 | identifiers = ["arn:aws:iam::1234567890:root"] 83 | }] 84 | actions = ["secretsmanager:DescribeSecret"] 85 | resources = ["*"] 86 | } 87 | } 88 | 89 | # Version 90 | ignore_secret_changes = true 91 | secret_string = jsonencode({ 92 | engine = "mariadb", 93 | host = "mydb.cluster-123456789012.us-east-1.rds.amazonaws.com", 94 | username = "Bill", 95 | password = "Initial" 96 | dbname = "ThisIsMySuperSecretString12356!&*()", 97 | port = 3306 98 | }) 99 | 100 | # Rotation 101 | enable_rotation = true 102 | rotation_lambda_arn = "arn:aws:lambda:us-east-1:123456789012:function:my-function" 103 | rotation_rules = { 104 | # This should be more sensible in production 105 | schedule_expression = "rate(1 minute)" 106 | } 107 | 108 | tags = { 109 | Environment = "Development" 110 | Project = "Example" 111 | } 112 | } 113 | ``` 114 | 115 | ## Examples 116 | 117 | Examples codified under the [`examples`](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/tree/master/examples) are intended to give users references for how to use the module(s) as well as testing/validating changes to the source code of the module. If contributing to the project, please be sure to make any appropriate updates to the relevant examples to allow maintainers to test your changes and to keep the examples up to date for users. Thank you! 118 | 119 | - [Complete](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/tree/master/examples/complete) 120 | 121 | 122 | ## Requirements 123 | 124 | | Name | Version | 125 | |------|---------| 126 | | [terraform](#requirement\_terraform) | >= 1.0 | 127 | | [aws](#requirement\_aws) | >= 5.0 | 128 | | [random](#requirement\_random) | >= 3.0 | 129 | 130 | ## Providers 131 | 132 | | Name | Version | 133 | |------|---------| 134 | | [aws](#provider\_aws) | >= 5.0 | 135 | | [random](#provider\_random) | >= 3.0 | 136 | 137 | ## Modules 138 | 139 | No modules. 140 | 141 | ## Resources 142 | 143 | | Name | Type | 144 | |------|------| 145 | | [aws_secretsmanager_secret.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource | 146 | | [aws_secretsmanager_secret_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_policy) | resource | 147 | | [aws_secretsmanager_secret_rotation.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_rotation) | resource | 148 | | [aws_secretsmanager_secret_version.ignore_changes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource | 149 | | [aws_secretsmanager_secret_version.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource | 150 | | [random_password.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | 151 | | [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 152 | 153 | ## Inputs 154 | 155 | | Name | Description | Type | Default | Required | 156 | |------|-------------|------|---------|:--------:| 157 | | [block\_public\_policy](#input\_block\_public\_policy) | Makes an optional API call to Zelkova to validate the Resource Policy to prevent broad access to your secret | `bool` | `null` | no | 158 | | [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no | 159 | | [create\_policy](#input\_create\_policy) | Determines whether a policy will be created | `bool` | `false` | no | 160 | | [create\_random\_password](#input\_create\_random\_password) | Determines whether a random password will be generated | `bool` | `false` | no | 161 | | [description](#input\_description) | A description of the secret | `string` | `null` | no | 162 | | [enable\_rotation](#input\_enable\_rotation) | Determines whether secret rotation is enabled | `bool` | `false` | no | 163 | | [force\_overwrite\_replica\_secret](#input\_force\_overwrite\_replica\_secret) | Accepts boolean value to specify whether to overwrite a secret with the same name in the destination Region | `bool` | `null` | no | 164 | | [ignore\_secret\_changes](#input\_ignore\_secret\_changes) | Determines whether or not Terraform will ignore changes made externally to `secret_string` or `secret_binary`. Changing this value after creation is a destructive operation | `bool` | `false` | no | 165 | | [kms\_key\_id](#input\_kms\_key\_id) | ARN or Id of the AWS KMS key to be used to encrypt the secret values in the versions stored in this secret. If you need to reference a CMK in a different account, you can use only the key ARN. If you don't specify this value, then Secrets Manager defaults to using the AWS account's default KMS key (the one named `aws/secretsmanager` | `string` | `null` | no | 166 | | [name](#input\_name) | Friendly name of the new secret. The secret name can consist of uppercase letters, lowercase letters, digits, and any of the following characters: `/_+=.@-` | `string` | `null` | no | 167 | | [name\_prefix](#input\_name\_prefix) | Creates a unique name beginning with the specified prefix | `string` | `null` | no | 168 | | [override\_policy\_documents](#input\_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 | 169 | | [policy\_statements](#input\_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 | `map(any)` | `{}` | no | 170 | | [random\_password\_length](#input\_random\_password\_length) | The length of the generated random password | `number` | `32` | no | 171 | | [random\_password\_override\_special](#input\_random\_password\_override\_special) | Supply your own list of special characters to use for string generation. This overrides the default character list in the special argument | `string` | `"!@#$%&*()-_=+[]{}<>:?"` | no | 172 | | [recovery\_window\_in\_days](#input\_recovery\_window\_in\_days) | Number of days that AWS Secrets Manager waits before it can delete the secret. This value can be `0` to force deletion without recovery or range from `7` to `30` days. The default value is `30` | `number` | `null` | no | 173 | | [replica](#input\_replica) | Configuration block to support secret replication | `map(any)` | `{}` | no | 174 | | [rotation\_lambda\_arn](#input\_rotation\_lambda\_arn) | Specifies the ARN of the Lambda function that can rotate the secret | `string` | `""` | no | 175 | | [rotation\_rules](#input\_rotation\_rules) | A structure that defines the rotation configuration for this secret | `map(any)` | `{}` | no | 176 | | [secret\_binary](#input\_secret\_binary) | Specifies binary data that you want to encrypt and store in this version of the secret. This is required if `secret_string` is not set. Needs to be encoded to base64 | `string` | `null` | no | 177 | | [secret\_string](#input\_secret\_string) | Specifies text data that you want to encrypt and store in this version of the secret. This is required if `secret_binary` is not set | `string` | `null` | no | 178 | | [source\_policy\_documents](#input\_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 | 179 | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | 180 | | [version\_stages](#input\_version\_stages) | Specifies a list of staging labels that are attached to this version of the secret. A staging label must be unique to a single version of the secret | `list(string)` | `null` | no | 181 | 182 | ## Outputs 183 | 184 | | Name | Description | 185 | |------|-------------| 186 | | [secret\_arn](#output\_secret\_arn) | The ARN of the secret | 187 | | [secret\_binary](#output\_secret\_binary) | The secret binary | 188 | | [secret\_id](#output\_secret\_id) | The ID of the secret | 189 | | [secret\_name](#output\_secret\_name) | The name of the secret | 190 | | [secret\_replica](#output\_secret\_replica) | Attributes of the replica created | 191 | | [secret\_string](#output\_secret\_string) | The secret string | 192 | | [secret\_version\_id](#output\_secret\_version\_id) | The unique identifier of the version of the secret | 193 | 194 | 195 | ## License 196 | 197 | Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/blob/master/LICENSE). 198 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Please note - the examples provided serve two primary means: 4 | 5 | 1. Show users working examples of the various ways in which the module can be configured and features supported 6 | 2. A means of testing/validating module changes 7 | 8 | Please do not mistake the examples provided as "best practices". It is up to users to consult the AWS service documentation for best practices, usage recommendations, etc. 9 | -------------------------------------------------------------------------------- /examples/complete/README.md: -------------------------------------------------------------------------------- 1 | # Complete AWS Secrets Manager Example 2 | 3 | Configuration in this directory creates: 4 | 5 | - Standard Secrets Manager Secret 6 | - Secrets Manager Secret with rotation enabled 7 | 8 | ## Usage 9 | 10 | To run this example you need to execute: 11 | 12 | ```bash 13 | $ terraform init 14 | $ terraform plan 15 | $ terraform apply 16 | ``` 17 | 18 | If you wish to test the rotated secret, after provisioning the resources you can go into the console and under the rotated secret click `Rotate secret immediately`. This will trigger the lambda function to rotate the secret. You can then go to the `Secret value` tab and click `Retrieve secret value` to see the new secret value. 19 | 20 | After rotating the secret, you can run `terraform plan` and see that there are no detected changes. 21 | 22 | :warning: Replicated secrets are not cleaned up by Terraform. You will need to manually delete these secrets. Ref: https://github.com/hashicorp/terraform-provider-aws/issues/23316 23 | 24 | Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources. 25 | 26 | 27 | ## Requirements 28 | 29 | | Name | Version | 30 | |------|---------| 31 | | [terraform](#requirement\_terraform) | >= 1.0 | 32 | | [aws](#requirement\_aws) | >= 5.0 | 33 | 34 | ## Providers 35 | 36 | | Name | Version | 37 | |------|---------| 38 | | [aws](#provider\_aws) | >= 5.0 | 39 | 40 | ## Modules 41 | 42 | | Name | Source | Version | 43 | |------|--------|---------| 44 | | [lambda](#module\_lambda) | terraform-aws-modules/lambda/aws | ~> 6.0 | 45 | | [secrets\_manager](#module\_secrets\_manager) | ../.. | n/a | 46 | | [secrets\_manager\_disabled](#module\_secrets\_manager\_disabled) | ../.. | n/a | 47 | | [secrets\_manager\_rotate](#module\_secrets\_manager\_rotate) | ../.. | n/a | 48 | 49 | ## Resources 50 | 51 | | Name | Type | 52 | |------|------| 53 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 54 | | [aws_iam_policy_document.this](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 | | [rotate\_secret\_arn](#output\_rotate\_secret\_arn) | The ARN of the secret | 65 | | [rotate\_secret\_id](#output\_rotate\_secret\_id) | The ID of the secret | 66 | | [rotate\_secret\_replica](#output\_rotate\_secret\_replica) | Attributes of the replica created | 67 | | [rotate\_secret\_string](#output\_rotate\_secret\_string) | The secret string | 68 | | [rotate\_secret\_version\_id](#output\_rotate\_secret\_version\_id) | The unique identifier of the version of the secret | 69 | | [standard\_secret\_arn](#output\_standard\_secret\_arn) | The ARN of the secret | 70 | | [standard\_secret\_id](#output\_standard\_secret\_id) | The ID of the secret | 71 | | [standard\_secret\_name](#output\_standard\_secret\_name) | The name of the secret | 72 | | [standard\_secret\_replica](#output\_standard\_secret\_replica) | Attributes of the replica created | 73 | | [standard\_secret\_string](#output\_standard\_secret\_string) | The secret string | 74 | | [standard\_secret\_version\_id](#output\_standard\_secret\_version\_id) | The unique identifier of the version of the secret | 75 | 76 | 77 | Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-secrets-manager/blob/master/LICENSE). 78 | -------------------------------------------------------------------------------- /examples/complete/function.py: -------------------------------------------------------------------------------- 1 | # Credit: https://github.com/aws-samples/aws-secrets-manager-rotation-lambdas/blob/master/SecretsManagerRDSMariaDBRotationSingleUser/lambda_function.py 2 | 3 | import boto3 4 | import json 5 | import logging 6 | import os 7 | 8 | logger = logging.getLogger() 9 | logger.setLevel(logging.INFO) 10 | 11 | CLIENT = boto3.client('secretsmanager') 12 | 13 | def lambda_handler(event, context): 14 | """Secrets Manager RDS MariaDB Handler 15 | 16 | This handler uses the single-user rotation scheme to rotate an RDS MariaDB user credential. This rotation scheme 17 | logs into the database as the user and rotates the user's own password, immediately invalidating the user's 18 | previous password. 19 | 20 | The Secret SecretString is expected to be a JSON string with the following format: 21 | { 22 | 'engine': , 23 | 'host': , 24 | 'username': , 25 | 'password': , 26 | 'dbname': , 27 | 'port': 28 | } 29 | """ 30 | arn = event['SecretId'] 31 | token = event['ClientRequestToken'] 32 | step = event['Step'] 33 | 34 | # Make sure the version is staged correctly 35 | metadata = CLIENT.describe_secret(SecretId=arn) 36 | if "RotationEnabled" in metadata and not metadata['RotationEnabled']: 37 | logger.error("Secret %s is not enabled for rotation" % arn) 38 | raise ValueError("Secret %s is not enabled for rotation" % arn) 39 | versions = metadata['VersionIdsToStages'] 40 | if token not in versions: 41 | logger.error("Secret version %s has no stage for rotation of secret %s." % (token, arn)) 42 | raise ValueError("Secret version %s has no stage for rotation of secret %s." % (token, arn)) 43 | if "AWSCURRENT" in versions[token]: 44 | logger.info("Secret version %s already set as AWSCURRENT for secret %s." % (token, arn)) 45 | return 46 | elif "AWSPENDING" not in versions[token]: 47 | logger.error("Secret version %s not set as AWSPENDING for rotation of secret %s." % (token, arn)) 48 | raise ValueError("Secret version %s not set as AWSPENDING for rotation of secret %s." % (token, arn)) 49 | 50 | # Call the appropriate step 51 | if step == "createSecret": 52 | create_secret(CLIENT, arn, token) 53 | 54 | elif step == "setSecret": 55 | # Just an example, not setting secret 56 | pass 57 | 58 | elif step == "testSecret": 59 | # Just an example, not testing secret 60 | pass 61 | 62 | elif step == "finishSecret": 63 | finish_secret(CLIENT, arn, token) 64 | 65 | else: 66 | logger.error("lambda_handler: Invalid step parameter %s for secret %s" % (step, arn)) 67 | raise ValueError("Invalid step parameter %s for secret %s" % (step, arn)) 68 | 69 | 70 | def create_secret(service_client, arn, token): 71 | """Generate a new secret 72 | 73 | This method first checks for the existence of a secret for the passed in token. If one does not exist, it will generate a 74 | new secret and put it with the passed in token. 75 | 76 | Args: 77 | service_client (client): The secrets manager service client 78 | arn (string): The secret ARN or other identifier 79 | token (string): The ClientRequestToken associated with the secret version 80 | """ 81 | # Make sure the current secret exists 82 | current_dict = get_secret_dict(service_client, arn, "AWSCURRENT") 83 | 84 | # Now try to get the secret version, if that fails, put a new secret 85 | try: 86 | get_secret_dict(service_client, arn, "AWSPENDING", token) 87 | logger.info("createSecret: Successfully retrieved secret for %s." % arn) 88 | except service_client.exceptions.ResourceNotFoundException: 89 | # Get exclude characters from environment variable 90 | exclude_characters = os.environ.get('EXCLUDE_CHARACTERS', '/@"\'\\') 91 | # Generate a random password 92 | passwd = service_client.get_random_password(ExcludeCharacters=exclude_characters) 93 | current_dict['password'] = passwd['RandomPassword'] 94 | 95 | # Put the secret 96 | service_client.put_secret_value(SecretId=arn, ClientRequestToken=token, SecretString=json.dumps(current_dict), VersionStages=['AWSPENDING']) 97 | logger.info("createSecret: Successfully put secret for ARN %s and version %s." % (arn, token)) 98 | 99 | 100 | def finish_secret(service_client, arn, token): 101 | """Finish the rotation by marking the pending secret as current 102 | 103 | This method finishes the secret rotation by staging the secret staged AWSPENDING with the AWSCURRENT stage. 104 | 105 | Args: 106 | service_client (client): The secrets manager service client 107 | arn (string): The secret ARN or other identifier 108 | token (string): The ClientRequestToken associated with the secret version 109 | """ 110 | # First describe the secret to get the current version 111 | metadata = service_client.describe_secret(SecretId=arn) 112 | current_version = None 113 | for version in metadata["VersionIdsToStages"]: 114 | if "AWSCURRENT" in metadata["VersionIdsToStages"][version]: 115 | if version == token: 116 | # The correct version is already marked as current, return 117 | logger.info("finishSecret: Version %s already marked as AWSCURRENT for %s" % (version, arn)) 118 | return 119 | current_version = version 120 | break 121 | 122 | # Finalize by staging the secret version current 123 | service_client.update_secret_version_stage(SecretId=arn, VersionStage="AWSCURRENT", MoveToVersionId=token, RemoveFromVersionId=current_version) 124 | logger.info("finishSecret: Successfully set AWSCURRENT stage to version %s for secret %s." % (token, arn)) 125 | 126 | 127 | def get_secret_dict(service_client, arn, stage, token=None): 128 | """Gets the secret dictionary corresponding for the secret arn, stage, and token 129 | 130 | This helper function gets credentials for the arn and stage passed in and returns the dictionary by parsing the JSON string 131 | 132 | Args: 133 | service_client (client): The secrets manager service client 134 | arn (string): The secret ARN or other identifier 135 | token (string): The ClientRequestToken associated with the secret version, or None if no validation is desired 136 | stage (string): The stage identifying the secret version 137 | Returns: 138 | SecretDictionary: Secret dictionary 139 | """ 140 | required_fields = ['host', 'username', 'password'] 141 | 142 | # Only do VersionId validation against the stage if a token is passed in 143 | if token: 144 | secret = service_client.get_secret_value(SecretId=arn, VersionId=token, VersionStage=stage) 145 | else: 146 | secret = service_client.get_secret_value(SecretId=arn, VersionStage=stage) 147 | plaintext = secret['SecretString'] 148 | secret_dict = json.loads(plaintext) 149 | 150 | # Run validations against the secret 151 | if 'engine' not in secret_dict or secret_dict['engine'] != 'mariadb': 152 | raise KeyError("Database engine must be set to 'mariadb' in order to use this rotation lambda") 153 | for field in required_fields: 154 | if field not in secret_dict: 155 | raise KeyError("%s key is missing from secret JSON" % field) 156 | 157 | # Parse and return the secret JSON string 158 | return secret_dict 159 | -------------------------------------------------------------------------------- /examples/complete/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | } 4 | 5 | data "aws_caller_identity" "current" {} 6 | 7 | locals { 8 | region = "eu-west-1" 9 | name = "secrets-manager-ex-${basename(path.cwd)}" 10 | 11 | tags = { 12 | Name = local.name 13 | Example = local.name 14 | Repository = "https://github.com/terraform-aws-modules/terraform-aws-secrets-manager" 15 | } 16 | } 17 | 18 | ################################################################################ 19 | # Secrets Manager 20 | ################################################################################ 21 | 22 | module "secrets_manager" { 23 | source = "../.." 24 | 25 | # Secret 26 | name_prefix = local.name 27 | description = "Example Secrets Manager secret" 28 | recovery_window_in_days = 0 29 | replica = { 30 | # Can set region as key 31 | us-east-1 = {} 32 | another = { 33 | # Or as attribute 34 | region = "us-west-2" 35 | } 36 | } 37 | 38 | # Policy 39 | create_policy = true 40 | block_public_policy = true 41 | policy_statements = { 42 | read = { 43 | sid = "AllowAccountRead" 44 | principals = [{ 45 | type = "AWS" 46 | identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] 47 | }] 48 | actions = ["secretsmanager:GetSecretValue"] 49 | resources = ["*"] 50 | } 51 | } 52 | 53 | # Version 54 | create_random_password = true 55 | random_password_length = 64 56 | random_password_override_special = "!@#$%^&*()_+" 57 | 58 | tags = local.tags 59 | } 60 | 61 | module "secrets_manager_rotate" { 62 | source = "../.." 63 | 64 | # Secret 65 | name_prefix = local.name 66 | description = "Rotated example Secrets Manager secret" 67 | recovery_window_in_days = 0 68 | 69 | # Policy 70 | create_policy = true 71 | block_public_policy = true 72 | policy_statements = { 73 | lambda = { 74 | sid = "LambdaReadWrite" 75 | principals = [{ 76 | type = "AWS" 77 | identifiers = [module.lambda.lambda_role_arn] 78 | }] 79 | actions = [ 80 | "secretsmanager:DescribeSecret", 81 | "secretsmanager:GetSecretValue", 82 | "secretsmanager:PutSecretValue", 83 | "secretsmanager:UpdateSecretVersionStage", 84 | ] 85 | resources = ["*"] 86 | } 87 | account = { 88 | sid = "AccountDescribe" 89 | principals = [{ 90 | type = "AWS" 91 | identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] 92 | }] 93 | actions = ["secretsmanager:DescribeSecret"] 94 | resources = ["*"] 95 | } 96 | } 97 | 98 | # Version 99 | ignore_secret_changes = true 100 | secret_string = jsonencode({ 101 | engine = "mariadb", 102 | host = "mydb.cluster-123456789012.us-east-1.rds.amazonaws.com", 103 | username = "Bill", 104 | password = "ThisIsMySuperSecretString12356!" 105 | dbname = "mydb", 106 | port = 3306 107 | }) 108 | 109 | # Rotation 110 | enable_rotation = true 111 | rotation_lambda_arn = module.lambda.lambda_function_arn 112 | rotation_rules = { 113 | # This should be more sensible in production 114 | schedule_expression = "rate(6 hours)" 115 | } 116 | 117 | tags = local.tags 118 | } 119 | 120 | module "secrets_manager_disabled" { 121 | source = "../.." 122 | 123 | create = false 124 | } 125 | 126 | ################################################################################ 127 | # Supporting Resources 128 | ################################################################################ 129 | 130 | # https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-required-permissions-function.html 131 | data "aws_iam_policy_document" "this" { 132 | statement { 133 | actions = [ 134 | "secretsmanager:DescribeSecret", 135 | "secretsmanager:GetSecretValue", 136 | "secretsmanager:PutSecretValue", 137 | "secretsmanager:UpdateSecretVersionStage", 138 | ] 139 | resources = [module.secrets_manager.secret_arn] 140 | } 141 | 142 | statement { 143 | actions = ["secretsmanager:GetRandomPassword"] 144 | resources = ["*"] 145 | } 146 | 147 | statement { 148 | actions = ["secretsmanager:GetRandomPassword"] 149 | resources = ["*"] 150 | } 151 | } 152 | 153 | module "lambda" { 154 | source = "terraform-aws-modules/lambda/aws" 155 | version = "~> 6.0" 156 | 157 | function_name = local.name 158 | description = "Example Secrets Manager secret rotation lambda function" 159 | 160 | handler = "function.lambda_handler" 161 | runtime = "python3.10" 162 | timeout = 60 163 | memory_size = 512 164 | source_path = "${path.module}/function.py" 165 | 166 | attach_policy_json = true 167 | policy_json = data.aws_iam_policy_document.this.json 168 | 169 | publish = true 170 | allowed_triggers = { 171 | secrets = { 172 | principal = "secretsmanager.amazonaws.com" 173 | } 174 | } 175 | 176 | cloudwatch_logs_retention_in_days = 7 177 | 178 | tags = local.tags 179 | } 180 | -------------------------------------------------------------------------------- /examples/complete/outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Standard 3 | ################################################################################ 4 | 5 | output "standard_secret_arn" { 6 | description = "The ARN of the secret" 7 | value = module.secrets_manager.secret_arn 8 | } 9 | 10 | output "standard_secret_id" { 11 | description = "The ID of the secret" 12 | value = module.secrets_manager.secret_id 13 | } 14 | 15 | output "standard_secret_name" { 16 | description = "The name of the secret" 17 | value = module.secrets_manager.secret_name 18 | } 19 | 20 | output "standard_secret_replica" { 21 | description = "Attributes of the replica created" 22 | value = module.secrets_manager.secret_replica 23 | } 24 | 25 | output "standard_secret_version_id" { 26 | description = "The unique identifier of the version of the secret" 27 | value = module.secrets_manager.secret_version_id 28 | } 29 | 30 | output "standard_secret_string" { 31 | description = "The secret string" 32 | sensitive = true 33 | value = module.secrets_manager.secret_string 34 | } 35 | 36 | ################################################################################ 37 | # Rotate 38 | ################################################################################ 39 | 40 | output "rotate_secret_arn" { 41 | description = "The ARN of the secret" 42 | value = module.secrets_manager_rotate.secret_arn 43 | } 44 | 45 | output "rotate_secret_id" { 46 | description = "The ID of the secret" 47 | value = module.secrets_manager_rotate.secret_id 48 | } 49 | 50 | output "rotate_secret_replica" { 51 | description = "Attributes of the replica created" 52 | value = module.secrets_manager_rotate.secret_replica 53 | } 54 | 55 | output "rotate_secret_version_id" { 56 | description = "The unique identifier of the version of the secret" 57 | value = module.secrets_manager_rotate.secret_version_id 58 | } 59 | 60 | output "rotate_secret_string" { 61 | description = "The secret string" 62 | sensitive = true 63 | value = module.secrets_manager_rotate.secret_string 64 | } 65 | -------------------------------------------------------------------------------- /examples/complete/variables.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-secrets-manager/f7749e2d1a58eb548314f3c061b320c67a23b669/examples/complete/variables.tf -------------------------------------------------------------------------------- /examples/complete/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | 2 | ################################################################################ 3 | # Secret 4 | ################################################################################ 5 | 6 | resource "aws_secretsmanager_secret" "this" { 7 | count = var.create ? 1 : 0 8 | 9 | description = var.description 10 | force_overwrite_replica_secret = var.force_overwrite_replica_secret 11 | kms_key_id = var.kms_key_id 12 | name = var.name 13 | name_prefix = var.name_prefix 14 | recovery_window_in_days = var.recovery_window_in_days 15 | 16 | dynamic "replica" { 17 | for_each = var.replica 18 | 19 | content { 20 | kms_key_id = try(replica.value.kms_key_id, null) 21 | region = try(replica.value.region, replica.key) 22 | } 23 | } 24 | 25 | tags = var.tags 26 | } 27 | 28 | ################################################################################ 29 | # Policy 30 | ################################################################################ 31 | 32 | data "aws_iam_policy_document" "this" { 33 | count = var.create && var.create_policy ? 1 : 0 34 | 35 | source_policy_documents = var.source_policy_documents 36 | override_policy_documents = var.override_policy_documents 37 | 38 | dynamic "statement" { 39 | for_each = var.policy_statements 40 | 41 | content { 42 | sid = try(statement.value.sid, null) 43 | actions = try(statement.value.actions, null) 44 | not_actions = try(statement.value.not_actions, null) 45 | effect = try(statement.value.effect, null) 46 | resources = try(statement.value.resources, null) 47 | not_resources = try(statement.value.not_resources, null) 48 | 49 | dynamic "principals" { 50 | for_each = try(statement.value.principals, []) 51 | 52 | content { 53 | type = principals.value.type 54 | identifiers = principals.value.identifiers 55 | } 56 | } 57 | 58 | dynamic "not_principals" { 59 | for_each = try(statement.value.not_principals, []) 60 | 61 | content { 62 | type = not_principals.value.type 63 | identifiers = not_principals.value.identifiers 64 | } 65 | } 66 | 67 | dynamic "condition" { 68 | for_each = try(statement.value.conditions, []) 69 | 70 | content { 71 | test = condition.value.test 72 | values = condition.value.values 73 | variable = condition.value.variable 74 | } 75 | } 76 | } 77 | } 78 | } 79 | 80 | resource "aws_secretsmanager_secret_policy" "this" { 81 | count = var.create && var.create_policy ? 1 : 0 82 | 83 | secret_arn = aws_secretsmanager_secret.this[0].arn 84 | policy = data.aws_iam_policy_document.this[0].json 85 | block_public_policy = var.block_public_policy 86 | } 87 | 88 | ################################################################################ 89 | # Version 90 | ################################################################################ 91 | 92 | resource "aws_secretsmanager_secret_version" "this" { 93 | count = var.create && !(var.enable_rotation || var.ignore_secret_changes) ? 1 : 0 94 | 95 | secret_id = aws_secretsmanager_secret.this[0].id 96 | secret_string = var.create_random_password ? random_password.this[0].result : var.secret_string 97 | secret_binary = var.secret_binary 98 | version_stages = var.version_stages 99 | } 100 | 101 | resource "aws_secretsmanager_secret_version" "ignore_changes" { 102 | count = var.create && (var.enable_rotation || var.ignore_secret_changes) ? 1 : 0 103 | 104 | secret_id = aws_secretsmanager_secret.this[0].id 105 | secret_string = var.create_random_password ? random_password.this[0].result : var.secret_string 106 | secret_binary = var.secret_binary 107 | version_stages = var.version_stages 108 | 109 | lifecycle { 110 | ignore_changes = [ 111 | secret_string, 112 | secret_binary, 113 | version_stages, 114 | ] 115 | } 116 | } 117 | 118 | resource "random_password" "this" { 119 | count = var.create && var.create_random_password ? 1 : 0 120 | 121 | length = var.random_password_length 122 | special = true 123 | override_special = var.random_password_override_special 124 | } 125 | 126 | ################################################################################ 127 | # Rotation 128 | ################################################################################ 129 | 130 | resource "aws_secretsmanager_secret_rotation" "this" { 131 | count = var.create && var.enable_rotation ? 1 : 0 132 | 133 | rotation_lambda_arn = var.rotation_lambda_arn 134 | 135 | dynamic "rotation_rules" { 136 | for_each = [var.rotation_rules] 137 | 138 | content { 139 | automatically_after_days = try(rotation_rules.value.automatically_after_days, null) 140 | duration = try(rotation_rules.value.duration, null) 141 | schedule_expression = try(rotation_rules.value.schedule_expression, null) 142 | } 143 | } 144 | 145 | secret_id = aws_secretsmanager_secret.this[0].id 146 | } 147 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Secret 3 | ################################################################################ 4 | 5 | output "secret_arn" { 6 | description = "The ARN of the secret" 7 | value = try(aws_secretsmanager_secret.this[0].arn, null) 8 | } 9 | 10 | output "secret_id" { 11 | description = "The ID of the secret" 12 | value = try(aws_secretsmanager_secret.this[0].id, null) 13 | } 14 | 15 | output "secret_name" { 16 | description = "The name of the secret" 17 | value = try(aws_secretsmanager_secret.this[0].name, null) 18 | } 19 | 20 | output "secret_replica" { 21 | description = "Attributes of the replica created" 22 | value = try(aws_secretsmanager_secret.this[0].replica, null) 23 | } 24 | 25 | output "secret_string" { 26 | description = "The secret string" 27 | sensitive = true 28 | value = try(aws_secretsmanager_secret_version.this[0].secret_string, aws_secretsmanager_secret_version.ignore_changes[0].secret_string, null) 29 | } 30 | 31 | output "secret_binary" { 32 | description = "The secret binary" 33 | sensitive = true 34 | value = try(aws_secretsmanager_secret_version.this[0].secret_binary, aws_secretsmanager_secret_version.ignore_changes[0].secret_binary, null) 35 | } 36 | 37 | ################################################################################ 38 | # Version 39 | ################################################################################ 40 | 41 | output "secret_version_id" { 42 | description = "The unique identifier of the version of the secret" 43 | value = try(aws_secretsmanager_secret_version.this[0].version_id, aws_secretsmanager_secret_version.ignore_changes[0].version_id, null) 44 | } 45 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "create" { 2 | description = "Determines whether resources will be created (affects all resources)" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "tags" { 8 | description = "A map of tags to add to all resources" 9 | type = map(string) 10 | default = {} 11 | } 12 | 13 | ################################################################################ 14 | # Secret 15 | ################################################################################ 16 | 17 | variable "description" { 18 | description = "A description of the secret" 19 | type = string 20 | default = null 21 | } 22 | 23 | variable "force_overwrite_replica_secret" { 24 | description = "Accepts boolean value to specify whether to overwrite a secret with the same name in the destination Region" 25 | type = bool 26 | default = null 27 | } 28 | 29 | variable "kms_key_id" { 30 | description = "ARN or Id of the AWS KMS key to be used to encrypt the secret values in the versions stored in this secret. If you need to reference a CMK in a different account, you can use only the key ARN. If you don't specify this value, then Secrets Manager defaults to using the AWS account's default KMS key (the one named `aws/secretsmanager`" 31 | type = string 32 | default = null 33 | } 34 | 35 | variable "name" { 36 | description = "Friendly name of the new secret. The secret name can consist of uppercase letters, lowercase letters, digits, and any of the following characters: `/_+=.@-`" 37 | type = string 38 | default = null 39 | } 40 | 41 | variable "name_prefix" { 42 | description = "Creates a unique name beginning with the specified prefix" 43 | type = string 44 | default = null 45 | } 46 | 47 | variable "recovery_window_in_days" { 48 | description = "Number of days that AWS Secrets Manager waits before it can delete the secret. This value can be `0` to force deletion without recovery or range from `7` to `30` days. The default value is `30`" 49 | type = number 50 | default = null 51 | } 52 | 53 | variable "replica" { 54 | description = "Configuration block to support secret replication" 55 | type = map(any) 56 | default = {} 57 | } 58 | 59 | ################################################################################ 60 | # Policy 61 | ################################################################################ 62 | 63 | variable "create_policy" { 64 | description = "Determines whether a policy will be created" 65 | type = bool 66 | default = false 67 | } 68 | 69 | variable "source_policy_documents" { 70 | description = "List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s" 71 | type = list(string) 72 | default = [] 73 | } 74 | 75 | variable "override_policy_documents" { 76 | 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`" 77 | type = list(string) 78 | default = [] 79 | } 80 | 81 | variable "policy_statements" { 82 | 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" 83 | type = map(any) 84 | default = {} 85 | } 86 | 87 | variable "block_public_policy" { 88 | description = "Makes an optional API call to Zelkova to validate the Resource Policy to prevent broad access to your secret" 89 | type = bool 90 | default = null 91 | } 92 | 93 | ################################################################################ 94 | # Version 95 | ################################################################################ 96 | 97 | variable "ignore_secret_changes" { 98 | description = "Determines whether or not Terraform will ignore changes made externally to `secret_string` or `secret_binary`. Changing this value after creation is a destructive operation" 99 | type = bool 100 | default = false 101 | } 102 | 103 | variable "secret_string" { 104 | description = "Specifies text data that you want to encrypt and store in this version of the secret. This is required if `secret_binary` is not set" 105 | type = string 106 | default = null 107 | } 108 | 109 | variable "secret_binary" { 110 | description = "Specifies binary data that you want to encrypt and store in this version of the secret. This is required if `secret_string` is not set. Needs to be encoded to base64" 111 | type = string 112 | default = null 113 | } 114 | 115 | variable "version_stages" { 116 | description = "Specifies a list of staging labels that are attached to this version of the secret. A staging label must be unique to a single version of the secret" 117 | type = list(string) 118 | default = null 119 | } 120 | 121 | variable "create_random_password" { 122 | description = "Determines whether a random password will be generated" 123 | type = bool 124 | default = false 125 | } 126 | 127 | variable "random_password_length" { 128 | description = "The length of the generated random password" 129 | type = number 130 | default = 32 131 | } 132 | 133 | variable "random_password_override_special" { 134 | description = "Supply your own list of special characters to use for string generation. This overrides the default character list in the special argument" 135 | type = string 136 | default = "!@#$%&*()-_=+[]{}<>:?" 137 | } 138 | 139 | ################################################################################ 140 | # Rotation 141 | ################################################################################ 142 | 143 | variable "enable_rotation" { 144 | description = "Determines whether secret rotation is enabled" 145 | type = bool 146 | default = false 147 | } 148 | 149 | variable "rotation_lambda_arn" { 150 | description = "Specifies the ARN of the Lambda function that can rotate the secret" 151 | type = string 152 | default = "" 153 | } 154 | 155 | variable "rotation_rules" { 156 | description = "A structure that defines the rotation configuration for this secret" 157 | type = map(any) 158 | default = {} 159 | } 160 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 3.0" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /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/secrets-manager/aws//wrappers" 16 | # Alternative source: 17 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-secrets-manager.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/secrets-manager/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/main.tf: -------------------------------------------------------------------------------- 1 | module "wrapper" { 2 | source = "../" 3 | 4 | for_each = var.items 5 | 6 | block_public_policy = try(each.value.block_public_policy, var.defaults.block_public_policy, null) 7 | create = try(each.value.create, var.defaults.create, true) 8 | create_policy = try(each.value.create_policy, var.defaults.create_policy, false) 9 | create_random_password = try(each.value.create_random_password, var.defaults.create_random_password, false) 10 | description = try(each.value.description, var.defaults.description, null) 11 | enable_rotation = try(each.value.enable_rotation, var.defaults.enable_rotation, false) 12 | force_overwrite_replica_secret = try(each.value.force_overwrite_replica_secret, var.defaults.force_overwrite_replica_secret, null) 13 | ignore_secret_changes = try(each.value.ignore_secret_changes, var.defaults.ignore_secret_changes, false) 14 | kms_key_id = try(each.value.kms_key_id, var.defaults.kms_key_id, null) 15 | name = try(each.value.name, var.defaults.name, null) 16 | name_prefix = try(each.value.name_prefix, var.defaults.name_prefix, null) 17 | override_policy_documents = try(each.value.override_policy_documents, var.defaults.override_policy_documents, []) 18 | policy_statements = try(each.value.policy_statements, var.defaults.policy_statements, {}) 19 | random_password_length = try(each.value.random_password_length, var.defaults.random_password_length, 32) 20 | random_password_override_special = try(each.value.random_password_override_special, var.defaults.random_password_override_special, "!@#$%&*()-_=+[]{}<>:?") 21 | recovery_window_in_days = try(each.value.recovery_window_in_days, var.defaults.recovery_window_in_days, null) 22 | replica = try(each.value.replica, var.defaults.replica, {}) 23 | rotation_lambda_arn = try(each.value.rotation_lambda_arn, var.defaults.rotation_lambda_arn, "") 24 | rotation_rules = try(each.value.rotation_rules, var.defaults.rotation_rules, {}) 25 | secret_binary = try(each.value.secret_binary, var.defaults.secret_binary, null) 26 | secret_string = try(each.value.secret_string, var.defaults.secret_string, null) 27 | source_policy_documents = try(each.value.source_policy_documents, var.defaults.source_policy_documents, []) 28 | tags = try(each.value.tags, var.defaults.tags, {}) 29 | version_stages = try(each.value.version_stages, var.defaults.version_stages, null) 30 | } 31 | -------------------------------------------------------------------------------- /wrappers/outputs.tf: -------------------------------------------------------------------------------- 1 | output "wrapper" { 2 | description = "Map of outputs of a wrapper." 3 | value = module.wrapper 4 | sensitive = true # At least one sensitive module output (secret_string) found (requires Terraform 0.14+) 5 | } 6 | -------------------------------------------------------------------------------- /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.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 3.0" 12 | } 13 | } 14 | } 15 | --------------------------------------------------------------------------------