I'm running into an issue where I have a stack that uses HttpsRedirect
from aws-route53-patterns
.
It's part of my Codepipeline and every time I release a change (even without changing any code) the logical ID is recreated, and different (which I didn't think was supposed to happen).
All my other stacks behave normally, but it seems that the HttpsRedirect construct gets a new Logical ID each time and then fails because the Route53 records it tries to create a resource that already exist.
Here's my stack:
export class CdnStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const domainName = ssm.StringParameter.valueForStringParameter(
this,
`/env/domainName`,
1
);
const hostedZoneId = ssm.StringParameter.valueForStringParameter(
this,
`/env/hostedZoneId`,
1
);
const wwwDomainName = `www.${domainName}`;
const redirect = new HttpsRedirect(this, "HttpsRedirectToWww", {
recordNames: [domainName],
targetDomain: wwwDomainName,
zone: route53.HostedZone.fromHostedZoneAttributes(this, "HostedZone", {
hostedZoneId,
zoneName: domainName,
}),
});
}
}
Here is the code in context in case it helps.
Any help is appreciated!
I've tried to update the logical ID manually but couldn't figure out the interface for the HttpsRedirect construct.
Looks like it's creating a hash for each domain and because I use tokens, it's changing every update/deploy. Since I use the valueForStringParameter
, it has a different token each time, which is hashed into a new value too.
Here's the source code from the package that was causing the issue:
domainNames.forEach((domainName) => {
const hash = md5hash(domainName).slice(0, 6);
const aliasProps = {
recordName: domainName,
zone: props.zone,
target: RecordTarget.fromAlias(new CloudFrontTarget(redirectDist)),
};
new ARecord(this, `RedirectAliasRecord${hash}`, aliasProps);
new AaaaRecord(this, `RedirectAliasRecordSix${hash}`, aliasProps);
});
And here is a link to the package in cdk in case it changes.
So my fix was to manually create the pattern locally, which looks like this in case anyone needs it:
export class CdnStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const domainName = ssm.StringParameter.valueForStringParameter(
this,
`/env/domainName`,
1
);
const hostedZoneId = ssm.StringParameter.valueForStringParameter(
this,
`/env/hostedZoneId`,
1
);
const wwwDomainName = `www.${domainName}`;
const zone = route53.HostedZone.fromHostedZoneAttributes(this, "MyZone", {
hostedZoneId,
zoneName: domainName,
});
const certificate = new acm.DnsValidatedCertificate(this, "Certificate", {
domainName,
subjectAlternativeNames: [`*.${domainName}`],
hostedZone: zone,
region: "us-east-1",
});
const redirectBucket = new s3.Bucket(this, "RedirectBucket", {
websiteRedirect: {
hostName: wwwDomainName,
protocol: s3.RedirectProtocol.HTTPS,
},
removalPolicy: cdk.RemovalPolicy.DESTROY,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
});
const redirectDist = new cloudfront.CloudFrontWebDistribution(
this,
"RedirectDistribution",
{
defaultRootObject: "",
originConfigs: [
{
behaviors: [{ isDefaultBehavior: true }],
customOriginSource: {
domainName: redirectBucket.bucketWebsiteDomainName,
originProtocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
},
},
],
viewerCertificate: cloudfront.ViewerCertificate.fromAcmCertificate(
certificate,
{
aliases: [domainName],
}
),
comment: `Redirect to ${wwwDomainName} from ${domainName}`,
priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL,
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
}
);
const redirectRecordProps = {
zone,
recordName: domainName,
target: route53.RecordTarget.fromAlias(
new targets.CloudFrontTarget(redirectDist)
),
};
new route53.ARecord(this, "ARedirectAliasRecord", redirectRecordProps);
new route53.AaaaRecord(
this,
"AaaaRedirectAliasRecord",
redirectRecordProps
);
}
Hopefully it helps someone else that gets stuck following a similar pattern.