Dynamically create resource names in AWS SAM using parameters - amazon-web-services

I want to dynamically create names for my resources in my Cloud Formation stack when using AWS SAM if this is possible.
E.g. when I package or deploy I want to be able to add soemthing to the command line like this:
sam package --s3-bucket..... --parameters stage=prod
When in my template.yml file somehow to do something like this:
Resources:
OrdersApi:
Type: AWS::Serverless::Function
Properties:
FunctionName: orders-api-${stage}
CodeUri: ./src/api/services/orders/
...
Note for the OrdersApi property of FunctionName I want to dynamically set it to orders-api-prod which is the value I attempted to pass in on the CLI.
I can do this quite easily using the Serverless Framework but I can't quite work out how to do it with SAM.

You can use functions like Sub to construct resource names in CloudFormation. Something along the lines:
Parameters:
stage:
Type: String
Default: dev
AllowedValues:
- dev
- prod
Resources:
OrdersApi:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub 'orders-api-${stage}'

The answer posted by lexicore is correct and you can form values in certain parts of the template.yaml file using the !Sub function e.g.
FunctionName: !Sub 'orders-api-${stage}'
The missing part of why this wouldn't work is that you need to pass the parameters through to the sam deploy command in a specific format. From reading the AWS docs, sam deployis shorthand for aws cloudformation deploy.... That command allows you to pass parameters using the following syntax:
aws cloudformation deploy .... --parameter-overrides stage=dev
This syntax can also be used with the sam deploy command e.g.
sam deploy --template-file packaged.yml ..... --parameter-overrides stage=dev
Note that in this example stage=dev applies to the Parameters section of the template.yaml file e.g.
Parameters:
stage:
Type: String
AllowedValues:
- dev
- stage
- prod
This approach allowed me to pass in parameters and dynamically change values as the cloud formation stack was deployed.

Related

How to reference the --s3-bucket value in my Outputs?

I do a SAM deployment from my local console. With sam package ... --s3-bucket xy I specify the existing s3-bucket where deployment relevant objects are stored. I want that bucket to appear in the Output section in Cloudformation but I don't know how to reference it.
If I look under Template in the web console I see the path:
Resources:
MyLambda:
Properties:
CodeUri: s3://xy/1b26f7841...
So I tried to add
Outputs:
SourceBucket:
Value: !GetAtt MyLambda.Properties.CodeUri
to my template.yaml, but the deployment fails with Requested attribute Properties.CodeUri does not exist in schema for AWS::Lambda::Function
Sadly you can't do this. AWS::Serverless::Function does not return such information. You would have to use custom resource to get it.
Alternatively, if you pass the bucket uri as input parameter to your template, you can output the parameter's value directly.

AWS SAM: can we use a already existing api in aws sam template?

I have a lambda function that needs to be triggered via Amazon API Gateway. Is there a way to include an already existing API (created using the AWS console) into AWS SAM template?
SAM doesn't support the !ImportValue in the template yet.
Issue on Github
On the GitHub of the aws/serverless-application-model there's an open PR for that feature
See here
If you want you could help and contribute to that PR so then you could start using !ImportValue in your SAM template.yml
Else, I suggest you go with the old way, you create a CI/CD with a CloudFormation template that can use the !ImportValue and is linked to an S3 bucket where your lambda function code lives.
Examples of Cloudformation Templates
Update
SAM CLI now support the !ImportValue, the issue on Github is closed.
You can use it as follow
# You need to export the resource that you want to use in another template first
# This goes at the end of your template.yml file, after the Resources
# template.yml in the first repo
Outputs:
myExportedResource:
Value: !Ref TheResource
Export:
Name: !Sub "{environment}-nice-export-name"
# template.yml in the second repo (This obviously goes in Resources)
MyLambda:
Type: AWS::Serverless::Function
Properties:
FunctionName: awesome-lambda
CodeUri: ./dist
Handler: this-file.handler
Role: !GetAtt LambdaRole.Arn
VpcConfig:
SecurityGroupIds:
- !GetAtt SecurityGroup.GroupId
SubnetIds:
- Fn::ImportValue: !Sub "${environment}-nice-export-name"
You can use it exactly as a normal cloudformation template
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html
Note that here I used Fn:ImportValue because I needed to use !Sub but if you don't need to reference a parameter in your import value simply use !ImportValue

