--- # This template is designed to be interchangable with other Database cfn templates. This is the required parameter set for compatibility # DBMasterUserPassword # CustomDBSecurityGroup # Subnet1ID # Subnet2ID # DBAutoMinorVersionUpgrade # DBAllocatedStorage # DBBackupRetentionPeriod # DBInstanceClass # DBIops # DBMasterUsername # DBMultiAZ # DBPort # DBAllocatedStorageEncrypted # DBStorageType AWSTemplateFormatVersion: 2010-09-09 Description: RDS Postgres Database for use in Atlassian Standard Infrastructure (qs-1aj6s44e4) Metadata: QSLint: Exclusions: [W4002, E3012, E1001, E9101, W9006, W9002, W9003, ERDSDBInstancePubliclyAccessible, EIAMPolicyWildcardResource, ERDSStorageEncryptionEnabled] Parameters: DBEngineVersion: Default: 10 AllowedValues: - 9.6 - 10 - 11 - 12 Description: "The database engine version to use. We'll install a supported minor version that's suitable for your chosen engine. (Warning: Amazon RDS for PostgreSQL 9.6 will reach end of life on January 31st, 2022. Deployments after this date should not be made using this version. If you wish to upgrade to a major version from 9 see: https://confluence.atlassian.com/x/1IRlQQ)" Type: String DBAutoMinorVersionUpgrade: Default: true AllowedValues: - true - false Description: "Select true/false to setup Auto Minor Version upgrade. e.g. PostgreSQL 9.6.8 -> 9.6.11" Type: String DBBackupRetentionPeriod: Default: "7" Description: "The number of days for which automatic database snapshots are retained." Type: String DBInstanceClass: Default: db.m5.large AllowedValues: - db.m5.large - db.m5.xlarge - db.m5.2xlarge - db.m5.4xlarge - db.m5.12xlarge - db.m5.24xlarge - db.m4.large - db.m4.xlarge - db.m4.2xlarge - db.m4.4xlarge - db.m4.10xlarge - db.m4.16xlarge - db.r5.large - db.r5.xlarge - db.r5.2xlarge - db.r5.4xlarge - db.r5.12xlarge - db.r5.24xlarge - db.r4.large - db.r4.xlarge - db.r4.2xlarge - db.r4.4xlarge - db.r4.8xlarge - db.r4.16xlarge - db.t3.medium - db.t3.large - db.t3.xlarge - db.t3.2xlarge - db.t2.medium - db.t2.large - db.t2.xlarge - db.t2.2xlarge ConstraintDescription: Must be a valid RDS instance class from the list Description: RDS instance type Type: String DBIops: Default: 1000 ConstraintDescription: Must be in the range 1000 - 30000. Description: 'Must be in the range of 1000 - 30000 and a multiple of 1000. This value is only used with Provisioned IOPS. Note: The ratio of IOPS per allocated-storage must be between 3.00 and 10.00.' MinValue: 1000 MaxValue: 30000 Type: Number DBMasterUsername: AllowedPattern: '[a-zA-Z][a-zA-Z0-9]*' ConstraintDescription: must begin with a letter and contain only alphanumeric characters. Default: postgres Description: The database admin account username MaxLength: '16' MinLength: '1' NoEcho: 'true' Type: String DBMasterUserPassword: AllowedPattern: >- ^(?=^.{8,255}$)(?=.*[a-z])(?=.*[A-Z])(?=.*\d)((?=.*[^A-Za-z0-9])(?!.*[@/"'])).*$ ConstraintDescription: >- Min 8 chars. Must include 1 uppercase, 1 lowercase, 1 number, 1 (non / @ " ') symbol Description: Password for the master ('postgres') account. MinLength: 8 MaxLength: 128 NoEcho: true Type: String DBMultiAZ: Description: Whether to provision a multi-AZ RDS instance. Default: false AllowedValues: - true - false ConstraintDescription: Must be 'true' or 'false'. Type: String DBAllocatedStorage: Default: 10 Description: Database allocated storage size, in gigabytes (GB). If you choose Provisioned IOPS, storage should be between 100 and 6144 Type: Number DBAllocatedStorageEncrypted: Default: false AllowedValues: - true - false Description: Whether or not to encrypt the database Type: String DBStorageType: Default: General Purpose (SSD) AllowedValues: - General Purpose (SSD) - Provisioned IOPS ConstraintDescription: Must be 'General Purpose (SSD)' or 'Provisioned IOPS'. Description: Database storage type Type: String CustomDBSecurityGroup: Description: The security group to apply to the database Type: AWS::EC2::SecurityGroup::Id Subnet1ID: Description: Private Subnet 1 ID Type: AWS::EC2::Subnet::Id Subnet2ID: Description: Private Subnet 2 ID Type: AWS::EC2::Subnet::Id Conditions: DBProvisionedIops: !Equals [!Ref DBStorageType, io1] UseDatabaseEncryption: !Equals [!Ref DBAllocatedStorageEncrypted, true] Resources: DBSubnetGroup: Type: "AWS::RDS::DBSubnetGroup" Properties: DBSubnetGroupDescription: "Subnets available for the Postgres database instance" SubnetIds: - !Ref Subnet1ID - !Ref Subnet2ID EncryptionKey: Condition: UseDatabaseEncryption DeletionPolicy: Retain UpdateReplacePolicy: Retain Type: AWS::KMS::Key Properties: KeyPolicy: Version: 2012-10-17 Id: !Sub "${AWS::StackName}" Statement: - Effect: Allow Principal: AWS: - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:root" Action: - kms:CancelKeyDeletion - kms:ConnectCustomKeyStore - kms:CreateAlias - kms:CreateCustomKeyStore - kms:CreateGrant - kms:CreateKey - kms:Decrypt - kms:DeleteAlias - kms:DeleteCustomKeyStore - kms:DeleteImportedKeyMaterial - kms:DescribeCustomKeyStores - kms:DescribeKey - kms:DisableKey - kms:DisableKeyRotation - kms:DisconnectCustomKeyStore - kms:EnableKey - kms:EnableKeyRotation - kms:Encrypt - kms:GenerateDataKey - kms:GenerateDataKeyPair - kms:GenerateDataKeyPairWithoutPlaintext - kms:GenerateDataKeyWithoutPlaintext - kms:GenerateRandom - kms:GetKeyPolicy - kms:GetKeyRotationStatus - kms:GetParametersForImport - kms:GetPublicKey - kms:ImportKeyMaterial - kms:ListAliases - kms:ListGrants - kms:ListKeyPolicies - kms:ListKeys - kms:ListResourceTags - kms:ListRetirableGrants - kms:PutKeyPolicy - kms:ReEncryptFrom - kms:ReEncryptTo - kms:ReplicateKey - kms:RetireGrant - kms:RevokeGrant - kms:ScheduleKeyDeletion - kms:Sign - kms:SynchronizeMultiRegionKey - kms:TagResource - kms:UntagResource - kms:UpdateAlias - kms:UpdateCustomKeyStore - kms:UpdateKeyDescription - kms:UpdatePrimaryRegion - kms:Verify Resource: '*' Tags: - Key: Name Value: !Sub ["${StackName} Encryption Key", StackName: !Ref 'AWS::StackName'] EnableKeyRotation: true EncryptionKeyAlias: Condition: UseDatabaseEncryption Type: AWS::KMS::Alias Properties: AliasName: !Sub "alias/${AWS::StackName}" TargetKeyId: !Ref EncryptionKey ## We need a custom resource to generate a meaningful name. If the stack name ## is longer than 57 characters, using {StackName}-db would cause a deployment failure DbInstanceName: Type: Custom::DbInstanceName Version: 1.0 Properties: ServiceToken: !GetAtt DBNameGenerator.Arn StackName: !Ref 'AWS::StackName' DBNameGenerator: Type: "AWS::Lambda::Function" Properties: Handler: index.lambda_handler Role: !GetAtt DBNameGeneratorExecutionRole.Arn Runtime: python3.7 Timeout: 120 Code: ZipFile: | import cfnresponse def lambda_handler(event, context): stack_name = event['ResourceProperties']['StackName'] responseData = {} responseData['DBInstanceName'] = stack_name[:57].replace('-','') + "-db" cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) DBNameGeneratorExecutionRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: !Sub "arn:${AWS::Partition}:logs:*:${AWS::AccountId}:log-group:/aws/lambda/*DBNameGenerator*" DBParamGroup: Type: AWS::RDS::DBParameterGroup Properties: Description: "DB Parameter Group for security error logging" Family: !Join [ "", [ "postgres", !Ref DBEngineVersion ] ] Parameters: # The following are recommended to assist security scanning. log_statement: "ddl" log_min_duration_statement: "15000" DB: Type: AWS::RDS::DBInstance Properties: AllocatedStorage: !Ref DBAllocatedStorage AutoMinorVersionUpgrade: !Ref DBAutoMinorVersionUpgrade BackupRetentionPeriod: !Ref DBBackupRetentionPeriod DBInstanceClass: !Ref DBInstanceClass DBInstanceIdentifier: !GetAtt DbInstanceName.DBInstanceName DBParameterGroupName: !Ref DBParamGroup DBSubnetGroupName: !Ref DBSubnetGroup Engine: postgres EngineVersion: !Ref DBEngineVersion Iops: !If [DBProvisionedIops, !Ref DBIops, !Ref 'AWS::NoValue'] KmsKeyId: !If [UseDatabaseEncryption, !GetAtt EncryptionKey.Arn, !Ref 'AWS::NoValue'] MasterUsername: !Ref DBMasterUsername MasterUserPassword: !Ref DBMasterUserPassword MultiAZ: !Ref DBMultiAZ StorageEncrypted: !If [UseDatabaseEncryption, !Ref DBAllocatedStorageEncrypted, !Ref 'AWS::NoValue'] StorageType: !If [DBProvisionedIops, io1, gp2] Tags: - Key: Name Value: !Sub ["${StackName} PostgreSQL Database", StackName: !Ref 'AWS::StackName'] # NOTE: The leading COMMIT/TIMESTAMP are used to locate the position to update; see scripts/update-tags.py - Key: "atl:quickstart:commit-id" Value: "COMMIT: ebd2820214350ed6bb34df8cc0f7fadbb3ffa5e2" - Key: "atl:quickstart:timestamp" Value: "TIMESTAMP: 2021-06-09T01:26:03Z" VPCSecurityGroups: [!Ref CustomDBSecurityGroup] Outputs: RDSEndPointAddress: Description: The Database Connection String Value: !GetAtt DB.Endpoint.Address RDSEndPointPort: Description: The port the DB endpoint listens on Value: !GetAtt DB.Endpoint.Port RDSEncryptionKey: Condition: UseDatabaseEncryption Description: The alias of the encryption key created for RDS Value: !Ref EncryptionKeyAlias