diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml
index 6b6c9ce..bd5f2df 100644
--- a/.github/workflows/lock.yml
+++ b/.github/workflows/lock.yml
@@ -8,7 +8,7 @@ jobs:
   lock:
     runs-on: ubuntu-latest
     steps:
-      - uses: dessant/lock-threads@v4
+      - uses: dessant/lock-threads@v5
         with:
           github-token: ${{ secrets.GITHUB_TOKEN }}
           issue-comment: >
diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml
index cb32a0f..1e50760 100644
--- a/.github/workflows/pr-title.yml
+++ b/.github/workflows/pr-title.yml
@@ -14,7 +14,7 @@ jobs:
     steps:
       # Please look up the latest version from
       # https://github.com/amannn/action-semantic-pull-request/releases
-      - uses: amannn/action-semantic-pull-request@v5.0.2
+      - uses: amannn/action-semantic-pull-request@v5.5.3
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
index cb82671..a19ff83 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit.yml
@@ -7,8 +7,8 @@ on:
       - master
 
 env:
-  TERRAFORM_DOCS_VERSION: v0.16.0
-  TFLINT_VERSION: v0.44.1
+  TERRAFORM_DOCS_VERSION: v0.19.0
+  TFLINT_VERSION: v0.53.0
 
 jobs:
   collectInputs:
@@ -18,11 +18,11 @@ jobs:
       directories: ${{ steps.dirs.outputs.directories }}
     steps:
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Get root directories
         id: dirs
-        uses: clowdhaus/terraform-composite-actions/directories@v1.8.3
+        uses: clowdhaus/terraform-composite-actions/directories@v1.9.0
 
   preCommitMinVersions:
     name: Min TF pre-commit
@@ -32,19 +32,27 @@ jobs:
       matrix:
         directory: ${{ fromJson(needs.collectInputs.outputs.directories) }}
     steps:
+      # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449
+      - name: Delete huge unnecessary tools folder
+        run: |
+          rm -rf /opt/hostedtoolcache/CodeQL
+          rm -rf /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk
+          rm -rf /opt/hostedtoolcache/Ruby
+          rm -rf /opt/hostedtoolcache/go
+
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Terraform min/max versions
         id: minMax
-        uses: clowdhaus/terraform-min-max@v1.2.4
+        uses: clowdhaus/terraform-min-max@v1.3.1
         with:
           directory: ${{ matrix.directory }}
 
       - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }}
         # Run only validate pre-commit check on min version supported
         if: ${{ matrix.directory !=  '.' }}
-        uses: clowdhaus/terraform-composite-actions/pre-commit@v1.8.3
+        uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1
         with:
           terraform-version: ${{ steps.minMax.outputs.minVersion }}
           tflint-version: ${{ env.TFLINT_VERSION }}
@@ -53,7 +61,7 @@ jobs:
       - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }}
         # Run only validate pre-commit check on min version supported
         if: ${{ matrix.directory ==  '.' }}
-        uses: clowdhaus/terraform-composite-actions/pre-commit@v1.8.3
+        uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1
         with:
           terraform-version: ${{ steps.minMax.outputs.minVersion }}
           tflint-version: ${{ env.TFLINT_VERSION }}
@@ -64,18 +72,26 @@ jobs:
     runs-on: ubuntu-latest
     needs: collectInputs
     steps:
+      # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449
+      - name: Delete huge unnecessary tools folder
+        run: |
+          rm -rf /opt/hostedtoolcache/CodeQL
+          rm -rf /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk
+          rm -rf /opt/hostedtoolcache/Ruby
+          rm -rf /opt/hostedtoolcache/go
+
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           ref: ${{ github.event.pull_request.head.ref }}
           repository: ${{github.event.pull_request.head.repo.full_name}}
 
       - name: Terraform min/max versions
         id: minMax
-        uses: clowdhaus/terraform-min-max@v1.2.4
+        uses: clowdhaus/terraform-min-max@v1.3.1
 
       - name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }}
