# Deploy HA Gateways with CloudFormation

The following guide shows an example of how to quickly create a pair of StrongDM gateways using AWS's CloudFormation. The only requirement is a StrongDM [admin token](https://docs.strongdm.com/admin/principals/admin-tokens) with the ability to list and create gateways. When creating the admin token, check the Relays - List and Relays - Create permissions.

### Procedure

1. Navigate to your AWS console.
2. Search for and open the CloudFormation service.
3. Click **Create stack**.
4. Choose **Upload a template file**.
5. Upload the below YAML file.
6. Follow on-screen instructions.

### Parameters

When launched, this stack will prompt you for the following parameters:

1. **PublicSubnet1**: Designates the subnet in which to launch the EC2 instance. **This subnet needs to be public**.
2. **PublicSubnet2**: Designates the subnet in which to launch a second EC2 instance for high availability. **This subnet needs to be public**.
3. **VPC**: Select the VPC that the subnet above belongs to. This VPC needs [DNS hostnames](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html) enabled for the gateway to properly register.
4. **SDMListenPort**: This port number will be used for clients to connect to the this gateway.
5. **SDMAdminToken**: Input a StrongDM admin token that has the *Relays / Create* permission.

### Resources

This template will create the following resources

1. EC2 Instance Gateway One
   * Instance type `t3.medium`
   * Operating system `Amazon Linux 2`
2. EC2 Instance Gateway Two
   * Instance type `t3.medium`
   * Operating system `Amazon Linux 2`
3. Security group
   * This security group allows connections from StrongDM clients into your VPC
   * The `SDMlistenPort` specified during creation time will be open from anywhere

### Outputs

This template exports the EC2 security group so that it may be used as an input rule for your databases and servers in other templates.

### CloudFormation Template

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: StrongDM self-registering gateway
Parameters:
  # Network information
  PublicSubnet1:
    Description: Subnet to use as an access point into your AWS network. Must contain a route open to the internet.
    Type: AWS::EC2::Subnet::Id
  PublicSubnet2:
    Description: Subnet to use as an access point into your AWS network. Must contain a route open to the internet.
    Type: AWS::EC2::Subnet::Id
  VPC:
    Description: The VPC that contains the public subnet listed above
    Type: AWS::EC2::VPC::Id
  # StrongDM variables
  SDMListenPort:
    Type: Number
    Default: 5000
    MinValue: 1024
    MaxValue: 65535
    Description: The TCP port that will be exposed to the internet
  SDMAdminToken:
    AllowedPattern: (.+)
    Type: String
    Description: Paste your StrongDM admin token to create the gateway
  # Get latest Amazon Linux AMI
  LatestAmiId:
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
# Organization structure for parameters
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      -
        Label:
          default: "AWS Configuration"
        Parameters:
          - PublicSubnet1
          - PublicSubnet2
          - VPC
      -
        Label:
          default: "StrongDM Configuration"
        Parameters:
          - SDMAdminToken
          - SDMListenPort
      -
        Label:
          default: "This will grab the latest Amazon Linux 2 AMI ID"
        Parameters:
          - LatestAmiId
# Resources to be created
Resources:
  SDMGWONE:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t3.medium
      Tags: 
        - Key: "Name"
          Value: "StrongDM Gateway One"
      NetworkInterfaces:
        - DeviceIndex: '0'
          SubnetId: !Ref 'PublicSubnet1'
          AssociatePublicIpAddress: 'true'
          DeleteOnTermination: 'true'
          GroupSet: [!GetAtt [ EC2SecurityGroup, GroupId ]]
      ImageId: !Ref LatestAmiId
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash -xe
            # set environment variables
            mkdir -p /home/ec2-user/.sdm
            touch /home/ec2-user/.sdm/sdm.log
            export TARGET_USER=ec2-user
            export SDM_LISTEN_PORT=${SDMListenPort}
            export SDM_GATEWAY_NAME=AWS-CloudFormation-One-$(date +%s)
            export SDM_HOSTNAME="$(curl http://169.254.169.254/latest/meta-data/public-hostname)"
            export SDM_HOME="/home/ec2-user/.sdm"
            # downloads sdm binary
            yum update -y && yum install -y unzip curl
            curl -J -O -L https://app.strongdm.com/releases/cli/linux && unzip sdmcli* && rm sdmcli*
            # Generate a gateway token
            export SDM_RELAY_TOKEN="$(./sdm --admin-token=${SDMAdminToken} relay create-gateway --name $SDM_GATEWAY_NAME $SDM_HOSTNAME:$SDM_LISTEN_PORT 0.0.0.0:$SDM_LISTEN_PORT)"
            chown -R ec2-user:ec2-user /home/ec2-user/.sdm
            # Install SDM
            ./sdm install --node --token=$SDM_RELAY_TOKEN --user $TARGET_USER
  SDMGWTWO:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t3.medium
      Tags: 
        - Key: "Name"
          Value: "StrongDM Gateway Two"
      NetworkInterfaces:
        - DeviceIndex: '0'
          SubnetId: !Ref 'PublicSubnet2'
          AssociatePublicIpAddress: 'true'
          DeleteOnTermination: 'true'
          GroupSet: [!GetAtt [ EC2SecurityGroup, GroupId ]]
      ImageId: !Ref LatestAmiId
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash -xe
            # set environment variables
            mkdir -p /home/ec2-user/.sdm
            touch /home/ec2-user/.sdm/sdm.log
            export TARGET_USER=ec2-user
            export SDM_LISTEN_PORT=${SDMListenPort}
            export SDM_GATEWAY_NAME=AWS-CloudFormation-Two-$(date +%s)
            export SDM_HOSTNAME="$(curl http://169.254.169.254/latest/meta-data/public-hostname)"
            export SDM_HOME="/home/ec2-user/.sdm"
            # downloads sdm binary
            yum update -y && yum install -y unzip curl
            curl -J -O -L https://app.strongdm.com/releases/cli/linux && unzip sdmcli* && rm sdmcli*
            # Generate a gateway token
            export SDM_RELAY_TOKEN="$(./sdm --admin-token=${SDMAdminToken} relay create-gateway --name $SDM_GATEWAY_NAME $SDM_HOSTNAME:$SDM_LISTEN_PORT 0.0.0.0:$SDM_LISTEN_PORT)"
            chown -R ec2-user:ec2-user /home/ec2-user/.sdm
            # Install SDM
            ./sdm install --node --token=$SDM_RELAY_TOKEN --user $TARGET_USER
  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Expose StrongDM listening port"
      GroupName: !Sub "${AWS::StackName}"
      VpcId: !Ref 'VPC'
      Tags: 
        - Key: "Name"
          Value: !Sub "${AWS::StackName}"
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: !Ref 'SDMListenPort'
        ToPort: !Ref 'SDMListenPort'
        CidrIp: 0.0.0.0/0
Outputs:
  SDMGatewaySecurityGroupID:
    Description: Security Group ID for StrongDM gateway
    Value: !GetAtt [ EC2SecurityGroup, GroupId ]
    Export:
      Name: !Join [ ':', [ !Ref 'AWS::StackName', 'SecurityGroupID' ] ]
```

{% hint style="info" %}
If your organization uses a control plane located in a region other than the default, add a `--region yourdomain` flag to the install commands, such as:

```sh
./sdm install --region app.uk.strongdm.com --node --token=$SDM_RELAY_TOKEN --user $TARGET_USER
```

{% endhint %}
