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

[aws-certificatemanager] DnsValidatedCertificate support for separate hosted zones and accounts #8934

Closed
podoprigo opened this issue Jul 8, 2020 · 24 comments · Fixed by #21982
Labels
@aws-cdk/aws-certificatemanager Related to Amazon Certificate Manager effort/large Large work item – several weeks of effort feature-request A feature should be added or improved. p1

Comments

@podoprigo
Copy link

Hello there!

Currently, DnsValidatedCertificate construct only allows using a single hosted zone.
If the ACM certificate I'm creating needs SANs that belong to multiple hosted zones, stack creation fails.

DnsValidatedCertificate should allow skipping/waiting for SANs that belong to different hosted zones. For example, it would update correct DNS records in the target hosted zone, and display validation records for the zones it doesn't have access to, while halting stack update.

Use Case

I'm working on a project that involves separate AWS accounts per region/stage.
My 'root' AWS account contains the hosted zone for the root domain (example.com), while regional accounts contain hosted zones for sub-domains (us-west-2.example.com, us-east-1.example.com).

While I can work around hosted zones creation (and the necessity to copy NS records from us-west-2.example.com to example.com), doing the same for ACM certificates is quite difficult.

For example, I need to create a certificate for us-west-2.example.com, with a SAN record for example.com.

DnsValidatedCertificate doesn't allow me to do that, since it tries to update us-west-2.example.com's DNS with 2 validation records, and the one for example.com doesn't belong there.

Certificate also doesn't allow me to do that, since it waits for DNS records to be updated : one for example.com and one for us-west-2.example.com. The problem here is that both hosted zone and certificate for us-west-2.example.com are in the same stack, so I have a bit of a chicken/egg problem.


This is a 🚀 Feature Request

@podoprigo podoprigo added feature-request A feature should be added or improved. needs-triage This issue or PR still needs to be triaged. labels Jul 8, 2020
@podoprigo podoprigo changed the title [aws-certificatemanager] DnsValidatedCertificate allows hosted zones for SANs [aws-certificatemanager] DnsValidatedCertificate allows using separate hosted zones for SANs Jul 8, 2020
@github-actions github-actions bot added the @aws-cdk/aws-certificatemanager Related to Amazon Certificate Manager label Jul 8, 2020
@jogold
Copy link
Contributor

jogold commented Jul 8, 2020

Would #8552 solve this?

@podoprigo
Copy link
Author

While it solves the problem with multiple hosted zones, it doesn't solve cross-account support, afaik.
To solve this part of the issue, there should be an ability to assume cross-account roles and have role-per-hostedzone mapping.

@podoprigo podoprigo changed the title [aws-certificatemanager] DnsValidatedCertificate allows using separate hosted zones for SANs [aws-certificatemanager] DnsValidatedCertificate allows using separate hosted zones for SANs (for cross-account use cases) Jul 8, 2020
@SomayaB SomayaB assigned njlynch and unassigned skinny85 Jul 10, 2020
@konstantinj
Copy link

see #4469

@rrrix
Copy link

rrrix commented Jul 14, 2020

@podoprigo can give some background on how you would use such a certificate? If a Route53 hosted zone is exclusive to a single account, particularly the apex domain example.com, how would multiple accounts effectively "attach" services to a single apex domain?

Are you trying to do latency based / regional DNS routing across multiple accounts? Like with an ALB or Regional API gateway in each "regional account" that only serves content for that region, but the latency based DNS routing for example.com can go to any one of those regional accounts?

@podoprigo
Copy link
Author

@rrrix - you are right. Since opening this ticket, we pivoted a bit. Our setup is :

  • account A owns the hosted zone for example.com, located in CDK stack 1
  • account B owns the hosted zone for us-west-2.example.com, located in CDK stack 2
  • account C owns the hosted zone for us-east-1.example.com, located in CDK stack 2
  • ...
  • account Z for <region>.example.com, located in CDK stack 2

Latency based routing is setup in account A. Accounts B,C,...,Z have ACM certs for example.com and *.example.com, created using Certificate with validationMethod: ValidationMethod.DNS.

