Skip to main content

Command Palette

Search for a command to run...

Solving the AWS CloudFront Cross-Region Certificate Puzzle with CDK

Updated
7 min read

The Problem That Makes Developers Pull Their Hair Out

When you design infrastructure across multiple AWS regions, you expect each region to be self-contained — its own EKS clusters, databases, Lambdas, and certificates — all neatly separated by region.

That’s how my CDK setup was built: a clean, modular design that could deploy the same infrastructure stack in any region (ap-southeast-2, ap-south-1, etc.) just by switching configuration.

Everything worked perfectly… until CloudFront entered the picture.

The moment I added CloudFront for my frontend, deployments started failing with confusing certificate errors like:

Error: Certificate for alternate domain name is not issued by AWS Certificate Manager

Or worse:

The certificate that is attached to your distribution doesn't cover the alternate domain name

After debugging, I realized the issue wasn’t with my setup at all — it was an AWS constraint hiding in plain sight:

CloudFront requires SSL certificates to be created in us-east-1, no matter where your infrastructure lives.

That single requirement completely broke my otherwise predictable, region-specific deployment model. This post walks through how I solved it — a clean, battle-tested pattern to handle CloudFront’s cross-region certificate constraint using AWS CDK, without messy hacks or manual intervention.

Problem 1: The Hidden us-east-1 Certificate Requirement

The Problem

AWS CloudFront is a global service, but it has a peculiar requirement: SSL/TLS certificates used with CloudFront distributions must be created in the us-east-1 region. This isn't clearly stated in error messages, and developers often waste hours trying to debug certificate validation issues.

Common symptoms:

  • Certificate validation succeeds in your primary region but CloudFront still fails

  • "Certificate not found" errors despite the certificate existing

  • CloudFormation rollbacks with cryptic certificate-related errors

The Solution

Create a dedicated stack for us-east-1 resources that extends your domain stack:

// src/stacks/domain/us-east1-domain.stack.ts
export class USEast1DomainStack extends DomainStack {
  constructor(scope: Construct, id: string, props: IStackProps) {
    super(scope, id, {
      ...props,
      description: 'Stack to create domain resources in us-east-1',
    }, true);

    // Enforce us-east-1 deployment
    if (props.env?.region !== 'us-east-1' ||
        props.config.region !== 'us-east-1') {
      throw new Error('This stack must be deployed in us-east-1 region');
    }

    // Create wildcard certificate for CloudFront
    this.createWildcardCertificate();
    this.createWebAppReviewCertificates();
  }
}

Why this works: By explicitly creating a separate stack for us-east-1 resources, you maintain clean separation of concerns while ensuring certificates are in the correct region.

Problem 2: Cross-Region Reference Failures

The Problem

When your main infrastructure is in ap-southeast-2 but certificates are in us-east-1, CDK throws errors:

Error: Stack "FrontendStack" cannot reference "USEast1DomainStack" across regions without crossRegionReferences enabled

Even worse, CloudFormation exports don't automatically work across regions, leading to deployment failures.

The Solution

Enable cross-region references when creating the us-east-1 stack:

// src/stages/microservices-platform.stage.ts
const usEast1DomainStack = new USEast1DomainStack(
  this,
  'USEast1DomainStack',
  {
    ...props,
    env: {
      ...props.env,
      region: 'us-east-1',  // Override region
    },
    config: {
      ...props.config,
      region: 'us-east-1',  // Update config region too
    },
    crossRegionReferences: true,  // Critical!
  },
);

// Frontend stack can now reference us-east-1 certificates
const frontendStack = new FrontendStack(this, 'FrontendStack', {
  ...props,
  domainStack: usEast1DomainStack,  // Pass the us-east-1 stack
  crossRegionReferences: true,
});

Why this works: The crossRegionReferences flag tells CDK to use AWS Systems Manager Parameter Store to share values between regions, working around CloudFormation's regional limitations.

Problem 3: Stack Dependency and Deployment Order

The Problem

Complex dependency chains emerge when you have:

  • A domain stack in your primary region (for Route53)

  • A us-east-1 domain stack (for certificates)

  • Frontend stacks that need both

Incorrect dependency setup leads to:

  • Circular dependency errors

  • Resources created in wrong order

  • "Export already exists" conflicts

The Solution

Implement intelligent dependency management:

// Conditional domain stack creation based on region
let domainStack: DomainStack | undefined = undefined;

if (this.config.region !== 'us-east-1') {
  domainStack = new DomainStack(this, 'DomainStack', props, true);
}

const usEast1DomainStack = new USEast1DomainStack(
  this,
  'USEast1DomainStack',
  { /* ... */ }
);

// Set up dependency chain
if (domainStack) {
  usEast1DomainStack.addDependency(domainStack);
} else {
  domainStack = usEast1DomainStack;  // us-east-1 IS the domain stack
}

// Frontend depends on us-east-1 stack for certificates
const frontendStack = new FrontendStack(this, 'FrontendStack', {
  ...props,
  domainStack: usEast1DomainStack,
  crossRegionReferences: true,
});

frontendStack.addDependency(usEast1DomainStack);

Why this works: This pattern ensures:

  1. Route53 hosted zone is created first (if needed)

  2. Certificates are created in us-east-1 with DNS validation

  3. Frontend resources wait for certificates to be ready

Problem 4: Managing Multiple Apps and Review Deployments

The Problem

