Skip to content
5 changes: 5 additions & 0 deletions packages/aws-cdk-lib/aws-route53/lib/hosted-zone-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,8 @@ export interface HostedZoneAttributes {
* Reference to a public hosted zone
*/
export interface PublicHostedZoneAttributes extends HostedZoneAttributes { }

/**
* Reference to a private hosted zone
*/
export interface PrivateHostedZoneAttributes extends HostedZoneAttributes { }
29 changes: 26 additions & 3 deletions packages/aws-cdk-lib/aws-route53/lib/hosted-zone.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Construct } from 'constructs';
import { HostedZoneProviderProps } from './hosted-zone-provider';
import { HostedZoneAttributes, IHostedZone, PublicHostedZoneAttributes } from './hosted-zone-ref';
import { HostedZoneAttributes, IHostedZone, PublicHostedZoneAttributes, PrivateHostedZoneAttributes } from './hosted-zone-ref';
import { IKeySigningKey, KeySigningKey } from './key-signing-key';
import { CaaAmazonRecord, ZoneDelegationRecord } from './record-set';
import { CfnHostedZone, CfnDNSSEC, CfnKeySigningKey } from './route53.generated';
Expand Down Expand Up @@ -314,7 +314,7 @@ export interface PublicHostedZoneProps extends CommonHostedZoneProps {
/**
* Represents a Route 53 public hosted zone
*/
export interface IPublicHostedZone extends IHostedZone {}
export interface IPublicHostedZone extends IHostedZone { }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a whitespace change. Could you please revert this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have addressed the whitespace change as requested.


/**
* Create a Route53 public hosted zone.
Expand Down Expand Up @@ -481,7 +481,7 @@ export interface PrivateHostedZoneProps extends CommonHostedZoneProps {
/**
* Represents a Route 53 private hosted zone
*/
export interface IPrivateHostedZone extends IHostedZone {}
export interface IPrivateHostedZone extends IHostedZone { }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a whitespace change. Could you please revert this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have addressed the whitespace change as requested.


/**
* Create a Route53 private hosted zone for use in one or more VPCs.
Expand Down Expand Up @@ -520,6 +520,29 @@ export class PrivateHostedZone extends HostedZone implements IPrivateHostedZone
return new Import(scope, id);
}

/**
* Imports a private hosted zone from another stack.
*
* Use when both hosted zone ID and hosted zone name are known.
*
* @param scope the parent Construct for this Construct
* @param id the logical name of this Construct
* @param attrs the PrivateHostedZoneAttributes (hosted zone ID and hosted zone name)
*/
public static fromPrivateHostedZoneAttributes(scope: Construct, id: string, attrs: PrivateHostedZoneAttributes): IPrivateHostedZone {
class Import extends Resource implements IPrivateHostedZone {
public readonly hostedZoneId = attrs.hostedZoneId;
public readonly zoneName = attrs.zoneName;
public get hostedZoneArn(): string {
return makeHostedZoneArn(this, this.hostedZoneId);
}
public grantDelegation(grantee: iam.IGrantable): iam.Grant {
return makeGrantDelegation(grantee, this.hostedZoneArn);
}
}
return new Import(scope, id);
}

constructor(scope: Construct, id: string, props: PrivateHostedZoneProps) {
super(scope, id, props);
// Enhanced CDK Analytics Telemetry
Expand Down
116 changes: 116 additions & 0 deletions packages/aws-cdk-lib/aws-route53/test/hosted-zone.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,122 @@ test('grantDelegation on private imported zones', () => {
});
});

describe('PrivateHostedZone.fromPrivateHostedZoneAttributes', () => {
test('can import private hosted zone with both id and name', () => {
// GIVEN
const stack = new cdk.Stack(undefined, 'TestStack', {
env: { account: '123456789012', region: 'us-east-1' },
});

// WHEN
const privateZone = PrivateHostedZone.fromPrivateHostedZoneAttributes(stack, 'PrivateZone', {
hostedZoneId: 'private-hosted-id',
zoneName: 'example.local',
});

// THEN
expect(privateZone.hostedZoneId).toBe('private-hosted-id');
expect(privateZone.zoneName).toBe('example.local');
});

test('provides access to both hostedZoneId and zoneName', () => {
// GIVEN
const stack = new cdk.Stack(undefined, 'TestStack', {
env: { account: '123456789012', region: 'us-east-1' },
});

// WHEN
const privateZone = PrivateHostedZone.fromPrivateHostedZoneAttributes(stack, 'PrivateZone', {
hostedZoneId: 'Z123456789',
zoneName: 'private.example.com',
});

// THEN
expect(privateZone.hostedZoneId).toBe('Z123456789');
expect(privateZone.zoneName).toBe('private.example.com');
// Should not throw error when accessing zoneName (unlike fromPrivateHostedZoneId)
expect(() => privateZone.zoneName).not.toThrow();
});

test('constructs correct ARN', () => {
// GIVEN
const stack = new cdk.Stack(undefined, 'TestStack', {
env: { account: '123456789012', region: 'us-east-1' },
});

// WHEN
const privateZone = PrivateHostedZone.fromPrivateHostedZoneAttributes(stack, 'PrivateZone', {
hostedZoneId: 'Z123456789',
zoneName: 'private.example.com',
});

// THEN
expect(stack.resolve(privateZone.hostedZoneArn)).toEqual({
'Fn::Join': [
'',
[
'arn:',
{ Ref: 'AWS::Partition' },
':route53:::hostedzone/Z123456789',
],
],
});
});

test('supports grantDelegation', () => {
// GIVEN
const stack = new cdk.Stack(undefined, 'TestStack', {
env: { account: '123456789012', region: 'us-east-1' },
});

const role = new iam.Role(stack, 'Role', {
assumedBy: new iam.AccountRootPrincipal(),
});

// WHEN
const privateZone = PrivateHostedZone.fromPrivateHostedZoneAttributes(stack, 'PrivateZone', {
hostedZoneId: 'Z123456789',
zoneName: 'private.example.com',
});
privateZone.grantDelegation(role);

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
PolicyDocument: {
Statement: [
{
Action: 'route53:ChangeResourceRecordSets',
Effect: 'Allow',
Resource: {
'Fn::Join': [
'',
[
'arn:',
{
Ref: 'AWS::Partition',
},
':route53:::hostedzone/Z123456789',
],
],
},
Condition: {
'ForAllValues:StringEquals': {
'route53:ChangeResourceRecordSetsRecordTypes': ['NS'],
'route53:ChangeResourceRecordSetsActions': ['UPSERT', 'DELETE'],
},
},
},
{
Action: 'route53:ListHostedZonesByName',
Effect: 'Allow',
Resource: '*',
},
],
},
});
});
});

describe('Hosted Zone with dot', () => {
test('Hosted Zone constructs without trailing dot by default', () => {
// GIVEN
Expand Down
Loading