AWSTemplateFormatVersion: '2010-09-09'
Description: 'JFrog Artifactory Quick Start Deployment into an Existing VPC (qs-1q037efj0)'
Metadata:
  QuickStartDocumentation:
    EntrypointName: "Parameters for launching into an existing VPC"
    Order: "2"
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Security configuration
        Parameters:
          - KeyPairName
          - AccessCidr
          - RemoteAccessCidr
      - Label:
          default: Network configuration
        Parameters:
          - AvailabilityZones
          - VpcId
          - VpcCidr
          - PublicSubnet1Id
          - PublicSubnet2Id
          - PrivateSubnet1Id
          - PrivateSubnet2Id
          - PrivateSubnet1Cidr
          - PrivateSubnet2Cidr
          - ELBScheme
      - Label:
          default: Bastion configuration
        Parameters:
          - ProvisionBastionHost
          - BastionInstanceType
          - BastionOs
          - BastionRootVolumeSize
          - BastionEnableTcpForwarding
          - NumBastionHosts
          - BastionEnableX11Forwarding
      - Label:
          default: Amazon EC2 configuration
        Parameters:
          - VolumeSize
          - InstanceType
      - Label:
          default: JFrog Artifactory configuration
        Parameters:
          - ArtifactoryProduct
          - ArtifactoryVersion
          - NumberOfSecondary
          - SmLicenseName
          - SmCertName
          - ArtifactoryServerName
          - MasterKey
          - ExtraJavaOptions
          - DefaultJavaMemSettings
      - Label:
          default: Amazon RDS configuration
        Parameters:
          - DatabaseName
          - DatabaseEngine
          - DatabaseUser
          - DatabasePassword
          - DatabaseInstance
          - DatabaseAllocatedStorage
          - DatabasePreferredAz
          - MultiAzDatabase
      - Label:
          default: AWS Quick Start configuration
        Parameters:
          - QsS3BucketName
          - QsS3KeyPrefix
          - QsS3BucketRegion
      - Label:
          default: JFrog Xray Configuration
        Parameters:
          - InstallXray
          - XrayVersion
          - XrayNumberOfInstances
          - XrayInstanceType
          - XrayDatabaseUser
          - XrayDatabasePassword
    ParameterLabels:
      AvailabilityZones:
        default: Availability Zones
      KeyPairName:
        default: SSH key name
      VpcId:
        default: VPC ID
      VpcCidr:
        default: VPC CIDR
      PublicSubnet1Id:
        default: Public subnet 1 ID
      PublicSubnet2Id:
        default: Public subnet 2 ID
      PrivateSubnet1Id:
        default: Private subnet 1 ID
      PrivateSubnet2Id:
        default: Private subnet 2 ID
      PrivateSubnet1Cidr:
        default: Private subnet 1 CIDR
      PrivateSubnet2Cidr:
        default: Private subnet 2 CIDR
      AccessCidr:
        default: Permitted IP range
      RemoteAccessCidr:
        default: Remote access CIDR
      ELBScheme:
        default: Elastic Load Balancing scheme
      ProvisionBastionHost:
        default: Bastion instance
      BastionInstanceType:
        default: Bastion instance type
      BastionRootVolumeSize:
        default: Bastion root volume size
      BastionEnableTcpForwarding:
        default: Bastion enable TCP forwarding
      BastionEnableX11Forwarding:
        default: Bastion enable X11 forwarding
      BastionOs:
        default: Bastion operating system
      NumBastionHosts:
        default: Number of bastion instances
      VolumeSize:
        default: EBS root volume size
      InstanceType:
        default: EC2 instance type
      NumberOfSecondary:
        default: Secondary instances
      ArtifactoryProduct:
        default: Artifactory product to install
      ArtifactoryVersion:
        default: Artifactory version
      SmLicenseName:
        default: Artifactory licenses secret name
      SmCertName:
        default: Artifactory certificate secret name
      ArtifactoryServerName:
        default: Artifactory server name
      MasterKey:
        default: Master server key
      ExtraJavaOptions:
        default: Extra Java options
      DefaultJavaMemSettings:
        default: Default Java memory settings
      DatabaseName:
        default: Database name
      DatabaseEngine:
        default: Database engine
      DatabaseUser:
        default: Database user
      DatabasePassword:
        default: Database password
      DatabaseInstance:
        default: Database instance type
      DatabaseAllocatedStorage:
        default: Database allocated storage
      DatabasePreferredAz:
        default: Database preferred Availability Zone
      MultiAzDatabase:
        default: High-availability database
      QsS3BucketName:
        default: Quick Start S3 bucket name
      QsS3KeyPrefix:
        default: Quick Start S3 key prefix
      QsS3BucketRegion:
        default: Quick Start S3 bucket region
      InstallXray:
        default: Install JFrog Xray
      XrayVersion:
        default: Version of Xray to install
      XrayNumberOfInstances:
        default: Number of JFrog Xray instances
      XrayInstanceType:
        default: Xray instance type
      XrayDatabaseUser:
        default: Xray Database user
      XrayDatabasePassword:
        default: Xray Database password
