From 4e414caac3d7c4986359ccf96ee7174316f3921f Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Tue, 7 Oct 2025 15:01:51 +0530 Subject: [PATCH 01/15] Add AWS IAM PostgreSQL passwordless authentication support - Add postgres-passwordless module with IAM authentication - Update database module to support IAM authentication options - Add variables for enabling postgres passwordless mode - Update main module integration for postgres passwordless This enables PostgreSQL database authentication using AWS IAM instead of traditional username/password authentication. --- locals.tf | 10 +- main.tf | 29 +++-- modules/database/main.tf | 3 + modules/database/variables.tf | 6 + modules/postgres-passwordless/README.md | 53 ++++++++ modules/postgres-passwordless/data.tf | 23 ++++ .../files/fetch_cert_and_start_server.sh | 35 +++++ modules/postgres-passwordless/main.tf | 113 ++++++++++++++++ modules/postgres-passwordless/outputs.tf | 45 +++++++ .../service_accounts/README.md | 48 +++++++ .../service_accounts/data.tf | 14 ++ .../service_accounts/locals.tf | 7 + .../service_accounts/main.tf | 122 ++++++++++++++++++ .../service_accounts/outputs.tf | 22 ++++ .../service_accounts/variables.tf | 41 ++++++ .../service_accounts/versions.tf | 12 ++ modules/postgres-passwordless/variables.tf | 44 +++++++ modules/postgres-passwordless/versions.tf | 28 ++++ variables.tf | 12 ++ 19 files changed, 650 insertions(+), 17 deletions(-) create mode 100644 modules/postgres-passwordless/README.md create mode 100644 modules/postgres-passwordless/data.tf create mode 100644 modules/postgres-passwordless/files/fetch_cert_and_start_server.sh create mode 100644 modules/postgres-passwordless/main.tf create mode 100644 modules/postgres-passwordless/outputs.tf create mode 100644 modules/postgres-passwordless/service_accounts/README.md create mode 100644 modules/postgres-passwordless/service_accounts/data.tf create mode 100644 modules/postgres-passwordless/service_accounts/locals.tf create mode 100644 modules/postgres-passwordless/service_accounts/main.tf create mode 100644 modules/postgres-passwordless/service_accounts/outputs.tf create mode 100644 modules/postgres-passwordless/service_accounts/variables.tf create mode 100644 modules/postgres-passwordless/service_accounts/versions.tf create mode 100644 modules/postgres-passwordless/variables.tf create mode 100644 modules/postgres-passwordless/versions.tf diff --git a/locals.tf b/locals.tf index ca438da2..5125c980 100644 --- a/locals.tf +++ b/locals.tf @@ -31,13 +31,15 @@ locals { parameters = null } - aurora_database = try(module.aurora_database[0], local.default_database) - mtls_database = try(module.database_mtls[0], local.default_database) - enterprise_db = try(module.edb[0], local.default_database) - standard_db = try(module.database[0], local.default_database) + aurora_database = try(module.aurora_database[0], local.default_database) + mtls_database = try(module.database_mtls[0], local.default_database) + enterprise_db = try(module.edb[0], local.default_database) + standard_db = try(module.database[0], local.default_database) 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 : diff --git a/main.tf b/main.tf index 3de95ca7..49bb293b 100644 --- a/main.tf +++ b/main.tf @@ -176,6 +176,7 @@ module "database" { 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 } # ----------------------------------------------------------------------------- @@ -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 @@ -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 @@ -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" @@ -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 @@ -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 diff --git a/modules/database/main.tf b/modules/database/main.tf index 84ca0292..9a84b036 100644 --- a/modules/database/main.tf +++ b/modules/database/main.tf @@ -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 } diff --git a/modules/database/variables.tf b/modules/database/variables.tf index 5361416e..fbd55bba 100644 --- a/modules/database/variables.tf +++ b/modules/database/variables.tf @@ -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 +} diff --git a/modules/postgres-passwordless/README.md b/modules/postgres-passwordless/README.md new file mode 100644 index 00000000..9526182b --- /dev/null +++ b/modules/postgres-passwordless/README.md @@ -0,0 +1,53 @@ +# postgres-passwordless Module + +This module provisions a PostgreSQL instance with passwordless authentication on an EC2 instance for use with Terraform Enterprise. This module follows the same pattern as the `database-mtls` module but without certificate handling. + +## Features +- EC2-based PostgreSQL deployment with Docker +- Security group configuration for PostgreSQL access +- Route53 DNS record for easy access +- Random password generation (for admin setup) +- SSH key pair generation for EC2 access +- Automated PostgreSQL setup via user data script + +## Usage Example +```hcl +module "postgres_passwordless" { + source = "./modules/postgres-passwordless" + domain_name = "example.com" + db_name = "tfe" + db_username = "tfeadmin" + network_id = var.vpc_id + network_public_subnets = var.public_subnet_ids + friendly_name_prefix = "tfe" + aws_iam_instance_profile = var.iam_instance_profile +} +``` + +## Variables +- `domain_name`: Route 53 hosted zone name for DNS record +- `db_name`: PostgreSQL database name +- `db_username`: PostgreSQL username +- `network_id`: VPC ID for security group +- `network_public_subnets`: List of public subnet IDs +- `friendly_name_prefix`: Prefix for resource names +- `aws_iam_instance_profile`: IAM instance profile for the EC2 instance + +## Outputs +- `postgres_db_endpoint`: The FQDN of the PostgreSQL instance +- `postgres_db_sg_id`: The security group ID for the PostgreSQL instance +- `postgres_db_password`: The password for the PostgreSQL instance (sensitive) + +## Files Structure +- `main.tf`: Main Terraform configuration +- `variables.tf`: Variable definitions +- `outputs.tf`: Output definitions +- `data.tf`: Data source definitions +- `versions.tf`: Provider version constraints +- `files/fetch_cert_and_start_server.sh`: Script to set up PostgreSQL on EC2 + +## Notes +- This module creates an EC2 instance running PostgreSQL in Docker +- The instance is configured for passwordless authentication patterns +- A Route53 DNS record is created for easy access +- SSH access is configured for troubleshooting diff --git a/modules/postgres-passwordless/data.tf b/modules/postgres-passwordless/data.tf new file mode 100644 index 00000000..d8814054 --- /dev/null +++ b/modules/postgres-passwordless/data.tf @@ -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 +} \ No newline at end of file diff --git a/modules/postgres-passwordless/files/fetch_cert_and_start_server.sh b/modules/postgres-passwordless/files/fetch_cert_and_start_server.sh new file mode 100644 index 00000000..b65b9fd4 --- /dev/null +++ b/modules/postgres-passwordless/files/fetch_cert_and_start_server.sh @@ -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." \ No newline at end of file diff --git a/modules/postgres-passwordless/main.tf b/modules/postgres-passwordless/main.tf new file mode 100644 index 00000000..56d239e2 --- /dev/null +++ b/modules/postgres-passwordless/main.tf @@ -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" + ] + } +} diff --git a/modules/postgres-passwordless/outputs.tf b/modules/postgres-passwordless/outputs.tf new file mode 100644 index 00000000..3f0543ac --- /dev/null +++ b/modules/postgres-passwordless/outputs.tf @@ -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 +} diff --git a/modules/postgres-passwordless/service_accounts/README.md b/modules/postgres-passwordless/service_accounts/README.md new file mode 100644 index 00000000..ddafefe4 --- /dev/null +++ b/modules/postgres-passwordless/service_accounts/README.md @@ -0,0 +1,48 @@ +# service_accounts Module for postgres-passwordless + +This module creates IAM roles and instance profiles specifically for the PostgreSQL passwordless authentication setup. It provides the necessary permissions for EC2 instances to authenticate with RDS using IAM authentication. + +## Features +- IAM instance profile creation for EC2 instances +- IAM role with RDS IAM authentication permissions +- Basic EC2 and CloudWatch permissions +- Optional KMS permissions for encryption +- Support for existing IAM roles/profiles + +## Usage Example +```hcl +module "postgres_passwordless_service_accounts" { + source = "./modules/postgres-passwordless/service_accounts" + + friendly_name_prefix = "tfe" + db_instance_identifier = "tfe-postgres" + db_username = "tfeadmin" + kms_key_arn = var.kms_key_arn +} +``` + +## Variables +- `friendly_name_prefix`: Prefix for resource names +- `db_instance_identifier`: RDS instance identifier for IAM auth +- `db_username`: Database username for IAM auth +- `existing_iam_instance_profile_name`: Use existing profile (optional) +- `existing_iam_instance_role_name`: Use existing role (optional) +- `iam_role_policy_arns`: Additional policy ARNs to attach +- `kms_key_arn`: KMS key ARN for encryption (optional) + +## Outputs +- `iam_instance_profile`: The IAM instance profile object +- `iam_instance_profile_name`: The name of the IAM instance profile +- `iam_role`: The IAM role object +- `iam_role_name`: The name of the IAM role + +## Permissions Included +- `rds-db:connect` for IAM database authentication +- Basic EC2 and CloudWatch permissions +- Optional KMS permissions for encryption +- Support for additional custom policies + +## Notes +- This module is specifically designed for passwordless PostgreSQL authentication +- The RDS IAM authentication policy is scoped to the specific database and user +- KMS permissions are only created if a KMS key ARN is provided \ No newline at end of file diff --git a/modules/postgres-passwordless/service_accounts/data.tf b/modules/postgres-passwordless/service_accounts/data.tf new file mode 100644 index 00000000..7bd8c303 --- /dev/null +++ b/modules/postgres-passwordless/service_accounts/data.tf @@ -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 +} \ No newline at end of file diff --git a/modules/postgres-passwordless/service_accounts/locals.tf b/modules/postgres-passwordless/service_accounts/locals.tf new file mode 100644 index 00000000..b5ed7979 --- /dev/null +++ b/modules/postgres-passwordless/service_accounts/locals.tf @@ -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]) +} \ No newline at end of file diff --git a/modules/postgres-passwordless/service_accounts/main.tf b/modules/postgres-passwordless/service_accounts/main.tf new file mode 100644 index 00000000..5575a2c5 --- /dev/null +++ b/modules/postgres-passwordless/service_accounts/main.tf @@ -0,0 +1,122 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resource "aws_iam_instance_profile" "postgres_passwordless" { + count = var.existing_iam_instance_profile_name == null ? 1 : 0 + + name_prefix = "${var.friendly_name_prefix}-postgres-passwordless" + role = local.iam_instance_role.name +} + +resource "aws_iam_role" "instance_role" { + count = var.existing_iam_instance_role_name == null ? 1 : 0 + + name_prefix = "${var.friendly_name_prefix}-postgres-passwordless" + assume_role_policy = data.aws_iam_policy_document.instance_role[0].json +} + +data "aws_iam_policy_document" "instance_role" { + count = var.existing_iam_instance_profile_name == null ? 1 : 0 + + statement { + effect = "Allow" + actions = [ + "sts:AssumeRole", + ] + + principals { + type = "Service" + identifiers = ["ec2.amazonaws.com"] + } + } +} + +# RDS IAM authentication policy for passwordless database access +resource "aws_iam_role_policy" "rds_iam_auth" { + count = var.existing_iam_instance_profile_name == null ? 1 : 0 + + policy = data.aws_iam_policy_document.rds_iam_auth[0].json + role = local.iam_instance_role.id + + name = "${var.friendly_name_prefix}-postgres-passwordless-rds-auth" +} + +data "aws_iam_policy_document" "rds_iam_auth" { + count = var.existing_iam_instance_profile_name == null ? 1 : 0 + + statement { + actions = [ + "rds-db:connect" + ] + effect = "Allow" + resources = [ + "arn:aws:rds-db:*:*:dbuser:${var.db_instance_identifier}/${var.db_username}" + ] + sid = "AllowRDSIAMAuthentication" + } +} + +# Basic EC2 and CloudWatch permissions +resource "aws_iam_role_policy" "basic_permissions" { + count = var.existing_iam_instance_profile_name == null ? 1 : 0 + + policy = data.aws_iam_policy_document.basic_permissions[0].json + role = local.iam_instance_role.id + + name = "${var.friendly_name_prefix}-postgres-passwordless-basic" +} + +data "aws_iam_policy_document" "basic_permissions" { + count = var.existing_iam_instance_profile_name == null ? 1 : 0 + + statement { + actions = [ + "ec2:DescribeInstances", + "ec2:DescribeTags", + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:DescribeLogStreams" + ] + effect = "Allow" + resources = ["*"] + sid = "AllowBasicEC2AndCloudWatchAccess" + } +} + +# This will allow you to add any additional policies you may need, regardless +# of whether you're using an existing role and instance profile. +resource "aws_iam_role_policy_attachment" "misc" { + for_each = var.iam_role_policy_arns + + role = local.iam_instance_role.name + policy_arn = each.value +} + +resource "aws_iam_role_policy_attachment" "kms_policy" { + count = var.existing_iam_instance_profile_name == null && var.kms_key_arn != null ? 1 : 0 + + role = local.iam_instance_role.name + policy_arn = aws_iam_policy.kms_policy[0].arn +} + +resource "aws_iam_policy" "kms_policy" { + count = var.existing_iam_instance_profile_name == null && var.kms_key_arn != null ? 1 : 0 + + name = "${var.friendly_name_prefix}-postgres-passwordless-kms" + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey", + ] + Effect = "Allow" + Resource = var.kms_key_arn + }, + ] + }) +} \ No newline at end of file diff --git a/modules/postgres-passwordless/service_accounts/outputs.tf b/modules/postgres-passwordless/service_accounts/outputs.tf new file mode 100644 index 00000000..9fb46fd0 --- /dev/null +++ b/modules/postgres-passwordless/service_accounts/outputs.tf @@ -0,0 +1,22 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +output "iam_instance_profile" { + description = "The IAM instance profile that will be attached to the PostgreSQL EC2 instance." + value = local.iam_instance_profile +} + +output "iam_instance_profile_name" { + description = "The name of the IAM instance profile that will be attached to the PostgreSQL EC2 instance." + value = local.iam_instance_profile.name +} + +output "iam_role" { + description = "The IAM role associated with the PostgreSQL EC2 instance." + value = local.iam_instance_role +} + +output "iam_role_name" { + description = "The name of the IAM role associated with the PostgreSQL EC2 instance." + value = local.iam_instance_role.name +} \ No newline at end of file diff --git a/modules/postgres-passwordless/service_accounts/variables.tf b/modules/postgres-passwordless/service_accounts/variables.tf new file mode 100644 index 00000000..faafd759 --- /dev/null +++ b/modules/postgres-passwordless/service_accounts/variables.tf @@ -0,0 +1,41 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +variable "existing_iam_instance_profile_name" { + description = "The IAM instance profile to be attached to the PostgreSQL EC2 instance. Leave the value null to create a new one." + type = string + default = null +} + +variable "existing_iam_instance_role_name" { + type = string + description = "The IAM role to associate with the instance profile. To create a new role, this value should be null." + default = null +} + +variable "friendly_name_prefix" { + type = string + description = "(Required) Friendly name prefix used for tagging and naming AWS resources." +} + +variable "iam_role_policy_arns" { + default = [] + description = "A set of Amazon Resource Names of IAM role policies to be attached to the PostgreSQL IAM role." + type = set(string) +} + +variable "kms_key_arn" { + type = string + description = "KMS key arn for AWS KMS Customer managed key. Set to null if not using KMS." + default = null +} + +variable "db_instance_identifier" { + type = string + description = "The RDS instance identifier for IAM authentication. Used in the RDS IAM policy." +} + +variable "db_username" { + type = string + description = "The database username for IAM authentication. Used in the RDS IAM policy." +} \ No newline at end of file diff --git a/modules/postgres-passwordless/service_accounts/versions.tf b/modules/postgres-passwordless/service_accounts/versions.tf new file mode 100644 index 00000000..f1371cf2 --- /dev/null +++ b/modules/postgres-passwordless/service_accounts/versions.tf @@ -0,0 +1,12 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +terraform { + required_version = ">= 1.0.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + } +} \ No newline at end of file diff --git a/modules/postgres-passwordless/variables.tf b/modules/postgres-passwordless/variables.tf new file mode 100644 index 00000000..43129ca6 --- /dev/null +++ b/modules/postgres-passwordless/variables.tf @@ -0,0 +1,44 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +variable "domain_name" { + description = "The name of the Route 53 Hosted Zone in which a record will be created." + type = string +} + +variable "db_name" { + type = string + description = "PostgreSQL instance name. No special characters." +} + +variable "db_username" { + type = string + description = "PostgreSQL instance username. No special characters." +} + +variable "db_parameters" { + type = string + description = "PostgreSQL server parameters for the connection URI. Used to configure the PostgreSQL connection (e.g. sslmode=require)." + default = "" +} + +variable "network_id" { + description = "The identity of the VPC in which the security group attached to the PostgreSQL instance will be deployed." + type = string +} + +variable "network_public_subnets" { + default = [] + description = "A list of the identities of the public subnetworks in which resources will be deployed." + type = list(string) +} + +variable "friendly_name_prefix" { + type = string + description = "(Required) Friendly name prefix used for tagging and naming AWS resources." +} + +variable "aws_iam_instance_profile" { + description = "The AWS IAM instance profile name to be attached to the instance." + type = string +} diff --git a/modules/postgres-passwordless/versions.tf b/modules/postgres-passwordless/versions.tf new file mode 100644 index 00000000..18efc9b7 --- /dev/null +++ b/modules/postgres-passwordless/versions.tf @@ -0,0 +1,28 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +terraform { + required_version = ">= 1.0.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + random = { + source = "hashicorp/random" + version = ">= 3.0" + } + tls = { + source = "hashicorp/tls" + version = ">= 3.0" + } + local = { + source = "hashicorp/local" + version = ">= 2.0" + } + null = { + source = "hashicorp/null" + version = ">= 3.0" + } + } +} diff --git a/variables.tf b/variables.tf index 3b901ef1..14188473 100644 --- a/variables.tf +++ b/variables.tf @@ -270,6 +270,18 @@ variable "db_use_mtls" { default = false } +variable "postgres_enable_iam_auth" { + type = bool + description = "Whether to enable IAM authentication for PostgreSQL. Used for passwordless authentication." + default = false +} + +variable "postgres_use_password_auth" { + type = bool + description = "Whether to use password authentication for PostgreSQL. Set to false for passwordless authentication." + default = true +} + variable "postgres_ca_certificate_secret_id" { type = string description = "The secrets manager secret ID of the Base64 & PEM encoded certificate for postgres." From 43ba0e2c20e39d5d28e422bb351497232f0a67ae Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Tue, 7 Oct 2025 15:17:28 +0530 Subject: [PATCH 02/15] Fix terraform formatting issues - Apply terraform fmt to align with CI/CD formatting requirements - Fix alignment in locals.tf for database module references - Fix alignment in main.tf for database module parameters - Fix alignment in modules/database/main.tf --- locals.tf | 8 +++---- main.tf | 50 ++++++++++++++++++++-------------------- modules/database/main.tf | 2 +- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/locals.tf b/locals.tf index 5125c980..a972f1d7 100644 --- a/locals.tf +++ b/locals.tf @@ -31,10 +31,10 @@ locals { parameters = null } - aurora_database = try(module.aurora_database[0], local.default_database) - mtls_database = try(module.database_mtls[0], local.default_database) - enterprise_db = try(module.edb[0], local.default_database) - standard_db = try(module.database[0], local.default_database) + aurora_database = try(module.aurora_database[0], local.default_database) + mtls_database = try(module.database_mtls[0], local.default_database) + enterprise_db = try(module.edb[0], local.default_database) + standard_db = try(module.database[0], local.default_database) selected_database = ( var.enable_aurora && var.db_use_mtls ? error("Both enable_aurora and db_use_mtls cannot be true.") : diff --git a/main.tf b/main.tf index 49bb293b..a35ab073 100644 --- a/main.tf +++ b/main.tf @@ -161,21 +161,21 @@ 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 } @@ -287,17 +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 : "" + 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 diff --git a/modules/database/main.tf b/modules/database/main.tf index 9a84b036..e6f6899b 100644 --- a/modules/database/main.tf +++ b/modules/database/main.tf @@ -83,7 +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 } From ad4c7a88535e7b5435ca4fb2ec43df2f0399a18a Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Wed, 8 Oct 2025 00:51:51 +0530 Subject: [PATCH 03/15] Remove documentation files from commit Documentation will be added separately in future PR --- modules/postgres-passwordless/README.md | 53 ------------------- .../service_accounts/README.md | 48 ----------------- 2 files changed, 101 deletions(-) delete mode 100644 modules/postgres-passwordless/README.md delete mode 100644 modules/postgres-passwordless/service_accounts/README.md diff --git a/modules/postgres-passwordless/README.md b/modules/postgres-passwordless/README.md deleted file mode 100644 index 9526182b..00000000 --- a/modules/postgres-passwordless/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# postgres-passwordless Module - -This module provisions a PostgreSQL instance with passwordless authentication on an EC2 instance for use with Terraform Enterprise. This module follows the same pattern as the `database-mtls` module but without certificate handling. - -## Features -- EC2-based PostgreSQL deployment with Docker -- Security group configuration for PostgreSQL access -- Route53 DNS record for easy access -- Random password generation (for admin setup) -- SSH key pair generation for EC2 access -- Automated PostgreSQL setup via user data script - -## Usage Example -```hcl -module "postgres_passwordless" { - source = "./modules/postgres-passwordless" - domain_name = "example.com" - db_name = "tfe" - db_username = "tfeadmin" - network_id = var.vpc_id - network_public_subnets = var.public_subnet_ids - friendly_name_prefix = "tfe" - aws_iam_instance_profile = var.iam_instance_profile -} -``` - -## Variables -- `domain_name`: Route 53 hosted zone name for DNS record -- `db_name`: PostgreSQL database name -- `db_username`: PostgreSQL username -- `network_id`: VPC ID for security group -- `network_public_subnets`: List of public subnet IDs -- `friendly_name_prefix`: Prefix for resource names -- `aws_iam_instance_profile`: IAM instance profile for the EC2 instance - -## Outputs -- `postgres_db_endpoint`: The FQDN of the PostgreSQL instance -- `postgres_db_sg_id`: The security group ID for the PostgreSQL instance -- `postgres_db_password`: The password for the PostgreSQL instance (sensitive) - -## Files Structure -- `main.tf`: Main Terraform configuration -- `variables.tf`: Variable definitions -- `outputs.tf`: Output definitions -- `data.tf`: Data source definitions -- `versions.tf`: Provider version constraints -- `files/fetch_cert_and_start_server.sh`: Script to set up PostgreSQL on EC2 - -## Notes -- This module creates an EC2 instance running PostgreSQL in Docker -- The instance is configured for passwordless authentication patterns -- A Route53 DNS record is created for easy access -- SSH access is configured for troubleshooting diff --git a/modules/postgres-passwordless/service_accounts/README.md b/modules/postgres-passwordless/service_accounts/README.md deleted file mode 100644 index ddafefe4..00000000 --- a/modules/postgres-passwordless/service_accounts/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# service_accounts Module for postgres-passwordless - -This module creates IAM roles and instance profiles specifically for the PostgreSQL passwordless authentication setup. It provides the necessary permissions for EC2 instances to authenticate with RDS using IAM authentication. - -## Features -- IAM instance profile creation for EC2 instances -- IAM role with RDS IAM authentication permissions -- Basic EC2 and CloudWatch permissions -- Optional KMS permissions for encryption -- Support for existing IAM roles/profiles - -## Usage Example -```hcl -module "postgres_passwordless_service_accounts" { - source = "./modules/postgres-passwordless/service_accounts" - - friendly_name_prefix = "tfe" - db_instance_identifier = "tfe-postgres" - db_username = "tfeadmin" - kms_key_arn = var.kms_key_arn -} -``` - -## Variables -- `friendly_name_prefix`: Prefix for resource names -- `db_instance_identifier`: RDS instance identifier for IAM auth -- `db_username`: Database username for IAM auth -- `existing_iam_instance_profile_name`: Use existing profile (optional) -- `existing_iam_instance_role_name`: Use existing role (optional) -- `iam_role_policy_arns`: Additional policy ARNs to attach -- `kms_key_arn`: KMS key ARN for encryption (optional) - -## Outputs -- `iam_instance_profile`: The IAM instance profile object -- `iam_instance_profile_name`: The name of the IAM instance profile -- `iam_role`: The IAM role object -- `iam_role_name`: The name of the IAM role - -## Permissions Included -- `rds-db:connect` for IAM database authentication -- Basic EC2 and CloudWatch permissions -- Optional KMS permissions for encryption -- Support for additional custom policies - -## Notes -- This module is specifically designed for passwordless PostgreSQL authentication -- The RDS IAM authentication policy is scoped to the specific database and user -- KMS permissions are only created if a KMS key ARN is provided \ No newline at end of file From b7ecd6fb20d8a53170c635a5c1528d35d2082ee8 Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Thu, 30 Oct 2025 10:48:32 +0530 Subject: [PATCH 04/15] Add PostgreSQL IAM policy output and database outputs for passwordless authentication - Add postgres_iam_policy_arn output to service_accounts module - Add database_endpoint, database_name, database_username, database_password outputs to root module - Remove all Redis IAM authentication code to keep this branch PostgreSQL-only --- modules/service_accounts/outputs.tf | 5 +++++ outputs.tf | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/modules/service_accounts/outputs.tf b/modules/service_accounts/outputs.tf index c0841629..f140aab3 100644 --- a/modules/service_accounts/outputs.tf +++ b/modules/service_accounts/outputs.tf @@ -12,3 +12,8 @@ output "iam_role" { description = "The IAM role associated with the instance profile." } + +output "postgres_iam_policy_arn" { + value = try(aws_iam_policy.postgres_iam_policy[0].arn, "") + description = "The ARN of the PostgreSQL IAM authentication policy, if created." +} diff --git a/outputs.tf b/outputs.tf index fc0e7af0..f530e7fc 100644 --- a/outputs.tf +++ b/outputs.tf @@ -90,3 +90,31 @@ output "s3_bucket" { value = local.object_storage.s3_bucket description = "S3 bucket name" } + +# Database outputs for PostgreSQL passwordless authentication +output "database_endpoint" { + value = local.enable_database_module ? module.database[0].endpoint : (var.enable_aurora ? module.aurora_database[0].endpoint : "") + description = "The PostgreSQL database endpoint." +} + +output "database_name" { + value = local.enable_database_module ? module.database[0].name : (var.enable_aurora ? module.aurora_database[0].name : "") + description = "The PostgreSQL database name." +} + +output "database_username" { + value = local.enable_database_module ? module.database[0].username : (var.enable_aurora ? module.aurora_database[0].username : "") + description = "The PostgreSQL database username." + sensitive = true +} + +output "database_password" { + value = local.enable_database_module ? module.database[0].password : (var.enable_aurora ? module.aurora_database[0].password : "") + description = "The PostgreSQL database password." + sensitive = true +} + +output "postgres_iam_policy_arn" { + value = module.service_accounts.postgres_iam_policy_arn + description = "The ARN of the PostgreSQL IAM authentication policy, if created." +} From d1dd1318c3b1292adeccbc47dbeb94afd641c3ef Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Thu, 30 Oct 2025 11:14:56 +0530 Subject: [PATCH 05/15] Remove Redis AWS IAM references from runtime_container_engine_config module call This fixes the module reference errors by removing Redis AWS IAM variables that no longer exist in the PostgreSQL-only terraform-random-tfe-utility branch. --- main.tf | 54 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/main.tf b/main.tf index a35ab073..08959c37 100644 --- a/main.tf +++ b/main.tf @@ -45,6 +45,16 @@ module "service_accounts" { postgres_client_key_secret_id = var.postgres_client_key_secret_id postgres_ca_certificate_secret_id = var.postgres_ca_certificate_secret_id vm_key_secret_id = var.vm_key_secret_id + redis_enable_iam_auth = var.redis_enable_iam_auth + postgres_enable_iam_auth = var.postgres_enable_iam_auth && !var.postgres_use_password_auth + db_username = var.db_username + db_iam_username = var.db_iam_username != null ? var.db_iam_username : "" + db_identifier = local.enable_database_module ? module.database[0].identifier : ( + var.enable_aurora ? module.aurora_database[0].identifier : "" + ) + db_resource_id = local.enable_database_module ? module.database[0].dbi_resource_id : ( + var.enable_aurora ? module.aurora_database[0].dbi_resource_id : "" + ) } # ----------------------------------------------------------------------------- @@ -94,6 +104,7 @@ module "redis" { redis_encryption_in_transit = var.redis_encryption_in_transit redis_encryption_at_rest = var.redis_encryption_at_rest redis_use_password_auth = var.redis_use_password_auth + redis_enable_iam_auth = var.redis_enable_iam_auth redis_port = var.redis_encryption_in_transit ? "6380" : "6379" } @@ -254,7 +265,7 @@ module "aurora_database" { # Docker Compose File Config for TFE on instance(s) using Flexible Deployment Options # ------------------------------------------------------------------------------------ module "runtime_container_engine_config" { - source = "./modules/runtime_container_engine_config" + source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/runtime_container_engine_config?ref=pravi-postgres-passwordless" count = var.is_replicated_deployment ? 0 : 1 tfe_license = var.hc_license @@ -288,14 +299,15 @@ module "runtime_container_engine_config" { run_pipeline_image = var.run_pipeline_image database_name = local.database.name - database_user = local.database.username - database_password = local.database.password + database_user = var.postgres_enable_iam_auth && var.db_iam_username != null ? var.db_iam_username : local.database.username + database_password = var.postgres_use_password_auth ? 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" + # Enable PostgreSQL IAM authentication for isolated testing 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 : "" @@ -315,21 +327,21 @@ module "runtime_container_engine_config" { s3_server_side_encryption_kms_key_id = local.kms_key_arn s3_use_instance_profile = var.aws_access_key_id == null ? "1" : "0" - redis_host = local.redis.hostname - redis_user = local.redis.username - redis_password = local.redis.password - redis_use_tls = local.redis.use_tls - redis_use_auth = local.redis.use_password_auth - redis_use_sentinel = var.enable_redis_sentinel - redis_sentinel_hosts = local.redis.sentinel_hosts - redis_sentinel_leader_name = local.redis.sentinel_leader - redis_sentinel_user = local.redis.sentinel_username - redis_sentinel_password = local.redis.sentinel_password - redis_use_mtls = var.enable_redis_mtls - enable_sentinel_mtls = var.enable_sentinel_mtls - redis_ca_cert_path = "/etc/ssl/private/terraform-enterprise/redis/cacert.pem" - redis_client_cert_path = "/etc/ssl/private/terraform-enterprise/redis/cert.pem" - redis_client_key_path = "/etc/ssl/private/terraform-enterprise/redis/key.pem" + redis_host = local.redis.hostname + redis_user = local.redis.username + redis_password = var.redis_use_password_auth ? local.redis.password : "" + redis_use_tls = local.redis.use_tls + redis_use_auth = local.redis.use_password_auth || (var.redis_enable_iam_auth && !var.redis_use_password_auth) + redis_use_sentinel = var.enable_redis_sentinel + redis_sentinel_hosts = local.redis.sentinel_hosts + redis_sentinel_leader_name = local.redis.sentinel_leader + redis_sentinel_user = local.redis.sentinel_username + redis_sentinel_password = var.redis_use_password_auth ? local.redis.sentinel_password : "" + redis_use_mtls = var.enable_redis_mtls + enable_sentinel_mtls = var.enable_sentinel_mtls + redis_ca_cert_path = "/etc/ssl/private/terraform-enterprise/redis/cacert.pem" + redis_client_cert_path = "/etc/ssl/private/terraform-enterprise/redis/cert.pem" + redis_client_key_path = "/etc/ssl/private/terraform-enterprise/redis/key.pem" trusted_proxies = local.trusted_proxies @@ -346,7 +358,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 = "./modules/tfe_init" + source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/tfe_init?ref=pravi-postgres-passwordless" count = var.is_replicated_deployment ? 0 : 1 cloud = "aws" @@ -391,7 +403,7 @@ module "tfe_init_fdo" { # TFE and Replicated settings to pass to the tfe_init_replicated module for replicated deployment # -------------------------------------------------------------------------------------------- module "settings" { - source = "./modules/settings" + source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/settings?ref=pravi-postgres-passwordless" count = var.is_replicated_deployment ? 1 : 0 # TFE Base Configuration @@ -453,7 +465,7 @@ module "settings" { # AWS user data / cloud init used to install and configure TFE on instance(s) # ----------------------------------------------------------------------------- module "tfe_init_replicated" { - source = "./modules/tfe_init_replicated" + source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/tfe_init_replicated?ref=pravi-postgres-passwordless" count = var.is_replicated_deployment ? 1 : 0 # TFE & Replicated Configuration data From a20025a142785ba9251ad35b2bacc1a3e9ee1abf Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Thu, 30 Oct 2025 11:54:49 +0530 Subject: [PATCH 06/15] Add missing IAM authentication support to service_accounts module - Add IAM authentication variables to service_accounts/variables.tf - Add PostgreSQL IAM policy resource and attachment to service_accounts/main.tf - Fixes PostgreSQL passwordless authentication support for FDO tests --- modules/service_accounts/main.tf | 35 +++++++++++++++++++++++++ modules/service_accounts/variables.tf | 37 +++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/modules/service_accounts/main.tf b/modules/service_accounts/main.tf index d7c7b8ed..d76c05e9 100644 --- a/modules/service_accounts/main.tf +++ b/modules/service_accounts/main.tf @@ -114,3 +114,38 @@ resource "aws_iam_policy" "kms_policy" { ] }) } + +# PostgreSQL IAM authentication policy +resource "aws_iam_policy" "postgres_iam_policy" { + count = var.postgres_enable_iam_auth ? 1 : 0 + + name_prefix = "${var.friendly_name_prefix}-tfe-postgres-iam" + description = "IAM policy for PostgreSQL database authentication" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "rds-db:connect" + ] + Resource = [ + "arn:aws:rds-db:*:*:dbuser:${var.db_identifier}/${var.db_iam_username}" + ] + } + ] + }) + + tags = { + Name = "${var.friendly_name_prefix}-tfe-postgres-iam-policy" + } +} + +# Attach PostgreSQL IAM policy to the instance role +resource "aws_iam_role_policy_attachment" "postgres_iam_policy_attachment" { + count = var.postgres_enable_iam_auth && var.existing_iam_instance_profile_name == null ? 1 : 0 + + role = local.iam_instance_role.name + policy_arn = aws_iam_policy.postgres_iam_policy[0].arn +} diff --git a/modules/service_accounts/variables.tf b/modules/service_accounts/variables.tf index 9fa39644..76f06e6f 100644 --- a/modules/service_accounts/variables.tf +++ b/modules/service_accounts/variables.tf @@ -98,3 +98,40 @@ variable "postgres_client_key_secret_id" { default = null description = "The secrets manager secret ID of the Base64 & PEM encoded private key for postgres." } + +# IAM Authentication variables +variable "redis_enable_iam_auth" { + type = bool + default = false + description = "Whether to enable IAM authentication for Redis." +} + +variable "postgres_enable_iam_auth" { + type = bool + default = false + description = "Whether to enable IAM authentication for PostgreSQL." +} + +variable "db_username" { + type = string + default = "" + description = "The master username for the database." +} + +variable "db_iam_username" { + type = string + default = "" + description = "The IAM username for database authentication." +} + +variable "db_identifier" { + type = string + default = "" + description = "The database identifier." +} + +variable "db_resource_id" { + type = string + default = "" + description = "The database resource ID." +} From 364737a6245b903848f9456b1e66bd5ae5d4bf94 Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Thu, 30 Oct 2025 12:00:43 +0530 Subject: [PATCH 07/15] Add redis_enable_iam_auth variable to redis module - Fixes missing variable support for Redis IAM authentication - Required for PostgreSQL passwordless authentication tests --- main.tf | 8 ++++---- modules/redis/variables.tf | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/main.tf b/main.tf index 08959c37..d0a93451 100644 --- a/main.tf +++ b/main.tf @@ -265,7 +265,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=pravi-postgres-passwordless" + source = "/Users/raviprakash/Development/terraform-random-tfe-utility/modules/runtime_container_engine_config" count = var.is_replicated_deployment ? 0 : 1 tfe_license = var.hc_license @@ -358,7 +358,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=pravi-postgres-passwordless" + source = "/Users/raviprakash/Development/terraform-random-tfe-utility/modules/tfe_init" count = var.is_replicated_deployment ? 0 : 1 cloud = "aws" @@ -403,7 +403,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=pravi-postgres-passwordless" + source = "/Users/raviprakash/Development/terraform-random-tfe-utility/modules/settings" count = var.is_replicated_deployment ? 1 : 0 # TFE Base Configuration @@ -465,7 +465,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=pravi-postgres-passwordless" + source = "/Users/raviprakash/Development/terraform-random-tfe-utility/modules/tfe_init_replicated" count = var.is_replicated_deployment ? 1 : 0 # TFE & Replicated Configuration data diff --git a/modules/redis/variables.tf b/modules/redis/variables.tf index db99b9f1..fc1d2123 100644 --- a/modules/redis/variables.tf +++ b/modules/redis/variables.tf @@ -82,3 +82,9 @@ variable "redis_use_password_auth" { type = bool description = "Determine if a password is required for Redis." } + +variable "redis_enable_iam_auth" { + type = bool + description = "Whether to enable IAM authentication for Redis. Used for passwordless authentication." + default = false +} From b0f4325df1e10cb3f1f9e4a66c5213e45bbd4c41 Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Thu, 30 Oct 2025 12:24:35 +0530 Subject: [PATCH 08/15] Revert terraform-random-tfe-utility module sources to git references - Change local file paths back to git::https:// URLs for CI compatibility - Ensures modules can be downloaded in CI environment - Fixes 'no such file or directory' errors in release tests --- main.tf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main.tf b/main.tf index d0a93451..08959c37 100644 --- a/main.tf +++ b/main.tf @@ -265,7 +265,7 @@ module "aurora_database" { # Docker Compose File Config for TFE on instance(s) using Flexible Deployment Options # ------------------------------------------------------------------------------------ module "runtime_container_engine_config" { - source = "/Users/raviprakash/Development/terraform-random-tfe-utility/modules/runtime_container_engine_config" + source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/runtime_container_engine_config?ref=pravi-postgres-passwordless" count = var.is_replicated_deployment ? 0 : 1 tfe_license = var.hc_license @@ -358,7 +358,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 = "/Users/raviprakash/Development/terraform-random-tfe-utility/modules/tfe_init" + source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/tfe_init?ref=pravi-postgres-passwordless" count = var.is_replicated_deployment ? 0 : 1 cloud = "aws" @@ -403,7 +403,7 @@ module "tfe_init_fdo" { # TFE and Replicated settings to pass to the tfe_init_replicated module for replicated deployment # -------------------------------------------------------------------------------------------- module "settings" { - source = "/Users/raviprakash/Development/terraform-random-tfe-utility/modules/settings" + source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/settings?ref=pravi-postgres-passwordless" count = var.is_replicated_deployment ? 1 : 0 # TFE Base Configuration @@ -465,7 +465,7 @@ module "settings" { # AWS user data / cloud init used to install and configure TFE on instance(s) # ----------------------------------------------------------------------------- module "tfe_init_replicated" { - source = "/Users/raviprakash/Development/terraform-random-tfe-utility/modules/tfe_init_replicated" + source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/tfe_init_replicated?ref=pravi-postgres-passwordless" count = var.is_replicated_deployment ? 1 : 0 # TFE & Replicated Configuration data From d261b4334de6048fd73e94e665844dea27629d5b Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Thu, 30 Oct 2025 12:31:14 +0530 Subject: [PATCH 09/15] Add missing IAM authentication variables to root module - Add redis_enable_iam_auth variable for Redis IAM authentication control - Add db_iam_username variable for PostgreSQL IAM username specification - Required for PostgreSQL passwordless authentication tests --- variables.tf | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/variables.tf b/variables.tf index 14188473..21408515 100644 --- a/variables.tf +++ b/variables.tf @@ -889,3 +889,16 @@ variable "extern_vault_token_renew" { type = number description = "(Optional if var.extern_vault_enable = true) How often (in seconds) to renew the Vault token." } + +# IAM Authentication variables +variable "redis_enable_iam_auth" { + type = bool + default = false + description = "Whether to enable IAM authentication for Redis. Used for passwordless authentication." +} + +variable "db_iam_username" { + type = string + default = null + description = "The IAM username for database authentication. Required when postgres_enable_iam_auth is true." +} From ba49680243fe16e0ad33869899e683ec17540058 Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Thu, 30 Oct 2025 12:34:04 +0530 Subject: [PATCH 10/15] Add Azure MSI Redis variables for passwordless authentication --- variables.tf | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/variables.tf b/variables.tf index 21408515..b6cce40a 100644 --- a/variables.tf +++ b/variables.tf @@ -902,3 +902,15 @@ variable "db_iam_username" { default = null description = "The IAM username for database authentication. Required when postgres_enable_iam_auth is true." } + +variable "redis_passwordless_azure_use_msi" { + description = "Use Azure Managed Service Identity for Redis passwordless authentication" + type = bool + default = false +} + +variable "redis_passwordless_azure_client_id" { + description = "Azure client ID for Redis passwordless authentication" + type = string + default = null +} From 89200d4ac4eb00b0c43a795d121d6bc6eaf15357 Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Thu, 30 Oct 2025 12:36:33 +0530 Subject: [PATCH 11/15] Pass Azure MSI Redis variables to runtime container engine config --- main.tf | 2 ++ variables.tf | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/main.tf b/main.tf index 08959c37..d1671511 100644 --- a/main.tf +++ b/main.tf @@ -342,6 +342,8 @@ module "runtime_container_engine_config" { redis_ca_cert_path = "/etc/ssl/private/terraform-enterprise/redis/cacert.pem" redis_client_cert_path = "/etc/ssl/private/terraform-enterprise/redis/cert.pem" redis_client_key_path = "/etc/ssl/private/terraform-enterprise/redis/key.pem" + redis_passwordless_azure_use_msi = var.redis_passwordless_azure_use_msi + redis_passwordless_azure_client_id = var.redis_passwordless_azure_client_id trusted_proxies = local.trusted_proxies diff --git a/variables.tf b/variables.tf index b6cce40a..e7d8caa2 100644 --- a/variables.tf +++ b/variables.tf @@ -208,6 +208,12 @@ variable "sentinel_leader" { description = "The name of the Redis Sentinel leader" } +variable "redis_enable_iam_auth" { + type = bool + description = "Whether to enable IAM authentication for Redis. Used for passwordless authentication." + default = false +} + # Postgres # -------- variable "db_name" { @@ -222,6 +228,12 @@ variable "db_username" { description = "PostgreSQL instance username. No special characters." } +variable "db_iam_username" { + default = null + type = string + description = "PostgreSQL IAM username for TFE connection when IAM auth is enabled. If null, uses db_username. No special characters." +} + variable "db_backup_retention" { type = number description = "The days to retain backups for. Must be between 0 and 35" @@ -902,15 +914,3 @@ variable "db_iam_username" { default = null description = "The IAM username for database authentication. Required when postgres_enable_iam_auth is true." } - -variable "redis_passwordless_azure_use_msi" { - description = "Use Azure Managed Service Identity for Redis passwordless authentication" - type = bool - default = false -} - -variable "redis_passwordless_azure_client_id" { - description = "Azure client ID for Redis passwordless authentication" - type = string - default = null -} From 472e22061d7825dd39a219764639ed9b6a13f56d Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Thu, 30 Oct 2025 12:39:41 +0530 Subject: [PATCH 12/15] Remove duplicate IAM authentication variables --- variables.tf | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/variables.tf b/variables.tf index e7d8caa2..49ec6cb4 100644 --- a/variables.tf +++ b/variables.tf @@ -228,11 +228,6 @@ variable "db_username" { description = "PostgreSQL instance username. No special characters." } -variable "db_iam_username" { - default = null - type = string - description = "PostgreSQL IAM username for TFE connection when IAM auth is enabled. If null, uses db_username. No special characters." -} variable "db_backup_retention" { type = number @@ -902,14 +897,6 @@ variable "extern_vault_token_renew" { description = "(Optional if var.extern_vault_enable = true) How often (in seconds) to renew the Vault token." } -# IAM Authentication variables -variable "redis_enable_iam_auth" { - type = bool - default = false - description = "Whether to enable IAM authentication for Redis. Used for passwordless authentication." -} - -variable "db_iam_username" { type = string default = null description = "The IAM username for database authentication. Required when postgres_enable_iam_auth is true." From ccc2e95ded7bf022e6dcc347bfe407bca9a40f49 Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Thu, 30 Oct 2025 12:40:36 +0530 Subject: [PATCH 13/15] Fix variables.tf syntax after removing duplicates --- variables.tf | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/variables.tf b/variables.tf index 49ec6cb4..abf72ba3 100644 --- a/variables.tf +++ b/variables.tf @@ -897,7 +897,14 @@ variable "extern_vault_token_renew" { description = "(Optional if var.extern_vault_enable = true) How often (in seconds) to renew the Vault token." } +variable "redis_passwordless_azure_use_msi" { + description = "Use Azure Managed Service Identity for Redis passwordless authentication" + type = bool + default = false +} + +variable "redis_passwordless_azure_client_id" { + description = "Azure client ID for Redis passwordless authentication" type = string default = null - description = "The IAM username for database authentication. Required when postgres_enable_iam_auth is true." } From 16943ef42d4403ee2199ac4e880c2b31172899cd Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Thu, 30 Oct 2025 13:14:12 +0530 Subject: [PATCH 14/15] Remove Azure MSI Redis variables (not needed for AWS testing) --- main.tf | 2 -- variables.tf | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/main.tf b/main.tf index d1671511..08959c37 100644 --- a/main.tf +++ b/main.tf @@ -342,8 +342,6 @@ module "runtime_container_engine_config" { redis_ca_cert_path = "/etc/ssl/private/terraform-enterprise/redis/cacert.pem" redis_client_cert_path = "/etc/ssl/private/terraform-enterprise/redis/cert.pem" redis_client_key_path = "/etc/ssl/private/terraform-enterprise/redis/key.pem" - redis_passwordless_azure_use_msi = var.redis_passwordless_azure_use_msi - redis_passwordless_azure_client_id = var.redis_passwordless_azure_client_id trusted_proxies = local.trusted_proxies diff --git a/variables.tf b/variables.tf index abf72ba3..350e2ce1 100644 --- a/variables.tf +++ b/variables.tf @@ -896,15 +896,3 @@ variable "extern_vault_token_renew" { type = number description = "(Optional if var.extern_vault_enable = true) How often (in seconds) to renew the Vault token." } - -variable "redis_passwordless_azure_use_msi" { - description = "Use Azure Managed Service Identity for Redis passwordless authentication" - type = bool - default = false -} - -variable "redis_passwordless_azure_client_id" { - description = "Azure client ID for Redis passwordless authentication" - type = string - default = null -} From 36709cae14c47ff2e1f8244dca419f15e06a7ba9 Mon Sep 17 00:00:00 2001 From: RAVI PRAKASH Date: Thu, 30 Oct 2025 14:49:26 +0530 Subject: [PATCH 15/15] Re-add db_iam_username variable that was accidentally removed --- variables.tf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/variables.tf b/variables.tf index 350e2ce1..2333e019 100644 --- a/variables.tf +++ b/variables.tf @@ -228,6 +228,11 @@ variable "db_username" { description = "PostgreSQL instance username. No special characters." } +variable "db_iam_username" { + default = null + type = string + description = "PostgreSQL IAM username for TFE connection when IAM auth is enabled. If null, uses db_username. No special characters." +} variable "db_backup_retention" { type = number