---
#
# This template starts a 2-node Linux cluster in AWS using c5.large instances.
# it is recommended that you change the security group to only allow inbound
# SSH access for your own IP address (line 366).  Costs will be incurred by starting
# a stack with this template.  The AMIs are standard empty AWS Linux and need to have
# shared file systems, etc configured.
#
Description: Example Linux Cluster
AWSTemplateFormatVersion: '2010-09-09'

# Define template parameters
Parameters:

  # Define the project name to be used in resource tags
  ProjectName:
    Type: String
    Default: ExampleCluster
    Description: The project name to be used in resource tags.

  # Define the SSH key name
  SshKeyName:
    Type: String
    Description: The name of the SSH key to use

  # Define which AZ to run in
  AvailabilityZone:
    Type: String
    Default: us-east-1a
    AllowedValues:
      - us-east-1a
      - us-east-1b
      - us-east-1c
      - us-east-1d
      - us-east-1e
    Description: Create the stack in this Availability Zone (AZ). Default is us-east-1a

  # Define VPC CIDR Block
  VpcCidrBlock:
    Type: String
    Default: 10.0.0.0/16
    Description: CIDR Block for the Virtual Private Cloud (VPC). Default is 10.0.0.0/16

  # Define the compute node subnet
  SubnetCidrBlock:
    Type: String
    Default: 10.0.0.0/24
    Description: CIDR Block for the private compute node subnet.  Max of 254 IP addresses.

  # Define the head node's AMI ID
  HeadNodeAmiId:
    Type: String
    Default: ami-0de53d8956e8dcf80
    Description: AMI ID for the head node

  # Define the head node's instance type
  HeadNodeInstanceType:
    Type: String
    Default: c5.large
    Description: Instance type for the head node

  # Define the compute nodes' AMI ID
  ComputeNodeAmiId:
    Type: String
    Default: ami-0de53d8956e8dcf80
    Description: AMI ID for the compute nodes

  # Define the compute nodes' instance type
  ComputeNodeInstanceType:
    Type: String
    Default: c5.large
    Description: Instance type for the compute nodes


