With AWS Lambda you can deploy and execute code that can be triggered from a multitude of event sources without provisioning or the need to maintain any host servers. Lambda functions can run a number of different languages to include PowerShell Core. In this chapter, we’ll explain how to set up AWS Lambda using PowerShell and to execute PowerShell code as a Lambda function.
We’ll begin by going over prerequisites to set up your development environment and install required packages and modules. Then we’ll show you a couple ways to generate and deploy a PowerShell-Based Lambda function. We’ll walk through the execution of this Lambda function. We will evaluate the function execution and resultant output logs. Finally, we will set up an execution schedule for our new Lambda function.
There is one exercise at the end of this chapter. We’ll show you how to use AWS PowerShell on Lambda function to update an AWS Auto Scaling group ImageId that is triggered by an SNS notification.
To explore and learn more about the many features we’ll be talking about, open your browser and head over to the AWS Lambda console at https://console.aws.amazon.com/lambda .
PowerShell-Based Lambda Prerequisites
Lambda’s support for PowerShell is based on the cross-platform PowerShell Core. Because PowerShell Core is built on top of .NET Core, we will also need .NET Core installed. There are several new PowerShell publishing cmdlets to generate and deploy PowerShell-Based Lambda functions, and these require the .NET Core SDK.
We can download and install the latest .NET Core SDK from www.microsoft.com/net/download .
Once download and installation is complete, we need to install PowerShell Core. Downloads for various Linux and Windows installers can be found at https://github.com/PowerShell/PowerShell/releases .
Instructions for installing PowerShell Core on your specific development environment can be found at https://docs.microsoft.com/en-us/PowerShell/scripting/setup/installing-PowerShell?view=PowerShell-6 .
Note
Lambda support for PowerShell requires version 2.1 or greater of the .NET Core SDK.
Note
Lambda support for PowerShell requires PowerShell Core Edition 6.0 or greater. This version number can be seen as the PSVersion key value of the $PSVersionTable variable.
Note
Be sure to use version 3.3.270.0 or greater of AWSPowerShell.NetCore, which optimizes the cmdlet import process. If you use an older version, you will experience longer cold starts. To update your existing install to the latest version, run Update-Module AWSPowerShell.NetCore –Force.
The AWSPowerShell.NetCore module provides the SDK PowerShell cmdlets to interact with AWS infrastructure and services.
Authoring PowerShell-Based Lambda Functions
There are four new cmdlets available that we now have access to as a result of installing these prerequisites. Each of these cmdlets provides the tools necessary to quickly build, package, and deploy a PowerShell-Based Lambda function.
Creating a Script Template
Lambda Templates
Template | Description |
---|---|
Basic | Bare bones script |
CloudFormationCustomResource | PowerShell handler base for use with CloudFormation custom resource events |
CodeCommitTrigger | Script to process AWS CodeCommit Triggers |
DetectLabels | Use Amazon Rekognition service to tag image files in S3 with detected labels |
KinesisStreamProcessor | Script to process a Kinesis Stream |
S3Event | Script to process S3 events |
S3EventToSNS | Script to process SNS Records triggered by S3 events |
S3EventToSNSToSQS | Script to process SQS Messages, subscribed to an SNS Topic that is triggered by S3 events |
S3EventToSQS | Script to process SQS Messages triggered by S3 events |
SNSSubscription | Script to be subscribed to an SNS Topic |
SNSToSQS | Script to be subscribed to an SQS Queue, that is, subscribed to an SNS Topic |
SQSQueueProcessor | Script to be subscribed to an SQS Queue |
Included in the script are a number of comments that will guide us in creating our PowerShell-Based Lambda function. In the next sections, we will investigate and understand the components of this template. We will also be adding some of our own code before saving it.
Understanding Modules
This statement indicates that the script requires the AWSPowerShell.NetCore module with a version of 3.3.335.0 or greater, we had installed earlier in this chapter. Note that 3.3.335.0 is the minimum version at the time of this books writing and any value greater than this in the script template is acceptable. For more information on the "#Requires" statement, see https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_requires?view=powershell-6 .
Understanding Input
At the top of the script template, we see how input is defined in a PowerShell-Based Lambda function. The $LambdaInput variable is a PSObject that contains all the data that is passed as input to the Lambda function when executed. This includes the data passed from triggering events. In Figure 18-2 we see that the SNS event trigger subject and message property values have already been assigned to the $subject and $message variables. We can easily adapt our code to perform logical operations from the $LambdaInput variable or one of its properties.
Understanding Output and Logging
Write-Output places data into the pipeline so it does not get written to CloudWatch Logs. It shares a similar characteristic to the return statement in that it can be used in place of a return. Lambda function output will either be the last item added to the pipeline such as the last Write-Output statement or from a return statement if utilized. More details on Lambda function logging using PowerShell can be found at https://docs.aws.amazon.com/lambda/latest/dg/PowerShell-logging.html .
Understanding Errors
Lambda function executions end in either a succeeded or failed result. Failure to execute, timeouts, or script-level terminating errors are some of the ways that a Lambda function will result in a failed state. A successful execution of a Lambda function is all that’s needed for a passing result.
You can control the result of your Lambda function by using the throw or write-error statement in your script. The throw statement is a terminating error that will exit your script and Lambda function immediately. The write-error statement will continue executing your Lambda function script but will ultimately result in your function ending with a failed result. For more information on Lambda function errors in PowerShell, see https://docs.aws.amazon.com/lambda/latest/dg/PowerShell-exceptions.html .
The LambdaContext Variable
Additional data on the Lambda Context Object for PowerShell can be found at https://docs.aws.amazon.com/lambda/latest/dg/PowerShell-context-object.html .
Creating a PowerShell Lambda Package
Now that we have a better understanding of PowerShell-Based Lambda functions script components and a completed script, save the changes and close it.
Before we can deploy our function, we first need to create a deployment package. There are a couple cmdlets to create a Lambda package. The first cmdlet Publish-AWSPowerShellLambda will create a temporary package and then publish it to Lambda automatically. The second cmdlet New-AWSPowerShellLambdaPackage will only create a named package we can deploy later. New-AWSPowerShellLambdaPackage is primarily used to generate packages as a part of an automated deployment package such as a CI/CD pipeline or as a part of a CloudFormation template. We are going to start with the New-AWSPowerShellLambdaPackage cmdlet so we can look at the generated package components and demonstrate a couple different deployment options.
Open the output package LatestAMIIDPackage.zip and review its contents. Among the many dll assemblies needed to host PowerShell Core, notice that our LatestAMIID.ps1 file is contained in this package as well as a folder labelled “Modules”. The modules folder contains the AWSPowerShell.NetCore module that is listed in our script requires statement. When we use either the New-AWSPowerShellLambdaPackage or Publish-AWSPowerShellLambda cmdlets, the requires statement is read from the script and those modules are placed into the packaged modules directory.
Publishing a PowerShell-Based Lambda Function
We are now ready to publish our new package. We will be working through a couple PowerShell-Based methods to publish a PowerShell Lambda package. The first method will use the package we just created and publish using some of the legacy AWS PowerShell Lambda cmdlets. The second method will use the newer Publish-AWSPowerShellLambda cmdlet.
Note
The remainder of this chapter will require administrative-level privileges to make modification to your AWS account. If you have not done so already, load your credentials in the current PowerShell 6 Core Console and set your default region to us-east-1. Lambda is a regional service, and the following chapter activities will be performed in the us-east-1 region.
Note
Due to the large number of parameters, many of the remaining examples in this chapter will pass parameters as a hashtable. This is known as PowerShell Splatting. More information on Splatting can be found at https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_splatting .
Let’s spend a minute talking about some of the Publish-LMFunction parameters before continuing. The ZipFilename parameter identifies the location of our Lambda package zip. Handler defines what script Lambda calls as the entry point in our package to begin execution. If you recall, this value was defined when we created the Lambda package earlier in this chapter. The Role parameter gets assigned the ARN value of the IAM role we just created.
Timeout is the maximum amount of time the Lambda function can execute before force stopping with a failure result. Runtime is the environment our function will execute under. For PowerShell code we use the dotnetcore2.1 value.
MemorySize determines how performant our function will be as this value determines both available memory and the CPU power available but increases the cost of an execution. CPU power will effectively double every 128MB of memory allocated and can have considerable impact on how quickly your function executes. Too little memory can result in unexpected failures or hitting the function timeout, while too much results in costly unutilized capacity.
PowerShell modules and script executions require a bit more resources than the defaults allow. We increase the timeout from the default of 3 seconds to 30 seconds and increased the MemorySize from the default of 128 to 256.
For more details on configuration settings go to https://docs.aws.amazon.com/lambda/latest/dg/resource-model.html .
While we are reviewing the console, let’s run a manual invocation. In the upper right corner of the LatestAMIID Lambda console window, select the Test button. When prompted by the Configure Test Event window, input “test1” into the event name box and click the Create button to close the window. The Lambda test console requires a Test Event configured before we can execute even if our function does not require any input.
We see that the parameter values we used to publish the function match the console and Get-LMFunctionConfiguration output.
We executed with the force switch to automatically accept confirmation prompts. If we check the console or try the Get-LMFunctionConfiguration query again, we find the function no longer exists.
We can now see that a number of the properties we had to define earlier are automatically configured for us. The Handler, Runtime, Timeout, and MemorySize properties are all set automatically with default values that would allow executing a basic PowerShell script. Another nice feature of the Publish-AWSPowerShellLambda cmdlet is it can be run repeatedly to make a code change or update modules. It will automatically update the target Lambda function with whatever components that need changed.
Note
Publish-AWSPowerShellLambda cmdlet has a number of additional parameters to publish and update PowerShell-Based Lambda functions. For a full list, review the cmdlet help by executing “get-help Publish-AWSPowerShellLambda -full”.
Invoking Lambda Functions
Now that we have seen a couple ways to create and publish a PowerShell-Based Lambda function, let’s see how we can manually execute it and review the output.
Normally Lambda functions are invoked as a result of an event trigger such as an SNS notification, CloudWatch event, or write to an S3 bucket, to name a few. We can also invoke a Lambda function manually. This can be useful for debugging, testing or even creating our own code to initiate a Lambda function when appropriate.
We then see several lines of text where some are labelled as Information and others as Warning or Verbose. Remember that our code uses several different approaches to write log, and we can see here how Lambda automatically tags the log entry with the log level it was written under. Make note of the log entry labelled with Warning.
Now we are going to run our Lambda function again, but this time we are going to give it an input payload and capture its output data.
RequestResponse – Executes and collects the output data upon completion
Event – Performs a quick asynchronous execution with no output data
DryRun – Used to test execution without actually executing the function
We no longer see the Warning log entry and now see that one of the Information entries that was previously empty now contains data. Remember the "Write-Host (ConvertTo-Json -InputObject $LambdaInput -Compress -Depth 5)" and "Write-Host (ConvertTo-Json -InputObject $LambdaContext -Compress -Depth 5)" that we uncommented and added earlier to our script? These have correlating log entries we now see. LambdaInput object contains the data we passed as input with the Payload parameter. We also have another log entry that contains JSON-formatted data that includes FunctionName, LogGroupName, and MemoryLimitInMB, among many other attributes of the Lambda execution. This correlates to writing the LambdaContext object to logs.
Lambda CloudWatch Logs
As we know, all log outputs for Lambda functions are recorded in CloudWatch Logs automatically. There will be times when we will need to check the history of a Lambda execution or need more granular data to debug our function. While this is not specific to Lambda, it can be difficult to find relevant examples for searching CloudWatch Logs using PowerShell. We will filter for and return the CloudWatch Logs for our recent Lambda execution as a demonstration.
CloudWatch Logs are separated as log groups. All AWS Lambda functions write to the /aws/lambda/{Function Name} log group. Log groups are further broken down as log streams. Log streams in Lambda log groups are a collection of executions and their events that occurred within a time range.
To get our execution event logs, we need to filter through all of CloudWatch Logs and get the CloudWatch Logs log stream name that contains our recent Lambda execution request ID. We can only search through 1MB of logs per query so we use a do while loop to keep searching through the logs. The loop runs until we find a matching log entry or we complete searching through all log streams.
Notice how we formatted the request ID as the FilterPattern value. This is because CloudWatch Logs filtering requires string matching searches to be in double quotes. We used escape characters to ensure that the double quotes were retained in the query. When we find matching logs, we identify the log stream name from the first matching log found and write the entire log stream as output. The matching log stream output will be formatted as a table so we can wrap the text to our screen for review.
Using this query, we can see the complete log for our Lambda execution.
Lambda Event Trigger
Up to this point, we have created a Lambda PowerShell script and packaged and published it. We also manually executed our Lambda function and evaluated output and log data. For our function to be useful, we also have to define a trigger. There are many Amazon services and conditions that can be used as triggers for Lambda functions. The configurations of these different triggers could easily span several chapters of their own. In this final topic and the chapter exercise, we will demonstrate two of the possible options.
Lambda service does not support schedules but the CloudWatch service does. In the following steps, we will be creating a CloudWatch Rule that will target our Lambda function every 60 minutes. We will then set a policy on our function allowing the 60 minute CloudWatch rule to invoke it.
Now we create a CloudWatch Event Rule. We will define the name as “60MinuteTimer”. The ScheduleExpression parameter takes a rate or cron expression. In our example, we define a rate expression of 60 minutes. For details on Schedule Expressions, go to https://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html .
All done. Our PowerShell-Based Lambda function will execute every 60 minutes. You can easily verify this by reviewing the function CloudWatch Logs for hourly entries.
Exercise 18.1: Update Auto Scale Group With Latest ImageId
In Chapter 8 we learned how to create an Auto Scaling group and corresponding Launch Configuration. In Chapter 15 we learned how to get the latest ImageId using SSM Parameter Store. In this exercise we will combine what we learned from those two chapters along with what we learned in this chapter to create a new Lambda function. This new function will automatically update our Auto Scale group with the latest ImageId whenever a new version is announced through a public SNS topic subscription.
Create the Script
You can see in the script that we find the latest public ImageId from Parameter Store and compare it to the value set in the Auto Scale groups Launch Configuration we created in Chapter 8. The two ImageId values are compared, and if they do not match, a new Launch Configuration is created. The new Launch Configuration is then assigned to the Auto Scale group.
Create the IAM Role
Publish the Lambda Function
Subscribe to the SNS Topic
Note This is a public SNS topic used by AWS to announce when new versions of public Amazon Machine Images have been released. This occurs at least once a month, and more details can be found at https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/windows-ami-version-history.html .
Permit SNS Invocation
In this exercise we learned how to build and deploy a Lambda function that will update our Auto Scaling group with a new Amazon Machine Image. We also learned how to subscribe to and enable SNS as a trigger for our Lambda function.
Summary
In this chapter, we saw how to install the prerequisites for creating and deploying PowerShell-Based Lambda functions. We saw how to create a script, understand its components, and deploy it as a Lambda function. Using a few cmdlets, we saw how to execute our function and review the output as well as parse resulting CloudWatch Logs. Finally, we demonstrated how to create and assign a schedule trigger for our function. Lambda is a truly powerful tool to execute PowerShell code without owning or managing a single server.