DEV Community

Avinash
Avinash

Posted on • Updated on

Creating AWS EC2 instance with javascript.

AWS EC2 is a computing service that can be used for various purposes. EC2 is one of the core services that AWS provides. This blog will walk us through creating an EC2 instance and its essential components.
AWS also provides SDK for different languages. But we will be using AWS SDK that is provided for javascript.

EC2 Client

AWS EC2 is one of the services that AWS provides. This blog assumes that you already have an AWS account(or an IAM account with EC2 policy and permission). AWS EC2 provides compute service on the AWS cloud.

To initialize a EC2 instance client we can do that using

const client = new EC2Client({region: "us-east-1"})
Enter fullscreen mode Exit fullscreen mode

This uses the EC2 instance client with region us-east-1 . One can choose other regions depending on where they want to host their servers.

Credentials

Setting up credentials is the first thing to do. One should have proper permissions in order to have access to service. There are different ways to set credentials.

  1. Load from AWS IAM roles from AWS EC2
  2. from shared credentials file ~/.aws/credentials
  3. Using environment variables

AWS IAM roles from AWS EC2

If we want to run our node.js application on an existing EC2 instance then the Node.js application can automatically pick up the role from the existing EC2 instances roles provided. One can use IAM roles to make API requests from EC2 instance.

Shared credentials

We can have a single file that has the AWS credentials init. By default when the AWS SDK launches it starts looking for the default credential file at ~/.aws/credentials in Linux based system, and C:\Users\USER_NAME\.aws\credentials . You can learn more from here.

You can also use AWS CLI’s aws configure commands to update the credentials in ~/.aws/credentials file. When you run the aws configure flowing wizard gets poped up.

It will ask for following things:

  • AWS Access key
  • AWS secret acess key
  • and session token(optional)

AWS CLI credentials

Environment variables

SDK will automatically detect the environment variables. In my opinion, this is the easiest way to provide credentials to the AWS SDK.

AWS_ACCESS_KEY_ID=<YOUR_ACCESS_KEY>

AWS_SECRET_ACCESS_KEY=<YOUR_SECRET_ACCESS_KEY>

AWS_SESSION_TOKEN=<YOUR_SESSION_TOKEN>
Enter fullscreen mode Exit fullscreen mode

There are other ways for adding credentials other than these. You can find and read about it here.

AMI

AMI stands for Amazon machine image and they are the basic entity for creating the EC2 instance. You can think of it as a docker image for running the docker container. It consists of all the information(for example operating system, applications etc) that is required to initialise and start the EC2 instance. For more check out the official documentation.

Now how to get the AMI image IDs using the AWS SDK.

const { EC2Client, DescribeImagesCommand } = require("@aws-sdk/client-ec2");

 // note: region is "us-east-1"
const client = new EC2Client({ region: "us-east-1" });

const command = new DescribeImagesCommand(params);

const start = async () => {
  try {
    const data = await client.send(command);
    console.log({ data: JSON.stringify(data.Images) });

  } catch (error) {

    console.error(error);
  }
};
Enter fullscreen mode Exit fullscreen mode

In the above code I want to host my EC2 instance in us-east-1 region so I wanted to know the images that are provided in that region.

Note: If you want to get launch a EC2 instance the AMI image should be present in that region.

DescribeImagesCommand this cmd comes in handy when we need to verify whether the image is present in the same location or the image is not deprecated.

AWS Virtual private cloud

AWS provides us with a private network that allows us to deploy the instance or any other compute services in an isolated network that is secure from the outside. Each VPC can consist of subnets that hold your resources. The best thing is as soon as we create an account a default VPC is automatically created for us. We can use @aws-sdk/client-ec2 ’s DescribeAccountAttributesCommand to get the information about the default VPC.

const client = new EC2Client({ region: "us-east-1" });
const input = {
  AttributeNames: ["default-vpc"],
};
const vpc = new DescribeAccountAttributesCommand(input);
const describe = await client.send(d);
console.log({ data: JSON.stringify(describe)});
Enter fullscreen mode Exit fullscreen mode

This will give the output as follows

