AWS Cloud Formation allows you to programmatically create a template that you can use to spin up multiple instances of an object (such as an RDS server) and repeat the process on multiple locations, accounts, etc. Today I am setting up an RDS instance that needs to be the same in three different AWS accounts. I have one for development (DEV), implementation (IMPL) and production (PROD).
This blog post assumes you have a basic understanding of how Cloud Formation works as well as basic familiarity with coding in YAML.
BEFORE STARTING MY Cloud Formation (CF) Template, I have the following already configured in my AWS Account.
- VPC
- IAM User Account
- MSAD (Microsoft Active Directory Services – Enterprise)
- SES (AWS Simple Email Service) This can be skipped or set up later if not needed.
I will deploy three CF Scripts to create the RESOURCES that I need to complete this. At first this may seem like a longer method than using the wizard to set up an RDS Server, but if you have more than two instances to create in multiple environments, I think this will save you time in the long run.
QUICK REVIEW OF CLOUD FORMATION
My templates have four sections:
- METADATA: This tells CF how to organize the initial input form.
- PARAMATERS: These are the values that will need to be supplied for each use.
- CONDITIONS: If not equal to this value, template will not run.
- RESOURCES: This is detail of each resource that CF will create, when run.
SHARED RESOURCES SCRIPT
My first CF Script will create these resources which may be shared among all RDS instances:
- KMS ENCRYPTION KEY (I advise against using the AWS default and will create my own.)
- RDS IAM ROLE
- S3 BUCKETS ( I will set up three buckets. Two for RDS and one for my CF scripts.)
- SECURITY GROUPS
AWSTemplateFormatVersion: 2010-09-09
Description: (June 2022 version) This template (database-shared-resources) sets up kms key, s3 buckets. All IPs are examples only and need updating.
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Network configuration
Parameters:
- EnvironmentName
- EnvironmentType
- ThisVPC
- CidrIp
- CidrIp2
- CidrIp3
ParameterLabels:
EnvironmentName:
default: "Environment Name"
EnviornmentType:
default: "Enviornmetn Type"
CidrIp:
default: "IP range to grant to ports"
ThisVPC:
default: "VPC"
Parameters:
EnvironmentName:
Description: Environment Name.
Default: mycompany
Type: String
AllowedValues:
- mycompany
EnvironmentType:
Description: Environment Type.
Type: String
AllowedValues:
- prod
- impl
- dev
ConstraintDescription: must specify prod, impl or dev.
ThisVPC:
Description: VPC to operate in
Type: AWS::EC2::VPC::Id
CidrIp:
Description: IP range to grant to ports (internal aws)
Type: String
Default: "10.2.7.0/23"
CidrIp2:
Description: IP range to grant to ports (workspaces public)
Type: String
Default: "18.2.7.0/23"
CidrIp3:
Description: IP range to grant to ports (workspaces adapter)
Type: String
Default: "198.2.7.0/23"
Conditions:
isUsEastOne: !Equals
- !Ref AWS::Region
- us-east-1
Resources:
## Create KMS Encryption Key to be used by RDS
RsaDatabaseKey:
Type: 'AWS::KMS::Key'
Properties:
Description: Multi-Region primary key for use by rds
MultiRegion: true
EnableKeyRotation: true
PendingWindowInDays: 10
KeyPolicy:
Version: 2012-10-17
Id: !Sub ${EnvironmentName}-${EnvironmentType}-database-key-01
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Action: 'kms:*'
Resource: '*'
# TODO MAY NEED TO COMMENT OUT -- until resource is created...... This will allow Rds to backup and restore.
# MANUALLY ADD This configuration if commented out.
# - Sid: Allow use of the key
# Effect: Allow
# Principal:
# AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:role/${EnvironmentName}-${EnvironmentType}-database-rds-role'
# Action: 'kms:*'
# Resource: '*'
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-${EnvironmentType}-database-key-01
RsaDatabaseKeyAlias:
Type: 'AWS::KMS::Alias'
Properties:
AliasName: !Sub alias/${EnvironmentName}-${EnvironmentType}-database-key-01
TargetKeyId: !Ref RsaDatabaseKey
## Create S3 Buckets for rds to use
DatabaseS3bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${EnvironmentName}-${EnvironmentType}-database
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: 'aws:kms'
KMSMasterKeyID: !Ref RsaDatabaseKey
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
DatabaseAuditS3bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${EnvironmentName}-${EnvironmentType}-database-audit
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: 'aws:kms'
KMSMasterKeyID: !Ref RsaDatabaseKey
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
## Deny access to S3 bucket if connection is not secure.
DatabaseAuditS3bucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref DatabaseAuditS3bucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Action: 's3:*'
Effect: Deny
Principal: '*'
Resource:
- !Sub "arn:aws:s3:::${EnvironmentName}-${EnvironmentType}-database-audit"
- !Sub "arn:aws:s3:::${EnvironmentName}-${EnvironmentType}-database-audit/*"
Condition:
Bool:
'aws:SecureTransport': false
## Create a bucket to store this CF Script and future ones as well.
CloudFormScriptsS3bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${EnvironmentName}-${EnvironmentType}-cloud-formation
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: 'aws:kms'
KMSMasterKeyID: !Ref RsaDatabaseKey
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
## Create IAM Role for rds
DatabaseRdsIamRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
- ssm.amazonaws.com
- rds.amazonaws.com
- ds.amazonaws.com
Action:
- 'sts:AssumeRole'
Description: Role to provide rds with s3 access
#Path: /if/required/
#PermissionsBoundary: !Sub arn:aws:iam::${AWS::AccountId}:policy/if/required/boundary-policy
Policies:
- PolicyName: !Sub ${EnvironmentName}-${EnvironmentType}-database-rds-access
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:ListAllMyBuckets
- s3:GetAccessPoint
- s3:GetAccountPublicAccessBlock
- s3:GetBucketLocation
- s3:ListAccessPoints
- s3:ListBucket
- s3:ListJobs
- s3:PutObject
- s3:PutObjectAcl
Resource: '*'
- Effect: Allow
Action:
- ds:DescribeDirectories
- ds:AuthorizeApplication
- ds:UnauthorizeApplication
- ds:GetAuthorizedApplicationDetails
Resource: '*'
- Effect: Allow
Action:
- rds:*
Resource: '*'
- Effect: Allow
Action: kms:*
Resource: !Ref RsaDatabaseKey
# TODO If the above should fail, replace it with the example below, after looking up KMS Key arn, when rerunning the script.
# Resource: !Sub 'arn:aws:kms:us-east-1:${AWS::AccountId}:key/mrk-1234567890abcdef'
- Effect: Allow
Action:
- iam:AttachRolePolicy
- iam:CreateRole
- iam:CreateServiceLinkedRole
- iam:DeleteRolePolicy
- iam:DeleteRole
- iam:DeleteServiceLinkedRole
- iam:GetPolicy
- iam:GetPolicyVersion
- iam:GetRole
- iam:GetRolePolicy
- iam:GetServiceLinkedRoleDeletionStatus
- iam:ListAttachedRolePolicies
- iam:ListPolicies
- iam:ListRolePolicies
- iam:ListRoles
- iam:ListRoleTags
- iam:PassRole
- iam:PutRolePolicy
- iam:UpdateRole
- iam:UpdateRoleDescription
Resource: '*'
#- "arn:aws:iam::*:role/aws-service-role/*"
- Effect: Allow
Action:
- s3:*
Resource:
- '*'
- !Sub 'arn:aws:s3:::${EnvironmentName}-${EnvironmentType}-database/*'
- !Sub 'arn:aws:s3:::${EnvironmentName}-${EnvironmentType}-database-audit/*'
RoleName: !Sub ${EnvironmentName}-${EnvironmentType}-database-rds-role
## Security Groups for Rds
DBServerRdsSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable 1433 access from VPC for sql rds
GroupName: !Sub ${EnvironmentName}-${EnvironmentType}-database-rds
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '1433'
ToPort: '1433'
CidrIp: !Ref CidrIp
Description: Entire VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-${EnvironmentType}-database-rds
VpcId: !Ref ThisVPC
DBServerSsrsSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable 8443 access from VPC for sql rds ssrs
GroupName: !Sub ${EnvironmentName}-${EnvironmentType}-database-rds-ssrs
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '8443'
ToPort: '8443'
CidrIp: !Ref CidrIp
Description: Entire VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-${EnvironmentType}-database-rds-ssrs
VpcId: !Ref ThisVPC
# Create security groups that will allow access to the RDP instance.
MSSQLExternalSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable 1433 access from VPC for sql rds
GroupName: !Sub ${EnvironmentName}-${EnvironmentType}-database-mssql-external
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '1433'
ToPort: '1433'
CidrIp: !Ref CidrIp
Description: Instance 1 Internal East
- IpProtocol: tcp
FromPort: '1433'
ToPort: '1433'
CidrIp: !Ref CidrIp2
Description: Instance 1 Workspaces Public East
- IpProtocol: tcp
FromPort: '1433'
ToPort: '1433'
CidrIp: !Ref CidrIp3
Description: Instance 1 Workspaces adapter East
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-${EnvironmentType}-database-mssql-external
VpcId: !Ref ThisVPC
# Create security group that PowerShell will use. (This is not used by RDS and may be skipped here.)
PowerShellSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable 5985 access for remote PowerShell management
GroupName: !Sub ${EnvironmentName}-${EnvironmentType}-powershell
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '5985'
ToPort: '5985'
CidrIp: !Ref CidrIp
Description: Entire VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-${EnvironmentType}-powershell
VpcId: !Ref ThisVPC
# Create security group that will use AWS Simple Email Service (SES) to send database mail.
SesVpcSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for Amazon SES SMTP VPC endpoint
GroupName: !Sub ${EnvironmentName}-${EnvironmentType}-ses-vpc-endpoint-security-group
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '587'
ToPort: '587'
CidrIp: !Ref CidrIp
Description: Entire VPC
- IpProtocol: tcp
FromPort: '465'
ToPort: '465'
CidrIp: !Ref CidrIp
Description: Entire VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-${EnvironmentType}-ses-vpc-endpoint-security-group
VpcId: !Ref ThisVPC
# COMMENT OUT BELOW: The below is an additional group that would be created if you plan on using SSRS on an EC2 instance.
SsrsSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable port access from VPC for ssrs web server(s)
GroupName: !Sub ${EnvironmentName}-${EnvironmentType}-ssrs-web
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: !Ref CidrIp
Description: Entire VPC
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: !Ref CidrIp2
Description: Workspace Public IP address
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: !Ref CidrIp3
Description: Workspace Adapter IP address
- IpProtocol: tcp
FromPort: '443'
ToPort: '443'
CidrIp: !Ref CidrIp
Description: Entire VPC
- IpProtocol: tcp
FromPort: '443'
ToPort: '443'
CidrIp: !Ref CidrIp2
Description: Workspace Public IP address
- IpProtocol: tcp
FromPort: '443'
ToPort: '443'
CidrIp: !Ref CidrIp3
Description: Workspace Adapter IP address
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-${EnvironmentType}-ssrs-web
VpcId: !Ref ThisVPC
Line 61: Creates a KMS Encruyption Key. It is important that I create my own and not use AWS default key since I plan on utilizing services such as SSRS and the new subscription email feature.
Line 88: Assigns an alias to the KMS Key.
Line 94: Creates three (3) S3 buckets for use by RDS, RDS Auditing, and to store my CF scripts.
Line 117: Defines the bucket policy to deny any connection that is not secure.
Line 146: Creates the RDS IAM ROLE that RDS will later require.
Line 164: Defines the Policies for the above role.
Line 230: Sets up security groups to allow connections to the RDS instance(s).
I’ve added a couple extra optional groups that I use for PowerShell and SQL Server on EC2.
I will upload this script to Cloud Formation as shown.
RDS SCRIPT PART A (Options)
TO create the RDS Server I will want to make sure I have my PARAMETER GROUP, OPTION GROUP and SUBNET GROUP all set up.
I will configure these PARAMETERS:
cost threshold for parallelism: 50
max degree of parallelism: 2
clr enabled: 1
max server memory (mb): 28672
database mail xps: 1
rds.force_ssl: 1
I will include these OPTIONS:
SQLSERVER_BACKUP_RESTORE: This will use teh IAM ROLE I created earlier.
SQLSERVER_AUDIT: This will use the S3 bucker I created previously.
SSIS
SSRS: To configure Subscription Email options, please see my post on that subject.
AWSTemplateFormatVersion: 2010-09-09
Description: This template (example-database-rds2019-msad-PartA) sets up kms key, s3 buckets, role, param group, option group and rds instance(s)
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Network configuration
Parameters:
- ThisVPC
- EnvironmentName
- EnvironmentType
- Label:
default: Amazon EC2 configuration
Parameters:
- InstanceType
- SqlServerInstance1Name
- InstanceDeleteProtection
- DBAllocatedStorage
- DBAllocatedStorageMax
- DBbIops
- InstanceMultiAZ
- RdsRsaKey
- Label:
default: Active Directory configuration
Parameters:
- MSDirectoryService
- SubnetList
- VPCSecurityGroups
- VpcSsrsOptionSecurityGroup
- Label:
default: SQL Server RDS configuration
Parameters:
- SqlVersion
- DBAdminUser
- DBAdminPassword
- BackupRetentionDays
- DatabaseS3bucket
- DatabaseAuditS3bucket
ParameterLabels:
ThisVPC:
default: "VPC"
EnvironmentName:
default: "Environment Name"
EnviornmentType:
default: "Enviornment Type"
InstanceType:
default: "Instance type size"
SqlServerInstance1Name:
default: "Enter a name for RDS server"
InstanceDeleteProtection:
default: "Protect the instance from being deleted"
DBAllocatedStorage:
default: "Initial storage (GiB)"
DBAllocatedStorageMax:
default: "Maximum allowed storage (GiB)"
DBbIops:
default: "The number of I/O operations per second (IOPS) that the database provisions"
InstanceMultiAZ:
default: "Deploy in multiple availability zones"
RdsRsaKey:
default: "Rsa encryption key"
MSDirectoryService:
default: "Directory Services"
SubnetList:
default: "Select at least two (2) subnets for Multi-AZ"
VPCSecurityGroups:
default: "Attach these security groups to allow access"
VpcSsrsOptionSecurityGroup:
default: "Security group configured for Rds Options in Ssrs"
SqlVersion:
default: "Versions: (14 = SQL 2017 | 15 = SQL 2019)"
DBAdminUser:
default: "The default user of --admin-- should be used unless a change is required"
DBAdminPassword:
default: "Enter a password (8-40 characters, alpha numeric, at least one capital letter and one number)"
BackupRetentionDays:
default: "Enter numberof days to retain snapshot"
DatabaseS3bucket:
default: "Bucket to access for storing backups"
DatabaseAuditS3bucket:
default: "Bucket to access for storing sql audit logs"
Parameters:
ThisVPC:
Description: VPC to operate in
Type: AWS::EC2::VPC::Id
EnvironmentName:
Description: Environment Name.
Default: mycompany
Type: String
AllowedValues:
- mycompany
EnvironmentType:
Description: Environment Type.
Type: String
AllowedValues:
- prod
- impl
- dev
ConstraintDescription: must specify prod, impl or dev.
DBAdminUser:
NoEcho: 'false'
Default: 'admin'
Description: Database admin account username
Type: String
MinLength: '1'
MaxLength: '16'
AllowedPattern: "[a-zA-Z][a-zA-Z0-9]*"
ConstraintDescription: must begin with a letter and contain only alphanumeric characters.
DBAdminPassword:
NoEcho: 'true'
Description: Admin password
Type: String
MinLength: '8'
MaxLength: '41'
AllowedPattern: "^(?=.*[0-9])(?=.*[a-zA-Z])([a-zA-Z0-9]+)"
ConstraintDescription: Must contain only alphanumeric characters with at least one capital letter and one number.
SqlServerInstance1Name:
Description: RDS SQL Server Instance 1 Name (alpha,numberic,-,_)
Type: String
MinLength: '1'
MaxLength: '63'
AllowedPattern: "[a-zA-Z][a-zA-Z0-9]*"
ConstraintDescription: Must contain only alphanumeric characters.
InstanceMultiAZ:
Description: Deploy into multiple availability zones.
Type: String
AllowedValues:
- true
- false
ConstraintDescription: Select true for Multi-AZ, else false.
InstanceDeleteProtection:
Description: Do NOT allow deletion of the instance.
Type: String
Default: false
AllowedValues:
- true
- false
InstanceType:
Description: Instance type (cpu:2-4-8| ram:16-32-64)
Type: String
AllowedValues:
- db.r5.xlarge
- db.r5.2xlarge
- db.r5.4xlarge
- db.r5.8xlarge
DBAllocatedStorage:
Default: '500'
Description: The size of the database (GiB)
Type: Number
MinValue: '20'
MaxValue: '65536'
ConstraintDescription: must be between 20 and 65536 GiB.
DBAllocatedStorageMax:
Default: '1000'
Description: The max size of the database (GiB) may grow.
Type: Number
MinValue: '20'
MaxValue: '65536'
ConstraintDescription: must be between 20 and 65536 GiB.
DBbIops:
Default: '1000'
Description: The Iops
Type: Number
SqlVersion:
Description: Intall version 14 (SQL 2017) or 15 (SQL 2019)
Type: String
Default: 15.00.4073.23.v1
AllowedValues:
- 14.00.3381.3.v1
- 15.00.4073.23.v1
SubnetList:
Description: Subnet IDs for Rds to Use
Type: "List<AWS::EC2::Subnet::Id>"
VPCSecurityGroups:
Description: Security Groups to allow Rds
Type: "List<AWS::EC2::SecurityGroup::Id>"
VpcSsrsOptionSecurityGroup:
Description: Security Group for SSRS
Type: "List<AWS::EC2::SecurityGroup::Id>"
MSDirectoryService:
Description: "MS AD"
Type: String
AllowedValues:
# TODO Update with current SAD accounts
- "d-9009009a1"
- "d-9009009b2"
- "d-9009009c3"
ConstraintDescription: Select MS AD for this enviornment.
BackupRetentionDays:
Default: '7'
Description: Number of daily snapshots to retain.
Type: Number
MinValue: '1'
MaxValue: '35'
ConstraintDescription: must be between 1 and 35.
RdsRsaKey:
Description: RSA Encryption Key
Type: String
AllowedValues:
# TODO Update these values with current KMS ARN from environemnts
- "arn:aws:kms:us-east-1:111111111111:key/mrk-1234567890abcdef1234567890abcdef"
- "arn:aws:kms:us-east-1:222222222222:key/mrk-98765432109876543210987654321098"
- "arn:aws:kms:us-east-1:333333333333:key/mrk-abcdef12345678987654321fedcba1"
ConstraintDescription: Select RSA Key.
DatabaseS3bucket:
Description: S3 bucket access for rds backup files
Type: String
AllowedValues:
- "mycompany-dev-database"
- "mycompany-impl-database"
- "mycompany-prod-database"
ConstraintDescription: Select S3 bucket for backups.
DatabaseAuditS3bucket:
Description: S3 bucket to store sql audit logs
Type: String
AllowedValues:
- "mycompany-dev-database-audit"
- "mycompany-impl-database-audit"
- "mycompany-prod-database-audit"
ConstraintDescription: Select S3 bucket for audit.
Conditions:
isUsEastOne: !Equals
- !Ref AWS::Region
- us-east-1
Resources:
## Rds Patamater Group for Sql EE | Naming for this is automated ie: StackName+RandomGUID
rdsdbparametergroup15:
Type: 'AWS::RDS::DBParameterGroup'
Properties:
Description: Sql Server 2019 ee custom paramaters
Family: sqlserver-ee-15.0
Parameters:
cost threshold for parallelism: 50
max degree of parallelism: 2
clr enabled: 1
max server memory (mb): 28672
database mail xps: 1
rds.force_ssl: 1
## Option Group Customization for Rds SSIS and SSRS
RdsOptionGroupSqlEe15:
Type: AWS::RDS::OptionGroup
Properties:
EngineName: sqlserver-ee
MajorEngineVersion: '15.00'
OptionGroupDescription: OptionGroup for sql server 2019 ee with ssrs, ssis, backup, audit
OptionConfigurations:
- OptionName: SQLSERVER_BACKUP_RESTORE
OptionSettings:
- Name: IAM_ROLE_ARN
Value: !Sub arn:aws:iam::${AWS::AccountId}:role/${EnvironmentName}-${EnvironmentType}-database-rds-role
#Value: !Sub arn:aws:iam::${AWS::AccountId}:role/PermissionsBoundary/IfNeeded/${EnvironmentName}-${EnvironmentType}-database-rds-role
- OptionName: SSIS
- OptionName: SSRS
OptionSettings:
- Name: MAX_MEMORY
Value: 25
Port: 8443
VpcSecurityGroupMemberships: !Ref VpcSsrsOptionSecurityGroup
- OptionName: SQLSERVER_AUDIT
OptionSettings:
- Name: S3_BUCKET_ARN
Value: !Sub arn:aws:s3:::${EnvironmentName}-${EnvironmentType}-database-audit
- Name: IAM_ROLE_ARN
Value: !Sub arn:aws:iam::${AWS::AccountId}:role/${EnvironmentName}-${EnvironmentType}-database-rds-role
#Value: !Sub arn:aws:iam::${AWS::AccountId}:role/PermissionsBoundary/IfNeeded/${EnvironmentName}-${EnvironmentType}-database-rds-role
Tags:
- Key: Name
Value: RdsOptionGroupSqlEe15ssrs
# Subnet Group is used to create the MULTI-AZ instance. Two or more subnets must be selected in the SubnetList this references from PARAMETERS.
DBRdsSubnetGroup:
Properties:
DBSubnetGroupDescription: Rds data tier subnet group
SubnetIds: !Ref SubnetList
Tags:
-
Key: Name
Value: !Sub ${EnvironmentName}-rds-private-subnets
Type: "AWS::RDS::DBSubnetGroup"
After I’ve successfully run the script I will check to make sure my groups were created and write down the ARN numbers to use in the next template.
RDS SCRIPT PART B (SQL Server)
My script will create the RDS database instance. If you are thinking that PART A and PART B could be combined, you are correct. I have split it up here, because this gives flexibility so that I can delete, modify, and add other RDS instances without affecting the other option resources.
AWSTemplateFormatVersion: 2010-09-09
Description: This template (example-database-rds2019-msad-partB) sets up kms key, s3 buckets, role, param group, option group and rds instance(s)
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Network configuration
Parameters:
- ThisVPC
- EnvironmentName
- EnvironmentType
- Label:
default: Amazon EC2 configuration
Parameters:
- InstanceType
- SqlServerInstance1Name
- InstanceDeleteProtection
- DBAllocatedStorage
- DBAllocatedStorageMax
- DBbIops
- InstanceMultiAZ
- RdsRsaKey
- Label:
default: Active Directory configuration
Parameters:
- MSDirectoryService
- VPCSecurityGroups
- VpcSsrsOptionSecurityGroup
- Label:
default: SQL Server RDS configuration
Parameters:
- SqlVersion
- DBAdminUser
- DBAdminPassword
- BackupRetentionDays
- DatabaseS3bucket
- DatabaseAuditS3bucket
ParameterLabels:
ThisVPC:
default: "VPC"
EnvironmentName:
default: "Environment Name"
EnviornmentType:
default: "Enviornment Type"
InstanceType:
default: "Instance type size"
SqlServerInstance1Name:
default: "Enter a name for RDS server"
InstanceDeleteProtection:
default: "Protect the instance from being deleted"
DBAllocatedStorage:
default: "Initial storage (GiB)"
DBAllocatedStorageMax:
default: "Maximum allowed storage (GiB)"
DBbIops:
default: "The number of I/O operations per second (IOPS) that the database provisions"
InstanceMultiAZ:
default: "Deploy in multiple availability zones"
RdsRsaKey:
default: "Rsa encryption key"
MSDirectoryService:
default: "Directory Services"
VPCSecurityGroups:
default: "Attach these security groups to allow access"
VpcSsrsOptionSecurityGroup:
default: "Security group configured for Rds Options in Ssrs"
SqlVersion:
default: "Versions: (14 = SQL 2017 | 15 = SQL 2019)"
DBAdminUser:
default: "The default user of --admin-- should be used unless a change is required"
DBAdminPassword:
default: "Enter a password (8-40 characters, alpha numeric, at least one capital letter and one number)"
BackupRetentionDays:
default: "Enter numberof days to retain snapshot"
DatabaseS3bucket:
default: "Bucket to access for storing backups"
DatabaseAuditS3bucket:
default: "Bucket to access for storing sql audit logs"
Parameters:
ThisVPC:
Description: VPC to operate in
Type: AWS::EC2::VPC::Id
EnvironmentName:
Description: Environment Name.
Default: mycompany
Type: String
AllowedValues:
- mycompany
EnvironmentType:
Description: Environment Type.
Type: String
AllowedValues:
- prod
- impl
- dev
ConstraintDescription: must specify prod, impl or dev.
DBAdminUser:
NoEcho: 'false'
Default: 'admin'
Description: Database admin account username
Type: String
MinLength: '1'
MaxLength: '16'
AllowedPattern: "[a-zA-Z][a-zA-Z0-9]*"
ConstraintDescription: must begin with a letter and contain only alphanumeric characters.
DBAdminPassword:
NoEcho: 'true'
Description: Admin password
Type: String
MinLength: '8'
MaxLength: '41'
AllowedPattern: "^(?=.*[0-9])(?=.*[a-zA-Z])([a-zA-Z0-9]+)"
ConstraintDescription: Must contain only alphanumeric characters with at least one capital letter and one number.
SqlServerInstance1Name:
Description: RDS SQL Server Instance 1 Name (alpha,numberic,-,_)
Type: String
MinLength: '1'
MaxLength: '63'
AllowedPattern: "[a-zA-Z][a-zA-Z0-9]*"
ConstraintDescription: Must contain only alphanumeric characters.
InstanceMultiAZ:
Description: Deploy into multiple availability zones.
Type: String
AllowedValues:
- true
- false
ConstraintDescription: Select true for Multi-AZ, else false.
InstanceDeleteProtection:
Description: Do NOT allow deletion of the instance.
Type: String
Default: false
AllowedValues:
- true
- false
InstanceType:
Description: Instance type (cpu:2-4-8| ram:16-32-64-128)
Type: String
AllowedValues:
- db.r5.xlarge
- db.r5.2xlarge
- db.r5.4xlarge
- db.r5.8xlarge
DBAllocatedStorage:
Default: '500'
Description: The size of the database (GiB)
Type: Number
MinValue: '20'
MaxValue: '65536'
ConstraintDescription: must be between 20 and 65536 GiB.
DBAllocatedStorageMax:
Default: '1000'
Description: The max size of the database (GiB) may grow.
Type: Number
MinValue: '20'
MaxValue: '65536'
ConstraintDescription: must be between 20 and 65536 GiB.
DBbIops:
Default: '1000'
Description: The Iops
Type: Number
SqlVersion:
Description: Intall version 14 (SQL 2017) or 15 (SQL 2019)
Type: String
Default: 15.00.4073.23.v1
AllowedValues:
- 14.00.3381.3.v1
- 15.00.4073.23.v1
VPCSecurityGroups:
Description: Security Groups to allow Rds
Type: "List<AWS::EC2::SecurityGroup::Id>"
VpcSsrsOptionSecurityGroup:
Description: Security Group for SSRS
Type: "List<AWS::EC2::SecurityGroup::Id>"
MSDirectoryService:
Description: "MS AD"
Type: String
AllowedValues:
# TODO Update with current SAD accounts
- "d-9009009a1"
- "d-9009009b2"
- "d-9009009c3"
ConstraintDescription: Select MS AD for this enviornment.
BackupRetentionDays:
Default: '7'
Description: Number of daily snapshots to retain.
Type: Number
MinValue: '1'
MaxValue: '35'
ConstraintDescription: must be between 1 and 35.
RdsRsaKey:
Description: RSA Encryption Key
Type: String
AllowedValues:
# TODO Update these values with current KMS ARN from environemnts
- "arn:aws:kms:us-east-1:111111111111:key/mrk-1234567890abcdef1234567890abcdef"
- "arn:aws:kms:us-east-1:222222222222:key/mrk-98765432109876543210987654321098"
- "arn:aws:kms:us-east-1:333333333333:key/mrk-abcdef12345678987654321fedcba1"
ConstraintDescription: Select RSA Key.
DatabaseS3bucket:
Description: S3 bucket access for rds backup files
Type: String
AllowedValues:
- "mycompany-dev-database"
- "mycompany-impl-database"
- "mycompany-prod-database"
ConstraintDescription: Select S3 bucket for backups.
DatabaseAuditS3bucket:
Description: S3 bucket to store sql audit logs
Type: String
AllowedValues:
- "mycompany-dev-database-audit"
- "mycompany-impl-database-audit"
- "mycompany-prod-database-audit"
ConstraintDescription: Select S3 bucket for audit.
Conditions:
isUsEastOne: !Equals
- !Ref AWS::Region
- us-east-1
Resources:
# Create the RDS Instance
DBInstanceWithAD:
Type: AWS::RDS::DBInstance
Properties:
AllocatedStorage: !Ref DBAllocatedStorage
AllowMajorVersionUpgrade: 'false'
AutoMinorVersionUpgrade: 'true'
BackupRetentionPeriod: !Ref BackupRetentionDays
DBInstanceClass: !Ref InstanceType
DBInstanceIdentifier: !Ref SqlServerInstance1Name
# Change this (below) accordingly ....
DBSubnetGroupName: mycompany-database-rds2019a-dbrdssubnetgroup-987654
# Change this (below) accordingly ....
DBParameterGroupName: mycompany-database-rds2019a-rdsdbparametergroup15-1123abcd
DeletionProtection: !Ref InstanceDeleteProtection
Domain: !Ref MSDirectoryService
DomainIAMRoleName: !Sub ${EnvironmentName}-${EnvironmentType}-database-rds-role
#DomainIAMRoleName: !Sub PermissionsBoundary/IfNeeded/${EnvironmentName}-${EnvironmentType}-database-rds-role
EnableCloudwatchLogsExports:
- error
- agent
Engine: sqlserver-ee
EngineVersion: !Ref SqlVersion
Iops: !Ref DBbIops
KmsKeyId: !Ref RdsRsaKey
LicenseModel: license-included
MasterUsername: !Ref DBAdminUser
MasterUserPassword: !Ref DBAdminPassword
MaxAllocatedStorage: !Ref DBAllocatedStorageMax
MultiAZ: !Ref InstanceMultiAZ
# Change this (below) accordingly ....
OptionGroupName: mycompany-database-rds2019a-rdsoptiongroupsqlee15-zxyasd456
PubliclyAccessible: 'false'
PreferredBackupWindow: 01:00-02:00
PreferredMaintenanceWindow: sun:04:00-sun:05:00
StorageEncrypted: 'true'
StorageType: io1
VPCSecurityGroups: !Ref VPCSecurityGroups
DeletionPolicy: Snapshot
My CF Form should look similar to this:
I proceeded through the steps until the RDS Server is set up.
Depending on my configuration, it may take hours before the status updates to AVAILABLE.
Once I get that green indicator, I open up SSMS on a workspace and verify that I can log in.