Parameters:

  InstanceType:
    Type: String
    Description: The instance type. Changing this value will result in a short disruption of service.
    Default: t3.small
    AllowedValues:
      - t2.nano
      - t2.micro
      - t2.small
      - t2.medium
      - t2.large
      - t2.xlarge
      - t2.2xlarge
      - t3.nano
      - t3.micro
      - t3.small
      - t3.medium
      - t3.large
      - t3.xlarge
      - t3.2xlarge
      - c4.large
      - c4.xlarge
      - c4.2xlarge
      - c4.4xlarge
      - c4.8xlarge
      - c5.large
      - c5.xlarge
      - c5.2xlarge
      - c5.4xlarge
      - c5.9xlarge
      - c5.12xlarge
      - c5.18xlarge
      - c5.24xlarge
      - c6i.large
      - c6i.xlarge
      - c6i.2xlarge
      - c6i.4xlarge
      - c6i.8xlarge
      - c6i.12xlarge
      - c6i.16xlarge
      - c6i.24xlarge
      - c6i.32xlarge
      - m4.large
      - m4.xlarge
      - m4.2xlarge
      - m4.4xlarge
      - m4.10xlarge
      - m4.16xlarge
      - m5.large
      - m5.xlarge
      - m5.2xlarge
      - m5.4xlarge
      - m5.8xlarge
      - m5.12xlarge
      - m5.16xlarge
      - m5.24xlarge
      - m6i.large
      - m6i.xlarge
      - m6i.2xlarge
      - m6i.4xlarge
      - m6i.8xlarge
      - m6i.12xlarge
      - m6i.16xlarge
      - m6i.24xlarge
      - m6i.32xlarge
      - r4.large
      - r4.xlarge
      - r4.2xlarge
      - r4.4xlarge
      - r4.8xlarge
      - r4.16xlarge
      - r5.large
      - r5.xlarge
      - r5.2xlarge
      - r5.4xlarge
      - r5.8xlarge
      - r5.12xlarge
      - r5.16xlarge
      - r5.24xlarge
      - r6i.large
      - r6i.xlarge
      - r6i.2xlarge
      - r6i.4xlarge
      - r6i.8xlarge
      - r6i.12xlarge
      - r6i.16xlarge
      - r6i.24xlarge
      - r6i.32xlarge
    ConstraintDescription: Must be a valid t2, t3, c4, c5, c6i, m4, m5, m6i, r4, r5, or r6i instance type.

  VolumeSize:
    Type: Number
    Description: The size, in gigabytes, of the instance root volume. Changing this value will result in a short disruption of service.
    Default: 8
    MinValue: 8
    MaxValue: 1024
    ConstraintDescription: Must be a number between 8 and 1024.

  VolumeType:
    Type: String
    Description: The type of the instance root volume. Changing this value will result in a short disruption of service.
    Default: gp3
    AllowedValues:
      - gp2
      - gp3
    ConstraintDescription: Must be gp2 or gp3.

  KeyPair:
    Type: AWS::EC2::KeyPair::KeyName
    Description: The key pair for SSH. Changing this value will result in a short disruption of service.
    MinLength: 1
    ConstraintDescription: Must be an existing key pair.

  SubnetSSH:
    Type: String
    Description: The allowed subnet for SSH access to the instances. Leave blank to not allow SSH from any subnet. Connecting to the instance via the Session Manager console or CLI does not require SSH.
    Default: ""
    AllowedPattern: (^$)|(^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\/([0-9]|1[0-9]|2[0-9]|3[0-2])$)
    ConstraintDescription: Must be blank or a valid subnet in the form x.x.x.x/x.

  SubnetsSIP:
    Type: String
    Description: The comma delimited list of allowed subnets for SIP. Include the subnets of your providers and your softswitch/PBX. Do not include the ClearIP subnets.
    AllowedPattern: ^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\/([0-9]|1[0-9]|2[0-9]|3[0-2])(,([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\/([0-9]|1[0-9]|2[0-9]|3[0-2])){0,24}$
    ConstraintDescription: Must be a comma delimited list of valid subnets each in the form x.x.x.x/x. Must contain at least 1 and no more than 25 subnets.

  SIPRequestURI:
    Type: String
    Description: The SIP request URI used for sending SIP INVITEs to ClearIP. Changing this value will result in a momentary disruption of service.
    Default: sip.clearip.com
    AllowedPattern: ^([a-z][a-z0-9]{0,24}\.)?sip\.clearip\.com$
    ConstraintDescription: Must be sip.clearip.com or a valid subdomain of sip.clearip.com.

  SIPNoRoutesFoundCode:
    Type: Number
    Description: The response code the proxy will send if no routes are available. Changing this value will result in a momentary disruption of service.
    Default: 404
    AllowedValues:
      - 404
      - 503
    ConstraintDescription: Must be 404 or 503.

  TimoutTrying:
    Type: Number
    Description: The amount of time, in seconds, that the proxy will wait for a SIP 100 Trying response before trying the next route. Changing this value will result in a momentary disruption of service.
    Default: 2
    MinValue: 1
    MaxValue: 15
    ConstraintDescription: Must be a number between 1 and 15.

  TimeoutRinging:
    Type: Number
    Description: The amount of time, in seconds, that the proxy will wait for a SIP 180 Ringing or SIP 183 Session Progress response before trying the next route. Changing this value will result in a momentary disruption of service.
    Default: 10
    MinValue: 1
    MaxValue: 60
    ConstraintDescription: Must be a number between 1 and 60.

  MemoryPrivate:
    Type: Number
    Description: The amount of private memory, in megabytes, that the proxy will allocate for each process. Changing this value will result in a momentary disruption of service.
    Default: 4
    MinValue: 1
    MaxValue: 64
    ConstraintDescription: Must be a number between 1 and 64.

  MemoryShared:
    Type: Number
    Description: The amount of shared memory, in megabytes, that the proxy will allocate. Changing this value will result in a momentary disruption of service.
    Default: 256
    MinValue: 8
    MaxValue: 1024
    ConstraintDescription: Must be a number between 8 and 1024.

  WorkersUDP:
    Type: Number
    Description: The number of processes that the proxy will spawn for processing SIP signaling over UDP. Changing this value will result in a momentary disruption of service.
    Default: 16
    MinValue: 2
    MaxValue: 1024
    ConstraintDescription: Must be a number between 2 and 1024.

  WorkersTCP:
    Type: Number
    Description: The number of processes that the proxy will spawn for processing SIP signaling over TCP. Changing this value will result in a momentary disruption of service.
    Default: 16
    MinValue: 2
    MaxValue: 1024
    ConstraintDescription: Must be a number between 2 and 1024.

  LogLevel:
    Type: Number
    Description: The proxy log level. Changing this value will result in a momentary disruption of service.
    Default: 0
    MinValue: -3
    MaxValue: 4
    AllowedValues:
      - -3
      - -2
      - -1
      - 0
      - 1
      - 2
      - 3
      - 4
    ConstraintDescription: Must be a number between -3 and 4.

  LogRetention:
    Type: Number
    Description: The number of days that CloudWatch will retain the logs.
    Default: 30
    MinValue: 1
    MaxValue: 3653
    ConstraintDescription: Must be a number between 1 and 3653.

  AlarmEmail:
    Type: String
    Description: The email to send proxy CPU, memory, and disk usage alerts to. Leave blank to disable email alerts.
    Default: ""
    AllowedPattern: (^$)|(^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$)
    ConstraintDescription: Must be blank or a valid email.

  AlarmThresholdCPU:
    Type: Number
    Description: The percentage the proxy CPU usage must exceed to trigger an alarm.
    Default: 75
    MinValue: 1
    MaxValue: 99
    ConstraintDescription: Must be a number between 1 and 99.

  AlarmThresholdMemory:
    Type: Number
    Description: The percentage the proxy memory usage must exceed to trigger an alarm.
    Default: 75
    MinValue: 1
    MaxValue: 99
    ConstraintDescription: Must be a number between 1 and 99.

  AlarmThresholdDisk:
    Type: Number
    Description: The percentage the proxy disk usage must exceed to trigger an alarm.
    Default: 75
    MinValue: 1
    MaxValue: 99
    ConstraintDescription: Must be a number between 1 and 99.

  AmazonLinux2ImageId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Description: Do not change this value. If a new Amazon Linux 2 AMI ID has been published, any updates to the stack will result in a short disruption of service.
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
    AllowedValues:
      - /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
    ConstraintDescription: Must be /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2.


Conditions:

  AlarmEmailDefined: !Not [ !Equals [ "" , !Ref AlarmEmail ] ]
  SubnetSSHDefined: !Not [ !Equals [ "" , !Ref SubnetSSH ] ]
  SubnetsSIP00Defined: !Not [ !Equals [ "" , !Select [ 00 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP01Defined: !Not [ !Equals [ "" , !Select [ 01 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP02Defined: !Not [ !Equals [ "" , !Select [ 02 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP03Defined: !Not [ !Equals [ "" , !Select [ 03 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP04Defined: !Not [ !Equals [ "" , !Select [ 04 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP05Defined: !Not [ !Equals [ "" , !Select [ 05 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP06Defined: !Not [ !Equals [ "" , !Select [ 06 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP07Defined: !Not [ !Equals [ "" , !Select [ 07 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP08Defined: !Not [ !Equals [ "" , !Select [ 08 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP09Defined: !Not [ !Equals [ "" , !Select [ 09 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP10Defined: !Not [ !Equals [ "" , !Select [ 10 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP11Defined: !Not [ !Equals [ "" , !Select [ 11 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP12Defined: !Not [ !Equals [ "" , !Select [ 12 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP13Defined: !Not [ !Equals [ "" , !Select [ 13 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP14Defined: !Not [ !Equals [ "" , !Select [ 14 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP15Defined: !Not [ !Equals [ "" , !Select [ 15 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP16Defined: !Not [ !Equals [ "" , !Select [ 16 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP17Defined: !Not [ !Equals [ "" , !Select [ 17 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP18Defined: !Not [ !Equals [ "" , !Select [ 18 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP19Defined: !Not [ !Equals [ "" , !Select [ 19 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP20Defined: !Not [ !Equals [ "" , !Select [ 20 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP21Defined: !Not [ !Equals [ "" , !Select [ 21 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP22Defined: !Not [ !Equals [ "" , !Select [ 22 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP23Defined: !Not [ !Equals [ "" , !Select [ 23 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]
  SubnetsSIP24Defined: !Not [ !Equals [ "" , !Select [ 24 , !Split [ "," , !Sub [ "${SubnetsSIP},,,,,,,,,,,,,,,,,,,,,,,," , {} ] ] ] ] ]


Resources:

  RoleInstanceProfile:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${AWS::StackName}-RoleInstanceProfile
      Description: !Sub ${AWS::StackName}-RoleInstanceProfile
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service: ec2.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
        - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-RoleInstanceProfile

  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Sub ${AWS::StackName}-InstanceProfile
      Roles:
        - !Ref RoleInstanceProfile

  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub ${AWS::StackName}-LogGroup
      RetentionInDays: !Ref LogRetention
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-LogGroup

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.1.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-VPC

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-InternetGateway

  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  SubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.1.1.0/24
      AvailabilityZone: !Select [ 0 , !GetAZs ]
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-SubnetA

  SubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.1.2.0/24
      AvailabilityZone: !Select [ 1 , !GetAZs ]
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-SubnetB

  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-RouteTable

  Route:
    Type: AWS::EC2::Route
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  SubnetRouteTableAssociationA:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetA
      RouteTableId: !Ref RouteTable

  SubnetRouteTableAssociationB:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetB
      RouteTableId: !Ref RouteTable

  PrefixList:
    Type: AWS::EC2::PrefixList
    Properties:
      PrefixListName: !Sub ${AWS::StackName}-PrefixList
      AddressFamily: IPv4
      MaxEntries: 25
      Entries:
        - !If [ SubnetsSIP00Defined , Cidr: !Select [ 00 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP01Defined , Cidr: !Select [ 01 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP02Defined , Cidr: !Select [ 02 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP03Defined , Cidr: !Select [ 03 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP04Defined , Cidr: !Select [ 04 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP05Defined , Cidr: !Select [ 05 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP06Defined , Cidr: !Select [ 06 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP07Defined , Cidr: !Select [ 07 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP08Defined , Cidr: !Select [ 08 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP09Defined , Cidr: !Select [ 09 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP10Defined , Cidr: !Select [ 10 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP11Defined , Cidr: !Select [ 11 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP12Defined , Cidr: !Select [ 12 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP13Defined , Cidr: !Select [ 13 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP14Defined , Cidr: !Select [ 14 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP15Defined , Cidr: !Select [ 15 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP16Defined , Cidr: !Select [ 16 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP17Defined , Cidr: !Select [ 17 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP18Defined , Cidr: !Select [ 18 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP19Defined , Cidr: !Select [ 19 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP20Defined , Cidr: !Select [ 20 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP21Defined , Cidr: !Select [ 21 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP22Defined , Cidr: !Select [ 22 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP23Defined , Cidr: !Select [ 23 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
        - !If [ SubnetsSIP24Defined , Cidr: !Select [ 24 , !Split [ "," , !Ref SubnetsSIP ] ] , !Ref AWS::NoValue ]
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-PrefixList

  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VPC
      GroupName: !Sub ${AWS::StackName}-SecurityGroup
      GroupDescription: !Sub ${AWS::StackName}-SecurityGroup
      SecurityGroupIngress:
        - !If
          - SubnetSSHDefined
          - Description: Allow SSH
            CidrIp: !Ref SubnetSSH
            IpProtocol: tcp
            FromPort: 22
            ToPort: 22
          - !Ref AWS::NoValue
        - Description: Allow SIP over UDP
          SourcePrefixListId: !Ref PrefixList
          IpProtocol: udp
          FromPort: 5060
          ToPort: 5060
        - Description: Allow SIP over TCP
          SourcePrefixListId: !Ref PrefixList
          IpProtocol: tcp
          FromPort: 5060
          ToPort: 5060
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-SecurityGroup

  LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: !Sub ${AWS::StackName}-LaunchTemplate
      LaunchTemplateData:
        MetadataOptions:
          HttpTokens: required
          HttpPutResponseHopLimit: 1

  InstanceA:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AmazonLinux2ImageId
      InstanceType: !Ref InstanceType
      SubnetId: !Ref SubnetA
      KeyName: !Ref KeyPair
      IamInstanceProfile: !Ref InstanceProfile
      Monitoring: true
      LaunchTemplate:
          LaunchTemplateId: !Ref LaunchTemplate
          Version: !GetAtt LaunchTemplate.LatestVersionNumber
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize: !Ref VolumeSize
            VolumeType: !Ref VolumeType
            DeleteOnTermination: true
      SecurityGroupIds:
        - !Ref SecurityGroup
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-InstanceA
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -x
          yum update -y aws-cfn-bootstrap
          /opt/aws/bin/cfn-init --stack ${AWS::StackId} --resource InstanceA --region ${AWS::Region}
          /opt/aws/bin/cfn-signal -e $? "${WaitConditionHandleA}"
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            rpm:
              epel: https://files.transnexus.com/clearip/opensips-yum-releases-3.2-6.el7.noarch.rpm
            yum:
              opensips: []
              amazon-cloudwatch-agent: []
              tcpdump: []
              wireshark: []
          files:
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                interval=1
              mode: "000400"
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.InstanceA.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init --stack ${AWS::StackId} --resource InstanceA --region ${AWS::Region}
                runas=root
              mode: "000400"
              owner: root
              group: root
            /etc/cron.d/cloudwatch-systemd-metrics:
              content: !Sub |
                * * * * * root /usr/bin/cloudwatch-systemd-metrics
              mode: "000400"
              owner: root
              group: root
            /usr/bin/cloudwatch-systemd-metrics:
              content: !Sub |
                INSTANCE_ID=$(ec2-metadata --instance-id | sed 's/^instance-id: //')

                systemctl is-active --quiet opensips
                OPENSIPS_STATUS_CODE=$?

                systemctl is-active --quiet amazon-ssm-agent
                AMAZONSSMAGENT_STATUS_CODE=$?

                systemctl is-active --quiet amazon-cloudwatch-agent
                AMAZONCLOUDWATCHAGENT_STATUS_CODE=$?

                systemctl is-active --quiet cfn-hup
                CFNHUP_STATUS_CODE=$?

                METRIC_DATA=$(cat <<-END
                [
                  {
                    "MetricName": "opensips_status_code",
                    "Dimensions": [
                      {
                        "Name": "InstanceId",
                        "Value": "${!INSTANCE_ID}"
                      }
                    ],
                    "Unit": "Count",
                    "Value": ${!OPENSIPS_STATUS_CODE}
                  },
                  {
                    "MetricName": "amazon-ssm-agent_status_code",
                    "Dimensions": [
                      {
                        "Name": "InstanceId",
                        "Value": "${!INSTANCE_ID}"
                      }
                    ],
                    "Unit": "Count",
                    "Value": ${!AMAZONSSMAGENT_STATUS_CODE}
                  },
                  {
                    "MetricName": "amazon-cloudwatch-agent_status_code",
                    "Dimensions": [
                      {
                        "Name": "InstanceId",
                        "Value": "${!INSTANCE_ID}"
                      }
                    ],
                    "Unit": "Count",
                    "Value": ${!AMAZONCLOUDWATCHAGENT_STATUS_CODE}
                  },
                  {
                    "MetricName": "cfn-hup_status_code",
                    "Dimensions": [
                      {
                        "Name": "InstanceId",
                        "Value": "${!INSTANCE_ID}"
                      }
                    ],
                    "Unit": "Count",
                    "Value": ${!CFNHUP_STATUS_CODE}
                  }
                ]
                END
                )

                aws cloudwatch put-metric-data --region ${AWS::Region} --namespace systemd --metric-data "$METRIC_DATA"
              mode: "000500"
              owner: root
              group: root
            /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json:
              content: !Sub |
                {
                  "agent" : {
                    "metrics_collection_interval": 60
                  },
                  "logs": {
                    "logs_collected": {
                      "files": {
                        "collect_list": [
                          {
                            "file_path": "/var/log/messages",
                            "log_group_name": "${AWS::StackName}-LogGroup",
                            "log_stream_name": "{instance_id}"
                          }
                        ]
                      }
                    }
                  },
                  "metrics": {
                    "append_dimensions": {
                      "InstanceId": "${!aws:InstanceId}"
                    },
                    "metrics_collected": {
                      "disk": {
                        "drop_device": true,
                        "resources": [
                          "/"
                        ],
                        "measurement": [
                          "used_percent"
                        ]
                      },
                      "mem": {
                        "measurement": [
                          "used_percent"
                        ]
                      }
                    }
                  }
                }
              mode: "000640"
              owner: root
              group: root
            /etc/sysconfig/opensips:
              content: !Sub |
                S_MEMORY=${MemoryShared}
                P_MEMORY=${MemoryPrivate}
                OPTIONS=""
              mode: "000640"
              owner: root
              group: root
            /etc/opensips/opensips.cfg:
              content: !Sub |
                auto_aliases = no
                disable_503_translation = yes
                disable_core_dump = yes
                disable_dns_failover = yes
                dns_try_naptr = no
                dns_use_search_list = no
                log_level = ${LogLevel}
                log_stderror = no
                udp_workers = ${WorkersUDP}
                tcp_workers = ${WorkersTCP}
                open_files_limit = 8192
                server_signature = no
                tcp_connect_timeout = 1000
                tcp_connection_lifetime = 100
                tcp_keepalive = 1
                tcp_keepcount = 20
                tcp_keepidle = 5
                tcp_keepinterval = 5
                tcp_socket_backlog = 1024
                tcp_max_connections = 65536
                tcp_max_msg_time = 1

                socket = udp:eth0:5060
                socket = tcp:eth0:5060

                advertised_address = "${EIPA}"

                mpath = "/usr/lib64/opensips/modules"
                loadmodule "proto_udp.so"
                loadmodule "proto_tcp.so"
                loadmodule "sl.so"
                loadmodule "tm.so"
                loadmodule "signaling.so"
                loadmodule "maxfwd.so"
                loadmodule "rr.so"
                loadmodule "options.so"
                loadmodule "avpops.so"
                loadmodule "sipmsgops.so"
                loadmodule "uac_redirect.so"
                loadmodule "cfgutils.so"

                modparam("tm", "fr_timeout", ${TimoutTrying})
                modparam("tm", "fr_inv_timeout", ${TimeoutRinging})
                modparam("tm", "onreply_avp_mode", 1)
                modparam("tm", "restart_fr_on_each_reply", 1)

                route {
                  if ($ml > 8192) {
                    sl_send_reply(413, "Request Entity Too Large");
                    return;
                  }

                  if (!mf_process_maxfwd_header(10)) {
                    sl_send_reply(483, "Too Many Hops");
                    return;
                  }

                  record_route();

                  if (loose_route()) {
                    if (is_method("INVITE")) {
                      if (!t_relay(0x02)) {
                        sl_reply_error();
                      }
                      return;
                    } else if (is_method("ACK")) {
                      if (!t_relay(0x02)) {
                        sl_reply_error();
                      }
                      return;
                    }
                  } else {
                    if (is_method("BYE")) {
                      if (!t_check_trans()) {
                        sl_send_reply(400, "Bad Request");
                      }
                      return;
                    }
                  }

                  if (is_method("INVITE")) {
                    sl_send_reply(100, "Trying");
                    if (!t_check_trans()) {
                      route(2);
                    }
                  } else if (is_method("ACK")) {
                    if (t_check_trans()) {
                      t_relay();
                    }
                  } else if (is_method("OPTIONS")) {
                    sl_send_reply(200, "OK");
                  } else {
                    t_relay();
                  }
                }

                route[2] {
                  $T_fr_inv_timeout = null;
                  $avp(identity) = null;

                  t_on_reply("1");
                  t_on_failure("1");

                  remove_hf("P-Source-Device");
                  append_hf("P-Source-Device: $si\r\n");

                  $rd = "${SIPRequestURI}";
                  $rp = 5060;
                  $du = "sip:${SIPRequestURI}:5060;transport=tcp";
                  if (!t_relay()) {
                    sl_send_reply(502, "Bad Gateway");
                    exit;
                  }
                }

                route[3] {
                  if (next_branches()) {
                    t_on_branch("1");
                    t_on_reply("1");
                    t_on_failure("1");
                    t_relay();
                  } else {
                    t_reply(${SIPNoRoutesFoundCode}, "No Routes Found");
                  }
                }

                onreply_route[1] {
                  if (t_check_status("180") || t_check_status("183")) {
                    $T_fr_inv_timeout = 100;
                  }
                }

                failure_route[1] {
                  if (t_check_status("3[0-9][0-9]")) {
                    if ($(<reply>hdr(Identity)) != null) {
                      $avp(identity) = $(<reply>hdr(Identity));
                    } else {
                      $avp(identity) = $(<reply>hdr(X-Identity));
                    }
                    $avp(pai) = $(<reply>hdr(P-Asserted-Identity));
                    get_redirects();
                    set_count($branch, $var(branchcount));
                    if ($var(branchcount) > 0) {
                      serialize_branches(0);
                      route(3);
                    } else {
                      t_on_branch("1");
                      t_on_reply("1");
                      t_on_failure("1");
                      t_relay();
                    }
                  } else if (t_check_status("486") || t_check_status("487") || t_check_status("6[0-9][0-9]")) {
                    return;
                  } else if (t_check_status("408")) {
                    if (t_local_replied("last")) {
                      route(3);
                    }
                  } else {
                    route(3);
                  }
                }

                branch_route[1] {
                  if ($avp(identity) != null) {
                    remove_hf("Identity");
                    append_hf("Identity: $avp(identity)\r\n");
                  }

                  if ($avp(pai) != null) {
                    remove_hf("P-Asserted-Identity");
                    append_hf("P-Asserted-Identity: $avp(pai)\r\n");
                  }
                }
              mode: "000640"
              owner: opensips
              group: opensips
          services:
            systemd:
              opensips:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/sysconfig/opensips
                  - /etc/opensips/opensips.cfg
                packages:
                  yum:
                    - opensips
              amazon-ssm-agent:
                enabled: true
                ensureRunning: true
              amazon-cloudwatch-agent:
                enabled: true
                ensureRunning: true
                files:
                  - /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
                packages:
                  yum:
                    - amazon-cloudwatch-agent
              cfn-hup:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf

  InstanceB:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AmazonLinux2ImageId
      InstanceType: !Ref InstanceType
      SubnetId: !Ref SubnetB
      KeyName: !Ref KeyPair
      IamInstanceProfile: !Ref InstanceProfile
      Monitoring: true
      LaunchTemplate:
          LaunchTemplateId: !Ref LaunchTemplate
          Version: !GetAtt LaunchTemplate.LatestVersionNumber
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize: !Ref VolumeSize
            VolumeType: !Ref VolumeType
            DeleteOnTermination: true
      SecurityGroupIds:
        - !Ref SecurityGroup
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-InstanceB
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -x
          yum update -y aws-cfn-bootstrap
          /opt/aws/bin/cfn-init --stack ${AWS::StackId} --resource InstanceB --region ${AWS::Region}
          /opt/aws/bin/cfn-signal -e $? "${WaitConditionHandleB}"
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            rpm:
              epel: https://yum.opensips.org/3.2/releases/el/7/x86_64/opensips-yum-releases-3.2-6.el7.noarch.rpm
            yum:
              opensips: []
              amazon-cloudwatch-agent: []
              tcpdump: []
              wireshark: []
          files:
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                interval=1
              mode: "000400"
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.InstanceB.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init --stack ${AWS::StackId} --resource InstanceB --region ${AWS::Region}
                runas=root
              mode: "000400"
              owner: root
              group: root
            /etc/cron.d/cloudwatch-systemd-metrics:
              content: !Sub |
                * * * * * root /usr/bin/cloudwatch-systemd-metrics
              mode: "000400"
              owner: root
              group: root
            /usr/bin/cloudwatch-systemd-metrics:
              content: !Sub |
                INSTANCE_ID=$(ec2-metadata --instance-id | sed 's/^instance-id: //')

                systemctl is-active --quiet opensips
                OPENSIPS_STATUS_CODE=$?

                systemctl is-active --quiet amazon-ssm-agent
                AMAZONSSMAGENT_STATUS_CODE=$?

                systemctl is-active --quiet amazon-cloudwatch-agent
                AMAZONCLOUDWATCHAGENT_STATUS_CODE=$?

                systemctl is-active --quiet cfn-hup
                CFNHUP_STATUS_CODE=$?

                METRIC_DATA=$(cat <<-END
                [
                  {
                    "MetricName": "opensips_status_code",
                    "Dimensions": [
                      {
                        "Name": "InstanceId",
                        "Value": "${!INSTANCE_ID}"
                      }
                    ],
                    "Unit": "Count",
                    "Value": ${!OPENSIPS_STATUS_CODE}
                  },
                  {
                    "MetricName": "amazon-cloudwatch-agent_status_code",
                    "Dimensions": [
                      {
                        "Name": "InstanceId",
                        "Value": "${!INSTANCE_ID}"
                      }
                    ],
                    "Unit": "Count",
                    "Value": ${!AMAZONCLOUDWATCHAGENT_STATUS_CODE}
                  },
                  {
                    "MetricName": "amazon-ssm-agent_status_code",
                    "Dimensions": [
                      {
                        "Name": "InstanceId",
                        "Value": "${!INSTANCE_ID}"
                      }
                    ],
                    "Unit": "Count",
                    "Value": ${!AMAZONSSMAGENT_STATUS_CODE}
                  },
                  {
                    "MetricName": "cfn-hup_status_code",
                    "Dimensions": [
                      {
                        "Name": "InstanceId",
                        "Value": "${!INSTANCE_ID}"
                      }
                    ],
                    "Unit": "Count",
                    "Value": ${!CFNHUP_STATUS_CODE}
                  }
                ]
                END
                )

                aws cloudwatch put-metric-data --region ${AWS::Region} --namespace systemd --metric-data "$METRIC_DATA"
              mode: "000500"
              owner: root
              group: root
            /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json:
              content: !Sub |
                {
                  "agent" : {
                    "metrics_collection_interval": 60
                  },
                  "logs": {
                    "logs_collected": {
                      "files": {
                        "collect_list": [
                          {
                            "file_path": "/var/log/messages",
                            "log_group_name": "${AWS::StackName}-LogGroup",
                            "log_stream_name": "{instance_id}"
                          }
                        ]
                      }
                    }
                  },
                  "metrics": {
                    "append_dimensions": {
                      "InstanceId": "${!aws:InstanceId}"
                    },
                    "metrics_collected": {
                      "disk": {
                        "drop_device": true,
                        "resources": [
                          "/"
                        ],
                        "measurement": [
                          "used_percent"
                        ]
                      },
                      "mem": {
                        "measurement": [
                          "used_percent"
                        ]
                      }
                    }
                  }
                }
              mode: "000640"
              owner: root
              group: root
            /etc/sysconfig/opensips:
              content: !Sub |
                S_MEMORY=${MemoryShared}
                P_MEMORY=${MemoryPrivate}
                OPTIONS=""
              mode: "000640"
              owner: root
              group: root
            /etc/opensips/opensips.cfg:
              content: !Sub |
                auto_aliases = no
                disable_503_translation = yes
                disable_core_dump = yes
                disable_dns_failover = yes
                dns_try_naptr = no
                dns_use_search_list = no
                log_level = ${LogLevel}
                log_stderror = no
                udp_workers = ${WorkersUDP}
                tcp_workers = ${WorkersTCP}
                open_files_limit = 8192
                server_signature = no
                tcp_connect_timeout = 1000
                tcp_connection_lifetime = 100
                tcp_keepalive = 1
                tcp_keepcount = 20
                tcp_keepidle = 5
                tcp_keepinterval = 5
                tcp_socket_backlog = 1024
                tcp_max_connections = 65536
                tcp_max_msg_time = 1

                socket = udp:eth0:5060
                socket = tcp:eth0:5060

                advertised_address = "${EIPB}"

                mpath = "/usr/lib64/opensips/modules"
                loadmodule "proto_udp.so"
                loadmodule "proto_tcp.so"
                loadmodule "sl.so"
                loadmodule "tm.so"
                loadmodule "signaling.so"
                loadmodule "maxfwd.so"
                loadmodule "rr.so"
                loadmodule "options.so"
                loadmodule "avpops.so"
                loadmodule "sipmsgops.so"
                loadmodule "uac_redirect.so"
                loadmodule "cfgutils.so"

                modparam("tm", "fr_timeout", ${TimoutTrying})
                modparam("tm", "fr_inv_timeout", ${TimeoutRinging})
                modparam("tm", "onreply_avp_mode", 1)
                modparam("tm", "restart_fr_on_each_reply", 1)

                route {
                  if ($ml > 8192) {
                    sl_send_reply(413, "Request Entity Too Large");
                    return;
                  }

                  if (!mf_process_maxfwd_header(10)) {
                    sl_send_reply(483, "Too Many Hops");
                    return;
                  }

                  record_route();

                  if (loose_route()) {
                    if (is_method("INVITE")) {
                      if (!t_relay(0x02)) {
                        sl_reply_error();
                      }
                      return;
                    } else if (is_method("ACK")) {
                      if (!t_relay(0x02)) {
                        sl_reply_error();
                      }
                      return;
                    }
                  } else {
                    if (is_method("BYE")) {
                      if (!t_check_trans()) {
                        sl_send_reply(400, "Bad Request");
                      }
                      return;
                    }
                  }

                  if (is_method("INVITE")) {
                    sl_send_reply(100, "Trying");
                    if (!t_check_trans()) {
                      route(2);
                    }
                  } else if (is_method("ACK")) {
                    if (t_check_trans()) {
                      t_relay();
                    }
                  } else if (is_method("OPTIONS")) {
                    sl_send_reply(200, "OK");
                  } else {
                    t_relay();
                  }
                }

                route[2] {
                  $T_fr_inv_timeout = null;
                  $avp(identity) = null;

                  t_on_reply("1");
                  t_on_failure("1");

                  remove_hf("P-Source-Device");
                  append_hf("P-Source-Device: $si\r\n");

                  $rd = "${SIPRequestURI}";
                  $rp = 5060;
                  $du = "sip:${SIPRequestURI}:5060;transport=tcp";
                  if (!t_relay()) {
                    sl_send_reply(502, "Bad Gateway");
                    exit;
                  }
                }

                route[3] {
                  if (next_branches()) {
                    t_on_branch("1");
                    t_on_reply("1");
                    t_on_failure("1");
                    t_relay();
                  } else {
                    t_reply(${SIPNoRoutesFoundCode}, "No Routes Found");
                  }
                }

                onreply_route[1] {
                  if (t_check_status("180") || t_check_status("183")) {
                    $T_fr_inv_timeout = 100;
                  }
                }

                failure_route[1] {
                  if (t_check_status("3[0-9][0-9]")) {
                    if ($(<reply>hdr(Identity)) != null) {
                      $avp(identity) = $(<reply>hdr(Identity));
                    } else {
                      $avp(identity) = $(<reply>hdr(X-Identity));
                    }
                    $avp(pai) = $(<reply>hdr(P-Asserted-Identity));
                    get_redirects();
                    set_count($branch, $var(branchcount));
                    if ($var(branchcount) > 0) {
                      serialize_branches(0);
                      route(3);
                    } else {
                      t_on_branch("1");
                      t_on_reply("1");
                      t_on_failure("1");
                      t_relay();
                    }
                  } else if (t_check_status("486") || t_check_status("487") || t_check_status("6[0-9][0-9]")) {
                    return;
                  } else if (t_check_status("408")) {
                    if (t_local_replied("last")) {
                      route(3);
                    }
                  } else {
                    route(3);
                  }
                }

                branch_route[1] {
                  if ($avp(identity) != null) {
                    remove_hf("Identity");
                    append_hf("Identity: $avp(identity)\r\n");
                  }

                  if ($avp(pai) != null) {
                    remove_hf("P-Asserted-Identity");
                    append_hf("P-Asserted-Identity: $avp(pai)\r\n");
                  }
                }
              mode: "000640"
              owner: opensips
              group: opensips
          services:
            systemd:
              opensips:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/sysconfig/opensips
                  - /etc/opensips/opensips.cfg
                packages:
                  yum:
                    - opensips
              amazon-ssm-agent:
                enabled: true
                ensureRunning: true
              amazon-cloudwatch-agent:
                enabled: true
                ensureRunning: true
                files:
                  - /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
                packages:
                  yum:
                    - amazon-cloudwatch-agent
              cfn-hup:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf

  EIPA:
    Type: AWS::EC2::EIP
    DependsOn: VPCGatewayAttachment
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-EIPA

  EIPB:
    Type: AWS::EC2::EIP
    DependsOn: VPCGatewayAttachment
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-EIPB

  EIPAssociationA:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt EIPA.AllocationId
      InstanceId: !Ref InstanceA

  EIPAssociationB:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt EIPB.AllocationId
      InstanceId: !Ref InstanceB

  Topic:
    Type: AWS::SNS::Topic
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-Topic

  Subscription:
    Type: AWS::SNS::Subscription
    Condition: AlarmEmailDefined
    Properties:
      Endpoint: !Ref AlarmEmail
      Protocol: email
      TopicArn: !Ref Topic

  AlarmCpuA:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmCPUA
      Namespace: AWS/EC2
      MetricName: CPUUtilization
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceA
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: !Ref AlarmThresholdCPU
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Percent

  AlarmCpuB:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmCPUB
      Namespace: AWS/EC2
      MetricName: CPUUtilization
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceB
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: !Ref AlarmThresholdCPU
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Percent

  AlarmMemoryA:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmMemoryA
      Namespace: CWAgent
      MetricName: mem_used_percent
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceA
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: !Ref AlarmThresholdMemory
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Percent

  AlarmMemoryB:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmMemoryB
      Namespace: CWAgent
      MetricName: mem_used_percent
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceB
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: !Ref AlarmThresholdMemory
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Percent

  AlarmDiskA:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmDiskA
      Namespace: CWAgent
      MetricName: disk_used_percent
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceA
        - Name: path
          Value: /
        - Name: fstype
          Value: xfs
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: !Ref AlarmThresholdDisk
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Percent

  AlarmDiskB:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmDiskB
      Namespace: CWAgent
      MetricName: disk_used_percent
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceB
        - Name: path
          Value: /
        - Name: fstype
          Value: xfs
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: !Ref AlarmThresholdDisk
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Percent

  AlarmOpensipsA:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmOpensipsA
      Namespace: systemd
      MetricName: opensips_status_code
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceA
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: 0
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Count

  AlarmOpensipsB:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmOpensipsB
      Namespace: systemd
      MetricName: opensips_status_code
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceB
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: 0
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Count

  AlarmAmazonCloudWatchAgentA:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmAmazonCloudWatchAgentA
      Namespace: systemd
      MetricName: amazon-cloudwatch-agent_status_code
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceA
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: 0
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Count

  AlarmAmazonCloudWatchAgentB:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmAmazonCloudWatchAgentB
      Namespace: systemd
      MetricName: amazon-cloudwatch-agent_status_code
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceB
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: 0
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Count

  AlarmAmazonSSMAgentA:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmOpensipsA
      Namespace: systemd
      MetricName: amazon-ssm-agent_status_code
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceA
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: 0
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Count

  AlarmAmazonSSMAgentB:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmOpensipsB
      Namespace: systemd
      MetricName: amazon-ssm-agent_status_code
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceB
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: 0
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Count

  AlarmCfnHupA:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmCfnHupA
      Namespace: systemd
      MetricName: cfn-hup_status_code
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceA
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: 0
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Count

  AlarmCfnHupB:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub ${AWS::StackName}-AlarmCfnHupB
      Namespace: systemd
      MetricName: cfn-hup_status_code
      Dimensions:
        - Name: InstanceId
          Value: !Ref InstanceB
      OKActions:
        - !Ref Topic
      AlarmActions:
        - !Ref Topic
      InsufficientDataActions:
        - !Ref Topic
      Threshold: 0
      Statistic: Maximum
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      Period: 60
      Unit: Count

  Dashboard:
    Type: AWS::CloudWatch::Dashboard
    Properties:
      DashboardName: !Sub ${AWS::StackName}-Dashboard
      DashboardBody: !Sub |
        {
          "start": "-PT1H",
          "periodOverride": "auto",
          "widgets": [
            {
              "x": 0,
              "y": 0,
              "width": 12,
              "height": 2,
              "type": "alarm",
              "properties": {
                "title": "OpenSIPS Service Status",
                "alarms": [
                  "${AlarmOpensipsA.Arn}",
                  "${AlarmOpensipsB.Arn}"
                ]
              }
            },
            {
              "type": "alarm",
              "x": 0,
              "y": 2,
              "width": 12,
              "height": 2,
              "type": "alarm",
              "properties": {
                "title": "Amazon CloudWatch Agent Service Status",
                "alarms": [
                  "${AlarmAmazonCloudWatchAgentA.Arn}",
                  "${AlarmAmazonCloudWatchAgentB.Arn}"
                ]
              }
            },
            {
              "type": "alarm",
              "x": 0,
              "y": 4,
              "width": 12,
              "height": 2,
              "type": "alarm",
              "properties": {
                "title": "Amazon SSM Agent Service Status",
                "alarms": [
                  "${AlarmAmazonSSMAgentA.Arn}",
                  "${AlarmAmazonSSMAgentB.Arn}"
                ]
              }
            },
            {
              "type": "alarm",
              "x": 0,
              "y": 6,
              "width": 12,
              "height": 2,
              "type": "alarm",
              "properties": {
                "title": "cfn-hup Service Status",
                "alarms": [
                  "${AlarmCfnHupA.Arn}",
                  "${AlarmCfnHupB.Arn}"
                ]
              }
            },
            {
              "x": 0,
              "y": 8,
              "width": 12,
              "height": 10,
              "type": "text",
              "properties": {
                "markdown": "### Public IPs\n${EIPA} and ${EIPB}\n\n### SIP Subnets\n${SubnetsSIP}\n\n### CloudFormation Stack\n[button:primary:View CloudFormation Stack](https://${AWS::Region}.console.aws.amazon.com/cloudformation/home?region=${AWS::Region}#/stacks/stackinfo?stackId=${AWS::StackId}&filteringStatus=active&filteringText=&viewNested=true&hideStacks=false) [button:primary:Update CloudFormation Stack](https://${AWS::Region}.console.aws.amazon.com/cloudformation/home?region=${AWS::Region}#/stacks/update/template?stackId=${AWS::StackId})\n\n### Connect to Instance using Session Manager\n[button:primary:Connect to InstanceA](https://${AWS::Region}.console.aws.amazon.com/systems-manager/session-manager/${InstanceA}?region=${AWS::Region}) [button:primary:Connect to InstanceB](https://${AWS::Region}.console.aws.amazon.com/systems-manager/session-manager/${InstanceB}?region=${AWS::Region})\n\n### Connect to Instance using SSH\nFollow the [Mac/Linux SSH instructions](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html) or [Windows (PuTTY) SSH instructions](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/putty.html).\n- Key Pair: ${KeyPair}\n- Username: ec2-user\n- Subnet: ${SubnetSSH}\n\n### Contact Support\n[button:primary:Contact AWS Support](https://${AWS::Region}.console.aws.amazon.com/support/home?region=${AWS::Region}#/)\n[button:primary:Contact TransNexus Support](https://tickets.transnexus.com/open.php)"
              }
            },
            {
              "x": 12,
              "y": 0,
              "width": 12,
              "height": 6,
              "type": "metric",
              "properties": {
                "title": "CPU Usage",
                "metrics": [
                  [ "AWS/EC2", "CPUUtilization", "InstanceId", "${InstanceA}", { "label": "InstanceA" } ],
                  [ "AWS/EC2", "CPUUtilization", "InstanceId", "${InstanceB}", { "label": "InstanceB" } ]
                ],
                "region": "${AWS::Region}",
                "view": "timeSeries",
                "stat": "Maximum",
                "period": 60,
                "yAxis": {
                  "left": {
                    "min": 0,
                    "max": 100
                  }
                }
              }
            },
            {
              "x": 12,
              "y": 6,
              "width": 12,
              "height": 6,
              "type": "metric",
              "properties": {
                "title": "Memory Usage",
                "metrics": [
                  [ "CWAgent", "mem_used_percent", "InstanceId", "${InstanceA}", { "label": "InstanceA" } ],
                  [ "CWAgent", "mem_used_percent", "InstanceId", "${InstanceB}", { "label": "InstanceB" } ]
                ],
                "region": "${AWS::Region}",
                "view": "timeSeries",
                "stat": "Maximum",
                "period": 60,
                "yAxis": {
                  "left": {
                    "min": 0,
                    "max": 100
                  }
                }
              }
            },
            {
              "x": 12,
              "y": 12,
              "width": 12,
              "height": 6,
              "type": "metric",
              "properties": {
                "title": "Disk Usage",
                "metrics": [
                  [ "CWAgent", "disk_used_percent", "path", "/", "InstanceId", "${InstanceA}", "fstype", "xfs", { "label": "InstanceA" } ],
                  [ "CWAgent", "disk_used_percent", "path", "/", "InstanceId", "${InstanceB}", "fstype", "xfs", { "label": "InstanceB" } ]
                ],
                "region": "${AWS::Region}",
                "view": "timeSeries",
                "stat": "Maximum",
                "period": 60,
                "yAxis": {
                  "left": {
                    "min": 0,
                    "max": 100
                  }
                }
              }
            },
            {
              "x": 0,
              "y": 18,
              "width": 24,
              "height": 14,
              "type": "log",
              "properties": {
                "title": "Logs",
                "query": "SOURCE '${LogGroup}' | fields @timestamp, @message\n| sort @timestamp desc",
                "region": "${AWS::Region}",
                "view": "table"
              }
            }
          ]
        }

  WaitConditionHandleA:
    Type: AWS::CloudFormation::WaitConditionHandle

  WaitConditionA:
    Type: AWS::CloudFormation::WaitCondition
    DependsOn: InstanceA
    Properties:
      Handle: !Ref WaitConditionHandleA
      Timeout: 300

  WaitConditionHandleB:
    Type: AWS::CloudFormation::WaitConditionHandle

  WaitConditionB:
    Type: AWS::CloudFormation::WaitCondition
    DependsOn: InstanceB
    Properties:
      Handle: !Ref WaitConditionHandleB
      Timeout: 300