The plan for stack extension is the following:

  1. add a new region/account to stack 2.
  2. synthesize the new change for stack 2
    1. CF will halt at DNS validation step, waiting for validation data to appear in example.com DNS record
    2. copy/paste DNS validation records from stack 2 into stack 1 and synthesize stack 1
    3. wait for stack 2 synthesis to finish
  3. update stack 1 so that it now knows to redirect traffic to the new region

It works, but it requires manual intervention and copying strings across stacks. As pointed out by @konstantinj, we'll most probably need to rollout our own automation, if we need it.

@njlynch njlynch added effort/large Large work item – several weeks of effort p2 and removed needs-triage This issue or PR still needs to be triaged. labels Jul 31, 2020
@Tanuel
Copy link

Tanuel commented Aug 21, 2020

Hi, i have a quick question: Does this issue only regard cross-account use cases?
I wanted to use DnsValidatedCertificate with SANs on multiple zones for CloudFront, however it only allows to be used on a single HostedZone. The rest of the stack(s) is deployed to eu-central-1, however for CloudFront i need a certificate in us-east-1

While Certificate supports SANs, i cannot deploy it to another region, thus forcing me to deploy everything to us-east-1 since cross-region exports are also not supported and i would prefer to not use a workaround like using parameter store. we operate almost exclusively in the eu-central region so would be great to deploy our infra there

Would appreciate if DnsValidatedCertificate would support SANs

Want to follow with Code-Example:

    // approach with DnsValidatedCertificate
    const cert = new DnsValidatedCertificate(this, "MyCert", {
      hostedZone: mainZone, // <- this parameter is not required using the Certificate Class (see below)
      domainName: mainZone.zoneName,
      region: "us-east-1", // good thing here is i can set region so i can use it with CloudFront
      validation: CertificateValidation.fromDnsMultiZone(allZones),
      subjectAlternativeNames: sans,
    });

    //approach with Certificate - does not allow setting region, but doesnt require to set a specific zone
    //this works, but only in the current region
    const cert = new Certificate(this, "Certificate", {
      domainName,
      validation: CertificateValidation.fromDnsMultiZone(allZones),
      subjectAlternativeNames: sans,
    });

Error Message for reference (using DnsValidatedCertificate with multizone):

8:57:10 AM | UPDATE_FAILED | AWS::CloudFormation::CustomResource | MyStackCertifica...orResource12345
Failed to update resource. [RRSet with DNS name _asdfghjkl.example.com. is not permitted in zone www.beispiel.de.]

This writeup turned out longer than expected so im sorry for hijacking this issue
TL;DR: I think this issue does not only regard cross-account use cases

@njlynch njlynch changed the title [aws-certificatemanager] DnsValidatedCertificate allows using separate hosted zones for SANs (for cross-account use cases) [aws-certificatemanager] DnsValidatedCertificate support for separate hosted zones and accounts Aug 21, 2020
@aripalo
Copy link

aripalo commented Sep 30, 2020

I agree with what @Tanuel said, that this issue is not just about cross-account.

It's quite common to see zone delegation even within single account, since it keeps things a bit more organized, but at the same time there are use cases to having ACM certificate that targets multiple zones.

Then again, having to deploy CloudFront certificate separately in different stack to different region (us-east-1) makes things unnecessarily complicated - compared to having a certificate construct that can deploy to us-east-1 from another region.

Having support for setting the region in certificatemanager.Certificate construct would solve these issues.

@kmturley
Copy link

kmturley commented Dec 22, 2020

I need this feature too. It's common to centralize Domain names and certificates in a single AWS Account.
Tried using Resource Access Manager, but it doesn't allowing sharing of Domain Names and Certificates cross-account

This seems to be the best solution currently:
https://stackoverflow.com/questions/58101817/cdk-dnsvalidatedcertificate-can-create-a-certificate-in-a-linked-aws-account-w

@GibzonDev
Copy link
Contributor

