Powershell Functions in AWS SAM Local

It’s hard to explain why but I find it delightful to contort powershell into new environments and use cases it really, really doesn’t seem to make much sense. A few of my favorites are linux docker containers, modularized and unit testable applications, and AWS Lambda functions.
Today I’m trying a new spin on one of those — AWS Lambda functions, but running behind AWS Sam Local. Let’s dive in!
Credit where credit is due, there are a lot of great resources for getting started with AWS Sam local, .NET in AWS Lambda, and Powershell in Lambda. But I couldn’t find anything about running powershell functions in AWS SAM Local. Could that be because no one has a need or desire to do so? Yeah, probably, but I didn’t let that stop me! Let’s go!
Start off by downloading .NET from Microsoft — I’m on windows, but you should be able to pick whatever version you prefer. I’m on 5.0
for this post.
Then get through enough of the SAM Local getting started blog to be able to run a sam local invoke
command. The example was in python, but it will prove to you that you can run a SAM local function.
Personally I didn’t really want to spin up a dynamo DB table, so I modified the example a little to make it easier. At the end of the day my template.yaml looked like this
AWSTemplateFormatVersion : '2010-09-09'Transform: AWS::Serverless-2016-10-31
Resources:
VoteSpacesTabs:
Type: "AWS::Serverless::Function"
Properties:
Runtime: python3.8
Handler: lambda_function.lambda_handler
Policies: AmazonDynamoDBFullAccess
Environment:
Events:
Vote:
Type: Api
Properties:
Path: /
Method: get
And my very simple python script looked like
import os
import jsondef lambda_handler(event, context):
print(event)
return {'body': "testing!"}
And the output looks like I would expect

That means I’ve got sam local and docker installed and running! Time to jump into the .NET world. I grabbed the Lambda .NET runtime identifier from this page and updated the template to use that. Then I found the tutorial for writing a .NET lambda function and used the dotnet cli to create a new template.
I used this blog to get a sample .NET function up and running. This was really helpful because at the end of the day your powershell function will be compiled down to a .DLL just like the c# functions. I did have to run sam build
to compile the c# function.
In the end my project and build directory looked like this (shamelessly stealing the API name from the first example)

And after I inserted the template.yaml
AWSTemplateFormatVersion : '2010-09-09'Transform: AWS::Serverless-2016-10-31
Resources:
VoteSpacesTabs:
Type: "AWS::Serverless::Function"
Properties:
Runtime: dotnetcore3.1
CodeUri: ./src/MyFunction/
Handler: MyFunction::MyFunction.Function::FunctionHandler
Policies: AmazonDynamoDBFullAccess
Environment:
Events:
Vote:
Type: Api
Properties:
Path: /
Method: get
And the event.json file (the escaping is important — without it I got deserialization errors)

I was able to run a very similar command to trigger this function

Tada!

So now we have a functioning sam local environment, and an example of running a .NET lambda function. Let’s see if we can get it working with powershell!
The getting started docs are pretty helpful. I used this command to get myself a basic lambda environment
New-AWSPowerShellLambda -ScriptName MyFirstPSScript -Template Basic
Out of the box the script is blank so I updated it to return “hello world” and write a similar string to the logs
#Requires -Modules @{ModuleName='AWS.Tools.Common';ModuleVersion='4.0.5.0'}write-host "Hello world!!!";Write-Host (ConvertTo-Json -InputObject $LambdaInput -Compress -Depth 5);
# throw "this is an exception!";
return "hello world!";
Now I need it to look like the built version of my example c# function. To do that I used New-AWSPowerShellLambdaPackage
. From the docs that function is to create a powershell lambda deployment package that can you can use in a pipeline. I ran it with these parameters
New-AWSPowerShellLambdaPackage -ScriptPath .\MyFirstPSScript.ps1 -OutputPackage myfirstscripts.zip
This gave me a zip in my directory, and when I extract the archive it looks like I’m on the right track

I see a couple of .DLLs like I was expecting

And conveniently the output from the packaging command tells me the handler for the powershell function

So now I tweak my sam template and copy it to the directory
AWSTemplateFormatVersion : '2010-09-09'Transform: AWS::Serverless-2016-10-31
Resources:
VoteSpacesTabs:
Type: "AWS::Serverless::Function"
Properties:
Runtime: dotnetcore3.1
CodeUri: myfirstscript
Handler: MyFirstPSScript::MyFirstPSScript.Bootstrap::ExecuteFunction
Policies: AmazonDynamoDBFullAccess
Environment:
Events:
Vote:
Type: Api
Properties:
Path: /
Method: get
It looks like I’m ready to invoke, but when I do I get a timeout error

Which is interesting because there isn’t a whole lot happening here, but the powershell function might take some time to setup. I updated the Timeout
parameter from 3 -> 300 seconds (go big or go home)
Resources:
VoteSpacesTabs:
Type: AWS::Serverless::Function
Properties:
Timeout: 300
Runtime: dotnetcore3.1
.....
And now I get the output I expected

Looks like it takes just under 7 seconds for the function to start up and run the function.
And there you have it! To summarize the steps
- Install your tools (.NET, AWS Sam, SAM local, and any dependencies)
- Create an example sam local python function because it’s easy
- Create an example powershell function
- Package it into a zip using
New-AWSPowershellLambdaPackage
- Unzip it so you have access to it from SAM
- Update your
template.yaml
to point to it - Run away!
I’m sure if you were motivated you could automate steps 4 and 5 in a single script.
Should you do this
I think the real question is should you run powershell in lambda. And the answer is….maybe?
I’ve used several different AWS SDKs (.NET, python, ruby, Java, and Node) and the Powershell tools are the easiest implementation to get started with. For simple activities like starting and stopping instances, checking S3 objects, or other general AWS management tools they’re a pretty good fit.
If you’re develping those kinds of solutions having a way to test them locally is great!
If you’re building more complex lambda functions, then a language more suited to your use case is likely a better way to go.