Parameters:
  AvailabilityZones:
    Description: List of Availability Zones to use for the subnets in the VPC. Two
      Availability Zones are used for this deployment.
    Type: List<AWS::EC2::AvailabilityZone::Name>
  KeyPairName:
    Description: Name of an existing key pair,
      which allows you to connect securely to your instance after it launches.
      This is the key pair you created in your preferred Region.
    Type: AWS::EC2::KeyPair::KeyName
  VpcId:
    Description: ID of your existing VPC (e.g., vpc-0343606e).
    Type: "AWS::EC2::VPC::Id"
  VpcCidr:
    Description: CIDR block for the VPC.
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.0.0/16
    Type: String
  PublicSubnet1Id:
    Description: ID of the public subnet in Availability Zone 1 of your existing VPC (e.g., subnet-z0376dab).
    Type: "AWS::EC2::Subnet::Id"
  PublicSubnet2Id:
    Description: ID of the public subnet in Availability Zone 2 of your existing VPC (e.g., subnet-a29c3d84).
    Type: "AWS::EC2::Subnet::Id"
  PrivateSubnet1Id:
    Description: ID of the private subnet in Availability Zone 1 of your existing VPC (e.g., subnet-a0246dcd).
    Type: "AWS::EC2::Subnet::Id"
  PrivateSubnet2Id:
    Description: ID of the private subnet in Availability Zone 2 of your existing VPC (e.g., subnet-b58c3d67).
    Type: "AWS::EC2::Subnet::Id"
  PrivateSubnet1Cidr:
    Description: CIDR of the private subnet in Availability Zone 1 of your existing VPC (e.g., 10.0.0.0/19).
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.0.0/19
    Type: String
  PrivateSubnet2Cidr:
    Description: CIDR of the private subnet in Availability Zone 2 of your existing VPC (e.g., 10.0.32.0/19).
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.32.0/19
    Type: String
  AccessCidr:
    Description: CIDR IP range that is permitted to access Artifactory.
      We recommend that you set this value to a trusted IP range.
      For example, you might want to grant only your corporate network access to the software.
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    Type: String
  RemoteAccessCidr:
    Description: Remote CIDR range that allows you to connect to the bastion instance by using SSH.
      We recommend that you set this value to a trusted IP range.
      For example, you might want to grant specific ranges inside your corporate network SSH access.
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    Type: String
  ELBScheme:
    Description: Choose whether this is internet facing or internal.
    AllowedValues:
      - internal
      - internet-facing
    Default: internet-facing
    Type: String
  ProvisionBastionHost:
    Description: Choose Disabled to skip creating a bastion instance. Due to the JFrog Container Registry nodes being
      created in private subnets, the default setting of Enabled this is highly recommended.
    AllowedValues:
      - "Enabled"
      - "Disabled"
    Default: "Enabled"
    Type: String
  BastionInstanceType:
    Description: Size of the bastion instances.
    AllowedValues:
      - t3.nano
      - t3.micro
      - t3.small
      - t3.medium
      - t3.large
      - m5.large
      - m5.xlarge
      - m5.2xlarge
      - m5.4xlarge
    Default: "t3.micro"
    Type: String
  BastionRootVolumeSize:
    Description: Size of the root volume on the bastion instances.
    Default: 10
    Type: Number
  BastionEnableTcpForwarding:
    Description: Choose whether to enable TCPForwarding via the bootstrapping of the bastion instance
      or not.
    AllowedValues:
      - "true"
      - "false"
    Default: "true"
    Type: String
  BastionEnableX11Forwarding:
    Description: Choose true to enable X11 via the bootstrapping of the bastion host.
      Setting this value to true will enable X Windows over SSH.
      X11 forwarding can be useful, but it is also a security risk, so it's recommended
      that you keep the default (false) setting.
    AllowedValues:
      - "true"
      - "false"
    Default: "false"
    Type: String
  BastionOs:
    Description: Linux distribution for the Amazon Machine Image (AMI) to be used for the bastion instances.
    AllowedValues:
      - "Amazon-Linux2-HVM"
      - "CentOS-7-HVM"
      - "Ubuntu-Server-20.04-LTS-HVM"
      - "SUSE-SLES-15-HVM"
    Default: "Amazon-Linux2-HVM"
    Type: String
  NumBastionHosts:
    Description: Number of bastion instances to create.
    AllowedValues:
      - '1'
      - '2'
      - '3'
      - '4'
    Default: '1'
    Type: String
  VolumeSize:
    Description: Size in gigabytes of the available storage (min 10GB); the Quick Start will create an
      Amazon Elastic Block Store (Amazon EBS) volumes of this size.
    Default: 200
    Type: Number
  InstanceType:
    Description: EC2 type for the Artifactory instances.
    AllowedValues:
      - m5.large
      - m5.xlarge
      - m5.2xlarge
      - m5.4xlarge
      - m5.8xlarge
      - m5.12xlarge
      - m5.16xlarge
      - m5.24xlarge
      - m5.metal
      - m5d.large
      - m5d.xlarge
      - m5d.2xlarge
      - m5d.4xlarge
      - m5d.8xlarge
      - m5d.12xlarge
      - m5d.16xlarge
      - m5d.24xlarge
      - m5d.metal
      - m5a.large
      - m5a.xlarge
      - m5a.2xlarge
      - m5a.4xlarge
      - m5a.8xlarge
      - m5a.12xlarge
      - m5a.16xlarge
      - m5a.24xlarge
    ConstraintDescription: Must contain valid instance type.
    Default: m5.xlarge
    Type: String
  NumberOfSecondary:
    Description: Number of secondary Artifactory servers to complete your
      HA deployment. To align with Artifactory best practices, the minimum number
      is two and the maximum is seven. Do not select more instances than you
      have licenses for.
    AllowedValues:
      - 0
      - 1
      - 2
      - 3
      - 4
      - 5
      - 6
      - 7
    Default: 2
    Type: Number
  ArtifactoryProduct:
    Description: JFrog Artifactory product you want to install into an AMI.
    AllowedValues:
      - JFrog-Artifactory-Pro
      - JFrog-Artifactory-Enterprise
      - JFrog-Container-Registry
    Default: JFrog-Artifactory-Enterprise
    Type: String
  ArtifactoryVersion:
    Description: Version of Artifactory that you want to deploy into the Quick Start.
      See the release notes to select the version you want to deploy at
      https://www.jfrog.com/confluence/display/RTF/Release+Notes.
    AllowedPattern: ^(([0-9]|[1-9][0-9])\.){2}([1-9][0-9]|[0-9])$
    ConstraintDescription: A version that matches X.X.X per Artifactory releases
    Default: 7.18.6
    Type: String
  SmLicenseName:
    Description: Secret name created in AWS Secrets Manager, which contains the Artifactory licenses.
    Default: ''
    Type: String
  SmCertName:
    Description: Secret name created in AWS Secrets Manager, which contains the SSL certificate and certificate key.
    Default: ''
    Type: String
  ArtifactoryServerName:
    Description: Name of your Artifactory server. Ensure that this matches your certificate.
    Type: String
  MasterKey:
    Description: Master key for the Artifactory cluster. Generate a master key by using the command '$openssl rand -hex 16'.
    AllowedPattern: ^[a-zA-Z0-9]+$
    MinLength: '1'
    MaxLength: '64'
    ConstraintDescription: Only capital or lowercase letters and numbers, with a Max of 64 characters.
    NoEcho: 'true'
    Type: String
  ExtraJavaOptions:
    Description: Set Java options to pass to the JVM for Artifactory. For more information, see the Artifactory
      system requirements at https://www.jfrog.com/confluence/display/RTF/System+Requirements#SystemRequirements-RecommendedHardware.
      Do not add Xms or Xmx settings without disabling DefaultJavaMemSettings.
    Default: -Xss256k -XX:+UseG1GC
    Type: String
  DefaultJavaMemSettings:
    Description: Choose false to overwrite the standard memory-calculation options to pass to the Artifactory JVM.
      If you plan to overwrite them, ensure they are added to the ExtraJavaOptions to prevent the stack provision from failing.
    ConstraintDescription: True or False
    AllowedValues:
      - "true"
      - "false"
    Default: "true"
    Type: String
  DatabaseName:
    Description: Name of your database instance. The name must be unique across all instances
      owned by your AWS account in the current Region. The database instance identifier is case-insensitive,
      but it's stored in lowercase (as in "mydbinstance").
    AllowedPattern: ^[a-zA-Z]([a-zA-Z0-9])+$
    MinLength: '1'
    MaxLength: '60'
    ConstraintDescription: 1 to 60 alphanumeric characters First character must be a letter.
    Default: artdb
    Type: String
  DatabaseEngine:
    Description: Database engine that you want to run, which is currently locked to MySQL.
    AllowedValues:
      - Postgres
    Default: Postgres
    Type: String
  DatabaseUser:
    Description: Login ID for the master user of your database instance.
    MinLength: '1'
    MaxLength: '16'
    AllowedPattern: ^[a-zA-Z]([a-zA-Z0-9])+$
    ConstraintDescription: 1 to 16 alphanumeric characters. First character must be a letter.
    Default: artifactory
    Type: String
  DatabasePassword:
    Description: Password for the Artifactory database user.
    AllowedPattern: ^[^ \\']+$
    MinLength: '8'
    MaxLength: '12'
    ConstraintDescription: Must be at least 8 and no more than
      12 characters containing letters and (minimum 1 capital letter), numbers and
      symbols.
    NoEcho: 'true'
    Type: String
  DatabaseInstance:
    Description: Size of the database to be deployed as part of the Quick Start.
    AllowedValues:
      - db.m5.large
      - db.m5.xlarge
      - db.m5.2xlarge
      - db.m5.10xlarge
      - db.m5.16xlarge
      - db.m5.large
      - db.m5.xlarge
      - db.m5.2xlarge
      - db.m5.4xlarge
      - db.m5.12xlarge
      - db.m5.24xlarge
    ConstraintDescription: Must be a valid database Instance Type.
    Default: db.m5.large
    Type: String
  DatabaseAllocatedStorage:
    Description: Size in gigabytes of the available storage for the database instance.
    MinValue: 5
    MaxValue: 1024
    Default: 10
    Type: Number
  DatabasePreferredAz:
    Description: Preferred availability zone for Amazon RDS primary instance
    Type: String
    Default: ''
  MultiAzDatabase:
    Description: Choose false to create an Amazon RDS instance in a single Availability Zone.
    ConstraintDescription: True or False
    AllowedValues:
      - "true"
      - "false"
    Default: "true"
    Type: String
  QsS3BucketName:
    Description: S3 bucket name for the Quick Start assets. This string can include
      numbers, lowercase letters, and hyphens (-). It cannot start
      or end with a hyphen (-).
    AllowedPattern: ^[0-9a-z]+([0-9a-z-]*[0-9a-z])*$
    ConstraintDescription: Quick Start bucket name can include numbers, lowercase
      letters, and hyphens (-). It cannot start or end with a hyphen (-).
    Default: jfrog-aws
    Type: String
  QsS3KeyPrefix:
    Description: S3 key prefix for the Quick Start assets. Quick Start key prefix
      can include numbers, lowercase letters, uppercase letters, hyphens (-), and
      forward slash (/).
    AllowedPattern: ^[0-9a-zA-Z-/]*$
    ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters,
      uppercase letters, hyphens (-), and forward slash (/).
    Default: artifactory7/latest/
    Type: String
  QsS3BucketRegion:
    Default: 'us-east-1'
    Description: AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. If you use your own bucket, you must specify your own value.
    Type: String
  InstallXray:
    Description: Choose true to install JFrog Xray instance(s).
    ConstraintDescription: True or False
    AllowedValues:
      - "true"
      - "false"
    Default: "true"
    Type: String
  XrayVersion:
    Description: The version of Xray that you want to deploy into the Quick Start.
    AllowedPattern: ^(([0-9]|[1-9][0-9])\.){2}([1-9][0-9]|[0-9])$
    ConstraintDescription: A version that matches X.X.X per Xray releases.
    Default: 3.24.2
    Type: String
  XrayNumberOfInstances:
    Description: The number of Xray instances servers to complete your
      HA deployment. The minimum number is one; the maximum is seven.
      Do not select more than instances than you have licenses for.
    MinValue: 1
    MaxValue: 7
    Default: 1
    Type: Number
  XrayInstanceType:
    Description: The EC2 instance type for the Xray instances.
    AllowedValues:
      - c5.2xlarge
      - c5.4xlarge
    ConstraintDescription: Must contain valid instance type.
    Default: c5.2xlarge
    Type: String
  XrayDatabaseUser:
    Description: The login ID for the Xray database user.
    MinLength: '1'
    MaxLength: '16'
    AllowedPattern: ^[a-zA-Z]([a-zA-Z0-9])+$
    ConstraintDescription: 1 to 16 alphanumeric characters. First character must be a letter.
    Default: xray
    Type: String
  XrayDatabasePassword:
    Description: The password for the Xray database user.
    AllowedPattern: ^[^ \\']+$
    MinLength: '8'
    MaxLength: '12'
    ConstraintDescription: Must be at least 8 and no more than
      12 characters containing letters and (minimum 1 capital letter), numbers and
      symbols.
    NoEcho: 'true'
    Type: String

Conditions:
  EnableBastion: !Equals [!Ref 'ProvisionBastionHost', 'Enabled']
  IsArtifactory: !Not [!Equals [!Ref ArtifactoryProduct, 'JFrog-Container-Registry']]
  HasSecondaryNodes: !Not [!Equals [!Ref NumberOfSecondary, '0']]
  DefaultJava: !Equals [!Ref DefaultJavaMemSettings, "true"]
  UsingDefaultBucket: !Equals [!Ref QsS3BucketName, 'aws-quickstart']
  EnableXray: !Equals [!Ref InstallXray, 'true']
  SmCertNameNotExists: !Equals [!Ref 'SmCertName', '']
  SmCertNameExists: !Not [!Equals [!Ref 'SmCertName', '']]

Resources:
  BastionRole:
    Condition: EnableBastion
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: QSBucketAccess
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action: s3:GetObject
                Resource: !Sub "arn:${AWS::Partition}:s3:::${QsS3BucketName}/*"
              - Effect: Allow
                Action:
                  - logs:CreateLogStream
                  - logs:GetLogEvents
                  - logs:PutLogEvents
                  - logs:DescribeLogGroups
                  - logs:DescribeLogStreams
                  - logs:PutRetentionPolicy
                  - logs:PutMetricFilter
                  - logs:CreateLogGroup
                Resource: !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*:*"
              - Effect: Allow
                Action:
                  - ec2:AssociateAddress
                  - ec2:DescribeAddresses
                Resource: "*"
  BastionStack:
    Condition: EnableBastion
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Sub
        - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QsS3KeyPrefix}submodules/quickstart-linux-bastion/templates/linux-bastion.template
        - S3Bucket: !If [UsingDefaultBucket, !Sub '${QsS3BucketName}-${AWS::Region}', !Ref 'QsS3BucketName']
          S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref 'QsS3BucketRegion']
      Parameters:
        VPCID: !Ref VpcId
        PublicSubnet1ID: !Ref PublicSubnet1Id
        PublicSubnet2ID: !Ref PublicSubnet2Id
        KeyPairName: !Ref KeyPairName
        QSS3BucketName: !Ref QsS3BucketName
        QSS3KeyPrefix: !Sub '${QsS3KeyPrefix}submodules/quickstart-linux-bastion/'
        QSS3BucketRegion: !Ref QsS3BucketRegion
        RemoteAccessCIDR: !Ref RemoteAccessCidr
        BastionInstanceType: !Ref BastionInstanceType
        RootVolumeSize: !Ref BastionRootVolumeSize
        BastionAMIOS: !Ref BastionOs
        EnableTCPForwarding: !Ref BastionEnableTcpForwarding
        EnableX11Forwarding: !Ref BastionEnableX11Forwarding
        AlternativeIAMRole: !Ref BastionRole
        NumBastionHosts: !Ref NumBastionHosts

  ArtifactoryCoreInfraStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Sub
        - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QsS3KeyPrefix}templates/jfrog-artifactory-core-infrastructure.template.yaml
        - S3Bucket: !If [UsingDefaultBucket, !Sub '${QsS3BucketName}-${AWS::Region}', !Ref 'QsS3BucketName']
          S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref 'QsS3BucketRegion']
      Parameters:
        AvailabilityZones:
          Fn::Join:
            - ','
            - Ref: AvailabilityZones
        VpcId: !Ref VpcId
        VpcCidr: !Ref VpcCidr
        PrivateSubnet1Cidr: !Ref PrivateSubnet1Cidr
        PrivateSubnet2Cidr: !Ref PrivateSubnet2Cidr
        PrivateSubnet3Cidr: !Ref PrivateSubnet2Cidr  # This should end up in no new rule but required for EKS
        SubnetIds: !Join [",", [!Ref PrivateSubnet1Id, !Ref PrivateSubnet2Id]]
        DatabaseAllocatedStorage: !Ref DatabaseAllocatedStorage
        DatabasePreferredAz: !Ref DatabasePreferredAz
        MultiAzDatabase: !Ref MultiAzDatabase
        DatabaseEngine: !Ref DatabaseEngine
        DatabaseUser: !Ref DatabaseUser
        DatabasePassword: !Ref DatabasePassword
        DatabaseInstance: !Ref DatabaseInstance
        DatabaseName: !Ref DatabaseName
        InstanceType: !Ref InstanceType
        ArtifactoryHostRole: !Ref ArtifactoryHostRole
        VolumeSize: !Ref VolumeSize
  ArtifactoryElb:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      IpAddressType: ipv4
      Scheme: !Ref ELBScheme
      Subnets:
        - !Ref PublicSubnet1Id
        - !Ref PublicSubnet2Id
      Type: network
      # Type: application
  ArtifactorySslTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckEnabled: True
      HealthCheckIntervalSeconds: 30
      HealthCheckProtocol: TCP
      HealthCheckTimeoutSeconds: 10
      HealthyThresholdCount: 3
      HealthCheckPort: "8082"
      Port: 443
      Protocol: TCP
      TargetType: instance
      UnhealthyThresholdCount: 3
      VpcId: !Ref VpcId
  ArtifactoryTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckEnabled: True
      HealthCheckIntervalSeconds: 30
      HealthCheckProtocol: TCP
      HealthCheckTimeoutSeconds: 10
      HealthyThresholdCount: 3
      HealthCheckPort: "8082"
      Port: 80
      Protocol: TCP
      TargetType: instance
      UnhealthyThresholdCount: 3
      VpcId: !Ref VpcId
  ArtifactorySslElbListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref ArtifactorySslTargetGroup
          Type: forward
      LoadBalancerArn: !Ref ArtifactoryElb
      Port: 443
      Protocol: TCP
  ArtifactoryElbListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Condition: SmCertNameNotExists
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref ArtifactoryTargetGroup
          Type: forward
      LoadBalancerArn: !Ref ArtifactoryElb
      Port: 80
      Protocol: TCP
  ArtifactoryInternalElb:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      IpAddressType: ipv4
      # Name: ArtifactoryInternal-ELB
      Scheme: internal
      Subnets:
        - !Ref PrivateSubnet1Id
        - !Ref PrivateSubnet2Id
      Type: network
  ArtifactoryInternalTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckEnabled: True
      HealthCheckIntervalSeconds: 30
      HealthCheckProtocol: TCP
      HealthCheckTimeoutSeconds: 10
      HealthyThresholdCount: 3
      HealthCheckPort: "8082"
      # Name: artifactory-internal-http
      Port: 80
      Protocol: TCP
      TargetType: instance
      UnhealthyThresholdCount: 3
      VpcId: !Ref VpcId
  ArtifactoryInternalElbListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref ArtifactoryInternalTargetGroup
          Type: forward
      LoadBalancerArn: !Ref ArtifactoryInternalElb
      Port: 80
      Protocol: TCP
  ArtifactoryEc2Sg:
    Type: AWS::EC2::SecurityGroup
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${ArtifactoryProduct}-ec2-instances-sg
      GroupDescription: SG for EC2 instances (also permits access using SSH from the bastion host)
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref VpcCidr
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: !Ref VpcCidr
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: !Ref AccessCidr
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: !Ref AccessCidr
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: !Ref VpcCidr
        - IpProtocol: tcp
          FromPort: 8081
          ToPort: 8082
          CidrIp: !Ref VpcCidr
        - IpProtocol: tcp
          FromPort: 8046
          ToPort: 8046
          CidrIp: !Ref VpcCidr
      SecurityGroupEgress:
        - IpProtocol: "-1"
          CidrIp: 0.0.0.0/0
  ArtifactoryHostRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - 'sts:AssumeRole'
            Principal:
              Service:
                - ec2.amazonaws.com
            Effect: Allow
        Version: 2012-10-17
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonEC2RoleforSSM'
      Policies:
        - PolicyName: "JFrogAMI-policy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: "ec2:Describe*"
                Resource: "*"
              - Effect: "Allow"
                Action: "ec2:AttachVolume"
                Resource: "*"
              - Effect: "Allow"
                Action: "ec2:DetachVolume"
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - "s3:GetObject"
                  - "s3:ListObject"
                  - "s3:ListBucket"
                Resource: "*"
        - PolicyName: 'CloudWatch-policy'
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                  - "logs:DescribeLogStreams"
                Resource: "arn:aws:logs:*:*:*"
              - Effect: "Allow"
                Action:
                  - "s3:GetObject"
                Resource: "*"
        - PolicyName: 'SecretsMaanger-policy'
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "secretsmanager:GetSecretValue"
                Resource: "arn:aws:secretsmanager:*:*:secret:*"
  ArtifactoryHostProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Ref ArtifactoryHostRole
      Roles:
        - !Ref ArtifactoryHostRole
      Path: /
  ArtifactoryPrimary:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Sub
        - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QsS3KeyPrefix}templates/jfrog-artifactory-ec2-instance.template.yaml
        - S3Bucket: !If [UsingDefaultBucket, !Sub '${QsS3BucketName}-${AWS::Region}', !Ref 'QsS3BucketName']
          S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref 'QsS3BucketRegion']
      Parameters:
        PrivateSubnetIds: !Join [',', [!Ref PrivateSubnet1Id]]
        MinScalingNodes: '1'  # Always have 1 PrimaryNode
        MaxScalingNodes: '1'  # Always have 1 PrimaryNode
        DeploymentTag: !If [IsArtifactory, "ArtifactoryPrimary", "JcrPrimary"]
        HostRole: !Ref ArtifactoryHostRole
        QsS3BucketName: !Ref QsS3BucketName
        QsS3KeyPrefix: !Ref QsS3KeyPrefix
        QsS3Uri: !Sub
          - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QsS3KeyPrefix}
          - S3Bucket: !If
              - UsingDefaultBucket
              - !Sub 'aws-quickstart-${AWS::Region}'
              - !Ref 'QsS3BucketName'
            S3Region: !If
              - UsingDefaultBucket
              - !Ref 'AWS::Region'
              - !Ref 'QsS3BucketRegion'
        ArtifactoryProduct: !Ref ArtifactoryProduct
        ArtifactoryLicensesSecretName: !Ref SmLicenseName
        ArtifactoryServerName: !Ref ArtifactoryServerName
        EnableSSL: !If [SmCertNameExists, true, false]
        Certificate: !If [SmCertNameExists, !Sub '{{resolve:secretsmanager:${SmCertName}:SecretString:Certificate}}', '']
        CertificateKey: !If [SmCertNameExists, !Sub '{{resolve:secretsmanager:${SmCertName}:SecretString:CertificateKey}}', '']
        CertificateDomain: !If [SmCertNameExists, !Sub '{{resolve:secretsmanager:${SmCertName}:SecretString:CertificateDomain}}', '']
        ArtifactoryS3Bucket: !GetAtt ArtifactoryCoreInfraStack.Outputs.S3Bucket
        DatabaseUrl: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabaseUrl
        DatabaseDriver: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabaseDriver
        DatabasePlugin: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabasePlugin
        DatabasePluginUrl: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabasePluginUrl
        DatabaseType: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabaseType
        DatabaseUser: !Ref DatabaseUser
        DatabasePassword: !Ref DatabasePassword
        ArtifactoryPrimary: true
        MasterKey: !Ref MasterKey
        ExtraJavaOptions: !If [DefaultJava, !Sub "${ArtifactoryCoreInfraStack.Outputs.JavaOpts} ${ExtraJavaOptions}", !Ref ExtraJavaOptions]
        ArtifactoryVersion: !Ref ArtifactoryVersion
        KeyPairName: !Ref KeyPairName
        HostProfile: !Ref ArtifactoryHostProfile
        SecurityGroups: !Ref ArtifactoryEc2Sg
        InstanceType: !Ref InstanceType
        PrimaryVolume: !GetAtt ArtifactoryCoreInfraStack.Outputs.ArtifactoryEbsVolume
        VolumeSize: !Ref VolumeSize
        TargetGroupARN: !Ref ArtifactoryTargetGroup
        SSLTargetGroupARN: !Ref ArtifactorySslTargetGroup
        InternalTargetGroupARN: !Ref ArtifactoryInternalTargetGroup

  ArtifactorySecondary:
    Condition: HasSecondaryNodes
    DependsOn: ArtifactoryPrimary
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Sub
        - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QsS3KeyPrefix}templates/jfrog-artifactory-ec2-instance.template.yaml
        - S3Bucket: !If [UsingDefaultBucket, !Sub '${QsS3BucketName}-${AWS::Region}', !Ref 'QsS3BucketName']
          S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref 'QsS3BucketRegion']
      Parameters:
        PrivateSubnetIds: !Join [',', [!Ref PrivateSubnet1Id, !Ref PrivateSubnet2Id]]
        MinScalingNodes: !Ref NumberOfSecondary
        MaxScalingNodes: !Ref NumberOfSecondary
        DeploymentTag: ArtifactorySecondary
        HostRole: !Ref ArtifactoryHostRole
        ArtifactoryProduct: !Ref ArtifactoryProduct
        ArtifactoryLicensesSecretName: !Ref SmLicenseName
        ArtifactoryServerName: !Ref ArtifactoryServerName
        EnableSSL: !If [SmCertNameExists, true, false]
        Certificate: !If [SmCertNameExists, !Sub '{{resolve:secretsmanager:${SmCertName}:SecretString:Certificate}}', '']
        CertificateKey: !If [SmCertNameExists, !Sub '{{resolve:secretsmanager:${SmCertName}:SecretString:CertificateKey}}', '']
        CertificateDomain: !If [SmCertNameExists, !Sub '{{resolve:secretsmanager:${SmCertName}:SecretString:CertificateDomain}}', '']
        ArtifactoryS3Bucket: !GetAtt ArtifactoryCoreInfraStack.Outputs.S3Bucket
        DatabaseUrl: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabaseUrl
        DatabaseDriver: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabaseDriver
        DatabasePlugin: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabasePlugin
        DatabasePluginUrl: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabasePluginUrl
        DatabaseType: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabaseType
        DatabaseUser: !Ref DatabaseUser
        DatabasePassword: !Ref DatabasePassword
        ArtifactoryPrimary: false
        MasterKey: !Ref MasterKey
        ExtraJavaOptions: !If [DefaultJava, !Sub "${ArtifactoryCoreInfraStack.Outputs.JavaOpts} ${ExtraJavaOptions}", !Ref ExtraJavaOptions]
        ArtifactoryVersion: !Ref ArtifactoryVersion
        KeyPairName: !Ref KeyPairName
        HostProfile: !Ref ArtifactoryHostProfile
        SecurityGroups: !Ref ArtifactoryEc2Sg
        InstanceType: !Ref InstanceType
        PrimaryVolume: !GetAtt ArtifactoryCoreInfraStack.Outputs.ArtifactoryEbsVolume
        VolumeSize: !Ref VolumeSize
        TargetGroupARN: !Ref ArtifactoryTargetGroup
        SSLTargetGroupARN: !Ref ArtifactorySslTargetGroup
        InternalTargetGroupARN: !Ref ArtifactoryInternalTargetGroup
        QsS3BucketName: !Ref QsS3BucketName
        QsS3KeyPrefix: !Ref QsS3KeyPrefix
        QsS3Uri: !Sub
          - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QsS3KeyPrefix}
          - S3Bucket: !If
              - UsingDefaultBucket
              - !Sub 'aws-quickstart-${AWS::Region}'
              - !Ref 'QsS3BucketName'
            S3Region: !If
              - UsingDefaultBucket
              - !Ref 'AWS::Region'
              - !Ref 'QsS3BucketRegion'
  XrayHostRole:
    Condition: EnableXray
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - 'sts:AssumeRole'
            Principal:
              Service:
                - ec2.amazonaws.com
            Effect: Allow
        Version: 2012-10-17
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonEC2RoleforSSM'
      Policies:
        - PolicyName: "JFrogAMI-policy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: "ec2:Describe*"
                Resource: "*"
              - Effect: "Allow"
                Action: "ec2:AttachVolume"
                Resource: "*"
              - Effect: "Allow"
                Action: "ec2:DetachVolume"
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - "s3:GetObject"
                  - "s3:ListObject"
                  - "s3:ListBucket"
                Resource: "*"
        - PolicyName: 'CloudWatch-policy'
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                  - "logs:DescribeLogStreams"
                Resource: "arn:aws:logs:*:*:*"
              - Effect: "Allow"
                Action:
                  - "s3:GetObject"
                Resource: "*"
  XrayHostProfile:
    Condition: EnableXray
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Ref XrayHostRole
      Roles:
        - !Ref XrayHostRole
      Path: /
  XrayExistingVpcStack:
    Condition: EnableXray
    DependsOn: ArtifactoryPrimary
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Sub
        - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QsS3KeyPrefix}templates/jfrog-xray-ec2-instance.template.yaml
        - S3Bucket: !If [UsingDefaultBucket, !Sub '${QsS3BucketName}-${AWS::Region}', !Ref 'QsS3BucketName']
          S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref 'QsS3BucketRegion']
      Parameters:
        PrivateSubnet1Id: !Ref PrivateSubnet1Id
        PrivateSubnet2Id: !Ref PrivateSubnet2Id
        KeyPairName: !Ref KeyPairName
        MinScalingNodes: !Ref XrayNumberOfInstances
        MaxScalingNodes: !Ref XrayNumberOfInstances
        DeploymentTag: 'xray'
        ArtifactoryProduct: !Ref ArtifactoryProduct
        QsS3BucketName: !Ref QsS3BucketName
        QsS3KeyPrefix: !Ref QsS3KeyPrefix
        QsS3Uri: !Sub
          - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QsS3KeyPrefix}
          - S3Bucket: !If
              - UsingDefaultBucket
              - !Sub 'aws-quickstart-${AWS::Region}'
              - !Ref 'QsS3BucketName'
            S3Region: !If
              - UsingDefaultBucket
              - !Ref 'AWS::Region'
              - !Ref 'QsS3BucketRegion'
        DatabaseDriver: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabaseDriver
        DatabaseType: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabaseType
        DatabaseUser: !Ref DatabaseUser
        DatabasePassword: !Ref DatabasePassword
        MasterKey: !Ref MasterKey
        SecurityGroups: !Ref ArtifactoryEc2Sg
        VolumeSize: !Ref VolumeSize
        ExtraJavaOptions: !GetAtt ArtifactoryCoreInfraStack.Outputs.JavaOpts
        XrayInstanceType: !Ref XrayInstanceType
        JfrogInternalUrl: !Sub "http://${ArtifactoryInternalElb.DNSName}"
        XrayDatabaseUser: !Ref XrayDatabaseUser
        XrayDatabasePassword: !Ref XrayDatabasePassword
        XrayMasterDatabaseUrl: !GetAtt ArtifactoryCoreInfraStack.Outputs.XrayMasterDatabaseUrl
        XrayDatabaseUrl: !GetAtt ArtifactoryCoreInfraStack.Outputs.XrayDatabaseUrl
        XrayVersion: !Ref XrayVersion
        XrayHostRole: !Ref XrayHostRole
        XrayHostProfile: !Ref XrayHostProfile