Continuing discussion from #12657
@njlynch You made a suggestion that a possible workaround could be to create a Role in the "child account" and grant access to modifying the host zone in the "root account".
I tried this but to no luck.

I'm posting my setup in case someone can spot an error. It would be great with a workaround for this problem.

const certRole = iam.Role.fromRoleArn(this, 'Role', 'arn:aws:iam::222222222222:role/TestRole', { mutable: true });
const dnsValidatedCertificate = new acm.DnsValidatedCertificate(this, 'CrossRegionCertificate', {
  domainName: 'sub.domain.com',
  hostedZone: domainZone, // Comes from "root account"
  region: this.region,
  customResourceRole: certRole,
});

TestRole has AdministratorAccess (for the sake of testing), and also an inline policy to assume a role in the root account.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::111111111111:role/ChildAccountCDKUser"
        }
    ]
}

I've added "lambda.amazonaws.com" to the trust relationship for this role (Action: sts:AssumeRole).

The ChildAccountCDKUser in the "root account" has AdministratorAccess (for the sake of testing), and also Trust relationship set to allow sts:AssumeRole for "arn:aws:iam::222222222222:root"

I'm getting this error:

9:41:45 AM | CREATE_FAILED        | AWS::CloudFormation::CustomResource       | CrossRegionCertifi...orResource/Default
Failed to create resource. User: arn:aws:sts::222222222222:assumed-role/TestRole/BackendStack-CrossRegionCertificateCertificate-YJRIF5965XXX is not authorized to access this resource

Any ideas?

@skulljoi
Copy link

I need this feature too
add a possibility for acm.Certificate to add property region will be great!
or making DnsValidatedCertificate support subjectAlternativeNames and validation from differents dns hosted zone

@Stf-F
Copy link

Stf-F commented Apr 10, 2021

Interested in that feature too

@jvlch
Copy link

jvlch commented Jun 30, 2021

I've found somewhat of a workaround for my use case:

USE CASE:
Desired Certificate: {region: 'us-east-1', domainName: '*.api.env.mydomain.com', SAN: ['*.mydomain.com']}

-AccountA|HostedZoneA: mydomain.com
-AccountB|HostedZoneB: env.mydomain.com

This certificate will be shared amongst multiple downstream stacks in AccountB

My Stack is deployed to AccountB:us-east-2, and includes a cloudfront distribution that needs to use a us-east-1 certificate.

The dns validation entry for *.mydomain.com has already been done for AccountB, so that is already handled

I use acm.DnsValidatedCertificate to auto-create the *.api.env.mydomain.com entry in HostedZoneB, then use a custom resource to request the Desired Certificate without validation in us-east-1

There's no additional validation steps required after, as the dns validation entries are already in HostedZoneA and HostedZoneB