{
  "$metadata": {
    "httpStatusCode": 200,
    "requestId": "b8d6169c-b1b2-46d3-bd3a-516b48e7e2d6",
    "attempts": 1,
    "totalRetryDelay": 0
  },
  "AccountAttributes": [
    {
      "AttributeName": "default-vpc",
      "AttributeValues": [
        {
          "AttributeValue": "vpc-00df8d4abfe081231"
        }
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Since we defined the attributes default-vpc. We got the AttributeValue as the VPC id of default values. AWS VPC is one of the core services that are essential to learning, as every resource is launched on an AWS VPC by default. Another important thing to make note of it is the resources and VPC’s region and availability zone should be the same else

Security Groups

Security Groups act as a firewall for an ec2 instance, it controls the traffic going in and out of the instance. One thing to remember is when we don’t specify the security group at the time of the launch of the EC2 instance the default security group is assigned to it.

The default security group comes with its own baggage like all the inbound traffic is restricted by default. So we need to explicitly add rules to allow traffic to our EC2 instance else no one will be able to access our instance from nowhere (eg: SSH, HTTP or HTTPS)

To make our instance access to the outside world we need to open up the ports for TCP. But first, we have to verify if the port is already open but by any other existing security group

// find if we have a HTTP port already opened up
const client = new EC2Client({ region: "us-east-1" });

const sg = new DescribeSecurityGroupsCommand({
  Filters: [
    {
      Name: "egress.ip-permission.to-port",
      Values: ["80"],
    },
  ],
});

const describe = await client.send(securityG);
    console.log({ data: JSON.stringify(describe)});
Enter fullscreen mode Exit fullscreen mode

The above code will return an object if we already have SG(security groups) configured with outbound ports opened for port 80 (TCP connection).

Output

{
  "$metadata": {
    "httpStatusCode": 200,
    "requestId": "28e8921a-8048-4e42-b2f2-51fb0e973bbb",
    "attempts": 1,
    "totalRetryDelay": 0
  },
  "SecurityGroups": [
    {
      "Description": "Description",
      "GroupName": "Name of SG",
      "IpPermissions": [
        {
          "FromPort": 80,
          "IpProtocol": "tcp",
          "IpRanges": [{ "CidrIp": "0.0.0.0/0" }],
          "Ipv6Ranges": [{ "CidrIpv6": "::/0" }],
          "PrefixListIds": [],
          "ToPort": 80,
          "UserIdGroupPairs": []
        }
    ]
}]
Enter fullscreen mode Exit fullscreen mode

This is the object that gets returned from the above function. Let's look into IpPermission object. IpPermission holds the rules that determine the traffic coming in and out of the instance.

  • FromPort : Range of the ports that are allowed
  • IpProtocol: it will be TCP or UDP network layer protocol (i.e in case of HTTP/1 or HTTP/2 its TCP and in the case of HTTP/3 it will be UDP as HTTP/3 uses QUIC protocol that is built on top of UDP.
  • IpRanges: The range of IP addresses are allowed. In the case of "0.0.0.0/0" all the Ip addresses are allowed
  • IpV6Ranges: Ip v6 addresses that are allowed.
  • ToPort: The end range of the port
  • UserIdGroupPairs: The security group ids for which the ids belong to.

If the ports are not the opend we can create new security group

Launch Instance

Once we have opend the ports we are ready for the launching the instances. The instance can be launched using RunInstancesCommand

//launching an instnace

const client = new EC2Client(config);

const inputConfig = {
            MaxCount: 1,
            MinCount: 1,
            ImageId: 'ami-02f3f602d23f1659d',
            InstanceType: 't2.micro',
            SecurityGroupIds: ['SG_ID'],
            SecurityGroups: ['securityGroupName'],
            KeyName: 'name',
            TagSpecifications: [
              {
                ResourceType: "instance",
                Tags: [
                  {
                    Key: "Name",
                    Value: 'instanceName',
                  },
                ],
              },
            ],
          };

const instance = new RunInstancesCommand(inputConfig);
const response = await client.send(command);
Enter fullscreen mode Exit fullscreen mode

this is the bare minimum config that is needed to launch an instance. There is a huge list of configs that is not provided by the default section. Most of the configs (VPC, Security Groups) are created by default when the account is created and the same default configs will be used if the config is not provided.

Another interesting attribute is UserDate which is not a required option but is very useful when we want to set up some additional things on our instance at the time of starting the instance. For example, let's say you want to install the docker on the instance right after the instance starts you can do this by

const userData = [
        `#!/bin/bash
        yum install -y docker`,
        "usermod -aG docker ec2-user",
        "service docker start",
      ].join(" && ");

const bufferString = Buffer.from(userData).toString("base64");
const instance = new RunInstancesCommand({
...inputConfig,
userData
);
const response = await client.send(command);
Enter fullscreen mode Exit fullscreen mode

You can also pass user data as text instead of base 64 but I was facing some issues while doing so. Another way is to use cloud init directives in cloud-init. You debug your instance using looking connecting to instance through SSH and checking the logs /var/log/cloud-init-output.log . I had some issue while running bash scripts because aws was thinking bash scripts as cloud-init scripts and i had no idea why it was doing so.

Conclusion

Thank you guys for coming this far. AWS EC2 is one of the core services of AWS. We have covered various concepts of EC2 like networking and AMI and planing to cover the rest of the concepts in future. I have not covered storage in this blog, as i wanted to keep it short. Hope you have liked it.

Please let me know if i have missed anything or I’m wrong about anything ill try to correct it

Top comments (2)

Collapse
 
avas77 profile image
Avas77 • Edited

Nice. Really helpful article👍

Collapse
 
avinash8847 profile image
Avinash

Thanks