Outputs:
  ArtifactoryUrl:
    Description: URL of the ELB to access Artifactory
    Value: !If [SmCertNameExists, !Sub "https://${ArtifactoryElb.DNSName}", !Sub "http://${ArtifactoryElb.DNSName}"]
    Export:
      Name: !Sub '${AWS::StackName}-ArtifactoryUrl'
  ArtifactoryInternalUrl:
    Description: URL of the internal ELB to access Artifactory
    Value: !Sub "http://${ArtifactoryInternalElb.DNSName}"
    Export:
      Name: !Sub '${AWS::StackName}-ArtifactoryInternalUrl'
  DatabaseType:
    Description: Type of database
    Value: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabaseType
    Export:
      Name: !Sub '${AWS::StackName}-DatabaseType'
  DatabaseDriver:
    Description: Database driver
    Value: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabaseDriver
    Export:
      Name: !Sub '${AWS::StackName}-DatabaseDriver'
  DatabaseUrl:
    Description: Database driver
    Value: !GetAtt ArtifactoryCoreInfraStack.Outputs.DatabaseUrl
    Export:
      Name: !Sub '${AWS::StackName}-DatabaseUrl'
  ArtifactoryTargetGroup:
    Description: Artifactory target group
    Value: !Ref ArtifactoryTargetGroup
    Export:
      Name: !Sub '${AWS::StackName}-ArtifactoryTargetGroup'
  ArtifactorySslTargetGroup:
    Description: Artifactory SSL target group
    Value: !Ref ArtifactorySslTargetGroup
    Export:
      Name: !Sub '${AWS::StackName}-ArtifactorySslTargetGroup'
  ArtifactoryEc2Sg:
    Description: Artifactory EC2 sercurity group
    Value: !Ref ArtifactoryEc2Sg
    Export:
      Name: !Sub '${AWS::StackName}-ArtifactoryEc2Sg'
  BastionIp:
    Value: !If
      - EnableBastion
      - !GetAtt BastionStack.Outputs.EIP1
      - ""
  XrayMasterDatabaseUrl:
    Description: Database driver
    Value: !GetAtt ArtifactoryCoreInfraStack.Outputs.XrayMasterDatabaseUrl
    Export:
      Name: !Sub '${AWS::StackName}-XrayMasterDatabaseUrl'
  XrayDatabaseUrl:
    Description: Database driver
    Value: !GetAtt ArtifactoryCoreInfraStack.Outputs.XrayDatabaseUrl
    Export:
      Name: !Sub '${AWS::StackName}-XrayDatabaseUrl'
