A CDK construct for running Liquibase migrations against Amazon RDS instances and clusters using AWS CodeBuild.
- ✅ Universal RDS Support: Works with any RDS instance or Aurora cluster
- ✅ Flexible Commands: Execute any Liquibase command (update, rollback, validate, etc.)
- ✅ Secure: Uses IAM roles and VPC security groups for secure database access
- ✅ Configurable: Customizable Docker images, timeouts, and environment variables
- ✅ Monitored: Built-in CloudWatch logging with configurable retention
- ✅ Multi-language: Available in TypeScript, Python, Java, and C#
Note: This construct is currently in development. Publishing to package managers is disabled until the first stable release.
For now, you can use this construct by:
- Cloning the repository
- Building locally with
npm run build - Installing as a local dependency
git clone https://github.com/alest314/LiquibaseRDS.git
cd LiquibaseRDS
npm install
npm run buildThen in your CDK project:
npm install /path/to/LiquibaseRDSimport{LiquibaseRDS}from'LiquibaseRDS';import*asrdsfrom'aws-cdk-lib/aws-rds';import*asec2from'aws-cdk-lib/aws-ec2';// Assume you have an existing RDS instance and VPCdeclareconstdatabase: rds.DatabaseInstance;declareconstvpc: ec2.Vpc;newLiquibaseRDS(this,'MyLiquibaseMigration',{rdsInstance: database,liquibaseCommand: 'update',changelogPath: './database/changelogs',databaseUsername: 'admin',databasePassword: database.secret?.secretArn,
vpc,});The construct creates:
- CodeBuild Project: Runs Liquibase commands using the official Docker image
- IAM Role: Provides necessary permissions for RDS access and S3 operations
- S3 Assets: Uploads your changelog files to S3 for CodeBuild access
- CloudWatch Logs: Captures execution logs (optional)
- Security Groups: Manages network access between CodeBuild and RDS
| Property | Type | Description |
|---|---|---|
rdsInstance | rds.IDatabaseInstance | rds.IDatabaseCluster | The RDS instance or cluster to run migrations against |
liquibaseCommand | string | The Liquibase command to execute (e.g., 'update', 'rollback') |
changelogPath | string | Local path to the directory containing changelog files |
| Property | Type | Default | Description |
|---|---|---|---|
databaseUsername | string | 'admin' | Database username for connection |
databasePassword | string | - | Database password (ARN for Secrets Manager) |
databaseName | string | - | Specific database name to connect to |
databasePort | number | 5432 | Database port number |
vpc | ec2.IVpc | - | VPC for CodeBuild execution |
subnets | ec2.SubnetSelection | - | Subnets for CodeBuild |
securityGroups | ec2.ISecurityGroup[] | - | Security groups for CodeBuild |
liquibaseImage | string | 'liquibase/liquibase:latest' | Docker image to use |
additionalArgs | string[] | [] | Additional Liquibase arguments |
environmentVariables | object | {} | Custom environment variables |
timeout | Duration | Duration.hours(1) | CodeBuild timeout |
enableLogging | boolean | true | Enable CloudWatch logging |
logRetention | RetentionDays | ONE_WEEK | Log retention period |
import{LiquibaseRDS}from'LiquibaseRDS';import*asrdsfrom'aws-cdk-lib/aws-rds';import*asec2from'aws-cdk-lib/aws-ec2';import{Duration}from'aws-cdk-lib';// Create RDS instance with Secrets Managerconstdatabase=newrds.DatabaseInstance(this,'Database',{engine: rds.DatabaseInstanceEngine.postgres({version: rds.PostgresEngineVersion.VER_13_13,}),instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3,ec2.InstanceSize.MICRO),
vpc,credentials: rds.Credentials.fromGeneratedSecret('admin'),databaseName: 'myapp',});// Run Liquibase migrationsnewLiquibaseRDS(this,'DatabaseMigration',{rdsInstance: database,liquibaseCommand: 'update',changelogPath: './database/migrations',databaseUsername: 'admin',databasePassword: database.secret?.secretArn,databaseName: 'myapp',databasePort: 5432,
vpc,subnets: {subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,},timeout: Duration.minutes(30),additionalArgs: ['--log-level=INFO'],});// Aurora PostgreSQL clusterconstcluster=newrds.DatabaseCluster(this,'Cluster',{engine: rds.DatabaseClusterEngine.auroraPostgres({version: rds.AuroraPostgresEngineVersion.VER_13_7,}),instanceProps: {instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3,ec2.InstanceSize.MEDIUM),
vpc,},credentials: rds.Credentials.fromGeneratedSecret('admin'),});// Validation run with custom Liquibase versionnewLiquibaseRDS(this,'DatabaseValidation',{rdsInstance: cluster,liquibaseCommand: 'validate',changelogPath: './database/changelogs',databaseUsername: 'admin',databasePassword: cluster.secret?.secretArn,
vpc,liquibaseImage: 'liquibase/liquibase:4.24',environmentVariables: {LIQUIBASE_HUB_MODE: {value: 'off'},JAVA_OPTS: {value: '-Xmx1g'},},enableLogging: true,});constmysqlDb=newrds.DatabaseInstance(this,'MySQLDB',{engine: rds.DatabaseInstanceEngine.mysql({version: rds.MysqlEngineVersion.VER_8_0,}),// ... other configuration});newLiquibaseRDS(this,'DatabaseRollback',{rdsInstance: mysqlDb,liquibaseCommand: 'rollback-count',changelogPath: './database/changelogs',databasePort: 3306,additionalArgs: ['1'],// Rollback 1 changeset// ... other configuration});Your changelog directory should contain Liquibase changelog files. Here's an example structure:
changelogs/
├── changelog.xml # Master changelog
├── 001-create-users-table.xml
├── 002-create-posts-table.xml
└── 003-add-foreign-keys.xml
Example master changelog (changelog.xml):
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLogxmlns="http://www.liquibase.org/xml/ns/dbchangelog"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.0.xsd">
<includefile="001-create-users-table.xml"relativeToChangelogFile="true"/>
<includefile="002-create-posts-table.xml"relativeToChangelogFile="true"/>
<includefile="003-add-foreign-keys.xml"relativeToChangelogFile="true"/>
</databaseChangeLog>- Recommended: Use AWS Secrets Manager to store database credentials
- Pass the secret ARN to the
databasePasswordproperty - The construct automatically grants the CodeBuild role permission to read the secret
// Using Secrets ManagerdatabasePassword: database.secret?.secretArn,- Deploy CodeBuild in private subnets with NAT Gateway access
- Use security groups to restrict database access
- The construct automatically creates security group rules for RDS connectivity
// Create security group for CodeBuildconstcodeBuildSG=newec2.SecurityGroup(this,'CodeBuildSG',{ vpc });// Allow CodeBuild to connect to RDSdatabase.connections.allowFrom(codeBuildSG,ec2.Port.tcp(5432));newLiquibaseRDS(this,'Migration',{// ... other propssecurityGroups: [codeBuildSG],subnets: {subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,},});The construct provides built-in CloudWatch integration:
- Execution Logs: All Liquibase output is captured in CloudWatch Logs
- Build Status: Monitor CodeBuild execution status
- Custom Metrics: Add custom CloudWatch metrics as needed
newLiquibaseRDS(this,'Migration',{// ... other propsenableLogging: true,logRetention: logs.RetentionDays.ONE_MONTH,});| Command | Description |
|---|---|
update | Apply all pending changesets |
validate | Validate changelog syntax |
status | Show pending changesets |
rollback-count | Rollback specified number of changesets |
rollback-to-tag | Rollback to a specific tag |
generate-changelog | Generate changelog from existing database |
- Connection Timeout: Ensure CodeBuild can reach RDS through security groups and NACLs
- Permission Denied: Verify IAM roles have necessary RDS and S3 permissions
- Changelog Not Found: Check that changelog files are in the specified path
- Database Connection: Verify database credentials and endpoint configuration
Enable debug logging for troubleshooting:
newLiquibaseRDS(this,'Migration',{// ... other propsadditionalArgs: ['--log-level=DEBUG'],environmentVariables: {LIQUIBASE_LOG_LEVEL: {value: 'DEBUG'},},});Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Made with ❤️ by AlexTech314