Deploying a relational database for demos or experiments should not cost a fortune. In this post we will use the AWS Cloud Development Kit (CDK) with Python to provision a Virtual Private Cloud (VPC), a bastion host, and an Amazon RDS PostgreSQL instance that stays within (or very close to) the AWS Free Tier.
π GitHub Repository: https://github.com/r3xakead0/aws-cdk-rds
The full project is available in the repository root of this article (aws-cdk-rds
). We will walk through the architecture, configuration knobs, and the commands you need to deploy and destroy the stack safely.
Why CDK for RDS?
Infrastructure as code makes it trivial to share, reproduce, and tear down environments. CDK lets you write that definition in familiar Python, combining high-level constructs with fine-grained AWS configuration.
Our stack delivers:
- A VPC with public and private isolated subnets across two Availability Zones.
- An Amazon Linux bastion host (
t3.nano
) accessible only from your trusted IP range. - A PostgreSQL 17 instance (
db.t3.micro
) with 20β―GB of GP3 storage, encryption at rest, and daily backups. - Optional integration with AWS Secrets Manager for password generation.
- CloudFormation outputs to simplify client connections (endpoint, port, secret ARN, bastion instance ID).
Prerequisites
- Python 3.9 or newer.
- Node.js 16+ and the AWS CDK Toolkit (
npm install -g aws-cdk
). - AWS credentials with permissions for VPC, EC2, RDS, Secrets Manager, and CloudFormation.
- Internet access to install Python packages and (optionally) to fetch the AdventureWorks sample dataset.
If
cdk init
could not create a virtual environment automatically, just runpython3 -m venv .venv
manually.
Project Structure
.
βββ app.py # CDK entry point
βββ aws_cdk_rds/
β βββ aws_cdk_rds_stack.py # Stack definition (VPC, bastion, RDS, outputs)
βββ database/
β βββ adventureworks.sql # Sample dataset you can load manually
βββ script/
β βββ psql.sh # Helper script that loads AdventureWorks via psql
βββ README.md / README.es.md # Reference documentation (English and Spanish)
βββ aws_cdk_rds_diagram.py # Optional diagram generator using diagrams + graphviz
Configure Your Environment
Create and activate the virtual environment, then install dependencies:
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
Customizable Parameters
You can configure the stack through environment variables or CDK context parameters (cdk deploy -c key=value
). The most common knobs are:
Parameter | Default | Description |
---|---|---|
DB_NAME |
adventureworks |
Initial database name. |
DB_USERNAME |
dbadmin |
Master username for the RDS instance. |
DB_PASSWORD |
(generated) | Master password. If omitted, CDK creates a Secrets Manager secret. |
BASTION_ALLOW_CIDR |
0.0.0.0/0 |
CIDR range allowed to SSH into the bastion. Restrict this. |
DB_ALLOWED_CIDR |
(none) | Optional extra CIDR with direct access to port 5432. |
Example using environment variables:
export DB_USERNAME=demo_admin
export BASTION_ALLOW_CIDR=203.0.113.5/32
cdk deploy
Example using CDK context flags:
cdk deploy \
-c db_username=demo_admin \
-c bastion_allow_cidr=203.0.113.5/32
Deploying the Stack
Run the standard CDK workflow:
# Bootstrap (only once per account/region)
cdk bootstrap aws://<ACCOUNT_ID>/<REGION>
# Review the CloudFormation template
cdk synth
# Deploy everything
cdk deploy
Once deployment finishes, note the outputs:
- DbEndpoint / DbPort β connection details for PostgreSQL.
- BastionId β EC2 instance ID, useful for AWS Systems Manager Session Manager.
- DbSecretArn β ARN of the generated secret (present only when the password was not provided manually).
Accessing the Database
-
Connect to the bastion host
-
SSH:
ssh -i <your-key.pem> ec2-user@<bastion-public-ip>
-
Session Manager:
aws ssm start-session --target <BastionId> --region <REGION>
-
SSH:
Launch
psql
export PGHOST=<DbEndpoint>
export PGPORT=<DbPort>
export PGUSER=<DB_USERNAME>
export PGPASSWORD='<PASSWORD>' # Retrieve from Secrets Manager if auto-generated
psql -h "$PGHOST" -U "$PGUSER" -d "$DB_NAME" -c 'SELECT version();'
To fetch the password from the generated secret:
aws secretsmanager get-secret-value \
--secret-id <DbSecretArn> \
--query 'SecretString' \
--output text | jq -r '.password'
Loading the AdventureWorks Dataset
The repo ships with database/adventureworks.sql
, a script you can run to populate the database. Copy it to the bastion (or download it) and execute:
psql -h "$PGHOST" -U "$PGUSER" -d "$DB_NAME" -f adventureworks.sql
Verify the schemas:
psql -h "$PGHOST" -U "$PGUSER" -d "$DB_NAME" -c '\dn'
psql -h "$PGHOST" -U "$PGUSER" -d "$DB_NAME" -c '\dt humanresources.*'
The helper script at script/psql.sh
bundles these steps; update endpoints and credentials before running it in your environment.
Cleaning Up
Remember to remove everything to avoid charges:
cdk destroy
This command deletes the RDS instance, the bastion host, the Secrets Manager secret, and the networking resources that were deployed.
Troubleshooting Checklist
-
SSH timeout? Make sure
BASTION_ALLOW_CIDR
includes your current public IP. -
psql
connection refused? Confirm the bastion security group is allowed to reach port 5432 and that the database finished provisioning. - Password missing? Retrieve it from Secrets Manager using the command above.
-
Graphviz errors when generating diagrams? Install Graphviz so that the
dot
executable is available (e.g.,brew install graphviz
), then rerunpython3 aws_cdk_rds_diagram.py
.
Final Thoughts
With fewer than 250 lines of Python, this CDK stack gives you a reusable, low-cost PostgreSQL environment tailored for workshops or proofs of concept. From here you can:
- Add application servers or AWS Lambda functions in the same VPC.
- Introduce parameter groups, enhanced monitoring, or automatic backups with longer retention.
- Swap the manual data load for AWS Systems Manager Automation or CodeBuild pipelines.
Resources
- π» Source Code on GitHub
- π AWS CDK Documentation
- π PostgreSQL Documentation
Happy building! If you extend the stack, share your experience or drop a commentβI'd love to hear about your use case.
Top comments (0)