Skip to content

Commit ef6a114

Browse files
committed
added public accessibility flag and greatly improved module documentation and reusability
1 parent f21094a commit ef6a114

8 files changed

+255
-124
lines changed

README.md

+11-13
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ This AWS PostgreSQL database terraform module requires these input variables.
5858

5959
| Input Variable | Type | Comment |
6060
|:-------- |:---- |:------- |
61-
**in_database_name** | String | **alphanumeric only name** to give the new database and it must start with a letter - note that providing a different name causes terraform to create a different database
62-
**in_clone_snapshot** | Boolean | if true the in_id_of_db_to_clone must be provided and will cause the database to be created from the last available snapshot of the specified database
63-
**in_id_of_db_to_clone** | String | this ID must be provided if **`in_clone_snapshot`** is true causing the database to be created from the last available snapshot
64-
**in_security_group_id** | String | the security group that will constrain traffic to and from the database
65-
**in_db_subnet_ids** | List | list of private subnet IDs in at least two availability zones (see example) for housing database instances
61+
**`in_database_name`** | String | **alphanumeric only name** to give the new database and it must start with a letter - note that providing a different name causes terraform to create a different database
62+
**`in_clone_snapshot`** | Boolean | if true the in_id_of_db_to_clone must be provided and will cause the database to be created from the last available snapshot of the specified database
63+
**`in_id_of_db_to_clone`** | String | this ID must be provided if **`in_clone_snapshot`** is true causing the database to be created from the last available snapshot
64+
**`in_security_group_id`** | String | the security group that will constrain traffic to and from the database
65+
**`in_db_subnet_ids`** | List | list of private subnet IDs in at least two availability zones (see example) for housing database instances
6666

6767

6868
### Resource Tag Inputs
@@ -84,11 +84,9 @@ $ export TF_VAR_in_description="was created by $USER@$HOSTNAME on $(date)."
8484

8585
## Outputs
8686

87-
| Output Variable | Type | Comment |
88-
|:------------------------ |:------ |:------- |
89-
**out_database_username** | String | The first database username prefixed with user_rw_ followed by randomized characters.
90-
**out_database_password** | String | Robust terraform created 48 character password that includes the allowed special characters
91-
**out_clone_db_hostname** | String | The addressable hostname of the database that has been cloned from a snapshot
92-
**out_clone_db_endpoint** | String | The database endpoint with protool suffix of the database that has been cloned from a snapshot
93-
**out_fresh_db_hostname** | String | The addressable hostname of the newly created (fresh) database
94-
**out_fresh_db_endpoint** | String | The database endpoint with protool suffix of the newly created (fresh) database
87+
| Output Variable | Type | Comment |
88+
|:-------------------------- |:------ |:------- |
89+
**`out_database_username`** | String | The first database username prefixed with user_rw_ followed by randomized characters.
90+
**`out_database_password`** | String | Robust terraform created 48 character password that includes the allowed special characters
91+
**`out_clone_db_hostname`** | String | The addressable hostname of the database that has been cloned from a snapshot
92+
**`out_fresh_db_hostname`** | String | The addressable hostname of the newly created (fresh) database

