Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/aws-ec2-privatelink/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ module "privatelink" {
vpc_id = module.rdi_quickstart_postgres.vpc_id
subnets = module.rdi_quickstart_postgres.vpc_public_subnets
target_type = "instance"
target = module.rdi_quickstart_postgres.instance_id
targets = [module.rdi_quickstart_postgres.instance_id]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allow 0 or more targets instead of requiring exactly 1. This way we can reuse the module for both examples.

security_groups = [module.rdi_quickstart_postgres.security_group_id]
allowed_principals = [var.redis_privatelink_arn]
}
Expand Down
8 changes: 8 additions & 0 deletions examples/aws-rds-privatelink/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.terraform
.idea
producer
producer-west
userdata.tgz
scripts
mysql.log
*.tfstate.*.backup
83 changes: 83 additions & 0 deletions examples/aws-rds-privatelink/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions examples/aws-rds-privatelink/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# AWS RDI RDS PrivateLink Demo

This directory contains example Terraform to connect Redis Cloud RDI to an Aurora Postgres RDS database and handle failover.

This blog post from AWS documents the architecture: https://aws.amazon.com/blogs/database/access-amazon-rds-across-vpcs-using-aws-privatelink-and-network-load-balancer/

## Setup

To use the example Terraform you must have:
- [Terraform](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli)
- [AWS CLI](https://aws.amazon.com/cli/)
- [AWS credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html)

Run `terraform init` to initialize the Terraform repository. This is only necessary the first time you use the repo.

## Usage

Copy the values from the Redis Cloud RDI UI into `example.tfvars`.

Run `terraform apply -var-file example.tfvars`

## Connecting to the database

You can connect to the postgres database directly from your laptop by running `./psql.sh`.

## Tearing down

Run `terraform destroy -var-file example.tfvars` to destroy the resources.

## Submodules

There are 4 submodules which can be reused:

- `aws-rds-chinook` - creates a VPC, Security Group and RDS database with 2 instances
- `aws-rds-lambda` - creates a Lambda function to update the Load Balancer target group based on SNS events from RDS
- `aws-privatelink` - creates a Network Load Balancer and PrivateLink Service Endpoint to permit connectivity from Redis Cloud to the database
- `aws-secret-manager` - creates a Secret Manager secret with IAM permissions to work with Redis Cloud
6 changes: 6 additions & 0 deletions examples/aws-rds-privatelink/example.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
region = "us-east-1"
azs = ["use1-az2", "use1-az4", "use1-az6"]
port = 5432
name = "rdi-rds"
redis_secrets_arn = ""
redis_privatelink_arn = ""
31 changes: 31 additions & 0 deletions examples/aws-rds-privatelink/inputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
variable "region" {
type = string
}

variable "port" {
type = number
}

variable "name" {
type = string
}

variable "redis_secrets_arn" {
type = string
validation {
condition = var.redis_secrets_arn != ""
error_message = "redis_secrets_arn must be configured with the ARN from the UI"
}
}

variable "redis_privatelink_arn" {
type = string
validation {
condition = var.redis_privatelink_arn != ""
error_message = "redis_privatelink_arn must be configured with the ARN from the UI"
}
}

variable "azs" {
type = list(string)
}
76 changes: 76 additions & 0 deletions examples/aws-rds-privatelink/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
terraform {
required_version = ">= 1.5.7"

backend "local" {
path = "producer/terraform.tfstate"
}

required_providers {
random = {
source = "hashicorp/random"
version = "~> 3.0"
}
}
}

provider "aws" {
# Configure the region for the resources
region = var.region
}

# Create an RDI quickstart Postgres database on RDS
module "rdi_quickstart_postgres" {
source = "../../modules/aws-rds-chinook"

identifier = var.name
db_password = random_password.pg_password.result
db_port = var.port
azs = var.azs
}

module "rds_lambda" {
source = "../../modules/aws-rds-lambda"
depends_on = [module.rdi_quickstart_postgres]

identifier = var.name
elb_tg_arn = module.privatelink.tg_arn
db_endpoint = module.rdi_quickstart_postgres.rds_endpoint
rds_arn = module.rdi_quickstart_postgres.rds_arn
db_port = var.port
}

# Create an NLB and PrivateLink Endpoint Service which allows secure connection to the database from Redis Cloud.
# This has no targets but we will add a Lambda function to update the target.
module "privatelink" {
source = "../../modules/aws-privatelink"

identifier = var.name
port = var.port
vpc_id = module.rdi_quickstart_postgres.vpc_id
subnets = module.rdi_quickstart_postgres.vpc_public_subnets
target_type = "ip"
targets = []
security_groups = [module.rdi_quickstart_postgres.security_group_id]
allowed_principals = [var.redis_privatelink_arn]
}

# Create a secret in AWS Secret Manager with the database credentials
module "secret_manager" {
source = "../../modules/aws-secret-manager"

# Because Secret Manager secrets are soft-deleted, add a random suffix to make the name unique.
# Otherwise running multiple apply-destroy cycles will fail because of the names conflicting.
identifier = "${var.name}-${random_id.secret_suffix.hex}"
allowed_principals = [var.redis_secrets_arn]
username = "postgres"
password = random_password.pg_password.result
}

resource "random_id" "secret_suffix" {
byte_length = 8
}

resource "random_password" "pg_password" {
length = 16
special = false
}
29 changes: 29 additions & 0 deletions examples/aws-rds-privatelink/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
output "vpc_endpoint_service_name" {
value = module.privatelink.vpc_endpoint_service_name
description = "The VPC Endpoint service name for the database, to be configured in Redis Cloud"
}

output "secret_arn" {
value = module.secret_manager.secret_arn
description = "The Secret Manager secret ARN, to be configured in Redis Cloud"
}

output "database" {
value = "chinook"
description = "The name of the Postgres reference database"
}

output "port" {
value = var.port
description = "The port for the NLB"
}

output "password" {
value = random_password.pg_password.result
sensitive = true
description = "The postgres password. This is not used for RDI setup, only to connect to the DB with psql"
}

output "psql_host" {
value = module.privatelink.lb_hostname
}
5 changes: 5 additions & 0 deletions examples/aws-rds-privatelink/psql.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

set -euo pipefail

psql "postgresql://postgres:$(terraform output -raw password)@$(terraform output -raw psql_host)/chinook"
19 changes: 19 additions & 0 deletions examples/aws-rds-privatelink/psql_setup.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
resource "null_resource" "setup_chinook" {
depends_on = [
module.rdi_quickstart_postgres,
module.rds_lambda
]
provisioner "local-exec" {
environment = {
PGPASSWORD : nonsensitive(random_password.pg_password.result)
}
command = <<EOF
#!/bin/sh
set -x
mkdir scripts
curl https://raw.githubusercontent.com/Redislabs-Solution-Architects/rdi-quickstart-postgres/refs/heads/main/scripts/Chinook_PostgreSql.sql -o scripts/Chinook_PostgreSql.sql
curl https://raw.githubusercontent.com/Redislabs-Solution-Architects/rdi-quickstart-postgres/refs/heads/main/scripts/track.csv -o scripts/track.csv
psql -h ${module.privatelink.lb_hostname} -d chinook -U postgres -p ${var.port} -f scripts/Chinook_PostgreSql.sql > mysql.log
EOF
}
}
6 changes: 3 additions & 3 deletions modules/aws-privatelink/inputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ variable "port" {
description = "The port to listen on and forward to in the target group"
}

variable "target" {
type = string
description = "The identifier of the load balancer target - this can be an ip or EC2 instance ID"
variable "targets" {
type = list(string)
description = "The identifier of the load balancer targets - this can be an ip or EC2 instance ID"
}

variable "target_type" {
Expand Down
4 changes: 3 additions & 1 deletion modules/aws-privatelink/load-balancer.tf
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ resource "aws_lb_target_group" "producer_tg" {

# Attach the EC2 instance to the target group
resource "aws_lb_target_group_attachment" "producer_tga" {
for_each = toset(var.targets)

target_group_arn = aws_lb_target_group.producer_tg.arn
target_id = var.target
target_id = each.value
port = var.port
}
8 changes: 8 additions & 0 deletions modules/aws-privatelink/output.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,11 @@ output "vpc_endpoint_service_id" {
value = aws_vpc_endpoint_service.producer_service.id
description = "The ID of the VPC endpoint service - source Private Link service ID"
}

output "lb_hostname" {
value = aws_lb.producer_nlb.dns_name
}

output "tg_arn" {
value = aws_lb_target_group.producer_tg.arn
}
4 changes: 4 additions & 0 deletions modules/aws-rds-chinook/data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Needed for subnet creation
data "aws_availability_zones" "available" {
state = "available"
}
44 changes: 44 additions & 0 deletions modules/aws-rds-chinook/input.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
variable "vpc_cidr" {
description = "The CIDR block for the VPC"
type = string
default = "10.0.0.0/20"
}

variable "azs" {
description = "A list of availability zones to deploy the EKS cluster into"
type = list(string)
default = ["use1-az2", "use1-az4", "use1-az6"]
}

variable "public_subnet_cidr" {
description = "The CIDR block for the public subnet"
type = list(string)
default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}

variable "private_subnet_cidr" {
description = "The CIDR block for the public subnet"
type = list(string)
default = ["10.0.7.0/24", "10.0.8.0/24", "10.0.9.0/24"]
}

variable "database_subnet_cidr" {
description = "The CIDR block for the public subnet"
type = list(string)
default = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
}

variable "identifier" {
description = "The identifier for the resources/test"
type = string
}

variable "db_password" {
description = "The password for connecting to the Producer Source Database"
type = string
}

variable "db_port" {
description = "The port for connecting to the Producer Source Database"
type = number
}
Loading