-        uses: clowdhaus/terraform-composite-actions/pre-commit@v1.8.3
+        uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1
         with:
           terraform-version: ${{ steps.minMax.outputs.maxVersion }}
           tflint-version: ${{ env.TFLINT_VERSION }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 81f6747..4a94226 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -20,18 +20,18 @@ jobs:
     if: github.repository_owner == 'terraform-aws-modules'
     steps:
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           persist-credentials: false
           fetch-depth: 0
 
       - name: Release
-        uses: cycjimmy/semantic-release-action@v3
+        uses: cycjimmy/semantic-release-action@v4
         with:
-          semantic_version: 18.0.0
+          semantic_version: 23.0.2
           extra_plugins: |
-            @semantic-release/changelog@6.0.0
-            @semantic-release/git@10.0.0
-            conventional-changelog-conventionalcommits@4.6.3
+            @semantic-release/changelog@6.0.3
+            @semantic-release/git@10.0.1
+            conventional-changelog-conventionalcommits@7.0.2
         env:
           GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}
diff --git a/.github/workflows/stale-actions.yaml b/.github/workflows/stale-actions.yaml
index 5037995..6ccd0ed 100644
--- a/.github/workflows/stale-actions.yaml
+++ b/.github/workflows/stale-actions.yaml
@@ -7,7 +7,7 @@ jobs:
   stale:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/stale@v6
+      - uses: actions/stale@v9
         with:
           repo-token: ${{ secrets.GITHUB_TOKEN }}
           # Staling issues and PR's
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 071427d..7e4e7da 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,9 +1,9 @@
 repos:
   - repo: https://github.com/antonbabenko/pre-commit-terraform
-    rev: v1.77.1
+    rev: v1.96.1
     hooks:
       - id: terraform_fmt
-      - id: terraform_validate
+      - id: terraform_wrapper_module_for_each
       - id: terraform_docs
         args:
           - '--args=--lockfile=false'
@@ -22,8 +22,10 @@ repos:
           - '--args=--only=terraform_required_providers'
           - '--args=--only=terraform_standard_module_structure'
           - '--args=--only=terraform_workspace_remote'
+      - id: terraform_validate
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.4.0
+    rev: v5.0.0
     hooks:
       - id: check-merge-conflict
       - id: end-of-file-fixer
+      - id: trailing-whitespace
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0fe5523..ccbba5e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,52 @@
 
 All notable changes to this project will be documented in this file.
 
