Creating AWS EC2 instance with javascript.
Tags: aws, ec2, node, javascript
Published: 2023-04-18 14:58:32

---
title: Creating AWS EC2 instance with javascript.
published: true
description: AWS EC2 SDK can be used to create, launch and delete AWS instances. This can be used for a number of automation tasks. In this blog, we will walk through the functionality that AWS SDK provides us and some of the essential components of the EC2 instance   
tags: AWS, EC2, Node, javascript
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-04-18 14:51 +0000
---
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 

```jsx
const client = new EC2Client({region: "us-east-1"})
```

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.](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html)

**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](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-shared.html).

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ywq021fas0kl9azcoitl.png)

**Environment variables**

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

```bash
AWS_ACCESS_KEY_ID=<YOUR_ACCESS_KEY>

AWS_SECRET_ACCESS_KEY=<YOUR_SECRET_ACCESS_KEY>

AWS_SESSION_TOKEN=<YOUR_SESSION_TOKEN>
```

There are other ways for adding credentials other than these. You can find and read about it [here](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html).

### 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](https://aws.amazon.com/ec2/instance-types/).

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

```jsx
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);
  }
};
```

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. 

```jsx
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)});
```

This will give the output as follows 

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

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

```jsx
// 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)});
```

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 

```json
{
  "$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": []
        }
    ]
}]
```

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](https://www.smashingmagazine.com/2021/08/http3-core-concepts-part1/) 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](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ec2/classes/createsecuritygroupcommand.html)  

### Launch Instance

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

```jsx
//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);
```

this is the bare minimum config that is needed to launch an instance. There is a huge [list](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ec2/classes/runinstancescommand.html) 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

```jsx
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);
```

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](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/amazon-linux-ami-basics.html#amazon-linux-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