Skip to content

Commit ed027d7

Browse files
authored
feat: Add db_instance_role_association functionality (#508)
Co-authored-by: magreenbaum <magreenbaum>
1 parent 8b2f5c5 commit ed027d7

File tree

16 files changed

+503
-0
lines changed

16 files changed

+503
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ override.tf.json
2727
# Ignore CLI configuration files
2828
.terraformrc
2929
terraform.rc
30+
31+
# Lambda directories
32+
builds/

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ No providers.
220220
| Name | Source | Version |
221221
|------|--------|---------|
222222
| <a name="module_db_instance"></a> [db\_instance](#module\_db\_instance) | ./modules/db_instance | n/a |
223+
| <a name="module_db_instance_role_association"></a> [db\_instance\_role\_association](#module\_db\_instance\_role\_association) | ./modules/db_instance_role_association | n/a |
223224
| <a name="module_db_option_group"></a> [db\_option\_group](#module\_db\_option\_group) | ./modules/db_option_group | n/a |
224225
| <a name="module_db_parameter_group"></a> [db\_parameter\_group](#module\_db\_parameter\_group) | ./modules/db_parameter_group | n/a |
225226
| <a name="module_db_subnet_group"></a> [db\_subnet\_group](#module\_db\_subnet\_group) | ./modules/db_subnet_group | n/a |
@@ -252,6 +253,7 @@ No resources.
252253
| <a name="input_create_db_subnet_group"></a> [create\_db\_subnet\_group](#input\_create\_db\_subnet\_group) | Whether to create a database subnet group | `bool` | `false` | no |
253254
| <a name="input_create_monitoring_role"></a> [create\_monitoring\_role](#input\_create\_monitoring\_role) | Create IAM role with a defined name that permits RDS to send enhanced monitoring metrics to CloudWatch Logs | `bool` | `false` | no |
254255
| <a name="input_custom_iam_instance_profile"></a> [custom\_iam\_instance\_profile](#input\_custom\_iam\_instance\_profile) | RDS custom iam instance profile | `string` | `null` | no |
256+
| <a name="input_db_instance_role_associations"></a> [db\_instance\_role\_associations](#input\_db\_instance\_role\_associations) | A map of DB instance supported feature name to role association ARNs. | `map(any)` | `{}` | no |
255257
| <a name="input_db_instance_tags"></a> [db\_instance\_tags](#input\_db\_instance\_tags) | Additional tags for the DB instance | `map(string)` | `{}` | no |
256258
| <a name="input_db_name"></a> [db\_name](#input\_db\_name) | The DB name to create. If omitted, no database is created initially | `string` | `null` | no |
257259
| <a name="input_db_option_group_tags"></a> [db\_option\_group\_tags](#input\_db\_option\_group\_tags) | Additional tags for the DB option group | `map(string)` | `{}` | no |
@@ -342,6 +344,7 @@ No resources.
342344
| <a name="output_db_instance_name"></a> [db\_instance\_name](#output\_db\_instance\_name) | The database name |
343345
| <a name="output_db_instance_port"></a> [db\_instance\_port](#output\_db\_instance\_port) | The database port |
344346
| <a name="output_db_instance_resource_id"></a> [db\_instance\_resource\_id](#output\_db\_instance\_resource\_id) | The RDS Resource ID of this instance |
347+
| <a name="output_db_instance_role_associations"></a> [db\_instance\_role\_associations](#output\_db\_instance\_role\_associations) | A map of DB Instance Identifiers and IAM Role ARNs separated by a comma |
345348
| <a name="output_db_instance_status"></a> [db\_instance\_status](#output\_db\_instance\_status) | The RDS instance status |
346349
| <a name="output_db_instance_username"></a> [db\_instance\_username](#output\_db\_instance\_username) | The master username for the database |
347350
| <a name="output_db_listener_endpoint"></a> [db\_listener\_endpoint](#output\_db\_listener\_endpoint) | Specifies the listener connection endpoint for SQL Server Always On |
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# RDS DB instance role association example for PostgreSQL
2+
3+
Configuration in this directory creates a DB instance role association to invoke a lambda function.
4+
5+
Further database configurations for creating extension and invoking from postgres: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/PostgreSQL-Lambda.html
6+
7+
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
8+
## Requirements
9+
10+
| Name | Version |
11+
|------|---------|
12+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
13+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.0 |
14+
15+
## Providers
16+
17+
| Name | Version |
18+
|------|---------|
19+
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.0 |
20+
21+
## Modules
22+
23+
| Name | Source | Version |
24+
|------|--------|---------|
25+
| <a name="module_db"></a> [db](#module\_db) | ../../ | n/a |
26+
| <a name="module_lambda"></a> [lambda](#module\_lambda) | terraform-aws-modules/lambda/aws | ~> 6.0 |
27+
| <a name="module_rds_invoke_lambda_policy"></a> [rds\_invoke\_lambda\_policy](#module\_rds\_invoke\_lambda\_policy) | terraform-aws-modules/iam/aws//modules/iam-policy | ~> 5.28.0 |
28+
| <a name="module_rds_invoke_lambda_role"></a> [rds\_invoke\_lambda\_role](#module\_rds\_invoke\_lambda\_role) | terraform-aws-modules/iam/aws//modules/iam-assumable-role | ~> 5.28.0 |
29+
| <a name="module_security_group"></a> [security\_group](#module\_security\_group) | terraform-aws-modules/security-group/aws | ~> 4.0 |
30+
| <a name="module_vpc"></a> [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 |
31+
32+
## Resources
33+
34+
| Name | Type |
35+
|------|------|
36+
| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
37+
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
38+
| [aws_iam_policy_document.rds_invoke_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
39+
| [aws_iam_policy_document.rds_invoke_lambda_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
40+
41+
## Inputs
42+
43+
No inputs.
44+
45+
## Outputs
46+
47+
| Name | Description |
48+
|------|-------------|
49+
| <a name="output_db_enhanced_monitoring_iam_role_arn"></a> [db\_enhanced\_monitoring\_iam\_role\_arn](#output\_db\_enhanced\_monitoring\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the monitoring role |
50+
| <a name="output_db_instance_address"></a> [db\_instance\_address](#output\_db\_instance\_address) | The address of the RDS instance |
51+
| <a name="output_db_instance_arn"></a> [db\_instance\_arn](#output\_db\_instance\_arn) | The ARN of the RDS instance |
52+
| <a name="output_db_instance_availability_zone"></a> [db\_instance\_availability\_zone](#output\_db\_instance\_availability\_zone) | The availability zone of the RDS instance |
53+
| <a name="output_db_instance_cloudwatch_log_groups"></a> [db\_instance\_cloudwatch\_log\_groups](#output\_db\_instance\_cloudwatch\_log\_groups) | Map of CloudWatch log groups created and their attributes |
54+
| <a name="output_db_instance_endpoint"></a> [db\_instance\_endpoint](#output\_db\_instance\_endpoint) | The connection endpoint |
55+
| <a name="output_db_instance_engine"></a> [db\_instance\_engine](#output\_db\_instance\_engine) | The database engine |
56+
| <a name="output_db_instance_engine_version_actual"></a> [db\_instance\_engine\_version\_actual](#output\_db\_instance\_engine\_version\_actual) | The running version of the database |
57+
| <a name="output_db_instance_hosted_zone_id"></a> [db\_instance\_hosted\_zone\_id](#output\_db\_instance\_hosted\_zone\_id) | The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record) |
58+
| <a name="output_db_instance_identifier"></a> [db\_instance\_identifier](#output\_db\_instance\_identifier) | The RDS instance identifier |
59+
| <a name="output_db_instance_master_user_secret_arn"></a> [db\_instance\_master\_user\_secret\_arn](#output\_db\_instance\_master\_user\_secret\_arn) | The ARN of the master user secret (Only available when manage\_master\_user\_password is set to true) |
60+
| <a name="output_db_instance_name"></a> [db\_instance\_name](#output\_db\_instance\_name) | The database name |
61+
| <a name="output_db_instance_port"></a> [db\_instance\_port](#output\_db\_instance\_port) | The database port |
62+
| <a name="output_db_instance_resource_id"></a> [db\_instance\_resource\_id](#output\_db\_instance\_resource\_id) | The RDS Resource ID of this instance |
63+
| <a name="output_db_instance_role_associations"></a> [db\_instance\_role\_associations](#output\_db\_instance\_role\_associations) | The outputs for the role associations |
64+
| <a name="output_db_instance_status"></a> [db\_instance\_status](#output\_db\_instance\_status) | The RDS instance status |
65+
| <a name="output_db_instance_username"></a> [db\_instance\_username](#output\_db\_instance\_username) | The master username for the database |
66+
| <a name="output_db_parameter_group_arn"></a> [db\_parameter\_group\_arn](#output\_db\_parameter\_group\_arn) | The ARN of the db parameter group |
67+
| <a name="output_db_parameter_group_id"></a> [db\_parameter\_group\_id](#output\_db\_parameter\_group\_id) | The db parameter group id |
68+
| <a name="output_db_subnet_group_arn"></a> [db\_subnet\_group\_arn](#output\_db\_subnet\_group\_arn) | The ARN of the db subnet group |
69+
| <a name="output_db_subnet_group_id"></a> [db\_subnet\_group\_id](#output\_db\_subnet\_group\_id) | The db subnet group name |
70+
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def lambda_handler(event, context):
2+
3+
return "Triggered by RDS Lambda!"
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
provider "aws" {
2+
region = local.region
3+
}
4+
5+
data "aws_caller_identity" "current" {}
6+
data "aws_availability_zones" "available" {}
7+
8+
locals {
9+
name = "role-association-invoke-lambda"
10+
region = "eu-west-1"
11+
12+
vpc_cidr = "10.0.0.0/16"
13+
azs = slice(data.aws_availability_zones.available.names, 0, 3)
14+
15+
tags = {
16+
Name = local.name
17+
Example = local.name
18+
Repository = "https://github.com/terraform-aws-modules/terraform-aws-rds"
19+
}
20+
}
21+
22+
################################################################################
23+
# RDS Module
24+
################################################################################
25+
26+
module "db" {
27+
source = "../../"
28+
29+
identifier = local.name
30+
31+
# All available versions: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts
32+
engine = "postgres"
33+
engine_version = "14"
34+
family = "postgres14" # DB parameter group
35+
major_engine_version = "14" # DB option group
36+
instance_class = "db.t4g.large"
37+
38+
allocated_storage = 20
39+
40+
# NOTE: Do NOT use 'user' as the value for 'username' as it throws:
41+
# "Error creating DB Instance: InvalidParameterValue: MasterUsername
42+
# user cannot be used as it is a reserved word used by the engine"
43+
db_name = "RoleAssociationInvokeLambda"
44+
username = "role_association_invoke_lambda"
45+
port = 5432
46+
47+
multi_az = true
48+
db_subnet_group_name = module.vpc.database_subnet_group
49+
vpc_security_group_ids = [module.security_group.security_group_id]
50+
51+
maintenance_window = "Mon:00:00-Mon:03:00"
52+
backup_window = "03:00-06:00"
53+
backup_retention_period = 0
54+
55+
deletion_protection = false
56+
57+
# https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/PostgreSQL-Lambda.html
58+
db_instance_role_associations = {
59+
Lambda = module.rds_invoke_lambda_role.iam_role_arn
60+
}
61+
62+
parameters = [
63+
{
64+
name = "rds.custom_dns_resolution"
65+
value = 1
66+
apply_method = "pending-reboot"
67+
},
68+
]
69+
70+
tags = local.tags
71+
}
72+
73+
################################################################################
74+
# Supporting Resources
75+
################################################################################
76+
77+
module "vpc" {
78+
source = "terraform-aws-modules/vpc/aws"
79+
version = "~> 5.0"
80+
81+
name = local.name
82+
cidr = local.vpc_cidr
83+
84+
azs = local.azs
85+
public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k)]
86+
private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 3)]
87+
database_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 6)]
88+
89+
create_database_subnet_group = true
90+
enable_nat_gateway = true
91+
92+
tags = local.tags
93+
}
94+
95+
module "security_group" {
96+
source = "terraform-aws-modules/security-group/aws"
97+
version = "~> 4.0"
98+
99+
name = local.name
100+
description = "Complete PostgreSQL example security group"
101+
vpc_id = module.vpc.vpc_id
102+
103+
# ingress
104+
ingress_with_cidr_blocks = [
105+
{
106+
from_port = 5432
107+
to_port = 5432
108+
protocol = "tcp"
109+
description = "PostgreSQL access from within VPC"
110+
cidr_blocks = module.vpc.vpc_cidr_block
111+
},
112+
]
113+
114+
# egress
115+
egress_with_cidr_blocks = [
116+
{
117+
from_port = 443
118+
to_port = 443
119+
protocol = "tcp"
120+
description = "Egress to AWS Lambda VPC"
121+
cidr_blocks = "0.0.0.0/0"
122+
}
123+
]
124+
125+
tags = local.tags
126+
}
127+
128+
module "rds_invoke_lambda_role" {
129+
source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role"
130+
version = "~> 5.28.0"
131+
132+
create_role = true
133+
role_requires_mfa = false
134+
135+
role_name_prefix = local.name
136+
137+
custom_role_policy_arns = [
138+
module.rds_invoke_lambda_policy.arn
139+
]
140+
custom_role_trust_policy = data.aws_iam_policy_document.rds_invoke_lambda_assume_role.json
141+
}
142+
143+
module "rds_invoke_lambda_policy" {
144+
source = "terraform-aws-modules/iam/aws//modules/iam-policy"
145+
version = "~> 5.28.0"
146+
147+
name = "${local.name}-policy"
148+
path = "/"
149+
description = "Invoke Lambda from RDS Postgresql policy"
150+
151+
policy = data.aws_iam_policy_document.rds_invoke_lambda.json
152+
}
153+
154+
data "aws_iam_policy_document" "rds_invoke_lambda" {
155+
statement {
156+
sid = "InvokeLambda"
157+
actions = [
158+
"lambda:InvokeFunction"
159+
]
160+
resources = [
161+
module.lambda.lambda_function_arn
162+
]
163+
}
164+
}
165+
166+
data "aws_iam_policy_document" "rds_invoke_lambda_assume_role" {
167+
statement {
168+
sid = "AssumeRole"
169+
170+
principals {
171+
type = "Service"
172+
identifiers = ["rds.amazonaws.com"]
173+
}
174+
175+
condition {
176+
test = "StringEquals"
177+
values = [data.aws_caller_identity.current.id]
178+
variable = "aws:SourceAccount"
179+
}
180+
181+
effect = "Allow"
182+
183+
actions = ["sts:AssumeRole"]
184+
}
185+
}
186+
187+
module "lambda" {
188+
source = "terraform-aws-modules/lambda/aws"
189+
version = "~> 6.0"
190+
191+
function_name = local.name
192+
handler = "lambda_function.lambda_handler"
193+
runtime = "python3.10"
194+
source_path = "${path.module}/fixtures/lambda_function.py"
195+
}

0 commit comments

Comments
 (0)