EC2 Instance Example in AWS CDK - Complete Guide

avatar

Borislav Hadzhiev

Last updated: Apr 15, 2022

banner

Check out my new book

Table of Contents #

  1. Creating an EC2 Instance in AWS CDK
  2. Adding User Data to an EC2 Instance in AWS CDK
  3. Deploying the EC2 Instance Provisioned with AWS CDK

Creating an EC2 Instance in AWS CDK #

In order to create an EC2 instance in AWS CDK, we have to instantiate and configure the Instance class.

Let's start by creating:

  • a VPC, in which we will launch our EC2 instance
  • a security group for the instance
  • a role for the instance
The code for this article is available on GitHub
lib/cdk-starter-stack.ts
import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as cdk from 'aws-cdk-lib'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // 👇 create VPC in which we'll launch the Instance const vpc = new ec2.Vpc(this, 'my-cdk-vpc', { cidr: '10.0.0.0/16', natGateways: 0, subnetConfiguration: [ {name: 'public', cidrMask: 24, subnetType: ec2.SubnetType.PUBLIC}, ], }); // 👇 create Security Group for the Instance const webserverSG = new ec2.SecurityGroup(this, 'webserver-sg', { vpc, allowAllOutbound: true, }); webserverSG.addIngressRule( ec2.Peer.anyIpv4(), ec2.Port.tcp(22), 'allow SSH access from anywhere', ); webserverSG.addIngressRule( ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'allow HTTP traffic from anywhere', ); webserverSG.addIngressRule( ec2.Peer.anyIpv4(), ec2.Port.tcp(443), 'allow HTTPS traffic from anywhere', ); // 👇 create a Role for the EC2 Instance const webserverRole = new iam.Role(this, 'webserver-role', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonS3ReadOnlyAccess'), ], }); } }
If you still use CDK version 1, switch to the cdk-v1 branch in the GitHub repository.

Let's go over what we did in the code snippet.

  1. We created a VPC with a PUBLIC subnet group. Our EC2 instance will be a web server, so we want it to be accessible from the world

  2. We created a security group for our EC2 instance. The security group allows all outbound access.

    For inbound access we have allowed:

    • SSH access from anywhere
    • HTTP traffic on port 80 from anywhere
    • HTTPS traffic on port 443 from anywhere
  3. We created an IAM role our EC2 instance will assume. As an example, we have attached a managed policy that grants S3 read access to the role.

The code for this article is available on GitHub

Let's add the code that creates the EC2 Instance:

lib/cdk-starter-stack.ts
import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as cdk from 'aws-cdk-lib'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // ... rest // 👇 create the EC2 Instance const ec2Instance = new ec2.Instance(this, 'ec2-instance', { vpc, vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC, }, role: webserverRole, securityGroup: webserverSG, instanceType: ec2.InstanceType.of( ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO, ), machineImage: new ec2.AmazonLinuxImage({ generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2, }), keyName: 'ec2-key-pair', }); } }

We created an EC2 instance, passing it the following props:

NameDescription
vpcthe VPC, the instance will be launched in
vpcSubnetsin which subnet of the VPC the instance will be launched
rolethe IAM role, the EC2 instance will assume
securityGroupa security group to assign to the EC2 instance
instanceTypethe class and size configuration for the EC2 instance, in our case t2.micro
machineImagethe Amazon Machine Image of the EC2 instance, in our case Amazon Linux 2
keyNamethe name of the SSH key pair we are going to use to SSH into the instance
In the code example we used the name ec2-key-pair for the name of the key. If a key with that name does not exist in your default AWS region, you will get an error when trying to deploy the EC2 instance.

Let's create a key pair in your default AWS region with the name of ec2-key-pair. Alternatively, you could replace the value of the keyName prop to one that already exists in your account.

To create a key pair, open the EC2 Management console and click on Key Pairs > Create key pair.

Depending on your operating system you can choose between pem (Mac, Linux) and ppk (Windows). I'm on Linux, so I've selected pem:

ec2 instance key pair creation

After the key pair has been created, navigate to the directory it was downloaded in and change its permissions:

shell
chmod 400 ec2-key-pair.pem

Next, we are going to add a User Data script to the ec2 instance.

Adding User Data to an EC2 Instance in AWS CDK #

EC2 User data allows us to add commands to the startup script of an instance.

To add user data, we are going to use the addUserData method on our EC2 instance.

The code for this article is available on GitHub
lib/cdk-starter-stack.ts
import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as cdk from 'aws-cdk-lib'; import {readFileSync} from 'fs'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // ... rest // 👇 load contents of script const userDataScript = readFileSync('./lib/user-data.sh', 'utf8'); // 👇 add the User Data script to the Instance ec2Instance.addUserData(userDataScript); } }

We used the readFileSync method from the fs module to load the contents of a script. We then passed the result to the addUserData method of the EC2 instance.

Let's add the code for the user data script at lib/user-data.sh:

lib/user-data.sh
#!/bin/bash yum update -y sudo su amazon-linux-extras install -y nginx1 systemctl start nginx systemctl enable nginx chmod 2775 /usr/share/nginx/html find /usr/share/nginx/html -type d -exec chmod 2775 {} \; find /usr/share/nginx/html -type f -exec chmod 0664 {} \; echo "<h1>It worked</h1>" > /usr/share/nginx/html/index.html

The startup script installs and starts nginx and writes a simple "It worked" heading tag to the root of our web server.

Deploying the EC2 Instance Provisioned with AWS CDK #

Let's run the deployment command and test if everything works as expected:

shell
npx aws-cdk deploy

It takes about 5 minutes for the EC2 instance to be launched in my default AWS region.

The inbound rules for the security group of the EC2 instance allow access on ports 22, 80, 443:

ec2 security inbound rules

Let's try go access the nginx server we installed and started. Copy the Public IPv4 address of your EC2 instance:

ec2 instance public ipv4

Now paste the public IPv4 address in your browser and you should see the response from our nginx web server:

ec2 instance response

Now let's SSH into the instance and change the message our nginx web server responds with.

Open your terminal in the directory where you placed your ec2-key-pair.pem file and issue the following command:

shell
ssh -i "ec2-key-pair.pem" ec2-user@YOUR_PUBLIC_IPv4
Make sure to replace the YOUR_PUBLIC_IPv4 placeholder with your EC2 instance's actual Public IPv4 address.

Once you're in the instance, change the response of the web server with the following commands:

shell
sudo su echo "<h1>Hello World</h1>" > /usr/share/nginx/html/index.html

If you now paste the public IPv4 address of the EC2 instance in your browser, you will se the updated response:

ec2 instance response updated

Clean up #

To delete the resources we have provisioned, issue the destroy command:

shell
npx aws-cdk destroy

Further Reading #

Use the search field on my Home Page to filter through my more than 3,000 articles.