-
Notifications
You must be signed in to change notification settings - Fork 4k
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-cloudfront: unable to set origin group as default behavior origin #31016
Comments
Would you be able to provide a minimal and complete reproduction stack? I'm assuming you're using an import * as cdk from "aws-cdk-lib";
import type { Construct } from "constructs";
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import * as origins from "aws-cdk-lib/aws-cloudfront-origins";
import * as s3 from "aws-cdk-lib/aws-s3";
export class TestStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const bucket = new s3.Bucket(this, "Bucket", {
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
const origin = new origins.OriginGroup({
primaryOrigin: new origins.S3Origin(bucket),
fallbackOrigin: new origins.HttpOrigin("www.example.com"),
});
new cloudfront.Distribution(this, "Distribution", {
defaultBehavior: { origin },
});
}
} |
Hello, i have done some tests and the issue occurs only when the Cloudfront distribution is created with an origin group that contains an ALB origin created with ECS pattern (ApplicationLoadBalancedFargateService). Here the code to reproduce: import * as cdk from "aws-cdk-lib";
import type { Construct } from "constructs";
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import * as origins from "aws-cdk-lib/aws-cloudfront-origins";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as path from 'path';
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as nodejs_lambda from "aws-cdk-lib/aws-lambda-nodejs";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as elbv2 from "aws-cdk-lib/aws-elasticloadbalancingv2";
import * as targets from "aws-cdk-lib/aws-elasticloadbalancingv2-targets";
export class AwsCdk31016Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const vpc = new cdk.aws_ec2.Vpc(this, "sap-cs-ecs-vpc", {
maxAzs: 2// Default is all AZs in region
});
const cluster = new cdk.aws_ecs.Cluster(this, "sap-cs-ecs-cluster", {
vpc: vpc
});
const ecs_pattern = new cdk.aws_ecs_patterns.ApplicationLoadBalancedFargateService(this, 'sap-cs-ecs-pattern', {
cluster: cluster,
desiredCount: 1,
cpu: 1024,
runtimePlatform: {
operatingSystemFamily: cdk.aws_ecs.OperatingSystemFamily.LINUX,
cpuArchitecture: cdk.aws_ecs.CpuArchitecture.ARM64
},
memoryLimitMiB: 2048,
taskImageOptions: {
// image: cdk.aws_ecs.ContainerImage.fromEcrRepository(ecr, dockerImageTagParam.valueAsString),
image: cdk.aws_ecs.ContainerImage.fromRegistry('nginx:latest'),
containerPort: 4000
},
serviceName: 'sap-cs-on-aws-svc',
deploymentController: {
// type: cdk.aws_ecs.DeploymentControllerType.EXTERNAL
type: cdk.aws_ecs.DeploymentControllerType.ECS
},
loadBalancerName: 'sap-cs-on-aws-alb',
assignPublicIp: true,
// internal: true,
publicLoadBalancer: true,
listenerPort: 80,
protocol: cdk.aws_elasticloadbalancingv2.ApplicationProtocol.HTTP,
// sslPolicy: cdk.aws_elasticloadbalancingv2.SslPolicy.TLS12,
// certificate: certificate,
// sslPolicy: SslPolicy.FORWARD_SECRECY_TLS12
healthCheck: {
command: [ "CMD-SHELL", "curl -f http://localhost:4000/ || exit 1" ],
interval: cdk.Duration.seconds(300),
retries: 10,
startPeriod: cdk.Duration.seconds(30),
timeout: cdk.Duration.seconds(30),
}
});
// Output
new cdk.CfnOutput(this, 'AwsCdk31016LoadBalancerALBUrl', {
value: `http://${ecs_pattern.loadBalancer.loadBalancerDnsName}`
});
const bucket = new s3.Bucket(this, "Bucket", {
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
const origin = new origins.OriginGroup({
primaryOrigin: new origins.LoadBalancerV2Origin(ecs_pattern.loadBalancer, {
protocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
}),
fallbackOrigin: new origins.S3Origin(bucket),
});
const distribution = new cloudfront.Distribution(this, "Distribution", {
defaultBehavior: { origin },
});
// Output
new cdk.CfnOutput(this, 'Cloudfrontdistributionurl', {
value: distribution.distributionDomainName
});
}
} |
I was unable to deploy your example, your task definition never passes its health check as nginx is running on port 80. I needed to update the following: const ecs_pattern = new cdk.aws_ecs_patterns.ApplicationLoadBalancedFargateService(this, 'sap-cs-ecs-pattern', {
// ...
taskImageOptions: {
image: cdk.aws_ecs.ContainerImage.fromRegistry('nginx:latest'),
- containerPort: 4000
},
// ...
healthCheck: {
- command: [ "CMD-SHELL", "curl -f http://localhost:4000/ || exit 1" ],
+ command: [ "CMD-SHELL", "curl -f http://localhost/ || exit 1" ], After that, I was able to successfully deploy the stack, I'm not getting the CloudFormation error mentioned in the bug report. Could you double check your repro stack? |
Hi @bersanf I was able to deploy this simplified code using CDK 2.148.0. export class AwsCdk31016Stack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const vpc = new ec2.Vpc(this, "sap-cs-ecs-vpc", {
maxAzs: 2// Default is all AZs in region
});
const cluster = new ecs.Cluster(this, "sap-cs-ecs-cluster", {
vpc,
});
const ecs_pattern = new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'sap-cs-ecs-pattern', {
cluster,
desiredCount: 1,
cpu: 1024,
memoryLimitMiB: 2048,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry('nginx:latest'),
containerPort: 80
},
listenerPort: 80,
});
// Output
new CfnOutput(this, 'AwsCdk31016LoadBalancerALBUrl', {
value: `http://${ecs_pattern.loadBalancer.loadBalancerDnsName}`
});
const bucket = new s3.Bucket(this, "Bucket", {
removalPolicy: RemovalPolicy.DESTROY,
});
const origin = new cfo.OriginGroup({
primaryOrigin: new cfo.LoadBalancerV2Origin(ecs_pattern.loadBalancer, {
protocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
}),
fallbackOrigin: new cfo.S3Origin(bucket),
});
const distribution = new cloudfront.Distribution(this, "Distribution", {
defaultBehavior: { origin },
});
// Output
new CfnOutput(this, 'Cloudfrontdistributionurl', {
value: distribution.distributionDomainName
});
}
} Can you first simplify your ECS and make sure ECS service is deployed and can be access through the ALB and then deploy the CloudFront distribution? |
Hello @pahud, @nmussy But, when the origin group contains a S3Origin with an S3 bucket configured with OAC to enforce security, then the CloudFormation stack fails. Here the code i am using: import * as cdk from "aws-cdk-lib";
import type { Construct } from "constructs";
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import * as origins from "aws-cdk-lib/aws-cloudfront-origins";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as path from 'path';
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as nodejs_lambda from "aws-cdk-lib/aws-lambda-nodejs";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as elbv2 from "aws-cdk-lib/aws-elasticloadbalancingv2";
import * as targets from "aws-cdk-lib/aws-elasticloadbalancingv2-targets";
import * as iam from 'aws-cdk-lib/aws-iam';
export class AwsCdk31016Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const vpc = new cdk.aws_ec2.Vpc(this, "sap-cs-ecs-vpc", {
maxAzs: 2// Default is all AZs in region
});
const cluster = new cdk.aws_ecs.Cluster(this, "sap-cs-ecs-cluster", {
vpc: vpc
});
const ecs_pattern = new cdk.aws_ecs_patterns.ApplicationLoadBalancedFargateService(this, 'sap-cs-ecs-pattern', {
cluster: cluster,
desiredCount: 1,
cpu: 1024,
runtimePlatform: {
operatingSystemFamily: cdk.aws_ecs.OperatingSystemFamily.LINUX,
cpuArchitecture: cdk.aws_ecs.CpuArchitecture.ARM64
},
memoryLimitMiB: 2048,
taskImageOptions: {
// image: cdk.aws_ecs.ContainerImage.fromEcrRepository(ecr, dockerImageTagParam.valueAsString),
image: cdk.aws_ecs.ContainerImage.fromRegistry('nginx:latest'),
containerPort: 80
},
serviceName: 'sap-cs-on-aws-svc',
deploymentController: {
// type: cdk.aws_ecs.DeploymentControllerType.EXTERNAL
type: cdk.aws_ecs.DeploymentControllerType.ECS
},
loadBalancerName: 'sap-cs-on-aws-alb',
assignPublicIp: true,
// internal: true,
publicLoadBalancer: true,
listenerPort: 80,
protocol: cdk.aws_elasticloadbalancingv2.ApplicationProtocol.HTTP,
// sslPolicy: cdk.aws_elasticloadbalancingv2.SslPolicy.TLS12,
// certificate: certificate,
// sslPolicy: SslPolicy.FORWARD_SECRECY_TLS12
healthCheck: {
command: [ "CMD-SHELL", "curl -f http://localhost/ || exit 1" ],
interval: cdk.Duration.seconds(300),
retries: 10,
startPeriod: cdk.Duration.seconds(30),
timeout: cdk.Duration.seconds(30),
}
});
// Output
new cdk.CfnOutput(this, 'AwsCdk31016LoadBalancerALBUrl', {
value: `http://${ecs_pattern.loadBalancer.loadBalancerDnsName}`
});
const bucket = new s3.Bucket(this, "Bucket", {
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
const origin = new origins.OriginGroup({
primaryOrigin: new origins.LoadBalancerV2Origin(ecs_pattern.loadBalancer, {
protocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
}),
fallbackOrigin: new origins.S3Origin(bucket),
});
const distribution = new cloudfront.Distribution(this, "Distribution", {
defaultBehavior: { origin },
});
// //// configure OAC for S3 bucket and CloudFront
const oac = new cloudfront.CfnOriginAccessControl(this, 'ComposableStorefrontOriginAccessControl', {
originAccessControlConfig: {
name: 'ComposableStorefrontOriginAccessControl',
originAccessControlOriginType: 's3',
signingBehavior: 'always',
signingProtocol: 'sigv4'
}
});
//configure S3 Bucket to be accessed by Cloudfront only
const allowCloudFrontReadOnlyPolicy = new iam.PolicyStatement({
sid: 'allowCloudFrontReadOnlyPolicy',
actions: ['s3:GetObject'],
principals: [new iam.ServicePrincipal('cloudfront.amazonaws.com')],
effect: iam.Effect.ALLOW,
conditions: {
'StringEquals': {
"AWS:SourceArn": "arn:aws:cloudfront::" + this.account + ":distribution/" + distribution.distributionId
}
},
resources: [bucket.bucketArn, bucket.bucketArn.concat('/').concat('*')]
});
bucket.addToResourcePolicy(allowCloudFrontReadOnlyPolicy)
// OAC is not supported by CDK L2 construct yet. L1 construct is required
const cfnDistribution = distribution.node.defaultChild as cloudfront.CfnDistribution
//enable OAC
cfnDistribution.addPropertyOverride(
'DistributionConfig.Origins.0.OriginAccessControlId',
oac.getAtt('Id')
)
//disable OAI
cfnDistribution.addPropertyOverride(
'DistributionConfig.Origins.0.S3OriginConfig.OriginAccessIdentity',
'',
)
// Output
new cdk.CfnOutput(this, 'Cloudfrontdistributionurl', {
value: distribution.distributionDomainName
});
}
} Please note that the code that breaks the cloud formation stack is the following: // OAC is not supported by CDK L2 construct yet. L1 construct is required
const cfnDistribution = distribution.node.defaultChild as cloudfront.CfnDistribution
//enable OAC
cfnDistribution.addPropertyOverride(
'DistributionConfig.Origins.0.OriginAccessControlId',
oac.getAtt('Id')
) |
internal tracking: V1479047809 |
OK this works for me. Check my full sample below: export class AwsCdk31016Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// const vpc = new cdk.aws_ec2.Vpc(this, "sap-cs-ecs-vpc", {
// maxAzs: 2// Default is all AZs in region
// });
const vpc = ec2.Vpc.fromLookup(this, 'VPC', { isDefault: true })
const cluster = new cdk.aws_ecs.Cluster(this, "sap-cs-ecs-cluster", {
vpc
});
const ecs_pattern = new cdk.aws_ecs_patterns.ApplicationLoadBalancedFargateService(this, 'sap-cs-ecs-pattern', {
cluster: cluster,
desiredCount: 1,
cpu: 1024,
runtimePlatform: {
operatingSystemFamily: cdk.aws_ecs.OperatingSystemFamily.LINUX,
cpuArchitecture: cdk.aws_ecs.CpuArchitecture.ARM64
},
memoryLimitMiB: 2048,
taskImageOptions: {
// image: cdk.aws_ecs.ContainerImage.fromEcrRepository(ecr, dockerImageTagParam.valueAsString),
image: cdk.aws_ecs.ContainerImage.fromRegistry('nginx:latest'),
containerPort: 80
},
serviceName: 'sap-cs-on-aws-svc',
deploymentController: {
type: cdk.aws_ecs.DeploymentControllerType.ECS
},
loadBalancerName: 'sap-cs-on-aws-alb',
assignPublicIp: true,
publicLoadBalancer: true,
listenerPort: 80,
protocol: cdk.aws_elasticloadbalancingv2.ApplicationProtocol.HTTP,
healthCheck: {
command: [ "CMD-SHELL", "curl -f http://localhost/ || exit 1" ],
interval: cdk.Duration.seconds(300),
retries: 10,
startPeriod: cdk.Duration.seconds(30),
timeout: cdk.Duration.seconds(30),
}
});
// Output
new cdk.CfnOutput(this, 'AwsCdk31016LoadBalancerALBUrl', {
value: `http://${ecs_pattern.loadBalancer.loadBalancerDnsName}`
});
const bucket = new s3.Bucket(this, "Bucket", {
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
const originGroup = new origins.OriginGroup({
primaryOrigin: new origins.LoadBalancerV2Origin(ecs_pattern.loadBalancer, {
protocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
}),
fallbackOrigin: new origins.S3Origin(bucket),
});
const distribution = new cloudfront.Distribution(this, "Distribution", {
defaultBehavior: { origin: originGroup },
});
// configure OAC for S3 bucket and CloudFront
const oac = new cloudfront.CfnOriginAccessControl(this, 'ComposableStorefrontOriginAccessControl', {
originAccessControlConfig: {
name: 'ComposableStorefrontOriginAccessControl',
originAccessControlOriginType: 's3',
signingBehavior: 'always',
signingProtocol: 'sigv4'
}
});
//configure S3 Bucket to be accessed by Cloudfront only
const allowCloudFrontReadOnlyPolicy = new iam.PolicyStatement({
sid: 'allowCloudFrontReadOnlyPolicy',
actions: ['s3:GetObject'],
principals: [new iam.ServicePrincipal('cloudfront.amazonaws.com')],
effect: iam.Effect.ALLOW,
conditions: {
'StringEquals': {
"AWS:SourceArn": "arn:aws:cloudfront::" + this.account + ":distribution/" + distribution.distributionId
}
},
resources: [bucket.bucketArn, bucket.bucketArn.concat('/').concat('*')]
});
bucket.addToResourcePolicy(allowCloudFrontReadOnlyPolicy)
// OAC is not supported by CDK L2 construct yet. L1 construct is required
const cfnDistribution = distribution.node.defaultChild as cloudfront.CfnDistribution
//enable OAC for S3(the 2nd Origin)
cfnDistribution.addPropertyOverride(
'DistributionConfig.Origins.1.OriginAccessControlId',
oac.getAtt('Id')
)
// disable OAI by setting an empty string for it
// see - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-s3originconfig.html
cfnDistribution.addOverride('Properties.DistributionConfig.Origins.1.S3OriginConfig.OriginAccessIdentity', "");
// Output
new cdk.CfnOutput(this, 'Cloudfrontdistributionurl', {
value: distribution.distributionDomainName
});
}
} |
Let me know if it works for you. |
Describe the bug
When deploying a CloudFront distribution with an origin group as default behavior origin, you get the following CloudFormation error:
Resource handler returned message: "Invalid request provided: Exactly one of CustomOriginConfig and S3OriginConfig must be specified"
When setting the origin group as default behavior origin in the AWS Console everything works.
Expected Behavior
Set the origin group as the origin in the default behavior of the cloudfront distribution.
Current Behavior
An error occurs when the CloudFormation stack gets deployed:
Resource handler returned message: "Invalid request provided: Exactly one of CustomOriginConfig and S3OriginConfig must be specified"
Reproduction Steps
Here a sample setup:
Possible Solution
No response
Additional Information/Context
No response
CDK CLI Version
2.151.0 (build b8289e2)
Framework Version
No response
Node.js Version
v20.16.0
OS
macos
Language
TypeScript
Language Version
Typescript 5.5.4
Other information
No response
The text was updated successfully, but these errors were encountered: