Michael Coxon
Michael Coxon

Reputation: 3535

AWS CDK: how to target API Gateway API from Route53

I have an existing domain name registered with AWS Route53 and I have set up a custom domain name in API Gateway. In the console i can configure things such that xxxxxx.zenxxxxxxfoundry.com from the outside, actually reaches the API Gateway API and then on through to my Lambda functions.

Now I want to achieve this with AWS CDK.

I have tried the following:

    const zone = route53.HostedZone.fromHostedZoneId(this, 'ZenithWebFoundryZone', 'ZXXXXXX04V8134');
    new route53.AliasRecord(this, 'BlogAPIRecord', {
      zone: zone,
      recordName: 'xxxxxx.zenxxxxxxfoundry.com',
      target: {
        bind: (): route53.AliasRecordTargetProps => ({
          dnsName: 'd-xxxxxxy00g.execute-api.ap-southeast-2.amazonaws.com',
          hostedZoneId: 'ZXXXXXX04V8134'
        })
      }
    });

which builds ok npm run build but when I run cdk synth I get the rather obtuse error:

$ cdk synth
HostedZone.fromHostedZoneId doesn't support "zoneName"
Subprocess exited with error 1

Switching on --trace didn't really help much: the additional info:

Error: Subprocess exited with error 1
    at ChildProcess.proc.on.code (/Users/mikecoxon/.npm-packages/lib/node_modules/aws-cdk/lib/api/cxapp/exec.ts:108:23)
    at ChildProcess.emit (events.js:189:13)
    at ChildProcess.EventEmitter.emit (domain.js:441:20)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:248:12)

I have looked through the whole stack script and there is no reference to zoneName anywhere. Does anyone know where this error comes from?

Upvotes: 13

Views: 17402

Answers (6)

lynkfox
lynkfox

Reputation: 2400

In the Documentation for Hosted Zones - specifically the methods fromHostedZoneID and fromHostedZoneAttributes:

https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-route53.HostedZone.html#static-fromwbrhostedwbrzonewbrattributesscope-id-attrs

it specifically mentions that "Hosted zone name becomes unavailable through this query"

the HostedZone object returned by fromHostedZoneID does not have an attribute for zoneName and so can't be used with route53.AliasRecord - you have to use fromHostedZoneAttributes. in order to retrieve the information setting the A record requires for cdk.

Of note: if you are assigning a domain in the declaration of your API, then use (in python cause that's what I have handy, sorry)

route53.ARecord(
        self, "DomainAliasLogicalID",
        zone=route53_host_zone,
        target=route53.RecordTarget.from_alias(route53_targets.ApiGateway(your_api_object)),
        record_name=your_domain_name
    )

If your setting up your own domain name object (not part of the API Gateway use route53_targets.ApiGatewayDomain(your_domain_object)

Upvotes: 0

MrfksIV
MrfksIV

Reputation: 930

I was facing the same issue using cdk 1.36.1 and managed to solve it using a new Custom Domain Name in ApiGateway, together withBasePath Mapping and then adding a CName record in an existing hosted zone on route53 pointing to the new custom domain:

import {BasePathMapping, DomainName, EndpointType, LambdaRestApi} from '@aws-cdk/aws-apigateway';
import {Certificate} from '@aws-cdk/aws-certificatemanager';
import {HostedZone, CnameRecord} from '@aws-cdk/aws-route53'

// First create a custom domain:
const customDomain = new DomainName(this, 'customDomain', {
      domainName: 'api.xxxxxxx.com',
      certificate: Certificate.fromCertificateArn(this, 'ACM_Certificate', ACM_CERTIFICATE_ARN),
      endpointType: EndpointType.EDGE
});

// create a new ApiGateway instance and associate a lambda function with it:
const api = new LambdaRestApi(this, 'MainGatewayEndpoint', {
      handler: toyFunction,
});

// Associate the Custom domain that we created with new APIGateway using BasePathMapping:
new BasePathMapping(this, 'CustomBasePathMapping', {
      domainName: custom,
      restApi: api
});

// Get a reference to AN EXISTING hosted zone using the HOSTED_ZONE_ID. You can get this from route53
const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
      hostedZoneId: PROD_HOSTED_ZONE_ID,
      zoneName: 'xxxxxxx.com'
});

