Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(awstf): Add SQL Database support #626

Merged
merged 3 commits into from
Jun 27, 2024
Merged
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
144 changes: 144 additions & 0 deletions cloud/aws/deploytf/.nitric/modules/rds/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Create a new random password for the RDS cluster
resource "random_password" "rds_password" {
length = 16
special = false
}

# Create a subnet group for the RDS instance
resource "aws_db_subnet_group" "rds_subnet_group" {
subnet_ids = var.private_subnet_ids
}

# Create a security group for the RDS instance
resource "aws_security_group" "rds_security_group" {
vpc_id = var.vpc_id

ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
self = true
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}

# Create a role for the codebuild job
resource "aws_iam_role" "codebuild_role" {
name = "nitric-codebuild-role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Service = "codebuild.amazonaws.com"
},
Action = "sts:AssumeRole"
}
]
})
}

# Attach managed policies to the codebuild role
locals {
codebuildManagedPolicies = {
"codeBuildAdmin" = "arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess"
"rdsAdmin" = "arn:aws:iam::aws:policy/AmazonRDSFullAccess"
"ec2Admin" = "arn:aws:iam::aws:policy/AmazonEC2FullAccess"
"cloudWatchLogs" = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
"ecrReadonly" = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
}
}

resource "aws_iam_role_policy_attachment" "codebuild_managed_policies" {
for_each = local.codebuildManagedPolicies

role = aws_iam_role.codebuild_role.name
policy_arn = each.value
}

# Create an RDS cluster with serverless v2
resource "aws_rds_cluster" "rds_cluster" {
cluster_identifier = "nitric-rds-cluster"
engine = "aurora-postgresql"
engine_mode = "provisioned"
engine_version = "13.14"
database_name = "nitric"
master_username = "nitric"
master_password = random_password.rds_password.result
db_subnet_group_name = aws_db_subnet_group.rds_subnet_group.id
vpc_security_group_ids = [aws_security_group.rds_security_group.id]
skip_final_snapshot = true
deletion_protection = false
serverlessv2_scaling_configuration {
max_capacity = var.max_capacity
min_capacity = var.min_capacity
}
}

# Create a rds cluster instance
resource "aws_rds_cluster_instance" "rds_cluster_instance" {
cluster_identifier = aws_rds_cluster.rds_cluster.id
instance_class = "db.serverless"
engine = aws_rds_cluster.rds_cluster.engine
engine_version = aws_rds_cluster.rds_cluster.engine_version
db_subnet_group_name = aws_rds_cluster.rds_cluster.db_subnet_group_name
}



# Create an AWS Codebuild job to create a database on the RDS cluster
resource "aws_codebuild_project" "create_database" {
name = "nitric-create-database"
description = "Create the database on the RDS cluster"
build_timeout = 60
service_role = aws_iam_role.codebuild_role.arn

artifacts {
type = "NO_ARTIFACTS"
}


environment {
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/amazonlinux2-x86_64-standard:4.0"
type = "LINUX_CONTAINER"

environment_variable {
name = "DB_PASSWORD"
value = random_password.rds_password.result
type = "PLAINTEXT"
}
}


vpc_config {
subnets = var.private_subnet_ids
security_group_ids = aws_rds_cluster.rds_cluster.vpc_security_group_ids
vpc_id = var.vpc_id
}

source {
type = "NO_SOURCE"
buildspec = jsonencode({
version = "0.2",
phases = {
build = {
commands = [
"echo 'Creating database $DB_NAME'",
# FIXME: Store the password in a secret manager
"export PGPASSWORD=$${DB_PASSWORD}",
# "CREATE DATABASE ${DB_NAME}" || echo "database ${DB_NAME} already exists"
"psql -h ${aws_rds_cluster.rds_cluster.endpoint} -U ${aws_rds_cluster.rds_cluster.master_username} -d ${aws_rds_cluster.rds_cluster.database_name} -c \"CREATE DATABASE $${DB_NAME}\" || echo \"database $${DB_NAME} already exists\""
]
}
}
})
}
}
29 changes: 29 additions & 0 deletions cloud/aws/deploytf/.nitric/modules/rds/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
output "cluster_endpoint" {
description = "The endpoint of the RDS cluster"
value = aws_rds_cluster.rds_cluster.endpoint
}

