Skip to content

Create EC2 Instance as publicly available and restricted

Updated: at 05:55 PM

Placing EC2 instances in both private and public subnets within an Amazon VPC (Virtual Private Cloud) is a common practice for building secure, scalable, and highly available web architectures. Instances in private subnets are not directly accessible from the internet, reducing their exposure to external threats. This is ideal for databases, application servers, and other backend systems that should not be directly exposed to the web.

Whether you want to place an instance in private or public subnet, you need to set following code-clock in the auto-scaling group or instance configuration.

vpcSubnets: {
  subnetType: SubnetType.PRIVATE_ISOLATED | SubnetType.PUBLIC;
}

Here is the full code example

import { Construct } from "constructs";
import { CDKContext } from "../../types";
import { Tags } from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import { SubnetType } from "aws-cdk-lib/aws-ec2";
import * as asg from "aws-cdk-lib/aws-autoscaling";
import * as iam from "aws-cdk-lib/aws-iam";

export class Ec2Construct extends Construct {
  public readonly vpc: ec2.IVpc;

  constructor(
    scope: Construct,
    private ctx: CDKContext,
    private vpc: ec2.IVpc,
    private ec2Role: iam.Role
  ) {
    super(scope, "ec2-" + ctx.serviceName);
    this.createVpc();
    const launchTemplate = this.createLaunchTemplate();
    const autoScalingGroup = this.addAutoScalingGroup(launchTemplate);
  }

  private createVpc() {
    this.vpc = new ec2.Vpc(this, ctx.serviceName + "-vpc", {
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: "PublicSubnet",
          subnetType: ec2.SubnetType.PUBLIC,
        },
        {
          cidrMask: 24,
          name: "IsolatedSubnet",
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
        },
      ],
    });
  }
  private createLaunchTemplate() {
    return new ec2.LaunchTemplate(this, "LaunchTemplate", {
      instanceType: new ec2.InstanceType("t3.micro"),
      machineImage: new ec2.GenericLinuxImage({
        "eu-west-1": "ami-ksdr9ssdfd2",
      }),
      instanceProfile: new iam.InstanceProfile(this, "ec2InstanceProfile", {
        role: this.ec2Role,
      }),
      securityGroup: createSecurityGroup(),
    });
  }

  private addAutoScalingGroup(launchTemplate: ec2.LaunchTemplate) {
    return new asg.AutoScalingGroup(this, "AutoScalingGroup", {
      vpc: this.vpc,
      launchTemplate,
      allowAllOutbound: true,
      minCapacity: 1,
      maxCapacity: 2, // add scaling policy based on metrics
      vpcSubnets: {
        subnetType: SubnetType.PRIVATE_ISOLATED,
      },
    });
  }

  private createSecurityGroup() {
    const ec2Ingress = new ec2.SecurityGroup(
      this,
      this.ctx.serviceName + "-ingress-sg",
      {
        vpc: this.vpc,
        allowAllOutbound: true,
      }
    );
    ec2Ingress.addIngressRule(
      ec2.Peer.anyIpv4(),
      ec2.Port.allTraffic(),
      "allow all traffic for now from anywhere"
    );
    return ec2Instance;
  }
}