+## [3.1.1](https://github.com/terraform-aws-modules/terraform-aws-kms/compare/v3.1.0...v3.1.1) (2024-10-11)
+
+
+### Bug Fixes
+
+* Update CI workflow versions to latest ([#35](https://github.com/terraform-aws-modules/terraform-aws-kms/issues/35)) ([c248050](https://github.com/terraform-aws-modules/terraform-aws-kms/commit/c2480502618251c45ab8bcf6c57ec37bd08a8370))
+
+## [3.1.0](https://github.com/terraform-aws-modules/terraform-aws-kms/compare/v3.0.0...v3.1.0) (2024-06-12)
+
+
+### Features
+
+* Grants output marked as sensitive data ([#33](https://github.com/terraform-aws-modules/terraform-aws-kms/issues/33)) ([965a52d](https://github.com/terraform-aws-modules/terraform-aws-kms/commit/965a52d6686898897bce31d2f409e2ff9e8d5268))
+
+## [3.0.0](https://github.com/terraform-aws-modules/terraform-aws-kms/compare/v2.2.1...v3.0.0) (2024-05-11)
+
+
+### ⚠ BREAKING CHANGES
+
+* Support `rotation_period_in_days`, AWS Provider v5, Terraform MSV 1.3 (#32)
+
+### Features
+
+* Support `rotation_period_in_days`, AWS Provider v5, Terraform MSV 1.3 ([#32](https://github.com/terraform-aws-modules/terraform-aws-kms/issues/32)) ([f8c96ce](https://github.com/terraform-aws-modules/terraform-aws-kms/commit/f8c96ce4bfc45fa2cb2e2cfa346d0d1930cdfce3))
+
+## [2.2.1](https://github.com/terraform-aws-modules/terraform-aws-kms/compare/v2.2.0...v2.2.1) (2024-03-06)
+
+
+### Bug Fixes
+
+* Update CI workflow versions to remove deprecated runtime warnings ([#28](https://github.com/terraform-aws-modules/terraform-aws-kms/issues/28)) ([866950f](https://github.com/terraform-aws-modules/terraform-aws-kms/commit/866950f91b3bc4411fa14d1f5c2c304145540d7f))
+
+## [2.2.0](https://github.com/terraform-aws-modules/terraform-aws-kms/compare/v2.1.0...v2.2.0) (2024-02-02)
+
+
+### Features
+
+* Add wrapper modules ([#26](https://github.com/terraform-aws-modules/terraform-aws-kms/issues/26)) ([e6eba07](https://github.com/terraform-aws-modules/terraform-aws-kms/commit/e6eba07467818a27670db60b3eb46f98dff19ef9))
+
+## [2.1.0](https://github.com/terraform-aws-modules/terraform-aws-kms/compare/v2.0.1...v2.1.0) (2023-11-03)
+
+
+### Features
+
+* Do not call data resources when `create` is `false` ([#25](https://github.com/terraform-aws-modules/terraform-aws-kms/issues/25)) ([4951c38](https://github.com/terraform-aws-modules/terraform-aws-kms/commit/4951c38f3cd569411eb53476ac8502981083f5d2))
+
 ### [2.0.1](https://github.com/terraform-aws-modules/terraform-aws-kms/compare/v2.0.0...v2.0.1) (2023-09-18)
 
 
diff --git a/README.md b/README.md
index 0e813f0..8616254 100644
--- a/README.md
+++ b/README.md
@@ -141,19 +141,19 @@ Examples codified under the [`examples`](https://github.com/terraform-aws-module
 
 - [Complete](https://github.com/terraform-aws-modules/terraform-aws-kms/tree/master/examples/complete)
 
-<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
+<!-- BEGIN_TF_DOCS -->
 ## Requirements
 
 | Name | Version |
 |------|---------|
-| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
-| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 4.33 |
+| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.3 |
+| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.49 |
 
 ## Providers
 
 | Name | Version |
 |------|---------|
-| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 4.33 |
+| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.49 |
 
 ## Modules
 
@@ -211,6 +211,7 @@ No modules.
 | <a name="input_policy"></a> [policy](#input\_policy) | A valid policy JSON document. Although this is a key policy, not an IAM policy, an `aws_iam_policy_document`, in the form that designates a principal, can be used | `string` | `null` | no |
 | <a name="input_primary_external_key_arn"></a> [primary\_external\_key\_arn](#input\_primary\_external\_key\_arn) | The primary external key arn of a multi-region replica external key | `string` | `null` | no |
 | <a name="input_primary_key_arn"></a> [primary\_key\_arn](#input\_primary\_key\_arn) | The primary key arn of a multi-region replica key | `string` | `null` | no |
+| <a name="input_rotation_period_in_days"></a> [rotation\_period\_in\_days](#input\_rotation\_period\_in\_days) | Custom period of time between each rotation date. Must be a number between 90 and 2560 (inclusive) | `number` | `null` | no |
 | <a name="input_route53_dnssec_sources"></a> [route53\_dnssec\_sources](#input\_route53\_dnssec\_sources) | A list of maps containing `account_ids` and Route53 `hosted_zone_arn` that will be allowed to sign DNSSEC records | `list(any)` | `[]` | no |
 | <a name="input_source_policy_documents"></a> [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 |
 | <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
@@ -228,7 +229,7 @@ No modules.
 | <a name="output_key_arn"></a> [key\_arn](#output\_key\_arn) | The Amazon Resource Name (ARN) of the key |
 | <a name="output_key_id"></a> [key\_id](#output\_key\_id) | The globally unique identifier for the key |
 | <a name="output_key_policy"></a> [key\_policy](#output\_key\_policy) | The IAM resource policy set on the key |
-<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
+<!-- END_TF_DOCS -->
 
 ## License
 
diff --git a/examples/complete/README.md b/examples/complete/README.md
index 48d306d..5eb9a53 100644
--- a/examples/complete/README.md
+++ b/examples/complete/README.md
@@ -19,19 +19,19 @@ $ terraform apply
 
 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.
 
-<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
+<!-- BEGIN_TF_DOCS -->
 ## Requirements
 
 | Name | Version |
 |------|---------|
-| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
-| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 4.33 |
+| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.3 |
+| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.49 |
 
 ## Providers
 
 | Name | Version |
 |------|---------|
-| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 4.33 |
+| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.49 |
 
 ## Modules
 
@@ -103,6 +103,6 @@ No inputs.
 | <a name="output_replica_key_policy"></a> [replica\_key\_policy](#output\_replica\_key\_policy) | The IAM resource policy set on the key |
 | <a name="output_replica_key_state"></a> [replica\_key\_state](#output\_replica\_key\_state) | The state of the CMK |
 | <a name="output_replica_key_usage"></a> [replica\_key\_usage](#output\_replica\_key\_usage) | The cryptographic operations for which you can use the CMK |
-<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
+<!-- END_TF_DOCS -->
 
 Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-kms/blob/master/LICENSE).
diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf
index 14a7ee2..f96e1b1 100644
--- a/examples/complete/versions.tf
+++ b/examples/complete/versions.tf
@@ -1,10 +1,10 @@
 terraform {
-  required_version = ">= 1.0"
+  required_version = ">= 1.3"
 
   required_providers {
     aws = {
       source  = "hashicorp/aws"
-      version = ">= 4.33"
+      version = ">= 5.49"
     }
   }
 }
diff --git a/main.tf b/main.tf
index 261e5a2..819de11 100644
--- a/main.tf
+++ b/main.tf
@@ -1,5 +1,15 @@
-data "aws_partition" "current" {}
-data "aws_caller_identity" "current" {}
+data "aws_partition" "current" {
+  count = var.create ? 1 : 0
+}
+data "aws_caller_identity" "current" {
+  count = var.create ? 1 : 0
+}
+
+locals {
+  account_id = try(data.aws_caller_identity.current[0].account_id, "")
+  partition  = try(data.aws_partition.current[0].partition, "")
+  dns_suffix = try(data.aws_partition.current[0].dns_suffix, "")
+}
 
 ################################################################################
 # Key
@@ -18,6 +28,7 @@ resource "aws_kms_key" "this" {
   key_usage                          = var.key_usage
   multi_region                       = var.multi_region
   policy                             = coalesce(var.policy, data.aws_iam_policy_document.this[0].json)
+  rotation_period_in_days            = var.rotation_period_in_days
 
   tags = var.tags
 }
@@ -98,7 +109,7 @@ data "aws_iam_policy_document" "this" {
 
       principals {
         type        = "AWS"
-        identifiers = ["arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:root"]
+        identifiers = ["arn:${local.partition}:iam::${local.account_id}:root"]
       }
     }
   }
@@ -342,7 +353,7 @@ data "aws_iam_policy_document" "this" {
 
       principals {
         type        = "Service"
-        identifiers = ["dnssec-route53.${data.aws_partition.current.dns_suffix}"]
+        identifiers = ["dnssec-route53.${local.dns_suffix}"]
       }
     }
   }
@@ -358,7 +369,7 @@ data "aws_iam_policy_document" "this" {
 
       principals {
         type        = "Service"
-        identifiers = ["dnssec-route53.${data.aws_partition.current.dns_suffix}"]
+        identifiers = ["dnssec-route53.${local.dns_suffix}"]
       }
 
       condition {
@@ -373,7 +384,7 @@ data "aws_iam_policy_document" "this" {
         content {
           test     = "StringEquals"
           variable = "aws:SourceAccount"
-          values   = try(condition.value.account_ids, [data.aws_caller_identity.current.account_id])
+          values   = try(condition.value.account_ids, [local.account_id])
         }
       }
 
@@ -383,7 +394,7 @@ data "aws_iam_policy_document" "this" {
         content {
           test     = "ArnLike"
           variable = "aws:SourceArn"
-          values   = [try(condition.value.hosted_zone_arn, "arn:${data.aws_partition.current.partition}:route53:::hostedzone/*")]
+          values   = [try(condition.value.hosted_zone_arn, "arn:${local.partition}:route53:::hostedzone/*")]
         }
       }
     }
diff --git a/outputs.tf b/outputs.tf
index 5f48c55..d002a3c 100644
--- a/outputs.tf
+++ b/outputs.tf
@@ -48,4 +48,5 @@ output "aliases" {
 output "grants" {
   description = "A map of grants created and their attributes"
   value       = aws_kms_grant.this
+  sensitive   = true
 }
diff --git a/variables.tf b/variables.tf
index 0eb9b90..b66b126 100644
--- a/variables.tf
+++ b/variables.tf
@@ -182,6 +182,12 @@ variable "route53_dnssec_sources" {
   default     = []
 }
 
+variable "rotation_period_in_days" {
+  description = "Custom period of time between each rotation date. Must be a number between 90 and 2560 (inclusive)"
+  type        = number
+  default     = null
+}
+
 ################################################################################
 # Replica Key
 ################################################################################
diff --git a/versions.tf b/versions.tf
index 14a7ee2..f96e1b1 100644
--- a/versions.tf
+++ b/versions.tf
@@ -1,10 +1,10 @@
 terraform {
-  required_version = ">= 1.0"
+  required_version = ">= 1.3"
 
   required_providers {
     aws = {
       source  = "hashicorp/aws"
-      version = ">= 4.33"
+      version = ">= 5.49"
     }
   }
 }
diff --git a/wrappers/README.md b/wrappers/README.md
new file mode 100644
index 0000000..891b5dd
--- /dev/null
+++ b/wrappers/README.md
@@ -0,0 +1,100 @@
+# Wrapper for the root module
+
+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).
+
+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.
+
+This wrapper does not implement any extra functionality.
+
+## Usage with Terragrunt
+
+`terragrunt.hcl`:
+
+```hcl
+terraform {
+  source = "tfr:///terraform-aws-modules/kms/aws//wrappers"
+  # Alternative source:
+  # source = "git::git@github.com:terraform-aws-modules/terraform-aws-kms.git//wrappers?ref=master"
+}
+
+inputs = {
+  defaults = { # Default values
+    create = true
+    tags = {
+      Terraform   = "true"
+      Environment = "dev"
+    }
+  }
+
+  items = {
+    my-item = {
+      # omitted... can be any argument supported by the module
+    }
+    my-second-item = {
+      # omitted... can be any argument supported by the module
+    }
+    # omitted...
+  }
+}
+```
+
+## Usage with Terraform
+
+```hcl
+module "wrapper" {
+  source = "terraform-aws-modules/kms/aws//wrappers"
+
+  defaults = { # Default values
+    create = true
+    tags = {
+      Terraform   = "true"
+      Environment = "dev"
+    }
+  }
+
+  items = {
+    my-item = {
+      # omitted... can be any argument supported by the module
+    }
+    my-second-item = {
+      # omitted... can be any argument supported by the module
+    }
+    # omitted...
+  }
+}
+```
+
+## Example: Manage multiple S3 buckets in one Terragrunt layer
+
+`eu-west-1/s3-buckets/terragrunt.hcl`:
+
+```hcl
+terraform {
+  source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers"
+  # Alternative source:
+  # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master"
+}
+
+inputs = {
+  defaults = {
+    force_destroy = true
+
+    attach_elb_log_delivery_policy        = true
+    attach_lb_log_delivery_policy         = true
+    attach_deny_insecure_transport_policy = true
+    attach_require_latest_tls_policy      = true
+  }
+
+  items = {
+    bucket1 = {
+      bucket = "my-random-bucket-1"
+    }
+    bucket2 = {
+      bucket = "my-random-bucket-2"
+      tags = {
+        Secure = "probably"
+      }
+    }
+  }
+}
+```
diff --git a/wrappers/main.tf b/wrappers/main.tf
new file mode 100644
index 0000000..6e2ebca
--- /dev/null
+++ b/wrappers/main.tf
@@ -0,0 +1,45 @@
+module "wrapper" {
+  source = "../"
+
+  for_each = var.items
+
+  aliases                                = try(each.value.aliases, var.defaults.aliases, [])
+  aliases_use_name_prefix                = try(each.value.aliases_use_name_prefix, var.defaults.aliases_use_name_prefix, false)
+  bypass_policy_lockout_safety_check     = try(each.value.bypass_policy_lockout_safety_check, var.defaults.bypass_policy_lockout_safety_check, null)
+  computed_aliases                       = try(each.value.computed_aliases, var.defaults.computed_aliases, {})
+  create                                 = try(each.value.create, var.defaults.create, true)
+  create_external                        = try(each.value.create_external, var.defaults.create_external, false)
+  create_replica                         = try(each.value.create_replica, var.defaults.create_replica, false)
+  create_replica_external                = try(each.value.create_replica_external, var.defaults.create_replica_external, false)
+  custom_key_store_id                    = try(each.value.custom_key_store_id, var.defaults.custom_key_store_id, null)
+  customer_master_key_spec               = try(each.value.customer_master_key_spec, var.defaults.customer_master_key_spec, null)
+  deletion_window_in_days                = try(each.value.deletion_window_in_days, var.defaults.deletion_window_in_days, null)
+  description                            = try(each.value.description, var.defaults.description, null)
+  enable_default_policy                  = try(each.value.enable_default_policy, var.defaults.enable_default_policy, true)
+  enable_key_rotation                    = try(each.value.enable_key_rotation, var.defaults.enable_key_rotation, true)
+  enable_route53_dnssec                  = try(each.value.enable_route53_dnssec, var.defaults.enable_route53_dnssec, false)
+  grants                                 = try(each.value.grants, var.defaults.grants, {})
+  is_enabled                             = try(each.value.is_enabled, var.defaults.is_enabled, null)
+  key_administrators                     = try(each.value.key_administrators, var.defaults.key_administrators, [])
+  key_asymmetric_public_encryption_users = try(each.value.key_asymmetric_public_encryption_users, var.defaults.key_asymmetric_public_encryption_users, [])
+  key_asymmetric_sign_verify_users       = try(each.value.key_asymmetric_sign_verify_users, var.defaults.key_asymmetric_sign_verify_users, [])
+  key_hmac_users                         = try(each.value.key_hmac_users, var.defaults.key_hmac_users, [])
+  key_material_base64                    = try(each.value.key_material_base64, var.defaults.key_material_base64, null)
+  key_owners                             = try(each.value.key_owners, var.defaults.key_owners, [])
+  key_service_roles_for_autoscaling      = try(each.value.key_service_roles_for_autoscaling, var.defaults.key_service_roles_for_autoscaling, [])
+  key_service_users                      = try(each.value.key_service_users, var.defaults.key_service_users, [])
+  key_statements                         = try(each.value.key_statements, var.defaults.key_statements, {})
+  key_symmetric_encryption_users         = try(each.value.key_symmetric_encryption_users, var.defaults.key_symmetric_encryption_users, [])
+  key_usage                              = try(each.value.key_usage, var.defaults.key_usage, null)
+  key_users                              = try(each.value.key_users, var.defaults.key_users, [])
+  multi_region                           = try(each.value.multi_region, var.defaults.multi_region, false)
+  override_policy_documents              = try(each.value.override_policy_documents, var.defaults.override_policy_documents, [])
+  policy                                 = try(each.value.policy, var.defaults.policy, null)
+  primary_external_key_arn               = try(each.value.primary_external_key_arn, var.defaults.primary_external_key_arn, null)
+  primary_key_arn                        = try(each.value.primary_key_arn, var.defaults.primary_key_arn, null)
+  rotation_period_in_days                = try(each.value.rotation_period_in_days, var.defaults.rotation_period_in_days, null)
+  route53_dnssec_sources                 = try(each.value.route53_dnssec_sources, var.defaults.route53_dnssec_sources, [])
+  source_policy_documents                = try(each.value.source_policy_documents, var.defaults.source_policy_documents, [])
+  tags                                   = try(each.value.tags, var.defaults.tags, {})
+  valid_to                               = try(each.value.valid_to, var.defaults.valid_to, null)
+}
diff --git a/wrappers/outputs.tf b/wrappers/outputs.tf
new file mode 100644
index 0000000..39779a5
--- /dev/null
+++ b/wrappers/outputs.tf
@@ -0,0 +1,5 @@
+output "wrapper" {
+  description = "Map of outputs of a wrapper."
+  value       = module.wrapper
+  sensitive   = true # At least one sensitive module output (grants) found (requires Terraform 0.14+)
+}
diff --git a/wrappers/variables.tf b/wrappers/variables.tf
new file mode 100644
index 0000000..a6ea096
--- /dev/null
+++ b/wrappers/variables.tf
@@ -0,0 +1,11 @@
+variable "defaults" {
+  description = "Map of default values which will be used for each item."
+  type        = any
+  default     = {}
+}
+
+variable "items" {
+  description = "Maps of items to create a wrapper from. Values are passed through to the module."
+  type        = any
+  default     = {}
+}
diff --git a/wrappers/versions.tf b/wrappers/versions.tf
new file mode 100644
index 0000000..f96e1b1
--- /dev/null
+++ b/wrappers/versions.tf
@@ -0,0 +1,10 @@
+terraform {
+  required_version = ">= 1.3"
+
+  required_providers {
+    aws = {
+      source  = "hashicorp/aws"
+      version = ">= 5.49"
+    }
+  }
+}