Running PowerShell On AWS Lambda
Posted on October 07, 2019
- and tagged as
- powershell,
- aws
Having previously written a few Amazon Echo Skills I knew AWS Lambda could run JavaScript and Python, but I recently found out it can also run PowerShell.
Requirements
PowerShell Core
The first requirement is that only PowerShell Core 6 and later are supported. One advantage of PowerShell Core is the its ability to run on Windows, Linux, macOS, and even ARM. Microsoft have installation instructions for each OS here.
In this post I’ll be using PowerShell Core v6.2.3.
PS C:\> $PSVersionTable
Name Value
---- -----
PSVersion 6.2.3
PSEdition Core
GitCommitId 6.2.3
OS Microsoft Windows 10.0.18362
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Installing PowerShell Core does not upgrade or replace the existing Windows PowerShell install. Due to backwards compatibility issues it is installed alongside Windows PowerShell.
PowerShell Core can be ran with the pwsh
command.
.NET Core 2.1 SDK
When we create PowerShell Lambda functions they are uploaded to AWS as a package in the form of a zip file that contains our code, any required modules, as well as the assemblies needed to run PowerShell Core.
This means we need to install the .NET Core 2.1 SDK which can also be downloaded from Microsoft. The specific version I’m using is v2.1.802 from here.
Install AWS PowerShell Modules
Finally, we need to install the AWS PowerShell and AWS Lambda PowerShell Core modules
Install-Module AWSPowerShell -Scope CurrentUser
Install-Module AWSLambdaPSCore -Scope CurrentUser
Install-Module AWSPowerShell.NetCore -Scope CurrentUser
Creating a deployment package
The AWSLambdaPSCore
module provides access to a number of templates which we can use to create the boilerplate we need for the deployment package.
Available templates can be viewed with the Get-AWSPowerShellLambdaTemplate
command.
PS C:\> Get-AWSPowerShellLambdaTemplate
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 be 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
Let’s create a package based on the Basic template, this is done with the New-AWSPowerShellLambda
cmdlet.
PS C:\temp> New-AWSPowerShellLambda -Template Basic -ScriptName TestAWSLambdaFunction
WARNING: This script requires the AWSPowerShell.NetCore module which is not installed locally.
WARNING: To use the AWS CmdLets execute "Install-Module AWSPowerShell.NetCore" and then update the #Requires statement to the version installed. If you are not going to use the AWS CmdLets then remove the #Requires statement from the script.
Created new AWS Lambda PowerShell script TestAWSLambdaFunction.ps1 from template Basic at C:\temp\TestAWSLambdaFunction
This command will create a directory under the current root with the name provided to the ScriptName
paramaeter.
PS C:\temp> dir .\TestAWSLambdaFunction\
Directory: C:\temp\TestAWSLambdaFunction
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 7/10/2019 5:34 PM 457 readme.txt
-a---- 7/10/2019 5:34 PM 852 TestAWSLambdaFunction.ps1
The contents of the default TestAWSLambdaFunction.ps1
file are as follows.
# 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='3.3.335.0'}
# Uncomment to send the input event to CloudWatch Logs
# Write-Host (ConvertTo-Json -InputObject $LambdaInput -Compress -Depth 5)
Two key lines are highlighted.
- We will have an input object called $LambdaInput
- The last object we output will be the returned value
With those in mind, let’s simply add Write-Output $LambdaInput | select *
to the bottom of the file. This will make our function echo back the $LambdaInput
object so we can see what data AWS Lambda has.
With those in mind, let’s modify the file to include two actions at the bottom, the first, to append a timestamp to the input object, and the second to return the modified input object.
$LambdaInput | Add-Member -MemberType NoteProperty -Name Timestamp -Value (Get-Date)
Write-Output $LambdaInput | select *
While this doesn’t accomplish much, it serves as a nice quick test.
Publishing the package to AWS Lambda
There are two options for publishing our function. We can either simply create a deployment zip file, which can be uploaded manually or using other tooling you may already have in place (terraform, etc).
The package can also be pushed directly up to AWS. Both options are covered below.
Creating a deployment zip
While the AWS Lambda PowerShell module allows for deployment directly to an AWS region, we may instead wish to simply generate the package zip file in case we have other deployment tooling available.
This section can be skipped if deploying directly to AWS Lambda.
PS C:\temp\TestAWSLambdaFunction> New-AWSPowerShellLambdaPackage -ScriptPath .\TestAWSLambdaFunction.ps1 -OutputPackage TestAWSLambdaFunction.zip
Staging deployment at C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction
Configuring PowerShell to version 6.1.1
Generating C# project C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction\TestAWSLambdaFunction.csproj used to create Lambda function bundle.
Generating C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction\Bootstrap.cs to load PowerShell script and required modules in Lambda environment.
Generating aws-lambda-tools-defaults.json config file with default values used when publishing project.
Copying PowerShell script to staging directory
Resolved full output package path as C:\temp\TestAWSLambdaFunction\TestAWSLambdaFunction.zip
Creating deployment package at C:\temp\TestAWSLambdaFunction\TestAWSLambdaFunction.zip
Restoring .NET Lambda deployment tool
Initiate packaging
When deploying this package to AWS Lambda you will need to specify the function handler. The handler for this package is: TestAWSLambdaFunction::TestAWSLambdaFunction.Bootstrap::ExecuteFunction. To request Lambda to invoke a specific PowerShell function in your script specify the name of the PowerShell function in the environment variable AWS_POWERSHELL_FUNCTION_HANDLER when publishing the package.
LambdaHandler PathToPackage PowerShellFunctionHandlerEnvVar
------------- ------------- -------------------------------
TestAWSLambdaFunction::TestAWSLambdaFunction.Bootstrap::ExecuteFunction C:\temp\TestAWSLambdaFunction\TestAWSLambdaFunction.zip AWS_POWERSHELL_FUNCTION_HANDLER
Deploying directly to AWS Lambda
Configuring an IAM role for Lambda execution
Create a new AWS IAM Role called lambda_basic_execution
and assign it the AWSLambdaBasicExecutionRole
policy from the existing default AWS IAM policies
Setting up AWS Authentication and Default Region
First we need to configure credentials the AWS Lambda PowerShell module can use to authenticate to AWS. This includes setting up an IAM user which is a little out of scope for this post, this will provide you with an API Access Key and an API SecretKey Key.
Initialize-AWSDefaultConfiguration -Region ap-southeast-2 -AccessKey "1234" -SecretKey "5678"
This should create a default AWS credential
PS C:\temp\TestAWSLambdaFunction> Get-AWSCredential -ListProfileDetail
ProfileName StoreTypeName ProfileLocation
----------- ------------- ---------------
default NetSDKCredentialsFile
Publishing to AWS
Publishing can be done using the Publish-AWSPowerShellLambda
cmdlet.
PS C:\temp\TestAWSLambdaFunction> Publish-AWSPowerShellLambda -ScriptPath .\TestAWSLambdaFunction.ps1 -Name TestAWSLambdaFunction -Region ap-southeast-2 -IAMRoleArn lambda_basic_execution
Staging deployment at C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction
Configuring PowerShell to version 6.1.1
Generating C# project C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction\TestAWSLambdaFunction.csproj used to create Lambda function bundle.
Generating C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction\Bootstrap.cs to load PowerShell script and required modules in Lambda environment.
Generating aws-lambda-tools-defaults.json config file with default values used when publishing project.
Copying PowerShell script to staging directory
Deploying to AWS Lambda
Restoring .NET Lambda deployment tool
Initiate deployment
Amazon Lambda Tools for .NET Core applications (3.3.1)
Project Home: https://github.com/aws/aws-extensions-for-dotnet-cli, https://github.com/aws/aws-lambda-dotnet
Executing publish command
... invoking 'dotnet publish', working folder 'C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction\bin\Release\netcoreapp2.1\publish'
... Disabling compilation context to reduce package size. If compilation context is needed pass in the "/p:PreserveCompilationContext=false" switch.
... publish: Microsoft (R) Build Engine version 16.2.32702+c4012a063 for .NET Core
... publish: Copyright (C) Microsoft Corporation. All rights reserved.
... publish: Restore completed in 1.2 sec for C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction\TestAWSLambdaFunction.csproj.
... publish: TestAWSLambdaFunction -> C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction\bin\Release\netcoreapp2.1\rhel.7.2-x64\TestAWSLambdaFunction.dll
... publish: TestAWSLambdaFunction -> C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction\bin\Release\netcoreapp2.1\publish\
Zipping publish folder C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction\bin\Release\netcoreapp2.1\publish to C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction\bin\Release\netcoreapp2.1\TestAWSLambdaFunction.zip
... zipping: Amazon.Lambda.Core.dll
... zipping: Amazon.Lambda.PowerShellHost.dll
<--output truncated -->
... zipping: Modules/AWSPowerShell.NetCore/3.3.335.0/CHANGELOG.md
... zipping: Modules/AWSPowerShell.NetCore/3.3.335.0/PSGetModuleInfo.xml
Created publish archive (C:\Users\md\AppData\Local\Temp\TestAWSLambdaFunction\bin\Release\netcoreapp2.1\TestAWSLambdaFunction.zip).
Creating new Lambda function TestAWSLambdaFunction
New Lambda function created
We can confirm deployment by checking the AWS Console.
Publishing changes
The same command can be ran to update the function. The final line of the output will indicate a code update rather than function creation.
Updating code for existing function TestAWSLambdaFunction
Running a test
Once live, we can run a Test function in the AWS console to see whether our function behaves as expected. The simple ‘Hello World’ test template will suffice.
Looks good, our input is echoed back to us, along with a timestamp.
Errors
Here’s a brief summary of some of the errors I got while writing this and how they were corrected.
.NET SDK Missing, incompatible version, or incorrect architecture installed
The installed .NET Core SDK does not meet the minimum requirement to build the PowerShell Lambda package bundle. Download the .NET Core 2.1 SDK from https://www.microsoft.com/net/download
At C:\Users\md\Documents\PowerShell\Modules\AWSLambdaPSCore\1.2.0.0\Private\_DeploymentFunctions.ps1:547 char:9
+ throw 'The installed .NET Core SDK does not meet the minimum ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (The installed .NET \u2026ft.com/net/download:String) [], RuntimeException
+ FullyQualifiedErrorId : The installed .NET Core SDK does not meet the minimum requirement to build the PowerShell Lambda package bundle. Download the .NET Core 2.1 SDK from https://www.microsoft.com/net/download
To validate what .NET SDKs you have installed, you can use dotnet --info
, it should look as follows
PS C:\temp\TestAWSLambdaFunction> dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 2.1.802 Commit: 177d0b2525
Runtime Environment:
OS Name: Windows
OS Version: 10.0.18362
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\2.1.802\
Host (useful for support):
Version: 2.1.13
Commit: 1a165a1588
.NET Core SDKs installed:
1.0.1 [C:\Program Files\dotnet\sdk]
1.0.4 [C:\Program Files\dotnet\sdk]
2.1.802 [C:\Program Files\dotnet\sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 1.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 1.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 1.1.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 1.1.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
To install additional .NET Core runtimes or SDKs:
https://aka.ms/dotnet-download
If you get an error like the one above, you’ve probably installed the incorrect .NET SDK architecture for your system, or you’ve entirely skipped the SDK install (make sure you install the SDK, not the runtime).
Authentication not configured correctly
Error retrieving configuration for function TestAWSLambdaFunction: The operation was canceled.
Error publishing PowerShell Lambda Function: -1
The error above indicates an issue with local AWS credentials created during Initialize-AWSDefaultConfiguration
configuration.
AWS Account does not have correct permissions
Error retrieving configuration for function TestAWSLambdaFunction: User: arn:aws:iam::151292711111:user/deployment is not authorized to perform: lambda:GetFunctionConfiguration on resource: arn:aws:lambda:ap-southeast-2:151292711111:function:TestAWSLambdaFunction
Unknown error executing command: One or more errors occurred. (User: arn:aws:iam::151292711111:user/deployment is not authorized to perform: iam:ListRoles on resource: arn:aws:iam::151292711111:role/)
Unknown error executing command: One or more errors occurred. (User: arn:aws:iam::151292711111:user/deployment is not authorized to perform: iam:GetRole on resource: role lambda_basic_execution)
Error creating Lambda function: User: arn:aws:iam::151292711111:user/deployment is not authorized to perform: iam:PassRole on resource: arn:aws:iam::151292711111:role/lambda_basic_execution
Check that your deployment account has access to Lambda and IAM. I configured both IAMFullAccessIAMFullAccess
and AWSLambdaFullAccess
policies.
Error creating Lambda function: 1 validation error detected: Value 'arn:aws:iam::151292711111:user/lambda_basic_execution' at 'role' failed to satisfy constraint: Member must satisfy regular expression pattern: arn:(aws[a-zA-Z-]*)?:iam::\d{12}:role/?[a-zA-Z_0-9+=,.@\-_/]+
You created an IAM User instead of an IAM Role. Don’t worry, you’re not the only one.