Referencing serverless stack name after pseudo parameters plugin deprecation - amazon-web-services

I'm wondering what is the correct way to reference AWS Cloudformation pseudo parameters in a serverless.yml now that pseudo parameter plugin has been deprecated.
All pseudo parameters are not available with the dollar sign syntax (e.g. ${aws:stackName} is not) in a similar manner as ${aws:region} is, for example. The serverless documentation on pseudo parameters is very short and I am not sure I fully understand it. I have tried to use Ref: "AWS::StackName", but when I try to generate an output
Fn::Sub:
- "${Stack}-someOutputResourceName"
- Stack:
Ref: "AWS::StackName"
, I get an error with [...]/Fn::Sub/1/Stack] 'null' values are not allowed in templates.
The pseudo-plugin page claims that
All functionalities as provided by this plugin are now supported by Serverless Framework natively
If this is true, how should I go about using pseudo-parameters?

It seems that while the above method does not work, I am able to use the pseudo variable directly without using Ref:
Name: !Sub "${AWS::StackName}-someOutputResourceName"

Related

How to deploy a AWS Lambda Function conditionally, using Serverless Framework, only to prd stage

thank you for taking the time to read this post. I’m trying to deploy an AWS Lambda Function conditionally, based on stage (only for “prd” stage).
This lambda has a Role, which deploys conditionally too. I already achieved this by using cloudformation conditions on the resources block, as shown below:
However, I don’t know how to make it work for the lambda function, as it is in the functions block I don’t have idea how to reference the condition. From the serverless.yml reference I decided to do what is shown below, and it doesn’t work:
Can someone help me to understand what am I doing wrong? And also what would be the solution to make this work? Thanks in advance
This can be achieved using the serverless if-else plugin
https://www.serverless.com/plugins/serverless-plugin-ifelse
You can use the plugin by adding them to your plugin section of the serverless.yml
plugins:
- serverless-plugin-ifelse
and set up conditions to update values in the serverless.yml for the functions and exclude them.
The include option isn't available, so your condition would be something like -
custom:
currentStage: ${opt:stage, self:provider.stage}
serverlessIfElse:
- If: '"${self:provider.stage}" == "prd"'
Set:
functions.startXtractUniversalInstance.role: <custom role for prod>
ElseExclude:
- functions.startXtractUniversalInstance
if you check the serverless.yml reference, there's no support for "conditions" key in the lambda
Serverless Framework definitions ARE NOT a 1:1 to CloudFormation
you can override the AWS CloudFormation resource generated by Serverless, to apply your own options, link here
which more or less would look like this:
functions:
startXtractUniversalInstance:
...
resources:
extensions:
StartXtractUniversalInstanceFunction:
Condition: ...
make sure to double check the name generated to your function, the above StartXtractUniversalInstanceFunction could be wrong

How can I use conditional configuration in serverless.yml for lambda?

I need to configure a lambda via serverless.yml to use different provision concurrency for different environments. Below is my lambda configuration:
myLambda:
handler: src/lambdas
name: myLambda
provisionedConcurrency: ${self:custom.pc}
...
custom:
pc: ${env:PC}
The value PC is loaded from environment variable. It works for values greater than 0 but I can't set a value 0 in one environment. What I want to do is to disable provision concurrency in dev environment.
I have read through this doc https://forum.serverless.com/t/conditional-serverless-yml-based-on-stage/1763/3 but it doesn't seem to help in my case.
How can I set provisionedConcurrency conditional based on environment?
Method 1: Stage-based variables via default values
This is a fairly simple trick by using a cascading value variable. The first value is the one you want, the second one being a default, or fallback value. Also called cascading variables.
// serverless.yml
provider:
stage: "dev"
custom:
provisionedConcurrency:
live: 100
staging: 50
other: 10
myLambda:
handler: src/lambdas
name: myLambda
provisionedConcurrency: ${self:custom.provisionedConcurrency.${self:provider.stage}, self:custom.provisionedConcurrency.other}
This above with stage set to dev will default to "other" value of 10, but if you set stage via serverless deploy --stage live then it will use the live value of 100.
See here for more details: https://www.serverless.com/framework/docs/providers/aws/guide/variables#syntax
Method 2: Asynchonous Value via Javascript
You can use an js include and put your conditional logic there. It's called "asynchronous value support". Basically, this allows you to put logic in a javascript file which you include and it can return different values depending on various things (like, what AWS account you're on, or if certain variables are set, or whatever). Basically, it allows you to do this...
provisionedConcurrency: ${file(./detect_env.js):get_provisioned_concurrency}
Which works if you create a javascript file in this folder called detect_env.js, and it has the contents similar to...
module.exports.get_provisioned_concurrency = () => {
if ("put logic to detect which env you are deploying to, eg for live") {
return Promise.resolve('100');
} else {
// Otherwise fallback to 10
return Promise.resolve('10');
}
}
For more info see: https://www.serverless.com/framework/docs/providers/aws/guide/variables#with-a-new-variables-resolver
I felt I had to reply here even though this was asked months ago because none of the answers were even remotely close to the right answer and I really felt sorry for the author or anyone who lands here.
For really sticky problems, I find it's useful to go to the Cloudformation script instead and use the Cloudformation Intrinsic Functions.
For this case, if you know all the environments you could use Fn::FindInMap
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-findinmap.html
Or if it's JUST production which needs 0 then you could use the conditional Fn::If and a boolean Condition test in the Cloudformation template to test if environment equals production, use 0, else use the templated value from SLS.
Potential SLS:
resources:
Conditions:
UseZero: !Equals ["production", ${provider.stage}]
Resources:
myLambda:
ProvisionedConcurrency: !If [UseZero, 0, ${self:custom.pc}]
You can explicitly remove the ProvisionedConcurrency property as well if you want:
resources:
Conditions:
UseZero: !Equals ["production", ${provider.stage}]
Resources:
myLambda:
ProvisionedConcurrency: !If [UseZero, AWS::NoValue, ${self:custom.pc}]
Edit: You can still use SLS to deploy; it simply compiles into a Cloudformation JSON template which you can explicitly modify with the SLS resources field.
The Serverless Framework provides a really useful dashboard tool with a feature called Parameters. Essentially what it lets you do is connect your service to it then you can set different values for different stages and then use those values in your serverless.yml with syntax like ${param:VARAIBLE_NANE_HERE} and it gets replaced at deploy time with the right value for whatever stage you are currently deploying. Super handy. There are also a bunch of other features in the dashboard such as monitoring and troubleshooting.
You can find out more about Parameters at the official documentation here: https://www.serverless.com/framework/docs/guides/parameters/
And how to get started with the dashboard here: https://www.serverless.com/framework/docs/guides/dashboard#enabling-the-dashboard-on-existing-serverless-framework-services
Just using a variable with a null value for dev environments during on deploy/package and SLS will skip this property:
provisionedConcurrency: ${self:custom.variables.provisionedConcurrency}