// Finally, add a CName record in the hosted zone with a value of the new custom domain that was created above:
new CnameRecord(this, 'ApiGatewayRecordSet', {
      zone: hostedZone,
      recordName: 'api',
      domainName: customDomain.domainNameAliasDomainName
});

Upvotes: 12

jogold
jogold

Reputation: 7407

With aws-cdk v1 should be able to do the following:

const zone = route53.HostedZone.fromHostedZoneAttributes(this, 'ZenithWebFoundryZone', {
  hostedZoneId: 'ZXXXXXX04V8134',
  zoneName: 'zenxxxxxxfoundry.com' // your zone name here
});

new route53.ARecord(this, 'BlogAPIRecord', {
  zone,
  recordName: 'xxxxxx.zenxxxxxxfoundry.com',
  target: route53.RecordTarget.fromAlias({
    bind() {
      return {
        dnsName: 'd-xxxxxxy00g.execute-api.ap-southeast-2.amazonaws.com', // Specify the applicable domain name for your API.,
        hostedZoneId: 'XXXX', // Specify the hosted zone ID for your API.
      };
    },
  }),
});

If your API is in the same stack/code base you can get the dnsName and hostedZoneId from it (it's a CF attribute).

Otherwise refer to DNSName and HostedZoneId in the AWS::Route53::RecordSet AliasTarget documentation.

Note: the hostedZoneId for your alias record is not the same as the hosted zone id of your own zone.

Upvotes: 9

cjjenkinson
cjjenkinson

Reputation: 367

I managed to get this working but was caught out with the Hosted Zone name requiring the '.' at the end of it.

This is for EXISTING hosted zones that have been created manually and need to be looked up to add alias records through CDK.

const hostedZone = route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
  hostedZoneId: config.apiHostedZoneId,
  name: `${config.apiDomainName}.`,
});

new route53.RecordSet(this, 'ApiRecordSetA', {
  zone: hostedZone,
  recordType: route53.RecordType.A,
  recordName: config.apiDomainName,
  target: route53.RecordTarget.fromAlias(new route53Targets.ApiGatewayDomain(apiDomain))
});

new route53.RecordSet(this, 'ApiRecordSetAAAA', {
  zone: hostedZone,
  recordType: route53.RecordType.AAAA,
  recordName: config.apiDomainName,
  target: route53.RecordTarget.fromAlias(new route53Targets.ApiGatewayDomain(apiDomain))
});

Upvotes: 2

Kennu
Kennu

Reputation: 1102

In AWS CDK 0.36.1 you can use the @aws-cdk/aws-route53-targets package to create aliases.

import { HostedZone, RecordSet, RecordType, RecordTarget } from '@aws-cdk/aws-route53'
import { ApiGatewayDomain } from '@aws-cdk/aws-route53-targets'
import { Certificate } from '@aws-cdk/aws-certificatemanager'

// ...

  const customDomain = new apigateway.DomainName(this, 'CustomDomain', {
    domainName: props.apiDomain,
    certificate: Certificate.fromCertificateArn(this, 'Certificate', props.certificateArn),
    endpointType: apigateway.EndpointType.EDGE,
  })

  const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
    hostedZoneId: props.hostedZoneId,
    zoneName: props.hostedZoneName,
  })

  new RecordSet(this, 'ApiRecordSetA', {
    zone: hostedZone,
    recordType: RecordType.A,
    recordName: 'api',
    target: RecordTarget.fromAlias(new ApiGatewayDomain(customDomain))
  })

  new RecordSet(this, 'ApiRecordSetAAAA', {
    zone: hostedZone,
    recordType: RecordType.AAAA,
    recordName: 'api',
    target: RecordTarget.fromAlias(new ApiGatewayDomain(customDomain))
  })

Upvotes: 5

i906
i906

Reputation: 1687

I got the same error with the following:

const zone = route53.HostedZone.fromHostedZoneId(this, 'MyZone', 'ZXXXXXXXXXXXXX');

I have a zone in Route53 similar to below:

Domain Name:    example.com.
Type:           Public Hosted Zone
Hosted Zone ID: ZXXXXXXXXXXXXX

I changed to the following and it worked (CDK 0.34.0):

const zone = new route53.HostedZoneProvider(this,  {
  domainName: 'example.com',
}).findAndImport(this, 'MyZone');

Upvotes: 0

Related Questions