Using AWS::CodeBuild::Project Environment variable in cloudformation template of repository

I want to create a continous delivery pipeline for a Lambda function.
As shown in this docs, the custom environment variables of AWS::CodeBuild::Project can be used in buildspec.yaml like:
aws cloudformation package --template-file template.yaml --s3-bucket $MYEVVARKEY --output-template-file outputtemplate.yaml
Wanted to use those CodeBuild Project environment variables in the SAM template of the repository also. As shown below, I tried with dollar signs, but it did not get it as a key but as a text as it is:
# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
TimeFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: $MY_FN_NAME_ENV_VAR
Role: $MY_ROLE_ARN_ENV_VAR
Handler: index.handler
Runtime: nodejs8.10
CodeUri: ./
So, is it possible to utilize CodeBuild Project environment variables in SAM template, if so what's the notation required to achieve that?
CloudFormation can't refer to environment variables, doesn't matter SAM or plain. What you can do is to pass environment variables as parameters via shell in CodeBuild buildspec.yaml file (--parameters ParameterKey=name,ParameterValue=${MY_ENV_VAR}).
Remember to add corresponding parameter to your Parameters section.
If you use aws cloudformation deploy then you should use --parameter-overrides, which is a little simpler form:
--parameter-overrides \
YourParam=${YOUR_ENV_VAR} \
Foo=Bar \

Difference between SAM template and Cloudformation template

