DEV Community

PikuBITSPilani
PikuBITSPilani

Posted on

A Serverless Chatbot based on AWS Lex and Lambda to control your EC2 instances

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 .

Disclaimer : 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

So , what am I trying to achieve here ?

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

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.

How does Lex know which intent to fire always?

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

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

What happens after the intent and slots are configured?

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

What does the lambda function do , and how does it connect back with AWS Lex ?

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);
Enter fullscreen mode Exit fullscreen mode

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

  • Delegate - delegating responsibility to AWS Lex to figure out the next step
  • ElicitSlot - 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
  • ConfirmIntent - 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.

Integrating the chatbot with Lex

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

Response Cards

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;
Enter fullscreen mode Exit fullscreen mode

And the response cards look something like this on the final UI -

Image description

Gotcha 1 : 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.

Limitations

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

Where can we go from here?

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.

Oldest comments (0)