The YugabyteDB Aeon documentation shows how to create an AWS Private Service Endpoint using the Aeon UI or the ybm CLI.
But can the same thing be automated with Terraform?
Yes.
The YugabyteDB Aeon Terraform provider includes a native ybm_private_service_endpoint resource. The resource also returns the AWS PrivateLink service name, allowing Terraform to pass it directly to the standard AWS aws_vpc_endpoint resource.
That means the entire connection can be managed declaratively:
YugabyteDB Aeon Private Service Endpoint
|
| service_name
v
AWS Interface VPC Endpoint
|
v
Application VPC
null_resource, a local-exec provisioner, or an external ybm CLI command when using a current version of the YugabyteDB Aeon Terraform provider. What Are We Creating?
This demo creates:
- ● A Private Service Endpoint on an existing YugabyteDB Aeon cluster
- ● An AWS security group for the interface endpoint
- ● An ingress rule for YSQL
- ● An optional ingress rule for YCQL
- ● An AWS interface VPC endpoint
- ● Terraform outputs containing the private host and AWS service name
The key connection between the two Terraform providers is:
service_name = ybm_private_service_endpoint.aws.service_name
The service name does not need to be copied manually from the Aeon UI or parsed from the output of a CLI command.
Prerequisites
You will need:
- ● A YugabyteDB Aeon dedicated cluster deployed on AWS
- ● The Aeon cluster ID
- ● A YugabyteDB Aeon API key
- ● An application VPC in AWS
- ● One or more subnets for the AWS interface endpoint
- ● The security group used by the application
- ● AWS credentials available to Terraform
- ● Terraform installed
For AWS PrivateLink, YugabyteDB Aeon creates the endpoint service and your AWS account creates the corresponding interface VPC endpoint. A PSE is created for each Aeon cluster region that requires private connectivity.
Create the Terraform Configuration
Create a file named main.tf:
terraform {
required_version = ">= 1.5.0"
required_providers {
ybm = {
source = "yugabyte/ybm"
version = "~> 1.0"
}
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
###############################################################################
# Variables
###############################################################################
variable "ybm_api_key" {
description = "YugabyteDB Aeon API key."
type = string
sensitive = true
}
variable "ybm_cluster_id" {
description = "ID of the YugabyteDB Aeon cluster."
type = string
}
variable "aws_region" {
description = "AWS region containing the Aeon cluster and application VPC."
type = string
default = "us-east-1"
}
variable "application_vpc_id" {
description = "ID of the AWS VPC containing the application."
type = string
}
variable "endpoint_subnet_ids" {
description = "Subnet IDs where the AWS interface endpoint will be created."
type = set(string)
}
variable "application_security_group_id" {
description = "Security group used by the application."
type = string
}
variable "enable_ycql" {
description = "Create an ingress rule for YCQL port 9042."
type = bool
default = false
}
###############################################################################
# Providers
###############################################################################
provider "ybm" {
host = "cloud.yugabyte.com"
use_secure_host = true
auth_token = var.ybm_api_key
}
provider "aws" {
region = var.aws_region
}
###############################################################################
# Discover the AWS Account ID
###############################################################################
data "aws_caller_identity" "current" {}
###############################################################################
# Create the Private Service Endpoint in YugabyteDB Aeon
###############################################################################
resource "ybm_private_service_endpoint" "aws" {
cluster_id = var.ybm_cluster_id
region = var.aws_region
security_principals = [
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
]
}
###############################################################################
# Security Group for the AWS Interface Endpoint
###############################################################################
resource "aws_security_group" "yugabyte_endpoint" {
name_prefix = "yugabyte-aeon-endpoint-"
description = "Security group for the YugabyteDB Aeon PrivateLink endpoint"
vpc_id = var.application_vpc_id
tags = {
Name = "yugabyte-aeon-private-endpoint"
}
}
###############################################################################
# Allow YSQL from the Application
###############################################################################
resource "aws_vpc_security_group_ingress_rule" "ysql" {
security_group_id = aws_security_group.yugabyte_endpoint.id
referenced_security_group_id = var.application_security_group_id
description = "Allow YSQL connections from the application"
ip_protocol = "tcp"
from_port = 5433
to_port = 5433
}
###############################################################################
# Optionally Allow YCQL from the Application
###############################################################################
resource "aws_vpc_security_group_ingress_rule" "ycql" {
count = var.enable_ycql ? 1 : 0
security_group_id = aws_security_group.yugabyte_endpoint.id
referenced_security_group_id = var.application_security_group_id
description = "Allow YCQL connections from the application"
ip_protocol = "tcp"
from_port = 9042
to_port = 9042
}
###############################################################################
# Create the AWS Interface VPC Endpoint
###############################################################################
resource "aws_vpc_endpoint" "yugabyte" {
vpc_id = var.application_vpc_id
vpc_endpoint_type = "Interface"
service_name = ybm_private_service_endpoint.aws.service_name
subnet_ids = var.endpoint_subnet_ids
security_group_ids = [
aws_security_group.yugabyte_endpoint.id
]
private_dns_enabled = true
tags = {
Name = "yugabyte-aeon-private-endpoint"
}
}
###############################################################################
# Outputs
###############################################################################
output "yugabyte_pse_endpoint_id" {
description = "ID of the YugabyteDB Aeon Private Service Endpoint."
value = ybm_private_service_endpoint.aws.endpoint_id
}
output "yugabyte_pse_state" {
description = "State of the YugabyteDB Aeon Private Service Endpoint."
value = ybm_private_service_endpoint.aws.state
}
output "yugabyte_pse_host" {
description = "Private YugabyteDB host name."
value = ybm_private_service_endpoint.aws.host
}
output "yugabyte_pse_service_name" {
description = "AWS PrivateLink service name published by Aeon."
value = ybm_private_service_endpoint.aws.service_name
}
output "yugabyte_pse_availability_zones" {
description = "Availability zones exposed by the Aeon endpoint service."
value = ybm_private_service_endpoint.aws.availability_zones
}
output "aws_vpc_endpoint_id" {
description = "ID of the AWS interface VPC endpoint."
value = aws_vpc_endpoint.yugabyte.id
}
output "aws_vpc_endpoint_state" {
description = "State of the AWS interface VPC endpoint."
value = aws_vpc_endpoint.yugabyte.state
}
The Aeon provider requires an API key, a host, and optionally the secure-host setting. The PSE resource requires the cluster ID, region, and permitted security principals. It exposes the service name, host, state, endpoint ID, and availability zones as computed values.
Supply the Environment-Specific Values
Create terraform.tfvars:
ybm_cluster_id = "00000000-1111-2222-3333-444444444444"
aws_region = "us-east-1"
application_vpc_id = "vpc-0123456789abcdef0"
endpoint_subnet_ids = [
"subnet-0123456789abcdef0"
]
application_security_group_id = "sg-0123456789abcdef0"
enable_ycql = false
Do not store the Aeon API key directly in terraform.tfvars.
Export it as a Terraform variable:
export TF_VAR_ybm_api_key=""
Export the database password:
export TF_VAR_ysql_password=""
Initialize Terraform
terraform init
When upgrading an existing configuration:
terraform init -upgrade
Confirm the selected providers:
terraform providers
Review the Terraform Plan
terraform plan -out=tfplan
The important dependency is created by this reference:
service_name = ybm_private_service_endpoint.aws.service_name
Terraform knows that the Aeon PSE must exist before it can create the AWS interface endpoint.
Apply the Configuration
terraform apply tfplan
Display the outputs:
terraform output
Example output:
aws_vpc_endpoint_id = "vpce-0123456789abcdef0"
aws_vpc_endpoint_state = "available"
yugabyte_pse_endpoint_id = "11111111-2222-3333-4444-555555555555"
yugabyte_pse_host = "pse-us-east-1.example.aws.yugabyte.cloud"
yugabyte_pse_service_name = "com.amazonaws.vpce.us-east-1.vpce-svc-0123456789abcdef0"
yugabyte_pse_state = "ACTIVE"
Verify the AWS Endpoint
Check the endpoint state:
aws ec2 describe-vpc-endpoints \
--vpc-endpoint-ids "$(terraform output -raw aws_vpc_endpoint_id)" \
--query 'VpcEndpoints[0].State' \
--output text
Expected result:
available
Check private DNS:
aws ec2 describe-vpc-endpoints \
--vpc-endpoint-ids "$(terraform output -raw aws_vpc_endpoint_id)" \
--query 'VpcEndpoints[0].PrivateDnsEnabled' \
--output text
Expected result:
True
The AWS Terraform provider exposes private_dns_enabled on interface endpoint resources. YugabyteDB’s AWS PrivateLink procedure directs users to create the application-side interface endpoint after creating the Aeon PSE.
Verify Private DNS
Run this from an EC2 instance, container, or application host inside the application VPC:
PSE_HOST="$(terraform output -raw yugabyte_pse_host)"
nslookup "${PSE_HOST}"
You can also use dig:
dig "${PSE_HOST}"
The hostname should resolve through the interface endpoint to private addresses.
Connect with ysqlsh
Download the cluster CA certificate and connect from a host inside the application VPC:
PSE_HOST="$(terraform output -raw yugabyte_pse_host)"
PGSSLMODE=verify-full \
PGSSLROOTCERT=./root.crt \
ysqlsh \
-h "${PSE_HOST}" \
-p 5433 \
-U \
-d yugabyte
Verify the connection:
SELECT version();
Match the Availability Zones
The PSE resource exposes the availability zones supported by the endpoint service:
ybm_private_service_endpoint.aws.availability_zones
The selected AWS endpoint subnets must be compatible with those zones.
Do not assume that an availability zone name such as us-east-1a represents the same physical zone in every AWS account. Compare the corresponding AWS availability zone IDs when necessary.
Create One PSE Per Aeon Region
For a multi-region Aeon cluster, create a PSE in every region where applications need private access. Each PSE has a corresponding interface endpoint in the application VPC.
A multi-region Terraform pattern can use for_each:
variable "pse_regions" {
type = set(string)
default = [
"us-east-1",
"us-west-2"
]
}
resource "ybm_private_service_endpoint" "aws" {
for_each = var.pse_regions
cluster_id = var.ybm_cluster_id
region = each.value
security_principals = [
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
]
}
The application-side AWS endpoint resources would also need to be created in the corresponding regions.
Why the Native Terraform Resource Is Better
| Capability | YBM CLI with local-exec | Native Terraform Resource |
|---|---|---|
| Terraform state | Endpoint is not fully represented | Endpoint attributes are stored in state |
| Service name | Must be parsed or copied | Available as a computed output |
| Dependencies | Managed through scripts and triggers | Handled by the dependency graph |
| Destroy | Requires separate cleanup logic | Handled by Terraform |
| Drift detection | Limited |
Visible during terraform plan
|
Destroy the Connection
terraform destroy
Terraform removes the AWS interface endpoint before removing the Aeon PSE because the dependency is represented directly in the configuration.
Final Takeaway
ybm_private_service_endpoint to create the YugabyteDB Aeon side of the connection. Pass its computed service_name directly to aws_vpc_endpoint. The result is a declarative PrivateLink deployment with state tracking, dependency management, reusable outputs, drift detection, and clean resource destruction. Resources
- 👉 YugabyteDB Aeon: Connect VPCs Using AWS PrivateLink
- 👉 YugabyteDB Aeon: Private Service Endpoints
- 👉 YugabyteDB Aeon Terraform Provider
- 👉 YugabyteDB Aeon Terraform Provider Configuration
- 👉
ybm_private_service_endpointTerraform Resource - 👉 HashiCorp AWS:
aws_vpc_endpointTerraform Resource - 👉 AWS: Configure an Interface VPC Endpoint
- 👉 AWS: Availability Zone IDs
Have Fun!
