cia-dist-cloudformation/src/main/resources/cia-dist-cloudformation.yml

Summary

Maintainability
Test Coverage
AWSTemplateFormatVersion: '2010-09-09'
Description: CIA CloudFormation runtime
Parameters:
  WebServerInstanceType:
    Description: WebServer Server EC2 instance type
    Type: String
    Default: c7g.2xlarge
    AllowedValues:
      - t4g.micro
      - t4g.small
      - t4g.medium
      - t4g.large
      - t4g.xlarge
      - t4g.2xlarge
      - m6g.large
      - m6g.xlarge
      - m6g.2xlarge
      - c6g.8xlarge
      - c6g.4xlarge
      - m6g.12xlarge
      - m6g.16xlarge
      - c6g.large
      - c6g.xlarge
      - c6g.2xlarge
      - c6g.4xlarge
      - c6g.8xlarge
      - c7g.large
      - c7g.xlarge
      - c7g.2xlarge
      - c7g.4xlarge
      - c7g.8xlarge
      - c7g.12xlarge
      - c7g.16xlarge
    ConstraintDescription: must be a valid EC2 instance type.
  WebServerAmi:
    Description: Web server ami ubuntu arm 20.04, recommend ebs encrypted ami based on ubuntu hvm-ssd server
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/canonical/ubuntu/server/20.04/stable/current/arm64/hvm/ebs-gp2/ami-id
  WebServerCount:
    Description: Number of EC2 instances to launch for the WebServer server
    Type: Number
    Default: '1'
  WebServerAppLogAppender:
    Description: App log output appender console/elasticsearch/cloudwatch/awslogs/kinesis
    Type: String
    Default: cloudwatch
  DomainNamePrefix:
    Description: Domain name prefix (optional), used to setup route53 dns
    Type: String
    Default: cia
  DomainName:
    Description: Domain name (optional), used to setup route53 dns
    Type: String
    Default: hack23.com
  DBEncryptionKmsAlias:
    Description: The alias for Key Management Service encryption key alias
    Type: String
    Default: ''
  DBName:
    Default: pgdb
    Description: The database name
    Type: String
    MinLength: '1'
    MaxLength: '8'
    AllowedPattern: '[a-zA-Z][a-zA-Z0-9]*'
    ConstraintDescription: must begin with a letter and contain only alphanumeric characters.
  DBUsername:
    Default: root
    NoEcho: 'true'
    Description: The 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.
  DBPassword:
    Default: root1234
    NoEcho: 'true'
    Description: The database admin account password
    Type: String
    MinLength: '8'
  RotationLambda:
    Description: arn of RotationLambda used to rotate password
    Default: ''
    Type: String
  DBSnapshotName:
    Description: The name of a DB snapshot (optional, if arn:aws:rds:eu-west-1:172017021075:snapshot:cia-demo-20170711 supplied DBUsername,DBPassword need to eris,discord23)
    Default: ''
    Type: String
  DebPackageUrl:
    Default: https://repo1.maven.org/maven2/com/hack23/cia/cia-dist-deb/2022.12.26/cia-dist-deb-2022.12.26.deb
    Description: The debian package url
    Type: String
    MinLength: '8'
  DBClass:
    Default: db.m6g.xlarge
    Description: Database instance class
    Type: String
    AllowedValues:
      - db.t3.micro
      - db.t3.small
      - db.t3.medium
      - db.t3.large
      - db.t3.xlarge
      - db.t3.2xlarge
      - db.m6g.large
      - db.m6g.xlarge
      - db.m6g.2xlarge
      - db.m6g.4xlarge
      - db.m6g.12xlarge
      - db.m6g.16xlarge
      - db.r6g.large
      - db.r6g.xlarge
      - db.r6g.2xlarge
      - db.r6g.4xlarge
      - db.r6g.8xlarge
      - db.r6g.16xlarge
    ConstraintDescription: must select a valid database instance type.
  DBAllocatedStorage:
    Default: '200'
    Description: The size of the database (Gb)
    Type: Number
    MinValue: '100'
    MaxValue: '6144'
    ConstraintDescription: must be between 10+
Mappings:
  SubnetConfig:
    VPC:
      CIDR: 10.40.0.0/16
    Public:
      CIDR: 10.40.10.0/24
    Public2:
      CIDR: 10.40.11.0/24
    Public3:
      CIDR: 10.40.12.0/24
    PrivateApp:
      CIDR: 10.40.30.0/24
    PrivateApp2:
      CIDR: 10.40.31.0/24
    PrivateApp3:
      CIDR: 10.40.32.0/24
    Private:
      CIDR: 10.40.20.0/24
    Private2:
      CIDR: 10.40.21.0/24
    Private3:
      CIDR: 10.40.22.0/24
  AWSRegionAvailabilityZone:
    us-east-1:
      FIRST: us-east-1a
      SECOND: us-east-1b
      THIRD: us-east-1c
    us-west-1:
      FIRST: us-west-1a
      SECOND: us-west-1b
      THIRD: us-west-1c
    us-west-2:
      FIRST: us-west-2a
      SECOND: us-west-2b
      THIRD: us-west-2c
    eu-west-1:
      FIRST: eu-west-1a
      SECOND: eu-west-1b
      THIRD: eu-west-1c
    eu-north-1:
      FIRST: eu-north-1a
      SECOND: eu-north-1b
      THIRD: eu-north-1c
    eu-central-1:
      FIRST: eu-central-1a
      SECOND: eu-central-1b
      THIRD: eu-central-1c
    ap-northeast-1:
      FIRST: ap-northeast-1a
      SECOND: ap-northeast-1b
      THIRD: ap-northeast-1c
    ap-southeast-1:
      FIRST: ap-southeast-1a
      SECOND: ap-southeast-1b
      THIRD: ap-southeast-1c
    ap-southeast-2:
      FIRST: ap-southeast-2a
      SECOND: ap-southeast-2b
      THIRD: ap-southeast-2c
    sa-east-1:
      FIRST: sa-east-1a
      SECOND: sa-east-1b
      THIRD: sa-east-1c
    cn-north-1:
      FIRST: cn-north-1a
      SECOND: cn-north-1b
      THIRD: cn-north-1c
