-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CodeBuild RDS Flyway example (#11)
Added a sample in rds-flyway-migrations that includes a Cloudformation stack that creates a VPC, RDS Serverless Aurora V2 instance, and CodeBuild script to run Flyway migrations.
- Loading branch information
Showing
7 changed files
with
415 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# RDS Flyway Migrations Sample | ||
|
||
This sample project creates an RDS instance within the private subnet of a VPC, and installs a CodeBuild project to run Flyway database migrations to provision the database. | ||
|
||
Setup steps: | ||
|
||
1. Install AWS CLI | ||
2. Access an AWS account with rights to create an RDS instance, VPC, and create and run CodeBuilds. | ||
3. Run the deploy.sh script - two positional parameters are | ||
- the name of the stack to create | ||
- the region to create your stack within | ||
|
||
- Example: | ||
```bash | ||
./deploy.sh rds-flyway-migrations us-east-2 | ||
``` | ||
4. Run the project created within CodeBuild to perform the migrations | ||
5. Observe the logs to see that the environment variables for the username, database name and password are not exposed to the logs | ||
6. You may also use the RDS control panel to perform queries against the database | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,362 @@ | ||
## | ||
## Sample RDS configuration with Flyway migrations | ||
## | ||
|
||
AWSTemplateFormatVersion: "2010-09-09" | ||
Description: "VPC, CodeBuild and RDS for Flyway Demo" | ||
|
||
Parameters: | ||
BaseCIDR: | ||
Description: "The first two bytes of a /16 CIDR that will be used for the VPC" | ||
Type: "String" | ||
Default: "10.10" | ||
|
||
GitHubRepoUrl: | ||
Description: "The URL of the project to clone" | ||
Type: "String" | ||
Default: "https://github.com/chariotsolutions/aws-examples.git" | ||
|
||
Resources: | ||
VPC: | ||
Type: "AWS::EC2::VPC" | ||
Properties: | ||
CidrBlock: !Sub "${BaseCIDR}.0.0/16" | ||
EnableDnsSupport: true | ||
EnableDnsHostnames: true | ||
Tags: | ||
- Key: "Name" | ||
Value: "rds-flyway-migrations-sample" | ||
|
||
PublicSubnet01: | ||
Type: "AWS::EC2::Subnet" | ||
Properties: | ||
VpcId: !Ref VPC | ||
CidrBlock: !Sub "${BaseCIDR}.0.0/20" | ||
AvailabilityZone: !Sub "${AWS::Region}a" | ||
MapPublicIpOnLaunch: true | ||
|
||
PublicSubnet02: | ||
Type: "AWS::EC2::Subnet" | ||
Properties: | ||
VpcId: !Ref VPC | ||
CidrBlock: !Sub "${BaseCIDR}.16.0/20" | ||
AvailabilityZone: !Sub "${AWS::Region}b" | ||
MapPublicIpOnLaunch: true | ||
|
||
InternetGateway: | ||
Type: "AWS::EC2::InternetGateway" | ||
|
||
InternetGatewayAttachment: | ||
Type: "AWS::EC2::VPCGatewayAttachment" | ||
Properties: | ||
VpcId: !Ref VPC | ||
InternetGatewayId: !Ref InternetGateway | ||
|
||
PublicSubnet01Routes: | ||
Type: "AWS::EC2::SubnetRouteTableAssociation" | ||
Properties: | ||
SubnetId: !Ref PublicSubnet01 | ||
RouteTableId: !Ref PublicRouteTable | ||
|
||
PublicSubnet02Routes: | ||
Type: "AWS::EC2::SubnetRouteTableAssociation" | ||
Properties: | ||
SubnetId: !Ref PublicSubnet02 | ||
RouteTableId: !Ref PublicRouteTable | ||
|
||
PublicRouteTableIGW: | ||
Type: "AWS::EC2::Route" | ||
Properties: | ||
RouteTableId: !Ref PublicRouteTable | ||
DestinationCidrBlock: "0.0.0.0/0" | ||
GatewayId: !Ref InternetGateway | ||
|
||
PublicRouteTable: | ||
Type: "AWS::EC2::RouteTable" | ||
Properties: | ||
VpcId: !Ref VPC | ||
|
||
## | ||
## Private Subnets - two are configured to support RDS (which requires two subnets for a serverless implementation) | ||
## | ||
|
||
PrivateSubnet01: | ||
Type: "AWS::EC2::Subnet" | ||
Properties: | ||
VpcId: !Ref VPC | ||
CidrBlock: !Sub "${BaseCIDR}.128.0/20" | ||
AvailabilityZone: !Sub "${AWS::Region}a" | ||
MapPublicIpOnLaunch: false | ||
|
||
PrivateSubnet02: | ||
Type: "AWS::EC2::Subnet" | ||
Properties: | ||
VpcId: !Ref VPC | ||
CidrBlock: !Sub "${BaseCIDR}.144.0/20" | ||
AvailabilityZone: !Sub "${AWS::Region}b" | ||
MapPublicIpOnLaunch: false | ||
|
||
PrivateRouteTable: | ||
Type: "AWS::EC2::RouteTable" | ||
Properties: | ||
VpcId: !Ref VPC | ||
|
||
PrivateSubnet01Routes: | ||
Type: "AWS::EC2::SubnetRouteTableAssociation" | ||
Properties: | ||
SubnetId: !Ref PrivateSubnet01 | ||
RouteTableId: !Ref PrivateRouteTable | ||
|
||
PrivateSubnet02Routes: | ||
Type: "AWS::EC2::SubnetRouteTableAssociation" | ||
Properties: | ||
SubnetId: !Ref PrivateSubnet02 | ||
RouteTableId: !Ref PrivateRouteTable | ||
|
||
## | ||
## NAT configuration - this is a demonstration sample, so we are only configuring a single NAT. | ||
## For production, each AZ would have its own NAT. | ||
## | ||
|
||
ElasticIP01: | ||
Type: "AWS::EC2::EIP" | ||
Properties: | ||
Domain: "vpc" | ||
|
||
NAT: | ||
Type: "AWS::EC2::NatGateway" | ||
Properties: | ||
SubnetId: !Ref PublicSubnet01 | ||
AllocationId: !GetAtt ElasticIP01.AllocationId | ||
|
||
PrivateRouteNAT: | ||
Type: "AWS::EC2::Route" | ||
Properties: | ||
RouteTableId: !Ref PrivateRouteTable | ||
DestinationCidrBlock: "0.0.0.0/0" | ||
NatGatewayId: !Ref NAT | ||
|
||
## | ||
## Configure a SecretsManager Secret to hold the database name, user name and password | ||
## in an encrypted store | ||
## | ||
|
||
DBSecrets: | ||
Type: AWS::SecretsManager::Secret | ||
Properties: | ||
Description: "Secrets for the demo application" | ||
GenerateSecretString: | ||
SecretStringTemplate: | | ||
{ | ||
"dbDatabaseName": "demodb", | ||
"dbUserName": "demouser" | ||
} | ||
GenerateStringKey: "dbPassword" | ||
ExcludePunctuation: true # easier to select for those times that you need to copy/paste | ||
PasswordLength: 20 | ||
|
||
## | ||
## Define network visibility rules via a security group and ingress rule | ||
## | ||
DatabaseSecurityGroup: | ||
Type: "AWS::EC2::SecurityGroup" | ||
Properties: | ||
GroupName: !Sub "${AWS::StackName}-dbSG" | ||
GroupDescription: "Allows access to the database (nominally from application)" | ||
VpcId: !Ref VPC | ||
|
||
DatabaseSecurityGroupCBtoDB: | ||
Type: "AWS::EC2::SecurityGroupIngress" | ||
Properties: | ||
GroupId: !Ref DatabaseSecurityGroup | ||
Description: "Access from codebuild container" | ||
IpProtocol: "tcp" | ||
FromPort: 5432 | ||
ToPort: 5432 | ||
SourceSecurityGroupId: !GetAtt CodeBuildSecurityGroup.GroupId | ||
|
||
## | ||
## Database - Aurora Serverless V2 | ||
## | ||
|
||
RDSParameterGroup: | ||
Type: "AWS::RDS::DBClusterParameterGroup" | ||
Properties: | ||
Description: !Sub "Managed by CloudFormation: ${AWS::StackName}" | ||
Family: "aurora-postgresql14" | ||
Parameters: | ||
client_encoding: "UTF8" | ||
|
||
RDSSubnetGroup: | ||
Type: "AWS::RDS::DBSubnetGroup" | ||
Properties: | ||
DBSubnetGroupName: !Sub "${AWS::StackName}-rds-subnet-group" | ||
DBSubnetGroupDescription: !Sub "Managed by CloudFormation: ${AWS::StackName}" | ||
SubnetIds: [ !Ref PrivateSubnet01, !Ref PrivateSubnet02 ] | ||
|
||
DatabaseCluster: | ||
Type: "AWS::RDS::DBCluster" | ||
Properties: | ||
Engine: "aurora-postgresql" | ||
EngineVersion: "14.6" | ||
ServerlessV2ScalingConfiguration: | ||
MinCapacity: 0.5 | ||
MaxCapacity: 2 | ||
# enable logs? | ||
# EnableCloudwatchLogsExports: | ||
# - postgresql | ||
DBClusterParameterGroupName: !Ref RDSParameterGroup | ||
MasterUsername: !Sub "{{resolve:secretsmanager:${DBSecrets}:SecretString:dbUserName}}" | ||
MasterUserPassword: !Sub "{{resolve:secretsmanager:${DBSecrets}:SecretString:dbPassword}}" | ||
DatabaseName: !Sub "{{resolve:secretsmanager:${DBSecrets}:SecretString:dbDatabaseName}}" | ||
Port: 5432 # note, default is 3306, even for Postgres | ||
DBSubnetGroupName: !Ref RDSSubnetGroup | ||
VpcSecurityGroupIds: | ||
- !Ref DatabaseSecurityGroup | ||
# can define when backups and maintenance run | ||
# PreferredMaintenanceWindow: "Sun:06:00-Sun:06:59" | ||
# PreferredBackupWindow: "05:00-05:30" | ||
BackupRetentionPeriod: 7 | ||
StorageEncrypted: true | ||
|
||
ServerlessInstance: | ||
Type: "AWS::RDS::DBInstance" | ||
Properties: | ||
DBClusterIdentifier: !Ref DatabaseCluster | ||
DBInstanceClass: db.serverless | ||
Engine: aurora-postgresql | ||
|
||
|
||
## | ||
## CodeBuild - set up security group, execution role and CodeBuild project | ||
## | ||
|
||
CodeBuildSecurityGroup: | ||
Type: "AWS::EC2::SecurityGroup" | ||
Properties: | ||
GroupName: !Sub "${AWS::StackName}-CodeBuildSG" | ||
GroupDescription: "Security Group for CodeBuild" | ||
VpcId: !Ref VPC | ||
|
||
|
||
CodeBuildExecutionRole: | ||
Type: "AWS::IAM::Role" | ||
Properties: | ||
RoleName: !Sub "${AWS::StackName}-cbExecRole" | ||
Path: "/builder/" | ||
AssumeRolePolicyDocument: | ||
Version: "2012-10-17" | ||
Statement: | ||
- Effect: "Allow" | ||
Principal: | ||
Service: | ||
- "codebuild.amazonaws.com" | ||
Action: | ||
- "sts:AssumeRole" | ||
ManagedPolicyArns: | ||
- "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser" | ||
- "arn:aws:iam::aws:policy/AWSCloudFormationReadOnlyAccess" | ||
Policies: | ||
- PolicyName: CodeBuildLoggingPolicy | ||
PolicyDocument: | ||
Version: "2012-10-17" | ||
Statement: | ||
- Effect: "Allow" | ||
Action: | ||
- "logs:CreateLogGroup" | ||
- "logs:CreateLogStream" | ||
- "logs:PutLogEvents" | ||
Resource: "*" | ||
- PolicyName: CodeBuildECSTaskExecution | ||
PolicyDocument: | ||
Version: "2012-10-17" | ||
Statement: | ||
- # Secrets manager | ||
Effect: "Allow" | ||
Action: | ||
- "secretsmanager:Describe*" | ||
- "secretsmanager:Get*" | ||
- "secretsmanager:List*" | ||
Resource: !Ref DBSecrets | ||
- PolicyName: CodeBuildEC2NetworkingAccess | ||
PolicyDocument: | ||
Version: "2012-10-17" | ||
Statement: | ||
- # Access to networking resources | ||
Effect: Allow | ||
Action: | ||
- "ec2:CreateNetworkInterface" | ||
- "ec2:DescribeDhcpOptions" | ||
- "ec2:DescribeNetworkInterfaces" | ||
- "ec2:DeleteNetworkInterface" | ||
- "ec2:DescribeSubnets" | ||
- "ec2:DescribeSecurityGroups" | ||
- "ec2:DescribeVpcs" | ||
- "ec2:CreateNetworkInterfacePermission" | ||
Resource: "*" | ||
|
||
MigrationsProject: | ||
Type: AWS::CodeBuild::Project | ||
Properties: | ||
Name: !Sub "${AWS::StackName}-cbMigrations" | ||
Description: Run DB Migrations for the platform | ||
ServiceRole: !GetAtt CodeBuildExecutionRole.Arn | ||
Artifacts: | ||
Type: NO_ARTIFACTS | ||
VpcConfig: | ||
VpcId: !Ref VPC | ||
SecurityGroupIds: [ !Ref CodeBuildSecurityGroup ] | ||
Subnets: [ !Ref PrivateSubnet01, !Ref PrivateSubnet02 ] | ||
LogsConfig: | ||
CloudWatchLogs: | ||
GroupName: "/codebuild/migration" | ||
Status: ENABLED | ||
StreamName: "codebuild-log" | ||
Environment: | ||
Type: LINUX_CONTAINER | ||
ComputeType: BUILD_GENERAL1_SMALL | ||
Image: aws/codebuild/amazonlinux2-x86_64-standard:4.0 | ||
PrivilegedMode: true | ||
EnvironmentVariables: | ||
- Name: "DB_HOSTNAME" | ||
Value: !GetAtt DatabaseCluster.Endpoint.Address | ||
|
||
- Name: "DB_PORT" | ||
Value: !GetAtt DatabaseCluster.Endpoint.Port | ||
|
||
- Name: "AWS_REGION" | ||
Value: !Sub "${AWS::Region}" | ||
|
||
- Name: "DB_SECRETS_ARN" | ||
Value: !Ref DBSecrets | ||
|
||
Source: | ||
Auth: | ||
Type: OAUTH | ||
Location: !Ref GitHubRepoUrl | ||
Type: GITHUB | ||
GitCloneDepth: 1 | ||
BuildSpec: | | ||
version: 0.2 | ||
env: | ||
secrets-manager: | ||
DB_DATABASE: ${DB_SECRETS_ARN}:dbDatabaseName | ||
DB_USERNAME: ${DB_SECRETS_ARN}:dbUserName | ||
DB_PASSWORD: ${DB_SECRETS_ARN}:dbPassword | ||
phases: | ||
install: | ||
runtime-versions: | ||
java: corretto17 | ||
build: | ||
commands: | ||
- wget -qO- https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/9.11.0/flyway-commandline-9.11.0-linux-x64.tar.gz | tar xvz && ln -s `pwd`/flyway-9.11.0/flyway /usr/local/bin | ||
- cd rds-flyway-migrations/flyway | ||
- flyway migrate -locations=filesystem:migrations/**/*.sql -password=$DB_PASSWORD -user=$DB_USERNAME -url=jdbc:postgresql://$DB_HOSTNAME:$DB_PORT/$DB_DATABASE -connectRetries=300 -X | ||
TimeoutInMinutes: 10 | ||
|
||
Outputs: | ||
DatabaseEndpointAddress: | ||
Value: !GetAtt DatabaseCluster.Endpoint.Address | ||
|
||
DatabaseEndpointPort: | ||
Value: !GetAtt DatabaseCluster.Endpoint.Port |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Use with: ./deploy.sh stackname region | ||
aws cloudformation deploy --stack-name $1 --region $2 --capabilities CAPABILITY_NAMED_IAM --template-file cloudformation.yml |
1 change: 1 addition & 0 deletions
1
rds-flyway-migrations/flyway/migrations/V2023.02.08.001__config-db-options.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CREATE EXTENSION IF NOT EXISTS pgcrypto; |
Oops, something went wrong.