When creating Lambda function through the SAM CLI using template.yaml, I have to pass few environment variables, and they shouldn't be exposed on GitHub. Is there any way I can refer the environment variables in template.yaml through the .env file?
I didnt find any sources for the same.
Sample code snippet from template.yaml:
Properties:
CodeUri: student /
FunctionName: list
Handler: index.listHandler
Runtime: nodejs14.x
Environment:
Variables:
MONGODB_URI: mongodb://username:pwd
There are few options here.
Add them to the Parameters section of the template (be sure to add the NoEcho option) and pass them in at the time of deploying.
A slightly better option is to use Secrets Manager to store the value and then use dynamic references in the template. CloudFormation will retrieve the values from Secrets Manager for you, at the time you deploy.
A better option is to not pass them as environment variables at all (since anyone with permissions to view the function will be able to see the value). Instead, use Secrets Manager to store the value and look up the value in the code. If you decide to use this approach be sure to cache the value so that you can at least reuse it between warm starts of the lambda.
One more option is to encrypt the value using KMS, and pass in the encrypted (Base64 encoded) value to the function. You'll need to call KMS decrypt to get the decrypted value. This operation is pretty fast, and isn't likely to be throttled. I would still cache the value to help speed things up between warm starts.
By extension of #Jason's answer 2. here a full working example:
template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: My test secrets manager dynamic reference SAM template/ Cloudformation stack
Resources:
# lambdas
myLambda:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub ${AWS::StackName}-myLambda
Runtime: nodejs12.x
Handler: index.handler
CodeUri: ./src/handlers/myLambda
MemorySize: 128
Timeout: 10
Environment:
Variables:
someSecret: '{{resolve:secretsmanager:somePreviouslyStoredSecret}}'
src/handlers/myLambda/index.js
const { someSecret } = process.env;
exports.handler = (event, context, callback) => {
if (someSecret) callback(null, `secret: ${someSecret}`);
callback(`Unexpected error, secret: ${someSecret}`);
};
Related
I am working with the following AWS SAM Template.
Resources:
PaymentFunction:
Type: 'AWS::Serverless::Function'
Properties:
FunctionName: payment_function
CodeUri: PaymentFunction/
Description: 'A lambda function to do a payment'
...
...
...
FunctionUrlConfig:
AuthType: NONE
Cors:
AllowOrigins:
- "*"
AllowHeaders:
- "*"
AllowMethods:
- "*"
Outputs:
PaymentFunctionUrl:
Value:
Fn::GetAtt: PaymentFunctionUrl.FunctionUrl
When I deploy this function with aws deploy command. I get the following function url
https://{random_string}.lambda-url.{aws_region}.on.aws/
whenever I change the LogicalResourceId i.e. PaymentFunction or actual function name i.e. payment_function, it creates a new {random_string}. that means a new function URL.
Is it possible to change the function_name without changing function URL?
I don't think it is possible. The CFN docs for the FunctionName property of an AWS Lambda function clearly states that the updating of the name requires Replacement of the resource. This means that the old resource will be deleted and a new resource will be created with a new function URL.
Changing the LogicalResourceId of any cfn resource will automatically create a new resource in your AWS account and delete the old one. For Lambda functions, this always results in a different function URL.
If you want to invoke lambdas using URLs that you have more control over (and can easily change the lambda function that gets executed for each path), have a look at the REST API of the Amazon API Gateway service.
I'm trying to enable/disable lambda scheduled event based on environment, and not having much luck. If the env is prod, it should enable the schedule, and if it's dev it should disable it. However, the schedule status doesn't obey the condition. It's currently enabled in dev, but the template doesn't disable it. If I set the Enabled property to false manually, the schedule status does change to disable. So not sure where I'm going wrong (or if what I'm trying to do is possible). Any help is greatly appreciated!
Parameters:
env:
Type: String
Conditions:
isProd:
!Equals [!Ref env, prod]
Resources:
func:
Type: AWS::Serverless::Function
Properties:
Events:
ScheduledEvent:
Type: Schedule
Properties:
Schedule: rate(5 minutes)
Enabled: !If [isProd, true, false]
Okay, so after a bit more searching, it looks like setting the Enabled property with conditional statements doesn't work so nice. The workaround I've put in place is to use conditional statements in the Schedule property.
ScheduledEvent:
Type: Schedule
Properties:
Schedule: !If [isProd, "rate(5 minutes)", "cron(00 00 01 01 ? 1970)"]
Thanks to these posts for point me in the right direction:
Conditionally enabling an event schedule in AWS sam template file
Dynamically change event properties on aws cloudformation templates
Creating a SAM template to creation of an API + Lambda. Simples!
Resources:
HelloWorldApi:
Type: AWS::Serverless::Api
Properties:
StageName: prod
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: ./api.yaml
Throw into this a custom domain for the gateway and map it to the stage of the API.
Resources:
HelloWorldApi:
Type: AWS::Serverless::Api
Properties:
StageName: prod
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Domain:
DomainName:
Fn::Sub: api-${HelloWorldApi.Stage}.custom-domain.com
CertificateArn: arn:aws:certificate...
If I was to do this via the console, after creating the custom domain, and mapping the stage, I must configure the DNS Alias record in Route53 for API and mapping
My question is how to create the SAM template block for a Route53 alias record for a custom GatewayAPI domain
Thanks to #lamanus for inspiring me to read the docs and see the wood for the trees.
The crux of the original OP was the reference to the mapped custom domain created by AWS::Serverless::Api Getting that reference is not obvious. That said, you don't need to if you create the Route53 in the AWS::Serverless::Api block like so.
HelloWorldApi:
Type: AWS::Serverless::Api
Properties:
StageName: prod
Domain:
DomainName:
Fn::Sub: api-${HelloWorldApi.Stage}.custom-domain.com
CertificateArn: arn:cert...
Route53:
HostedZoneName: custom-domain.com.
EvaluateTargetHealth: true
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: ./api.yaml
This SAM resource will create a custom domain, and mapping, and the Route53 target alias.
You can use the CloudFormation template to create the Route 53 Record.
To get the endpoint, you can use the Ref function.
When the logical ID of this resource is provided to the Ref intrinsic function, it returns the ID of the underlying API Gateway API.
So, it is possible to rebuild the api gateway endpoint with the region value. Join the Ref function for the api gateway with the strings, regions such as:
!Join
- ''
- - !Ref HelloWorldApi
- .execute-api.
- !Ref AWS::Region (or specific value)
- .amazonaws.com
and then create a CNAME record to the Route 53 hosted zone. See the AWS docs.
I have written a CloudFormation template in YAML to deploy my AWS Lambda functions. I have to deploy multiple Lambda function and I want to be able to changes the key-value pair at run-time, so that I don't have to copy the entire thing again and again to make changes before deployment.
I was reading about the set builtin of Linux, but didn't find it of much help
NameOfMyLambda:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '#My_Function_Name'
Handler: app.lambda_handler
Runtime: python3.7
MemorySize: 256
Role: !GetAtt MyExecutionRole.Arn
CodeUri: 'path/to/my/python/file'
In the above given code, I want to be able to change the "FunctionName" and "Role" at time of deployment.
Your exact use case isn't completely clear (how are you running your cloudformation?), but it sounds like you want to use CloudFormation Parameters possibly combined with Nested stacks
This would allow you to reference your cloudformation template multiple times from within an outer template, passing different parameters each time.
The below lambda function is to associate a SNS topic to the existing directories, followed by a custom resource to invoke the lambda func itself. I see that the lambda creation is successful with the 'Register_event_topic' also completing. However, the stack fails after a while mostly because the 'custom resource failed to stabilize in expected time'; How can I ensure that the stack does not error out?
AWSTemplateFormatVersion: '2010-09-09'
#creating lambda function to register_event_topic
Description: Lambda function to register event topic with existing directory ID
Parameters:
RoleName:
Type: String
Description: "IAM Role used for Lambda execution"
Default: "arn:aws:iam::<<Accountnumber>>:role/LambdaExecutionRole"
EnvVariable:
Type: String
Description: "The Environment variable set for the lambda func"
Default: "ESdirsvcSNS"
Resources:
REGISTEREVENTTOPIC:
Type: 'AWS::Lambda::Function'
Properties:
FunctionName: dirsvc_snstopic_lambda
Handler: index.lambda_handler
Runtime: python3.6
Description: Lambda func code to assoc dirID with created SNS topic
Code:
ZipFile: |
import boto3
import os
import logging
dsclient = boto3.client('ds')
def lambda_handler(event, context):
response = dsclient.describe_directories()
directoryList = []
print(response)
for directoryList in response['DirectoryDescriptions']:
listTopics = dsclient.describe_event_topics(
DirectoryId=directoryList['DirectoryId']
)
eventTopics = listTopics['EventTopics']
topiclength = len(eventTopics)
if topiclength == 0:
response = dsclient.register_event_topic(
DirectoryId=directoryList['DirectoryId'],
TopicName= (os.environ['MONITORING_TOPIC_NAME'])
)
print(listTopics)
Timeout: 60
Environment:
Variables:
MONITORING_TOPIC_NAME: !Ref EnvVariable
Role: !Ref RoleName
InvokeLambda:
Type: Custom::InvokeLambda
Properties:
ServiceToken: !GetAtt REGISTEREVENTTOPIC.Arn
ReservedConcurrentExecutions: 1
Alas, writing a Custom Resource is not as simple as you'd initially think. Instead, special code must be added to post the response back to a URL.
You can see this in the sample Zip file provided on: Walkthrough: Looking Up Amazon Machine Image IDs - AWS CloudFormation
From the Custom Resources - AWS CloudFormation documentation:
The custom resource provider processes the AWS CloudFormation request and returns a response of SUCCESS or FAILED to the pre-signed URL. The custom resource provider provides the response in a JSON-formatted file and uploads it to the pre-signed S3 URL.
This is due to the asynchronous behaviour of CloudFormation. It doesn't simply call the Lambda function and then wait for a response. Rather, it triggers the Lambda function and the function must call back and trigger the next step in CloudFormation.
Your lambda doesn't support custom resource life cycle
In a Lambda backed custom resource, you implement your logic to
support creation, update and deletion of the resource. These
indications are sent from CloudFormation via the event and give you
information about the stack process.
In addition, you should also return your status back to CloudFormation
CloudFormation expects to get a response from your Lambda function after you're done with your logic. It will not continue with the deployment process if it doesn’t get a response, or at least until a 1 hour(!) timeout is reached. It can cost you a lot of time and frustration.
You can read more here