Skip to content

Conversation

matoom-nomu
Copy link
Contributor

@matoom-nomu matoom-nomu commented Sep 10, 2025

Issue # (if applicable)

#35450

Reason for this change

Lambda Function URLs natively support dual-stack IPv4/IPv6 connectivity, but CDK's FunctionUrlOrigin class did not expose the ipAddressType property to configure IP protocol preferences.

Description of changes

  • Added OriginIpAddressType enum with IPV4, IPV6, and DUALSTACK options
  • Added optional ipAddressType property to FunctionUrlOriginProps interface using the enum type
  • Default behavior follows CloudFormation default (IPv4 only) to avoid breaking changes
  • Updated both FunctionUrlOrigin and FunctionUrlOriginWithOAC classes to pass through the property
  • Added test coverage for default behavior, explicit value setting, and OAC integration
  • Updated README with usage examples and enum documentation

Describe any new or updated permissions being added

N/A

Description of how you validated changes

  • Added unit tests covering default behavior when ipAddressType is not specified
  • Added unit tests for explicit enum value setting (OriginIpAddressType.IPV4, OriginIpAddressType.IPV6, OriginIpAddressType.DUALSTACK)
  • Added test with Origin Access Control (OAC)

Checklist


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@aws-cdk-automation aws-cdk-automation requested a review from a team September 10, 2025 09:19
@github-actions github-actions bot added beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK p2 labels Sep 10, 2025
Copy link
Collaborator

@aws-cdk-automation aws-cdk-automation left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This review is outdated)

Copy link
Contributor

@badmintoncryer badmintoncryer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution! I've added some advices.

*
* @default 'dualstack'
*/
readonly ipAddressType?: 'ipv4' | 'ipv6' | 'dualstack';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot use union type in CDK. Could you please define new enum type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick review, and sorry for the elementary mistake.
i've added new enum.