Modern development workflows require:

  • Multiple frontend applications (admin portal, customer app, etc.)

  • Review/preview deployments for pull requests

  • Wildcard certificates for dynamic subdomains

Managing certificates for all these scenarios becomes complex.

The Solution

Use a combination of wildcard certificates and app-specific certificates:

// Base wildcard certificate for all standard subdomains
private createWildcardCertificate(): void {
  this._wildcardCertificate = new acm.Certificate(
    this,
    'WildcardCertificate',
    {
      domainName: `*.${this.domainName}`,
      validation: acm.CertificateValidation.fromDns(this.hostedZone),
    }
  );
}

// App-specific wildcard certificates for review deployments
private createWebAppReviewCertificates(): void {
  for (const frontEndAppName in this.config.domainConfig.frontendApp) {
    const enableReviews = this.config.frontendApps?.find(
      (app) => app.name === frontEndAppName,
    )?.enableReviews;

    if (!enableReviews) continue;

    const frontEndDomain =
      this.config.domainConfig.frontendApp[frontEndAppName]?.subDomainName;

    // Create wildcard for review apps: *.app.domain.com
    const certificate = new acm.Certificate(
      this,
      `${frontEndDomain}-certificate`,
      {
        domainName: `${frontEndDomain}.${this.domainName}`,
        subjectAlternativeNames: [`*.${frontEndDomain}.${this.domainName}`],
        validation: acm.CertificateValidation.fromDns(this.hostedZone),
      }
    );

    this.webappReviewCertificates.set(frontEndAppName, certificate);
  }
}

Why this works: This approach provides flexibility for both stable deployments and dynamic review environments without certificate proliferation.

Problem 5: Bootstrap and Environment Configuration

The Problem

Cross-region deployments require:

  • Both regions to be bootstrapped

  • Environment-specific configurations

  • Proper IAM trust relationships

Missing any of these causes deployment failures that are hard to debug.

The Solution

  1. Bootstrap both regions before deployment:
# Bootstrap primary region
cdk bootstrap aws://ACCOUNT_ID/ap-southeast-2

# Bootstrap us-east-1 for certificates
cdk bootstrap aws://ACCOUNT_ID/us-east-1
  1. Use environment-specific configuration:
// src/config/environments/dev/config.dev.ts
export const AUDevConfig: IConfig = {
  accountID: DEV_ACCOUNT_ID,
  region: 'ap-southeast-2',
  env: 'DEV',
  iluminrPrimaryRegion: 'ap-southeast-2',
  domainConfig: devDomainConfig,
  frontendApps: devFrontendConfig,
  // ... other config
};
  1. Consume configuration in your CDK app:
// Frontend stack integration
const frontendStack = new FrontendStack(this, 'FrontendStack', {
  ...props,
  domainStack: usEast1DomainStack,
  crossRegionReferences: true,
});

// Angular deployment using the cross-region certificate
new AngularAppDeployment(this, app.name, {
  appName: app.name,
  subDomainName: props.config.domainConfig.frontendApp[app.name]?.subDomainName,
  rootDomain: props.config.domainConfig.domainName,
  certificate: props.domainStack.wildcardCertificate,  // From us-east-1
  hostedZone: props.domainStack.hostedZone,
  env: props.config.env,
});

Why this works: Proper bootstrapping ensures CDK has the necessary resources in both regions, while configuration management keeps your infrastructure DRY and maintainable.

Best Practices

  1. Always validate certificates using DNS validation - It works across regions without manual intervention

  2. Use wildcard certificates strategically - Reduces certificate count and management overhead

  3. Tag your resources consistently - Makes debugging and cost allocation easier

  4. Export critical values - Certificate ARNs, distribution IDs for use by CI/CD pipelines

  5. Document region requirements - Add error checks that explicitly state region requirements

Common Pitfalls to Avoid

❌ Don't Forget to Bootstrap us-east-1

Even if your infrastructure is elsewhere, us-east-1 needs bootstrapping for certificate creation.

❌ Don't Mix Regional and Global Resources

Keep clear separation between regional stacks and us-east-1 stacks.

❌ Don't Ignore Deletion Order

When destroying stacks, delete in reverse dependency order:

# Delete frontend first
cdk destroy FrontendStack
# Then us-east-1 resources
cdk destroy USEast1DomainStack
# Finally, primary region resources
cdk destroy DomainStack

❌ Don't Hardcode Region Values

Always use configuration to manage regions:

// Bad
const stack = new Stack(this, 'Stack', { env: { region: 'us-east-1' } });

// Good
const stack = new Stack(this, 'Stack', {
  env: { region: config.certificateRegion }
});

Conclusion

The CloudFront cross-region certificate requirement is a classic AWS gotcha that has frustrated countless developers. By understanding the underlying constraints and implementing a clean separation between regional and us-east-1 resources, you can build robust, multi-region CDK applications without the headache.

Key takeaways:

  • Always create CloudFront certificates in us-east-1

  • Use crossRegionReferences: true for cross-region stack dependencies

  • Implement proper dependency chains between stacks

  • Bootstrap all regions before deployment

  • Use configuration-driven infrastructure for flexibility

This pattern has been battle-tested in production environments serving millions of requests. It provides a solid foundation for building globally distributed applications with AWS CDK while maintaining clean, maintainable infrastructure code.

Remember: The complexity is in understanding the problem. Once you know the constraints, the solution is straightforward.


Have you encountered other cross-region challenges with AWS CDK? What patterns have worked for you? Share your experiences and let's build better infrastructure together.

67 views

More from this blog