AWS Lambda Rest API: A sibling ({id}) of this resource already has a variable path part -- only one is allowed Unable to create resource at path

I'm particular new to Lambda and to AWS in general. I'm trying to setup a simple REST API Service with Lambda. I've used CloudFormat and CodePipeline to have a simple Express app.
I'm trying to figure out why during the deployment phase, during ExecuteChangeSet I have this error:
Errors found during import: Unable to create resource at path '/stations/{stationId}/allowedUsers': A sibling ({id}) of this resource already has a variable path part -- only one is allowed Unable to create resource at path '/stations/{stationId}/allowedUsers/{userId}': A sibling ({id}) of this resource already has a variable path part -- only one is allowed
This is what I have inside the template.yml
Events:
AllowedUsers:
Type: Api
Properties:
Path: /stations/{stationId}/allowedUsers
Method: get
AddAllowedUsers:
Type: Api
Properties:
Path: /stations/{stationId}/allowedUsers
Method: post
DeleteAllowedUsers:
Type: Api
Properties:
Path: /stations/{stationId}/allowedUsers/{userId}
Method: delete
GetAllowedUser:
Type: Api
Properties:
Path: /stations/{stationId}/allowedUsers/{userId}
Method: get
I searched a bit for this error but I'm not sure how to solve it.
For me, the issue was different from what is described in the GitHub issue Bryan mentioned.
I was using two different parameter names. Finishing the refactoring and using a single id name fixed the issue.
Example:
DeleteAllowedUsers:
Type: Api
Properties:
Path: /stations/{stationId}/allowedUsers/{id}
Method: delete
GetAllowedUser:
Type: Api
Properties:
Path: /stations/{stationId}/allowedUsers/{userId}
Method: get
Here is the walk around for this problem. It was posted on github by pettyalex.
link :https://github.com/serverless/serverless/issues/3785
You might encounter this issue when updating a variable path while using serverless ( and serverless.yaml ) to provision the AWS gatewayApi, here is a walk-around:
comment out the endpoint function to remove it completely
uncomment and deploy again

AWS MSK Cloud Formation Tags problems

When creating AWS::MSK::Cluster with Cloud Formation I am not able to set Tags in the usual way:
Tags:
- Key: Name
Value: !Ref Identifier
Because of this error:
Property validation failure: [Value of property {/Tags} does not match type {Map}]
As of the time of writing, the documentation states that, instead of the usual Type: List of Tag, I should use: Type: Json.
Also the same documentation states that:
You can specify tags in JSON or in YAML, depending on which format you use for your template
After further investigation (and AWS support help), the working (only on creation) example looks like this:
Tags:
Name: !Ref Identifier
Additionally, tags cannot be modified (the docs actually state that tags change require replacement), when tried a slightly confusing error shows up:
CloudFormation cannot update a stack when a custom-named resource requires replacing. Rename kafka-eu-west-1-dev and update the stack again.

How to get the latest lambda version in cloudformation yml?

I'm trying to reference an edge lambda for cloudfront distribution in cloudformation.
What I have now is:
LambdaFunctionARN:
Fn::GetAtt: [BasicAuthLambdaFunction,Arn]
But I get this error:
An error occurred: GGGCloudFrontDistribution - The function
ARN must reference a specific function version. (The ARN must end with
the version number.)
So.. is there some kind of technique to reference the latest version of the function?
You can't use the latest version. You have to use a specific version as the documentation you linked states:
You must specify the ARN of a function version; you can't specify a Lambda alias or $LATEST.
If you are creating the Lambda function in your template, you can also create a version and use that.
BasicAuthLambdaFunctionVersion:
Type: "AWS::Lambda::Version"
Properties:
FunctionName:
Ref: BasicAuthLambdaFunction
# ...
LambdaFunctionARN:
Fn::GetAtt: [BasicAuthLambdaFunctionVersion,Arn]
Note that when updating the stack, a new version will not be created when the Lambda function code changes. You have to manually create and use a new version by changing the name of BasicAuthLambdaFunctionVersion to BasicAuthLambdaFunctionVersion2 or something else. To automate this, you can edit the template with a script before using it.
If you are using the Serverless Framework, take a look at:
https://github.com/silvermine/serverless-plugin-cloudfront-lambda-edge
https://github.com/serverless/serverless/issues/3944