A Serverless Chatbot based on AWS Lex and Lambda to control your EC2 instances
Tags: serverless, cloud, coding, aws
Published: 2022-02-27 09:28:21

For a long time now ,I wanted to work on a chatbot , just to get my hands dirty with that kind of stuff . I had heard about AWS Lex and how it's pretty easy to setup a chatbot with Lex , so I wanted to give it a try .

**<u>Disclaimer </u>**: The ideas for the work that I am demonstrating here are derived from the following 2 blog posts

https://devpost.com/software/ec2-bot
https://chatbotslife.com/aws-lex-insights-after-building-my-shakira-chatbot-663f71967902

## <u>So , what am I trying to achieve here ?</u>

So , I went through the 2 blog posts mentioned above , and came upon the idea of creating a bot that would help me control my infrastructure , like say , allow me to do the following things:

- list my EC2 instances
- start/stop/terminate an EC2 server
- launch a new EC2 server based on some properties

Also I wanted to integrate my Lex bot with a slack channel so that I can use slack to test , rather than use the AWS Lex Test Console which is pretty basic .

The bot would finally look like this :

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/683caktqhnltsj1agmvi.png)

You would be able to use conversational message to control your EC2 infrastructure (basic functions) which in a way is pretty cool.

You can find the entire video of the bot functioning here-

https://vimeo.com/user90131375/review/292679817/1e58b77be0

## **Ok , how to get started?**

To understand how AWS Lex works and truly leverage it to solve your problems , you ideally need to go through the developer guide at least once :

https://docs.aws.amazon.com/lex/latest/dg/what-is.html

However the basic premise is that , you can create a number of intents for your bot . An intent should be created to fulfil a single intention - say for example ,

- an intent to describe instances
- an intent to launch new instances
- an intent to greet/say hi , and so on.

Your intents should not overlap both in sense or in functioning.

## **<u>How does Lex know which intent to fire always?</u>**

This is based on sample utterances for an intent. An intent can have one/more sample utterances . Enumerate all the different types of ways an end-user might interact with your bot . These sample utterances are the voice/text commands that lex interprets and tries to associate to one or more intent for your bot . For an intent to start a new instance , the sample utterances might be like this -

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/869ud4kso567p1y6av83.png)

Now , in Lex there is also a concept of slots . Slots can be used to extract important information from the utterances . For example , say I have an intent to operate on an instance like say Start/Stop an instance . Then , I can define 2 slots **{action}** and **{serverName}** . When , an user provides a sample utterance like -

**I want to start DEV** , it will correspond to the sample utterance -

**I want to {Action}{ServerName}** , and the slot values - **{Action}** will be populated to **"Start"** , and **{ServerName} **populated to **"DEV"**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nlkn6sduexssze6w1m3i.png)

## **<u>What happens after the intent and slots are configured?</u>**

You can configure Lex to call AWS Lambda functions both for initial validation as well as final fulfilment . Lex supports two points of interaction with AWS Lambda . You can perform validation first to check whether the slot values are proper or not , and perform custom validation in your lambda function . Also , you can use a lambda function for fulfillment. Fulfilment refers to the final resolution of an intent , meaning performing the operation that the intent was created to do .

For example , for an intent to BookACar , the final resolution might be calling an UberAPI with uber details to book the car.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/czm5n7080fj1q46hseeb.png)

## **<u>What does the lambda function do , and how does it connect back with AWS Lex ?</u>**


The lambda function can perform both initial validation as well as final fulfillment. Say , the slot values provided by the user are not values that are supported by the application  , then you can return an **ElicitSlot** event from the lambda function back to Lex , with an associated message , saying what was wrong with the slot value provided , and optionally providing a list of valid slot values. This event will be parsed by Lex , and Lex will show the message to the user , and ask for the valid slot values.

```
// If any slots are invalid, re-elicit for their value
TryInferSlots(instanceLaunchRequest, previousLaunchRequest);
ValidationResult validateResult = Validate(instanceLaunchRequest);
if (!validateResult.IsValid)
{
lexEvent.CurrentIntent.Slots[validateResult.ViolationSlot] = null;
return ElicitSlot(sessionAttributes, lexEvent.CurrentIntent.Name, lexEvent.CurrentIntent.Slots,
validateResult.ViolationSlot, validateResult.Message , validateResult.ResponseCard);
```

