Lambda and Powershell

Published on 07 October 2019

Lambda!

I am sure you have heard about it, but it allows you to run code with a server/instance, so "serverless", which is pretty neat. I have played aroud with it a little previously with Python but I know way more about Powershell than Python, so I wanted to get a simple Powershell Lambda function running.

So there are some prerequisites:

  1. Powershell Core 6.0

  2. .NET Core 2.1 SDK

  3. AWSPowerShell.NetCore module

  4. AWSLambdaPSCore module.

If you don't have Powershell Core, now is a great time. It is the future of Powershell, and all developement is being done for this, rather than "traditional" Powershell. You have probably heard the rest, it is cross-platform, built on .NET, etc., so really, there is no excuse not to install it. Additionally, it will integrate with VS Code, and you might find that a lot of your existing scripts run on Powershell Core without too many modifications. If not, now is a good time to start modifying them.

You also need the .NET Core SDK (2.1 is the minimum, I installed 3.0).

Once you have those done, you can fire up a Powershell Core window and grab the AWS modules:

install-module AWSPowershell.NetCore, AWSLambdaPSCore

That is it, prerequisites met.

Permissions

Before you run your lamba function, you need to ensure that it has permissions to do what you want it to do. So you need to create an IAM policy to access EC2, with the rights to write>startinstance, and write>stopinstance. This will allow the job to do the only what you want it to do. Call this EC2startstop or similar.

There are two other things permissions-wise that you probably want to consider though. You may also want to add AWSLambdaBasicExecutionRole, if you want it to be able to write to Cloudwatch (which I would recommend). Lastly, if you are going to do anything with tags, you also want to add read>describetags (and maybe list>describeinstances and list>describeinstancestatus if you are going to look at that).

IAMettings

Next, create a new role for the Lambda function and give it permissions to use the EC2startstop policy that we just created.

Code

So, now we have the role, we have the code components, we can create the actual Lambda function. Just one more thing we need to do, is ensure we have the credentials set to connect to the environment. You do this with the same command as normal Powershell:

    Set-AWSCredential -access AKIA**************** -secret zNxmmartSbYGA***************************

You need to use your credentials obviously. If you don't have these, you should have created them when the account was created. If you forgot, you can sort revoke/create in IAM.

Back to creating the Lambda function though, this is (finally) the easy bit.

    New-AWSPowershellLambda -scriptname StopEC2Instance -template Basic

And that is it, you have created a function. You can of course use your own name, and you can of course use a different template, but the basic one is a a good start here. When your function is created, you can edit your StopEC2Instance.ps1 file (or whatever you called the function), and edit like any other Powershell file.

    # 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.590.0'}

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

    $search = @{
        Name="tag:Environment";Value="Development" 
    }
    $DevelopmentInstances = Get-EC2Instance -Filter $search 
    $DevelopmentInstances | stop-ec2instance

Really, all we are doing is added the last few lines (from the $search onwards), finding any EC2 instance with a tag called "Environment" and a value of "Development," and stopping it. We save the file, and just upload it to AWS using the Publish-AWSPowerShellLambda command.

Publish

We can run it in several ways:

invoke-LMFunction StopEC2Instance -region "us-east-1"

or

aws lambda invoke --function-name StopEC2Instance out    

We should get a 200 back:

Result

Done, our instance should be stopped.

The next step should be to trigger it with Cloudwatch!

Troubleshooting

The two most common issues I would expect are:

  1. problems with your credentials This will return something like "Error publishing PowerShell Lambda Function -1". In that case, confirm that your credentials are correct, and that you have rights to publish.

  2. When running the function, "Invoke-LMFunction : Function not found: arn:aws:lambda:us-east-1:157624764727:function:StartEC2Instance". In the case, you need you published to the region you are running from. The "-region" switch is available on AWS commands, and you can also set a default region within your PowerShell session too.

Apart from that, check your Cloudwatch logs, and you should get a fair amount of information there about any possible error causes.

comments powered by Disqus