import cr = require('@aws-cdk/custom-resources');
import acm = require('@aws-cdk/aws-certificatemanager');
import r53 = require('@aws-cdk/aws-route53);
import logs = require('@aws-cdk/aws-logs');
import iam = require('@aws-cdk/aws-iam');



    this.publicHostedZone = new r53.PublicHostedZone(this, 'PublicHostedZone', {
      zoneName: `${env}.mydomain.com`
    });

    const certificateValidator = new acm.DnsValidatedCertificate(this, 'CertificateValidator', {
      domainName: `*.api.${env}.mydomain.com`,
      hostedZone: this.publicHostedZone,  
    })

    const certificateRequestCustomResourcePolicy = cr.AwsCustomResourcePolicy.fromStatements([new iam.PolicyStatement({
      actions: [
        'acm:ListCertificates',
        'acm:DescribeCertificates',
        'acm:RequestCertificate',
        'acm:DeleteCertificate',
      ],
      resources: ['*']
    })])

    const certificate = new cr.AwsCustomResource(this, 'Certificate', {
      policy: certificateRequestCustomResourcePolicy,
      installLatestAwsSdk: true,
      timeout: cdk.Duration.seconds(180),
      logRetention: logs.RetentionDays.ONE_WEEK,
      onCreate: {
        region: 'us-east-1',
        service: 'ACM',
        action: 'requestCertificate',
        parameters: {
          DomainName: `*.api.${env}.mydomain.com`,
          ValidationMethod: 'DNS',
          SubjectAlternativeNames: ['*.mydomain.com'],
        },
        physicalResourceId: cr.PhysicalResourceId.fromResponse('CertificateArn')
      },
      onDelete: {
        region: 'us-east-1',
        service: 'ACM',
        action: 'deleteCertificate',
        parameters: {
          CertificateArn: new cr.PhysicalResourceIdReference()
        },
      }
    })
   
    const certificateArn = certificate.getResponseField('CertificateArn')

@markandrus
Copy link

Agree with @Tanuel and @aripalo here — we need support for managing certificates (that must be created in us-east-1, for example) from other regions.

@denniskribl
Copy link

any updates here?

@kiernan
Copy link

kiernan commented May 17, 2022

Continuing discussion from #12657
@njlynch You made a suggestion that a possible workaround could be to create a Role in the "child account" and grant access to modifying the host zone in the "root account".
I tried this but to no luck.

I don't think this would work with the current custom resource lambda for DnsValidatedCertificate, as it has no logic to change which role or credentials are used when calling the Route53 API. The calls to ACM to create the certificate, and the calls to Route53 to create the DNS records would both be made using the same role, which implies the certificate and DNS records must be in the same account.
https://github.com/aws/aws-cdk/blob/0ef4bb4bf493a7e3b72b518841f676e91d014ba9/packages/%40aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/lib/index.js

For it to work, I think the lambda would need to accept another role that it can assume just when calling the Route53 API, and the construct would need to allow the lambda's role to assume it?

Or perhaps the certificate and DNS record creation could be done in separate custom resources, so they can each run with separate roles.

@taylorb-syd
Copy link

Identified 3 duplicates #15217, #20774 and #21040 that talk about this issue. We really need to up the priority of this issue as it is clear from these duplicates that we need to revise these issues.

Recommendations of prerequisites of how to model this with minimal disruption:

  • Make it clear in the documentation that certificatemanager.Certificate is the construct for creating certificates that duplicates the CloudFormation AWS::CertificateManager::Certificate resource and that certificatemanager.DnsValidatedCertificate is the automated method of validating certificates, thus certificatemanager.DnsValidatedCertificate should be "preferred".
  • Create a new resource called route53.CrossAccountHostedZone which a) can be dynamically created when performing cross account references of HostedZones and b) includes a set of valid principles and an assumable role that can perform record creation in Route53. These new properties can be repurposed to perform delegation as per the current cross_account_zone_delegation_principal and cross_account_zone_delegation_role_name in route53.PublicHostedZone as well, depreciating those
  • Expose these new properties from route53.CrossAccountHostedZone in the IHostedZone interface as optional properties.
  • Create a similar field to override the endpoint that can be exposed in the route53.IHostedZone interface for a custom Route53 endpoint as this is need for aws-cn.
  • Provide functions for customers to manually override these properties where relevant, e.g. imported from HostedZoneId.

With this we have the building blocks to update certificatemanager.DnsValidatedCertificate. We can then:

  • deprecate the hosted_zone property and route53_endpoint properties
  • instead grab the endpoint and role information from the route53.IHostedZone interface
  • use this role information to grant the ability for the certificatemanger.DnsValidatedCertificate resource role the ability to assume the role. If these properties are unset, assume it is same account.
  • Update the certificatemanager.DnsValidatedCertifcate to use the information from the validation, making Validation a required property in the documentation, but still technically optional.
  • For compatibility, if validation is not populated but hosted_zone is, print a warning about the deprecation during synthesis.

At the moment I need to have two stacks, or stick to a single hosted zone, which is quite frustrating as you can imagine.

Note: As I primarily use Python the names are derived from the Python Docs in these suggestions.

@github-actions
Copy link

github-actions bot commented Sep 4, 2022

