I am trying to achieve something similar to below in a AWS Cloudformation YAML file:
AWSTemplateFormatVersion: 2010-09-09
testAttribute = "test"
Resources:
Lambda:
Type: AWS::Lambda::Function
Properties:
Runtime: python3.7
Role: !GetAtt iam.Arn
MemorySize: 128
Timeout: 10
Handler: lambda_function.lambda_handler
FunctionName: "testName"+${testAttribute}
Description: 'This is my lambda'
Code:
S3Bucket: myBucket
S3Key: "lambda/testName"+${testAttribute}+".zip"
I know that above isn't quite correct, but I cant find a good answer when searching how to achieve it. Anyone who have some guidance on this matter?
It depends on the use case but if the "variable" would be static and you don't need the change it when deploying the stack, I would suggest an alternative solution, to use the Mappings section.
This allows you to define some static values without sending them when deploying the stack (you will have much cleaner deploy commands, and the logic would be on the template side instead of the deploy side).
In this case, I'm using !Sub intrinsic function with a mapping (you can set multiple variables to be substituted using !Sub):
AWSTemplateFormatVersion: 2010-09-09
Mappings:
attributes:
lambda:
testAttribute: "test"
Resources:
Lambda:
Type: AWS::Lambda::Function
Properties:
Runtime: python3.7
Role: !GetAtt iam.Arn
MemorySize: 128
Timeout: 10
Handler: lambda_function.lambda_handler
FunctionName: !Sub
- "testName${attr}"
- {attr: !FindInMap [attributes, lambda, testAttribute]}
Description: 'This is my lambda'
Code:
S3Bucket: myBucket
S3Key: !Sub
- "lambda/testName${attr}.zip"
- {attr: !FindInMap [attributes, lambda, testAttribute]}
Note: Mappings have a mandatory three-level nesting, take this into consideration while designing your solution
You could use Parameters with a default value, and Sub later in the template:
AWSTemplateFormatVersion: 2010-09-09
Parameters:
testAttribute:
Type: String
Default: test
Resources:
Lambda:
Type: AWS::Lambda::Function
Properties:
Runtime: python3.7
Role: !GetAtt iam.Arn
MemorySize: 128
Timeout: 10
Handler: lambda_function.lambda_handler
FunctionName: !Sub "testName${testAttribute}"
Description: 'This is my lambda'
Code:
S3Bucket: myBucket
S3Key: !Sub "lambda/testName${testAttribute}.zip"
[Edited for typo]
Related
I have added one layer to my Lambda function through CloudFormation. Now I have a requirement to add one more layer to my function. Basically, I need two layers in my existing Lambda function. Is it possible? I tried searching the AWS docs but I don't see it.
Resources:
LambdaLayer:
Type: "AWS::Lambda::LayerVersion"
Properties:
CompatibleRuntimes:
- python3.8
Content:
S3Bucket: !Sub "hello-${AWS::Region}"
S3Key: !Sub "myapp/layer1.zip"
LayerName: "layer1"
LambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Code:
S3Bucket: hello
S3Key: myapp/function.zip"
FunctionName: "hello-function"
Handler: "hello-function.lambda_handler"
Layers:
- !Ref LambdaLayer
Yes, is it possible. Add additional layers in the same way that you did the first layer, only append numbers to the resource names to distinguish them:
Resources:
LambdaLayer1:
Type: "AWS::Lambda::LayerVersion"
Properties:
CompatibleRuntimes:
- python3.8
Content:
S3Bucket: !Sub "hello-${AWS::Region}"
S3Key: !Sub "myapp/layer1.zip"
LayerName: "layer1"
LambdaLayer2:
Type: "AWS::Lambda::LayerVersion"
...
LayerName: "layer2"
LambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Code:
S3Bucket: hello
S3Key: myapp/function.zip"
FunctionName: "hello-function"
Handler: "hello-function.lambda_handler"
Layers:
- !Ref LambdaLayer1
- !Ref LambdaLayer2
So far I have this template.yml:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
My Lambda for doing something
Resources:
FirstLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: FirstLayer
Description: First layer of dependencies
ContentUri: layers/first-layer/
CompatibleRuntimes:
- nodejs14.x
Metadata:
BuildMethod: nodejs14.x
SecondLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: SecondLayer
Description: Second layer of dependencies
ContentUri: layers/second-layer/
CompatibleRuntimes:
- nodejs14.x
Metadata:
BuildMethod: nodejs14.x
MyLambda:
Type: AWS::Serverless::Function
Properties:
FunctionName: "MyLambda"
Policies:
- AmazonS3FullAccess
CodeUri: src/
Handler: lambda.handler
Timeout: 30
MemorySize: 2048 # Chrome will require higher memory
Runtime: nodejs14.x
Layers:
- !Ref FirstLayer
- !Ref SecondLayer
With this template I am able to start and invoke MyLambda locally and also deploy it to AWS. The problem I have is that I would like to reuse these same layers on other Lambdas as well, so for doing that I could simply extract these layers to another yml file, deploy them separately and then include the layers ARNs in the Layers property of my Lambda, but then, how can I run it locally with sam? I wouldn't like to have 2 template.yml files for my Lambda, one including the Layers on the Resources (like the one I already have) to run locally and another one with the refs to the actual layers ARNs to deploy on AWS, but that's the only solution I am seeing now.
The first question you need to ask is if those lambdas belong to the same application. If that´s not the case, you should use different templates, in order to deploy different stacks, to have isolated environments.
However, if you want to share resources, you have to very similar options:
Configure the layer in the parent template and pass the ARN as a parameter.
template.yml
Resources:
SharedLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: shared_layer
Description: Some code to share with the other lambda functions
ContentUri: ./layer
CompatibleRuntimes:
- nodejs14.x
RetentionPolicy: Delete
Application:
Type: "AWS::Serverless::Application"
Properties:
Location: "./app.template.yml"
Parameters:
SharedLayer: !Ref SharedLayer
app.template.yml
Parameters:
SharedLayer:
Type: String
Description: ARN of the SharedLayer
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./
Handler: index.handler
Layers:
- !Ref SharedLayer
Configure the layers in a nested template, set the ARN as an output, and then pass its output as a parameter to the other templates.
layers.template.yml
Resources:
SharedLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: shared_layer
Description: Some code to share with the other lambda functions
ContentUri: ./layer
CompatibleRuntimes:
- nodejs14.x
RetentionPolicy: Delete
Outputs:
SharedLayerARN:
Description: ARN of the Shared Layer
Value: !Ref SharedLayer
template.yml
Layer:
Type: "AWS::Serverless::Application"
Properties:
Location: "./layers.template.yml"
Application:
Type: "AWS::Serverless::Application"
Properties:
Location: "./app.template.yml"
Parameters:
SharedLayer: !GetAtt Layer.Outputs.SharedLayerARN
Both scenarios are supported by AWS SAM.
In a Cloudformation template, I define two S3 Buckets.
Bucket1:
Type: AWS::S3::Bucket
Properties:
...
Bucket2:
Type: AWS::S3::Bucket
Properties:
...
Outputs:
Bucket1:
Description: S3 Bucket
Value: !Ref Bucket1
Export:
Name: !Sub "${AWS::StackName}:Bucket1"
Bucket2:
Description: S3 Bucket
Value: !Ref Bucket2
Export:
Name: !Sub "${AWS::StackName}:Bucket2"
I use these exported buckets in two different cloudformation templates.
Template 1
Parameters:
LoaderCodeBucket:
Type: String
Resources:
MyLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket:
Fn::ImportValue:
!Sub "${LoaderCodeBucket}:Bucket1"
Template 2
Parameters:
ProcessorCodeBucket:
Type: String
Resources:
MyOtherLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket:
Fn::ImportValue:
!Sub "${ProcessorCodeBucket}:Bucket2"
Template 1 passes aws cloudformation validate-template --template-body ... while Template 2 fails due to
Template error: the attribute in Fn::ImportValue must not depend on any resources, imported values, or Fn::GetAZs.
The only difference is the lambda function in template 2 is used in an aws analytics application that is also defined in template 2.
I know for sure it's the S3 Bucket causing issues because when I remove that section of code, it passes the validation check.
I have been using this site to try to debug this issue, but none of the questions seem to answer this particular issue.
This is in same region/same account.
My question is:
Why is this particular section of code (template 2) throwing a template error when template 1 passes with no error?
This is a working example.
Template 1:
AWSTemplateFormatVersion: "2010-09-09"
Description: "Test"
Resources:
MyBucketOne:
Type: "AWS::S3::Bucket"
Properties:
BucketName: bucket-one-12341234
MyBucketTwo:
Type: "AWS::S3::Bucket"
Properties:
BucketName: bucket-two-12341234
Outputs:
MyBucketOneOutput:
Description: "Bucket Name of BucketOne"
Value: !Ref MyBucketOne
Export:
Name: !Sub "${AWS::StackName}-BucketOne"
MyBucketTwoOutput:
Description: "Bucket Name of BucketTwo"
Value: !Ref MyBucketTwo
Export:
Name: !Sub "${AWS::StackName}-BucketTwo"
Template 2: we can import it as !ImportValue my-s3-BucketOne
AWSTemplateFormatVersion: "2010-09-09"
Description: "Test"
Resources:
MyLambda:
Type: "AWS::Lambda::Function"
Properties:
Handler: index.handler
Runtime: nodejs12.x
FunctionName: "test-s3-import"
Code:
S3Bucket: !ImportValue my-s3-BucketOne
S3Key: "index.zip"
Description: "Test Lambda"
MemorySize: 128
Timeout: 60
Role: test-role-arn
If you do want to use from Parameter, it will be Fn::ImportValue: !Sub ${BucketExportNamePrefix}-BucketOne
AWSTemplateFormatVersion: "2010-09-09"
Description: "Test"
Parameters:
BucketExportNamePrefix:
Type: String
Default: "my-s3"
Resources:
MyLambda:
Type: "AWS::Lambda::Function"
Properties:
Handler: index.handler
Runtime: nodejs12.x
FunctionName: "test-s3-import"
Code:
S3Bucket:
Fn::ImportValue: !Sub ${BucketExportNamePrefix}-BucketOne
S3Key: "index.zip"
Description: "Test Lambda"
MemorySize: 128
Timeout: 60
Role: test-role-arn
I created a cloud formation stack as below, I'm setting MaximumRetryAttempts: 1 , but once the function is created the retry events is always set to 2 ( attached screenshot) . how can i change the value of this.
HelloWorldLambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: hello-world
Runtime: python3.7
Handler: index.lambda_handler
version:
Type: AWS::Lambda::Version
Properties:
FunctionName: !Ref HelloWorldLambdaFunction
asynconfig:
Type: AWS::Lambda::EventInvokeConfig
Properties:
FunctionName: !Ref HelloWorldLambdaFunction
MaximumRetryAttempts: 1
Qualifier: !GetAtt version.Version
The code looks fine. However, it seems to me that you are viewing $LATEST version in the console.
However, you set the asynconfig for version 1. Thus, in the console you have to explicitly select the correct lambda function version:
Update
To use latest version:
asynconfig:
Type: AWS::Lambda::EventInvokeConfig
Properties:
FunctionName: !Ref HelloWorldLambdaFunction
MaximumRetryAttempts: 1
Qualifier: $LATEST
I have a Cloudformation Custom Resource, that I want to use the outputs from, and call another Custom Resource.
I tried exporting the Outputs, and tinkered with using DependsOn, hoping it would set some order of precedence.
Is this Possible:
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: Run Lambda1, then run Lambda2 w/ outpu from Lambda1
Outputs:
lambda1Output:
Value:
Fn::GetAtt:
- lambda1
- test
Export:
Name: lambda1Outputs
Resources:
lambda1:
Type: Custom::test
Properties:
ServiceToken: arn:aws:lambda:us-east-1:761861444952:function:runOnce
lambda2:
Type: Custom::test2
DependsOn: lambda1
Properties:
ServiceToken: arn:aws:lambda:us-east-1:761861444952:function:runOnce
myParameter: !ImportValue lambda1Outputs
There's an easier way to achieve what you want.
Resources:
lambda1:
Type: Custom::test
Properties:
ServiceToken: arn:aws:lambda:us-east-1:761861444952:function:runOnce
lambda2:
Type: Custom::test2
Properties:
ServiceToken: arn:aws:lambda:us-east-1:761861444952:function:runOnce
myParameter: !GetAtt lambda1.test
So no need for the Outputs block or DependsOn. The CloudFormation dependency engine will understand that lambda1 needs to be executed before lambda2.