Automate, Accelerate, Accurate

Main menu

Post navigation

Writing PowerShell Core AWS Lambda Functions – Part IV

Overview

In this fourth blog, we’re going to write the PowerShell Lambda function. By the end of this blog, we’ll have tested our function, and be ready to package and upload it to AWS.

The Story So Far…

At this point, we have in place connectivity between our Facebook app and Lex and we’ve done a basic test to ensure that our commands are being parsed.

We’re ready to begin development of our PowerShell Lambda function! 🙂

The Goal

When Lex receives data from Messenger that matches an Utterance pattern, it invokes a PowerShell Lambda function we will later write. When the script runs it obtains the value of the command slot, and then obtains a synopsis of what the cmdlet does. This information is returned back to Lex which in turn sends it back to Messenger for it to display.

Define Module Requirements

The Requires definition restricts the script from running unless AWSPowerShell.Netcore, version 3.3.270.0, is available.

Write the Event Data to Cloudwatch

PowerShell

1

Write-Host($LambdaInput|ConvertTo-Json)

The contents of the Lambda functions input data variable, $LamdaInput are converted from its object format to JSON, before being output. Use of Write-Host in a PowerShell Lambda function results in the data being written to Cloudwatch. This results in an entry in the log similar to below:

Set PowerShell Error Handling

The default value for the $ErrorActionPreference variable for PowerShell running in a Lambda function is Continue. Unfortunately, it appears within a Lambda function, if an error occurs during execution of a PowerShell script, this causes it to immediately exit and return an error to the caller. In our code there exists two possibilities for errors to be raised. To address this we set $ErrorActionPreference to Stop, and then use a Try..Catch block to handle errors and prevent exit. The specifics of our Try..Catch block are details next.

PowerShell

1

2

3

4

5

6

7

8

9

$ErrorActionPreference='Stop'

try{

.

.

}

catch{

.

.

}

Process the Command Slot

As mentioned in an earlier blog in this series, input data is automatically read and cast into a PSobject called $LambdaInput. Using the sample JSON above as a guide, we can obtain the slot value for Command via $LambdaInput.currentIntent.slots.Command

An additional step we want to do is obtain the correct casing of the command. This is required because we will be querying a URL soon which will include the name of the command, and most web servers are case sensitive.

Obtain Online Help Information

A normal approach to getting help about a PowerShell command is via the Get-Help cmdlet. The AWSPowerShell.Netcore module implementation for PowerShell Lambda does not include help information. This is understandable, naturally, with Get-Help being an interactive command. So for us to obtain the synopsis information for a cmdlet we need to grab it from the online help.

For any given cmdlet’s page, we want to grab the Synopsis information. A look at the page source for the above one shows us this section:

We can use regex with a capture group to grab the text we need. I’ve opted to use the expression below after playing around a bit with the regex tester at Regex Storm. Note the use of a named capture group, synopsis, in our regex.

This is represented in PowerShell (with the addition of backticks to escape the double quotes) via:

PowerShell

1

$pattern="<div class=`"synopsis`">(?'synopsis'.*)</div>"

Now we can proceed with scraping the synopsis information.

PowerShell

1

2

$response=Invoke-WebRequest-Uri$url

$response.Content-match$pattern

We get the content of the web page, then perform the regex match against it.

PowerShell uses a special variable, $matches, which is automatically populated if a successful match occurs. Because we used a named capture group in our expression, synopsis, a hashtable entry with this name is created.

This makes referencing the information simple.

We also want to ensure that if no match is found a message returned to indicate this.

PowerShell

1

2

3

If(!($description=$Matches["synopsis"])){

$description="No help is available for $command"

}

The $description variable is set to the the value of the match. If this is null, however, it is updated to say that no help is available for the cmdlet.

Apply Catch Conditions

We need to apply the Catch conditions to react to errors that can occur within the Try block, since it could be that the :

Catch[System.Net.WebException]{$description="Sorry, no help exists for $commandparam"}

These are handled by the first and second Catch statements respectively.

Create the Response

We then make use of a template JSON format, using the documentation here for information.

PowerShell

1

2

3

4

5

6

7

8

9

10

11

12

13

14

# Response template for Lex

$template=@"

{

"sessionAttributes": {},

"dialogAction": {

"type": "Close",

"fulfillmentState": "Fulfilled",

"message": {

"contentType": "PlainText",

"content": "$description"

}

}

}

"@

These settings indicates that the workflow should end and that the result be marked as successful. The $description variable in the here-string is automatically expanded by PowerShell to contain the value set in the previous steps.

Return the Response

Lastly, the $template string is returned from the PowerShell Lambda function. This will be received by Lex.

PowerShell

1

Return$template

Test

At this point, you can perform a test locally on your system just by making a couple of changes to your script.

Surround the lines from #Requires up to and including $commandparam = $LambdaInput.currentIntent.slots.Command in a remark block

Add your own definition after for $commandparam

Now you can run the script locally and receive console output.

Remember to undo those changes mentioned once you have completed testing the script.

Conclusion

At this point, we have written our PowerShell Lambda function and tested it locally. We’re nearly there!

In the next blog in the series, we’ll publish the function to Lambda, configure Lex to invoke it, and show it in operation from a couple of devices.