--- AWSTemplateFormatVersion: '2010-09-09' Description: 'Aurora RDS' Mappings: SubnetConfig: VPC: CIDR: "10.0.0.0/16" Public0: CIDR: "10.0.0.0/24" Public1: CIDR: "10.0.1.0/24" Private0: CIDR: "10.0.16.0/20" Private1: CIDR: "10.0.32.0/20" AZRegions: ca-central-1: AZs: ["a", "b"] eu-central-1: AZs: ["a", "b"] eu-west-1: AZs: ["a", "b"] eu-west-2: AZs: ["a", "b"] sa-east-1: AZs: ["a", "b"] us-east-1: AZs: ["a", "b"] us-east-2: AZs: ["a", "b"] us-west-1: AZs: ["a", "b"] us-west-2: AZs: ["a", "b"] Resources: ## VPC VPC: Type: "AWS::EC2::VPC" Properties: EnableDnsSupport: "true" EnableDnsHostnames: "true" CidrBlock: Fn::FindInMap: - "SubnetConfig" - "VPC" - "CIDR" Tags: - Key: "Application" Value: Ref: "AWS::StackName" - Key: "Network" Value: "Public" - Key: "Name" Value: !Sub simpleaws-${AWS::StackName} PublicSubnet0: Type: "AWS::EC2::Subnet" Properties: VpcId: Ref: "VPC" AvailabilityZone: Fn::Sub: - "${AWS::Region}${AZ}" - AZ: !Select [ 0, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] CidrBlock: Fn::FindInMap: - "SubnetConfig" - "Public0" - "CIDR" MapPublicIpOnLaunch: "true" Tags: - Key: "Application" Value: Ref: "AWS::StackName" - Key: "Network" Value: "Public" - Key: "Name" Value: !Join - '' - - !Sub simpleaws-${AWS::StackName} - '-public-' - !Select [ 0, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] PublicSubnet1: Type: "AWS::EC2::Subnet" Properties: VpcId: Ref: "VPC" AvailabilityZone: Fn::Sub: - "${AWS::Region}${AZ}" - AZ: !Select [ 1, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] CidrBlock: Fn::FindInMap: - "SubnetConfig" - "Public1" - "CIDR" MapPublicIpOnLaunch: "true" Tags: - Key: "Application" Value: Ref: "AWS::StackName" - Key: "Network" Value: "Public" - Key: "Name" Value: !Join - '' - - !Sub simpleaws-${AWS::StackName} - '-public-' - !Select [ 1, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] PrivateSubnet0: Type: "AWS::EC2::Subnet" Properties: VpcId: Ref: "VPC" AvailabilityZone: Fn::Sub: - "${AWS::Region}${AZ}" - AZ: !Select [ 0, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] CidrBlock: Fn::FindInMap: - "SubnetConfig" - "Private0" - "CIDR" Tags: - Key: "Application" Value: Ref: "AWS::StackName" - Key: "Network" Value: "Private" - Key: "Name" Value: !Join - '' - - !Sub simpleaws-${AWS::StackName} - '-private-' - !Select [ 0, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] PrivateSubnet1: Type: "AWS::EC2::Subnet" Properties: VpcId: Ref: "VPC" AvailabilityZone: Fn::Sub: - "${AWS::Region}${AZ}" - AZ: !Select [ 1, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] CidrBlock: Fn::FindInMap: - "SubnetConfig" - "Private1" - "CIDR" Tags: - Key: "Application" Value: Ref: "AWS::StackName" - Key: "Network" Value: "Private" - Key: "Name" Value: !Join - '' - - !Sub simpleaws-${AWS::StackName} - '-private-' - !Select [ 1, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] InternetGateway: Type: "AWS::EC2::InternetGateway" Properties: Tags: - Key: "Application" Value: Ref: "AWS::StackName" - Key: "Network" Value: "Public" - Key: "Name" Value: !Join - '' - - !Sub simpleaws-${AWS::StackName} - '-IGW' GatewayToInternet: Type: "AWS::EC2::VPCGatewayAttachment" Properties: VpcId: Ref: "VPC" InternetGatewayId: Ref: "InternetGateway" PublicRouteTable: Type: "AWS::EC2::RouteTable" Properties: VpcId: Ref: "VPC" Tags: - Key: "Application" Value: Ref: "AWS::StackName" - Key: "Network" Value: "Public" - Key: "Name" Value: !Join - '' - - !Sub simpleaws-${AWS::StackName} - '-public-route-table' PublicRoute: Type: "AWS::EC2::Route" DependsOn: "GatewayToInternet" Properties: RouteTableId: Ref: "PublicRouteTable" DestinationCidrBlock: "0.0.0.0/0" GatewayId: Ref: "InternetGateway" PublicSubnetRouteTableAssociation0: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: Ref: "PublicSubnet0" RouteTableId: Ref: "PublicRouteTable" PublicSubnetRouteTableAssociation1: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: Ref: "PublicSubnet1" RouteTableId: Ref: "PublicRouteTable" PublicNetworkAcl: Type: "AWS::EC2::NetworkAcl" Properties: VpcId: Ref: "VPC" Tags: - Key: "Application" Value: Ref: "AWS::StackName" - Key: "Network" Value: "Public" - Key: "Name" Value: !Join - '' - - !Sub simpleaws-${AWS::StackName} - '-public-nacl' InboundHTTPPublicNetworkAclEntry: Type: "AWS::EC2::NetworkAclEntry" Properties: NetworkAclId: Ref: "PublicNetworkAcl" RuleNumber: "100" Protocol: "-1" RuleAction: "allow" Egress: "false" CidrBlock: "0.0.0.0/0" PortRange: From: "0" To: "65535" OutboundPublicNetworkAclEntry: Type: "AWS::EC2::NetworkAclEntry" Properties: NetworkAclId: Ref: "PublicNetworkAcl" RuleNumber: "100" Protocol: "-1" RuleAction: "allow" Egress: "true" CidrBlock: "0.0.0.0/0" PortRange: From: "0" To: "65535" PublicSubnetNetworkAclAssociation0: Type: "AWS::EC2::SubnetNetworkAclAssociation" Properties: SubnetId: Ref: "PublicSubnet0" NetworkAclId: Ref: "PublicNetworkAcl" PublicSubnetNetworkAclAssociation1: Type: "AWS::EC2::SubnetNetworkAclAssociation" Properties: SubnetId: Ref: "PublicSubnet1" NetworkAclId: Ref: "PublicNetworkAcl" ElasticIP0: Type: "AWS::EC2::EIP" Properties: Domain: "vpc" ElasticIP1: Type: "AWS::EC2::EIP" Properties: Domain: "vpc" NATGateway0: Type: "AWS::EC2::NatGateway" Properties: AllocationId: Fn::GetAtt: - "ElasticIP0" - "AllocationId" SubnetId: Ref: "PublicSubnet0" NATGateway1: Type: "AWS::EC2::NatGateway" Properties: AllocationId: Fn::GetAtt: - "ElasticIP1" - "AllocationId" SubnetId: Ref: "PublicSubnet1" PrivateRouteTable0: Type: "AWS::EC2::RouteTable" Properties: VpcId: Ref: "VPC" Tags: - Key: "Name" Value: !Join - '' - - !Sub simpleaws-${AWS::StackName} - '-private-route-table-0' PrivateRouteTable1: Type: "AWS::EC2::RouteTable" Properties: VpcId: Ref: "VPC" Tags: - Key: "Name" Value: !Join - '' - - !Sub simpleaws-${AWS::StackName} - '-private-route-table-1' PrivateRouteToInternet0: Type: "AWS::EC2::Route" Properties: RouteTableId: Ref: "PrivateRouteTable0" DestinationCidrBlock: "0.0.0.0/0" NatGatewayId: Ref: "NATGateway0" PrivateRouteToInternet1: Type: "AWS::EC2::Route" Properties: RouteTableId: Ref: "PrivateRouteTable1" DestinationCidrBlock: "0.0.0.0/0" NatGatewayId: Ref: "NATGateway1" PrivateSubnetRouteTableAssociation0: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: Ref: "PrivateSubnet0" RouteTableId: Ref: "PrivateRouteTable0" PrivateSubnetRouteTableAssociation1: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: Ref: "PrivateSubnet1" RouteTableId: Ref: "PrivateRouteTable1" ## Postgres DBEncryptionKMSKey: Type: AWS::KMS::Key Properties: Description: !Sub "KMS key used to encrypt RDS instance created in CFN stack: ${AWS::StackName}" KeyPolicy: Version: '2012-10-17' Id: default Statement: Sid: "Default" Effect: "Allow" Principal: AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root" Action: "kms:*" Resource: "*" DatabaseSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: CloudFormation managed DB subnet group SubnetIds: - !Ref PrivateSubnet0 - !Ref PrivateSubnet1 Tags: - Key: 'Name' Value: !Sub "Database-subnet-group-Simple-AWS" - Key: Application Value: "Simple AWS" DatabaseSecret: Type: AWS::SecretsManager::Secret Properties: Description: Master secret for the RDS database SecretString: !Ref 'AWS::NoValue' GenerateSecretString: ExcludeCharacters: !Join - '' - - '"' - '@/\!:|`();\\?,&<>%$' - "'" #Excluding characters "@/\!:|`();'?,&<>%$ GenerateStringKey: password PasswordLength: 40 SecretStringTemplate: !Sub '{"username":"SimpleAWS","port":"5432"}' Name: !Join ['', [!Ref 'AWS::StackName', '-RDSMasterSecret'] ] Tags: - Key: Application Value: "Simple AWS" DatabaseSecretAttachment: Type: AWS::SecretsManager::SecretTargetAttachment Properties: SecretId: !Ref DatabaseSecret TargetId: !Ref DatabaseCluster TargetType: AWS::RDS::DBCluster DatabaseCluster: Type: AWS::RDS::DBCluster DeletionPolicy: Snapshot UpdateReplacePolicy: Snapshot Properties: DatabaseName: 'simpleaws' DBClusterIdentifier: !Join [ '-', [ 'simpleaws', 'cluster', !Ref 'AWS::StackName' ] ] DBSubnetGroupName: !Ref DatabaseSubnetGroup Engine: 'aurora-postgresql' EngineVersion: '14.4' Port: 5432 EngineMode: provisioned BackupRetentionPeriod: 7 PreferredBackupWindow: '09:54-10:24' PreferredMaintenanceWindow: Sun:04:00-Sun:08:00 Tags: - Key: 'Name' Value: !Join ['', [!Ref 'AWS::StackName', '::DatabaseCluster'] ] - Key: Application Value: "Simple AWS" VpcSecurityGroupIds: - !GetAtt PostgresSecurityGroup.GroupId MasterUsername: !Sub '{{resolve:secretsmanager:${AWS::StackName}-RDSMasterSecret:SecretString:username}}' MasterUserPassword: !Sub '{{resolve:secretsmanager:${AWS::StackName}-RDSMasterSecret:SecretString:password}}' EnableIAMDatabaseAuthentication: false KmsKeyId: !Ref DBEncryptionKMSKey StorageEncrypted: true DependsOn: - DatabaseSubnetGroup - DatabaseSecret RDSDBInstance1: Type: "AWS::RDS::DBInstance" Properties: DBClusterIdentifier: !Ref DatabaseCluster DBInstanceClass: "db.t4g.medium" DBSubnetGroupName: !Ref DatabaseSubnetGroup Engine: 'aurora-postgresql' EngineVersion: '14.4' AvailabilityZone: Fn::GetAtt: - PrivateSubnet0 - AvailabilityZone PostgresSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: PostgresSecurityGroup GroupDescription: Postgres security group VpcId: !Ref VPC ClientVPNEndpointSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: ClientVPNEndpointSecurityGroup GroupDescription: Client VPN Endpoint security group VpcId: !Ref VPC PostgresSecurityGroupIngressFromClientVPN: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !GetAtt PostgresSecurityGroup.GroupId IpProtocol: tcp FromPort: 5432 ToPort: 5432 SourceSecurityGroupId: !GetAtt ClientVPNEndpointSecurityGroup.GroupId ClientVPNSecurityGroupIngressFromAnywhere: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !GetAtt ClientVPNEndpointSecurityGroup.GroupId IpProtocol: udp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 Outputs: TemplateVersion: Description: 'template version.' Value: '1.0' StackName: Description: 'Stack name.' Value: !Sub '${AWS::StackName}' InstanceName: Description: 'The name of the database cluster.' Value: !Ref DatabaseCluster Export: Name: !Sub '${AWS::StackName}-InstanceName' DatabaseReadWriteEndpointAddress: Description: The address of the DB RW endpoint Value: !GetAtt [DatabaseCluster, Endpoint.Address] DatabaseReadOnlyEndpointAddress: Description: The address of the DB Read Only endpoint Value: !GetAtt [DatabaseCluster, ReadEndpoint.Address]