Infrastructure at your Service

Daniel Westermann

Publishing a PowerShell script to AWS Lambda

By June 16, 2020 AWS No Comments

I’ve done some Lambda functions with Python in the past and it was quite easy to publish that to Lambda (by just uploading a zip file with all my code and dependencies). You might ask yourself why I want to do that with PowerShell but the reason is quite simple: There was a requirement at a customer to automatically collect all the KBs that are installed in the AWS Windows WorkSpaces for compliance reasons. Doing that for EC2 or on-prem instances is quite easy using Lambda for Python against SSM when you are using SSM for patching, but if you want to list the installed KBs of your deployed AWS WorkSpaces you need a different way of doing that. After discussing that with AWS Support it turned out that the easiest solution for this is to use the PowerShell Get-HotFix module remotely against the AWS WorkSpaces. Easy, I thought, when I can deploy Python code in Lambda I can easily do this for PowerShell as well. But this is definitely not true as the process is quite different. So, here we go …

The first bit you need to prepare is a PowerShell development environment for AWS. As I am running Linux (KDE Neon, if you want to know exactly), and PowerShell is available on Linux since quite some time, I’ll be showing how to do that on Linux (the process is more or less the same for Windows though).
Obviously PowerShell needs to be installed and this is documented by Microsoft quite well, no need to further explain this. Basically it is matter of:

$ wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb
$ sudo dpkg -i packages-microsoft-prod.deb
$ sudo apt-get update
$ sudo add-apt-repository universe
$ sudo apt-get install -y powershell

… and that’s it (take care to follow the steps for your Linux distribution). Once that is done PowerShell can be started:

$ pwsh
PowerShell 7.0.2
Copyright (c) Microsoft Corporation. All rights reserved.

https://aka.ms/powershell
Type 'help' to get help.

PS /home/dwe> 

The first additional module you’ll need is AWSLambdaPSCore:

PS /home/dwe> Install-Module AWSLambdaPSCore -Scope CurrentUser

Untrusted repository
You are installing the modules from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the 
Set-PSRepository cmdlet. Are you sure you want to install the modules from 'PSGallery'?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "N"): Y

Usually you want to work with other AWS Services in your Lambda code so it is recommended to install the AWS.Tools.Installer module as it provides a convenient way for installing the various tools required for working with the various AWS services. In addition the AWSPowerShell.NetCore module is required:

PS /home/dwe> Install-Module -Name AWS.Tools.Installer -Force    
PS /home/dwe> Install-Module -name AWSPowerShell.NetCore -Force 

Now, dependend on which AWS services you want to work with, just install what you need (in this example EC2, S3 and WorkSpaces):

PS /home/dwe> Install-AWSToolsModule AWS.Tools.EC2,AWS.Tools.S3 -CleanUp -Force                                                                             
Installing module AWS.Tools.EC2 version 4.0.6.0                                                                                                             
Installing module AWS.Tools.S3 version 4.0.6.0                                                                                                              
PS /home/dwe> Install-AWSToolsModule AWS.Tools.Workspaces -CleanUp -Force                                                                                   
Installing module AWS.Tools.WorkSpaces version 4.0.6.0  

Once you have that ready you can use the AWS tools for PowerShell to generate a template you can start with:

PS /home/dwe> 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


PS /home/dwe> cd ./Documents/aws
PS /home/dwe/Documents/aws> New-AWSPowerShellLambda -ScriptName MyFirstPowershellLambda -Template Basic
Configuring script to use installed version 4.0.6.0 of (@{ ModuleName = 'AWS.Tools.Common'; ModuleVersion = '4.0.5.0' }.Name)
Created new AWS Lambda PowerShell script MyFirstPowershellLambda.ps1 from template Basic at /home/dwe/Documents/aws/MyFirstPowershellLambda

PS /home/dwe/Documents/aws/MyFirstPowershellLambda> ls
MyFirstPowershellLambda.ps1  readme.txt

The generated template is quite simple but it gives you an idea how to start:

PS /home/dwe/Documents/aws/MyFirstPowershellLambda> cat ./MyFirstPowershellLambda.ps1
# 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 AWS.Tools.S3 module, add a "#Requires" statement
# indicating the module and version. If using an AWS.Tools.* module the AWS.Tools.Common module is also required.

#Requires -Modules @{ModuleName='AWS.Tools.Common';ModuleVersion='4.0.6.0'}

