AWSTemplateFormatVersion: '2010-09-09'
Description: Neo4j Enterprise Edition (qs-1shnl7a6p)
Metadata:
  QuickStartDocumentation:
    EntrypointName: "Parameters for deploying the Quick Start"
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "Neo4j Configuration"
        Parameters:
          - InstallGraphDataScience
          - GraphDataScienceLicenseKey
          - InstallBloom
          - BloomLicenseKey
          - Password
      - Label:
          default: "Infrastructure Configuration"
        Parameters:
          - NumberOfServers
          - InstanceType
          - DiskSize
          - SSHCIDR
    ParameterLabels:
      InstallGraphDataScience:
        default: Install Graph Data Science
      GraphDataScienceLicenseKey:
        default: Graph Data Science License Key
      InstallBloom:
        default: Install Bloom
      BloomLicenseKey:
        default: Bloom License Key
      Password:
        default: Password
      NumberOfServers:
        default: Number of Servers
      InstanceType:
        default: Instance Type
      DiskSize:
        default: Disk Size
      SSHCIDR:
        default: SSH CIDR

Parameters:
  InstallGraphDataScience:
    Description: Install Graph Data Science
    Type: String
    Default: 'False'
    AllowedValues:
      - 'True'
      - 'False'

  GraphDataScienceLicenseKey:
    Description: License Key for Graph Data Science (License keys will be sent to and stored by Neo4j. This information will only be used for the purposes of product activation.)
    Type: String
    Default: 'None'

  InstallBloom:
    Description: Install Bloom
    Type: String
    Default: 'False'
    AllowedValues:
      - 'True'
      - 'False'

  BloomLicenseKey:
    Description: License Key for Bloom (License keys will be sent to and stored by Neo4j. This information will only be used for the purposes of product activation.)
    Type: String
    Default: 'None'

  Password:
    Description: Password for Neo4j
    Type: String
    MinLength: 6
    NoEcho: true

  NumberOfServers:
    Description: Must be 3 or greater to form a cluster
    Type: Number
    Default: 3
    AllowedValues:
      - 1
      - 3
      - 4
      - 5
      - 6
      - 7
      - 8
      - 9
      - 10

  InstanceType:
    Description: EC2 instance type
    Type: String
    Default: t3.medium
    AllowedValues:
      - t3.medium
      - t3.large
      - t3.xlarge
      - t3.2xlarge
      - r6i.large
      - r6i.xlarge
      - r6i.2xlarge
      - r6i.4xlarge
      - r6i.8xlarge
      - r6i.12xlarge
      - r6i.16xlarge
      - r6i.24xlarge
      - r6i.32xlarge

  DiskSize:
    Description: Size in GB of the EBS volume on each node
    Type: Number
    Default: 100
    MinValue: 100
    ConstraintDescription: "Minimum disk size should be 100"

  SSHCIDR:
    Description: SSH CIDR (Specify an address range from which ec2 instances are accessible on port 22. You can use 0.0.0.0/0 to allow access from any IP address)
    Type: String
    MinLength: 9
    MaxLength: 18
    AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})"
    ConstraintDescription: "must be a valid CIDR range of the form x.x.x.x/x."


Rules:
  GDSCheck:
    RuleCondition: !Equals
      - !Ref InstallGraphDataScience
      - 'True'
    Assertions:
      - Assert: !Not
          - !Equals
            - !Ref NumberOfServers
            - '3'
        AssertDescription: Node Count cannot be set to 3 when InstallGraphDataScience is selected. Please set Node Count to 1 or set InstallGraphDataScience to false.

Conditions:
  CreateCluster: !Not
    - !Equals
      - !Ref NumberOfServers
      - '1'

