DEV Community

Cover image for Hands-On: Deploying a PostgreSQL + Bastion + VPC Stack Using AWS CDK + Python
Afu Tse (Chainiz)
Afu Tse (Chainiz)

Posted on

Hands-On: Deploying a PostgreSQL + Bastion + VPC Stack Using AWS CDK + Python

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 run python3 -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
Enter fullscreen mode Exit fullscreen mode

Configure Your Environment

Create and activate the virtual environment, then install dependencies:

python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Example using CDK context flags:

cdk deploy \
  -c db_username=demo_admin \
  -c bastion_allow_cidr=203.0.113.5/32
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

  1. 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>
  2. 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();'
Enter fullscreen mode Exit fullscreen mode

To fetch the password from the generated secret:

aws secretsmanager get-secret-value \
  --secret-id <DbSecretArn> \
  --query 'SecretString' \
  --output text | jq -r '.password'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Verify the schemas:

psql -h "$PGHOST" -U "$PGUSER" -d "$DB_NAME" -c '\dn'
psql -h "$PGHOST" -U "$PGUSER" -d "$DB_NAME" -c '\dt humanresources.*'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 rerun python3 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

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)