serverless-application-model: DependsOn AWS::Serverless:Api does not wait for all resources
Background
I try deploying a serverless application which has a couple of Lambdas, an API gateway and I want to lock it behind an API key.
When sending the definition to cloudformation with aws cloudformation deploy
I notice that DependsOn
does wait on the RestApi
resource to be ready, but it does not wait on the AWS:ApiGateway::Deployment
resource which also is created by SAM (but transparently).
My SAM.yaml for reference:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Some API key protected serverless application
Parameters:
targetStage:
Description: Define stage to which Lambdas/API Gateways should be deployed.
Type: String
Default: dev
AllowedValues:
- dev
- test
- prod
ConstraintDescription: Only stages dev, test, prod are allowed
Globals:
Function:
Runtime: nodejs6.10
MemorySize: 128
Timeout: 6
Resources:
createFunction:
Type: 'AWS::Serverless::Function'
Properties:
Handler: create.handler
CodeUri: ../dist/deployment_package.zip
FunctionName: !Sub "${AWS::StackName}-createCertificate-${targetStage}"
Runtime: nodejs6.10
Description: Create or overwrite an IoT certificate with given params.
Role: 'arn:aws:iam::1234:role/my-role'
deleteFunction:
Type: 'AWS::Serverless::Function'
Properties:
Handler: delete.handler
CodeUri: ../dist/deployment_package.zip
FunctionName: !Sub "${AWS::StackName}-deleteCertificate-${targetStage}"
Runtime: nodejs6.10
Description: Delete an IoT certificate with given params.
Role: 'arn:aws:iam::1234:role/my-role'
restApi:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref targetStage
DefinitionBody:
swagger: "2.0"
info:
version: "2017-11-09T13:59:26Z"
title: !Sub "${AWS::StackName}-api-${targetStage}"
basePath: !Sub "/${targetStage}"
schemes:
- "https"
paths:
/certificate:
post:
responses: {}
security:
- api_key: []
x-amazon-apigateway-integration:
uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${createFunction.Arn}/invocations"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
delete:
responses: {}
security:
- api_key: []
x-amazon-apigateway-integration:
uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${deleteFunction.Arn}/invocations"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
securityDefinitions:
api_key:
type: "apiKey"
name: "x-api-key"
in: "header"
x-amazon-apigateway-binary-media-types:
- "application/octet-stream"
apiKey:
Type: "AWS::ApiGateway::ApiKey"
DependsOn:
- restApi
Properties:
Name: !Sub "${AWS::StackName}-apiKey-${targetStage}"
Description: !Sub "API key for: ${AWS::StackName} / ${targetStage}"
Enabled: "true"
StageKeys:
- RestApiId: !Ref restApi
StageName: !Ref targetStage
Expected behaviour
DependsOn
used to wait on a AWS::Serverless:Api
should wait on all resources created by SAM for the API.
Actual behaviour
This causes CloudFormation to throw a CREATE_FAILED | AWS::ApiGateway::ApiKey | apiKey | Invalid stage identifier specified
error during deployment, since the stage which I want to associate with the API key does not exist yet and I have no means of referencing the (future) stage or referencing the AWS::ApiGateway::Deployment
in the DependsOn
.
Thoughts
The only workaround I can think of for now is to build all AWS::Serverless::Api
component resources (RestApi, Deployment, ApiKey) by hand and use DependsOn
on the respective resource identifiers.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 31
- Comments: 31 (9 by maintainers)
The bold lines fixed the issue for me - ApiId: !Ref ComputeApi Stage: !Ref ComputeApi.Stage
So I spend basically the whole day figuring out how to deploy my stack. I found an okay workaround by inspecting the stack log in the CloudFormation console. I noticed that the stage which is associated with the deployment is reliably created last and that this stage always has the name ${AWS::StackName}Stage so that I can reliably use
DependsOn
with that stage. This of course will only work as long as the naming convention within SAM won’t change. So I hope we can get a fix soon.This seemed to work for me
@jfuss for AWS::Serverless::Api resource now supports getting Stage and Deployment using following:
!Ref: MyApi.Stage
!Ref: MyApi.Deployment
However, issue still persists as DependsOn attribute doesn’t support usage of an intrinsic function(Ref in our case) for any of its value(s).
Possible approaches towards resolution might include:
So, both of these are true. If StageName is a straight up string, then it will be
<ID><StageName>Stage
. If it is an intrinsic function, it will be<ID>Stage
. This is because SAM cannot reliably resolve intrinsic functions. SAM runs outside of CloudFormation where the actual intrinsic function resolution happens.So just caught up on the complete issue here.
We should support some way to create ApiKeys. I thought this was part of #248 but doesn’t look like it made it there. Maybe it should be?
Maybe another way to help in solving this would be to do something similar to what we did to surface Versions and Aliases for
AWS::Serverless::Function
(Ref: FucntionLocicalId.Version). I think that would help in being able to make DependsOn for resources we generate but not sure how this really interacts and works outside the theAWS::Serverless::*
Types (it’s doable but think there is some additional work to make this supported).The suggestion you made about relying on the generated LogicalIds is the way to go (at least for now). I know of others that have suggested the same thing on other issues here. We have fully documented the generated resources we create here, so it is safe to depend on these naming conventions. Make sure to follow the docs, the stage logicalId is <
AWS::Serverless::Api
LogicalId><StageName value>Stage not based on theAWS::StackName
. So in your example it would berestApi<whatever Ref: targetStage resolves to>Stage
.Note:
AWS::Serverless::API
(explicit) andAWS::Serverless::Function
with an API Event type have different generates of resources. Please consult the documentation for references.I have encountered this issue too. It’s very annoying. It’s unfortunate that after a year still the problem. Solution provided by @sanathkr and @jfuss sometimes works and sometimes doesn’t. My workaround was to depends my resource on a function (which is itself dependent on api gatewqay). Here is an example:
See https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-api.html; it documents which resources are generated and how they’re referenceable.
@azarboon If a resource has a reference to another resource, CFN understands that it depends on that other resource and you don’t need to add the explicit “DependsOn” to it.
Is this issue going to be addressed? It’s been open for almost a year now and it’s a roadblock in using SAM template when you want to secure your API endpoints with Cognito.