Source: infrastructure/container-server/container-server.js

const { CfnOutput } = require('aws-cdk-lib')
const { Construct } = require('constructs')
const {
  Instance,
  InstanceType,
  InstanceClass,
  InstanceSize,
  GenericLinuxImage,
  Vpc,
  SecurityGroup,
  Peer,
  Port,
  UserData
} = require('aws-cdk-lib/aws-ec2')
const { HostedZone, ARecord, RecordTarget } = require('aws-cdk-lib/aws-route53')
const { Role, ServicePrincipal, Policy, PolicyStatement, Effect } = require('aws-cdk-lib/aws-iam')
const {
  getInstallNodeJs,
  getInstallNpm,
  getInstallDocker
} = require('./user-data-commands')
const { CaddyService } = require('./caddy.service')
const { DeploymentToolsService } = require('./deployment-tools.service')
const { AwsProfile } = require('./aws-profile')
const { SupportedRegions } = require('../../config')

/**
 * Platform template definition
 * @namespace ContainerServer
 * @typedef {Object} ContainerServerProps
 * @property {PlatformConfig} platformConfig - platform config
 * @property {Bucket} platformConfigBucket - aws bucket
 */
const InstanceImages = {
  UBUNTU: 'ami-092cce4a19b438926'
}

/**
  * @class
*/
class ContainerServer extends Construct {

  /**
   * EC2 Instance that docker containers are served on
   *
   * @param {object} scope - scope
   * @param {string} id - id
   * @param {ContainerServerProps} props - ContainerServerProps
   */
  constructor(scope, id, props) {
    super(scope, id)

    const hostedZone = HostedZone.fromLookup(this, 'HostedZone', { domainName: props.platformConfig.domain })

    const vpc = Vpc.fromLookup(this, 'VPC', {
      isDefault: true
    })

    const securityGroup = new SecurityGroup(this, 'SecurityGroup', {
      vpc,
      allowAllOutbound: true
    })

    securityGroup.addIngressRule(
      Peer.anyIpv4(),
      Port.tcp(22),
      'allow SSH access from anywhere'
    )

    securityGroup.addIngressRule(
      Peer.anyIpv4(),
      Port.tcp(80),
      'allow HTTP traffic from anywhere'
    )

    securityGroup.addIngressRule(
      Peer.anyIpv4(),
      Port.tcp(443),
      'allow HTTPS traffic from anywhere'
    )

    const role = new Role(this, 'role', {
      assumedBy: new ServicePrincipal('ec2.amazonaws.com')
    })

    role.attachInlinePolicy(new Policy(this, 'policy', {
      statements: [
        new PolicyStatement({
          effect: Effect.ALLOW,
          actions: [
            'route53:ListResourceRecordSets',
            'route53:GetChange',
            'route53:ChangeResourceRecordSets'
          ],
          resources: [
            `arn:aws:route53:::hostedzone/${hostedZone.hostedZoneId}`,
            'arn:aws:route53:::change/*'
          ]
        }),
        new PolicyStatement({
          effect: Effect.ALLOW,
          actions: [
            'route53:ListHostedZonesByName',
            'route53:ListHostedZones'
          ],
          resources: ['*']
        })
      ]
    }))

    const userData = UserData.forLinux()

    userData.addCommands(
      'sudo apt update -y',
      ...getInstallNodeJs(),
      ...getInstallNpm(),
      ...getInstallDocker(),
      'sudo apt-get install awscli --yes',
      ...AwsProfile.writeFile(role.roleArn, SupportedRegions.North),
      ...CaddyService.install(),
      ...DeploymentToolsService.install()
    )

    userData.addS3DownloadCommand({
      bucket: props.platformConfigBucket,
      bucketKey: 'platform-config.json',
      localFile: '/home/ubuntu/platform-config.json'
    })

    userData.addCommands(
      'sudo systemctl daemon-reload',
      ...CaddyService.enable(),
      ...DeploymentToolsService.enable(),
      ...CaddyService.start(),
      ...DeploymentToolsService.start()
    )

    const ec2Instance = new Instance(this, 'Ec2Instance', {
      vpc,
      securityGroup,
      userData,
      role,
      instanceType: InstanceType.of(
        InstanceClass.T3,
        InstanceSize.MICRO
      ),
      machineImage: new GenericLinuxImage({
        [SupportedRegions.North]: InstanceImages.UBUNTU
      }),
      keyName: props.platformConfig.awsKeyPairName
    })

    props.platformConfigBucket.grantRead(ec2Instance, '*')

    // const hostedZone = !!props.createNewHostedZone
    //   ? new PublicHostedZone(this, 'HostedZone', { zoneName: props.domain })
    //   : HostedZone.fromLookup(this, 'HostedZone', { domainName: props.domain });

    // eslint-disable-next-line no-new
    new ARecord(this, 'WildcardRecord', {
      zone: hostedZone,
      recordName: `*.${props.platformConfig.activeEnvironment}.${props.platformConfig.domain}`,
      target: RecordTarget.fromIpAddresses(ec2Instance.instancePublicIp)
    })

    // eslint-disable-next-line no-new
    new CfnOutput(this, 'Intance Ip', {
      value: ec2Instance.instancePublicIp,
      description: 'Public ip of the ec2 instance',
      exportName: 'instanceIp'
    })

  }
}

module.exports = {
  ContainerServer
}