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
S

I love to develop software that makes everyday life simpler, no matter big or small, but if it provides the user a push of productivity and efficiency, It makes my day.

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.

72 views

More from this blog