Photo by Ken Suarez on Unsplash
AWS CloudFormation: How to use API Gateway custom domain mapping
How to resolve the error: Invalid stage identifier specified (Service: ApiGateway, Status Code: 400)
API Gateway is a very popular service, and for good reasons. Setting up API Gateway via CloudFormation is very common. But I often find new CloudFormation users struggle with the custom domain mapping.
Custom domain names are simpler and more intuitive URLs that you can provide to your API users. An API's specific stages and versions can be associated with a custom domain name and managed through API Gateway. A custom domain allows you to change the underlying API Gateway for upgrades, testing or DR purposes without affecting the end-users. Hence it's recommended to use a custom domain when possible.
In CloudFormation, API Gateway and its custom domain are defined in the Resources section. And the examples we often find online shows something like this:
Resources:
MyAPIName:
Type: "AWS::Serverless::Api"
Properties:
StageName: production
Name: ModuleABCAPI
EndpointConfiguration: Regional
Cors:
AllowMethods: "'POST, GET, OPTIONS'"
AllowHeaders: "'Content-Type, X-Amz-Date, Authorization, X-Api-Key, x-requested-with'"
AllowOrigin: "'*'"
MaxAge: "'600'"
MyAPIMapping:
Type: AWS::ApiGateway::BasePathMapping
Properties:
BasePath: management
DomainName: api.appdomain.com
RestApiId: !Ref MyAPIName
Stage: production
But the deployment often fails with the below error:
Resource handler returned message: "Invalid stage identifier specified (Service: ApiGateway, Status Code: 400, .....xxxxx.... HandlerErrorCode: InvalidRequest)
So let's investigate further: The resource of type 'AWS::Serverless::Api' generates three resources, which are of type :
- AWS::ApiGateway::RestApi
- AWS::ApiGateway::Deployment
- AWS::ApiGateway::Stage
You can also refer to the below snippet from the AWS documentation:
When an AWS::Serverless::Api is specified, AWS Serverless Application Model (AWS SAM) always generates an AWS::ApiGateway::RestApi base AWS CloudFormation resource. In addition, it also always generates an AWS::ApiGateway::Stage and an AWS::ApiGateway::Deployment resource.
The resource of type "AWS::ApiGateway::Stage" needs to be created before the resource "AWS::ApiGateway::BasePathMapping" starts its creation, as the StageName mentioned in the "AWS::ApiGateway::BasePathMapping" resource, and it should be present before the resource of type "AWS::ApiGateway::BasePathMapping" start its creation.
When defining our stack like the above example, creating the resource of type "AWS::ApiGateway::Stage" isn't guaranteed to start before the "AWS::ApiGateway::BasePathMapping". It intermittently fails with the "Invalid stage identifier specified" error.
The sequence in which the resources generated by the resource "AWS::Serverless::Api" are created is as follows:
AWS::ApiGateway::RestApi --> AWS::ApiGateway::Deployment --> AWS::ApiGateway::Stage
So, to avoid the error and to make sure that the creation of the resource "MyAPIMapping(AWS::ApiGateway::BasePathMapping)" starts only after the resource of type 'AWS::ApiGateway::Stage' is created, we need to use the 'DependsOn' attribute in the resource "MyAPIMapping" for the resource of type 'AWS::ApiGateway::Stage' as below:
Resources:
MyAPIName:
Type: "AWS::Serverless::Api"
Properties:
StageName: production
Name: ModuleABCAPI
EndpointConfiguration: Regional
Cors:
AllowMethods: "'POST, GET, OPTIONS'"
AllowHeaders: "'Content-Type, X-Amz-Date, Authorization, X-Api-Key, x-requested-with'"
AllowOrigin: "'*'"
MaxAge: "'600'"
MyAPIMapping:
Type: AWS::ApiGateway::BasePathMapping
DependsOn:
- MyAPINameStage
Properties:
BasePath: management
DomainName: api.appdomain.com
RestApiId: !Ref MyAPIName
Stage: production
The Logical Name of the 'AWS::ApiGateway::Stage' resource type would be the concatenation of the API logical name and word Stage: Stage. You can also refer to this GitHub issue for further details.
So, based on our example, the DependsOn attribute in the 'MyAPIMapping' resource should refer to the "MyAPINameStage(AWS::ApiGateway::Stage)" resource as shown above.
Once the above attribute is added, the stack deployment should work. I hope you will find the info useful and possibly save some time in the process.