How to Write Serverless PowerShell Code for AWS Lambda

Love them or hate them, Serverless functions are a thing and will likely be around for a while. And while I personally enjoy using them to “glue together” various services that I have running on public cloud providers, my preference is to use an existing SDK whenever possible. In this case, I wanted to expand my library of AWS Lambda functions to include code written in PowerShell to better understand how the pipeline works, what sort of cold start times to expect for .NET, and to just generally take advantage of the years of sweat equity poured into the Rubrik PowerShell SDK.

In this post, I’ll walk through the various configuration steps necessary to write some basic functionality and publish a new function using PowerShell in AWS Lambda. The motivation for this was largely driven from lack of finding anything coherent on the topic, which I’m sure has to do with most folks preferring to use Go or Python for Lambda.

AWS Lambda Development Environment Requirements

The AWS documentation starts rather strong with the “Setting Up a PowerShell Development Environment” post. In a nutshell, you’ll need to grab:

  1. The PowerShell “Core” code (not Windows PowerShell)
  2. The .NET Core 2.2 SDK (the version linked, 2.1, is out of date as of this post)
  3. The AWSLambdaPSCore module (Install-Module AWSLambdaPSCore)

Are you ready to start? Nope.

Setting your AWS Credentials in PowerShell

I’d also suggest going a bit further by installing the AWSPowerShell.NetCore module (currently 3.3.485.0 as of this post), which they don’t mention in the development environment post. It contains a bazillion cmdlets used for interacting with AWS. The two that you will specifically need are:

  • Set-DefaultAWSRegion to specify your default AWS Region.
  • Set-AWSCredential to specify your AccessKey and SecretKey.

Without arguments for the parameters in these functions, most of the AWS documentation fails when attempting to publish a function and is quite frustrating. A hat tip to Norm Johanson for the announcement post where I found this.

Creating Your First PowerShell Function for Lambda

Most of the remaining documentation is fairly simple to grok. Use the “AWS Lambda Deployment Package in PowerShell” page to follow a few simple steps to generate your first basic template.

I followed along with the example for a bit and realized two things:

  1. $LambdaInput is a neat, hashtable-ish object that is used to pass along the input received by Lambda into your PowerShell function. It’s also noted in the Basic template’s header comments.
  2. For my needs, the Requires -Modules @{ModuleName='AWSPowerShell.NetCore';ModuleVersion='3.3.335.0'} requirement can safely be removed as it adds around 13 MB of overhead to the function and adds time to the compilation of the zip file (we’ll go over this in a bit).

Using #Requires for Module Dependencies

Before continuing with the documented examples, I also wanted to make sure that whenever I packaged up the function it included the Rubrik PowerShell SDK. To do this, just add another #Requires -Modules statement and add your dependencies.

 #Requires -Modules Rubrik

The Publish-AWSPowerShellLambda cmdlet will use the #Requires statement(s) to ensure that any dependencies are included in the zip file that you generate next. If you need a specific version of a module, make sure to call that out.

Note: If a matching version of the #Requires module is installed on your workstation, that copy will be used.

Example Lambda PowerShell Function Code

With all this out of the way, it’s time for the big reveal. What does a bit of PowerShell code in Lambda actually look like? Prepare to be underwhelmed.

In this example, I just wanted to be able to pass along a few inputs – the virtual machine name, SLA Domain, and primary cluster identifier – to receive back a little statement about the server’s name, IP address, and cluster. I then toss this over to AWS Lex as a chat bot operation.

#Requires -Modules Rubrik
Connect-Rubrik -Server $env:SERVER -Username $env:USER -Password (ConvertTo-SecureString $env:PASS -AsPlainText -Force) -Verbose
[Array]$List = Get-RubrikVM -Name $LambdaInput.Name -SLA $LambdaInput.SLA -PrimaryClusterID $LambdaInput.PrimaryClusterID -Verbose | Select-Object -Property name,ipAddress,clusterName
return "$($List[0].name) is in the $($List[0].clusterName) cluster at IP $($List[0].ipAddress)"

The code is a bit verbose just to show what’s going on:

  1. Require loading the Rubrik module.
  2. Connect to the Rubrik cluster using environmental variables.
  3. Get a list of any virtual machines matching my information and store it in an array.
  4. Take the first result of the array (hence the $List[0]) and prepare a human-readable statement about it.

Publishing Your Lambda PowerShell Function to AWS

The last step is to publish your function to AWS Lambda. In the documented example, you’ll use:

Publish-AWSPowerShellLambda -ScriptPath .\MyFirstPSScript.ps1 -Name  MyFirstPSScript -Region us-east-1

For new functions, I would also suggest specifying the role you’d like to use rather than waiting for the IAM Role prompt (shown below) to interrupt you. In my case, I’m using the roxie_lambda service-role since it can control my VPC networking configuration to retrieve information from my Rubrik cluster in California.

Choose wisely. Or just set it up later.

Once invoked, I get a short little blurb about my server.

Hello, server! ?‍♂️

Other Design Considerations for PowerShell Lambda Functions

As I further hacked away at my PowerShell function, a few other questions rattled around in my noodle. I’ll record each of the thoughts independently below to make it easier to find anything you may be searching for about PowerShell Lambda functions.

Returning Results from PowerShell to Lambda

The “return” from your function is the object passed back as the result. However, actions like -Verbose and Write-Host do get captured in the logs, which is nice. If you are used to writing smaller, lightweight functions that do a limited amount of logic (preferably one logical flow), there isn’t much difference with Lambda.

It’s worth noting that you’ll want to properly format your return into something that Lambda or the upstream trigger is looking for, such as converting a PowerShell hashtable into a JSON object.

Environmental Variables from Lambda to PowerShell

Environmental variables aren’t really documented anywhere, but work as you would expect. If you define a key / value pair of foo:bar then you can use the standard call of $env:foo to return bar. This is extremely handy for passing along regular or encrypted content into your function.

Let’s use FUN:TIMES and ask Lambda for the results of $env:FUN

Because who doesn’t love fun times?

When $env:FUN is invoked, the return is:

Success!

Summary

Compared to using Azure Functions with PowerShell or pretty much any other language with AWS Lambda, there isn’t much pleasure in jumping through all these hoops to use PowerShell in AWS Lambda. However, I do enjoy seeing all the places where PowerShell can be leveraged and did find this experience to be educational.

If you have questions or corrections, please let me know.