Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/aws-cdk-lib/aws-elasticloadbalancingv2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ declare const vpc: ec2.Vpc;
const tg1 = new elbv2.ApplicationTargetGroup(this, 'TG1', {
targetType: elbv2.TargetType.INSTANCE,
port: 80,
protocol: elbv2.ApplicationProtocol.HTTP,
stickinessCookieDuration: Duration.minutes(5),
vpc,
});
Expand All @@ -617,6 +618,7 @@ const tg1 = new elbv2.ApplicationTargetGroup(this, 'TG1', {
const tg2 = new elbv2.ApplicationTargetGroup(this, 'TG2', {
targetType: elbv2.TargetType.INSTANCE,
port: 80,
protocol: elbv2.ApplicationProtocol.HTTP,
stickinessCookieDuration: Duration.minutes(5),
stickinessCookieName: 'MyDeliciousCookie',
vpc,
Expand All @@ -639,6 +641,7 @@ const tg = new elbv2.ApplicationTargetGroup(this, 'TG', {
targetType: elbv2.TargetType.INSTANCE,
slowStart: Duration.seconds(60),
port: 80,
protocol: elbv2.ApplicationProtocol.HTTP,
vpc,
});
```
Expand Down Expand Up @@ -680,6 +683,7 @@ declare const vpc: ec2.Vpc;

const tg = new elbv2.ApplicationTargetGroup(this, 'TargetGroup', {
vpc,
protocol: elbv2.ApplicationProtocol.HTTP,
loadBalancingAlgorithmType: elbv2.TargetGroupLoadBalancingAlgorithmType.WEIGHTED_RANDOM,
enableAnomalyMitigation: true,
});
Expand All @@ -699,6 +703,7 @@ declare const vpc: ec2.Vpc;
const targetGroup = new elbv2.ApplicationTargetGroup(this, 'TargetGroup', {
vpc,
port: 80,
protocol: elbv2.ApplicationProtocol.HTTP,
targetType: elbv2.TargetType.INSTANCE,

// Whether cross zone load balancing is enabled.
Expand All @@ -720,13 +725,15 @@ declare const vpc: ec2.Vpc;
const ipv4ApplicationTargetGroup = new elbv2.ApplicationTargetGroup(this, 'IPv4ApplicationTargetGroup', {
vpc,
port: 80,
protocol: elbv2.ApplicationProtocol.HTTP,
targetType: elbv2.TargetType.INSTANCE,
ipAddressType: elbv2.TargetGroupIpAddressType.IPV4,
});

const ipv6ApplicationTargetGroup = new elbv2.ApplicationTargetGroup(this, 'Ipv6ApplicationTargetGroup', {
vpc,
port: 80,
protocol: elbv2.ApplicationProtocol.HTTP,
targetType: elbv2.TargetType.INSTANCE,
ipAddressType: elbv2.TargetGroupIpAddressType.IPV6,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -614,9 +614,18 @@ export class ApplicationTargetGroup extends TargetGroupBase implements IApplicat
protected validateTargetGroup(): string[] {
const ret = super.validateTargetGroup();

// Check if this is a Lambda target group (either explicitly set or will be set to Lambda)
const isLambdaTarget = this.targetType === TargetType.LAMBDA;

// For non-Lambda target groups, protocol is required
if (!isLambdaTarget && this.protocol === undefined) {
ret.push('Protocol is required for ApplicationTargetGroup');
}

// Port validation for when targets are added (existing logic for non-Lambda)
if (this.targetType !== undefined && this.targetType !== TargetType.LAMBDA
&& (this.protocol === undefined || this.port === undefined)) {
ret.push('At least one of \'port\' or \'protocol\' is required for a non-Lambda TargetGroup');
&& this.port === undefined) {
ret.push('Port is required for non-Lambda TargetGroup');
}

if (this.healthCheck) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1430,11 +1430,13 @@ describe('tests', () => {
const listener = lb.addListener('Listener', {
port: 443,
certificates: [importedCertificate(stack, 'cert1'), importedCertificate(stack, 'cert2')],
defaultTargetGroups: [new elbv2.ApplicationTargetGroup(stack, 'Group', { vpc, port: 80 })],
defaultTargetGroups: [new elbv2.ApplicationTargetGroup(stack, 'Group', { vpc, port: 80, protocol: elbv2.ApplicationProtocol.HTTP })],
});

listener.addTargets('Target1', {
priority: 10,
port: 80,
protocol: elbv2.ApplicationProtocol.HTTP,
conditions: [elbv2.ListenerCondition.pathPatterns(['/test/path/1', '/test/path/2'])],
});

Expand Down Expand Up @@ -1476,7 +1478,7 @@ describe('tests', () => {
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'Stack');
const lb = new elbv2.ApplicationLoadBalancer(stack, 'LB', { vpc });
const group1 = new elbv2.ApplicationTargetGroup(stack, 'Group1', { vpc, port: 80 });
const group1 = new elbv2.ApplicationTargetGroup(stack, 'Group1', { vpc, port: 80, protocol: elbv2.ApplicationProtocol.HTTP });
const group2 = new elbv2.ApplicationTargetGroup(stack, 'Group2', { vpc, port: 81, protocol: elbv2.ApplicationProtocol.HTTP });

// WHEN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,73 @@ describe('tests', () => {
expect(() => app.synth()).toThrow(/port\/protocol should not be specified for Lambda targets/);
});

test('ApplicationTargetGroup requires protocol when no targets added', () => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'Stack');
const vpc = new ec2.Vpc(stack, 'Vpc');

// WHEN - Create ApplicationTargetGroup without protocol or port
new elbv2.ApplicationTargetGroup(stack, 'TG', {
vpc,
// protocol and port are both missing
});

// THEN
expect(() => app.synth()).toThrow(/Protocol is required for ApplicationTargetGroup/);
});

test('ApplicationTargetGroup with protocol should not throw validation error', () => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'Stack');
const vpc = new ec2.Vpc(stack, 'Vpc');

// WHEN - Create ApplicationTargetGroup with protocol
new elbv2.ApplicationTargetGroup(stack, 'TG', {
vpc,
port: 80,
protocol: elbv2.ApplicationProtocol.HTTP,
});

// THEN - Should not throw
expect(() => app.synth()).not.toThrow();
});

test('ApplicationTargetGroup with Lambda target does not require protocol', () => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'Stack');

// WHEN - Create ApplicationTargetGroup for Lambda without protocol
new elbv2.ApplicationTargetGroup(stack, 'TG', {
targetType: elbv2.TargetType.LAMBDA,
// protocol is missing but should be allowed for Lambda
});

// THEN - Should not throw
expect(() => app.synth()).not.toThrow();
});

test('ApplicationTargetGroup requires protocol even when targets added later', () => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'Stack');
const vpc = new ec2.Vpc(stack, 'Vpc');

// WHEN - Create ApplicationTargetGroup without protocol or port, then add non-Lambda target
const tg = new elbv2.ApplicationTargetGroup(stack, 'TG', {
vpc,
// protocol and port are both missing
});

// Add a non-Lambda target
tg.addTarget(new elbv2.InstanceTarget('i-1234'));

// THEN - Should still require protocol
expect(() => app.synth()).toThrow(/Protocol is required for ApplicationTargetGroup/);
});

test('Can add self-registering target to imported TargetGroup', () => {
// GIVEN
const app = new cdk.App();
Expand Down Expand Up @@ -157,6 +224,7 @@ describe('tests', () => {
new elbv2.ApplicationTargetGroup(stack, 'Group', {
vpc,
ipAddressType,
protocol: elbv2.ApplicationProtocol.HTTP,
});

Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', {
Expand All @@ -174,6 +242,7 @@ describe('tests', () => {
new elbv2.ApplicationTargetGroup(stack, 'TargetGroup', {
stickinessCookieDuration: cdk.Duration.minutes(5),
vpc,
protocol: elbv2.ApplicationProtocol.HTTP,
});

// THEN
Expand Down Expand Up @@ -206,6 +275,7 @@ describe('tests', () => {
stickinessCookieDuration: cdk.Duration.minutes(5),
stickinessCookieName: 'MyDeliciousCookie',
vpc,
protocol: elbv2.ApplicationProtocol.HTTP,
});

// THEN
Expand Down Expand Up @@ -241,6 +311,7 @@ describe('tests', () => {
new elbv2.ApplicationTargetGroup(stack, 'TargetGroup', {
loadBalancingAlgorithmType: elbv2.TargetGroupLoadBalancingAlgorithmType.LEAST_OUTSTANDING_REQUESTS,
vpc,
protocol: elbv2.ApplicationProtocol.HTTP,
});

// THEN
Expand All @@ -267,6 +338,7 @@ describe('tests', () => {
// WHEN
new elbv2.ApplicationTargetGroup(stack, 'TargetGroup', {
vpc,
protocol: elbv2.ApplicationProtocol.HTTP,
protocolVersion: elbv2.ApplicationProtocolVersion.GRPC,
healthCheck: {
enabled: true,
Expand Down Expand Up @@ -370,6 +442,7 @@ describe('tests', () => {
new elbv2.ApplicationTargetGroup(stack, 'TargetGroup', {
slowStart: cdk.Duration.seconds(0),
vpc,
protocol: elbv2.ApplicationProtocol.HTTP,
});

// THEN
Expand Down Expand Up @@ -442,6 +515,7 @@ describe('tests', () => {
// WHEN
new elbv2.ApplicationTargetGroup(stack, 'TargetGroup', {
vpc,
protocol: elbv2.ApplicationProtocol.HTTP,
healthCheck: {
protocol: protocol,
},
Expand All @@ -462,6 +536,7 @@ describe('tests', () => {
const vpc = new ec2.Vpc(stack, 'VPC', {});
const tg = new elbv2.ApplicationTargetGroup(stack, 'TargetGroup', {
vpc,
protocol: elbv2.ApplicationProtocol.HTTP,
});

// WHEN
Expand Down Expand Up @@ -737,6 +812,7 @@ describe('tests', () => {
new elbv2.ApplicationTargetGroup(stack, 'TargetGroup', {
loadBalancingAlgorithmType: elbv2.TargetGroupLoadBalancingAlgorithmType.WEIGHTED_RANDOM,
vpc,
protocol: elbv2.ApplicationProtocol.HTTP,
enableAnomalyMitigation: true,
});

Expand Down Expand Up @@ -769,6 +845,7 @@ describe('tests', () => {
new elbv2.ApplicationTargetGroup(stack, 'TargetGroup', {
loadBalancingAlgorithmType: elbv2.TargetGroupLoadBalancingAlgorithmType.WEIGHTED_RANDOM,
vpc,
protocol: elbv2.ApplicationProtocol.HTTP,
enableAnomalyMitigation: false,
});

Expand Down Expand Up @@ -831,7 +908,7 @@ describe('tests', () => {
const vpc = new ec2.Vpc(stack, 'VPC', {});

// WHEN
new elbv2.ApplicationTargetGroup(stack, 'LB', { crossZoneEnabled, vpc });
new elbv2.ApplicationTargetGroup(stack, 'LB', { crossZoneEnabled, vpc, protocol: elbv2.ApplicationProtocol.HTTP });

Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', {
TargetGroupAttributes: [
Expand Down
Loading