Defining Lambda Resources in CloudFormation
This tutorial references the code in the aws-connectedcar-dotnet-serverless repository. If you're new to this course, see the introduction for information about setting up your workstation and getting the sample code.
In this tutorial we’re going to look at the CloudFormation resources used to define and deploy Lambdas. We’ll start by looking at the templates for the SAM version of the sample code, and then we’ll see what’s different with the OpenAPI version.
Defining Lambda Execution Roles
Before your Lambdas can do anything useful, there needs to be an execution role they can assume at run-time, with policies attached that grant access to the resources they will target. These policies should grant least-privilege access, meaning they should allow Lambdas to perform only the minimally required actions, and do so only for the specifically targeted resources.
To show how this is done in the sample code, let’s first look at the services.yaml template. This is the template that defines all the target resources with which the Lambdas will interact, like the Cognito User Pool and the DynamoDB tables. To make it easy to reference these resources in an IAM policy, we define this policy at the bottom of this same template where these resources are defined:
As you can see, defining least privilege access like this results in a larger policy, because you need to itemize the specific actions and resources.
Once you have a policy like this defined, you then need to attach it to an execution role that will be assumed by the Lambdas at run-time. Here’s the role that’s defined in the sample code, from the roles.yaml template. Note that the ARN for the above policy is returned by the outputs of the services.yaml template and is passed into the parameters of this roles.yaml template by the master.yaml parent template:
This role contains an AssumeRolePolicyDocument that in turn contains a Statement, and it’s the Statement that allows the principal (the Lambda service) to perform the action. The action in this case is simply the assumption of the role. Below the policy document, there's a list of the managed policy ARNs. These are the policies that will be attached to the role, which in this case includes the custom-defined policy that allows CloudWatch logging, and the services policy described above, which you can see referenced on line 48. As you can see, this list also includes two pre-defined AWS policies that enable X-Ray tracing and Lambda Insights for CloudWatch.
Defining Global Properties for Lambdas
Let’s now look at some of the Lambda resources that are defined in the admin.yaml template, starting with the Globals element. This element lets you define properties that are applied to all the resources of a given type in a SAM template. Several properties are defined this way in the admin.yaml template. Note that these properties can still be over-ridden by individual Lambda resources:
Here's more information about each of these properties:
Runtime
This identifies which of the supported platform runtimes is used for the Lambdas.
CodeUri
This is the S3 bucket and key to the zip file into which the Lambdas are packaged and uploaded.
MemorySize
This property actually specifies memory size and compute power, since these are combined with Lambdas.
Timeout
This is the request timeout, in seconds. Be aware that API Gateway, from which the requests for the Lambdas are being proxied, has a maximum timeout of 30 seconds.
Environment Variables
These are the custom environment variables that are accessible from code in the Lambdas. As you can see, most of these have values for the DynamoDB table names, as constructed in the services.yaml template, and following a naming convention for easy construction here.
Defining Lambdas in SAM Templates
We’ve looked at the Lambda resources previously in the API Gateway section, but our focus there was on the API event element. Let’s now look at the properties that define the Lambdas themselves. Here’s the CreateDealer Lambda, seen previously, from the admin.yaml template:
Here's more information about these properties, not including the ApiEvent element:
FunctionName
This is another example of a segmented resource name, designed to be unique within an account and region.
Handler
The template shown above is for the C# version, which specifies the code assembly, namespace and class name of the function, and then the function method name, all delimited by double colons. For Lambdas using the Java runtime, each function has to be defined in a separate class, and so this property is assigned the fully qualified name of that class.
Role
This property references the execution role that we covered above. This role is defined in the roles.yaml template, and passed as a parameter to this template in the form of an ARN.
Events
These are the properties for the endpoint integration that’s set up in the API and associated with this Lambda. Any path or query properties defined here will need to have matching names when reading them in the Lambda code.
AutoPublishAlias
We’ll cover Lambda aliases in more detail in the labs. But in summary, each time a Lambda is deployed and published, it gets a new version, and we use an alias to link a specific Lambda version to an endpoint integration in API Gateway. With this property set to true, a deployed Lambda will automatically get published and have the alias updated to reference the resulting new version.
Defining Lambda Integrations in OpenAPI Documents
We saw in the API Gateway section how Lambda resources in SAM templates don’t need the Events element when the API endpoints are defined with OpenAPI. Here’s the same CreateDealer Lambda in the admin.yaml template for OpenAPI deployment, showing the remaining properties:
With the SAM template, the associated API endpoint is included in the Lambda resource. OpenAPI does the reverse, where it includes the target URI of the Lambda as part of the endpoint definition.
Here’s the corresponding “create dealers” endpoint showing this, from the admin.openapi.yaml OpenAPI file:
Once more, we covered some of this in the API Gateway section. Line 28 declares the extension property for the endpoint integration; line 29 specifies that this is a proxy integration; line 30 specifies the HTTP method, which is always a POST to invoke a Lambda. Then we come to line 32, in which we’re constructing a URI for the target Lambda. Note that this URI actually targets the API Gateway endpoint integration for the Lambda, for the named stage. Also note that, strictly-speaking, this URI is actually a name (URN) in ARN format rather than a resource (URL).
Lastly, line 34 in the code above specifies an invocation role that API Gateway needs to assume in order to invoke the Lambda by name in this way. Here are the policy and role resources that are defined in the roles.yaml template for this purpose: