Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 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: 2 additions & 0 deletions locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ locals {

selected_database = (
var.enable_aurora && var.db_use_mtls ? error("Both enable_aurora and db_use_mtls cannot be true.") :
var.enable_aurora && var.postgres_enable_iam_auth ? error("Both enable_aurora and postgres_enable_iam_auth cannot be true.") :
var.db_use_mtls && var.postgres_enable_iam_auth ? error("Both db_use_mtls and postgres_enable_iam_auth cannot be true.") :
var.enable_aurora ? local.aurora_database :
var.db_use_mtls ? local.mtls_database :
var.enable_edb ? local.enterprise_db :
Expand Down
59 changes: 31 additions & 28 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -161,21 +161,22 @@ module "database" {
source = "./modules/database"
count = local.enable_database_module ? 1 : 0

db_size = var.db_size
db_backup_retention = var.db_backup_retention
db_backup_window = var.db_backup_window
db_name = var.db_name
db_parameters = var.db_parameters
db_username = var.db_username
engine_version = var.postgres_engine_version
friendly_name_prefix = var.friendly_name_prefix
network_id = local.network_id
network_private_subnet_cidrs = var.network_private_subnet_cidrs
network_subnets_private = local.network_private_subnets
tfe_instance_sg = module.vm.tfe_instance_sg
kms_key_arn = local.kms_key_arn
allow_major_version_upgrade = var.allow_major_version_upgrade
allow_multiple_azs = var.allow_multiple_azs
db_size = var.db_size
db_backup_retention = var.db_backup_retention
db_backup_window = var.db_backup_window
db_name = var.db_name
db_parameters = var.db_parameters
db_username = var.db_username
engine_version = var.postgres_engine_version
friendly_name_prefix = var.friendly_name_prefix
network_id = local.network_id
network_private_subnet_cidrs = var.network_private_subnet_cidrs
network_subnets_private = local.network_private_subnets
tfe_instance_sg = module.vm.tfe_instance_sg
kms_key_arn = local.kms_key_arn
allow_major_version_upgrade = var.allow_major_version_upgrade
allow_multiple_azs = var.allow_multiple_azs
enable_iam_database_authentication = var.postgres_enable_iam_auth && !var.postgres_use_password_auth
}

# -----------------------------------------------------------------------------
Expand Down Expand Up @@ -253,7 +254,7 @@ module "aurora_database" {
# Docker Compose File Config for TFE on instance(s) using Flexible Deployment Options
# ------------------------------------------------------------------------------------
module "runtime_container_engine_config" {
source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/runtime_container_engine_config?ref=main"
source = "./modules/runtime_container_engine_config"
count = var.is_replicated_deployment ? 0 : 1

tfe_license = var.hc_license
Expand Down Expand Up @@ -286,15 +287,17 @@ module "runtime_container_engine_config" {
iact_time_limit = var.iact_subnet_time_limit
run_pipeline_image = var.run_pipeline_image

database_name = local.database.name
database_user = local.database.username
database_password = local.database.password
database_host = local.database.endpoint
database_parameters = local.database.parameters
database_use_mtls = var.db_use_mtls
database_ca_cert_file = "/etc/ssl/private/terraform-enterprise/postgres/ca.crt"
database_client_cert_file = "/etc/ssl/private/terraform-enterprise/postgres/cert.crt"
database_client_key_file = "/etc/ssl/private/terraform-enterprise/postgres/key.key"
database_name = local.database.name
database_user = local.database.username
database_password = local.database.password
database_host = local.database.endpoint
database_parameters = local.database.parameters
database_use_mtls = var.db_use_mtls
database_ca_cert_file = "/etc/ssl/private/terraform-enterprise/postgres/ca.crt"
database_client_cert_file = "/etc/ssl/private/terraform-enterprise/postgres/cert.crt"
database_client_key_file = "/etc/ssl/private/terraform-enterprise/postgres/key.key"
database_passwordless_aws_use_iam = var.postgres_enable_iam_auth && !var.postgres_use_password_auth
database_passwordless_aws_region = var.postgres_enable_iam_auth && !var.postgres_use_password_auth ? data.aws_region.current.name : ""

explorer_database_name = local.explorer_database.name
explorer_database_user = local.explorer_database.username
Expand Down Expand Up @@ -343,7 +346,7 @@ module "runtime_container_engine_config" {
# AWS cloud init used to install and configure TFE on instance(s) using Flexible Deployment Options
# --------------------------------------------------------------------------------------------------
module "tfe_init_fdo" {
source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/tfe_init?ref=main"
source = "./modules/tfe_init"
count = var.is_replicated_deployment ? 0 : 1

cloud = "aws"
Expand Down Expand Up @@ -388,7 +391,7 @@ module "tfe_init_fdo" {
# TFE and Replicated settings to pass to the tfe_init_replicated module for replicated deployment
# --------------------------------------------------------------------------------------------
module "settings" {
source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/settings?ref=main"
source = "./modules/settings"
count = var.is_replicated_deployment ? 1 : 0

# TFE Base Configuration
Expand Down Expand Up @@ -450,7 +453,7 @@ module "settings" {
# AWS user data / cloud init used to install and configure TFE on instance(s)
# -----------------------------------------------------------------------------
module "tfe_init_replicated" {
source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/tfe_init_replicated?ref=main"
source = "./modules/tfe_init_replicated"
count = var.is_replicated_deployment ? 1 : 0

# TFE & Replicated Configuration data
Expand Down
3 changes: 3 additions & 0 deletions modules/database/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,7 @@ resource "aws_db_instance" "postgresql" {
kms_key_id = var.kms_key_arn
storage_type = "gp2"
vpc_security_group_ids = [aws_security_group.postgresql.id]

# Enable IAM database authentication if requested
iam_database_authentication_enabled = var.enable_iam_database_authentication
}
6 changes: 6 additions & 0 deletions modules/database/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,9 @@ variable "allow_multiple_azs" {
description = "Determine Amazon RDS Postgres deployment strategy."
default = true
}

variable "enable_iam_database_authentication" {
type = bool
description = "Enable IAM database authentication for the RDS instance."
default = false
}
23 changes: 23 additions & 0 deletions modules/postgres-passwordless/data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

data "aws_ami" "ubuntu" {
most_recent = true

filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}

filter {
name = "virtualization-type"
values = ["hvm"]
}

owners = ["099720109477"] # Canonical
}

data "aws_route53_zone" "postgres_zone" {
name = var.domain_name
private_zone = false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/bash
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

set -eu pipefail

apt-get update -y && apt-get install -y docker.io postgresql-client openssl unzip jq
systemctl enable --now docker
usermod -aG docker ubuntu

curl -sS --noproxy '*' "https://awscli.amazonaws.com/awscli-exe-linux-$(uname -m | grep -q 'arm\|aarch' && echo 'aarch64' || echo 'x86_64').zip" -o "awscliv2.zip" > /dev/null 2>&1
unzip -q awscliv2.zip > /dev/null 2>&1
./aws/install > /dev/null 2>&1
rm -rf aws awscliv2.zip > /dev/null 2>&1

# For passwordless postgres, we start with basic configuration
# IAM authentication will be handled at the RDS level
docker run -d \
--name postgres \
-p 5432:5432 \
-e POSTGRES_USER="$POSTGRES_USER" \
-e POSTGRES_PASSWORD="$POSTGRES_PASSWORD" \
-e POSTGRES_DB="$POSTGRES_DB" \
postgres:16

# Wait until PostgreSQL is up
echo "Waiting for PostgreSQL to become ready..."
timeout=180
start=$(date +%s)
while ! docker exec postgres pg_isready -U "$POSTGRES_USER" >/dev/null 2>&1; do
sleep 1
[[ $(( $(date +%s) - start )) -gt $timeout ]] && echo "Timeout waiting for PostgreSQL" && docker logs postgres && exit 1
done

echo "PostgreSQL with passwordless authentication is fully up and running."
113 changes: 113 additions & 0 deletions modules/postgres-passwordless/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

# This module provisions a PostgreSQL instance with passwordless authentication (IAM or similar)
# Adapted from database-mtls module, but without client certs/keys and with passwordless config

resource "random_string" "postgres_db_password" {
length = 128
special = true
override_special = "#$%&*"
}

resource "aws_route53_record" "postgres_db_dns" {
zone_id = data.aws_route53_zone.postgres_zone.zone_id
name = "${var.friendly_name_prefix}-postgres-passwordless"
type = "A"
ttl = 300

records = [aws_instance.postgres_db_instance.public_ip]
}

resource "aws_security_group" "postgres_db_sg" {
description = "The security group of the PostgreSQL deployment for TFE."
name = "${var.friendly_name_prefix}-postgres-passwordless"
vpc_id = var.network_id
}

resource "aws_security_group_rule" "postgres_db_ingress" {
security_group_id = aws_security_group.postgres_db_sg.id
type = "ingress"
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

resource "aws_security_group_rule" "postgres_db_ssh_ingress" {
security_group_id = aws_security_group.postgres_db_sg.id
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

resource "aws_security_group_rule" "postgres_db_egress" {
security_group_id = aws_security_group.postgres_db_sg.id
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

resource "aws_instance" "postgres_db_instance" {
ami = data.aws_ami.ubuntu.id
instance_type = "m5.xlarge"
associate_public_ip_address = true
vpc_security_group_ids = [aws_security_group.postgres_db_sg.id]
iam_instance_profile = var.aws_iam_instance_profile
key_name = aws_key_pair.ec2_key.key_name
subnet_id = var.network_public_subnets[0]
root_block_device {
volume_type = "gp3"
volume_size = 100
delete_on_termination = true
encrypted = true
}

tags = {
Name = "Terraform-Postgres-Passwordless"
}
}

resource "local_file" "postgres_db_private_key" {
content = tls_private_key.postgres_db_ssh_key.private_key_pem
filename = "${path.module}/${var.friendly_name_prefix}-ec2-postgres-key.pem"
file_permission = "0600"
}

resource "tls_private_key" "postgres_db_ssh_key" {
algorithm = "RSA"
rsa_bits = 4096
}

resource "aws_key_pair" "ec2_key" {
key_name = "${var.friendly_name_prefix}-ec2-postgres-key"
public_key = tls_private_key.postgres_db_ssh_key.public_key_openssh
}

resource "null_resource" "postgres_db_server_start" {
depends_on = [aws_route53_record.postgres_db_dns]

connection {
type = "ssh"
user = "ubuntu"
private_key = tls_private_key.postgres_db_ssh_key.private_key_pem
host = aws_route53_record.postgres_db_dns.fqdn
}

provisioner "file" {
source = "${path.module}/files/fetch_cert_and_start_server.sh"
destination = "/home/ubuntu/fetch_cert_and_start_server.sh"
}

provisioner "remote-exec" {
inline = [
"sleep 60",
"chmod +x /home/ubuntu/fetch_cert_and_start_server.sh",
"sudo POSTGRES_PASSWORD='${random_string.postgres_db_password.result}' POSTGRES_USER=${var.db_username} POSTGRES_DB=${var.db_name} /home/ubuntu/fetch_cert_and_start_server.sh"
]
}
}
45 changes: 45 additions & 0 deletions modules/postgres-passwordless/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

output "endpoint" {
description = "The connection endpoint of the PostgreSQL instance in address:port format."
value = aws_route53_record.postgres_db_dns.fqdn
}

output "name" {
description = "The name of the PostgreSQL instance."
value = var.db_name
}

output "password" {
description = "The password of the main PostgreSQL user."
value = random_string.postgres_db_password.result
sensitive = true
}

output "username" {
description = "The name of the main PostgreSQL user."
value = var.db_username
}

output "parameters" {
description = "PostgreSQL server parameters for the connection URI."
value = var.db_parameters
}

# Legacy outputs for backward compatibility
output "postgres_db_endpoint" {
description = "The endpoint of the PostgreSQL instance."
value = aws_route53_record.postgres_db_dns.fqdn
}

output "postgres_db_sg_id" {
description = "The security group ID for the PostgreSQL instance."
value = aws_security_group.postgres_db_sg.id
}

output "postgres_db_password" {
description = "The password for the PostgreSQL instance."
value = random_string.postgres_db_password.result
sensitive = true
}
14 changes: 14 additions & 0 deletions modules/postgres-passwordless/service_accounts/data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

data "aws_iam_instance_profile" "existing_instance_profile" {
count = var.existing_iam_instance_profile_name != null ? 1 : 0

name = var.existing_iam_instance_profile_name
}

data "aws_iam_role" "existing_instance_role" {
count = var.existing_iam_instance_role_name != null ? 1 : 0

name = var.existing_iam_instance_role_name
}
7 changes: 7 additions & 0 deletions modules/postgres-passwordless/service_accounts/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

locals {
iam_instance_role = try(data.aws_iam_role.existing_instance_role[0], aws_iam_role.instance_role[0])
iam_instance_profile = try(data.aws_iam_instance_profile.existing_instance_profile[0], aws_iam_instance_profile.postgres_passwordless[0])
}
Loading
Loading