I'm finding it hard to understand the difference between SAM template and Cloudformation template. I know that SAM template can be used to define Serverless Applications like Lambda, but how does that make it different from Cloudformation template? Is the syntax different? I can still specify the Lambda definitions in cloudformation template. So, my question is why should I care about SAM? Won't knowing about just cloud formation template be sufficient?
From CloudFormation's perspective, SAM is a transform. Meaning: SAM templates are syntactically equivalent, but they allow you to define your serverless app with more brevity. The SAM template eventually gets expanded into full CFN behind the scenes. If you already know CFN, but want to write less YAML code, SAM may be beneficial to you. The idea is to reduce your effort.
SAM templates are a superset of Cloudformation. Any Cloudformation template can be run through SAM unchanged, and it will work. SAM supports all the types available in Cloudformation templates, so you can think of SAM as "CloudFormation++".
However, SAM also gives you additional "transforms" that allow you to define certain concepts succinctly, and SAM will figure out what you mean and fill in the the missing pieces to create a full, expanded, legal Cloudformation template.
Example: For SAM (and Serverless Framework) users, who deal mostly in Lambda functions, one of the more most useful transforms is the Events property on the Lambda function -- SAM will add all the objects needs to access that function through an API path in API Gateway.
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: HelloWorldFunction
Handler: app.lambdaHandler
Runtime: nodejs12.x
Events: # <--- "Events" property is not a real Cloudformation Lambda property
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
The SAM template snippet shown above gets transformed/expanded into several API Gateway objects (a RestApi, a deployment, and a stage). The AWS::Serverless::Function type used in this snippet is not a real Cloudformation type -- you won't find it in the docs. SAM expands it into a Cloudformation template containing a AWS::Lambda::Function object and several different AWS::ApiGateway::* objects that Cloudformation understands.
To give you an idea of how much manual coding this saves you, here's what the expanded version of the above SAM template looks like as a full Cloudformation template:
Resources:
HelloWorldFunctionHelloWorldPermissionProd:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
Principal: apigateway.amazonaws.com
FunctionName:
Ref: HelloWorldFunction
SourceArn:
Fn::Sub:
- arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/hello
- __Stage__: "*"
__ApiId__:
Ref: ServerlessRestApi
HelloWorldFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- sts:AssumeRole
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Tags:
- Value: SAM
Key: lambda:createdBy
ServerlessRestApiProdStage:
Type: AWS::ApiGateway::Stage
Properties:
DeploymentId:
Ref: ServerlessRestApiDeployment_NNN
RestApiId:
Ref: ServerlessRestApi
StageName: Prod
ServerlessRestApiDeployment_NNN:
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId:
Ref: ServerlessRestApi
Description: 'RestApi deployment id: ???'
StageName: Stage
ServerlessRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Body:
info:
version: '1.0'
title:
Ref: AWS::StackName
paths:
"/hello":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloWorldFunction.Arn}/invocations
responses: {}
swagger: '2.0'
HelloWorldFunction:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: aws-sam-cli-managed-default-samclisourcebucket-???
S3Key: temp/???
Tags:
- Value: SAM
Key: lambda:createdBy
Handler: app.lambdaHandler
Role:
Fn::GetAtt:
- HelloWorldFunctionRole
- Arn
Timeout: 3
Runtime: nodejs12.x
Previously, if you were authoring pure Cloudformation, you would have had to code all this by hand, over and over, for each API Gateway endpoint that you wanted to create. Now, with a SAM template, you define the API as an "Event" property of the Lambda function, and SAM (or Serverless Framework) takes care of the drudgery.
In the old days, when we had to do all this by hand, it totally sucked. But now, everything is glorious again.
Like #Luis Colon said, SAM is a transform. What that means, is that at the top of a SAM Template there is a Transform statement that lets CloudFormation know to run an intrinsic function, Transform, on this SAM template to turn it into a CloudFormation template. So, all SAM Templates will eventually be converted into CF templates, but for the end-user in most cases it is easier to just use the SAM template. For instance, for a simple application with Lambdas triggered by a new API you're creating, the SAM template will let you accomplish this in fewer lines than CloudFormation.
To extend this, the Serverless Framework behaves similarly. Serverless is designed to work across platforms (AWS, Azure, etc.). It's syntax looks very similar to SAM, and it too converts the template into the target platform's (ie. AWS) fuller version of the template (ie. CloudFormation template).
You can imagine SAM as an extended form of CloudFormation. SAM makes Serverless/Lambda deployments easier.
Even CloudFormation can deploy lambda scripts using inline scripts but it has a limitation of 4096 characters and you cannot pack custom dependencies, python libraries.
So to make Lambda/Serverless deployments easy SAM is used. SAM is a CLI tool. You cannot find SAM in AWS Console.
In case of python deployment, sam will read the requirements.txt file build a package, and will deploy the package when you wish to sam deploy
So at the end of the day you can write as much lengthy Lambda Code, use as many libraries you want and even import your custom libraries i.e. complete flexibility.

How to specfiy existing FunctionName on SAM Template

I'm trying to deploy AWS Lambda function by using SAM.
What I want to do is to update exsiting lambda function by deploying local source code.
In order to do that, I specified the existing lambda function name as 'FunctionName' in template.yaml like below.
However, 'FunctionName' does only support for creating new function, not updating to existing function.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-functionname
Are there any ways to specify Function Name in SAM in order to update lambda function?
[template.yaml]
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
HelloWorld:
Type: 'AWS::Serverless::Function'
Properties:
FunctionName: 'hello_world'
Description: ''
Handler: index.handler
MemorySize: 256
Role: 'arn:aws:iam::368834739507:role/lambda_basic_execution'
Runtime: nodejs6.10
Timeout: 120
Using SAM (and/or CloudFormation), you cannot update existing resources.
SAM (and CloudFormation) create and manage their own resources. All resources specified in the template are going to be created when the stack is created. They cannot be "taken over".
Instead, you should allow SAM (or CloudFormation) to create the Lambda function for you, then update users to reference the new function. After that, you can update your code using SAM.