Conditions:
  useDBSnapshot: !Not
    - !Equals
      - !Ref 'DBSnapshotName'
      - ''
  useDBEncryptionKmsAlias: !Not
    - !Equals
      - !Ref 'DBEncryptionKmsAlias'
      - ''
  useDomainName: !Not
    - !Equals
      - !Ref 'DomainName'
      - ''
  useRotationLambda: !Not
    - !Equals
      - !Ref 'RotationLambda'
      - ''
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !FindInMap
        - SubnetConfig
        - VPC
        - CIDR
      EnableDnsSupport: 'true'
      EnableDnsHostnames: 'true'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Public
  FlowLogsRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - vpc-flow-logs.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
  FlowLogsPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: FlowLogs
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action:
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
              - logs:DescribeLogGroups
              - logs:DescribeLogStreams
            Effect: Allow
            Resource: '*'
      Roles:
        - !Ref 'FlowLogsRole'
    DependsOn:
      - FlowLogsRole
  VPCFlowLog:
    Type: AWS::EC2::FlowLog
    Properties:
      DeliverLogsPermissionArn: !GetAtt 'FlowLogsRole.Arn'
      LogGroupName: !Join
        - ''
        - - VPCFlowLogsGroup
          - '-'
          - !Ref 'AWS::StackName'
      ResourceId: !Ref 'VPC'
      ResourceType: VPC
      TrafficType: ALL
  ArtifactBucket:
    Type: AWS::S3::Bucket
    DependsOn: LogsBucket
    DeletionPolicy: Retain
    Properties:
      AccessControl: Private
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              KMSMasterKeyID: !Ref 'ArtifactBucketEncryptionKey'
              SSEAlgorithm: aws:kms
      VersioningConfiguration:
        Status: Enabled
      LifecycleConfiguration:
        Rules:
          - Id: Rule for log prefix
            Status: Enabled
            Transitions:
              - TransitionInDays: 30
                StorageClass: STANDARD_IA
              - TransitionInDays: 90
                StorageClass: GLACIER
            ExpirationInDays: 365
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      LoggingConfiguration:
        DestinationBucketName: !Ref 'LogsBucket'
        LogFilePrefix: /logs/ArtifactBucket/
      Tags:
        - Key: DomainName
          Value: !Ref 'DomainName'
        - Key: DataClassificationConfidentiality
          Value: Proprietary
        - Key: DataClassificationIntegrity
          Value: Standard
        - Key: DataClassificationAvailability
          Value: Standard
  ArtifactBucketEncryptionKey:
    Type: AWS::KMS::Key
    Properties:
      Description: Key used for Server Fleet Management Solution artifact bucket
      Enabled: true
      EnableKeyRotation: true
      KeyPolicy:
        Statement:
          - Sid: manage-key
            Action:
              - kms:*
            Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
            Resource: '*'
          - Sid: ssm-access-policy-statement
            Action:
              - kms:*
            Effect: Allow
            Principal:
              Service: ssm.amazonaws.com
            Resource: '*'
  ArtifactBucketEncryptionKeyAlias:
    Type: AWS::KMS::Alias
    Properties:
      AliasName: alias/ssm-bucket-encryption-key
      TargetKeyId: !Ref 'ArtifactBucketEncryptionKey'
  ArtifactBucketPolicy:
    Type: AWS::S3::BucketPolicy
    DeletionPolicy: Retain
    Properties:
      Bucket: !Ref 'ArtifactBucket'
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: ArtifactBucketPermissionsCheck
            Effect: Allow
            Resource: !Sub '${ArtifactBucket.Arn}'
            Principal:
              Service: ssm.amazonaws.com
            Action:
              - s3:GetBucketAcl
          - Sid: ArtifactBucketDeliveryDenyHttp
            Effect: Deny
            Principal: '*'
            Action:
              - s3:GetObject
            Resource: !Sub '${ArtifactBucket.Arn}/*'
            Condition:
              Bool:
                aws:SecureTransport: 'false'
          - Sid: ArtifactBucketDelivery
            Effect: Allow
            Resource: !Sub '${ArtifactBucket.Arn}/*'
            Principal:
              Service: ssm.amazonaws.com
            Action:
              - s3:PutObject
            Condition:
              StringEquals:
                s3:x-amz-acl: bucket-owner-full-control
  MaintenanceWindowServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ssm.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonSSMMaintenanceWindowRole
      Policies:
        - PolicyName: PassRole
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - iam:PassRole
                Resource:
                  - '*'
  MaintenanceWindow:
    Type: AWS::SSM::MaintenanceWindow
    Properties:
      Description: Daily Maintenance Window
      AllowUnassociatedTargets: false
      Cutoff: 0
      Schedule: rate(1 hour)
      Duration: 1
      Name: hourly-patching
  MaintenanceWindowTarget:
    Type: AWS::SSM::MaintenanceWindowTarget
    Properties:
      Description: Server Fleet Management Solution Instances
      WindowId: !Ref 'MaintenanceWindow'
      ResourceType: INSTANCE
      Targets:
        - Key: tag:SystemPatchComplianceMaintenanceWindowActive
          Values:
            - 'true'
  MaintenanceWindowTaskPatchBaselineInstall:
    Type: AWS::SSM::MaintenanceWindowTask
    Properties:
      Description: Runs Server Fleet Management Solution patch baseline
      ServiceRoleArn: !GetAtt 'MaintenanceWindowServiceRole.Arn'
      MaxErrors: 1
      TaskArn: AWS-RunPatchBaseline
      MaxConcurrency: 100%
      WindowId: !Ref 'MaintenanceWindow'
      Priority: 1
      TaskType: RUN_COMMAND
      TaskInvocationParameters:
        MaintenanceWindowRunCommandParameters:
          Comment: Runs patch baseline
          TimeoutSeconds: 600
          Parameters:
            Operation:
              - Install
          OutputS3BucketName: !Ref 'ArtifactBucket'
      Targets:
        - Values:
            - !Ref 'MaintenanceWindowTarget'
          Key: WindowTargetIds
  MaintenanceWindowTaskPatchBaselineScan:
    Type: AWS::SSM::MaintenanceWindowTask
    Properties:
      Description: Runs Server Fleet Management Solution patch baseline
      ServiceRoleArn: !GetAtt 'MaintenanceWindowServiceRole.Arn'
      MaxErrors: 1
      TaskArn: AWS-RunPatchBaseline
      MaxConcurrency: 100%
      WindowId: !Ref 'MaintenanceWindow'
      Priority: 2
      TaskType: RUN_COMMAND
      TaskInvocationParameters:
        MaintenanceWindowRunCommandParameters:
          Comment: Runs patch baseline
          TimeoutSeconds: 600
          Parameters:
            Operation:
              - Scan
          OutputS3BucketName: !Ref 'ArtifactBucket'
      Targets:
        - Values:
            - !Ref 'MaintenanceWindowTarget'
          Key: WindowTargetIds
  UpdateSSMAgent:
    Type: AWS::SSM::Association
    Properties:
      AssociationName: UpdateSSMAgent
      Name: AWS-UpdateSSMAgent
      Parameters:
        allowDowngrade:
          - 'false'
      OutputLocation:
        S3Location:
          OutputS3BucketName: !Ref 'ArtifactBucket'
      ScheduleExpression: rate(7 days)
      Targets:
        - Key: tag:UpdateSSMAgent
          Values:
            - 'true'
  GatherSoftwareInventory:
    Type: AWS::SSM::Association
    DependsOn: ArtifactBucket
    Properties:
      AssociationName: GatherSoftwareInventory
      Name: AWS-GatherSoftwareInventory
      Parameters:
        windowsUpdates:
          - Disabled
        windowsRegistry:
          - Disabled
        windowsRoles:
          - Disabled
        services:
          - Disabled
      OutputLocation:
        S3Location:
          OutputS3BucketName: !Ref 'ArtifactBucket'
      ScheduleExpression: rate(1 hour)
      Targets:
        - Key: tag:GatherSoftwareInventory
          Values:
            - 'true'
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      AvailabilityZone: !FindInMap
        - AWSRegionAvailabilityZone
        - !Ref 'AWS::Region'
        - FIRST
      CidrBlock: !FindInMap
        - SubnetConfig
        - Public
        - CIDR
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Public
  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      AvailabilityZone: !FindInMap
        - AWSRegionAvailabilityZone
        - !Ref 'AWS::Region'
        - SECOND
      CidrBlock: !FindInMap
        - SubnetConfig
        - Public2
        - CIDR
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Public
  PublicSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      AvailabilityZone: !FindInMap
        - AWSRegionAvailabilityZone
        - !Ref 'AWS::Region'
        - THIRD
      CidrBlock: !FindInMap
        - SubnetConfig
        - Public3
        - CIDR
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Public
  PrivateAppSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      AvailabilityZone: !FindInMap
        - AWSRegionAvailabilityZone
        - !Ref 'AWS::Region'
        - FIRST
      CidrBlock: !FindInMap
        - SubnetConfig
        - PrivateApp
        - CIDR
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: PrivateApp
  PrivateAppSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      AvailabilityZone: !FindInMap
        - AWSRegionAvailabilityZone
        - !Ref 'AWS::Region'
        - SECOND
      CidrBlock: !FindInMap
        - SubnetConfig
        - PrivateApp2
        - CIDR
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: PrivateApp
  PrivateAppSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      AvailabilityZone: !FindInMap
        - AWSRegionAvailabilityZone
        - !Ref 'AWS::Region'
        - THIRD
      CidrBlock: !FindInMap
        - SubnetConfig
        - PrivateApp3
        - CIDR
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: PrivateApp
  SgFormonitoringVPCEndpoint:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group to allow access to monitoring VPC Endpoint
      SecurityGroupIngress:
        - Description: vpc endpoint inbound access
          CidrIp: !FindInMap
            - SubnetConfig
            - VPC
            - CIDR
          IpProtocol: tcp
          FromPort: '443'
          ToPort: '443'
      SecurityGroupEgress:
        - Description: vpc endpoint outbound access
          CidrIp: '0.0.0.0/0'
          IpProtocol: '-1'
      VpcId: !Ref 'VPC'
  monitoringVPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Join
        - ''
        - - com.amazonaws.
          - !Ref 'AWS::Region'
          - .monitoring
      VpcId: !Ref 'VPC'
      SubnetIds:
        - !Ref 'PrivateAppSubnet'
        - !Ref 'PrivateAppSubnet2'
        - !Ref 'PrivateAppSubnet3'
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref 'SgFormonitoringVPCEndpoint'
  SgForlogsVPCEndpoint:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group to allow access to logs VPC Endpoint
      SecurityGroupIngress:
        - Description: vpc endpoint inbound access
          CidrIp: !FindInMap
            - SubnetConfig
            - VPC
            - CIDR
          IpProtocol: tcp
          FromPort: '443'
          ToPort: '443'
      SecurityGroupEgress:
        - Description: vpc endpoint outbound access
          CidrIp: '0.0.0.0/0'
          IpProtocol: '-1'
      VpcId: !Ref 'VPC'
  logsVPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Join
        - ''
        - - com.amazonaws.
          - !Ref 'AWS::Region'
          - .logs
      VpcId: !Ref 'VPC'
      SubnetIds:
        - !Ref 'PrivateAppSubnet'
        - !Ref 'PrivateAppSubnet2'
        - !Ref 'PrivateAppSubnet3'
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref 'SgForlogsVPCEndpoint'
  SgForssmVPCEndpoint:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group to allow access to ssm VPC Endpoint
      SecurityGroupIngress:
        - Description: vpc endpoint inbound access
          CidrIp: !FindInMap
            - SubnetConfig
            - VPC
            - CIDR
          IpProtocol: tcp
          FromPort: '443'
          ToPort: '443'
      SecurityGroupEgress:
        - Description: vpc endpoint outbound access
          CidrIp: '0.0.0.0/0'
          IpProtocol: '-1'
      VpcId: !Ref 'VPC'
  ssmVPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Join
        - ''
        - - com.amazonaws.
          - !Ref 'AWS::Region'
          - .ssm
      VpcId: !Ref 'VPC'
      SubnetIds:
        - !Ref 'PrivateAppSubnet'
        - !Ref 'PrivateAppSubnet2'
        - !Ref 'PrivateAppSubnet3'
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref 'SgForssmVPCEndpoint'
  SgForssmmessagesVPCEndpoint:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group to allow access to ssmmessages VPC Endpoint
      SecurityGroupIngress:
        - Description: vpc endpoint inbound access
          CidrIp: !FindInMap
            - SubnetConfig
            - VPC
            - CIDR
          IpProtocol: tcp
          FromPort: '443'
          ToPort: '443'
      SecurityGroupEgress:
        - Description: vpc endpoint outbound access
          CidrIp: '0.0.0.0/0'
          IpProtocol: '-1'
      VpcId: !Ref 'VPC'
  ssmmessagesVPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Join
        - ''
        - - com.amazonaws.
          - !Ref 'AWS::Region'
          - .ssmmessages
      VpcId: !Ref 'VPC'
      SubnetIds:
        - !Ref 'PrivateAppSubnet'
        - !Ref 'PrivateAppSubnet2'
        - !Ref 'PrivateAppSubnet3'
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref 'SgForssmmessagesVPCEndpoint'
  SgForecVPCEndpoint:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group to allow access to ec2 VPC Endpoint
      SecurityGroupIngress:
        - Description: vpc endpoint inbound access
          CidrIp: !FindInMap
            - SubnetConfig
            - VPC
            - CIDR
          IpProtocol: tcp
          FromPort: '443'
          ToPort: '443'
      SecurityGroupEgress:
        - Description: vpc endpoint outbound access
          CidrIp: '0.0.0.0/0'
          IpProtocol: '-1'
      VpcId: !Ref 'VPC'
  ecVPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Join
        - ''
        - - com.amazonaws.
          - !Ref 'AWS::Region'
          - .ec2
      VpcId: !Ref 'VPC'
      SubnetIds:
        - !Ref 'PrivateAppSubnet'
        - !Ref 'PrivateAppSubnet2'
        - !Ref 'PrivateAppSubnet3'
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref 'SgForecVPCEndpoint'
  SgForecmessagesVPCEndpoint:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group to allow access to ec2messages VPC Endpoint
      SecurityGroupIngress:
        - Description: vpc endpoint inbound access
          CidrIp: !FindInMap
            - SubnetConfig
            - VPC
            - CIDR
          IpProtocol: tcp
          FromPort: '443'
          ToPort: '443'
      SecurityGroupEgress:
        - Description: vpc endpoint outbound access
          CidrIp: '0.0.0.0/0'
          IpProtocol: '-1'
      VpcId: !Ref 'VPC'
  ecmessagesVPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Join
        - ''
        - - com.amazonaws.
          - !Ref 'AWS::Region'
          - .ec2messages
      VpcId: !Ref 'VPC'
      SubnetIds:
        - !Ref 'PrivateAppSubnet'
        - !Ref 'PrivateAppSubnet2'
        - !Ref 'PrivateAppSubnet3'
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref 'SgForecmessagesVPCEndpoint'
  sVPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: !Join
        - ''
        - - com.amazonaws.
          - !Ref 'AWS::Region'
          - .s3
      VpcId: !Ref 'VPC'
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal: '*'
            Action:
              - '*'
            Resource:
              - '*'
      RouteTableIds:
        - !Ref 'PrivateAppRouteTable'
  PrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      AvailabilityZone: !FindInMap
        - AWSRegionAvailabilityZone
        - !Ref 'AWS::Region'
        - FIRST
      CidrBlock: !FindInMap
        - SubnetConfig
        - Private
        - CIDR
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Private
  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      AvailabilityZone: !FindInMap
        - AWSRegionAvailabilityZone
        - !Ref 'AWS::Region'
        - SECOND
      CidrBlock: !FindInMap
        - SubnetConfig
        - Private2
        - CIDR
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Private
  PrivateSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      AvailabilityZone: !FindInMap
        - AWSRegionAvailabilityZone
        - !Ref 'AWS::Region'
        - THIRD
      CidrBlock: !FindInMap
        - SubnetConfig
        - Private3
        - CIDR
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Private
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Public
  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::StackId'
        - Key: Network
          Value: Public
  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: GatewayToInternet
    Properties:
      RouteTableId: !Ref 'PublicRouteTable'
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref 'InternetGateway'
  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PublicSubnet'
      RouteTableId: !Ref 'PublicRouteTable'
  Public2SubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PublicSubnet2'
      RouteTableId: !Ref 'PublicRouteTable'
  Public3SubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PublicSubnet3'
      RouteTableId: !Ref 'PublicRouteTable'
  PrivateAppRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Private
  PrivateAppRoute:
    Type: AWS::EC2::Route
    DependsOn: GatewayToInternet
    Properties:
      RouteTableId: !Ref 'PrivateAppRouteTable'
      DestinationCidrBlock: '0.0.0.0/0'
      NatGatewayId: !Ref 'NAT'
  PrivateAppRouteTable2:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Private
  PrivateAppRoute2:
    Type: AWS::EC2::Route
    DependsOn: GatewayToInternet
    Properties:
      RouteTableId: !Ref 'PrivateAppRouteTable2'
      DestinationCidrBlock: '0.0.0.0/0'
      NatGatewayId: !Ref 'NAT2'
  PrivateAppRouteTable3:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Private
  PrivateAppRoute3:
    Type: AWS::EC2::Route
    DependsOn: GatewayToInternet
    Properties:
      RouteTableId: !Ref 'PrivateAppRouteTable3'
      DestinationCidrBlock: '0.0.0.0/0'
      NatGatewayId: !Ref 'NAT3'
  PrivateAppSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PrivateAppSubnet'
      RouteTableId: !Ref 'PrivateAppRouteTable'
  PrivateApp2SubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PrivateAppSubnet2'
      RouteTableId: !Ref 'PrivateAppRouteTable2'
  PrivateApp3SubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PrivateAppSubnet3'
      RouteTableId: !Ref 'PrivateAppRouteTable3'
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Private
  PrivateRoute:
    Type: AWS::EC2::Route
    DependsOn: NAT
    Properties:
      RouteTableId: !Ref 'PrivateRouteTable'
      DestinationCidrBlock: '0.0.0.0/0'
      NatGatewayId: !Ref 'NAT'
  PrivateRouteTable2:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Private
  PrivateRoute2:
    Type: AWS::EC2::Route
    DependsOn: NAT
    Properties:
      RouteTableId: !Ref 'PrivateRouteTable2'
      DestinationCidrBlock: '0.0.0.0/0'
      NatGatewayId: !Ref 'NAT2'
  PrivateRouteTable3:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Private
  PrivateRoute3:
    Type: AWS::EC2::Route
    DependsOn: NAT
    Properties:
      RouteTableId: !Ref 'PrivateRouteTable3'
      DestinationCidrBlock: '0.0.0.0/0'
      NatGatewayId: !Ref 'NAT3'
  PrivateSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PrivateSubnet'
      RouteTableId: !Ref 'PrivateRouteTable'
  Private2SubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PrivateSubnet2'
      RouteTableId: !Ref 'PrivateRouteTable2'
  Private3SubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PrivateSubnet3'
      RouteTableId: !Ref 'PrivateRouteTable3'
  PublicNetworkAcl:
    Type: AWS::EC2::NetworkAcl
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Public
  PrivateAppNetworkAcl:
    Type: AWS::EC2::NetworkAcl
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Public
  PrivateNetworkAcl:
    Type: AWS::EC2::NetworkAcl
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Network
          Value: Private
  InboundPostgresqlPrivateNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PrivateNetworkAcl'
      RuleNumber: '100'
      Protocol: '6'
      RuleAction: allow
      Egress: 'false'
      CidrBlock: !FindInMap
        - SubnetConfig
        - PrivateApp
        - CIDR
      PortRange:
        From: '5432'
        To: '5432'
  InboundPostgresqlPrivate2NetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PrivateNetworkAcl'
      RuleNumber: '200'
      Protocol: '6'
      RuleAction: allow
      Egress: 'false'
      CidrBlock: !FindInMap
        - SubnetConfig
        - PrivateApp2
        - CIDR
      PortRange:
        From: '5432'
        To: '5432'
  InboundPostgresqlPrivate3NetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PrivateNetworkAcl'
      RuleNumber: '300'
      Protocol: '6'
      RuleAction: allow
      Egress: 'false'
      CidrBlock: !FindInMap
        - SubnetConfig
        - PrivateApp3
        - CIDR
      PortRange:
        From: '5432'
        To: '5432'
  OutboundPostgresqlPrivateNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PrivateNetworkAcl'
      RuleNumber: '400'
      Protocol: '6'
      RuleAction: allow
      Egress: 'true'
      CidrBlock: !FindInMap
        - SubnetConfig
        - PrivateApp
        - CIDR
      PortRange:
        From: '1024'
        To: '65535'
  OutboundPostgresqlPrivate2NetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PrivateNetworkAcl'
      RuleNumber: '500'
      Protocol: '6'
      RuleAction: allow
      Egress: 'true'
      CidrBlock: !FindInMap
        - SubnetConfig
        - PrivateApp2
        - CIDR
      PortRange:
        From: '1024'
        To: '65535'
  OutboundPostgresqlPrivate3NetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PrivateNetworkAcl'
      RuleNumber: '600'
      Protocol: '6'
      RuleAction: allow
      Egress: 'true'
      CidrBlock: !FindInMap
        - SubnetConfig
        - PrivateApp3
        - CIDR
      PortRange:
        From: '1024'
        To: '65535'
  InboundPrivateAppNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PrivateAppNetworkAcl'
      RuleNumber: '700'
      Protocol: '6'
      RuleAction: allow
      Egress: 'false'
      CidrBlock: '0.0.0.0/0'
      PortRange:
        From: '1024'
        To: '65535'
  InboundDenyRdpPrivateAppNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PrivateAppNetworkAcl'
      RuleNumber: '70'
      Protocol: '6'
      RuleAction: deny
      Egress: 'false'
      CidrBlock: '0.0.0.0/0'
      PortRange:
        From: '3389'
        To: '3389'
  OutboundPrivateAppNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PrivateAppNetworkAcl'
      RuleNumber: '800'
      Protocol: '6'
      RuleAction: allow
      Egress: 'true'
      CidrBlock: '0.0.0.0/0'
      PortRange:
        From: '80'
        To: '65535'
  OutboundDenyRdpPrivateAppNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PrivateAppNetworkAcl'
      RuleNumber: '80'
      Protocol: '6'
      RuleAction: deny
      Egress: 'true'
      CidrBlock: '0.0.0.0/0'
      PortRange:
        From: '3389'
        To: '3389'
  InboundHttpsPortPublicNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PublicNetworkAcl'
      RuleNumber: '100'
      Protocol: '6'
      RuleAction: allow
      Egress: 'false'
      CidrBlock: '0.0.0.0/0'
      PortRange:
        From: '443'
        To: '443'
  InboundHttpNatPortPublicNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PublicNetworkAcl'
      RuleNumber: '200'
      Protocol: '6'
      RuleAction: allow
      Egress: 'false'
      CidrBlock: 10.40.0.0/16
      PortRange:
        From: '80'
        To: '80'
  InboundEphemeralPortPublicNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PublicNetworkAcl'
      RuleNumber: '300'
      Protocol: '6'
      RuleAction: allow
      Egress: 'false'
      CidrBlock: '0.0.0.0/0'
      PortRange:
        From: '1024'
        To: '65535'
  InboundEphemeralPortDenyRdpPublicNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PublicNetworkAcl'
      RuleNumber: '30'
      Protocol: '6'
      RuleAction: deny
      Egress: 'false'
      CidrBlock: '0.0.0.0/0'
      PortRange:
        From: '3389'
        To: '3389'
  OutboundHttpPublicNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PublicNetworkAcl'
      RuleNumber: '400'
      Protocol: '6'
      RuleAction: allow
      Egress: 'true'
      CidrBlock: '0.0.0.0/0'
      PortRange:
        From: '80'
        To: '80'
  OutboundHttpsPublicNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PublicNetworkAcl'
      RuleNumber: '500'
      Protocol: '6'
      RuleAction: allow
      Egress: 'true'
      CidrBlock: '0.0.0.0/0'
      PortRange:
        From: '443'
        To: '443'
  OutboundEphemeralPublicNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PublicNetworkAcl'
      RuleNumber: '600'
      Protocol: '6'
      RuleAction: allow
      Egress: 'true'
      CidrBlock: '0.0.0.0/0'
      PortRange:
        From: '1024'
        To: '65535'
  OutboundEphemeralDenyRdpPublicNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref 'PublicNetworkAcl'
      RuleNumber: '60'
      Protocol: '6'
      RuleAction: deny
      Egress: 'true'
      CidrBlock: '0.0.0.0/0'
      PortRange:
        From: '3389'
        To: '3389'
  PublicSubnetNetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref 'PublicSubnet'
      NetworkAclId: !Ref 'PublicNetworkAcl'
  Public2SubnetNetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref 'PublicSubnet2'
      NetworkAclId: !Ref 'PublicNetworkAcl'
  Public3SubnetNetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref 'PublicSubnet3'
      NetworkAclId: !Ref 'PublicNetworkAcl'
  PrivateAppSubnetNetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref 'PrivateAppSubnet'
      NetworkAclId: !Ref 'PrivateAppNetworkAcl'
  PrivateApp2SubnetNetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref 'PrivateAppSubnet2'
      NetworkAclId: !Ref 'PrivateAppNetworkAcl'
  PrivateApp3SubnetNetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref 'PrivateAppSubnet3'
      NetworkAclId: !Ref 'PrivateAppNetworkAcl'
  PrivateSubnetNetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref 'PrivateSubnet'
      NetworkAclId: !Ref 'PrivateNetworkAcl'
  Private2SubnetNetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref 'PrivateSubnet2'
      NetworkAclId: !Ref 'PrivateNetworkAcl'
  Private3SubnetNetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref 'PrivateSubnet3'
      NetworkAclId: !Ref 'PrivateNetworkAcl'
  DatabaseSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    DependsOn:
      - WebServerSecurityGroup
    Properties:
      GroupDescription: Database Access
      VpcId: !Ref 'VPC'
      SecurityGroupIngress:
        - Description: Postgres inbound access from server
          IpProtocol: tcp
          FromPort: '5432'
          ToPort: '5432'
          SourceSecurityGroupId: !Ref 'WebServerSecurityGroup'
      SecurityGroupEgress:
        - Description: Postgres outbound access to server
          IpProtocol: tcp
          FromPort: '5432'
          ToPort: '5432'
          SourceSecurityGroupId: !Ref 'WebServerSecurityGroup'
  DatabaseParamGroup:
    Type: AWS::RDS::DBParameterGroup
    Properties:
      Description: Database Parameter Group + pg_stat_statements
      Family: postgres14
      Parameters:
        max_prepared_transactions: LEAST({DBInstanceClassMemory/9531392},5000)
        max_parallel_workers_per_gather: '4'
        rds.force_ssl: '1'
        rds.force_admin_logging_level: notice
        rds.force_autovacuum_logging_level: notice
        rds.log_retention_period: '2400'
        shared_preload_libraries: pg_stat_statements, pgaudit
        pgaudit.log: ddl
        pg_stat_statements.track: all
        pg_stat_statements.max: '10000'
  myDBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: DB Private Subnet
      SubnetIds:
        - !Ref 'PrivateSubnet'
        - !Ref 'PrivateSubnet2'
        - !Ref 'PrivateSubnet3'
  RDSInstanceRotationSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: This is the secret for my RDS instance
      SecretString: !Join
        - ''
        - - '{"username" : "'
          - !Ref 'DBUsername'
          - '","password" : "'
          - !Ref 'DBPassword'
          - '"}'
      Tags:
        - Key: SecretGroup
          Value: RDS
  SecretRDSInstanceAttachment:
    Type: AWS::SecretsManager::SecretTargetAttachment
    Properties:
      SecretId: !Ref 'RDSInstanceRotationSecret'
      TargetId: !Ref 'Database'
      TargetType: AWS::RDS::DBInstance
  MySecretRotationSchedule:
    Type: AWS::SecretsManager::RotationSchedule
    DependsOn: SecretRotationLambdaInvokePermission
    Condition: useRotationLambda
    Properties:
      SecretId: !Ref 'RDSInstanceRotationSecret'
      RotationLambdaARN: !Ref 'RotationLambda'
      RotationRules:
        AutomaticallyAfterDays: 1
  SecretRotationLambdaInvokePermission:
    Type: AWS::Lambda::Permission
    DependsOn: SecretRDSInstanceAttachment
    Condition: useRotationLambda
    Properties:
      FunctionName: MySecretsManagerRotationFunction
      Action: lambda:InvokeFunction
      Principal: secretsmanager.amazonaws.com
  Database:
    Type: AWS::RDS::DBInstance
    DependsOn:
      - PrivateRoute
      - DatabaseSecurityGroup
    Properties:
      DBName: !If
        - useDBSnapshot
        - !Ref 'AWS::NoValue'
        - !Ref 'DBName'
      StorageEncrypted: 'true'
      KmsKeyId: !If
        - useDBEncryptionKmsAlias
        - !Ref 'DBEncryptionKmsAlias'
        - !Ref 'AWS::NoValue'
      AllocatedStorage: !Ref 'DBAllocatedStorage'
      CopyTagsToSnapshot: 'true'
      Tags:
        - Key: DomainName
          Value: !Ref 'DomainName'
        - Key: DataClassificationConfidentiality
          Value: Proprietary
        - Key: DataClassificationIntegrity
          Value: Standard
        - Key: DataClassificationAvailability
          Value: Standard
      DBInstanceClass: !Ref 'DBClass'
      Engine: postgres
      EngineVersion: '14.4'
      MultiAZ: 'true'
      PubliclyAccessible: 'false'
      BackupRetentionPeriod: '7'
      EnablePerformanceInsights: 'true'
      EnableCloudwatchLogsExports:
        - postgresql
        - upgrade
      StorageType: gp2
      DBInstanceIdentifier: rotation-instance
      MasterUsername: !If
        - useDBSnapshot
        - !Ref 'AWS::NoValue'
        - !Join
          - ''
          - - '{{resolve:secretsmanager:'
            - !Ref 'RDSInstanceRotationSecret'
            - :SecretString:username}}
      MasterUserPassword: !If
        - useDBSnapshot
        - !Ref 'AWS::NoValue'
        - !Join
          - ''
          - - '{{resolve:secretsmanager:'
            - !Ref 'RDSInstanceRotationSecret'
            - :SecretString:password}}
      DBSnapshotIdentifier: !If
        - useDBSnapshot
        - !Ref 'DBSnapshotName'
        - !Ref 'AWS::NoValue'
      DBSubnetGroupName: !Ref 'myDBSubnetGroup'
      DBParameterGroupName: !Ref 'DatabaseParamGroup'
      VPCSecurityGroups:
        - !GetAtt 'DatabaseSecurityGroup.GroupId'
  LogsBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    Properties:
      AccessControl: LogDeliveryWrite
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      LifecycleConfiguration:
        Rules:
          - Id: Rule for log prefix
            Status: Enabled
            Transitions:
              - TransitionInDays: 30
                StorageClass: STANDARD_IA
              - TransitionInDays: 90
                StorageClass: GLACIER
            ExpirationInDays: 365
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      LoggingConfiguration:
        LogFilePrefix: /logs/LogsBucket/
      Tags:
        - Key: DomainName
          Value: !Ref 'DomainName'
        - Key: DataClassificationConfidentiality
          Value: Proprietary
        - Key: DataClassificationIntegrity
          Value: Standard
        - Key: DataClassificationAvailability
          Value: Standard
  LogsBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref 'LogsBucket'
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: ELBAccessLogsAllowAws
            Effect: Allow
            Resource: !Join
              - ''
              - - 'arn:aws:s3:::'
                - !Ref 'LogsBucket'
                - /*
            Principal:
              AWS:
                - '156460612806'
            Action:
              - s3:PutObject
          - Sid: ELBAccessLogsDenyHttp
            Effect: Deny
            Principal: '*'
            Action:
              - s3:GetObject
            Resource: !Join
              - ''
              - - 'arn:aws:s3:::'
                - !Ref 'LogsBucket'
                - /*
            Condition:
              Bool:
                aws:SecureTransport: 'false'
  NAT:
    DependsOn: GatewayToInternet
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt 'EIP.AllocationId'
      SubnetId: !Ref 'PublicSubnet'
      Tags:
        - Key: Network
          Value: NAT
  NAT2:
    DependsOn: GatewayToInternet
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt 'EIP2.AllocationId'
      SubnetId: !Ref 'PublicSubnet2'
      Tags:
        - Key: Network
          Value: NAT
  NAT3:
    DependsOn: GatewayToInternet
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt 'EIP3.AllocationId'
      SubnetId: !Ref 'PublicSubnet3'
      Tags:
        - Key: Network
          Value: NAT
  EIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
  EIP2:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
  EIP3:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
  PublicElasticLoadBalancerV2:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Subnets:
        - !Ref 'PublicSubnet'
        - !Ref 'PublicSubnet2'
        - !Ref 'PublicSubnet3'
      SecurityGroups:
        - !Ref 'PublicLoadBalancerSecurityGroup'
      LoadBalancerAttributes:
        - Key: access_logs.s3.enabled
          Value: 'true'
        - Key: access_logs.s3.bucket
          Value: !Ref 'LogsBucket'
        - Key: access_logs.s3.prefix
          Value: Logs
        - Key: idle_timeout.timeout_seconds
          Value: '60'
        - Key: routing.http.drop_invalid_header_fields.enabled
          Value: 'true'
        - Key: routing.http.desync_mitigation_mode
          Value: strictest
    DependsOn: LogsBucketPolicy
  AppPolicy:
    Type: AWS::ResilienceHub::ResiliencyPolicy
    Properties:
      DataLocationConstraint: AnyLocation
      Policy:
        Software:
          RpoInSecs: 300
          RtoInSecs: 14400
        Hardware:
          RpoInSecs: 0
          RtoInSecs: 120
        AZ:
          RpoInSecs: 0
          RtoInSecs: 120
      PolicyDescription: MissionCritical
      PolicyName: MissionCritical
      Tier: MissionCritical
  Application:
    Type: AWS::ResilienceHub::App
    DependsOn: WebServerFleet
    Properties:
      AppAssessmentSchedule: Daily
      AppTemplateBody: !Sub '{"resources":[{"logicalResourceId":{"identifier":"LogsBucket","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::S3::Bucket","name":"s3bucket-logs"},{"logicalResourceId":{"identifier":"NAT","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::EC2::NatGateway","name":"natgateway-vpc"},{"logicalResourceId":{"identifier":"NAT2","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::EC2::NatGateway","name":"natgateway-vpc2"},{"logicalResourceId":{"identifier":"NAT3","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::EC2::NatGateway","name":"natgateway-vpc3"},{"logicalResourceId":{"identifier":"WebServerFleet","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::AutoScaling::AutoScalingGroup","name":"autoscalinggroup-fleet"},{"logicalResourceId":{"identifier":"ArtifactBucket","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::S3::Bucket","name":"s3bucket-art"},{"logicalResourceId":{"identifier":"Database","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::RDS::DBInstance","name":"rdsdbinstance-app"},{"logicalResourceId":{"identifier":"DNSRecordCNAME2PublicElasticLoadBalancerV2","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::Route53::RecordSet","name":"route53recordset-app"}],"appComponents":[{"name":"storage-s3bucket-logs","type":"AWS::ResilienceHub::StorageAppComponent","resourceNames":["s3bucket-logs"]},{"name":"networking-nat","type":"AWS::ResilienceHub::NetworkingAppComponent","resourceNames":["natgateway-vpc","natgateway-vpc2","natgateway-vpc3"]},{"name":"compute-app-fleet","type":"AWS::ResilienceHub::ComputeAppComponent","resourceNames":["autoscalinggroup-fleet"]},{"name":"storage-s3bucket-art","type":"AWS::ResilienceHub::StorageAppComponent","resourceNames":["s3bucket-art"]},{"name":"database-rds","type":"AWS::ResilienceHub::DatabaseAppComponent","resourceNames":["rdsdbinstance-app"]},{"name":"networking-dns","type":"AWS::ResilienceHub::NetworkingAppComponent","resourceNames":["route53recordset-app"]},{"name":"appcommon","type":"AWS::ResilienceHub::AppCommonAppComponent","resourceNames":[]}],"excludedResources":{"logicalResourceIds":[]},"version":2.0}'
      Description: Citizen Intelligence Agency
      Name: !Sub 'CIA'
      ResiliencyPolicyArn: !Ref 'AppPolicy'
      ResourceMappings:
        - LogicalStackName: !Ref 'AWS::StackName'
          MappingType: CfnStack
          PhysicalResourceId:
            Identifier: !Sub '${AWS::StackId}'
            Type: Arn
  AppAlarmTopic:
    Type: AWS::SNS::Topic
    DependsOn: Application
    Properties:
      Subscription:
        - Endpoint: !Sub 'admin@${DomainName}'
          Protocol: email
      KmsMasterKeyId: alias/aws/sns
  AWSResilienceHubRDSInstanceOverUtilizedCpuAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref 'AppAlarmTopic'
      AlarmDescription: Alarm by AWS Resilience Hub that reports when database CPU utilization is over 90%
      AlarmName: !Sub 'AWSResilienceHub-RDSInstanceOverUtilizedCpuAlarm-2020-04-01_CIA_rotation-instance'
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 3
      Dimensions:
        - Name: DBInstanceIdentifier
          Value: rotation-instance
      EvaluationPeriods: 5
      MetricName: CPUUtilization
      Namespace: AWS/RDS
      Period: 60
      Statistic: Maximum
      Threshold: 90
      TreatMissingData: missing
      Unit: Percent
  AWSResilienceHubRDSInstanceLowMemoryAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref 'AppAlarmTopic'
      AlarmDescription: Alarm by AWS Resilience Hub that reports when database free memory is under 10MB
      AlarmName: !Sub 'AWSResilienceHub-RDSInstanceLowMemoryAlarm-2020-04-01_CIA_rotation-instance'
      ComparisonOperator: LessThanOrEqualToThreshold
      DatapointsToAlarm: 3
      Dimensions:
        - Name: DBInstanceIdentifier
          Value: rotation-instance
      EvaluationPeriods: 5
      MetricName: FreeableMemory
      Namespace: AWS/RDS
      Period: 60
      Statistic: Minimum
      Threshold: 10485760
      TreatMissingData: missing
  AWSResilienceHubApplicationLoadBalancerElbHttp4xxCountAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub 'AWSResilienceHub-ApplicationLoadBalancerElbHttp4xxCountAlarm-2020-04-01_CIA'
      AlarmDescription: Alerts when the number of HTTP 4XX server error codes, which originate from the Application Load Balancer, is greater than or equal to the specified threshold
      AlarmActions:
        - !Ref 'AppAlarmTopic'
      MetricName: HTTPCode_ELB_4XX_Count
      Namespace: AWS/ApplicationELB
      Dimensions:
        - Name: LoadBalancer
          Value: !Ref 'PublicElasticLoadBalancerV2'
      Period: 60
      Statistic: Sum
      Threshold: 100
      EvaluationPeriods: 5
      DatapointsToAlarm: 3
      ComparisonOperator: GreaterThanOrEqualToThreshold
      TreatMissingData: notBreaching
      Unit: Count
  AWSResilienceHubApplicationLoadBalancerElbHttp5xxCountAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub 'AWSResilienceHub-ApplicationLoadBalancerElbHttp5xxCountAlarm-2020-04-01_CIA'
      AlarmDescription: Alerts when the number of HTTP 5XX server error codes, which originate from the Application Load Balancer, is greater than or equal to the specified threshold
      AlarmActions:
        - !Ref 'AppAlarmTopic'
      MetricName: HTTPCode_ELB_5XX_Count
      Namespace: AWS/ApplicationELB
      Dimensions:
        - Name: LoadBalancer
          Value: !Ref 'PublicElasticLoadBalancerV2'
      Period: 60
      Statistic: Sum
      Threshold: 100
      EvaluationPeriods: 5
      DatapointsToAlarm: 3
      ComparisonOperator: GreaterThanOrEqualToThreshold
      TreatMissingData: notBreaching
      Unit: Count
  AWSResilienceHubAsgAbnormalRequestPerTargetAlarmCIAHttpsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref 'AppAlarmTopic'
      AlarmDescription: Alarm by AWS Resilience Hub that reports when target group request count is anomalous with band width 1
      AlarmName: !Sub 'AWSResilienceHub-AsgAbnormalRequestPerTargetAlarm-2020-07-13_CIA-Https'
      ComparisonOperator: GreaterThanUpperThreshold
      EvaluationPeriods: 15
      DatapointsToAlarm: 10
      Metrics:
        - Expression: ANOMALY_DETECTION_BAND(m1,1)
          Id: ad1
        - Id: m1
          MetricStat:
            Metric:
              MetricName: RequestCountPerTarget
              Namespace: AWS/ApplicationELB
              Dimensions:
                - Name: TargetGroup
                  Value: !Ref 'TargetGroupHttps'
            Period: 60
            Stat: Average
      ThresholdMetricId: ad1
      TreatMissingData: missing
  AWSResilienceHubAsgManyDyingHostsAlarmrWebServerFleetAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref 'AppAlarmTopic'
      AlarmDescription: Alarm by AWS Resilience Hub that reports when over time an ASG has many unhealthy hosts
      AlarmName: !Sub 'AWSResilienceHub-AsgManyDyingHostsAlarm-2020-07-13_CIA_WebServerFleet'
      ComparisonOperator: GreaterThanThreshold
      EvaluationPeriods: 180
      DatapointsToAlarm: 5
      Dimensions:
        - Name: TargetGroup
          Value: !Ref 'TargetGroupHttps'
        - Name: LoadBalancer
          Value: !Ref 'PublicElasticLoadBalancerV2'
      MetricName: UnHealthyHostCount
      Namespace: AWS/ApplicationELB
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: missing
  WebACLAssociation:
    Type: AWS::WAFv2::WebACLAssociation
    DependsOn: BasicSecurityACL
    Properties:
      ResourceArn: !Ref 'PublicElasticLoadBalancerV2'
      WebACLArn: !GetAtt 'BasicSecurityACL.Arn'
  BasicSecurityACL:
    Type: AWS::WAFv2::WebACL
    DependsOn: PublicElasticLoadBalancerV2
    Properties:
      Name: BasicSecurityACL
      Scope: REGIONAL
      Description: This is a BasicSecurityACL
      DefaultAction:
        Allow: {}
      VisibilityConfig:
        SampledRequestsEnabled: true
        CloudWatchMetricsEnabled: true
        MetricName: BasicSecurityACLMetric
      Rules:
        - Name: RuleWithAWSManagedRulesAmazonIpReputationList
          Priority: 0
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: RuleWithAWSManagedRulesAmazonIpReputationList
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesAmazonIpReputationList
              ExcludedRules: []
        - Name: RuleWithAWSManagedRulesAnonymousIpList
          Priority: 1
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: RuleWithAWSManagedRulesAnonymousIpList
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesAnonymousIpList
              ExcludedRules: []
        - Name: RuleWithAWSManagedRulesCommonRuleSet
          Priority: 2
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: RuleWithAWSManagedRulesCommonRuleSet
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesCommonRuleSet
              ExcludedRules:
                - GenericLFI_BODY
                - GenericRFI_BODY
        - Name: RuleWithAWSManagedRulesKnownBadInputsRuleSet
          Priority: 3
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: RuleWithAWSManagedRulesKnownBadInputsRuleSet
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesKnownBadInputsRuleSet
              ExcludedRules: []
        - Name: RuleWithAWSManagedRulesLinuxRuleSet
          Priority: 4
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: RuleWithAWSManagedRulesLinuxRuleSet
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesLinuxRuleSet
              ExcludedRules: []
        - Name: RuleWithAWSManagedRulesUnixRuleSet
          Priority: 5
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: RuleWithAWSManagedRulesUnixRuleSet
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesUnixRuleSet
              ExcludedRules: []
  DNSRecordCNAME2PublicElasticLoadBalancerV2:
    Type: AWS::Route53::RecordSet
    Condition: useDomainName
    DependsOn: PublicElasticLoadBalancerV2
    Properties:
      HostedZoneName: !Join
        - ''
        - - !Ref 'DomainName'
          - .
      Comment: !Join
        - ''
        - - !Ref 'DomainNamePrefix'
          - .
          - !Ref 'DomainName'
          - .
      Name: !Join
        - ''
        - - !Ref 'DomainNamePrefix'
          - .
          - !Ref 'DomainName'
          - .
      Type: CNAME
      TTL: '900'
      ResourceRecords:
        - !GetAtt 'PublicElasticLoadBalancerV2.DNSName'
  LoadBalancerListenerHttpsCert:
    Type: AWS::CertificateManager::Certificate
    Properties:
      CertificateTransparencyLoggingPreference: ENABLED
      DomainName: !Join
        - ''
        - - !Ref 'DomainNamePrefix'
          - .
          - !Ref 'DomainName'
          - ''
      ValidationMethod: DNS
  LoadBalancerListenerHttpsV2:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref 'TargetGroupHttps'
      LoadBalancerArn: !Ref 'PublicElasticLoadBalancerV2'
      Certificates:
        - CertificateArn: !Ref 'LoadBalancerListenerHttpsCert'
      Port: 443
      Protocol: HTTPS
      SslPolicy: ELBSecurityPolicy-FS-1-2-Res-2019-08
  TargetGroupHttps:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: CIA-Https
      Port: 8443
      Protocol: HTTPS
      VpcId: !Ref 'VPC'
      HealthCheckPort: 8443
      HealthCheckIntervalSeconds: 5
      HealthCheckProtocol: HTTPS
      HealthCheckPath: /healthcheck/
      HealthCheckTimeoutSeconds: 4
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 6
      Matcher:
        HttpCode: '200'
      TargetGroupAttributes:
        - Key: stickiness.enabled
          Value: 'true'
        - Key: stickiness.type
          Value: lb_cookie
        - Key: stickiness.lb_cookie.duration_seconds
          Value: '180'
    DependsOn:
      - PublicElasticLoadBalancerV2
  PublicLoadBalancerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Public ELB Security Group with HTTPS access on port 443 from the internet
      VpcId: !Ref 'VPC'
      SecurityGroupIngress:
        - Description: Inbound https from public
          IpProtocol: tcp
          FromPort: '443'
          ToPort: '443'
          CidrIp: '0.0.0.0/0'
      SecurityGroupEgress:
        - Description: Outbound https from loadbalancer to server
          IpProtocol: tcp
          FromPort: '8443'
          ToPort: '8443'
          CidrIp: !FindInMap
            - SubnetConfig
            - VPC
            - CIDR
  WebServerFleet:
    Type: AWS::AutoScaling::AutoScalingGroup
    DependsOn:
      - PublicRoute
      - Database
    Properties:
      AvailabilityZones:
        - !GetAtt 'PrivateAppSubnet.AvailabilityZone'
        - !GetAtt 'PrivateAppSubnet2.AvailabilityZone'
        - !GetAtt 'PrivateAppSubnet3.AvailabilityZone'
      VPCZoneIdentifier:
        - !Ref 'PrivateAppSubnet'
        - !Ref 'PrivateAppSubnet2'
        - !Ref 'PrivateAppSubnet3'
      LaunchConfigurationName: !Ref 'WebServerLaunchConfig'
      MinSize: '2'
      MaxSize: '3'
      DesiredCapacity: !Ref 'WebServerCount'
      TargetGroupARNs:
        - !Ref 'TargetGroupHttps'
      Tags:
        - Key: Network
          Value: Public
          PropagateAtLaunch: 'true'
        - Key: SecurityBaselineCheck
          Value: 'true'
          PropagateAtLaunch: 'true'
        - Key: UpdateSSMAgent
          Value: 'true'
          PropagateAtLaunch: 'true'
        - Key: GatherSoftwareInventory
          Value: 'true'
          PropagateAtLaunch: 'true'
        - Key: SystemPatchComplianceMaintenanceWindowActive
          Value: 'true'
          PropagateAtLaunch: 'true'
    CreationPolicy:
      ResourceSignal:
        Timeout: PT45M
        Count: !Ref 'WebServerCount'
    UpdatePolicy:
      AutoScalingRollingUpdate:
        MinInstancesInService: '1'
        MaxBatchSize: '1'
        PauseTime: PT15M
        WaitOnResourceSignals: 'true'
  WebServerScaleUpPolicy:
    Type: AWS::AutoScaling::ScalingPolicy
    Properties:
      AdjustmentType: ChangeInCapacity
      AutoScalingGroupName: !Ref 'WebServerFleet'
      Cooldown: '60'
      ScalingAdjustment: '1'
  WebServerScaleDownPolicy:
    Type: AWS::AutoScaling::ScalingPolicy
    Properties:
      AdjustmentType: ChangeInCapacity
      AutoScalingGroupName: !Ref 'WebServerFleet'
      Cooldown: '60'
      ScalingAdjustment: '-1'
  CPUAlarmHigh:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: Scale-up if CPU > 60% for 30 minutes
      MetricName: CPUUtilization
      Namespace: AWS/EC2
      Statistic: Average
      Period: '900'
      EvaluationPeriods: '2'
      Threshold: '60'
      AlarmActions:
        - !Ref 'WebServerScaleUpPolicy'
      Dimensions:
        - Name: AutoScalingGroupName
          Value: !Ref 'WebServerFleet'
      ComparisonOperator: GreaterThanThreshold
  CPUAlarmLow:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: Scale-down if CPU < 40% for 30 minutes
      MetricName: CPUUtilization
      Namespace: AWS/EC2
      Statistic: Average
      Period: '900'
      EvaluationPeriods: '2'
      Threshold: '40'
      AlarmActions:
        - !Ref 'WebServerScaleDownPolicy'
      Dimensions:
        - Name: AutoScalingGroupName
          Value: !Ref 'WebServerFleet'
      ComparisonOperator: LessThanThreshold
  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref 'EC2SSMRole'
      InstanceProfileName: EC2SSMInstanceProfile
  EC2SSMRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: rdssecretaccess
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - secretsmanager:DescribeSecret
                  - secretsmanager:GetSecretValue
                Resource: !Ref 'RDSInstanceRotationSecret'
        - PolicyName: limitedssm
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - ssm:PutInventory
                  - ssm:PutComplianceItems
                  - ssm:PutConfigurePackageResult
                  - ssm:UpdateAssociationStatus
                  - ssm:UpdateInstanceAssociationStatus
                  - ssm:UpdateInstanceInformation
                  - ssm:ListAssociations
                  - ssm:DescribeAssociation
                  - ssm:ListInstanceAssociations
                  - ec2:DescribeInstanceStatus
                  - ec2messages:AcknowledgeMessage
                  - ec2messages:DeleteMessage
                  - ec2messages:FailMessage
                  - ec2messages:GetEndpoint
                  - ec2messages:GetMessages
                  - ec2messages:SendReply
                  - ssmmessages:CreateControlChannel
                  - ssmmessages:CreateDataChannel
                  - ssmmessages:OpenControlChannel
                  - ssmmessages:OpenDataChannel
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:DescribeLogGroups
                  - logs:DescribeLogStreams
                  - logs:PutLogEvents
                  - ssm:CreateAssociation
                  - ssm:CreateAssociationBatch
                  - ssm:RegisterTargetWithMaintenanceWindow
                  - ssm:RegisterTaskWithMaintenanceWindow
                  - ssm:StartAutomationExecution
                  - ssm:UpdateMaintenanceWindowTarget
                  - ssm:UpdateMaintenanceWindowTask
                  - cloudwatch:DescribeAlarmHistory
                  - cloudwatch:DescribeAlarms
                  - cloudwatch:GetMetricData
                  - cloudwatch:GetMetricStatistics
                  - cloudwatch:ListMetrics
                  - cloudwatch:PutMetricAlarm
                  - cloudwatch:PutMetricData
                Resource: '*'
        - PolicyName: allowgathersoftware
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action: ssm:GetDocument
                Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}::document/AWS-GatherSoftwareInventory'
        - PolicyName: allowrunpatchline
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action: ssm:GetDocument
                Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}::document/AWS-RunPatchBaseline'
        - PolicyName: allowupdatessmagent
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action: ssm:GetDocument
                Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}::document/AWS-UpdateSSMAgent'
      RoleName: EC2SSMRole
  WebServerLaunchConfig:
    Type: AWS::AutoScaling::LaunchConfiguration
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            apt:
              openjdk-17-jdk-headless: []
              ntp: []
              ntpdate: []
              ufw: []
              unzip: []
              language-pack-en: []
              apt-listchanges: []
              debian-goodies: []
              needrestart: []
              apt-show-versions: []
              sysstat: []
              debsecan: []
              debsums: []
              fail2ban: []
          files:
            /etc/cfn/cfn-hup.conf:
              content: !Join
                - ''
                - - "[main]\n"
                  - stack=
                  - !Ref 'AWS::StackId'
                  - "\n"
                  - region=
                  - !Ref 'AWS::Region'
                  - "\n"
              mode: '000400'
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Join
                - ''
                - - "[cfn-auto-reloader-hook]\n"
                  - "triggers=post.update\n"
                  - "path=Resources.WebServerLaunchConfig.Metadata.AWS::CloudFormation::Init\n"
                  - 'action=/opt/aws/bin/cfn-init -v '
                  - '         --stack '
                  - !Ref 'AWS::StackName'
                  - '         --resource WebServerLaunchConfig '
                  - '         --region '
                  - !Ref 'AWS::Region'
                  - "\n"
                  - "runas=root\n"
            /lib/systemd/system/cfn-hup.service:
              content: !Join
                - ''
                - - "[Unit]\n"
                  - "Description=cfn-hup daemon\n\n"
                  - "[Service]\n"
                  - "Type=simple\n"
                  - "ExecStart=/opt/aws/bin/cfn-hup\n"
                  - "Restart=always\n\n"
                  - "[Install]\n"
                  - WantedBy=multi-user.target
            /etc/awslogs.conf:
              content: !Join
                - ''
                - - '[general]'
                  - "\n"
                  - state_file = /var/awslogs/state/agent-state
                  - "\n"
                  - "\n"
                  - '[userdata]'
                  - "\n"
                  - datetime_format = %Y-%m-%d %H:%M:%S
                  - "\n"
                  - file = /var/log/user-data.log
                  - "\n"
                  - buffer_duration = 5000
                  - "\n"
                  - log_stream_name = {instance_id}-{hostname}-{ip_address}-userdata
                  - "\n"
                  - initial_position = start_of_file
                  - "\n"
                  - log_group_name = ec2awslogs
                  - "\n"
                  - "\n"
                  - '[auth]'
                  - "\n"
                  - datetime_format = %Y-%m-%d %H:%M:%S
                  - "\n"
                  - file = /var/log/auth.log
                  - "\n"
                  - buffer_duration = 5000
                  - "\n"
                  - log_stream_name = {instance_id}-{hostname}-{ip_address}-auth
                  - "\n"
                  - initial_position = start_of_file
                  - "\n"
                  - log_group_name = ec2awslogs
                  - "\n"
                  - "\n"
                  - '[syslog]'
                  - "\n"
                  - datetime_format = %Y-%m-%d %H:%M:%S
                  - "\n"
                  - file = /var/log/syslog
                  - "\n"
                  - buffer_duration = 5000
                  - "\n"
                  - log_stream_name = {instance_id}-{hostname}-{ip_address}-syslog
                  - "\n"
                  - initial_position = start_of_file
                  - "\n"
                  - log_group_name = ec2awslogs
                  - "\n"
                  - "\n"
                  - '[kern.log]'
                  - "\n"
                  - datetime_format = %Y-%m-%d %H:%M:%S
                  - "\n"
                  - file = /var/log/kern.log
                  - "\n"
                  - buffer_duration = 5000
                  - "\n"
                  - log_stream_name = {instance_id}-{hostname}-{ip_address}-kern.log
                  - "\n"
                  - initial_position = start_of_file
                  - "\n"
                  - log_group_name = ec2awslogs
                  - "\n"
                  - '[rkhunter.log]'
                  - "\n"
                  - datetime_format = %Y-%m-%d %H:%M:%S
                  - "\n"
                  - file = /var/log/rkhunter.log
                  - "\n"
                  - buffer_duration = 5000
                  - "\n"
                  - log_stream_name = {instance_id}-{hostname}-{ip_address}-rkhunter.log
                  - "\n"
                  - initial_position = start_of_file
                  - "\n"
                  - log_group_name = ec2awslogs
                  - "\n"
                  - '[lynis.log]'
                  - "\n"
                  - datetime_format = %Y-%m-%d %H:%M:%S
                  - "\n"
                  - file = /var/log/lynis.log
                  - "\n"
                  - buffer_duration = 5000
                  - "\n"
                  - log_stream_name = {instance_id}-{hostname}-{ip_address}-lynis.log
                  - "\n"
                  - initial_position = start_of_file
                  - "\n"
                  - log_group_name = ec2awslogs
                  - "\n"
                  - '[ciaapp.log]'
                  - "\n"
                  - datetime_format = %Y-%m-%d %H:%M:%S
                  - "\n"
                  - file = /opt/cia/cia-base/logs/*.log
                  - "\n"
                  - buffer_duration = 5000
                  - "\n"
                  - log_stream_name = {instance_id}-{hostname}-{ip_address}-ciaapp.log
                  - "\n"
                  - initial_position = start_of_file
                  - "\n"
                  - log_group_name = ec2awslogs
                  - "\n"
              mode: '000644'
              owner: root
              group: root
          services: {}
    Properties:
      ImageId: !Ref 'WebServerAmi'
      MetadataOptions:
        HttpEndpoint: enabled
        HttpPutResponseHopLimit: 1
        HttpTokens: required
      IamInstanceProfile: !Ref 'InstanceProfile'
      EbsOptimized: 'true'
      SecurityGroups:
        - !Ref 'WebServerSecurityGroup'
      InstanceType: !Ref 'WebServerInstanceType'
      AssociatePublicIpAddress: 'false'
      UserData: !Base64
        Fn::Join:
          - ''
          - - "#!/bin/bash -xe\n"
            - "exec > >(tee /var/log/user-data.log) 2>&1 \n"
            - "export DEBIAN_FRONTEND=noninteractive\n"
            - "apt_get_install()\n"
            - "{\n"
            - DEBIAN_FRONTEND=noninteractive apt-get -y \n
            - -o DPkg::Options::=--force-confnew \n
            - "install $@\n"
            - "}\n"
            - "timedatectl set-timezone Europe/Stockholm \n"
            - "dpkg-reconfigure -f noninteractive tzdata \n"
            - "echo 'LANG=\"en_US.UTF-8\"'> /etc/default/locale\n"
            - "locale-gen en_US.UTF-8 en_GB.UTF-8 sv_SE.UTF-8 \n"
            - "dpkg-reconfigure --frontend=noninteractive locales \n"
            - "apt-get update\n"
            - "apt-get -y install apt-transport-https\n"
            - "apt-get update\n"
            - "apt-get -y dist-upgrade\n"
            - "apt-get -y install python3-pip python3-setuptools unattended-upgrades chrony\n"
            - "echo 'server 169.254.169.123 prefer iburst minpoll 4 maxpoll 4' >> /etc/chrony/chrony.conf \n"
            - "sudo /etc/init.d/chrony restart\n"
            - "sudo pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz\n"
            - "mkdir -p /opt/aws/bin\n"
            - "sudo ln -s /usr/local/bin/cfn* /opt/aws/bin\n"
            - "##systemctl enable cfn-hup.service \n"
            - "##systemctl start cfn-hup.service \n"
            - "# Install the sample application\n"
            - '/opt/aws/bin/cfn-init -v '
            - '    --stack '
            - !Ref 'AWS::StackName'
            - '    --resource WebServerLaunchConfig '
            - '    --region '
            - !Ref 'AWS::Region'
            - "\n"
            - "ufw default allow outgoing\n"
            - "ufw default deny incoming\n"
            - "sudo /etc/init.d/ssh stop\n"
            - "sudo apt-get -y purge openssh-server\n"
            - "ufw allow 8443/tcp\n"
            - "ufw allow 53\n"
            - "ufw allow 123/udp\n"
            - "ufw enable\n"
            - "#ufw disable\n"
            - "ufw status verbose\n"
            - "echo 'Sysctl hardening'\n"
            - "echo 'kernel.core_uses_pid = 1' >> /etc/sysctl.conf \n"
            - "echo 'kernel.sysrq = 0' >> /etc/sysctl.conf \n"
            - "echo 'kernel.kptr_restrict = 2' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv4.conf.all.rp_filter = 1' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv4.conf.default = 1' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv4.conf.all.accept_redirects = 0' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv4.conf.default.accept_redirects = 0' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv4.conf.all.accept_redirects = 0' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv4.conf.all.log_martians = 1' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv4.conf.default.log_martians = 1' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv4.conf.all.log_martians = 1' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv4.conf.all.send_redirects = 0' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv4.conf.default.accept_source_route = 0' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv4.tcp_syncookies = 1' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv4.tcp_timestamps = 0' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv6.conf.all.accept_redirects = 0' >> /etc/sysctl.conf \n"
            - "echo 'net.ipv6.conf.default.accept_redirects = 0' >> /etc/sysctl.conf \n"
            - "sysctl -p\n"
            - "cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local\n"
            - "sudo snap install amazon-ssm-agent --classic\n"
            - "sudo systemctl start snap.amazon-ssm-agent.amazon-ssm-agent.service\n"
            - "sudo apt-get -y install curl python software-properties-common xz-utils bzip2 gnupg wget graphviz\n"
            - "sudo wget -O /tmp/awslogs-agent-setup.py https://s3.amazonaws.com/aws-cloudwatch/downloads/latest/awslogs-agent-setup.py\n"
            - "sudo chmod 775 /tmp/awslogs-agent-setup.py\n"
            - "sudo mkdir -p /var/awslogs/etc/\n"
            - "sudo /tmp/awslogs-agent-setup.py -n -r eu-west-1 -c /etc/awslogs.conf\n"
            - "sudo apt-get -y install libwww-perl libdatetime-perl\n"
            - "# INSTALL OPEN JDK\n"
            - "sudo ln -s /usr/lib/jvm/java-17-openjdk-arm64 /usr/lib/jvm/jdk-17\n"
            - "wget https://corretto.aws/downloads/latest/amazon-corretto-19-aarch64-linux-jdk.deb\n"
            - "dpkg -i amazon-corretto-19-aarch64-linux-jdk.deb\n"
            - "rm amazon-corretto-19-aarch64-linux-jdk.deb\n"
            - "ln -s /usr/lib/jvm/java-19-amazon-corretto /usr/lib/jvm/jdk-19\n"
            - 'wget '
            - !Ref 'DebPackageUrl'
            - " -O cia-dist-deb.deb \n"
            - "dpkg -i --force all cia-dist-deb.deb \n"
            - "/etc/init.d/cia stop\n"
            - echo 'database.name=
            - !Ref 'DBName'
            - "'  >> /opt/cia/cia-base/webapps/cia/WEB-INF/database.properties\n"
            - echo 'database.username=allhaildiscordia
            - "'  >> /opt/cia/cia-base/webapps/cia/WEB-INF/database.properties\n"
            - echo 'database.password=legalizeit
            - "'  >> /opt/cia/cia-base/webapps/cia/WEB-INF/database.properties\n"
            - echo 'database.aws.secret.arn=
            - !Ref 'RDSInstanceRotationSecret'
            - "'  >> /opt/cia/cia-base/webapps/cia/WEB-INF/database.properties\n"
            - echo 'database.aws.secret=true
            - "'  >> /opt/cia/cia-base/webapps/cia/WEB-INF/database.properties\n"
            - echo 'database.hostname=
            - !GetAtt 'Database.Endpoint.Address'
            - "'  >> /opt/cia/cia-base/webapps/cia/WEB-INF/database.properties\n"
            - "sed -i -e 's_\">/cia<_\">/<_' /opt/cia/cia-base/webapps/cia.xml\n"
            - "TOKEN=$(curl -X PUT 'http://169.254.169.254/latest/api/token' -H 'X-aws-ec2-metadata-token-ttl-seconds: 21600')\n"
            - "LOCAL_IP=$(curl -H 'X-aws-ec2-metadata-token: $TOKEN' http://169.254.169.254/latest/meta-data/local-ipv4)\n"
            - "HISTCONTROL=ignorespace\n"
            - "KEY_PWD=$(< /dev/urandom tr -dc A-Z-a-z-0-9 | head -c${1:-32};echo)\n"
            - "rm -rf /opt/cia/cia-base/etc/keystore.p12\n"
            - '/usr/lib/jvm/jdk-17/bin/keytool -genkey -noprompt -deststoretype pkcs12 -sigalg SHA256withRSA -validity 365 -keyalg RSA -keysize 4096 '
            - ' -alias jetty  -dname ''CN=$LOCAL_IP, OU=None, O=None, L=None, S=None, C=SE'' -keystore /opt/cia/cia-base/etc/keystore.p12 '
            - " -storepass $KEY_PWD -keypass $KEY_PWD\n"
            - "chown cia:cia /opt/cia/cia-base/etc/keystore.p12\n"
            - "chmod 700 /opt/cia/cia-base/etc/keystore.p12\n"
            - " sed -i -e 's_jetty.sslContext.keyManagerPassword=changeit_jetty.sslContext.keyManagerPassword='$KEY_PWD'_' /opt/cia/cia-base/start.d/ssl.ini\n"
            - " sed -i -e 's_jetty.sslContext.keyStorePassword=changeit_jetty.sslContext.keyStorePassword='$KEY_PWD'_' /opt/cia/cia-base/start.d/ssl.ini\n"
            - "TOTAL_MEM_KB=$(awk '/MemTotal:/ { print $2 }' /proc/meminfo)\n"
            - "let JAVAMEMSIZE=$TOTAL_MEM_KB-3046*1024\n"
            - "sed -i -e 's_4096m_'$JAVAMEMSIZE'k_' /etc/default/cia\n"
            - '# sed -i -e ''s/# -DUSE_LOG_APPENDER='
            - !Ref 'WebServerAppLogAppender'
            - / -DUSE_LOG_APPENDER=
            - !Ref 'WebServerAppLogAppender'
            - "/' /etc/default/cia\n"
            - "wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem\n"
            - "mkdir /opt/cia/.postgresql\n"
            - "cp rds-combined-ca-bundle.pem /opt/cia/.postgresql/root.crt\n"
            - "chown -R cia:cia /opt/cia/.postgresql\n"
            - "chmod -R 700 /opt/cia/.postgresql\n"
            - "/usr/lib/jvm/jdk-17/bin/keytool -import -keystore /etc/ssl/certs/java/cacerts -file rds-combined-ca-bundle.pem -storepass 'changeit' -noprompt -alias aws-rds-2016\n"
            - "/etc/init.d/cia stop\n"
            - "/etc/init.d/cia start\n"
            - "update-rc.d cia defaults\n"
            - "# Signal completion\n"
            - '/opt/aws/bin/cfn-signal -e $? '
            - '    --stack '
            - !Ref 'AWS::StackId'
            - '    --resource WebServerFleet '
            - '    --region '
            - !Ref 'AWS::Region'
            - "\n"
  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    DependsOn: PublicLoadBalancerSecurityGroup
    Properties:
      GroupDescription: Allow access from load balancer and bastion as well as outbound HTTP and HTTPS traffic
      VpcId: !Ref 'VPC'
      SecurityGroupIngress:
        - Description: Loadbalancer inbound access
          IpProtocol: tcp
          FromPort: '8443'
          ToPort: '8443'
          SourceSecurityGroupId: !Ref 'PublicLoadBalancerSecurityGroup'
      SecurityGroupEgress:
        - Description: Loadbalancer outbound access
          IpProtocol: tcp
          FromPort: '8443'
          ToPort: '8443'
          SourceSecurityGroupId: !Ref 'PublicLoadBalancerSecurityGroup'
        - Description: Server outbound http access
          IpProtocol: tcp
          FromPort: '80'
          ToPort: '80'
          CidrIp: '0.0.0.0/0'
        - Description: Server outbound https access
          IpProtocol: tcp
          FromPort: '443'
          ToPort: '443'
          CidrIp: '0.0.0.0/0'
        - Description: Server outbound postgres access
          IpProtocol: tcp
          FromPort: '5432'
          ToPort: '5432'
          CidrIp: !FindInMap
            - SubnetConfig
            - VPC
            - CIDR
        - Description: Server outbound ntp access
          IpProtocol: udp
          FromPort: '123'
          ToPort: '123'
          CidrIp: '0.0.0.0/0'
        - Description: Server outbound udp dns access
          IpProtocol: udp
          FromPort: '53'
          ToPort: '53'
          CidrIp: '0.0.0.0/0'
        - Description: Server outbound tcp dns access
          IpProtocol: tcp
          FromPort: '53'
          ToPort: '53'
          CidrIp: '0.0.0.0/0'
Outputs:
  WebSite:
    Description: URL of the website
    Value: !Join
      - ''
      - - https://
        - !GetAtt 'PublicElasticLoadBalancerV2.DNSName'
  JDBCConnectionString:
    Description: JDBC connection string for database
    Value: !Join
      - ''
      - - jdbc:postgresql://
        - !GetAtt 'Database.Endpoint.Address'
        - ':'
        - !GetAtt 'Database.Endpoint.Port'
        - /
        - !Ref 'DBName'