AWS Developer Tools Blog

Deploying PowerShell-based Lambda with AWS CloudFormation

We recently announced AWS Lambda support for PowerShell. In our initial blog post, we showed how you can use the new AWSLambdaPSCore module to deploy PowerShell scripts to Lambda. In this blog post, we talk about how you can integrate PowerShell-based Lambda functions with AWS CloudFormation.

It’s common to want to deploy a collection of Lambda functions and other AWS resources together as a single unit. For example, in our previous post, we developed a PowerShell-based Lambda function that removed any open Remote Desktop (RDP) ports in our Amazon EC2 security groups. Then we created an Amazon CloudWatch scheduled event to trigger the Lambda function. In the example, we deployed the function and configured CloudWatch as two separate tasks. By using AWS CloudFormation we can deploy the Lambda function and configure CloudWatch as a single task, with any additional Lambda functions or AWS resources we decide to add in the future.

AWS CloudFormation

When we deploy PowerShell-based Lambda functions with AWS CloudFormation, we don’t use the Publish-AWSPowerShellLambda cmdlet like we did in the previous blog post. Instead, we use the New-AWSPowerShellLambdaPackage cmdlet that creates the Lambda deployment package as a .zip file. For example, if we run the following command that contains our RDPLockDown.ps1 script created in the previous post, we get the Lambda deployment package RDPLockDown.zip with our script and all of the PowerShell-required modules ready to deploy.

New-AWSPowerShellLambdaPackage -ScriptPath RDPLockDown.ps1 -OutputPackage RDPLockDown.zip

Lambda Function Handler

When you deploy a Lambda function, you must specify the function handler string that tells the Lambda service the entry point into the deployment bundle. When you use Publish-AWSPowerShellLambda, that is handled for you transparently. If you are deploying the deployment bundle using other tools, like AWS CloudFormation, you need to know what to set the function handler parameter to. When you execute New-AWSPowerShellLambdaPackage take special notice of the output, which tells what to set the function handler parameter to. It’s usually in the pattern <script-name>::<script-name>.Bootstrap::ExecuteFunction. In our RDPLockDown.ps1 walkthrough, the function handler will be RDPLockDown::RDPLockDown.Bootstrap::ExecuteFunction.

AWS CloudFormation template

To deploy with AWS CloudFormation we need a template. Here is our template that defines a serverless function and an AWS Identity and Access Management (IAM) role to give our Lambda function permissions to access other AWS resources.


{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Transform": "AWS::Serverless-2016-10-31",
  "Resources": {

    "RDPLockDownFunction": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "Handler": "RDPLockDown::RDPLockDown.Bootstrap::ExecuteFunction",
        "Runtime": "dotnetcore2.1",
        "CodeUri": "RDPLockDown.zip",
        "MemorySize": 512,
        "Timeout": 30,
        "Role": {"Fn::GetAtt" : [ "LambdaRole", "Arn"]},
        "Environment": {
          "Variables": {
            
          }
        },
        "Events": {
          "ScanInterval": {
            "Type": "Schedule",
            "Properties": {
              "Schedule": "rate(5 minutes)"
            }
          }
        }
      }
    },

    "LambdaRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "ManagedPolicyArns": [
          "arn:aws:iam::aws:policy\/service-role\/AWSLambdaBasicExecutionRole"
        ],
        "Policies": [
          {
            "PolicyName": "EC2Access",
            "PolicyDocument": {
              "Version": "2012-10-17",
              "Statement": [
                {
                  "Action": [
                    "ec2:DescribeSecurityGroups",
               "ec2:RevokeSecurityGroupIngress"
                  ],
                  "Effect": "Allow",
                  "Resource": "*"
                }
              ]
            }
          }
        ],
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Action": [
                "sts:AssumeRole"
              ],
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "lambda.amazonaws.com"
                ]
              }
            }
          ]
        }
      }
    }
  }
}

The AWS::Serverless::Function resource is actually a meta resource defined in the AWS Serverless Application Model (AWS SAM) transformation which will be transformed into several AWS resources. This simplifies expressing serverless applications and their event sources. Notice the Events section is where the CloudWatch schedule event is declared to run every five minutes.

The Handler property is set to the function handler value returned by the New-AWSPowerShellLambdaPackage cmdlet.

The last thing to point out is that the CodeUri property is set to the local path of the Lambda deployment bundle. When we deploy, this gets updated to a location in Amazon S3.

If you’re developing a serverless application with multiple PowerShell-based Lambda functions, run New-AWSPowerShellLambdaPackage on each PowerShell script. Specify their deployment bundle .zip file and function handler in the AWS CloudFormation template for an AWS::Serverless::Function resource.

Deployment

To deploy our template, we’ll use two commands from the AWS CLI.

The first command uses the aws cloudformation package command. This command uploads our local paths to the deployment bundles to Amazon S3, and then updates the CodeUri to the location of the bundles in S3. Then it saves the template with the new locations in the CodeUri to the file indicated by the –output-template-file.

aws cloudformation package --template-file serverless.template --s3-bucket rdp-lockdown-blog --output-template-file updated.template

The second command takes our template that is updated to the locations of the deployment bundles in S3 and deploys the template to AWS CloudFormation. Because the template is creating an IAM role to give permissions to the Lambda function, we need to pass in the extra parameter –capabilities CAPABILITY_IAM to explicitly give AWS CloudFormation permission to create an IAM role.

aws cloudformation deploy --template-file updated.template --stack-name RDPLockDown --capabilities CAPABILITY_IAM

Once that command is complete, our AWS CloudFormation stack is created and our Lambda function will execute every five minutes.

A great benefit of using AWS CloudFormation is that when we’re done testing, we can delete the AWS CloudFormation stack, which deletes all of the AWS resources that were created as part of the stack. The command below from the AWS CLI will delete the AWS CloudFormation stack.

Summary

In this post you learned how the New-AWSPowerShellLambdaPackage cmdlet can create a Lambda deployment bundle for a PowerShell script, which you can then use in your whatever deployment system you use. We looked at one of the most common use cases with the deployment bundle, which is to deploy with AWS CloudFormation with other AWS resources.

We always love hearing your feedback. For PowerShell-based Lambda, you can reach us on our GitHub repository.