Resources:
  Neo4jVPC:
    Type: AWS::EC2::VPC
    Properties:
      EnableDnsHostnames: true
      CidrBlock: 10.0.0.0/16
      Tags:
        - Key: StackID
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: !Ref 'AWS::StackName'

  Neo4jSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: true
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: !Ref 'AWS::Region'
      Tags:
        - Key: StackID
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: !Ref 'AWS::StackName'
      VpcId: !Ref Neo4jVPC

  Neo4jSubnet2:
    Type: AWS::EC2::Subnet
    Condition: CreateCluster
    Properties:
      CidrBlock: 10.0.2.0/24
      MapPublicIpOnLaunch: true
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: !Ref 'AWS::Region'
      Tags:
        - Key: StackID
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: !Ref 'AWS::StackName'
      VpcId: !Ref Neo4jVPC

  Neo4jSubnet3:
    Type: AWS::EC2::Subnet
    Condition: CreateCluster
    Properties:
      CidrBlock: 10.0.3.0/24
      MapPublicIpOnLaunch: true
      AvailabilityZone:
        Fn::Select:
          - 2
          - Fn::GetAZs: !Ref 'AWS::Region'
      Tags:
        - Key: StackID
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: !Ref 'AWS::StackName'
      VpcId: !Ref Neo4jVPC

  Neo4jRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      Tags:
        - Key: StackID
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: !Ref 'AWS::StackName'
      VpcId: !Ref Neo4jVPC

  Neo4jRoute:
    Type: AWS::EC2::Route
    Properties:
      GatewayId: !Ref Neo4jInternetGateway
      RouteTableId: !Ref Neo4jRouteTable
      DestinationCidrBlock: 0.0.0.0/0

  Neo4jSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref Neo4jRouteTable
      SubnetId: !Ref Neo4jSubnet1


  Neo4jSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Condition: CreateCluster
    Properties:
      RouteTableId: !Ref Neo4jRouteTable
      SubnetId: !Ref Neo4jSubnet2

  Neo4jSubnet3RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Condition: CreateCluster
    Properties:
      RouteTableId: !Ref Neo4jRouteTable
      SubnetId: !Ref Neo4jSubnet3

  Neo4jInternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: StackID
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: !Ref 'AWS::StackName'

  Neo4jInternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref Neo4jInternetGateway
      VpcId: !Ref Neo4jVPC

  Neo4jNetworkLoadBalancer:
    DependsOn: Neo4jInternetGateway
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      IpAddressType: ipv4
      Name: !Join
        - '-'
        - - !Ref 'AWS::StackName'
          - 'nlb'
      Scheme: internet-facing
      Subnets: !If [CreateCluster, [!Ref Neo4jSubnet1,!Ref Neo4jSubnet2,!Ref Neo4jSubnet3], [!Ref Neo4jSubnet1]]
      Tags:
        - Key: StackID
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: !Ref 'AWS::StackName'
      Type: network

  Neo4jHTTPListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref Neo4jHTTPTargetGroup
          Type: forward
      LoadBalancerArn: !Ref Neo4jNetworkLoadBalancer
      Port: 7474
      Protocol: TCP

  Neo4jBoltListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref Neo4jBoltTargetGroup
          Type: forward
      LoadBalancerArn: !Ref Neo4jNetworkLoadBalancer
      Port: 7687
      Protocol: TCP

  Neo4jHTTPTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Join
        - '-'
        - - !Ref 'AWS::StackName'
          - 'http'
          - 'tg'
      Port: 7474
      Protocol: TCP
      HealthCheckIntervalSeconds: 10
      Tags:
        - Key: StackID
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: !Ref 'AWS::StackName'
      VpcId: !Ref Neo4jVPC

  Neo4jBoltTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Join
        - '-'
        - - !Ref 'AWS::StackName'
          - 'bolt'
          - 'tg'
      Port: 7687
      Protocol: TCP
      HealthCheckIntervalSeconds: 10
      Tags:
        - Key: StackID
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: !Ref 'AWS::StackName'
      VpcId: !Ref Neo4jVPC

  Neo4jAutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    CreationPolicy:
      ResourceSignal:
        Count: !Ref NumberOfServers
        Timeout: PT8M
    Properties:
      AvailabilityZones: !If [CreateCluster, [!Select [ 0, Fn::GetAZs: !Ref 'AWS::Region' ] , !Select [ 1, Fn::GetAZs: !Ref 'AWS::Region' ] , !Select [ 2, Fn::GetAZs: !Ref 'AWS::Region' ]],[!Select [ 0, Fn::GetAZs: !Ref 'AWS::Region' ]]]
      LaunchConfigurationName:
        Ref: Neo4jLaunchConfiguration
      MinSize: !Ref NumberOfServers
      MaxSize: !Ref NumberOfServers
      VPCZoneIdentifier: !If [CreateCluster, [!Ref Neo4jSubnet1,!Ref Neo4jSubnet2,!Ref Neo4jSubnet3], [!Ref Neo4jSubnet1]]
      TargetGroupARNs:
        - Ref: Neo4jHTTPTargetGroup
        - Ref: Neo4jBoltTargetGroup
      DesiredCapacity:
        !Ref NumberOfServers
      Tags:
        - Key: StackID
          Value: !Ref 'AWS::StackId'
          PropagateAtLaunch: true
        - Key: Name
          Value: !Ref 'AWS::StackName'
          PropagateAtLaunch: true


  Neo4jLaunchConfiguration:
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      ImageId: !FindInMap
        - Neo4j
        - !Ref 'AWS::Region'
        - BYOL
      InstanceType:
        Ref: InstanceType
      SecurityGroups:
        - Ref: Neo4jExternalSecurityGroup
        - Ref: Neo4jInternalSecurityGroup
      EbsOptimized: true
      IamInstanceProfile:
        Ref: Neo4jInstanceProfile
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize:
              Ref: DiskSize
            VolumeType: gp3
            Encrypted: true
      UserData:
        Fn::Base64:
          !Join
          - ''
          - - "#!/bin/bash\n"
            - "set -euo pipefail\n"
            - "echo Running startup script...\n"

            - "installGraphDataScience="
            - Ref: InstallGraphDataScience
            - "\n"

            - "graphDataScienceLicenseKey="
            - Ref: GraphDataScienceLicenseKey
            - "\n"

            - "installBloom="
            - Ref: InstallBloom
            - "\n"

            - "bloomLicenseKey="
            - Ref: BloomLicenseKey
            - "\n"

            - "password="
            - Ref: Password
            - "\n"

            - "nodeCount="
            - Ref: NumberOfServers
            - "\n"

            - "loadBalancerDNSName="
            - Fn::GetAtt: [Neo4jNetworkLoadBalancer,DNSName]
            - "\n"

            - "stackName="
            - Ref: AWS::StackName
            - "\n"

            - "region="
            - Ref: AWS::Region
            - "\n"

            - "install_neo4j_from_yum() {\n"
            - "  echo \"Installing Graph Database...\"\n"
            - "  export NEO4J_ACCEPT_LICENSE_AGREEMENT=yes\n"
            - "  ASG_NAME=$(aws --region $region cloudformation describe-stack-resources --stack-name $stackName --query 'StackResources[?ResourceType==`AWS::AutoScaling::AutoScalingGroup` && LogicalResourceId==`Neo4jAutoScalingGroup`].PhysicalResourceId' --output text)\n"
            - "  NEO4J_VERSION_ASG_TAG=$(aws --region $region autoscaling describe-auto-scaling-groups --auto-scaling-group-names $ASG_NAME --query 'AutoScalingGroups[0].Tags[?Key==`NEO4J_VERSION`].Value' --output text)\n"
            - "  if [[ ! -z $NEO4J_VERSION_ASG_TAG ]]; then\n"
            - "    echo \"Found NEO4J_VERSION_ASG_TAG Tag on AutoScalingGroup $ASG_NAME: NEO4J_VERSION_ASG_TAG=$NEO4J_VERSION_ASG_TAG\"\n"
            - "    PACKAGE_VERSION=$NEO4J_VERSION_ASG_TAG\n"
            - "    local -r NEO4J_YUM_PACKAGE=\"neo4j-enterprise-$PACKAGE_VERSION\"\n"
            - "  else\n"
            - "    PACKAGE_VERSION=$(curl --fail http://versions.neo4j-templates.com/target.json | jq -r '.aws.\"5\"' || echo \"\")\n"
            - "    if [[ ! -z $PACKAGE_VERSION && $PACKAGE_VERSION != \"null\" ]]; then\n"
            - "      echo \"Found PACKAGE_VERSION from http://versions.neo4j-templates.com : PACKAGE_VERSION=$PACKAGE_VERSION\"\n"
            - "      local -r NEO4J_YUM_PACKAGE=\"neo4j-enterprise-$PACKAGE_VERSION\"\n"
            - "    else\n"
            - "      echo 'Failed to resolve Neo4j version from http://versions.neo4j-templates.com, using PACKAGE_VERSION=latest'\n"
            - "      PACKAGE_VERSION=\"latest\"\n"
            - "      local -r NEO4J_YUM_PACKAGE='neo4j-enterprise'\n"
            - "    fi\n"
            - "  fi\n"
            - "  yum -y install \"${NEO4J_YUM_PACKAGE}\"\n"
            - "  yum update -y aws-cfn-bootstrap\n"
            - "  systemctl enable neo4j\n"
            - "  if [[ \"$PACKAGE_VERSION\" == \"latest\" ]]; then\n"
            - "    PACKAGE_VERSION=$(/usr/share/neo4j/bin/neo4j --version)\n"
            - "  fi\n"
            - "}\n"

            - "install_apoc_plugin() {\n"
            - "  echo \"Installing APOC...\"\n"
            - "  mv /var/lib/neo4j/labs/apoc-*-core.jar /var/lib/neo4j/plugins\n"
            - "}\n"

            - "configure_graph_data_science() {\n"
            - "  if [[ \"${installGraphDataScience}\" == True && \"${nodeCount}\" == 1 ]]; then\n"
            - "    echo \"Installing Graph Data Science...\"\n"
            - "    cp /var/lib/neo4j/products/neo4j-graph-data-science-*.jar /var/lib/neo4j/plugins\n"
            - "  fi\n"
            - "  if [[ $graphDataScienceLicenseKey != None ]]; then\n"
            - "    echo \"Writing GDS license key...\"\n"
            - "    mkdir -p /etc/neo4j/licenses\n"
            - "    echo \"${graphDataScienceLicenseKey}\" > /etc/neo4j/licenses/neo4j-gds.license\n"
            - "    sed -i '$a gds.enterprise.license_file=/etc/neo4j/licenses/neo4j-gds.license' /etc/neo4j/neo4j.conf\n"
            - "  fi\n"
            - "}\n"

            - "configure_bloom() {\n"
            - "  if [[ $installBloom == True ]]; then\n"
            - "    echo \"Installing Bloom...\"\n"
            - "    cp /var/lib/neo4j/products/bloom-plugin-*.jar /var/lib/neo4j/plugins\n"
            - "  fi\n"
            - "  if [[ $bloomLicenseKey != None ]]; then\n"
            - "    echo \"Writing Bloom license key...\"\n"
            - "    mkdir -p /etc/neo4j/licenses\n"
            - "    echo \"${bloomLicenseKey}\" > /etc/neo4j/licenses/neo4j-bloom.license\n"
            - "    sed -i '$a dbms.bloom.license_file=/etc/neo4j/licenses/neo4j-bloom.license' /etc/neo4j/neo4j.conf\n"
            - "  fi\n"
            - "}\n"

            - "extension_config() {\n"
            - "  echo Configuring extensions and security in neo4j.conf...\n"
            - "  sed -i s~#server.unmanaged_extension_classes=org.neo4j.examples.server.unmanaged=/examples/unmanaged~server.unmanaged_extension_classes=com.neo4j.bloom.server=/bloom,semantics.extension=/rdf~g /etc/neo4j/neo4j.conf\n"
            - "  sed -i s/#dbms.security.procedures.unrestricted=my.extensions.example,my.procedures.*/dbms.security.procedures.unrestricted=gds.*,apoc.*,bloom.*/g /etc/neo4j/neo4j.conf\n"
            - "  echo \"dbms.security.http_auth_allowlist=/,/browser.*,/bloom.*\" >> /etc/neo4j/neo4j.conf\n"
            - "  echo \"dbms.security.procedures.allowlist=apoc.*,gds.*,bloom.*\" >> /etc/neo4j/neo4j.conf\n"
            - "}\n"

            - "build_neo4j_conf_file() {\n"
            - "  local -r privateIP=\"$(hostname -i | awk '{print $NF}')\"\n"
            - "  echo \"Configuring network in neo4j.conf...\"\n"
            - "  sed -i 's/#server.default_listen_address=0.0.0.0/server.default_listen_address=0.0.0.0/g' /etc/neo4j/neo4j.conf\n"
            - "  sed -i s/#server.default_advertised_address=localhost/server.default_advertised_address=\"${loadBalancerDNSName}\"/g /etc/neo4j/neo4j.conf\n"
            - "  sed -i s/#server.discovery.advertised_address=:5000/server.discovery.advertised_address=\"${privateIP}\":5000/g /etc/neo4j/neo4j.conf\n"
            - "  sed -i s/#server.cluster.advertised_address=:6000/server.cluster.advertised_address=\"${privateIP}\":6000/g /etc/neo4j/neo4j.conf\n"
            - "  sed -i s/#server.cluster.raft.advertised_address=:7000/server.cluster.raft.advertised_address=\"${privateIP}\":7000/g /etc/neo4j/neo4j.conf\n"
            - "  sed -i s/#server.routing.advertised_address=:7688/server.routing.advertised_address=\"${privateIP}\":7688/g /etc/neo4j/neo4j.conf\n"
            - "  sed -i s/#server.discovery.listen_address=:5000/server.discovery.listen_address=\"${privateIP}\":5000/g /etc/neo4j/neo4j.conf\n"
            - "  sed -i s/#server.routing.listen_address=0.0.0.0:7688/server.routing.listen_address=\"${privateIP}\":7688/g /etc/neo4j/neo4j.conf\n"
            - "  sed -i s/#server.cluster.listen_address=:6000/server.cluster.listen_address=\"${privateIP}\":6000/g /etc/neo4j/neo4j.conf\n"
            - "  sed -i s/#server.cluster.raft.listen_address=:7000/server.cluster.raft.listen_address=\"${privateIP}\":7000/g /etc/neo4j/neo4j.conf\n"
            - "  sed -i s/#server.bolt.listen_address=:7687/server.bolt.listen_address=\"${privateIP}\":7687/g /etc/neo4j/neo4j.conf\n"
            - "  sed -i s/#server.bolt.advertised_address=:7687/server.bolt.advertised_address=\"${privateIP}\":7687/g /etc/neo4j/neo4j.conf\n"
            - "  neo4j-admin server memory-recommendation >> /etc/neo4j/neo4j.conf\n"
            - "  echo \"server.metrics.enabled=true\" >> /etc/neo4j/neo4j.conf\n"
            - "  echo \"server.metrics.jmx.enabled=true\" >> /etc/neo4j/neo4j.conf\n"
            - "  echo \"server.metrics.prefix=neo4j\" >> /etc/neo4j/neo4j.conf\n"
            - "  echo \"server.metrics.filter=*\" >> /etc/neo4j/neo4j.conf\n"
            - "  echo \"server.metrics.csv.interval=5s\" >> /etc/neo4j/neo4j.conf\n"
            - "  echo \"dbms.routing.default_router=SERVER\" >> /etc/neo4j/neo4j.conf\n"

            - "  if [[ ${nodeCount} == 1 ]]; then\n"
            - "    echo \"Running on a single node.\"\n"
            - "  else\n"
            - "    echo \"Running on multiple nodes.  Configuring membership in neo4j.conf...\"\n"
            - "    sed -i s/#initial.dbms.default_primaries_count=1/initial.dbms.default_primaries_count=3/g /etc/neo4j/neo4j.conf\n"
            - "    sed -i s/#initial.dbms.default_secondaries_count=0/initial.dbms.default_secondaries_count=$(expr ${nodeCount} - 3)/g /etc/neo4j/neo4j.conf\n"
            - "    sed -i s/#server.bolt.listen_address=:7687/server.bolt.listen_address=\"${privateIP}\":7687/g /etc/neo4j/neo4j.conf\n"
            - "    echo \"dbms.cluster.minimum_initial_system_primaries_count=${nodeCount}\" >> /etc/neo4j/neo4j.conf\n"
            - "    region=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed 's/.$//')\n"
            - "    instanceId=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)\n"
            - "    stackName=$(aws cloudformation describe-stack-resources --physical-resource-id $instanceId --query 'StackResources[0].StackName' --output text --region $region)\n"
            - "    coreMembers=$(aws autoscaling describe-auto-scaling-instances --region $region --output text --query \"AutoScalingInstances[?contains(AutoScalingGroupName,'$stackName-Neo4jAutoScalingGroup')].[InstanceId]\" | xargs -n1 -I {} aws ec2 describe-instances --instance-ids {} --region $region --query \"Reservations[].Instances[].PrivateIpAddress\" --output text --filter \"Name=tag:aws:cloudformation:stack-name,Values=$stackName\")\n"
            - "    coreMembers=$(echo ${coreMembers} | sed 's/ /:5000,/g')\n"
            - "    coreMembers=$(echo \"${coreMembers}\"):5000\n"
            - "    sed -i s/#dbms.cluster.discovery.endpoints=localhost:5000,localhost:5001,localhost:5002/dbms.cluster.discovery.endpoints=${coreMembers}/g /etc/neo4j/neo4j.conf\n"
            - "  fi\n"
            - "}\n"

            - "start_neo4j() {\n"
            - "  echo \"Starting Neo4j...\"\n"
            - "  service neo4j start\n"
            - "  neo4j-admin dbms set-initial-password \"${password}\"\n"
            - "  while [[ \"$(curl -s -o /dev/null -m 3 -L -w '%{http_code}' http://localhost:7474 )\" != \"200\" ]];\n"
            - "    do echo \"Waiting for cluster to start\"\n"
            - "    sleep 5\n"
            - "  done\n"
            - "  /opt/aws/bin/cfn-signal -e $? --stack \"${stackName}\" --resource Neo4jAutoScalingGroup --region \"${region}\"\n"
            - "}\n"

            #this is to prevent SSRF attacks
            #Read more here https://neo4j.com/developer/kb/protecting-against-ssrf/
            - "add_cypher_ip_blocklist() {\n"
            - "  echo \"internal.dbms.cypher_ip_blocklist=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.169.0/24,fc00::/7,fe80::/10,ff00::/8\" >> /etc/neo4j/neo4j.conf\n"
            - "}\n"

            - "tag_asg_with_neo4j_version() {\n"
            - "  if [[ -z $NEO4J_VERSION_ASG_TAG ]]; then\n"
            - "    echo \"Tagging AutoScalingGroup $ASG_NAME with Key NEO4J_VERSION and Value $PACKAGE_VERSION\"\n"
            - "    aws --region $region autoscaling create-or-update-tags --tags ResourceId=$ASG_NAME,ResourceType=auto-scaling-group,Key=NEO4J_VERSION,Value=$PACKAGE_VERSION,PropagateAtLaunch=true\n"
            - "  else\n"
            - "    echo \"ASG is already tagged with NEO4J_VERSION\"\n"
            - "  fi\n"
            - "  /opt/aws/bin/cfn-signal -e $? --stack \"${stackName}\" --resource Neo4jAutoScalingGroup --region \"${region}\"\n"
            - "}\n"

            - "install_neo4j_from_yum\n"
            - "install_apoc_plugin\n"
            - "extension_config\n"
            - "build_neo4j_conf_file\n"
            - "add_cypher_ip_blocklist\n"
            - "configure_graph_data_science\n"
            - "configure_bloom\n"
            - "start_neo4j\n"
            - "tag_asg_with_neo4j_version\n"


  Neo4jInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - Ref: Neo4jRole
  Neo4jRole:
    Type: AWS::IAM::Role
    Metadata:
      cfn-lint:
        config:
          ignore_checks:
            - EIAMPolicyWildcardResource
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: Neo4jPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - autoscaling:CreateOrUpdateTags
                  - autoscaling:DescribeAutoScalingInstances
                  - autoscaling:DescribeAutoScalingGroups
                  - cloudformation:DescribeStackDriftDetectionStatus
                  - cloudformation:DescribeStackEvents
                  - cloudformation:DescribeStackInstance
                  - cloudformation:DescribeStackResource
                  - cloudformation:DescribeStackResourceDrifts
                  - cloudformation:DescribeStackResources
                  - cloudformation:DescribeStacks
                  - cloudformation:DescribeStackSet
                  - cloudformation:DescribeStackSetOperation
                  - ec2:DescribeInstances
                Resource:
                  - "*"

  Neo4jExternalSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable SSH and Neo4j External Ports
      VpcId: !Ref Neo4jVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SSHCIDR
        - IpProtocol: tcp
          FromPort: 7474
          ToPort: 7474
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 7687
          ToPort: 7687
          CidrIp: 0.0.0.0/0

  Neo4jInternalSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable Neo4j Internal Ports
      VpcId: !Ref Neo4jVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 5000
          ToPort: 5000
          SourceSecurityGroupId: !GetAtt Neo4jExternalSecurityGroup.GroupId
        - IpProtocol: tcp
          FromPort: 6000
          ToPort: 6000
          SourceSecurityGroupId: !GetAtt Neo4jExternalSecurityGroup.GroupId
        - IpProtocol: tcp
          FromPort: 7000
          ToPort: 7000
          SourceSecurityGroupId: !GetAtt Neo4jExternalSecurityGroup.GroupId
        - IpProtocol: tcp
          FromPort: 7688
          ToPort: 7688
          SourceSecurityGroupId: !GetAtt Neo4jExternalSecurityGroup.GroupId
        - IpProtocol: tcp
          FromPort: 2003
          ToPort: 2003
          SourceSecurityGroupId: !GetAtt Neo4jExternalSecurityGroup.GroupId
        - IpProtocol: tcp
          FromPort: 2004
          ToPort: 2004
          SourceSecurityGroupId: !GetAtt Neo4jExternalSecurityGroup.GroupId
        - IpProtocol: tcp
          FromPort: 3637
          ToPort: 3637
          SourceSecurityGroupId: !GetAtt Neo4jExternalSecurityGroup.GroupId
        - IpProtocol: tcp
          FromPort: 5005
          ToPort: 5005
          SourceSecurityGroupId: !GetAtt Neo4jExternalSecurityGroup.GroupId

