Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
dc1b2f8
feat: add PostgreSQL AWS IAM authentication support
raviharshicorp Nov 5, 2025
900ca2e
Fix PostgreSQL IAM authentication template variable mapping
raviharshicorp Nov 12, 2025
fe76681
Add automated PostgreSQL IAM user creation
raviharshicorp Nov 12, 2025
5a84216
Fix AWS region configuration for PostgreSQL IAM user creation
raviharshicorp Nov 12, 2025
4f60be5
Add DATABASE_URL environment variable for PostgreSQL IAM authentication
raviharshicorp Nov 12, 2025
772f18e
Fix DATABASE_URL construction for null password in IAM auth
raviharshicorp Nov 12, 2025
19a7d7e
Fix DATABASE_URL for PostgreSQL IAM auth: add placeholder password
raviharshicorp Nov 12, 2025
4473130
Implement AWS RDS IAM authentication with runtime token generation
raviharshicorp Nov 12, 2025
0f5b8a9
Fix PostgreSQL IAM auth: use pgmultiauth library approach
raviharshicorp Nov 12, 2025
e81d3fa
Fix DATABASE_URL construction: remove duplicate port specification
raviharshicorp Nov 12, 2025
8f6ce5b
Remove TFE_DATABASE_PASSWORD entirely for IAM authentication
raviharshicorp Nov 13, 2025
6639de4
Fix PostgreSQL IAM auth by adding TFE_DATABASE_PASSWORDLESS environme…
raviharshicorp Nov 13, 2025
bc92f8c
Fix PostgreSQL passwordless authentication environment variables
raviharshicorp Nov 14, 2025
349ad9a
Remove unwanted Atlas environment variables for PostgreSQL IAM auth
raviharshicorp Nov 14, 2025
929385d
Add automated PostgreSQL IAM user setup during instance startup
raviharshicorp Nov 15, 2025
be9c385
Optimize user_data script to fit AWS 16KB limit
raviharshicorp Nov 15, 2025
56adf6b
Fix cloud-init script failures for PostgreSQL IAM setup
raviharshicorp Nov 15, 2025
7ef4799
Replace SSM approach with direct PostgreSQL IAM user creation
raviharshicorp Nov 15, 2025
78ee692
Ultra-compact PostgreSQL IAM user setup to fit 16KB limit
raviharshicorp Nov 15, 2025
cbcdf17
Fix PostgreSQL IAM user creation with better logging and error handling
raviharshicorp Nov 17, 2025
f05f510
Remove PostgreSQL IAM user setup from user_data script
raviharshicorp Nov 17, 2025
30901b7
Restore PostgreSQL IAM user creation in user_data script
raviharshicorp Nov 17, 2025
be42204
Create ultra-compact PostgreSQL IAM user setup for AWS 16KB limit
raviharshicorp Nov 17, 2025
fcc9bec
Remove postgres_iam_setup_ssm_document variable and references
raviharshicorp Nov 17, 2025
0a09d3c
Clean up postgres passwordless implementation
raviharshicorp Nov 20, 2025
bc26dc9
Fix tflint warnings: remove 8 unused variable declarations
raviharshicorp Nov 20, 2025
df2c413
Fix tflint warnings: remove 2 unused database IAM variable declarations
raviharshicorp Nov 20, 2025
24a895a
Fix invalid TFE database environment variable
raviharshicorp Nov 24, 2025
0e51754
Restore Explorer database passwordless Azure variables
raviharshicorp Nov 24, 2025
33b6172
Address PR feedback for PostgreSQL passwordless configuration
raviharshicorp Nov 27, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
locals {
database = {
TFE_DATABASE_USER = var.database_user
TFE_DATABASE_PASSWORD = var.database_password
TFE_DATABASE_PASSWORD = ""
TFE_DATABASE_HOST = var.database_host
TFE_DATABASE_NAME = var.database_name
TFE_DATABASE_PARAMETERS = var.database_parameters
Expand All @@ -14,8 +14,12 @@ locals {
TFE_DATABASE_CLIENT_KEY_FILE = var.database_client_key_file
TFE_DATABASE_PASSWORDLESS_AZURE_USE_MSI = var.database_passwordless_azure_use_msi
TFE_DATABASE_PASSWORDLESS_AZURE_CLIENT_ID = var.database_passwordless_azure_client_id
# AWS IAM authentication for PostgreSQL passwordless
TFE_DATABASE_PASSWORDLESS_AWS_USE_INSTANCE_PROFILE = var.database_passwordless_aws_use_iam
TFE_DATABASE_PASSWORDLESS_AWS_REGION = var.database_passwordless_aws_region
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we can do it in a separate PR, but I am thinking the variables database_passwordless_aws_region and s3_region have the same values. Does it makes sense to merge them into one?

}
database_configuration = local.disk ? {} : local.database

explorer_database = {
TFE_EXPLORER_DATABASE_HOST = var.explorer_database_host
TFE_EXPLORER_DATABASE_NAME = var.explorer_database_name
Expand Down
12 changes: 12 additions & 0 deletions modules/runtime_container_engine_config/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,18 @@ variable "database_passwordless_azure_client_id" {
description = "Azure Managed Service Identity (MSI) Client ID. If not set, System Assigned Managed Identity will be used."
}

variable "database_passwordless_aws_use_iam" {
default = false
type = bool
description = "Whether or not to use AWS IAM authentication to connect to the PostgreSQL database. Defaults to false if no value is given."
}

variable "database_passwordless_aws_region" {
default = ""
type = string
description = "AWS region for IAM database authentication. Required when database_passwordless_aws_use_iam is true."
}

variable "explorer_database_host" {
type = string
default = null
Expand Down
15 changes: 15 additions & 0 deletions modules/tfe_init/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,22 @@ locals {
redis_bootstrap_key_pathname = local.redis_bootstrap_key_pathname
redis_bootstrap_ca_pathname = local.redis_bootstrap_ca_pathname

# Database configuration for templates
Database = {
Copy link
Contributor

Choose a reason for hiding this comment

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

why are we adding this

Passwordless = {
AWSUseInstanceProfile = var.database_passwordless_aws_use_iam
AWSRegion = var.database_passwordless_aws_region
}
}

database_azure_msi_auth_enabled = var.database_passwordless_azure_use_msi
database_aws_iam_auth_enabled = var.database_passwordless_aws_use_iam
database_aws_iam_region = var.database_passwordless_aws_region
database_host = var.database_host
database_name = var.database_name
admin_database_username = var.admin_database_username
admin_database_password = var.admin_database_password
database_iam_username = var.database_iam_username

proxy_ip = var.proxy_ip
proxy_port = var.proxy_port
Expand Down
17 changes: 17 additions & 0 deletions modules/tfe_init/templates/aws.ubuntu.docker.tfe.sh.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,21 @@ mkdir -p $tfe_dir

echo ${docker_compose} | base64 -d > $tfe_dir/compose.yaml

%{ if database_iam_username != null && database_iam_username != "" ~}
echo "[$(date +"%FT%T")] Setting up PostgreSQL IAM user" | tee -a $log_pathname
sudo apt-get update -qq && sudo apt-get install -y postgresql-client-16 >/dev/null 2>&1
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks a little fragile, what happens when we use pg 15 or 17 as in compatibility tests?

export PGPASSWORD="${admin_database_password}"
for i in $(seq 1 20); do
if psql -h "${database_host}" -U "${admin_database_username}" -d "${database_name}" -c "SELECT 1;" >/dev/null 2>&1; then
echo "DB connected on attempt $i" | tee -a $log_pathname
psql -h "${database_host}" -U "${admin_database_username}" -d "${database_name}" -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${database_iam_username}') THEN CREATE USER \"${database_iam_username}\" WITH LOGIN; GRANT rds_iam TO \"${database_iam_username}\"; GRANT CONNECT ON DATABASE \"${database_name}\" TO \"${database_iam_username}\"; GRANT USAGE, CREATE ON SCHEMA public TO \"${database_iam_username}\"; GRANT ALL ON ALL TABLES IN SCHEMA public TO \"${database_iam_username}\"; GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO \"${database_iam_username}\"; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO \"${database_iam_username}\"; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO \"${database_iam_username}\"; RAISE NOTICE 'IAM user created'; ELSE RAISE NOTICE 'IAM user exists'; END IF; END \$\$;" >/dev/null 2>&1
echo "IAM user ${database_iam_username} ready" | tee -a $log_pathname
Comment on lines +193 to +194
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this what we suggest to our clients as well? To give admin privileges to the IAM user on all schemas? The reason I ask - release tests should be similar in deployment to what we suggest our clients, if the privileges we suggest change some time in the future the test should break.

Also, have we tried doing this via IAM roles and policies? If not, could we first try setting up that manually and if it works then incorporate it here?

break
else
echo "DB attempt $i/20 failed" | tee -a $log_pathname
sleep 10
fi
done
%{ endif ~}

docker compose -f /etc/tfe/compose.yaml up -d
89 changes: 89 additions & 0 deletions modules/tfe_init/templates/tfe.sh.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,95 @@ mkdir -p $tfe_dir

echo ${docker_compose} | base64 -d > $tfe_dir/compose.yaml

%{ if database_aws_iam_auth_enabled ~}
echo "[$(date +"%FT%T")] [Terraform Enterprise] Setting up PostgreSQL IAM user" | tee -a $log_pathname

# Set AWS region for CLI commands
export AWS_DEFAULT_REGION="${database_aws_iam_region}"
echo "[$(date +"%FT%T")] [Terraform Enterprise] AWS region set to: $AWS_DEFAULT_REGION" | tee -a $log_pathname

# Install PostgreSQL client for database operations
if command -v apt-get >/dev/null 2>&1; then
echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing PostgreSQL client" | tee -a $log_pathname
apt-get update -qq
apt-get install -y postgresql-client-15 postgresql-client-common
elif command -v yum >/dev/null 2>&1; then
echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing PostgreSQL client" | tee -a $log_pathname
yum update -y
yum install -y postgresql15
fi

# Function to create PostgreSQL IAM user
create_postgres_iam_user() {
local db_endpoint="${database_host}"
local admin_user="${admin_database_username}"
local admin_password="${admin_database_password}"
local iam_user="${database_iam_username}"
local db_name="${database_name}"

echo "[$(date +"%FT%T")] [Terraform Enterprise] Creating PostgreSQL IAM user: $iam_user" | tee -a $log_pathname

# Wait for database to be ready
echo "[$(date +"%FT%T")] [Terraform Enterprise] Waiting for PostgreSQL database to be ready..." | tee -a $log_pathname
max_attempts=30
attempt=0

while [ $attempt -lt $max_attempts ]; do
if PGPASSWORD="$admin_password" psql -h "$db_endpoint" -U "$admin_user" -d "$db_name" -c 'SELECT 1;' >/dev/null 2>&1; then
echo "[$(date +"%FT%T")] [Terraform Enterprise] Database is ready!" | tee -a $log_pathname
break
fi
attempt=$((attempt + 1))
echo "[$(date +"%FT%T")] [Terraform Enterprise] Waiting for PostgreSQL... (attempt $attempt/$max_attempts)" | tee -a $log_pathname
sleep 10
done

if [ $attempt -ge $max_attempts ]; then
echo "[$(date +"%FT%T")] [Terraform Enterprise] ERROR: Database not ready after $max_attempts attempts" | tee -a $log_pathname
return 1
fi

# Create IAM user
echo "[$(date +"%FT%T")] [Terraform Enterprise] Creating IAM user in PostgreSQL..." | tee -a $log_pathname
PGPASSWORD="$admin_password" psql -h "$db_endpoint" -U "$admin_user" -d "$db_name" -v ON_ERROR_STOP=1 << EOF
DO \$\$
BEGIN
-- Check if user exists
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '$iam_user') THEN
-- Create the IAM user
CREATE USER "$iam_user";
-- Grant rds_iam role (this role exists automatically in RDS PostgreSQL with IAM auth enabled)
GRANT rds_iam TO "$iam_user";
-- Grant necessary database permissions
GRANT CONNECT ON DATABASE "$db_name" TO "$iam_user";
GRANT USAGE ON SCHEMA public TO "$iam_user";
GRANT CREATE ON SCHEMA public TO "$iam_user";
-- Grant table permissions
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "$iam_user";
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO "$iam_user";
-- Grant default privileges for future objects
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "$iam_user";
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO "$iam_user";
RAISE NOTICE 'Successfully created IAM user: $iam_user';
ELSE
RAISE NOTICE 'IAM user already exists: $iam_user';
END IF;
Comment on lines +240 to +262
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the difference between the things we are doing here and in modules/tfe_init/templates/aws.ubuntu.docker.tfe.sh.tpl?

END
\$\$;
EOF

if [ $? -eq 0 ]; then
echo "[$(date +"%FT%T")] [Terraform Enterprise] PostgreSQL IAM user setup completed successfully" | tee -a $log_pathname
else
echo "[$(date +"%FT%T")] [Terraform Enterprise] ERROR: Failed to create PostgreSQL IAM user" | tee -a $log_pathname
return 1
fi
}

# Create the IAM user before starting TFE
create_postgres_iam_user
%{ endif ~}

docker compose -f /etc/tfe/compose.yaml up -d

%{ if distribution == "rhel" && cloud != "google" ~}
Expand Down
18 changes: 18 additions & 0 deletions modules/tfe_init/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,21 @@ variable "database_passwordless_azure_use_msi" {
type = bool
description = "Whether or not to use Azure Managed Service Identity (MSI) to connect to the PostgreSQL database. Defaults to false if no value is given."
}

variable "database_passwordless_aws_use_iam" {
default = false
type = bool
description = "Whether or not to use AWS IAM authentication to connect to the PostgreSQL database. Defaults to false if no value is given."
}

variable "database_passwordless_aws_region" {
default = ""
type = string
description = "AWS region for IAM database authentication. Required when database_passwordless_aws_use_iam is true."
}

variable "database_iam_username" {
default = null
type = string
description = "PostgreSQL IAM user for AWS IAM authentication."
}
Loading