# Uncomment to send the input event to CloudWatch Logs
# Write-Host (ConvertTo-Json -InputObject $LambdaInput -Compress -Depth 5)

Just add the modules for the specific AWS services you want to work with in the “#Requires” section (you need to install them before of course) and write your script:

PS /home/dwe/Documents/aws/MyFirstPowershellLambda> cat ./MyFirstPowershellLambda.ps1
# 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 AWS.Tools.S3 module, add a "#Requires" statement
# indicating the module and version. If using an AWS.Tools.* module the AWS.Tools.Common module is also required.

#Requires -Modules @{ModuleName='AWS.Tools.Common';ModuleVersion='4.0.6.0'}
#Requires -Modules @{ModuleName='AWS.Tools.S3';ModuleVersion='4.0.6.0'}
#Requires -Modules @{ModuleName='AWS.Tools.EC2';ModuleVersion='4.0.6.0'}

# Uncomment to send the input event to CloudWatch Logs
# Write-Host (ConvertTo-Json -InputObject $LambdaInput -Compress -Depth 5)
Write-Output "Test"

The AWS documentation for the PowerShell Cmdlets is here.

Assuming that the script is completed (the above script does a simple print to the console) you need to deploy it to Lambda. For Python all you need to do is to zip your code and upload that to AWS Lambda. For PowerShell you need to call the “Publish-AWSPowerShellLambda” module passing in the script, a name for the Lambda function and the AWS region you want to have the function deployed to:

PS /home/dwe/Documents/aws/MyFirstPowershellLambda> Publish-AWSPowerShellLambda -ScriptPath ./MyFirstPowershellLambda.ps1 -Name MyFirstPowershellLambda  -Region eu-central-1

… and this will fail with:

Get-Command: /home/dwe/.local/share/powershell/Modules/AWSLambdaPSCore/2.0.0.0/Private/_DeploymentFunctions.ps1:544
Line |
 544 |      $application = Get-Command -Name dotnet
     |                     ~~~~~~~~~~~~~~~~~~~~~~~~
     | The term 'dotnet' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name,
     | or if a path was included, verify that the path is correct and try again.

Exception: /home/dwe/.local/share/powershell/Modules/AWSLambdaPSCore/2.0.0.0/Private/_DeploymentFunctions.ps1:547
Line |
 547 |          throw '.NET Core 3.1 SDK was not found which is required to b …
     |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | .NET Core 3.1 SDK was not found which is required to build the PowerShell Lambda package bundle. Download the .NET Core 3.1 SDK from
     | https://www.microsoft.com/net/download

The error message is quite clear: You need to install the “.NET Core 3.1 SDK” but as we added the Microsoft repositories above this is just a matter of (again, adjust for your package manager):

$ sudo apt-get install -y dotnet-sdk-3.1

Trying the same again and this time it succeeds:

PS /home/dwe/Documents/aws/MyFirstPowershellLambda> Publish-AWSPowerShellLambda -ScriptPath ./MyFirstPowershellLambda.ps1 -Name MyFirstPowershellLambda  -Region eu-central-1
Staging deployment at /tmp/MyFirstPowershellLambda
Configuring PowerShell to version 7.0.0
Generating C# project /tmp/MyFirstPowershellLambda/MyFirstPowershellLambda.csproj used to create Lambda function bundle.
Generating /tmp/MyFirstPowershellLambda/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
...
... zipping:   adding: Namotion.Reflection.dll (deflated 58%)
... zipping:   adding: System.Diagnostics.PerformanceCounter.dll (deflated 60%)
... zipping:   adding: MyFirstPowershellLambda.ps1 (deflated 53%)
... zipping:   adding: System.Management.dll (deflated 62%)
... zipping:   adding: Markdig.Signed.dll (deflated 62%)
... zipping:   adding: libpsl-native.so (deflated 69%)
...
Creating new Lambda function MyFirstPowershellLambda
Enter name of the new IAM Role:
dwe-tmp-role
...
Select IAM Policy to attach to the new role and grant permissions
    1) AWSLambdaFullAccess (Provides full access to Lambda, S3, DynamoDB, CloudWatch Metrics and  ...)
    2) AWSLambdaReplicator
...
1
Waiting for new IAM Role to propagate to AWS regions
...............  Done
New Lambda function created

Heading over to the AWS console we can see that the function is there:

Hope this helps…

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Daniel Westermann
Daniel Westermann

Principal Consultant & Technology Leader Open Infrastructure