Outputs:
  Neo4jBrowserURL:
    Description: URL for Neo4j Browser
    Value: !Join
      - ''
      - - 'http://'
        - !GetAtt Neo4jNetworkLoadBalancer.DNSName
        - ':'
        - '7474'
  Neo4jURI:
    Description: Neo4j URI Scheme
    Value: !Join
      - ''
      - - 'neo4j://'
        - !GetAtt Neo4jNetworkLoadBalancer.DNSName
        - ':'
        - '7687'
  Neo4jUsername:
    Description: Username for Neo4j
    Value: 'The username is neo4j.  The password is what you provided to the template.'

Mappings:
  Neo4j:
    us-east-1:
      BYOL: ami-0e400af847eb9a531
    us-east-2:
      BYOL: ami-0107e71b1207b90c1
    us-west-1:
      BYOL: ami-01a47d8951b35c272
    us-west-2:
      BYOL: ami-074caf8265c21fd4d
    ca-central-1:
      BYOL: ami-07887e0c7c8cd3151
    eu-central-1:
      BYOL: ami-01321a57a6c5786c4
    eu-west-1:
      BYOL: ami-0f91decef80d7d93b
    eu-west-2:
      BYOL: ami-0a7d3edbd43b7eb80
    eu-west-3:
      BYOL: ami-084419b01954a6223
    eu-north-1:
      BYOL: ami-05fed050da0ade42e
    eu-south-1:
      BYOL: ami-02c3742c42b59e7ef
    ap-southeast-1:
      BYOL: ami-09a4e375812f71b6a
    ap-southeast-2:
      BYOL: ami-0a95c0a2258c32e85
    ap-southeast-3:
      BYOL: ami-09093311aa2cb29a8
    ap-south-1:
      BYOL: ami-0fceddde2778cc2e0
    ap-northeast-1:
      BYOL: ami-0dbecd5269ffad97f
    ap-northeast-2:
      BYOL: ami-0e78f50ef5b1e902b
    ap-northeast-3:
      BYOL: ami-0ff14d64aef37f40d
    ap-east-1:
      BYOL: ami-05f54d0dc0905041d
    sa-east-1:
      BYOL: ami-0a431786eba166f75
    me-south-1:
      BYOL: ami-0c77db260e85bb422
    af-south-1:
      BYOL: ami-041936db2e9c3abc2
    us-gov-east-1:
      BYOL: ami-02ed55fa8cb841061
    us-gov-west-1:
      BYOL: ami-05f0b2155478f8259