in packages/aws-cdk-lib/aws-cloudfront/lib/web-distribution.ts [828:1001]
constructor(scope: Construct, id: string, props: CloudFrontWebDistributionProps) {
super(scope, id);
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
// Comments have an undocumented limit of 128 characters
const trimmedComment =
props.comment && props.comment.length > 128
? `${props.comment.slice(0, 128 - 3)}...`
: props.comment;
const behaviors: BehaviorWithOrigin[] = [];
const origins: CfnDistribution.OriginProperty[] = [];
const originGroups: CfnDistribution.OriginGroupProperty[] = [];
let originIndex = 1;
for (const originConfig of props.originConfigs) {
let originId = `origin${originIndex}`;
const originProperty = this.toOriginProperty(originConfig, originId);
if (originConfig.failoverCustomOriginSource || originConfig.failoverS3OriginSource) {
const originSecondaryId = `originSecondary${originIndex}`;
const originSecondaryProperty = this.toOriginProperty(
{
s3OriginSource: originConfig.failoverS3OriginSource,
customOriginSource: originConfig.failoverCustomOriginSource,
originPath: originConfig.originPath,
originHeaders: originConfig.originHeaders,
originShieldRegion: originConfig.originShieldRegion,
},
originSecondaryId,
);
const originGroupsId = `OriginGroup${originIndex}`;
const failoverCodes = originConfig.failoverCriteriaStatusCodes ?? [500, 502, 503, 504];
originGroups.push({
id: originGroupsId,
members: {
items: [{ originId }, { originId: originSecondaryId }],
quantity: 2,
},
failoverCriteria: {
statusCodes: {
items: failoverCodes,
quantity: failoverCodes.length,
},
},
});
originId = originGroupsId;
origins.push(originSecondaryProperty);
}
for (const behavior of originConfig.behaviors) {
behaviors.push({ ...behavior, targetOriginId: originId });
}
origins.push(originProperty);
originIndex++;
}
origins.forEach(origin => {
if (!origin.s3OriginConfig && !origin.customOriginConfig) {
throw new cdk.ValidationError(`Origin ${origin.domainName} is missing either S3OriginConfig or CustomOriginConfig. At least 1 must be specified.`, this);
}
});
const originGroupsDistConfig =
originGroups.length > 0
? {
items: originGroups,
quantity: originGroups.length,
}
: undefined;
const defaultBehaviors = behaviors.filter(behavior => behavior.isDefaultBehavior);
if (defaultBehaviors.length !== 1) {
throw new cdk.ValidationError('There can only be one default behavior across all sources. [ One default behavior per distribution ].', this);
}
const otherBehaviors: CfnDistribution.CacheBehaviorProperty[] = [];
for (const behavior of behaviors.filter(b => !b.isDefaultBehavior)) {
if (!behavior.pathPattern) {
throw new cdk.ValidationError('pathPattern is required for all non-default behaviors', this);
}
otherBehaviors.push(this.toBehavior(behavior, props.viewerProtocolPolicy) as CfnDistribution.CacheBehaviorProperty);
}
let distributionConfig: CfnDistribution.DistributionConfigProperty = {
comment: trimmedComment,
enabled: props.enabled ?? true,
defaultRootObject: props.defaultRootObject ?? 'index.html',
httpVersion: props.httpVersion || HttpVersion.HTTP2,
priceClass: props.priceClass || PriceClass.PRICE_CLASS_100,
ipv6Enabled: props.enableIpV6 ?? true,
// eslint-disable-next-line max-len
customErrorResponses: props.errorConfigurations, // TODO: validation : https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-customerrorresponse.html#cfn-cloudfront-distribution-customerrorresponse-errorcachingminttl
webAclId: props.webACLId,
origins,
originGroups: originGroupsDistConfig,
defaultCacheBehavior: this.toBehavior(defaultBehaviors[0], props.viewerProtocolPolicy),
cacheBehaviors: otherBehaviors.length > 0 ? otherBehaviors : undefined,
};
if (props.aliasConfiguration && props.viewerCertificate) {
throw new cdk.ValidationError([
'You cannot set both aliasConfiguration and viewerCertificate properties.',
'Please only use viewerCertificate, as aliasConfiguration is deprecated.',
].join(' '), this);
}
let _viewerCertificate = props.viewerCertificate;
if (props.aliasConfiguration) {
const { acmCertRef, securityPolicy, sslMethod, names: aliases } = props.aliasConfiguration;
_viewerCertificate = ViewerCertificate.fromAcmCertificate(
certificatemanager.Certificate.fromCertificateArn(this, 'AliasConfigurationCert', acmCertRef),
{ securityPolicy, sslMethod, aliases },
);
}
if (_viewerCertificate) {
const { props: viewerCertificate, aliases } = _viewerCertificate;
Object.assign(distributionConfig, { aliases, viewerCertificate });
const { minimumProtocolVersion, sslSupportMethod } = viewerCertificate;
if (minimumProtocolVersion != null && sslSupportMethod != null) {
const validProtocols = this.VALID_SSL_PROTOCOLS[sslSupportMethod as SSLMethod];
if (validProtocols.indexOf(minimumProtocolVersion.toString()) === -1) {
throw new cdk.ValidationError(`${minimumProtocolVersion} is not compabtible with sslMethod ${sslSupportMethod}.\n\tValid Protocols are: ${validProtocols.join(', ')}`, this);
}
}
} else {
distributionConfig = {
...distributionConfig,
viewerCertificate: { cloudFrontDefaultCertificate: true },
};
}
if (props.loggingConfig) {
this.loggingBucket = props.loggingConfig.bucket || new s3.Bucket(this, 'LoggingBucket', {
encryption: s3.BucketEncryption.S3_MANAGED,
});
distributionConfig = {
...distributionConfig,
logging: {
bucket: this.loggingBucket.bucketRegionalDomainName,
includeCookies: props.loggingConfig.includeCookies || false,
prefix: props.loggingConfig.prefix,
},
};
}
if (props.geoRestriction) {
distributionConfig = {
...distributionConfig,
restrictions: {
geoRestriction: {
restrictionType: props.geoRestriction.restrictionType,
locations: props.geoRestriction.locations,
},
},
};
}
const distribution = new CfnDistribution(this, 'CFDistribution', { distributionConfig });
this.node.defaultChild = distribution;
this.domainName = distribution.attrDomainName;
this.distributionDomainName = distribution.attrDomainName;
this.distributionId = distribution.ref;
}