Writing PowerShell Core AWS Lambda Functions – Part V


In this fifth and final blog, we’re going to package and publish our recently completed PowerShell Lambda function. We’ll configure an IAM role to allowing publishing, reconfigure our Lex ‘bot to point to the Lambda, and give it a spin from Messenger on a couple of devices. 🙂

NOTE: All of the code over the five blogs is available at my github repository.

The Story So Far…

At this point, we have in place connectivity between our Facebook app and Lex, have written our PowerShell Lambda function, and tested it locally.


It’s important to bear in mind that processing of both AWS Tools for PowerShell and AWS Lambda PS Core cmdlets requires a set of AWS credentials (unless the command explicitly does not access AWS resources). These are used to sign the associated request, validating the authenticity of the source. Several methods exist for passing of credentials in requests. These vary from per command to as-default and may be assumed or specified.

The following steps will require one of these methods mentioned to be in place. For reference information on getting credentials setup for use with PowerShell, consult the Using AWS Credentials section of the AWS Tools for PowerShell documentation.

Publish the Package

Navigate to your project’s directory, and enter the following, choosing values for -Name and -ScriptPath that match your requirements.

In my case, the package will be called Get-Synopsis, and the source script file, GetSynopsis.ps1 is located within the GetSynopsis sub-directory.

Publish-AWSPowerShellLambda -Name Get-Synopsis -ScriptPath ./GetSynopsis/GetSynopsis.ps1

Shortly after, when the .zip file has been created of the package, you will be prompted to choose an IAM role for the Lambda function. Note that it is possible to specify the role via one of the cmdlet parameters, but we’re doing this manually to get a feel for what is being done.

  • Choose ### Create new IAM Role ###
  • At the Enter name of the new IAM Role prompt, enter aws-lambda-lex

Then you’ll be asked to chose an IAM Policy that will be attached to the new role.

Select the option that corresponds to AWSLambdaBasicExecutionRole (Provides write permissions to CloudWatch Logs.)

The process will then continue and soon after display a message confirming that the Lambda function has been created.

Verify IAM

Let’s check and see what’s been created in IAM.

  • Go to the main AWS console screen
  • Click Services
  • Click IAM
  • Click Roles

You should see a new role, called aws-lambda-lex. Click on it.

It will have the policy we specified, AWSLambdaBasicExecutionRole, assigned to it.

Check the Lambda

Let’s check the Lambda console section to validate that our package is setup.

  • Go to the main AWS console screen
  • Click Services
  • Click Lambda
  • Check the list on the right hand side for Get-Synopsis

Click Get-Synopsis to have a look at the configuration

Configure Lex

Now, we need to change the Lex configuration so that it triggers the Lambda function.

  • Click Services
  • Click Amazon Lex
  • Click MyPowerShellHelpBot
  • Go to the Fulfillment section
  • For Lambda function, select Get-Synopsis
  • For Version or alias, ensure it is set to Latest

If you are prompted to give permission to allow Lex to invoke Get-Synopsis, click OK

  • Click Save Intent
  • Click Build
  • After confirmation that the bot has built, the test window will open
  • Enter I want help with Get-EC2Host and press return.

  • Looks good! Click Publish
  • Select Prod from the drop down list
  • Click Publish
  • Click Close once the publishing is complete

Test from Messenger

Let’s test this both from our system and also phone.

  • If you need the link to the Messenger page, do the following:
    • Go to https://developers.facebook.com/
    • Click My Apps
    • Click AWS PowerShell Help
    • Under Products click Messenger
    • Click Settings
    • Scroll down to App Review for Messenger and click Page Settings at the bottom of it
    • In the Your Messenger Link, click Copy Link
  • Go to your web browser, and type/paste into the address bar the URL for the Messenger page.
  • Enter the same text as above, I want help with Get-EC2Host
  • You should see output similar to below:

  • Now go to your mobile device
  • Access Messenger, using either the link from above, or the client itself. If using the latter, you will also need to create a new message and enter the page name in the To: section.
  • Enter I want help with Get-S3Object
  • You should see output similar to the following (note that you’ll also see the request you have just done on the other system):


