Skip to content

Commit 6b3ea29

Browse files
committed
feat: auto-generate DB password, add connection URI, switch to name_prefix, and add validations + docs
1 parent 23c9467 commit 6b3ea29

File tree

6 files changed

+171
-21
lines changed

6 files changed

+171
-21
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.terraform/

.terraform.lock.hcl

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This Terraform module provisions a PostgreSQL database instance on Amazon RDS wi
88
- Sets up a dedicated VPC security group with configurable access rules
99
- Configures subnet groups for the RDS instance
1010
- Supports encryption, backups, and maintenance windows
11-
- Generates random pet names for resource identification
11+
- Uses a caller-provided name prefix for resource identification
1212

1313
## Usage
1414

@@ -19,9 +19,9 @@ module "postgres" {
1919
# Required variables
2020
vpc_id = "vpc-xxxxxxxx"
2121
subnet_ids = ["subnet-xxxxxxxx", "subnet-yyyyyyyy"]
22+
name_prefix = "myapp-prod"
2223
database_name = "myapp"
2324
username = "dbadmin"
24-
password = "your-secure-password"
2525
2626
# Optional variables
2727
instance_class = "db.t3.micro"
@@ -37,6 +37,19 @@ module "postgres" {
3737
}
3838
```
3939

40+
The module auto-generates a strong random password and exposes it as a sensitive Terraform output named `db_master_password`.
41+
Retrieve it after apply with:
42+
43+
```
44+
terraform output -raw db_master_password
45+
```
46+
47+
For a ready-to-use PostgreSQL connection URI (includes the password):
48+
49+
```
50+
terraform output -raw db_connection_uri
51+
```
52+
4053
## Requirements
4154

4255
- Terraform >= 1.0.0
@@ -58,9 +71,9 @@ module "postgres" {
5871
|------|-------------|------|---------|
5972
| vpc_id | VPC ID where RDS will be deployed | `string` | - |
6073
| subnet_ids | A list of VPC subnet IDs | `list(string)` | - |
74+
| name_prefix | Name prefix for RDS identifier and related resources | `string` | - |
6175
| database_name | The name of the database to create | `string` | - |
6276
| username | Username for the master DB user | `string` | - |
63-
| password | Password for the master DB user | `string` | - |
6477

6578
### Optional Variables
6679

@@ -81,6 +94,12 @@ module "postgres" {
8194
| egress_cidr_blocks | List of CIDR blocks to allow egress traffic from the database | `list(string)` | `["0.0.0.0/0"]` |
8295
| tags | A mapping of tags to assign to all resources | `map(string)` | `{}` |
8396

97+
## Naming Constraints
98+
99+
- name_prefix: 1–63 chars, lowercase, starts with a letter, contains only letters, numbers, and hyphens; no trailing hyphen; no consecutive hyphens.
100+
- database_name: 1–63 chars, lowercase, starts with a letter, contains only letters, numbers, and underscores.
101+
- username: 1–63 chars, lowercase, starts with a letter, contains only letters and numbers; reserved names not allowed: `postgres`, `rdsadmin`.
102+
84103
## Outputs
85104

86105
| Name | Description |
@@ -91,14 +110,18 @@ module "postgres" {
91110
| db_instance_port | The database port |
92111
| db_subnet_group_id | The db subnet group name |
93112
| db_security_group_id | The security group ID |
113+
| db_master_password | The generated master password (sensitive) |
114+
| db_connection_uri | PostgreSQL connection URI with credentials (sensitive) |
94115

95116
## Security Considerations
96117

97118
- By default, the security group allows inbound access on port 5432 from all IP addresses (0.0.0.0/0). It's strongly recommended to restrict this using the `ingress_cidr_blocks` variable in production environments.
98119
- Database encryption is enabled by default using AWS KMS.
99120
- Final snapshots are created by default when destroying the database (skip_final_snapshot = false).
100121
- The module uses Kubernetes backend configuration. Ensure your Terraform environment is properly configured for this.
122+
- The password is generated at apply time and marked as a sensitive output. Store it securely (e.g., AWS Secrets Manager) rather than relying on CLI history.
123+
- Ensure `name_prefix` conforms to AWS naming constraints for RDS identifiers (letters, numbers, hyphens; must start with a letter; max 63 characters).
101124

102125
## License
103126

104-
This module is maintained by Ryvn Technologies.
127+
This module is maintained by Ryvn Technologies.

main.tf

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
1-
resource "random_pet" "this" {
2-
length = 2
3-
separator = "-"
4-
prefix = "postgres"
5-
}
6-
7-
81
resource "aws_db_subnet_group" "this" {
9-
name = random_pet.this.id
2+
name = "${var.name_prefix}-subnet-group"
103
subnet_ids = var.subnet_ids
114

125
tags = var.tags
136
}
147

158
resource "aws_security_group" "this" {
16-
name = "${random_pet.this.id}-rds-sg"
17-
description = "Security group for ${random_pet.this.id} RDS instance"
9+
name = "${var.name_prefix}-rds-sg"
10+
description = "Security group for ${var.name_prefix} RDS instance"
1811
vpc_id = var.vpc_id
1912

2013
ingress {
@@ -43,7 +36,7 @@ data "aws_rds_orderable_db_instance" "postgres" {
4336
}
4437

4538
resource "aws_db_instance" "this" {
46-
identifier = random_pet.this.id
39+
identifier = var.name_prefix
4740

4841
engine = "postgres"
4942
engine_version = var.engine_version
@@ -55,7 +48,7 @@ resource "aws_db_instance" "this" {
5548

5649
db_name = var.database_name
5750
username = var.username
58-
password = var.password
51+
password = random_password.master.result
5952
port = 5432
6053

6154
multi_az = var.multi_az
@@ -70,3 +63,13 @@ resource "aws_db_instance" "this" {
7063

7164
tags = var.tags
7265
}
66+
67+
resource "random_password" "master" {
68+
length = 20
69+
special = true
70+
min_upper = 1
71+
min_lower = 1
72+
min_numeric = 1
73+
min_special = 1
74+
override_special = "!#$%&*()-_=+[]{}<>:?@"
75+
}

outputs.tf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,22 @@ output "db_security_group_id" {
2727
description = "The security group ID"
2828
value = aws_security_group.this.id
2929
}
30+
31+
output "db_master_password" {
32+
description = "The generated master password (sensitive)"
33+
value = random_password.master.result
34+
sensitive = true
35+
}
36+
37+
output "db_connection_uri" {
38+
description = "PostgreSQL connection URI including credentials (sensitive)"
39+
value = format(
40+
"postgresql://%s:%s@%s:%d/%s",
41+
urlencode(var.username),
42+
urlencode(random_password.master.result),
43+
aws_db_instance.this.address,
44+
aws_db_instance.this.port,
45+
var.database_name,
46+
)
47+
sensitive = true
48+
}

variables.tf

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,79 @@ variable "subnet_ids" {
1515
type = list(string)
1616
}
1717

18+
variable "name_prefix" {
19+
description = "Name prefix used for RDS identifier and related resources"
20+
type = string
21+
22+
validation {
23+
condition = length(var.name_prefix) > 0 && length(var.name_prefix) <= 63
24+
error_message = "name_prefix must be 1-63 characters long."
25+
}
26+
27+
validation {
28+
condition = lower(var.name_prefix) == var.name_prefix
29+
error_message = "name_prefix must be lowercase."
30+
}
31+
32+
validation {
33+
condition = can(regex("^[a-z][a-z0-9-]*$", var.name_prefix))
34+
error_message = "name_prefix must start with a letter and contain only lowercase letters, numbers, and hyphens."
35+
}
36+
37+
validation {
38+
condition = length(regexall("--", var.name_prefix)) == 0
39+
error_message = "name_prefix must not contain consecutive hyphens ('--')."
40+
}
41+
42+
validation {
43+
condition = endswith(var.name_prefix, "-") == false
44+
error_message = "name_prefix must not end with a hyphen."
45+
}
46+
}
47+
1848
variable "database_name" {
1949
description = "The name of the database to create"
2050
type = string
51+
52+
validation {
53+
condition = length(var.database_name) > 0 && length(var.database_name) <= 63
54+
error_message = "database_name must be 1-63 characters long."
55+
}
56+
57+
validation {
58+
condition = lower(var.database_name) == var.database_name
59+
error_message = "database_name must be lowercase."
60+
}
61+
62+
validation {
63+
condition = can(regex("^[a-z][a-z0-9_]*$", var.database_name))
64+
error_message = "database_name must start with a letter and contain only lowercase letters, numbers, and underscores."
65+
}
2166
}
2267

2368
variable "username" {
2469
description = "Username for the master DB user"
2570
type = string
26-
}
2771

28-
variable "password" {
29-
description = "Password for the master DB user"
30-
type = string
31-
sensitive = true
72+
validation {
73+
condition = length(var.username) > 0 && length(var.username) <= 63
74+
error_message = "username must be 1-63 characters long."
75+
}
76+
77+
validation {
78+
condition = lower(var.username) == var.username
79+
error_message = "username must be lowercase."
80+
}
81+
82+
validation {
83+
condition = can(regex("^[a-z][a-z0-9]*$", var.username))
84+
error_message = "username must start with a letter and contain only lowercase letters and numbers."
85+
}
86+
87+
validation {
88+
condition = !(var.username == "postgres" || var.username == "rdsadmin")
89+
error_message = "username cannot be one of the reserved names: postgres, rdsadmin."
90+
}
3291
}
3392

3493
# Optional variables with defaults

0 commit comments

Comments
 (0)