example/README.md

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
2+
# Terraform Docker Example | Clone PostgreSQL Database | Create New PostgreSQL Database
3+
4+
This example clones an RDS PostgreSQL database and creates a brand new RDS database.
5+
6+
### Step 1 | git clone into docker volume
7+
8+
First we create a docker volume (called **`vol.postgres.tfstate`**) and add the terraform module code to it by way of an **alpine git** container.
9+
10+
```
11+
docker volume create vol.postgres.tfstate
12+
docker run --interactive \
13+
--tty \
14+
--rm \
15+
--volume vol.postgres.tfstate:/terraform-work \
16+
alpine/git \
17+
clone https://github.com/devops4me/terraform-aws-postgres-rds /terraform-work
18+
sudo ls -lah /var/lib/docker/volumes/vol.postgres.tfstate/_data
19+
```
20+
21+
**verify** - when you list the files in the container you will see the terraform module's contents there.
22+
23+
24+
### Step 2 | terraform init via docker
25+
26+
As our volume contains the terraform module code from git we are now ready to perform a terraform init. We use the **[devops4me/terraform container](https://cloud.docker.com/repository/docker/devops4me/terraform/general)** container which adds a VOLUME mapping to the **[hashicorp/terraform](https://hub.docker.com/r/hashicorp/terraform/)** container at the **`/terraform-work`** location.
27+
28+
```
29+
docker run --interactive \
30+
--tty \
31+
--rm \
32+
--name vm.terraform \
33+
--volume vol.postgres.tfstate:/terraform-work \
34+
devops4me/terraform init example
35+
sudo ls -lah /var/lib/docker/volumes/vol.postgres.tfstate/_data
36+
```
37+
38+
**verify** - the directory listing now contains a **`.terraform`** directory.
39+
40+
41+
42+
### Step 3 | terraform apply via docker
43+
44+
At last we can run the terraform apply. Provide a **role arn** only if your organization works with roles alongside the other 3 AWS authentication keys.
45+
46+
```
47+
docker run --interactive \
48+
--tty \
49+
--rm \
50+
--name vm.terraform \
51+
--env AWS_DEFAULT_REGION=<<aws-region-key>> \
52+
--env AWS_ACCESS_KEY_ID=<<aws-access-key>> \
53+
--env AWS_SECRET_ACCESS_KEY=<<aws-secret-key>> \
54+
--env TF_VAR_in_role_arn=<<aws-role-arn>> \
55+
--env TF_VAR_in_id_of_db_to_clone=<<database_id>> \
56+
--env TF_VAR_in_publicly_accessible=true \
57+
--volume vol.postgres.tfstate:/terraform-work \
58+
devops4me/terraform apply -auto-approve example
59+
sudo ls -lah /var/lib/docker/volumes/vol.postgres.tfstate/_data
60+
```
61+
62+
**verify** - the **docker volume** now has a **tfstate file** which documents the state of your infrastructure after terraform apply.
63+
64+
65+
### Step 4 | terraform destroy via docker
66+
67+
After running plan and apply either once or multiple times you may feel the need to **`terraform destroy`** the infrastructure.
68+
69+
```
70+
docker run --interactive \
71+
--tty \
72+
--rm \
73+
--name vm.terraform \
74+
--env AWS_DEFAULT_REGION=<<aws-region-key>> \
75+
--env AWS_ACCESS_KEY_ID=<<aws-access-key>> \
76+
--env AWS_SECRET_ACCESS_KEY=<<aws-secret-key>> \
77+
--env TF_VAR_in_role_arn=<<aws-role-arn>> \
78+
--volume vol.postgres.tfstate:/terraform-work \
79+
devops4me/terraform destroy -auto-approve example
80+
sudo ls -lah /var/lib/docker/volumes/vol.postgres.tfstate/_data
81+
```
82+
83+
**verify** - check your AWS console and also note that the volume now has a **tfstate backup file** created by terraform.

example/postgres.example-main.tf

+40-86
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11

2-
### ############################ ###
3-
### Example RDS Postgres Outputs ###
4-
### ############################ ###
5-
6-
output out_fresh_database_hostname { value = module.fresh_db.out_fresh_db_hostname }
7-
output out_fresh_database_endpoint { value = module.fresh_db.out_fresh_db_endpoint }
8-
9-
output out_clone_database_hostname { value = module.clone_db.out_clone_db_hostname }
10-
output out_clone_database_endpoint { value = module.clone_db.out_clone_db_endpoint }
11-
12-
output out_fresh_database_username { value = module.fresh_db.out_database_username }
13-
output out_fresh_database_password { value = module.fresh_db.out_database_password }
14-
15-
output out_clone_database_username { value = module.clone_db.out_database_username }
16-
output out_clone_database_password { value = module.clone_db.out_database_password }
17-
2+
/*
3+
| --
4+
| -- If you are using an IAM role as the AWS access mechanism then
5+
| -- pass it as in_role_arn commonly through an environment variable
6+
| -- named TF_VAR_in_role_arn in addition to the usual AWS access
7+
| -- key, secret key and default region parameters.
8+
| --
9+
*/
10+
provider aws {
11+
dynamic assume_role {
12+
for_each = length( var.in_role_arn ) > 0 ? [ var.in_role_arn ] : []
13+
content {
14+
role_arn = assume_role.value
15+
}
16+
}
17+
}
1818

1919
module fresh_db {
2020

2121
source = "./.."
2222

23-
in_security_group_id = module.security-group.out_security_group_id
24-
in_db_subnet_ids = module.vpc-network.out_private_subnet_ids
25-
in_database_name = local.fresh_db_name
23+
in_publicly_accessible = var.in_publicly_accessible
24+
in_db_subnet_ids = var.in_publicly_accessible ? module.vpc-network.out_public_subnet_ids : module.vpc-network.out_private_subnet_ids
25+
in_security_group_id = module.security-group.out_security_group_id
26+
in_database_name = local.fresh_db_name
2627

27-
in_ecosystem = local.ecosystem_name
28+
in_ecosystem = local.ecosystem_name
2829
in_timestamp = local.timestamp
2930
in_description = local.description
3031
}
@@ -34,100 +35,53 @@ module clone_db {
3435

3536
source = "./.."
3637

37-
in_security_group_id = module.security-group.out_security_group_id
38-
in_db_subnet_ids = module.vpc-network.out_private_subnet_ids
39-
in_id_of_db_to_clone = var.in_id_of_db_to_clone
40-
in_clone_snapshot = true
38+
in_publicly_accessible = var.in_publicly_accessible
39+
in_db_subnet_ids = var.in_publicly_accessible ? module.vpc-network.out_public_subnet_ids : module.vpc-network.out_private_subnet_ids
40+
in_id_of_db_to_clone = var.in_id_of_db_to_clone
41+
in_security_group_id = module.security-group.out_security_group_id
42+
in_clone_snapshot = true
4143

4244
in_database_name = local.clone_db_name
4345

44-
in_ecosystem = local.ecosystem_name
46+
in_ecosystem = local.ecosystem_name
4547
in_timestamp = local.timestamp
4648
in_description = local.description
4749
}
4850

4951

5052
module vpc-network {
5153

52-
source = "devops4me/vpc-network/aws"
53-
version = "1.0.2"
54+
source = "devops4me/vpc-network/aws"
55+
version = "~> 1.0.2"
5456

5557
in_vpc_cidr = "10.81.0.0/16"
56-
in_num_public_subnets = 3
57-
in_num_private_subnets = 3
58+
in_num_public_subnets = 2
59+
in_num_private_subnets = var.in_publicly_accessible ? 0 : 2
5860

59-
in_ecosystem = local.ecosystem_name
61+
in_ecosystem = local.ecosystem_name
6062
in_timestamp = local.timestamp
6163
in_description = local.description
6264
}
6365

6466

6567
module security-group {
6668

67-
source = "github.com/devops4me/terraform-aws-security-group"
68-
in_ingress = [ "postgres" ]
69-
in_vpc_id = module.vpc-network.out_vpc_id
69+
source = "github.com/devops4me/terraform-aws-security-group"
70+
in_ingress = [ "postgres", "ssh" ]
71+
in_vpc_id = module.vpc-network.out_vpc_id
7072

71-
in_ecosystem_name = local.ecosystem_name
72-
in_tag_timestamp = local.timestamp
73-
in_tag_description = local.description
73+
in_ecosystem = local.ecosystem_name
74+
in_timestamp = local.timestamp
75+
in_description = local.description
7476
}
7577

7678

7779
locals {
80+
7881
fresh_db_name = "brandnewdb"
7982
clone_db_name = "snapshotdb"
80-
}
81-
82-
83-
variable in_id_of_db_to_clone {
84-
description = "The ID of mummy database to clone from."
85-
}
86-
87-
8883

89-
/*
90-
| --
91-
| -- If you are using an IAM role as the AWS access mechanism then
92-
| -- pass it as in_role_arn commonly through an environment variable
93-
| -- named TF_VAR_in_role_arn in addition to the usual AWS access
94-
| -- key, secret key and default region parameters.
95-
| --
96-
| -- Individuals and small businesses without hundreds of AWS accounts
97-
| -- can omit the in_role_arn variable. and thanks to dynamic assignment
98-
| --
99-
*/
100-
provider aws {
101-
dynamic assume_role {
102-
for_each = length( var.in_role_arn ) > 0 ? [ var.in_role_arn ] : []
103-
content {
104-
role_arn = assume_role.value
105-
}
106-
}
107-
}
108-
109-
variable in_role_arn {
110-
description = "The Role ARN to use when we assume role to implement the provisioning."
111-
default = ""
112-
}
113-
114-
115-
/*
116-
| --
117-
| -- ### ############# ###
118-
| -- ### Resource Tags ###
119-
| -- ### ############# ###
120-
| --
121-
| -- Terraform will tag every significant resource allowing you to report and collate
122-
| --
123-
| -- [1] - all infrastructure in all environments dedicated to your app (ecosystem_name)
124-
| -- [2] - the infrastructure dedicated to this environment instance (timestamp)
125-
| --
126-
| -- The human readable description reveals the when, where and what of the infrastructure.
127-
| --
128-
*/
129-
locals {
130-
ecosystem_name = "dbstack"
84+
ecosystem_name = "sanjievesdb"
13185
timestamp = formatdate( "YYMMDDhhmmss", timestamp() )
13286
date_time = formatdate( "EEEE DD-MMM-YY hh:mm:ss ZZZ", timestamp() )
13387
description = "was created by me on ${ local.date_time }."

example/postgres.example-outputs.tf

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
### ############################ ###
3+
### Example RDS Postgres Outputs ###
4+
### ############################ ###
5+
6+
output out_fresh_database_hostname {
7+
value = module.fresh_db.out_fresh_db_hostname
8+
}
9+
10+
output out_fresh_database_username {
11+
value = module.fresh_db.out_database_username
12+
}
13+
14+
output out_fresh_database_password {
15+
value = module.fresh_db.out_database_password
16+
}
17+
18+
output out_clone_database_hostname {
19+
value = module.clone_db.out_clone_db_hostname
20+
}
21+
22+
output out_clone_database_username {
23+
value = module.clone_db.out_database_username
24+
}
25+
26+
output out_clone_database_password {
27+
value = module.clone_db.out_database_password
28+
}

example/postgres.example-variables.tf

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
/*
3+
| --
4+
| -- If cloning the database then the module needs to know the RDS ID of
5+
| -- the database whose last available snapshot will be sought after.
6+
| --
7+
*/
8+
variable in_id_of_db_to_clone {
9+
description = "The ID of mummy database to clone from."
10+
}
11+
12+
13+
/*
14+
| --
15+
| -- You must state whether you want the created databases to be publicly
16+
| -- accessible or not. If yes then the database must live in public subnets.
17+
| --
18+
*/
19+
variable in_publicly_accessible {
20+
description = "Make the RDS database publicly accessible inside a public subnet and appropriate security group."
21+
type = bool
22+
}
23+
24+
25+
/*
26+
| --
27+
| -- If you are using an IAM role as the AWS access mechanism then
28+
| -- pass it as in_role_arn commonly through an environment variable
29+
| -- named TF_VAR_in_role_arn in addition to the usual AWS access
30+
| -- key, secret key and default region parameters.
31+
| --
32+
*/
33+
variable in_role_arn {
34+
description = "The Role ARN to use when we assume role to implement the provisioning."
35+
default = ""
36+
}

rds.postgres-main.tf

+3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ locals {
2525
resource aws_db_instance fresh {
2626

2727
count = var.in_clone_snapshot ? 0 : 1
28+
2829
identifier = "${ var.in_database_name }-fresh-${ var.in_ecosystem }-${ var.in_timestamp }"
30+
publicly_accessible = var.in_publicly_accessible
2931

3032
name = var.in_database_name
3133
username = local.db_username
@@ -73,6 +75,7 @@ resource aws_db_instance clone {
7375

7476
snapshot_identifier = data.aws_db_snapshot.from[0].id
7577
identifier = "${ var.in_database_name }-clone-${ var.in_ecosystem }-${ var.in_timestamp }"
78+
publicly_accessible = var.in_publicly_accessible
7679

7780
name = var.in_database_name
7881
port = 5432

rds.postgres-outputs.tf

-9
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,6 @@ output out_fresh_db_hostname {
1111
value = length( aws_db_instance.fresh ) == 0 ? "n/a" : aws_db_instance.fresh[0].address
1212
}
1313

14-
output out_fresh_db_endpoint {
15-
value = length( aws_db_instance.fresh ) == 0 ? "n/a" : aws_db_instance.fresh[0].endpoint
16-
}
17-
1814
output out_clone_db_hostname {
1915
value = length( aws_db_instance.clone ) == 0 ? "n/a" : aws_db_instance.clone[0].address
2016
}
21-
22-
output out_clone_db_endpoint {
23-
value = length( aws_db_instance.clone ) == 0 ? "n/a" : aws_db_instance.clone[0].endpoint
24-
}
25-

0 commit comments

Comments
 (0)