@@ -76,6 +85,7 @@ export class FunctionUrlOrigin extends cloudfront.OriginBase {
originProtocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY,
originReadTimeout: this.props.readTimeout?.toSeconds(),
originKeepaliveTimeout: this.props.keepaliveTimeout?.toSeconds(),
ipAddressType: this.props.ipAddressType ?? 'dualstack',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the cloudformation default value of ipAddressType? (If we deploy cfn with specifying ipAddressType to undefined, is the IP address type set to dual stack?)

If that default value is not 'dualstack', this modification is a breaking change and you need to implement a feature flag.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the cloudformation default value of ipAddressType? (If we deploy cfn with specifying ipAddressType to undefined, is the IP address type set to dual stack?)

I checked in my personal account and if we don't explicitly define IpAddressType in CloudFormation, IpAddressType defaults to IPv4.

If that default value is not 'dualstack', this modification is a breaking change and you need to implement a feature flag.

I have removed the setting that makes dualstack the default and implemented only the ipAddressType property. Although I initially set dualstack as the default based on the description in #35450,
this would be a breaking change, so it might be better to limit this PR just adding the property for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your confirmation and I agree with your opinion.

@badmintoncryer
Copy link
Contributor

Could you please add integ test?

@matoom-nomu
Copy link
Contributor Author

Could you please add integ test?

sorry for being late, i just added integ-test's snapshot.

@aws-cdk-automation aws-cdk-automation dismissed their stale review September 11, 2025 12:05

✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.

Comment on lines 447 to 471
test('Does not include ipAddressType when not specified (uses CloudFormation default)', () => {
const fn = new lambda.Function(stack, 'MyFunction', {
code: lambda.Code.fromInline('exports.handler = async () => {};'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_20_X,
});

const fnUrl = fn.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});

const origin = new FunctionUrlOrigin(fnUrl);
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' });

expect(stack.resolve(originBindConfig.originProperty)).toEqual({
id: 'StackOriginLambdaFunctionURL',
domainName: {
'Fn::Select': [2, { 'Fn::Split': ['/', { 'Fn::GetAtt': ['MyFunctionFunctionUrlFF6DE78C', 'FunctionUrl'] }] }],
},
customOriginConfig: {
originProtocolPolicy: 'https-only',
originSslProtocols: ['TLSv1.2'],
},
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this test is not necessary.

Suggested change
test('Does not include ipAddressType when not specified (uses CloudFormation default)', () => {
const fn = new lambda.Function(stack, 'MyFunction', {
code: lambda.Code.fromInline('exports.handler = async () => {};'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_20_X,
});
const fnUrl = fn.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});
const origin = new FunctionUrlOrigin(fnUrl);
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' });
expect(stack.resolve(originBindConfig.originProperty)).toEqual({
id: 'StackOriginLambdaFunctionURL',
domainName: {
'Fn::Select': [2, { 'Fn::Split': ['/', { 'Fn::GetAtt': ['MyFunctionFunctionUrlFF6DE78C', 'FunctionUrl'] }] }],
},
customOriginConfig: {
originProtocolPolicy: 'https-only',
originSslProtocols: ['TLSv1.2'],
},
});
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

understood,i deleted this test case.

Comment on lines 473 to 543
test('Correctly sets ipAddressType to dualstack', () => {
const fn = new lambda.Function(stack, 'MyFunction', {
code: lambda.Code.fromInline('exports.handler = async () => {};'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_20_X,
});

const fnUrl = fn.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});

const origin = new FunctionUrlOrigin(fnUrl, {
ipAddressType: OriginIpAddressType.DUALSTACK,
});

const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' });

expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({
customOriginConfig: {
ipAddressType: OriginIpAddressType.DUALSTACK,
},
});
});

test('Correctly sets ipAddressType to ipv4', () => {
const fn = new lambda.Function(stack, 'MyFunction', {
code: lambda.Code.fromInline('exports.handler = async () => {};'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_20_X,
});

const fnUrl = fn.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});

const origin = new FunctionUrlOrigin(fnUrl, {
ipAddressType: OriginIpAddressType.IPV4,
});

const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' });

expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({
customOriginConfig: {
ipAddressType: OriginIpAddressType.IPV4,
},
});
});

test('Correctly sets ipAddressType to ipv6', () => {
const fn = new lambda.Function(stack, 'MyFunction', {
code: lambda.Code.fromInline('exports.handler = async () => {};'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_20_X,
});

const fnUrl = fn.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});

const origin = new FunctionUrlOrigin(fnUrl, {
ipAddressType: OriginIpAddressType.IPV6,
});

const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' });

expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({
customOriginConfig: {
ipAddressType: OriginIpAddressType.IPV6,
},
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to aggregate these tests like below.

Suggested change
test('Correctly sets ipAddressType to dualstack', () => {
const fn = new lambda.Function(stack, 'MyFunction', {
code: lambda.Code.fromInline('exports.handler = async () => {};'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_20_X,
});
const fnUrl = fn.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});
const origin = new FunctionUrlOrigin(fnUrl, {
ipAddressType: OriginIpAddressType.DUALSTACK,
});
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' });
expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({
customOriginConfig: {
ipAddressType: OriginIpAddressType.DUALSTACK,
},
});
});
test('Correctly sets ipAddressType to ipv4', () => {
const fn = new lambda.Function(stack, 'MyFunction', {
code: lambda.Code.fromInline('exports.handler = async () => {};'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_20_X,
});
const fnUrl = fn.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});
const origin = new FunctionUrlOrigin(fnUrl, {
ipAddressType: OriginIpAddressType.IPV4,
});
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' });
expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({
customOriginConfig: {
ipAddressType: OriginIpAddressType.IPV4,
},
});
});
test('Correctly sets ipAddressType to ipv6', () => {
const fn = new lambda.Function(stack, 'MyFunction', {
code: lambda.Code.fromInline('exports.handler = async () => {};'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_20_X,
});
const fnUrl = fn.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});
const origin = new FunctionUrlOrigin(fnUrl, {
ipAddressType: OriginIpAddressType.IPV6,
});
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' });
expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({
customOriginConfig: {
ipAddressType: OriginIpAddressType.IPV6,
},
});
});
test.each([OriginIpAddressType.DUALSTACK, OriginIpAddressType.IPV4, OriginIpAddressType.IPV6])('Correctly sets ipAddressType to %s', (ipAddressType) => {
const fn = new lambda.Function(stack, 'MyFunction', {
code: lambda.Code.fromInline('exports.handler = async () => {};'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_20_X,
});
const fnUrl = fn.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});
const origin = new FunctionUrlOrigin(fnUrl, {
ipAddressType,
});
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' });
expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({
customOriginConfig: {
ipAddressType,
},
});
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absolutely right, I aggregated testcase as you wrote.

Comment on lines 7 to 25
/**
* IP address type for CloudFront to use when connecting to the origin.
*/
export enum OriginIpAddressType {
/**
* CloudFront uses IPv4 only to connect to the origin.
*/
IPV4 = 'ipv4',

/**
* CloudFront uses IPv6 only to connect to the origin.
*/
IPV6 = 'ipv6',

/**
* CloudFront uses both IPv4 and IPv6 to connect to the origin.
*/
DUALSTACK = 'dualstack',
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this enum can be used not only fn url origin but also other origins, it might be better to define this enum in cloudfront/origin.ts.

https://github.com/aws/aws-cdk/pull/35445/files#diff-37c2a90cbbc7ef8b599be8e78ed33d7f1d2eb9cffd2f6ea688ea1191dd84301b

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to define this enum in cloudfront/origin.ts. same as your PR.
i really appreciate your specific feedbacks, I learned a lot !

@badmintoncryer
Copy link
Contributor

badmintoncryer commented Sep 11, 2025

Could you please fix this lint error and merge the newest main branch?

@aws-cdk-testing/framework-integ: /codebuild/output/src419940982/src/actions-runner/_work/aws-cdk/aws-cdk/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront-origins/test/integ.function-url-origin-ip-address-type.ts
@aws-cdk-testing/framework-integ:   58:4  error  Newline required at end of file but not found  eol-last

@matoom-nomu matoom-nomu changed the title feat(cloudfront-origins): add ipAddressType property to Lambda Function URL origins with dualstack default feat(cloudfront-origins): add ipAddressType property to Lambda Function URL origins Sep 12, 2025
@badmintoncryer
Copy link
Contributor

Could you please resolve these problems?
image

Copy link
Contributor

@badmintoncryer badmintoncryer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@aws-cdk-automation aws-cdk-automation added the pr/needs-maintainer-review This PR needs a review from a Core Team Member label Sep 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK p2 pr/needs-maintainer-review This PR needs a review from a Core Team Member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants