Serverless Framework + AWS: automatically creating certificate and domain for your app
Tags: serverless, aws, certificate, domain
Published: 2022-03-04 10:16:30

This is a guide showing how you can automatically create an SSL certificate on Amazon Certificate Manager (ACM) and configure a subdomain on Route 53 Hosted Zone using Serverless Framework with some wonderful plugins.

Before start:

- You need an AWS account;
- Have a [Hosted Zone configured on Route53](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/CreatingHostedZone.html) with some domain name;
- Have a little bit of knowledge of using Serverless Framework.

Firstly, you need to know that we'll use some Serverless Framework plugins to turn our life easier:

- [Serverless Domain Manager](https://www.serverless.com/plugins/serverless-domain-manager) to control Route53 rules and API Gateway custom domain configs.
- [Serverless Certificate Creator](https://github.com/schwamster/serverless-certificate-creator#readme) to create certificates on Amazon Certificate Manager and confirm by DNS method.

> Note: I’ll show some scripts using NodeJS, but Serverless Framework can be used on almost any language (to not say literally anywhere).

---

With this in mind, let's go ahead! Start installing dependencies:

`npm install serverless-domain-manager serverless-certificate-creator —-save-dev`

## Create a custom domain section

I usually separate domains to allow customization for each one, it would be like this in my stack:

```yml
# serverless.yml
...

custom:
  domains:
    # References to 'prod' stage
    prod:
      domainName: api.tiagoboeing.com
      certificateName: api.tiagoboeing.com
      
    # References to 'dev' stage  
    dev:
      domainName: api-dev.tiagoboeing.com
      certificateName: api-dev.tiagoboeing.com

    ...
```

Of course, the certificate name is chosen by you. I only like to use the same name of the domain to keep easier to locate on ACM.

> This is the way how I organize my apps, take this as an example to customize/start.

See our `custom` section:

```yml
custom:
  # Amazon Certificate Manager
  customCertificate:
    # Route 53 Hosted Zone name
    # don't forget the dot on the end!
    hostedZoneNames: "tiagoboeing.com."

    # Here we get our certificate name inside custom.domain.STAGE.certificateName
    # STAGE will be automatically filled with the value from "provider > stage"
    certificateName: ${self:custom.domains.${self:provider.stage}.certificateName}

    region: ${self:provider.region}

  # Route53
  customDomain:
    # Get value from "domains" section using stage that is being deployed
    domainName: ${self:custom.domains.${self:provider.stage}.domainName}

    # Same case of certificaName inside customCertificate
    certificateName: ${self:custom.domains.${self:provider.stage}.certificateName}

    # Enable plugin to create an A Alias and AAAA Alias records in Route53
    # mapping the domainName to the generated distribution domain name.
    createRoute53Record: true

    # Enable plugin to autorun create_domain/delete_domain as part of sls deploy/remove
    autoDomain: true
```

## Deploy

Take the following tips and tricks:

### To create the certificate on ACM run:

```
serverless create-cert

# or

npx serverless create-cert
```

Create the certificate on the deploying stage, you can change the stage using `--stage prod` flag or `STAGE=prod` env.

### Create domain - simply run

To create domain on Route 53, use:

```
serverless create_domain

# or

npx serverless create_domain
```

Keep in mind that `createRoute53Record` and `autoDomain` were enabled on `serverless.yml`, a simple `serverless deploy` will start automatically this step.

Deploying to another stage (not on the default: **dev**) you need to use the option `--stage` ([read more here](https://www.serverless.com/framework/docs/providers/aws/cli-reference/deploy)). As an example for the first time you can use:

```
# First deploy PROD stage

serverless create-cert --stage=prod

serverless deploy --stage=prod
```

It’s possible to manually generate a domain using serverless-domain-manager, you can do it using the command `serverless create_domain`, this isn’t needed if you have the `autoDomain` flag enabled (as mentioned earlier).

## Complete stack

See the complete Serverless stack below.

```yml
service: sls-my-app

plugins:
  - serverless-domain-manager
  - serverless-certificate-creator
  
provider:
  name: aws
  runtime: nodejs14.x
  stage: ${opt:stage, 'dev'}
  region: ${opt:region, 'us-east-1'}
  apiGateway:
    shouldStartNameWithService: true
  timeout: 5

  # passing base domain name and stage to our functions
  environment:
    DOMAIN: ${self:custom.customDomain.domainName}
    STAGE: ${self:provider.stage}

functions:
  authenticate:
    handler: src/functions/hello.handler
    description: Return a hello world
    timeout: 3
    events:
      - http:
          method: GET
          path: /
          cors: true

custom:
  domains:
    prod:
      domainName: api.tiagoboeing.com
      certificateName: api.tiagoboeing.com
    dev:
      domainName: api-dev.tiagoboeing.com
      certificateName: api-dev.tiagoboeing.com

  # Amazon Certificate Manager
  customCertificate:
    hostedZoneNames: "tiagoboeing.com." # don't forget the dot on the end - is required by Route53
    certificateName: ${self:custom.domains.dev.certificateName}
    region: ${self:provider.region}

  # Route53
  customDomain:
    domainName: ${self:custom.domains.${self:provider.stage}.domainName}
    certificateName: ${self:custom.domains.dev.certificateName}
    createRoute53Record: true
    autoDomain: true
```

---

📝 Thanks, Paulo Weber by the review this text.

### Read more

- [Automating changelogs in your NodeJS project](https://tiagoboeing.medium.com/automating-changelog-in-your-nodejs-project-c54bdbb56e57)

**PT-BR:**

- [CDN de alta disponibilidade na AWS com S3, CloudFront e Route53](https://tiagoboeing.medium.com/cdn-de-alta-disponibilidade-na-aws-com-s3-cloudfront-e-route53-d08da8fab0ab)
- [Automatizando geração de changelogs em seus projetos NodeJS](Automatizando geração de changelogs em seus projetos NodeJS)