This issue has received a significant amount of attention so we are automatically upgrading its priority. A member of the community will see the re-prioritization and provide an update on the issue.

@github-actions github-actions bot added p1 and removed p2 labels Sep 4, 2022
@rix0rrr
Copy link
Contributor

rix0rrr commented Sep 9, 2022

Certificatemanager.DnsValidatedCertificate is the automated method of validating certificates, thus certificatemanager.DnsValidatedCertificate should be "preferred".

I actually rather think the reverse is true. DnsValidatedCertificate was written at a time when Route53 validation via CloudFormation was not possible yet, but these days it is: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-certificatemanager-certificate-domainvalidationoption.html

So DnsValidatedCertificate should probably be deprecated.

At the moment I need to have two stacks [...] which is quite frustrating as you can imagine.

I'd like to interrogate this a little. Why is having 2 stacks for resources in 2 regions frustrating ?

Conversely, adding custom resources and ways to create "out-of-region" resources for every resource where people might want them seems worse: it's less scalable development-wise, and brings more maintenance burden and chance of bugs.

Let me offer a different solution that accomplishes ~the same thing: how about we add the ability to make NestedStacks cross-region? Yes, it would be with a Custom Resource, but at least it would be scalable -- you would then be able to create cross-region anything, not just the resources we implemented explicit support for.

@codeedog
Copy link

codeedog commented Sep 9, 2022

I'm the filer of #15217 which has been merged into this. As a developer, I'm a big fan of reusable solutions and the cross-stack, cross-region discussion is a worthy feature that very well may cover a lot of use cases in a general fashion. I will say that the deployment requirements (and not abstract stack/region architecture) dictate that SSL Certificates must be created and live in us-east-1 while I would prefer my operational stack to live in a different region. I haven't imposed cross-region requirements on myself, AWS has. If, by your proposal, I will have to create and manage recursive stacks just to get certifcates deployed, well, that feels like punishment, not convenience. So, my request would be if you choose to build the general case, then rewrite DnsValidatedCertificate to use it.

@taylorb-syd
Copy link

I think based upon codeedog@'s feedback, with regards to this specific use case I think that we should probably talk to ACM about making it possible to create "global" certificates, and fix this issue once and for all. This operational constraint doesn't just apply to CDK.

I don't think this will be a trivial change as ACM uses KMS keys that are not global, meaning that certificates cannot be shared between regions. With some architectural and implementation changes I can imagine a situation where either a scope and/or custom key can be provided as part of the certificate creation process. In theory the scope would allow for global certificates that replicated using a multi-region KMS key, and the custom key would even allow for cross-account certificates.

However, this looks like it would be a way of, so until we either have a generalized way to create cross region references, or cross-region/cross-account ACM certificates, I personally do not support the deprecation of the DnsValidatedCertificate.

@taylorb-syd
Copy link

Certificatemanager.DnsValidatedCertificate is the automated method of validating certificates, thus certificatemanager.DnsValidatedCertificate should be "preferred".

I actually rather think the reverse is true. DnsValidatedCertificate was written at a time when Route53 validation via CloudFormation was not possible yet, but these days it is: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-certificatemanager-certificate-domainvalidationoption.html

The goal with my notes here was to minimize disruption, which to me means "don't deprecate the currently preferred method", as that is obviously going to be a breaking change for CDK consumers.

That being said, I actually agree with you, creating custom resources when they're not needed is not scalable. We should minimize the number of custom code paths we need to manage. So ultimately, deprecation of this resource is going to be preferred, and in fact seems to what has been decided for CDK 3 as per #21982.

However, there is one thing that ValidatedCertificate can't do, and the model I proposed above can, that is Cross Account Certificate Validation. This is probably an acceptable trade-off, but one we should validate if we deprecate this resource entirely.