Congratulations! You’ve reached the end of this blog series on writing PowerShell Core Lambda functions and now have an interactive, cross platform, mobile help facility for the AWS PowerShell cmdlets available.

Keep an eye out for forthcoming blogs about PowerShell Core Lambda functions.

Thanks for reading! Feedback welcome!


Writing PowerShell Core AWS Lambda Functions – Part IV


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.

Writing the Lambda Function

Let’s set about getting our PowerShell script in place.

Create the package

  • Launch PowerShell
  • Type New-AWSPowerShellLambda -Template Basic -ScriptName GetAWSPowerShellHelp
  • A directory will be created, GetAWSPowerShellHelp, containing two files, GetAWSPowerShellHelp.ps1 and readme.txt
  • Open GetAWSPowerShellHelp.ps1 in your editor of choice.
  • Copy the script below into the clipboard
  • Paste into your editor, removing any existing text
  • Save the file
# PowerShell script file to be executed as a AWS Lambda function. 
# When executing in Lambda the following variables will be predefined.
#   $LambdaInput - A PSObject that contains the Lambda function input data.
#   $LambdaContext - An Amazon.Lambda.Core.ILambdaContext object that contains information about the currently running Lambda environment.
# The last item in the PowerShell pipeline will be returned as the result of the Lambda function.
# To include PowerShell modules with your Lambda function, like the AWSPowerShell.NetCore module, add a "#Requires" statement 
# indicating the module and version.

#Requires -Modules @{ModuleName='AWSPowerShell.NetCore';ModuleVersion=''}

#Log the event data to CloudWatch
Write-Host ($LambdaInput | ConvertTo-Json)

$ErrorActionPreference = 'Stop'

