Skip to content

Commit

Permalink
Refine rotation configuration in secrets-manager-secret
Browse files Browse the repository at this point in the history
  • Loading branch information
posquit0 committed May 22, 2024
1 parent b5a06a4 commit 5ca5ef7
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 34 deletions.
15 changes: 7 additions & 8 deletions modules/secrets-manager-secret/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ This module creates following resources.

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.5 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 4.22 |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.6 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.43 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.19.0 |
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.50.0 |

## Modules

Expand All @@ -42,19 +42,18 @@ This module creates following resources.
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_name"></a> [name](#input\_name) | (Required) Friendly name of the new secret. The secret name can consist of uppercase letters, lowercase letters, digits, and any of the following characters: `/_+=.@-`. | `string` | n/a | yes |
| <a name="input_block_public_policy"></a> [block\_public\_policy](#input\_block\_public\_policy) | (Optional) Whether to reject calls to PUT a resource policy if the policy allows public access. | `bool` | `false` | no |
| <a name="input_block_public_policy"></a> [block\_public\_policy](#input\_block\_public\_policy) | (Optional) Whether to reject calls to PUT a resource policy if the policy allows public access. Defaults to `true`. | `bool` | `true` | no |
| <a name="input_deletion_window_in_days"></a> [deletion\_window\_in\_days](#input\_deletion\_window\_in\_days) | (Optional) Duration in days after which the secret is deleted after destruction of the resource. Valid value is between `7` and `30` days. Defaults to `30`. | `number` | `30` | no |
| <a name="input_description"></a> [description](#input\_description) | (Optional) The description of the secret. | `string` | `"Managed by Terraform."` | no |
| <a name="input_kms_key"></a> [kms\_key](#input\_kms\_key) | (Optional) The ARN or Id of the AWS KMS key to be used to encrypt the secret values in this secret. If you don't specify this value, then Secrets Manager defaults to using the AWS account's default KMS key named `aws/secretsmanager`. If the default KMS key with that name doesn't yet exist, then AWS Secrets Manager creates it for you automatically the first time. | `string` | `null` | no |
| <a name="input_module_tags_enabled"></a> [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no |
| <a name="input_overwrite_in_replicas"></a> [overwrite\_in\_replicas](#input\_overwrite\_in\_replicas) | (Optional) Whether to overwrite a secret with the same name in the destination region during replication. | `bool` | `false` | no |
| <a name="input_overwrite_in_replicas"></a> [overwrite\_in\_replicas](#input\_overwrite\_in\_replicas) | (Optional) Whether to overwrite a secret with the same name in the destination region during replication. Defaults to `false`. | `bool` | `false` | no |
| <a name="input_policy"></a> [policy](#input\_policy) | (Optional) A valid JSON document representing a resource policy. | `string` | `null` | no |
| <a name="input_replicas"></a> [replicas](#input\_replicas) | (Optional) A list of replica configurations of the Secrets Manager secret. Each value of `replicas` block as defined below.<br> (Required) `region` - The region for replicating the secret.<br> (Optional) `kms_key` - The ARN, Key ID, or Alias of the AWS KMS key within the region secret is replicated to. If one is not specified, then Secrets Manager defaults to using the AWS account's default KMS key named `aws/secretsmanager` in the region. If the default KMS key with that name doesn't yet exist, then AWS Secrets Manager creates it for you automatically the first time. | `list(map(string))` | `[]` | no |
| <a name="input_replicas"></a> [replicas](#input\_replicas) | (Optional) A list of replica configurations of the Secrets Manager secret. Each value of `replicas` block as defined below.<br> (Required) `region` - The region for replicating the secret.<br> (Optional) `kms_key` - The ARN, Key ID, or Alias of the AWS KMS key within the region secret is replicated to. If one is not specified, then Secrets Manager defaults to using the AWS account's default KMS key named `aws/secretsmanager` in the region. If the default KMS key with that name doesn't yet exist, then AWS Secrets Manager creates it for you automatically the first time. | <pre>list(object({<br> region = string<br> kms_key = optional(string)<br> }))</pre> | `[]` | no |
| <a name="input_resource_group_description"></a> [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no |
| <a name="input_resource_group_enabled"></a> [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no |
| <a name="input_resource_group_name"></a> [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no |
| <a name="input_rotation_duration_in_days"></a> [rotation\_duration\_in\_days](#input\_rotation\_duration\_in\_days) | (Optional) The number of days between automatic scheduled rotations of the secret. Required if `rotation_lambda_function` is configured. | `number` | `null` | no |
| <a name="input_rotation_lambda_function"></a> [rotation\_lambda\_function](#input\_rotation\_lambda\_function) | (Optional) The ARN of the Lambda function that can rotate the secret. | `string` | `null` | no |
| <a name="input_rotation"></a> [rotation](#input\_rotation) | (Optional) A rotation configurations of the Secrets Manager secret. `rotation` block as defined below.<br> (Optional) `enabled` - Whether to enable automatic rotation of the secret. Defaults to `false`.<br> (Optionial) `rotate_immediately` - Whether to rotate the secret immediately or wait until the next scheduled rotation window. The rotation schedule is defined in rotation\_rules. For secrets that use a Lambda rotation function to rotate, if you don't immediately rotate the secret, Secrets Manager tests the rotation configuration by running the testSecret step. Defaults to `true`.<br> (Optional) `lambda_function` - The ARN of the Lambda function that can rotate the secret.<br> (Optional) `schedule_frequency` - The number of days between automatic scheduled rotations of the secret. Either `schedule_frequency` or `schedule_expression` must be specified.<br> (Optional) `schedule_expression` - A cron expression such as `cron(a b c d e f)` or a rate expression such as `rate(10 days)`. Either `schedule_frequency` or `schedule_expression` must be specified.<br> (Optional) `duration` - The length of the rotation window in hours. | <pre>object({<br> enabled = optional(bool, false)<br> rotate_immediately = optional(bool, true)<br> lambda_function = optional(string)<br> schedule_frequency = optional(number)<br> schedule_expression = optional(string)<br> duration = optional(number)<br> })</pre> | `{}` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no |
| <a name="input_type"></a> [type](#input\_type) | (Optional) The intended type of the secret. Valid values are `TEXT`, `KEY_VALUE` or `BINARY`. | `string` | `"KEY_VALUE"` | no |
| <a name="input_value"></a> [value](#input\_value) | (Optional) The secret value that you want to encrypt and store in the current version of the secret. Specify plaintext data with `string` type if `type` is `TEXT`. Specify key-value data with `map` type if `type` is `KEY_VALUE`. Specify binary data with `string` type if `type` is `BINARY`. The `aws_secretsmanager_secret_version` resource is deleted from Terraform if you set the value to `null`. However, `AWSCURRENT` staging label is still active on the version event after the resource is deleted from Terraform. | `any` | `null` | no |
Expand Down
19 changes: 13 additions & 6 deletions modules/secrets-manager-secret/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ locals {
# Secrets Manager Secret
###################################################

# INFO: Use a separate resource
# - 'policy'
# INFO: Not supported attributes
# - `name_prefix`
resource "aws_secretsmanager_secret" "this" {
name = var.name
description = var.description

# Use `aws_secretsmanager_policy` instead
policy = null
kms_key_id = var.kms_key
recovery_window_in_days = var.deletion_window_in_days
force_overwrite_replica_secret = var.overwrite_in_replicas
Expand Down Expand Up @@ -127,12 +129,17 @@ resource "aws_secretsmanager_secret_policy" "this" {
###################################################

resource "aws_secretsmanager_secret_rotation" "this" {
count = var.rotation_lambda_function != null ? 1 : 0
count = var.rotation.enabled ? 1 : 0

secret_id = aws_secretsmanager_secret.this.id
rotation_lambda_arn = var.rotation_lambda_function
secret_id = aws_secretsmanager_secret.this.id

rotate_immediately = var.rotation.rotate_immediately
rotation_lambda_arn = var.rotation.lambda_function

rotation_rules {
automatically_after_days = var.rotation_duration_in_days
automatically_after_days = var.rotation.schedule_frequency
schedule_expression = var.rotation.schedule_expression

duration = var.rotation.duration != null ? "${var.rotation.duration}h" : null
}
}
17 changes: 14 additions & 3 deletions modules/secrets-manager-secret/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,12 @@ output "deletion_window_in_days" {
output "rotation" {
description = "The configuration of the automatic rotation for the secret."
value = {
enabled = var.rotation_lambda_function != null
lambda_function = one(aws_secretsmanager_secret_rotation.this[*].rotation_lambda_arn)
duration_in_days = var.rotation_duration_in_days
enabled = var.rotation.enabled
lambda_function = one(aws_secretsmanager_secret_rotation.this[*].rotation_lambda_arn)

schedule_frequency = var.rotation.schedule_frequency
schedule_expression = one(aws_secretsmanager_secret_rotation.this[*].rotation_rules[0].schedule_expression)
duration_in_days = var.rotation.duration
}
}

Expand All @@ -86,3 +89,11 @@ output "overwrite_in_replicas" {
description = "Whether to overwrite a secret with the same name in the destination region during replication."
value = aws_secretsmanager_secret.this.force_overwrite_replica_secret
}

# output "debug" {
# value = {
# for k, v in aws_secretsmanager_secret.this :
# k => v
# if !contains(["tags", "tags_all", "policy", "id", "arn", "name", "description", "kms_key_id", "recovery_window_in_days", "force_overwrite_replica_secret", "name_prefix", "replica"], k)
# }
# }
55 changes: 40 additions & 15 deletions modules/secrets-manager-secret/variables.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
variable "name" {
description = "(Required) Friendly name of the new secret. The secret name can consist of uppercase letters, lowercase letters, digits, and any of the following characters: `/_+=.@-`."
type = string
nullable = false
}

variable "description" {
Expand All @@ -26,6 +27,7 @@ variable "value" {
description = "(Optional) The secret value that you want to encrypt and store in the current version of the secret. Specify plaintext data with `string` type if `type` is `TEXT`. Specify key-value data with `map` type if `type` is `KEY_VALUE`. Specify binary data with `string` type if `type` is `BINARY`. The `aws_secretsmanager_secret_version` resource is deleted from Terraform if you set the value to `null`. However, `AWSCURRENT` staging label is still active on the version event after the resource is deleted from Terraform."
type = any
default = null
nullable = true
}

variable "versions" {
Expand Down Expand Up @@ -64,18 +66,20 @@ variable "kms_key" {
description = "(Optional) The ARN or Id of the AWS KMS key to be used to encrypt the secret values in this secret. If you don't specify this value, then Secrets Manager defaults to using the AWS account's default KMS key named `aws/secretsmanager`. If the default KMS key with that name doesn't yet exist, then AWS Secrets Manager creates it for you automatically the first time."
type = string
default = null
nullable = true
}

variable "policy" {
description = "(Optional) A valid JSON document representing a resource policy."
type = string
default = null
nullable = true
}

variable "block_public_policy" {
description = "(Optional) Whether to reject calls to PUT a resource policy if the policy allows public access."
description = "(Optional) Whether to reject calls to PUT a resource policy if the policy allows public access. Defaults to `true`."
type = bool
default = false
default = true
nullable = false
}

Expand All @@ -100,28 +104,49 @@ variable "replicas" {
(Required) `region` - The region for replicating the secret.
(Optional) `kms_key` - The ARN, Key ID, or Alias of the AWS KMS key within the region secret is replicated to. If one is not specified, then Secrets Manager defaults to using the AWS account's default KMS key named `aws/secretsmanager` in the region. If the default KMS key with that name doesn't yet exist, then AWS Secrets Manager creates it for you automatically the first time.
EOF
type = list(map(string))
default = []
nullable = false
type = list(object({
region = string
kms_key = optional(string)
}))
default = []
nullable = false
}

variable "overwrite_in_replicas" {
description = "(Optional) Whether to overwrite a secret with the same name in the destination region during replication."
description = "(Optional) Whether to overwrite a secret with the same name in the destination region during replication. Defaults to `false`."
type = bool
default = false
nullable = false
}

variable "rotation_lambda_function" {
description = "(Optional) The ARN of the Lambda function that can rotate the secret."
type = string
default = null
}
variable "rotation" {
description = <<EOF
(Optional) A rotation configurations of the Secrets Manager secret. `rotation` block as defined below.
(Optional) `enabled` - Whether to enable automatic rotation of the secret. Defaults to `false`.
(Optionial) `rotate_immediately` - Whether to rotate the secret immediately or wait until the next scheduled rotation window. The rotation schedule is defined in rotation_rules. For secrets that use a Lambda rotation function to rotate, if you don't immediately rotate the secret, Secrets Manager tests the rotation configuration by running the testSecret step. Defaults to `true`.
(Optional) `lambda_function` - The ARN of the Lambda function that can rotate the secret.
(Optional) `schedule_frequency` - The number of days between automatic scheduled rotations of the secret. Either `schedule_frequency` or `schedule_expression` must be specified.
(Optional) `schedule_expression` - A cron expression such as `cron(a b c d e f)` or a rate expression such as `rate(10 days)`. Either `schedule_frequency` or `schedule_expression` must be specified.
(Optional) `duration` - The length of the rotation window in hours.
EOF
type = object({
enabled = optional(bool, false)
rotate_immediately = optional(bool, true)
lambda_function = optional(string)
schedule_frequency = optional(number)
schedule_expression = optional(string)
duration = optional(number)
})
default = {}
nullable = false

variable "rotation_duration_in_days" {
description = "(Optional) The number of days between automatic scheduled rotations of the secret. Required if `rotation_lambda_function` is configured."
type = number
default = null
validation {
condition = anytrue([
var.rotation.duration == null,
var.rotation.duration != null && coalesce(var.rotation.duration, 0) > 0,
])
error_message = "Valid value for `duration` is greater than `0`."
}
}

variable "tags" {
Expand Down
4 changes: 2 additions & 2 deletions modules/secrets-manager-secret/versions.tf
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
terraform {
required_version = ">= 1.5"
required_version = ">= 1.6"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.22"
version = ">= 5.43"
}
}
}

0 comments on commit 5ca5ef7

Please sign in to comment.