@mergify mergify bot closed this as completed in #21982 Jan 25, 2023
mergify bot pushed a commit that referenced this issue Jan 25, 2023
Now that the official CloudFormation resource `AWS::CertificateManager::Certificate` (CDK's `Certificate` construct) supports DNS validation we do not want to recommend using the `DnsValidatedCertificate` construct.

The `DnsValidatedCertificate` construct uses CloudFormation custom resources to perform the certificate creation and this creates a lot of maintenance burden on our team (see the list of linked issues). Currently the primary use case for using `DnsValidatedCertificate` over `Certificate` is for cross region use cases. For this use case I have updated the README to have our suggested solution.

The example in the README is tested in this [integration test](https://github.com/aws/aws-cdk/blob/main/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts)

fixes #8934, #2914, #20698, #17349, #15217, #14519


----

### All Submissions:

* [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@github-actions
Copy link

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

@chrispaton
Copy link

Hello! I see that this issue has been closed, but it looks like at least one of the limitations covered by this issue remains: namely that I cannot use CDK to create and validate an ACM certificate if validation requires DNS entries in a Route 53 hosted zone that is located in another account. I cannot see any other open issues covering this, so I'd like to try and resume discussion - as per the Comment visibility warning above I'm tagging previous commenters @taylorb-syd and @corymhall for visibility; shout if I should take this chat elsewhere.

I would make the case that this use case is a reasonable, and indeed desirable, thing to do when following best practices about using multiple AWS accounts in an organisation (AWS has various recommendations around this as per aws multiple account best practices - Google Search). To summarise my own use case that led me here: I have a Route 53 hosted zone for myproduct.myorg.example that is located in a "core infrastructure" AWS account. Following best practice, I then have separate AWS accounts for each stage of my service to reduce the blast radius of issues. Naturally, I want my production website to be accessible at the apex of myproduct.myorg.example rather than something like website.eu-west-1.prod.myorg.example and so I need to create an ACM TLS certificate in the EU-prod account, but validating this requires DNS entries in the core account with the top-level hosted zone, which means I cannot use either DnsValidatedCertificate nor CertificateValidation.fromDns for the reasons discussed previously.

I see that there was an effort by @johnf to solve this for DnsValidatedCertificate in #23526 but the PR was closed by @TheRealAmazonKendra as that class is now deprecated.

So a few concrete questions to try progress the conversation:

  1. Is anyone aware of any other efforts to solve for this use case?
  2. Do any of the project maintainers have a view on whether the approach on feat(acm-certificatemanager): Add assume role for DNS validation record creation #23526 is worth rebasing onto the CertificateValidation class? This is something I could potentially have a go at progressing.

@AnthonyNGarcia
Copy link

AnthonyNGarcia commented Oct 18, 2023

Hey all, just wanted to share an approach that worked for us. Also to specifically answer @chrispaton question 1 . We basically emulated the behavior of CrossAccountZoneDelegationRecord. As a summary, the approach that construct takes is:

  1. Customer creates an IAM Role in the parent AWS account hosting the parent domain, which has permission to modify/add records to this (parent) hosted zone.
  2. Customer then provides that role when instantiating this CrossAccountZoneDelegationRecord construct in the child AWS account (hosting the subdomain hosted zone).
  3. Under the hood, this construct is leveraging an AWSCustomResource, where that role is provided to a Lambda function which makes a series of AWS SDK calls to modify the parent hosted zone.

We can do the same thing here for DNS validation. I believe the PR that was closed for this issue was taking a similar approach. Still, one could accomplish the same end result by implementing their own Lambda and AWSCustomResource to invoke it.

For example, first you'd have to create the ACM certificate and tell the CDK you'll be doing DNS validation yourself by providing undefined like this. And then the AWS custom resource can be created like this, but note where resources need to be provided with <>. Also this is a partial example since the resource listed here should ideally be broken up more for readability/maintainability:

(importing IAM, AWSCustomResource, Lambda, etc.. as needed from AWS CDK)

...

new Certificate(this, <name the certificate id here>, {
  domainName: <your domain name, the domain name to be on the certificate>
  validation: CertificateValidation.fromDns(undefined) // telling the CDK we will handle DNS validation
}

...

new AwsCustomResource(this, <name the custom resource id here>, {
  policy: AwsCustomResourcePolicy.fromSdkCalls({resources: AwsCustomResourcePolicy.ANY_RESOURCE}),
  role: new Role(this, <name the role id here>, { assumedBy: new ServicePrincipal('lambda.amazonaws.com') });,
  onCreate: {
    service: 'Lambda',
    action: 'invoke',
    physicalResourceId: PhysicalResourceId.of(Date.now().toString()),
    parameters: {
      FunctionName: <lambda function>.functionName
      Payload: JSON.stringify(<request>)
    }
  },
  <ideally implement onUpdate and onDelete as well>
})

You'll also have to create the lambda function(s) yourself but there is plenty of documentation on creating a Lambda Function (including a NodeJS Lambda Function). Depending on the language you write the Lambda function in, you'll have to find the right AWS SDK (e.g., boto3 for Python).

The lambda's logic is too verbose and AWS SDK language-specific to put useful snippets here but at a high level it would accomplish something like:

  1. Expect a request which includes a cross account role ARN to assume, the certificate ARN to validate, and the hosted zone name to modify
  2. Make an API call to AWS ACM to get the domain validations from the certificate ARN (aka what it needs to insert into parent's hosted zone, the cnameName and cnameValue).
  3. Make an API call to AWS STS to assume the role by the specified role ARN, and use it for ensuing requests to R53:
  4. Make an API call to AWS R53 to get the (parent) hosted zone ID from the hosted zone name
  5. Make an API call to AWS R53 to add record for DNS validation using previously fetched cnameName and cnameValue and hosted zone ID

This also gets more complicated if the domain has additional domain names which may be owned by separate AWS accounts. In that case you'll have to get a bit fancier with how you write the Lambda function(s) to make them more versatile/dynamic, or create separate custom resources (be mindful that they have a different physical ID).

Although the above works, it is a lot of work to implement, with a lot of pitfalls. Ideally, this should be abstracted and centralized into the AWS CDK with a neat high level construct (like CrossAccountZoneDelegationRecord).

Is the above approach acceptable to the repository owners? If so, then we could have a path forward for a PR which could get merged into CDK and finally address this years-old feature request.

Just spitballing the goal's construct behavior but what if the CDK construct was extended to support something like:

new Certificate(this, 'some-cert-id', {
  domainName: 'example.com',
  validation: CertificateValidation.fromCrossAccountDns(roleArn, hostedZoneName)
  // subjectAlternativeNames: ['more.example.com'], // if validating with cross account dns, it will be assumed these domains exist in that same cross account too
  subjectAlternativeNameValidations: [altValidation1, altValidation2, etc.], // this would be optional but mutually exclusive from subjectAlternativeNames; this is simply a way to perform the same cross-account granular logic but at the subject alternative name level as well, in case they exist in different accounts (and not all in one cross account).
});

// Example of altValidation1:
const altValidation1: Certificate.SubjectAlternativeNameValidation = {
  subjectAlternativeName: 'more.example.com',
  validation: CertificateValidation.fromCrossAccountDns(anotherRoleArn, anotherHostedZoneName)
}

Alternatively, can take an approach more like fromDnsMultiZone, introducing a new fromDnsMultiZoneCrossAccount which takes domain name by key, and {hostedZoneName, roleArn} object as value:

new Certificate(this, 'some-cert-id', {
  domainName: 'example.com',
  validation: CertificateValidation.fromDnsMultiZoneCrossAccount({
    'example.com': {hostedZoneName: 'example.com', roleArn: 'abc123'},
    'another.example.com': {hostedZoneName: 'another.example.com', roleArn: 'def456'}.
  })
});

I personally prefer the second option. Assuming this might be acceptable, then I might have a go at implementing this in the AWS CDK and raising a PR. Thoughts @taylorb-syd , @TheRealAmazonKendra ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-certificatemanager Related to Amazon Certificate Manager effort/large Large work item – several weeks of effort feature-request A feature should be added or improved. p1
Projects
None yet
Development

Successfully merging a pull request may close this issue.