#
# Define the AWS resources that we need to start
#
Resources:

  # Create the private network for the resources
  vpcExample:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock:
        Ref: VpcCidrBlock
      InstanceTenancy: default
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
      - Key: Name
        Value:
          Ref: ProjectName

  # Create a subnet on the private network
  subnetExample:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock:
        Ref: SubnetCidrBlock
      AvailabilityZone:
        Ref: AvailabilityZone
      VpcId:
        Ref: vpcExample
      Tags:
      - Key: Name
        Value: Private subnet

  # Create an internet gateway so that nodes in the VPC can connect to the internet
  internetgatewayExample:
    Type: AWS::EC2::InternetGateway
    Properties: {}

  # Create DHCP options
  dhcpoptionsExample:
    Type: AWS::EC2::DHCPOptions
    Properties:
      DomainName: compute.internal
      DomainNameServers:
      - AmazonProvidedDNS

  # Create a network ACL for the VPC
  networkaclExample:
    Type: AWS::EC2::NetworkAcl
    Properties:
      VpcId:
        Ref: vpcExample

  # Create a new route table
  routetable1Example:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: vpcExample

  # Create a new route table
  routetable2Example:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: vpcExample

  # Create a placement group with a cluster strategy
  clusterplacementgroupExample:
    Type: AWS::EC2::PlacementGroup
    Properties:
      Strategy: cluster

  # Create a new elastic IP (public) for the head node
  ec2headeipExample:
    Type: AWS::EC2::EIP
    DependsOn:
    - ec2headExample
    Properties:
      InstanceId:
        Ref: ec2headExample

  # EBS volume for data
  ebsvolumedataExample:
    Type: AWS::EC2::Volume
    Properties:
      AvailabilityZone:
        Ref: AvailabilityZone
      Size: 20
      SnapshotId: snap-01dcdfaf17cf1f34f
      Tags:
        - Key: Name
          Value:
            Ref: ProjectName
      VolumeType: gp2

  # Create the head node of the cluster
  ec2headExample:
    Type: AWS::EC2::Instance
    DependsOn:
    - clusterplacementgroupExample
    - ebsvolumedataExample
    Properties:
      PlacementGroupName:
        Ref: clusterplacementgroupExample
      DisableApiTermination: false
      EbsOptimized: true
      Volumes:
        - VolumeId:
            Ref: ebsvolumedataExample
          Device: /dev/sdf
      ImageId:
        Ref: HeadNodeAmiId
      InstanceType:
        Ref: HeadNodeInstanceType
      KeyName:
        Ref: SshKeyName
      Monitoring: false
      Tags:
      - Key: Name
        Value:
          Ref: ProjectName
      NetworkInterfaces:
      - DeleteOnTermination: true
        Description: Primary network interface
        DeviceIndex: 0
        SubnetId:
          Ref: subnetExample
        PrivateIpAddresses:
        - PrivateIpAddress: 10.0.0.10
          Primary: true
        GroupSet:
        - Ref: publicfacingnetworksecuritygroupExample

  # Create the compute nodes of the cluster
  ec2computeExample:
    Type: AWS::EC2::Instance
    DependsOn:
    - ec2headExample
    - clusterplacementgroupExample
    Properties:
      PlacementGroupName:
        Ref: clusterplacementgroupExample
      DisableApiTermination: false
      EbsOptimized: true
      ImageId:
        Ref: ComputeNodeAmiId
      InstanceType:
        Ref: ComputeNodeInstanceType
      KeyName:
        Ref: SshKeyName
      Monitoring: false
      Tags:
      - Key: Name
        Value:
          Ref: ProjectName
      NetworkInterfaces:
      - DeleteOnTermination: true
        Description: Primary network interface
        DeviceIndex: 0
        SubnetId:
          Ref: subnetExample
        PrivateIpAddresses:
        - PrivateIpAddress: 10.0.0.101
          Primary: true
        GroupSet:
        - Ref: publicfacingnetworksecuritygroupExample

  # Create the security group for the VPC
  privatenetworksecuritygroupExample:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Private network security group
      VpcId:
        Ref: vpcExample

  # Create the security group for the public-facing head node
  publicfacingnetworksecuritygroupExample:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Public network security group
      VpcId:
        Ref: vpcExample

  # Allow egress from the VPC on any port from any IP on the subnet
  acl1:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      CidrBlock: 0.0.0.0/0
      Egress: true
      Protocol: -1
      RuleAction: allow
      RuleNumber: 100
      NetworkAclId:
        Ref: networkaclExample

  # Allow all traffic between nodes on the subnet
  acl2:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      CidrBlock: 0.0.0.0/0
      Protocol: -1
      RuleAction: allow
      RuleNumber: 100
      NetworkAclId:
        Ref: networkaclExample

  # Associate the network ACL and the subnet
  subnetacl1:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      NetworkAclId:
        Ref: networkaclExample
      SubnetId:
        Ref: subnetExample

  # Associate the internet gateway and the VPC
  gw1:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId:
        Ref: vpcExample
      InternetGatewayId:
        Ref: internetgatewayExample

  # Associate the route table and the subnet
  subnetroute1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: routetable1Example
      SubnetId:
        Ref: subnetExample

  # Create a route entry
  route1:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      RouteTableId:
        Ref: routetable1Example
      GatewayId:
        Ref: internetgatewayExample
    DependsOn: gw1

  # Associate the DHCP options with the VPC
  dchpassoc1:
    Type: AWS::EC2::VPCDHCPOptionsAssociation
    Properties:
      VpcId:
        Ref: vpcExample
      DhcpOptionsId:
        Ref: dhcpoptionsExample

  # Allow all traffic on the private network
  privingress1:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId:
        Ref: privatenetworksecuritygroupExample
      IpProtocol: -1
      CidrIp: 10.0.0.0/16

  # Allow all traffic on the private network
  privingress2:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId:
        Ref: privatenetworksecuritygroupExample
      IpProtocol: -1
      CidrIp: 0.0.0.0/0

  # Allow all resources on the private subnet to access the public network
  publicingress1:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId:
        Ref: publicfacingnetworksecuritygroupExample
      IpProtocol: -1
      CidrIp: 10.0.0.0/16

  # Permit SSH from anywhere
  # TODO: Change this to be more restrictive for security purposes
  publicingress2:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId:
        Ref: publicfacingnetworksecuritygroupExample
      IpProtocol: tcp
      FromPort: 22
      ToPort: 22
      CidrIp: 128.117.0.0/16

  # Allow all outbound traffic
  privegress1:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      GroupId:
        Ref: privatenetworksecuritygroupExample
      IpProtocol: -1
      CidrIp: 0.0.0.0/0

  # Allow all outbound traffic
  publicegress2:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      GroupId:
        Ref: publicfacingnetworksecuritygroupExample
      IpProtocol: -1
      CidrIp: 0.0.0.0/0
