Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native serverless (CloudFormation) support for API Gateway custom domain #5878

Open
lqueryvg opened this issue Feb 28, 2019 · 6 comments
Open

Comments

@lqueryvg
Copy link

This is a Feature Proposal

Description

This feature proposal is exactly the same as #3906 which was closed with a referral to the custom-domain plugin.

The feature request is to natively (through CloudFormation) support API Gateway custom domains, like so:

provider:
  aws:
    apiGateway:
      customDomain: <custom domain>
      certificateArn: <arn of cert from ACM> 

Unfortunately the custom-domain plugin does not meet my use case which is as follows:

I have (fairly) complex infrastructure which combines Serverless with Terraform.
I'm creating the SSL & DNS records in a pipeline in Terraform and I want to
integrate these resources with my serverless deployment, using the approach suggested
here: https://serverless.com/blog/definitive-guide-terraform-serverless/
TLDR: Terraform outputs resource information to SSM variables which serverless.yml
can then consume as variables in serverless.yml.

Unfortunately, if my SSL certifcate changes, neither serverless nor the
custom-domain plugin will notice the change and apply the change to the API gateway.
Even if I were to run serverless create_domain every time in my pipeline, the
change will still not be detected.

The fundamental problem here is that the custom-domain plugin works by making
API calls to "enhance" an AWS resource that is supposed to be being managed
by the CloudFormation stack.

Surely, the correct solution would be for serverless to add the appropriate
CloudFormation syntax to the stack. As described here:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-domainname.html#cfn-apigateway-domainname-certificatearn

Whenever the certificate changes, CloudFormation will then change API Gateway accordingly.

@dougmoscrop
Copy link
Contributor

dougmoscrop commented Feb 28, 2019

I think you can just use regular CloudFormation (in your serverless.yml) for this:

resources:
  Resources:
    Domain:
      Type: AWS::ApiGateway::DomainName
      Properties: 
        CertificateArn: ${ssm:...}
        DomainName: ${ssm:...}
    BasePathMapping:
      Type: AWS::ApiGateway::BasePathMapping
      Properties:
        BasePath: ...
        DomainName:
        RestApiId: ...
        Stage: ...

That said, the DomainName resource takes ~40 minutes to provision sometimes, so we usually do that in a separate layer than Serverless - either with Terraform or a separate CloudFormation stack. Then we just set up the BasePathMapping in Serverless.

@lqueryvg
Copy link
Author

@dougmoscrop thanks I will give that a try.
(Although it would be nice if you could do this using regular serverless yaml.)

I'm guessing that it takes so long because it's waiting for the "hidden" CloudFront distribution to be updated ? Hopefully that will only happen when it's first created. I will do some experimentation.

@dougmoscrop
Copy link
Contributor

Yeah it's the CloudFront distribution that takes so long.

@lqueryvg
Copy link
Author

lqueryvg commented Aug 1, 2019

Sadly, when trying to add the BasePathMapping I'm hitting the following error:

Unhandled rejection ServerlessError: An error occurred: BasePathMapping - Invalid stage identifier specified (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: c2721bf6-548e-44a3-9e65-a1b7c37a0bcd).

It looks like it depends on the stage being available, but it hasn't been created yet.

Like this... #4029

@dougmoscrop
Copy link
Contributor

Did you put a DependsOn?

@lqueryvg
Copy link
Author

@dougmoscrop, here's how I did it in the end...

resources:
  Resources:
    apiGatewayDeployment:
      # Added to circumvent the problem:
      #   Unhandled rejection ServerlessError: An error occurred: BasePathMapping - Invalid stage
      #   identifier specified (Service: AmazonApiGateway; Status Code: 400; Error Code:
      #   BadRequestException; Request ID: .... etc...
      # Described here:
      #   https://github.com/serverless/serverless/issues/4029
      #
      Type: "AWS::ApiGateway::Deployment"
      DependsOn:
        # Make this resource depend on any API gateway function (see issue above) as follows:
        # 1. Choose any single function with an API gateway http event
        # 2. Take the value for the path (without slashes) and the value for the method
        # 3. Capitalize the first letter of each and concatenate them together.
        # 4. Prepend "ApiGatewayMethod"
        # Example:
        #      - http:
        #          path: items
        #          method: get
        # Becomes:
        #    ApiGatewayMethodItemsGet
        #
        - ApiGatewayMethodItemsGet
      Properties:
        RestApiId:
          Ref: ApiGatewayRestApi
        StageName: ${self:provider.stage}

    BasePathMapping:
      Type: AWS::ApiGateway::BasePathMapping
      DependsOn:
        - apiGatewayDeployment
      Properties:
        DomainName:  my.domain.name.com
        RestApiId:
          Ref: ApiGatewayRestApi
        Stage: ${self:provider.stage}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants