The Problem
I had an issue this week. I was initializing AWS accounts for our developers and they required a subdomain per account. Think user.blah.com.
I wanted the developer account to be able to manage all records inside this subdomain, so they could create api.user.blah.com, staging.user.blah.com and so on. And if you manage your own subdomain, you can also generate SSL certificates for them as well.
To achieve this with AWS, you simply need to create the hosted zone for the subdomain in Route53, find out which AWS nameservers it was allocated, and then add a 'NS' record for your subdomain using those nameservers in the DNS zone for the parent domain, ie blah.com.
Now, it sounds simple, but I don't want to do this every time I onboard a new developer. I want it to happen automatically when I deploy the developers initialization stack. Fortunately, it's possible, and not very difficult to implement!
The Solution
The solution here requires you to be using AWS Organizations to create AWS accounts for your developers.
The Parent Account
Firstly, we create a new CDK project and start editing our lib/stack.ts file.
I use a parameter I can pass in to my CDK stack to define the domain that will be created in Route53, eg. "blah.com". Lets capture that with CDK first:
// Get the domain from the user.
const domain = new cdk.CfnParameter(this, "domain", {
type: "String",
description: "The domain to be created. "
}).value.toString();
Next we're going to create the Route53 Hosted Zone:
// Create the Route53 zone
const hostedZone = new r53.PublicHostedZone(this, 'zone', {
zoneName: domain,
});
Now, this is where the magic happens. We're going to create a role that allows every other account which is part of your AWS organization to be able to add a NS record to the hosted zone we just created. Then, we grant the delegation access to the role. Be sure to change the o-xxxxxxx's to your Organization ID, of course.
// Create the role
const role = new Role(this, 'RootZoneOrganizationRole', {
assumedBy: new OrganizationPrincipal('o-xxxxxxxxxx'),
roleName: 'HostedZoneDelegationRole',
});
// Grant the delegation
hostedZone.grantDelegation(role);
CDK is doing a lot of heavy lifting under the hood here, it creates a role with correct permissions and a trust relationship that allows all the other accounts to do what we need it to do. Anyway, that's all we need for our parent account CDK stack! Save it and deploy it, probably something like this:
$ cdk deploy --parameters domain=user.blah.com --profile parent
The Child Account
Now we're going to create a stack for the child account, do your cdk init and so on to create another new stack project and get ready to add some code.
Lets capture the child account domain, in this case it we'll expect user.blah.com
// Get the subdomain from the user
const domain = new cdk.CfnParameter(this, "domain", {
type: "String",
description: "The subdomain to be created. "
}).value.toString();
Create the hosted zone in Route53
// Create the Route53 zone
const hostedZone = new r53.PublicHostedZone(this, 'zone', {
zoneName: domain,
});
Generate the delegation role ARN, specifying the parent account which hosts the blah.com domain
const delegationRoleArn = Stack.of(this).formatArn({
account: '1234567890',
region: '',
resource: 'role',
resourceName: 'HostedZoneDelegationRole',
service: 'iam',
});
Create the delegation role from this ARN
const delegationRole = iam.Role.fromRoleArn(this, 'DelegationRole', delegationRoleArn);
And now we can add the zone as a delegation record to the parent account Route53:
new r53.CrossAccountZoneDelegationRecord(this, 'DelegationRecord', {
delegationRole,
delegatedZone: hostedZone,
parentHostedZoneName: 'blah.com',
});
and that's it! that's all you need! any records added to the hostedZone from this point on will be completely valid, because the parent account now recognizes the child account's route53 configuration as the authority for that subdomain.
Huge shoutout to AWS Community Builder Matt Morgan who has created an awesome AWS Organizations for devs GitHub repository where you can learn about deploying full AWS Organizations!
Top comments (2)
I'm trying this approach with all my cdk libs updated to 2.87.0 but am getting hostedZone.grantDelegation is not a function... any ideas?
Found similar docs here: docs.aws.amazon.com/cdk/api/v2/doc...
Hmm, try import all from aws-iam instead? or at least Grant
The method is: