- Create an API instance
- Configure a stage
- Deploy API changes from swagger
Problems I Ran Into
As I attempted different appraoches, I experienced a few common problems:
- Deployment of revised swagger would not occur
- Updating the stack with revised swagger would error with “stage already exists”
- No deployment history
CloudFormation Basics
Being new to CloudFormation, I didn’t fully grok some key CloudFormation concepts which was a barrier to getting my template right. The primary concept is that CloudFormation templates dictate desired state, not a set of operations to perform. If a resource is defined in your template, it will be created. If a resource already exists, it will not be created, but can be updated if its properties change. If a resource is removed from the template it will be deleted.API Gateway Basics
- Swagger is used to define a REST API.
- That REST API is deployed.
- A stage references a deployment to make the API available via an endpoint.
The Template
--- AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Setup our API Gateway instances Parameters: StageName: Type: String Default: 'example_stage' Description: 'The name of the stage to be created and managed within our API Gateway instance.' Resources: Api: Type: AWS::ApiGateway::RestApi Properties: Name: ExampleApi EndpointConfiguration: Types: - REGIONAL # The body should contain the actual swagger Body: $SWAGGER_DEFINITION$ # Timestamp is added so that each deployment is unique. Without a new timestamp, the deployment will not actually occur ApiDeployment$TIMESTAMP$: Type: AWS::ApiGateway::Deployment DependsOn: [ Api ] # we want to retain our deployment history DeletionPolicy: Retain Properties: RestApiId: Ref: Api ApiStage: Type: AWS::ApiGateway::Stage DependsOn: [ApiDeployment$TIMESTAMP$] Properties: RestApiId: Ref: Api DeploymentId: Ref: ApiDeployment$TIMESTAMP$ StageName: {Ref: StageName} MethodSettings: - ResourcePath: "/*" HttpMethod: "*" LoggingLevel: INFO MetricsEnabled: true DataTraceEnabled: true Outputs: Endpoint: Description: Endpoint url Value: Fn::Sub: 'https://${Api}.execute-api.${AWS::Region}.amazonaws.com'
Key Points
The deployment resource can specify a StageName and StageDescription. If this is done the deployment will implicitly create a stage that is not managed by CloudFormation. If we attempt to create a stage with the same name we’ll get an error that it already exists. Instead we’re better off creating a deployment that doesn’t prescribe anything about the stage and explicitly defining a stage resource.Each deployment needs a unique id. Otherwise, CloudFormation will determine the deployment already exists and will not create a deployment. Without a new deployment, changes to our REST API will not be visible. Furthermore, we set a deletion policy on our deployment to retain the deployment. Otherwise, when the timestamp on our deployment changes, CloudFormation will want to delete the old deployment. If this deletion occurs, the deployment is removed from our stage deployment history. With this policy, the deployment is removed from the stack but not deleted from API Gateway.
EDIT:
A script must be written to preprocess the the template and replace the $TIMESTAMP$ token with the timestamp WITHOUT any separators e.g. 05012019355 Thanks to somebody for pointing this out in the comments.
Disclaimer: I wrote this post as an enthusiast. The content is not the official position of Amazon or AWS.