There are a few basic events that are supported by Lex , and they are as follows :

- **<u>Delegate</u>** - delegating responsibility to AWS Lex to figure out the next step
- **<u>ElicitSlot </u>**- provide a slot value and a message that Lex will display to the user asking the user to input the value , that Lex will then put into that slot and pass on to the lambda function in the next call
- **<u>ConfirmIntent </u>**- Asking the user to confirm that the user really wants to perform that action via a Yes/No response.

These events typically guide the lambda-lex integration and seamlessly allows the user to interact with the chatbot.

**<u>Integrating the chatbot with Lex</u>**

This is the easiest part . You can choose to publish your bot and integrate it with a messaging platform like Facebook Messenger/Slack . Here , I chose Slack . The steps are extremely well articulated here . Just follow them judiciously , and you are good to go.

https://docs.aws.amazon.com/lex/latest/dg/slack-bot-association.html

## **<u>Response Cards</u>**

Response cards are a nice way for users to interact with your chatbot . Rather than asking your users to write down an option , you can show users an image depicting the chatbot response , and provide options for the user to interact with . They are very easy to code , and a lambda response can return a response card , that Lex will then display to the user.

```
LexResponse.LexResponseCard card = new LexResponse.LexResponseCard();
List cardButtons = new List();
LexResponse.LexGenericAttachments cardGenericAttachments = new LexResponse.LexGenericAttachments();
card.Version = 1;
card.ContentType = "application/vnd.amazonaws.card.generic";
cardGenericAttachments.Title = "Instance count";
cardGenericAttachments.SubTitle = "Select the number of instances to launch";
cardButtons = new List()
{
 new LexResponse.LexButton(){Text = "1" , Value = "1"},
 new LexResponse.LexButton(){Text = "2" , Value = "2"},
 new LexResponse.LexButton(){Text = "3" , Value = "3"},
 new LexResponse.LexButton(){Text = "4" , Value = "4"},
 new LexResponse.LexButton(){Text = "5" , Value = "5"},
};
cardGenericAttachments.Buttons = cardButtons;
card.GenericAttachments = new List()
                                  { cardGenericAttachments };<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>                    return card;
```

<u>And the response cards look something like this on the final UI -</u>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r3uftbxb5xxa7dt719gp.png)

**<u>Gotcha 1 :</u>** The Lex Test Chatbot console does not render response cards , however the slack UI displays them perfectly . This is also true for indentation or special characters . If you add "\n" to the message , the messages will not come in new lines in the Test Chatbot Console in AWS , however they will appear perfectly on new lines in Slack.

## **<u>Limitations</u>**

This is a pretty basic chatbot , and not at all production ready . Also , I personally don't think launching EC2 instances via a chatbot is a viable practical application of AWS Lex as there are too many variables involved.

- An EC2 instance launch needs a key pair , that you can create/select and then download the private key of that key pair to later on login to the instance . You can not directly download the key pair from Slack while the conversation is going on . So , I have coded the lambda to create a new key pair with an auto-generated name , and saved it in a pre-configured AWS S3 bucket . The details will be communicated by the chatbot to the end user ,and then end user is expected to download that key from S3 , and use it to login to the instance.
- The instances will be created in a default VPC , using a default security group and a subnet (in the availability zone selected) . This can obviously be extended to select/create a VPC.
- Also , I could not properly configure Lex to inject slot values from the sample utterances to the custom slot type values . Say , for example I created a custom slot type with values - QA1 , QA2 , DEV1 , DEV2 . Then my expectation was that if user inputs QA3 , Lex will be able to correlate with the slot . However , in my case , this desired outcome was not coming.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cdutunw6p7ch6g261vpw.png)

## **<u>Where can we go from here?</u>**

Check out the video at https://vimeo.com/292679817 to understand how the chatbot works . Granted , this bot is pretty straightforward , but it was a really nice learning lesson.

Go ahead , play with the code or create something awesome from scratch using the same principles . Any as always , let me know if you have any feedback on the same.