output "cluster_username" {
description = "The username to connect to the RDS cluster"
value = aws_rds_cluster.rds_cluster.master_username
}

output "cluster_password" {
description = "The password to connect to the RDS cluster"
value = aws_rds_cluster.rds_cluster.master_password
}

output "codebuild_role_arn" {
description = "The arn of the codebuild role"
value = aws_iam_role.codebuild_role.arn
}

output "security_group_id" {
description = "The security group id for the RDS cluster"
value = aws_security_group.rds_security_group.id
}

output "create_database_project_name" {
description = "The name of the create database codebuild project"
value = aws_codebuild_project.create_database.name
}
24 changes: 24 additions & 0 deletions cloud/aws/deploytf/.nitric/modules/rds/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
variable "vpc_id" {
type = string
description = "the VPC to assign to the RDS cluster"
}

variable "private_subnet_ids" {
type = list(string)
description = "private subnets to assign to the RDS cluster"
}

variable "min_capacity" {
type = number
description = "the minimum capacity of the RDS cluster"
}

variable "max_capacity" {
type = number
description = "the maximum capacity of the RDS cluster"
}

variable "stack_id" {
type = string
description = "The nitric stack ID"
}
23 changes: 15 additions & 8 deletions cloud/aws/deploytf/.nitric/modules/service/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,6 @@ resource "aws_ecr_repository" "repo" {
data "aws_ecr_authorization_token" "ecr_auth" {
}

provider "docker" {
registry_auth {
address = data.aws_ecr_authorization_token.ecr_auth.proxy_endpoint
username = data.aws_ecr_authorization_token.ecr_auth.user_name
password = data.aws_ecr_authorization_token.ecr_auth.password
}
}

# Tag the provided docker image with the ECR repository url
resource "docker_tag" "tag" {
source_image = var.image
Expand Down Expand Up @@ -85,6 +77,13 @@ resource "aws_iam_role_policy_attachment" "basic-execution" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

# Attach vpc access execution role if subnets are provided
resource "aws_iam_role_policy_attachment" "vpc-access" {
count = length(var.subnet_ids) > 0 ? 1 : 0
role = aws_iam_role.role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
}

# Create a lambda function using the pushed image
resource "aws_lambda_function" "function" {
function_name = "${var.service_name}-${var.stack_id}"
Expand All @@ -97,6 +96,14 @@ resource "aws_lambda_function" "function" {
variables = var.environment
}

dynamic "vpc_config" {
for_each = length(var.subnet_ids) > 0 ? ["1"] : []
content {
subnet_ids = var.subnet_ids
security_group_ids = var.security_group_ids
}
}

depends_on = [docker_registry_image.push]

tags = {
Expand Down
39 changes: 27 additions & 12 deletions cloud/aws/deploytf/.nitric/modules/service/variables.tf
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
variable "service_name" {
type = string
description = "The name of the service"
type = string
description = "The name of the service"
}

variable "image" {
type = string
description = "The docker image to deploy"
type = string
description = "The docker image to deploy"
}

# environment variables
variable "environment" {
type = map(string)
description = "Environment variables to set on the lambda function"
type = map(string)
description = "Environment variables to set on the lambda function"
}

variable "stack_id" {
Expand All @@ -20,13 +20,28 @@ variable "stack_id" {
}

variable "memory" {
description = "The amount of memory to allocate to the lambda function"
type = number
default = 128
description = "The amount of memory to allocate to the lambda function"
type = number
default = 128
}

variable "timeout" {
description = "The timeout for the lambda function"
type = number
default = 10
description = "The timeout for the lambda function"
type = number
default = 10
}

variable "subnet_ids" {
description = "The subnet ids to use for the aws lambda function"
type = list(string)
default = []
}

variable "security_group_ids" {
description = "The security group ids to use for the aws lambda function"
type = list(string)
default = []

}


Loading
Loading