try {
    #Get the value of the Command slot
    $commandparam = $LambdaInput.currentIntent.slots.Command
    #Get the correct casing for the command
    $command = (get-command -Name $commandparam | Select-Object -ExpandProperty Name)
    $url = "https://docs.aws.amazon.com/powershell/latest/reference/items/$($command).html"
    $pattern = "<div class=`"synopsis`">(?'synopsis'.*)</div>"
    $response = Invoke-WebRequest -Uri $url
    $response.Content -match $pattern
    If (!($description = $Matches["synopsis"])) {
        $description = "No help is available for $command" 
Catch [System.Management.Automation.CommandNotFoundException] {$description = "Sorry, no AWS PowerShell cmdlet called $commandparam exists"}
Catch [System.Net.WebException] {$description = "Sorry, no help exists for $command"}

# Response template for Lex
$template = @"
	"sessionAttributes": {},
	"dialogAction": {
		"type": "Close",
		"fulfillmentState": "Fulfilled",
		"message": {
			"contentType": "PlainText",
			"content": "$description"

Return $template

Script Breakdown

Let’s take a look at the script in more detail:

Define Module Requirements

#Requires -Modules @{ModuleName='AWSPowerShell.NetCore';ModuleVersion=''}

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

Write the Event Data to Cloudwatch

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.

$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.

    $commandparam = $LambdaInput.currentIntent.slots.Command
    #Get the correct casing for the command
    $command = (get-command -Name $commandparam | Select-Object -ExpandProperty Name)

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.

Help is available from the AWS Tools for PowerShell Cmdlet Reference page for all cmdlets. For a specific one, this is available at:

https://docs.aws.amazon.com/powershell/latest/reference/items/cmdlet name.html

An example is below:

Knowing this, it is pretty straightforward to calculate the URL that we’ll be accessing:

 $url = "https://docs.aws.amazon.com/powershell/latest/reference/items/$($command).html"

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:

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

Now we can proceed with scraping the synopsis information.

    $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.

    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 :

  • cmdlet does not actually exist
  • cmdlet exists but there is no help webpage
Catch [System.Management.Automation.CommandNotFoundException] {$description = "Sorry, no AWS PowerShell cmdlet called $commandparam exists"}
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.

# 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.

Return $template


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.


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.

Thanks for reading! Feedback welcome!


Writing PowerShell Core AWS Lambda Functions – Part III


In this third blog, we’ll be setting up the initial configuration of the Lex ‘bot that is going to be the intermediary between the Facebook page we setup earlier and our forthcoming PowerShell Lambda function. After a brief overview of what’s going to be needed, we’ll setup a Slot Type (with a bit of help from PowerShell), Slot, Intent, and Facebook Channel. With this done, we’ll do a basic test of the ‘bot which to give some insight into the event data format that our function will be receiving and processing.

The Story So Far…

At this point, we have a Facebook app and page setup, our development environment in place with all the prerequisites for writing & publishing PowerShell Lambda packages and taken a trip through the (currently) four cmdlets offered by the AWSLambdaPSCore module.

About Lex

Lex is an AWS service geared towards allowing the use of natural language, written or spoken, as a mechanism for driving other applications and services. It allows you to connect social media services like Facebook, a device such as an Alexa Dot, or your own application to Lex and process the input before providing a response back to the source.

Your Lex bot consists of one or more :

  • Intents – An objective, performed via a combination of the items below.
  • Utterances – Sequence of words which activate a specific Intent
  • Slots – Sets of values, akin to parameters, that are utilized in order for the objective to be achieved. The value of a slot can be filtered from an Utterance, or prompted for separately. Also akin to typical parameter behavior, the values provided to Slots can be restricted to a given format or list of values, using what are known as Slot types.
  • Prompts – Requests for additional information or confirmation of details already given.
  • Fulfilments – The steps that will be taken with all the previous provided that allows the Intent to be completed. In our case this will involve the invoking of a Lambda function and receipt of its output.
  • Channels – Communications services (currently Facebook, Kik, Slack, and Twilio SMS) that Lex can by interact with.

What’s Required

For our ‘bot, with the above in mind, we’re going to go for the following:

Intent Name GetPowerShellHelp
Utterances I want help with {command}

What does {command} do

I want help with an AWS cmdlet

Slot Type PSCommand (custom)
Slot Type Values Every command in the AWSLambdaPSCore module(!)
Slot Name command
Fulfillment Our Lambda function
Channel Facebook

Let’s set about getting these setup. 🙂

Creating the ‘bot

    • Login to the AWS console
    • Click Services, Amazon Lex
  • With Bot selected on the left hand side, click Create

  • In the Create your bot window, click Custom bot
    • Bot name : MyPowerShellHelpBot
    • Language : Leave as default English (US)
    • Output voice: None. This is a text based application
    • Session timeout : 1 min
    • IAM Role : This is created for you automatically
    • COPPA : No
  • Click Create

A few more seconds should see our bot created. Before we start creating the Intent though, we’re going to create a file that will be used by the Intent, the Slot type PSCommand, mentioned above.

About Slot Types and Slots

Initially, Lex receives a set of words, the Utterance, which acts as a trigger to a specific Intent. In the simplest of Intents, no specifics are required other than the a fixed set of words.

e.g. “play a song for me”.

More often though, additional details will be required. After all, if we want to hear a song, sometimes you might want to hear music by a specific artist. However, creating an Utterance for every single artist would be an impossible task.

This is where Slots come into play. In the example above, the artist is your slot. Defining a Slot tells Lex that it needs specific information to process the request. Each Slot has an associated Slot type.

A Slot type really is just an enumeration that consist of a specific input value (or values), and the underlying value. A Slot type can have more than input value, known as a synonym.

e.g. In the Amazon.Number Slot type, an input value of “four” has an underlying value of 4.

Slot types help not only to translate input data into a more suitable format for processing, but also to restrict the input values that can be given. They can be used to prevent invalid data from being given BEFORE any processing is done. Whilst it also is possible to do pre-processing of input data, In our case, we don’t want our Lambda to run if it’s being provided with an invalid cmdlet name.

Creating the Slot Type File

We’re going to create a Slot type, called PScommand. This will consist of a list of commands from the current revision of the AWSPowerShell.NetCore module we have installed. Our Slot, command, is the parameter that we will be using within our PowerShell Lambda function. Slot types can be imported as a zip compressed JSON file.

We’ll write a PowerShell script to make this JSON and zip file for us. Jump to your development environment of choice, paste the code below, and save it as a .ps1 file.

$json = @"
"metadata": {
"schemaVersion": "1.0",
"importType": "LEX",
"importFormat": "JSON"
"resource": {
"name": "PScommand",
"version": "1",
"enumerationValues": [

"valueSelectionStrategy": "ORIGINAL_VALUE"

$slot = $json | ConvertFrom-Json
$command = Get-Command -Module 'AWSPowerShell.NetCore'

ForEach ($obj in $command) {
$slot.resource.enumerationValues += [PSCustomObject]@{
value = $obj.name
synonyms = @()

$slot | ConvertTo-Json -Depth 4 | Out-File psCommand.json -force
Compress-Archive -Path ./psCommand.json -DestinationPath ./psCommand.zip -Force

The here-string that you see is the core template format for slot type we’ll be using. The changes to be made here will involve filling the enumerationValues array with a list of the AWSPowerShell.NetCore modules cmdlets.

The script itself, after converting the JSON to a PSObject, enumerates through each command in the above module and appends an array consisting of a value element, set to the name of the command, and an empty array value for the synonyms attribute. Note that if we wanted to get really detailed, we could use the PowerShell aliases that exist for the commands in this module, placing them in the appropriate entries synonym value, but I’m going to keep it simple. Once this is complete, the PSObject is converted to a JSON file, before being zipped up.

Run the script to create both of these files.

Should you so want, you can take a look at psCommand.json.

Import the Slot Type File

Let’s import our new file:

    • Go back to the main Amazon Lex screen
    • Click Slot types
    • Click Actions, Import
    • On the Import slot type popup, click Browse
    • Navigate to the directory containing your script and the psCommand.zip, select the file and click Open
    • Click Import

Soon after, our Slot type, PScommand should be visible.

Create the Intent

Follow the steps below to create the intent. Similar to Slot types, it is possible to create a zipped JSON file with the Intent, but we’ll do this through the console for now to make it a bit clearer what is happening.

  • On the main Amazon Lex screen, click Bots
  • Click MyPowerShellHelpBot
  • Click +Create Intent
  • In the Add intent window, click Create intent

  • In the Create intent window, call the type getAWSPowerShellHelp for the Intents name.
  • Click Add

Next, we configure the intent. Set to the following.

  • Sample utterances :
    • What does {command} do
    • I want help with {command}
    • I want help with an AWS cmdlet
  • Lambda initialization and validation
    • Do not set
  • Slots
    • Required : Checked
    • Name: command
    • Slot typePScommand
    • Prompt: What cmdlet?
    • Click the blue + button to add the slot
  • Confirmation prompt
    • Leave unchecked
  • Fullfillment
    • Return parameters to client

Now, scroll down to the bottom of the screen and click Save Intent

Build and Publish the ‘Bot
Before we begin configuration of the Channel, we need to build and publish the ‘bot.

Click Build and wait for the build to finish. On completion, you should find the Test bot window has expanded.

  • Click Publish
  • Next, we are prompted to create an alias by providing it with a name. An alias is simply a reference to a version of a build.
  • Enter Prod for the alias name
  • Click Publish

Once the bot is published, a confirmation screen will appear.

  • Click Close

Create the Facebook Channel

The next step involves us setting up a Channel, in our case for Facebook. Successful configuration of a Channel allows the creation of an endpoint through which Facebook will communicate with Lex.

At this point, grab the Page Access Token and App Secret Key you recorded in Part I of this series.

  • Click the Channels tab
  • Click Facebook on the left hand side
  • Configure as follows:
    • Channel Name: MyFBChannel
    • Channel Description: AWS PowerShell Help Channel
    • KMS Key : Select aws/lex from the drop down list
    • Alias: Select Prod from the drop down list
    • Verify Token: MyVerifyToken
    • Page Access Token : Use the one you have from Part I
    • App Secret Key: Use the one you have from Part I
  • Click Activate

Activation results in a Callback URL, an endpoint that will be used by the Facebook app to communicate with Lex, being created.

Click Copy to copy into the clipboard.

Configure the Facebook Page and App

With the above configured in AWS, we now move on to configuring Facebook to use the Lex ‘bot.

Making the Application Live
In order to allow our application talk to Lex, the Facebook application (AWS PowerShell Help in my case) needs to go live. This requires, however, that the application is configured to provide a link to a privacy policy for users the app.

  • Click Settings, Basic.
  • Configure Privacy Policy URL with any valid web address. There does not need to be any policy a the address given.

    • At the top of the screen, click the slider to make the app live.
    • Set the category to Messaging
  • Click Confirm when prompted if you wish to make the application public.

Shortly afterwards, the slider will go green and indicate ‘ON’

Configure a Webhook and Subscription
Now we’re going to setup the app to use the Callback URL from earlier.

  • Click Messenger on the left side of the screen
  • Click Settings directly below
  • Scroll down to Webhooks
  • Click Setup Webhooks
  • On the New Page Subscription window that opens, use the following settings:
    • Callback URL : Paste the text that was copied into your clipboard from the AWS console
    • Verify Token : MyVerifyToken
    • Subscription Fields: messages, messaging_postbacks, messaging_options
  • Click Verify and Save

Your subscription will be created.

  • Go to the App Review for Messenger section further down the screen
  • Click pages_messaging
  • Click Add to Submission

  • Click the Page Settings hyperlink in the text Looking for pages_messaging_subscripts? It’s moved to Page Settings.
  • This will take you to the Messenger Platform Settings

Because we are using Messenger as our communication method with our Lex ‘bot, some additional configuration needs to be carried out.

We don’t want the app to require manual intervention, so we need to tell Facebook this.

  • Go to the General Settings section
  • Set Response Method to Responses are all automated

Additionally, we want our app to be the one first notified if a message is received by our Facebook page.

  • Go to the Subscribed Apps section
  • Set Primary Receiver to AWS PowerShell Help 
  • Ensure Secondary Receiver is now set to Page Inbox

Testing Interaction With Lex ‘bot

Now let’s perform some basic checks of our apps interoperability with Lex.

  • Scroll to the Your Messenger Link section
  • Click Copy link to place the address in your clipboard

  • Paste the URL into the address bar of a new tab in your browser
  • Enter the following text into the message bar:
    • What does Get-EC2Host do?
    • Press return
    • I want help with Get-EC2Host
    • Press return

For each of the text entries above, you should receive a message back.

Remember that at this point we have set Fulfillment to Return parameters to client in the bot’s Intent configuration. This is why you should be seeing the name of Intent being invoked and the Slot provided. Our Lambda still needs to be created.

We’ve verified connectivity between Facebook and Lex, that the messages we have sent are triggering the right Intent and the parameter (slot) is properly parsed.

Let’s jump back to Lex so we can take a look at how this information is natively output.

Viewing the JSON Output

Note that the response that you saw in the Messenger window was a modified version of the actual event data that is raised. This was handled by the Channel.

Let’s take a look at the pre-formatted data.

  • Go back to the Amazon Lex screen
  • Click Bots
  • Click MyPowerShellHelpBot
  • Expand the Test bot window on the right hand side of the screen
  • In Chat with your bot… type I want help with Get-EC2Host
  • In the Indirect response section below, click Detail

You Test bot window should resemble the below:

The text in the Indirect response window is the exact JSON data that our Lambda will receive once it’s up and running.


In this slightly longer than usual blog, we’ve covered the basics of a Lex ‘bot and components that are used with it, created a Slot type consisting of the AWS PowerShell commands, configured an Intent and Channel and setup interoperability between Lex (via the Channel) to the Facebook app. We then performed testing both from Facebook Messenger and also using the Test ‘bot in the AWS console. Lastly, we’ve had an introduction to the format of the event data that is going to be passed to our forthcoming PowerShell Lambda function.

This leads us nicely up to the next blog, in which we’ll create our PowerShell Lambda function, upload it to AWS, and configure it to be activated by our Lex ‘bot.

Thanks for reading! Feedback welcome!


Recommended Further Reading

Create and Edit Custom Slot Types – https://developer.amazon.com/docs/custom-skills/create-and-edit-custom-slot-types.html