|
variable "database_password" { |
|
type = "string" |
|
} |
|
variable "subnet_ids" { |
|
description = "Comma-delimited string of subnet ids" |
|
type = "string" |
|
} |
|
variable "security_group_id" { |
|
type = "string" |
|
} |
|
|
|
provider "archive" { |
|
version = "~> 1.1" |
|
} |
|
|
|
provider "aws" { |
|
version = "~> 1.23" |
|
} |
|
|
|
locals { |
|
# Solution from this comment to open issue on non-relative paths |
|
# https://github.com/hashicorp/terraform/issues/8204#issuecomment-332239294 |
|
|
|
# +1 for removing the "/" |
|
creation_filename = "${substr(data.archive_file.rds_creation_zip.output_path, length(path.cwd) + 1, -1)}" |
|
setup_filename = "${substr(data.archive_file.rds_setup_zip.output_path, length(path.cwd) + 1, -1)}" |
|
} |
|
|
|
data "archive_file" "rds_creation_zip" { |
|
type = "zip" |
|
output_path = "${path.module}/rds_creation.zip" |
|
source_dir = "${path.module}/rds_creation/" |
|
} |
|
|
|
data "archive_file" "rds_setup_zip" { |
|
type = "zip" |
|
output_path = "${path.module}/rds_setup.zip" |
|
source_dir = "${path.module}/rds_setup/" |
|
} |
|
|
|
# The SQL script that gets evaluated on new database instances. |
|
# Clearly, you will want to change this! |
|
data "template_file" "sql_script" { |
|
template = <<SQL |
|
SELECT 1; |
|
SQL |
|
} |
|
|
|
# Subscribe to all new database creation notifications |
|
resource "aws_db_event_subscription" "creation" { |
|
name = "rds-creation" |
|
sns_topic = "${aws_sns_topic.rds.arn}" |
|
source_type = "db-instance" |
|
event_categories = ["creation"] |
|
} |
|
|
|
# 'External' Lambda function that gets the new database SNS notification |
|
# and queries the AWS API to obtain further details about this. |
|
# |
|
# It then sends those details off to another SNS notification, which is |
|
# picked up by the 'internal' Lambda function. |
|
resource "aws_lambda_function" "rds_creation" { |
|
function_name = "rds-creation" |
|
handler = "index.handler" |
|
filename = "${local.creation_filename}" |
|
source_code_hash = "${base64sha256(file("${local.creation_filename}"))}" |
|
|
|
role = "${aws_iam_role.rds_external_lambda.arn}" |
|
runtime = "nodejs8.10" |
|
timeout = 10 |
|
|
|
environment { |
|
variables { |
|
SNS_TOPIC_ARN = "${aws_sns_topic.internal.arn}" |
|
} |
|
} |
|
} |
|
|
|
# 'Internal' Lambda function which receives database information from |
|
# the external function (via SNS) and then connects to the database |
|
# and evaluates the script against it. |
|
# |
|
# This operates within the VPC, and hence does not have access to the |
|
# internet or AWS APIs. |
|
resource "aws_lambda_function" "rds_setup" { |
|
function_name = "rds-setup" |
|
handler = "index.handler" |
|
filename = "${local.setup_filename}" |
|
source_code_hash = "${base64sha256(file("${local.setup_filename}"))}" |
|
|
|
role = "${aws_iam_role.rds_internal_lambda.arn}" |
|
runtime = "nodejs8.10" |
|
timeout = 10 |
|
|
|
vpc_config { |
|
subnet_ids = ["${split(",", var.subnet_ids)}"] |
|
security_group_ids = ["${var.security_group_id}"] |
|
} |
|
|
|
environment { |
|
variables = { |
|
PGPASSWORD = "${var.database_password}" |
|
SQL_SCRIPT = "${replace(trimspace(data.template_file.sql_script.rendered), "/\n/", " ")}" |
|
} |
|
} |
|
} |
|
|
|
resource "aws_lambda_permission" "rds_creation" { |
|
statement_id = "AllowExecutionFromSNS" |
|
action = "lambda:InvokeFunction" |
|
function_name = "${aws_lambda_function.rds_creation.function_name}" |
|
principal = "sns.amazonaws.com" |
|
source_arn = "${aws_sns_topic.rds.arn}" |
|
} |
|
|
|
resource "aws_lambda_permission" "rds_setup" { |
|
statement_id = "AllowExecutionFromSNS" |
|
action = "lambda:InvokeFunction" |
|
function_name = "${aws_lambda_function.rds_setup.function_name}" |
|
principal = "sns.amazonaws.com" |
|
source_arn = "${aws_sns_topic.internal.arn}" |
|
} |
|
|
|
# SNS Topic for new database creations (via RDS events) |
|
resource "aws_sns_topic" "rds" { |
|
name = "rds-creation" |
|
} |
|
|
|
# SNS Topic for database credentials (via the external lambda) |
|
resource "aws_sns_topic" "internal" { |
|
name = "rds-setup" |
|
} |
|
|
|
# Subscriptions connecting topics to lambdas |
|
resource "aws_sns_topic_subscription" "rds" { |
|
topic_arn = "${aws_sns_topic.rds.arn}" |
|
protocol = "lambda" |
|
endpoint = "${aws_lambda_function.rds_creation.arn}" |
|
} |
|
|
|
resource "aws_sns_topic_subscription" "rds_internal" { |
|
topic_arn = "${aws_sns_topic.internal.arn}" |
|
protocol = "lambda" |
|
endpoint = "${aws_lambda_function.rds_setup.arn}" |
|
} |
|
|
|
# IAM Role for 'External' lambda which has access to |
|
# CloudWatch, SNS, and RDS. |
|
resource "aws_iam_role" "rds_external_lambda" { |
|
name = "RDSExternal" |
|
|
|
assume_role_policy = <<EOF |
|
{ |
|
"Version": "2012-10-17", |
|
"Statement": [ |
|
{ |
|
"Action": "sts:AssumeRole", |
|
"Principal": { |
|
"Service": "lambda.amazonaws.com" |
|
}, |
|
"Effect": "Allow" |
|
} |
|
] |
|
} |
|
EOF |
|
} |
|
|
|
# IAM Role for 'Internal' lambda which has access to |
|
# CloudWatch and VPC behaviour. |
|
resource "aws_iam_role" "rds_internal_lambda" { |
|
name = "RDSInternal" |
|
|
|
assume_role_policy = <<EOF |
|
{ |
|
"Version": "2012-10-17", |
|
"Statement": [ |
|
{ |
|
"Action": "sts:AssumeRole", |
|
"Principal": { |
|
"Service": "lambda.amazonaws.com" |
|
}, |
|
"Effect": "Allow" |
|
} |
|
] |
|
} |
|
EOF |
|
} |
|
|
|
resource "aws_iam_role_policy" "rds_internal" { |
|
name = "RDSInternalNotifications" |
|
role = "${aws_iam_role.rds_internal_lambda.id}" |
|
policy = <<EOF |
|
{ |
|
"Version": "2012-10-17", |
|
"Statement": [{ |
|
"Effect": "Allow", |
|
"Action": [ |
|
"logs:CreateLogGroup", |
|
"logs:CreateLogStream", |
|
"logs:PutLogEvents" |
|
], |
|
"Resource": "*" |
|
}] |
|
} |
|
EOF |
|
} |
|
|
|
resource "aws_iam_role_policy" "rds_external" { |
|
name = "RDSExternalNotifications" |
|
role = "${aws_iam_role.rds_external_lambda.id}" |
|
policy = <<EOF |
|
{ |
|
"Version": "2012-10-17", |
|
"Statement": [{ |
|
"Effect": "Allow", |
|
"Action": [ |
|
"logs:CreateLogGroup", |
|
"logs:CreateLogStream", |
|
"logs:PutLogEvents", |
|
"rds:DescribeDBInstances", |
|
"sns:Publish" |
|
], |
|
"Resource": "*" |
|
}] |
|
} |
|
EOF |
|
} |
|
|
|
resource "aws_iam_role_policy_attachment" "rds_lambda_vpc" { |
|
role = "${aws_iam_role.rds_internal_lambda.id}" |
|
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" |
|
} |