Skip to content

Commit 3e80d9b

Browse files
authored
feat: Add support for random password generation and Terragrunt wrapper (#1)
1 parent ec75f6f commit 3e80d9b

File tree

11 files changed

+194
-4
lines changed

11 files changed

+194
-4
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ repos:
33
rev: v1.81.0
44
hooks:
55
- id: terraform_fmt
6+
- id: terraform_wrapper_module_for_each
67
- id: terraform_validate
78
- id: terraform_docs
89
args:

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ module "secrets_manager" {
3535
}
3636
3737
# Version
38-
secret_string = "ThisIsMySuperSecretString12356!&*()"
38+
create_random_password = true
39+
random_password_length = 64
40+
random_password_override_special = "!@#$%^&*()_+"
3941
4042
tags = {
4143
Environment = "Development"
@@ -123,12 +125,14 @@ Examples codified under the [`examples`](https://github.com/terraform-aws-module
123125
|------|---------|
124126
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
125127
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.0 |
128+
| <a name="requirement_random"></a> [random](#requirement\_random) | >= 3.0 |
126129

127130
## Providers
128131

129132
| Name | Version |
130133
|------|---------|
131134
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.0 |
135+
| <a name="provider_random"></a> [random](#provider\_random) | >= 3.0 |
132136

133137
## Modules
134138

@@ -143,6 +147,7 @@ No modules.
143147
| [aws_secretsmanager_secret_rotation.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_rotation) | resource |
144148
| [aws_secretsmanager_secret_version.ignore_changes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource |
145149
| [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 |
146151
| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
147152

148153
## Inputs
@@ -152,6 +157,7 @@ No modules.
152157
| <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 |
153158
| <a name="input_create"></a> [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no |
154159
| <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 |
155161
| <a name="input_description"></a> [description](#input\_description) | A description of the secret | `string` | `null` | no |
156162
| <a name="input_enable_rotation"></a> [enable\_rotation](#input\_enable\_rotation) | Determines whether secret rotation is enabled | `bool` | `false` | no |
157163
| <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 |
@@ -161,6 +167,8 @@ No modules.
161167
| <a name="input_name_prefix"></a> [name\_prefix](#input\_name\_prefix) | Creates a unique name beginning with the specified prefix | `string` | `null` | no |
162168
| <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 |
163169
| <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 | `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 |
164172
| <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 |
165173
| <a name="input_replica"></a> [replica](#input\_replica) | Configuration block to support secret replication | `any` | `{}` | no |
166174
| <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 |

examples/complete/main.tf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ module "secrets_manager" {
5151
}
5252

5353
# Version
54-
secret_string = "ThisIsMySuperSecretString12356!&*()"
54+
create_random_password = true
55+
random_password_length = 64
56+
random_password_override_special = "!@#$%^&*()_+"
5557

5658
tags = local.tags
5759
}

main.tf

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ resource "aws_secretsmanager_secret_version" "this" {
9393
count = var.create && !(var.enable_rotation || var.ignore_secret_changes) ? 1 : 0
9494

9595
secret_id = aws_secretsmanager_secret.this[0].id
96-
secret_string = var.secret_string
96+
secret_string = var.create_random_password ? random_password.this[0].result : var.secret_string
9797
secret_binary = var.secret_binary
9898
version_stages = var.version_stages
9999
}
@@ -102,7 +102,7 @@ resource "aws_secretsmanager_secret_version" "ignore_changes" {
102102
count = var.create && (var.enable_rotation || var.ignore_secret_changes) ? 1 : 0
103103

104104
secret_id = aws_secretsmanager_secret.this[0].id
105-
secret_string = var.secret_string
105+
secret_string = var.create_random_password ? random_password.this[0].result : var.secret_string
106106
secret_binary = var.secret_binary
107107
version_stages = var.version_stages
108108

@@ -115,6 +115,14 @@ resource "aws_secretsmanager_secret_version" "ignore_changes" {
115115
}
116116
}
117117

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+
118126
################################################################################
119127
# Rotation
120128
################################################################################

variables.tf

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,24 @@ variable "version_stages" {
118118
default = null
119119
}
120120

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+
121139
################################################################################
122140
# Rotation
123141
################################################################################

versions.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@ terraform {
66
source = "hashicorp/aws"
77
version = ">= 5.0"
88
}
9+
random = {
10+
source = "hashicorp/random"
11+
version = ">= 3.0"
12+
}
913
}
1014
}

wrappers/README.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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::[email protected]: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::[email protected]: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+
```

wrappers/main.tf

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
module "wrapper" {
2+
source = "../"
3+
4+
for_each = var.items
5+
6+
create = try(each.value.create, var.defaults.create, true)
7+
tags = try(each.value.tags, var.defaults.tags, {})
8+
description = try(each.value.description, var.defaults.description, null)
9+
force_overwrite_replica_secret = try(each.value.force_overwrite_replica_secret, var.defaults.force_overwrite_replica_secret, null)
10+
kms_key_id = try(each.value.kms_key_id, var.defaults.kms_key_id, null)
11+
name = try(each.value.name, var.defaults.name, null)
12+
name_prefix = try(each.value.name_prefix, var.defaults.name_prefix, null)
13+
recovery_window_in_days = try(each.value.recovery_window_in_days, var.defaults.recovery_window_in_days, null)
14+
replica = try(each.value.replica, var.defaults.replica, {})
15+
create_policy = try(each.value.create_policy, var.defaults.create_policy, false)
16+
source_policy_documents = try(each.value.source_policy_documents, var.defaults.source_policy_documents, [])
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+
block_public_policy = try(each.value.block_public_policy, var.defaults.block_public_policy, null)
20+
ignore_secret_changes = try(each.value.ignore_secret_changes, var.defaults.ignore_secret_changes, false)
21+
secret_string = try(each.value.secret_string, var.defaults.secret_string, null)
22+
secret_binary = try(each.value.secret_binary, var.defaults.secret_binary, null)
23+
version_stages = try(each.value.version_stages, var.defaults.version_stages, null)
24+
create_random_password = try(each.value.create_random_password, var.defaults.create_random_password, false)
25+
random_password_length = try(each.value.random_password_length, var.defaults.random_password_length, 32)
26+
random_password_override_special = try(each.value.random_password_override_special, var.defaults.random_password_override_special, "!@#$%&*()-_=+[]{}<>:?")
27+
enable_rotation = try(each.value.enable_rotation, var.defaults.enable_rotation, false)
28+
rotation_lambda_arn = try(each.value.rotation_lambda_arn, var.defaults.rotation_lambda_arn, "")
29+
rotation_rules = try(each.value.rotation_rules, var.defaults.rotation_rules, {})
30+
}

wrappers/outputs.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
output "wrapper" {
2+
description = "Map of outputs of a wrapper."
3+
value = module.wrapper
4+
# sensitive = false # No sensitive module output found
5+
}

wrappers/variables.tf

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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+
}

0 commit comments

Comments
 (0)