Skip to content

Commit 8c83dc4

Browse files
committed
fix: Updates for ephemeral/write-only arguments
1 parent ecc276f commit 8c83dc4

File tree

10 files changed

+151
-139
lines changed

10 files changed

+151
-139
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/antonbabenko/pre-commit-terraform
3-
rev: v1.96.1
3+
rev: v1.99.5
44
hooks:
55
- id: terraform_fmt
66
- id: terraform_wrapper_module_for_each

README.md

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,14 @@ Examples codified under the [`examples`](https://github.com/terraform-aws-module
123123

124124
| Name | Version |
125125
|------|---------|
126-
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
126+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.11 |
127127
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 6.0 |
128-
| <a name="requirement_random"></a> [random](#requirement\_random) | >= 3.0 |
129128

130129
## Providers
131130

132131
| Name | Version |
133132
|------|---------|
134133
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 6.0 |
135-
| <a name="provider_random"></a> [random](#provider\_random) | >= 3.0 |
136134

137135
## Modules
138136

@@ -147,7 +145,6 @@ No modules.
147145
| [aws_secretsmanager_secret_rotation.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_rotation) | resource |
148146
| [aws_secretsmanager_secret_version.ignore_changes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource |
149147
| [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 |
151148
| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
152149

153150
## Inputs
@@ -157,7 +154,6 @@ No modules.
157154
| <a name="input_block_public_policy"></a> [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 |
158155
| <a name="input_create"></a> [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no |
159156
| <a name="input_create_policy"></a> [create\_policy](#input\_create\_policy) | Determines whether a policy will be created | `bool` | `false` | no |
160-
| <a name="input_create_random_password"></a> [create\_random\_password](#input\_create\_random\_password) | Determines whether a random password will be generated | `bool` | `false` | no |
161157
| <a name="input_description"></a> [description](#input\_description) | A description of the secret | `string` | `null` | no |
162158
| <a name="input_enable_rotation"></a> [enable\_rotation](#input\_enable\_rotation) | Determines whether secret rotation is enabled | `bool` | `false` | no |
163159
| <a name="input_force_overwrite_replica_secret"></a> [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 |
@@ -166,16 +162,17 @@ No modules.
166162
| <a name="input_name"></a> [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 |
167163
| <a name="input_name_prefix"></a> [name\_prefix](#input\_name\_prefix) | Creates a unique name beginning with the specified prefix | `string` | `null` | no |
168164
| <a name="input_override_policy_documents"></a> [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-
| <a name="input_policy_statements"></a> [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-
| <a name="input_random_password_length"></a> [random\_password\_length](#input\_random\_password\_length) | The length of the generated random password | `number` | `32` | no |
171-
| <a name="input_random_password_override_special"></a> [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 |
165+
| <a name="input_policy_statements"></a> [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 | <pre>list(object({<br/> sid = optional(string)<br/> actions = optional(list(string))<br/> not_actions = optional(list(string))<br/> effect = optional(string)<br/> resources = optional(list(string))<br/> not_resources = optional(list(string))<br/> principals = optional(list(object({<br/> type = string<br/> identifiers = list(string)<br/> })))<br/> not_principals = optional(list(object({<br/> type = string<br/> identifiers = list(string)<br/> })))<br/> condition = optional(list(object({<br/> test = string<br/> values = list(string)<br/> variable = string<br/> })))<br/> }))</pre> | `null` | no |
172166
| <a name="input_recovery_window_in_days"></a> [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 |
173167
| <a name="input_region"></a> [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no |
174-
| <a name="input_replica"></a> [replica](#input\_replica) | Configuration block to support secret replication | `map(any)` | `{}` | no |
168+
| <a name="input_replica"></a> [replica](#input\_replica) | Configuration block to support secret replication | <pre>map(object({<br/> kms_key_id = optional(string)<br/> region = optional(string) # will default to the key name<br/> }))</pre> | `null` | no |
169+
| <a name="input_rotate_immediately"></a> [rotate\_immediately](#input\_rotate\_immediately) | Specifies whether to rotate the secret immediately or wait until the next scheduled rotation window. The rotation schedule is defined in `rotation_rules` | `bool` | `null` | no |
175170
| <a name="input_rotation_lambda_arn"></a> [rotation\_lambda\_arn](#input\_rotation\_lambda\_arn) | Specifies the ARN of the Lambda function that can rotate the secret | `string` | `""` | no |
176-
| <a name="input_rotation_rules"></a> [rotation\_rules](#input\_rotation\_rules) | A structure that defines the rotation configuration for this secret | `map(any)` | `{}` | no |
177-
| <a name="input_secret_binary"></a> [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 |
178-
| <a name="input_secret_string"></a> [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 |
171+
| <a name="input_rotation_rules"></a> [rotation\_rules](#input\_rotation\_rules) | A structure that defines the rotation configuration for this secret | <pre>object({<br/> automatically_after_days = optional(number)<br/> duration = optional(string)<br/> schedule_expression = optional(string)<br/> })</pre> | `null` | no |
172+
| <a name="input_secret_binary"></a> [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` or `secret_string_wo` is not set. Needs to be encoded to base64 | `string` | `null` | no |
173+
| <a name="input_secret_string"></a> [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` or `secret_string_wo` is not set | `string` | `null` | no |
174+
| <a name="input_secret_string_wo"></a> [secret\_string\_wo](#input\_secret\_string\_wo) | Specifies text data that you want to encrypt and store in this version of the secret. This is required if `secret_binary` or `secret_string` is not set | `string` | `null` | no |
175+
| <a name="input_secret_string_wo_version"></a> [secret\_string\_wo\_version](#input\_secret\_string\_wo\_version) | Used together with `secret_string_wo` to trigger an update. Increment this value when an update to `secret_string_wo` is required | `string` | `null` | no |
179176
| <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 |
180177
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
181178
| <a name="input_version_stages"></a> [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 |

examples/complete/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ Note that this example may create resources which will incur monetary charges on
2828

2929
| Name | Version |
3030
|------|---------|
31-
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
31+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.11 |
3232
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 6.0 |
33+
| <a name="requirement_random"></a> [random](#requirement\_random) | >= 3.7 |
3334

3435
## Providers
3536

@@ -41,9 +42,8 @@ Note that this example may create resources which will incur monetary charges on
4142

4243
| Name | Source | Version |
4344
|------|--------|---------|
44-
| <a name="module_lambda"></a> [lambda](#module\_lambda) | terraform-aws-modules/lambda/aws | ~> 6.0 |
45+
| <a name="module_lambda"></a> [lambda](#module\_lambda) | terraform-aws-modules/lambda/aws | ~> 8.0 |
4546
| <a name="module_secrets_manager"></a> [secrets\_manager](#module\_secrets\_manager) | ../.. | n/a |
46-
| <a name="module_secrets_manager_another_region"></a> [secrets\_manager\_another\_region](#module\_secrets\_manager\_another\_region) | ../.. | n/a |
4747
| <a name="module_secrets_manager_disabled"></a> [secrets\_manager\_disabled](#module\_secrets\_manager\_disabled) | ../.. | n/a |
4848
| <a name="module_secrets_manager_rotate"></a> [secrets\_manager\_rotate](#module\_secrets\_manager\_rotate) | ../.. | n/a |
4949

examples/complete/main.tf

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ module "secrets_manager" {
3838
# Policy
3939
create_policy = true
4040
block_public_policy = true
41-
policy_statements = {
42-
read = {
41+
policy_statements = [
42+
{
4343
sid = "AllowAccountRead"
4444
principals = [{
4545
type = "AWS"
@@ -48,12 +48,11 @@ module "secrets_manager" {
4848
actions = ["secretsmanager:GetSecretValue"]
4949
resources = ["*"]
5050
}
51-
}
51+
]
5252

5353
# Version
54-
create_random_password = true
55-
random_password_length = 64
56-
random_password_override_special = "!@#$%^&*()_+"
54+
secret_string_wo = ephemeral.random_password.password.result
55+
secret_string_wo_version = 1
5756

5857
tags = local.tags
5958
}
@@ -69,8 +68,8 @@ module "secrets_manager_rotate" {
6968
# Policy
7069
create_policy = true
7170
block_public_policy = true
72-
policy_statements = {
73-
lambda = {
71+
policy_statements = [
72+
{
7473
sid = "LambdaReadWrite"
7574
principals = [{
7675
type = "AWS"
@@ -83,8 +82,8 @@ module "secrets_manager_rotate" {
8382
"secretsmanager:UpdateSecretVersionStage",
8483
]
8584
resources = ["*"]
86-
}
87-
account = {
85+
},
86+
{
8887
sid = "AccountDescribe"
8988
principals = [{
9089
type = "AWS"
@@ -93,15 +92,15 @@ module "secrets_manager_rotate" {
9392
actions = ["secretsmanager:DescribeSecret"]
9493
resources = ["*"]
9594
}
96-
}
95+
]
9796

9897
# Version
9998
ignore_secret_changes = true
10099
secret_string = jsonencode({
101100
engine = "mariadb",
102101
host = "mydb.cluster-123456789012.us-east-1.rds.amazonaws.com",
103102
username = "Bill",
104-
password = "ThisIsMySuperSecretString12356!"
103+
password = "ThisIsMySuperSecretString12356!",
105104
dbname = "mydb",
106105
port = 3306
107106
})
@@ -123,21 +122,16 @@ module "secrets_manager_disabled" {
123122
create = false
124123
}
125124

126-
module "secrets_manager_another_region" {
127-
source = "../.."
128-
129-
region = "us-east-1"
130-
name_prefix = local.name
131-
132-
create_random_password = true
133-
134-
tags = local.tags
135-
}
136-
137125
################################################################################
138126
# Supporting Resources
139127
################################################################################
140128

129+
ephemeral "random_password" "password" {
130+
length = 16
131+
special = true
132+
override_special = "!#$%&*()-_=+[]{}<>:?"
133+
}
134+
141135
# https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-required-permissions-function.html
142136
data "aws_iam_policy_document" "this" {
143137
statement {
@@ -163,13 +157,13 @@ data "aws_iam_policy_document" "this" {
163157

164158
module "lambda" {
165159
source = "terraform-aws-modules/lambda/aws"
166-
version = "~> 6.0"
160+
version = "~> 8.0"
167161

168162
function_name = local.name
169163
description = "Example Secrets Manager secret rotation lambda function"
170164

171165
handler = "function.lambda_handler"
172-
runtime = "python3.10"
166+
runtime = "python3.12"
173167
timeout = 60
174168
memory_size = 512
175169
source_path = "${path.module}/function.py"

examples/complete/versions.tf

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
terraform {
2-
required_version = ">= 1.0"
2+
required_version = ">= 1.11"
33

44
required_providers {
55
aws = {
66
source = "hashicorp/aws"
77
version = ">= 6.0"
88
}
9+
random = {
10+
source = "hashicorp/random"
11+
version = ">= 3.7"
12+
}
913
}
1014
}

main.tf

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ resource "aws_secretsmanager_secret" "this" {
1616
recovery_window_in_days = var.recovery_window_in_days
1717

1818
dynamic "replica" {
19-
for_each = var.replica
19+
for_each = var.replica != null ? var.replica : {}
2020

2121
content {
22-
kms_key_id = try(replica.value.kms_key_id, null)
23-
region = try(replica.value.region, replica.key)
22+
kms_key_id = replica.value.kms_key_id
23+
region = coalesce(replica.value.region, replica.key)
2424
}
2525
}
2626

@@ -38,18 +38,18 @@ data "aws_iam_policy_document" "this" {
3838
override_policy_documents = var.override_policy_documents
3939

4040
dynamic "statement" {
41-
for_each = var.policy_statements
41+
for_each = var.policy_statements != null ? var.policy_statements : []
4242

4343
content {
44-
sid = try(statement.value.sid, null)
45-
actions = try(statement.value.actions, null)
46-
not_actions = try(statement.value.not_actions, null)
47-
effect = try(statement.value.effect, null)
48-
resources = try(statement.value.resources, null)
49-
not_resources = try(statement.value.not_resources, null)
44+
sid = statement.value.sid
45+
actions = statement.value.actions
46+
not_actions = statement.value.not_actions
47+
effect = statement.value.effect
48+
resources = statement.value.resources
49+
not_resources = statement.value.not_resources
5050

5151
dynamic "principals" {
52-
for_each = try(statement.value.principals, [])
52+
for_each = statement.value.principals != null ? statement.value.principals : []
5353

5454
content {
5555
type = principals.value.type
@@ -58,7 +58,7 @@ data "aws_iam_policy_document" "this" {
5858
}
5959

6060
dynamic "not_principals" {
61-
for_each = try(statement.value.not_principals, [])
61+
for_each = statement.value.not_principals != null ? statement.value.not_principals : []
6262

6363
content {
6464
type = not_principals.value.type
@@ -67,7 +67,7 @@ data "aws_iam_policy_document" "this" {
6767
}
6868

6969
dynamic "condition" {
70-
for_each = try(statement.value.conditions, [])
70+
for_each = statement.value.condition != null ? statement.value.condition : []
7171

7272
content {
7373
test = condition.value.test
@@ -84,9 +84,9 @@ resource "aws_secretsmanager_secret_policy" "this" {
8484

8585
region = var.region
8686

87-
secret_arn = aws_secretsmanager_secret.this[0].arn
88-
policy = data.aws_iam_policy_document.this[0].json
8987
block_public_policy = var.block_public_policy
88+
policy = data.aws_iam_policy_document.this[0].json
89+
secret_arn = aws_secretsmanager_secret.this[0].arn
9090
}
9191

9292
################################################################################
@@ -98,21 +98,25 @@ resource "aws_secretsmanager_secret_version" "this" {
9898

9999
region = var.region
100100

101-
secret_id = aws_secretsmanager_secret.this[0].id
102-
secret_string = var.create_random_password ? random_password.this[0].result : var.secret_string
103-
secret_binary = var.secret_binary
104-
version_stages = var.version_stages
101+
secret_id = aws_secretsmanager_secret.this[0].id
102+
secret_binary = var.secret_binary
103+
secret_string = var.secret_string
104+
secret_string_wo = var.secret_string_wo
105+
secret_string_wo_version = var.secret_string_wo_version
106+
version_stages = var.version_stages
105107
}
106108

107109
resource "aws_secretsmanager_secret_version" "ignore_changes" {
108110
count = var.create && (var.enable_rotation || var.ignore_secret_changes) ? 1 : 0
109111

110112
region = var.region
111113

112-
secret_id = aws_secretsmanager_secret.this[0].id
113-
secret_string = var.create_random_password ? random_password.this[0].result : var.secret_string
114-
secret_binary = var.secret_binary
115-
version_stages = var.version_stages
114+
secret_id = aws_secretsmanager_secret.this[0].id
115+
secret_binary = var.secret_binary
116+
secret_string = var.secret_string
117+
secret_string_wo = var.secret_string_wo
118+
secret_string_wo_version = var.secret_string_wo_version
119+
version_stages = var.version_stages
116120

117121
lifecycle {
118122
ignore_changes = [
@@ -123,14 +127,6 @@ resource "aws_secretsmanager_secret_version" "ignore_changes" {
123127
}
124128
}
125129

126-
resource "random_password" "this" {
127-
count = var.create && var.create_random_password ? 1 : 0
128-
129-
length = var.random_password_length
130-
special = true
131-
override_special = var.random_password_override_special
132-
}
133-
134130
################################################################################
135131
# Rotation
136132
################################################################################
@@ -140,15 +136,16 @@ resource "aws_secretsmanager_secret_rotation" "this" {
140136

141137
region = var.region
142138

139+
rotate_immediately = var.rotate_immediately
143140
rotation_lambda_arn = var.rotation_lambda_arn
144141

145142
dynamic "rotation_rules" {
146-
for_each = [var.rotation_rules]
143+
for_each = var.rotation_rules != null ? [var.rotation_rules] : []
147144

148145
content {
149-
automatically_after_days = try(rotation_rules.value.automatically_after_days, null)
150-
duration = try(rotation_rules.value.duration, null)
151-
schedule_expression = try(rotation_rules.value.schedule_expression, null)
146+
automatically_after_days = rotation_rules.value.automatically_after_days
147+
duration = rotation_rules.value.duration
148+
schedule_expression = rotation_rules.value.schedule_expression
152149
}
153150
}
154151

0 commit comments

Comments
 (0)