Configure AWS Application Load Balancer Listeners

The aws:alb/listener:Listener resource, part of the Pulumi AWS provider, defines how a load balancer accepts and routes incoming connections: its protocol, port, TLS configuration, and default actions. This guide focuses on four capabilities: HTTPS termination and traffic forwarding, weighted target group distribution, authentication (Cognito, JWT, mutual TLS), and HTTP-to-HTTPS redirection.

Listeners attach to existing load balancers and reference target groups, TLS certificates, and authentication providers. The examples are intentionally small. Combine them with your own load balancers, target groups, and security infrastructure.

Route HTTPS traffic to a target group

Most deployments accept HTTPS traffic on port 443 and forward requests to backend instances, terminating TLS at the load balancer.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const frontEnd = new aws.lb.LoadBalancer("front_end", {});
const frontEndTargetGroup = new aws.lb.TargetGroup("front_end", {});
const frontEndListener = new aws.lb.Listener("front_end", {
    loadBalancerArn: frontEnd.arn,
    port: 443,
    protocol: "HTTPS",
    sslPolicy: "ELBSecurityPolicy-2016-08",
    certificateArn: "arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4",
    defaultActions: [{
        type: "forward",
        targetGroupArn: frontEndTargetGroup.arn,
    }],
});
import pulumi
import pulumi_aws as aws

front_end = aws.lb.LoadBalancer("front_end")
front_end_target_group = aws.lb.TargetGroup("front_end")
front_end_listener = aws.lb.Listener("front_end",
    load_balancer_arn=front_end.arn,
    port=443,
    protocol="HTTPS",
    ssl_policy="ELBSecurityPolicy-2016-08",
    certificate_arn="arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4",
    default_actions=[{
        "type": "forward",
        "target_group_arn": front_end_target_group.arn,
    }])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lb"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		frontEnd, err := lb.NewLoadBalancer(ctx, "front_end", nil)
		if err != nil {
			return err
		}
		frontEndTargetGroup, err := lb.NewTargetGroup(ctx, "front_end", nil)
		if err != nil {
			return err
		}
		_, err = lb.NewListener(ctx, "front_end", &lb.ListenerArgs{
			LoadBalancerArn: frontEnd.Arn,
			Port:            pulumi.Int(443),
			Protocol:        pulumi.String("HTTPS"),
			SslPolicy:       pulumi.String("ELBSecurityPolicy-2016-08"),
			CertificateArn:  pulumi.String("arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4"),
			DefaultActions: lb.ListenerDefaultActionArray{
				&lb.ListenerDefaultActionArgs{
					Type:           pulumi.String("forward"),
					TargetGroupArn: frontEndTargetGroup.Arn,
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var frontEnd = new Aws.LB.LoadBalancer("front_end");

    var frontEndTargetGroup = new Aws.LB.TargetGroup("front_end");

    var frontEndListener = new Aws.LB.Listener("front_end", new()
    {
        LoadBalancerArn = frontEnd.Arn,
        Port = 443,
        Protocol = "HTTPS",
        SslPolicy = "ELBSecurityPolicy-2016-08",
        CertificateArn = "arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4",
        DefaultActions = new[]
        {
            new Aws.LB.Inputs.ListenerDefaultActionArgs
            {
                Type = "forward",
                TargetGroupArn = frontEndTargetGroup.Arn,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lb.LoadBalancer;
import com.pulumi.aws.lb.TargetGroup;
import com.pulumi.aws.lb.Listener;
import com.pulumi.aws.lb.ListenerArgs;
import com.pulumi.aws.lb.inputs.ListenerDefaultActionArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var frontEnd = new LoadBalancer("frontEnd");

        var frontEndTargetGroup = new TargetGroup("frontEndTargetGroup");

        var frontEndListener = new Listener("frontEndListener", ListenerArgs.builder()
            .loadBalancerArn(frontEnd.arn())
            .port(443)
            .protocol("HTTPS")
            .sslPolicy("ELBSecurityPolicy-2016-08")
            .certificateArn("arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4")
            .defaultActions(ListenerDefaultActionArgs.builder()
                .type("forward")
                .targetGroupArn(frontEndTargetGroup.arn())
                .build())
            .build());

    }
}
resources:
  frontEnd:
    type: aws:lb:LoadBalancer
    name: front_end
  frontEndTargetGroup:
    type: aws:lb:TargetGroup
    name: front_end
  frontEndListener:
    type: aws:lb:Listener
    name: front_end
    properties:
      loadBalancerArn: ${frontEnd.arn}
      port: '443'
      protocol: HTTPS
      sslPolicy: ELBSecurityPolicy-2016-08
      certificateArn: arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4
      defaultActions:
        - type: forward
          targetGroupArn: ${frontEndTargetGroup.arn}

The listener accepts connections on the specified port using the protocol (HTTPS here). The certificateArn provides the TLS certificate for encryption, and sslPolicy controls which cipher suites are allowed. The defaultActions array defines what happens to requests: here, they’re forwarded to the target group specified by targetGroupArn.

Split traffic across target groups with weights

Blue-green deployments and canary releases route traffic to multiple target groups with configurable weight distribution.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const frontEnd = new aws.lb.LoadBalancer("front_end", {});
const frontEndBlue = new aws.lb.TargetGroup("front_end_blue", {});
const frontEndGreen = new aws.lb.TargetGroup("front_end_green", {});
const frontEndListener = new aws.lb.Listener("front_end", {
    loadBalancerArn: frontEnd.arn,
    port: 443,
    protocol: "HTTPS",
    sslPolicy: "ELBSecurityPolicy-2016-08",
    certificateArn: "arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4",
    defaultActions: [{
        type: "forward",
        forward: {
            targetGroups: [
                {
                    arn: frontEndBlue.arn,
                    weight: 100,
                },
                {
                    arn: frontEndGreen.arn,
                    weight: 0,
                },
            ],
        },
    }],
});
import pulumi
import pulumi_aws as aws

front_end = aws.lb.LoadBalancer("front_end")
front_end_blue = aws.lb.TargetGroup("front_end_blue")
front_end_green = aws.lb.TargetGroup("front_end_green")
front_end_listener = aws.lb.Listener("front_end",
    load_balancer_arn=front_end.arn,
    port=443,
    protocol="HTTPS",
    ssl_policy="ELBSecurityPolicy-2016-08",
    certificate_arn="arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4",
    default_actions=[{
        "type": "forward",
        "forward": {
            "target_groups": [
                {
                    "arn": front_end_blue.arn,
                    "weight": 100,
                },
                {
                    "arn": front_end_green.arn,
                    "weight": 0,
                },
            ],
        },
    }])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lb"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		frontEnd, err := lb.NewLoadBalancer(ctx, "front_end", nil)
		if err != nil {
			return err
		}
		frontEndBlue, err := lb.NewTargetGroup(ctx, "front_end_blue", nil)
		if err != nil {
			return err
		}
		frontEndGreen, err := lb.NewTargetGroup(ctx, "front_end_green", nil)
		if err != nil {
			return err
		}
		_, err = lb.NewListener(ctx, "front_end", &lb.ListenerArgs{
			LoadBalancerArn: frontEnd.Arn,
			Port:            pulumi.Int(443),
			Protocol:        pulumi.String("HTTPS"),
			SslPolicy:       pulumi.String("ELBSecurityPolicy-2016-08"),
			CertificateArn:  pulumi.String("arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4"),
			DefaultActions: lb.ListenerDefaultActionArray{
				&lb.ListenerDefaultActionArgs{
					Type: pulumi.String("forward"),
					Forward: &lb.ListenerDefaultActionForwardArgs{
						TargetGroups: lb.ListenerDefaultActionForwardTargetGroupArray{
							&lb.ListenerDefaultActionForwardTargetGroupArgs{
								Arn:    frontEndBlue.Arn,
								Weight: pulumi.Int(100),
							},
							&lb.ListenerDefaultActionForwardTargetGroupArgs{
								Arn:    frontEndGreen.Arn,
								Weight: pulumi.Int(0),
							},
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var frontEnd = new Aws.LB.LoadBalancer("front_end");

    var frontEndBlue = new Aws.LB.TargetGroup("front_end_blue");

    var frontEndGreen = new Aws.LB.TargetGroup("front_end_green");

    var frontEndListener = new Aws.LB.Listener("front_end", new()
    {
        LoadBalancerArn = frontEnd.Arn,
        Port = 443,
        Protocol = "HTTPS",
        SslPolicy = "ELBSecurityPolicy-2016-08",
        CertificateArn = "arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4",
        DefaultActions = new[]
        {
            new Aws.LB.Inputs.ListenerDefaultActionArgs
            {
                Type = "forward",
                Forward = new Aws.LB.Inputs.ListenerDefaultActionForwardArgs
                {
                    TargetGroups = new[]
                    {
                        new Aws.LB.Inputs.ListenerDefaultActionForwardTargetGroupArgs
                        {
                            Arn = frontEndBlue.Arn,
                            Weight = 100,
                        },
                        new Aws.LB.Inputs.ListenerDefaultActionForwardTargetGroupArgs
                        {
                            Arn = frontEndGreen.Arn,
                            Weight = 0,
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lb.LoadBalancer;
import com.pulumi.aws.lb.TargetGroup;
import com.pulumi.aws.lb.Listener;
import com.pulumi.aws.lb.ListenerArgs;
import com.pulumi.aws.lb.inputs.ListenerDefaultActionArgs;
import com.pulumi.aws.lb.inputs.ListenerDefaultActionForwardArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var frontEnd = new LoadBalancer("frontEnd");

        var frontEndBlue = new TargetGroup("frontEndBlue");

        var frontEndGreen = new TargetGroup("frontEndGreen");

        var frontEndListener = new Listener("frontEndListener", ListenerArgs.builder()
            .loadBalancerArn(frontEnd.arn())
            .port(443)
            .protocol("HTTPS")
            .sslPolicy("ELBSecurityPolicy-2016-08")
            .certificateArn("arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4")
            .defaultActions(ListenerDefaultActionArgs.builder()
                .type("forward")
                .forward(ListenerDefaultActionForwardArgs.builder()
                    .targetGroups(                    
                        ListenerDefaultActionForwardTargetGroupArgs.builder()
                            .arn(frontEndBlue.arn())
                            .weight(100)
                            .build(),
                        ListenerDefaultActionForwardTargetGroupArgs.builder()
                            .arn(frontEndGreen.arn())
                            .weight(0)
                            .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  frontEnd:
    type: aws:lb:LoadBalancer
    name: front_end
  frontEndBlue:
    type: aws:lb:TargetGroup
    name: front_end_blue
  frontEndGreen:
    type: aws:lb:TargetGroup
    name: front_end_green
  frontEndListener:
    type: aws:lb:Listener
    name: front_end
    properties:
      loadBalancerArn: ${frontEnd.arn}
      port: '443'
      protocol: HTTPS
      sslPolicy: ELBSecurityPolicy-2016-08
      certificateArn: arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4
      defaultActions:
        - type: forward
          forward:
            targetGroups:
              - arn: ${frontEndBlue.arn}
                weight: 100
              - arn: ${frontEndGreen.arn}
                weight: 0

The forward block replaces the simple targetGroupArn with a targetGroups array. Each entry specifies an ARN and weight. Weights determine the percentage of traffic each target group receives. Setting one weight to 0 and another to 100 creates a blue-green deployment where you can gradually shift traffic by adjusting weights.

Redirect HTTP to HTTPS automatically

Security policies often require redirecting all HTTP traffic to HTTPS to ensure encrypted connections.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const frontEnd = new aws.lb.LoadBalancer("front_end", {});
const frontEndListener = new aws.lb.Listener("front_end", {
    loadBalancerArn: frontEnd.arn,
    port: 80,
    protocol: "HTTP",
    defaultActions: [{
        type: "redirect",
        redirect: {
            port: "443",
            protocol: "HTTPS",
            statusCode: "HTTP_301",
        },
    }],
});
import pulumi
import pulumi_aws as aws

front_end = aws.lb.LoadBalancer("front_end")
front_end_listener = aws.lb.Listener("front_end",
    load_balancer_arn=front_end.arn,
    port=80,
    protocol="HTTP",
    default_actions=[{
        "type": "redirect",
        "redirect": {
            "port": "443",
            "protocol": "HTTPS",
            "status_code": "HTTP_301",
        },
    }])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lb"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		frontEnd, err := lb.NewLoadBalancer(ctx, "front_end", nil)
		if err != nil {
			return err
		}
		_, err = lb.NewListener(ctx, "front_end", &lb.ListenerArgs{
			LoadBalancerArn: frontEnd.Arn,
			Port:            pulumi.Int(80),
			Protocol:        pulumi.String("HTTP"),
			DefaultActions: lb.ListenerDefaultActionArray{
				&lb.ListenerDefaultActionArgs{
					Type: pulumi.String("redirect"),
					Redirect: &lb.ListenerDefaultActionRedirectArgs{
						Port:       pulumi.String("443"),
						Protocol:   pulumi.String("HTTPS"),
						StatusCode: pulumi.String("HTTP_301"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var frontEnd = new Aws.LB.LoadBalancer("front_end");

    var frontEndListener = new Aws.LB.Listener("front_end", new()
    {
        LoadBalancerArn = frontEnd.Arn,
        Port = 80,
        Protocol = "HTTP",
        DefaultActions = new[]
        {
            new Aws.LB.Inputs.ListenerDefaultActionArgs
            {
                Type = "redirect",
                Redirect = new Aws.LB.Inputs.ListenerDefaultActionRedirectArgs
                {
                    Port = "443",
                    Protocol = "HTTPS",
                    StatusCode = "HTTP_301",
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lb.LoadBalancer;
import com.pulumi.aws.lb.Listener;
import com.pulumi.aws.lb.ListenerArgs;
import com.pulumi.aws.lb.inputs.ListenerDefaultActionArgs;
import com.pulumi.aws.lb.inputs.ListenerDefaultActionRedirectArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var frontEnd = new LoadBalancer("frontEnd");

        var frontEndListener = new Listener("frontEndListener", ListenerArgs.builder()
            .loadBalancerArn(frontEnd.arn())
            .port(80)
            .protocol("HTTP")
            .defaultActions(ListenerDefaultActionArgs.builder()
                .type("redirect")
                .redirect(ListenerDefaultActionRedirectArgs.builder()
                    .port("443")
                    .protocol("HTTPS")
                    .statusCode("HTTP_301")
                    .build())
                .build())
            .build());

    }
}
resources:
  frontEnd:
    type: aws:lb:LoadBalancer
    name: front_end
  frontEndListener:
    type: aws:lb:Listener
    name: front_end
    properties:
      loadBalancerArn: ${frontEnd.arn}
      port: '80'
      protocol: HTTP
      defaultActions:
        - type: redirect
          redirect:
            port: '443'
            protocol: HTTPS
            statusCode: HTTP_301

The redirect action sends clients to a different URL. Here, it changes the protocol to HTTPS and port to 443, using a 301 permanent redirect. This listener handles HTTP on port 80 and redirects to HTTPS without needing a certificate or target group.

Authenticate users with Cognito before forwarding

Applications that need user authentication can delegate to Cognito User Pools before allowing access to backend resources.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const frontEnd = new aws.lb.LoadBalancer("front_end", {});
const frontEndTargetGroup = new aws.lb.TargetGroup("front_end", {});
const pool = new aws.cognito.UserPool("pool", {});
const client = new aws.cognito.UserPoolClient("client", {});
const domain = new aws.cognito.UserPoolDomain("domain", {});
const frontEndListener = new aws.lb.Listener("front_end", {
    loadBalancerArn: frontEnd.arn,
    port: 80,
    protocol: "HTTP",
    defaultActions: [
        {
            type: "authenticate-cognito",
            authenticateCognito: {
                userPoolArn: pool.arn,
                userPoolClientId: client.id,
                userPoolDomain: domain.domain,
            },
        },
        {
            type: "forward",
            targetGroupArn: frontEndTargetGroup.arn,
        },
    ],
});
import pulumi
import pulumi_aws as aws

front_end = aws.lb.LoadBalancer("front_end")
front_end_target_group = aws.lb.TargetGroup("front_end")
pool = aws.cognito.UserPool("pool")
client = aws.cognito.UserPoolClient("client")
domain = aws.cognito.UserPoolDomain("domain")
front_end_listener = aws.lb.Listener("front_end",
    load_balancer_arn=front_end.arn,
    port=80,
    protocol="HTTP",
    default_actions=[
        {
            "type": "authenticate-cognito",
            "authenticate_cognito": {
                "user_pool_arn": pool.arn,
                "user_pool_client_id": client.id,
                "user_pool_domain": domain.domain,
            },
        },
        {
            "type": "forward",
            "target_group_arn": front_end_target_group.arn,
        },
    ])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cognito"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lb"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		frontEnd, err := lb.NewLoadBalancer(ctx, "front_end", nil)
		if err != nil {
			return err
		}
		frontEndTargetGroup, err := lb.NewTargetGroup(ctx, "front_end", nil)
		if err != nil {
			return err
		}
		pool, err := cognito.NewUserPool(ctx, "pool", nil)
		if err != nil {
			return err
		}
		client, err := cognito.NewUserPoolClient(ctx, "client", nil)
		if err != nil {
			return err
		}
		domain, err := cognito.NewUserPoolDomain(ctx, "domain", nil)
		if err != nil {
			return err
		}
		_, err = lb.NewListener(ctx, "front_end", &lb.ListenerArgs{
			LoadBalancerArn: frontEnd.Arn,
			Port:            pulumi.Int(80),
			Protocol:        pulumi.String("HTTP"),
			DefaultActions: lb.ListenerDefaultActionArray{
				&lb.ListenerDefaultActionArgs{
					Type: pulumi.String("authenticate-cognito"),
					AuthenticateCognito: &lb.ListenerDefaultActionAuthenticateCognitoArgs{
						UserPoolArn:      pool.Arn,
						UserPoolClientId: client.ID(),
						UserPoolDomain:   domain.Domain,
					},
				},
				&lb.ListenerDefaultActionArgs{
					Type:           pulumi.String("forward"),
					TargetGroupArn: frontEndTargetGroup.Arn,
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var frontEnd = new Aws.LB.LoadBalancer("front_end");

    var frontEndTargetGroup = new Aws.LB.TargetGroup("front_end");

    var pool = new Aws.Cognito.UserPool("pool");

    var client = new Aws.Cognito.UserPoolClient("client");

    var domain = new Aws.Cognito.UserPoolDomain("domain");

    var frontEndListener = new Aws.LB.Listener("front_end", new()
    {
        LoadBalancerArn = frontEnd.Arn,
        Port = 80,
        Protocol = "HTTP",
        DefaultActions = new[]
        {
            new Aws.LB.Inputs.ListenerDefaultActionArgs
            {
                Type = "authenticate-cognito",
                AuthenticateCognito = new Aws.LB.Inputs.ListenerDefaultActionAuthenticateCognitoArgs
                {
                    UserPoolArn = pool.Arn,
                    UserPoolClientId = client.Id,
                    UserPoolDomain = domain.Domain,
                },
            },
            new Aws.LB.Inputs.ListenerDefaultActionArgs
            {
                Type = "forward",
                TargetGroupArn = frontEndTargetGroup.Arn,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lb.LoadBalancer;
import com.pulumi.aws.lb.TargetGroup;
import com.pulumi.aws.cognito.UserPool;
import com.pulumi.aws.cognito.UserPoolClient;
import com.pulumi.aws.cognito.UserPoolDomain;
import com.pulumi.aws.lb.Listener;
import com.pulumi.aws.lb.ListenerArgs;
import com.pulumi.aws.lb.inputs.ListenerDefaultActionArgs;
import com.pulumi.aws.lb.inputs.ListenerDefaultActionAuthenticateCognitoArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var frontEnd = new LoadBalancer("frontEnd");

        var frontEndTargetGroup = new TargetGroup("frontEndTargetGroup");

        var pool = new UserPool("pool");

        var client = new UserPoolClient("client");

        var domain = new UserPoolDomain("domain");

        var frontEndListener = new Listener("frontEndListener", ListenerArgs.builder()
            .loadBalancerArn(frontEnd.arn())
            .port(80)
            .protocol("HTTP")
            .defaultActions(            
                ListenerDefaultActionArgs.builder()
                    .type("authenticate-cognito")
                    .authenticateCognito(ListenerDefaultActionAuthenticateCognitoArgs.builder()
                        .userPoolArn(pool.arn())
                        .userPoolClientId(client.id())
                        .userPoolDomain(domain.domain())
                        .build())
                    .build(),
                ListenerDefaultActionArgs.builder()
                    .type("forward")
                    .targetGroupArn(frontEndTargetGroup.arn())
                    .build())
            .build());

    }
}
resources:
  frontEnd:
    type: aws:lb:LoadBalancer
    name: front_end
  frontEndTargetGroup:
    type: aws:lb:TargetGroup
    name: front_end
  pool:
    type: aws:cognito:UserPool
  client:
    type: aws:cognito:UserPoolClient
  domain:
    type: aws:cognito:UserPoolDomain
  frontEndListener:
    type: aws:lb:Listener
    name: front_end
    properties:
      loadBalancerArn: ${frontEnd.arn}
      port: '80'
      protocol: HTTP
      defaultActions:
        - type: authenticate-cognito
          authenticateCognito:
            userPoolArn: ${pool.arn}
            userPoolClientId: ${client.id}
            userPoolDomain: ${domain.domain}
        - type: forward
          targetGroupArn: ${frontEndTargetGroup.arn}

The defaultActions array chains multiple actions: first authenticate-cognito, then forward. The authenticateCognito block specifies the Cognito User Pool, client, and domain. When a request arrives, the listener checks for a valid session cookie. If missing, it redirects to Cognito for login, then forwards authenticated requests to the target group.

Validate JWT tokens before routing requests

API gateways and microservices often validate JWT tokens at the edge to verify caller identity and claims before forwarding to backends.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const test = new aws.lb.Listener("test", {
    loadBalancerArn: testAwsLb.id,
    protocol: "HTTPS",
    port: 443,
    sslPolicy: "ELBSecurityPolicy-2016-08",
    certificateArn: testAwsIamServerCertificate.arn,
    defaultActions: [
        {
            type: "jwt-validation",
            jwtValidation: {
                issuer: "https://example.com",
                jwksEndpoint: "https://example.com/.well-known/jwks.json",
                additionalClaims: [
                    {
                        format: "string-array",
                        name: "claim_name1",
                        values: [
                            "value1",
                            "value2",
                        ],
                    },
                    {
                        format: "single-string",
                        name: "claim_name2",
                        values: ["value1"],
                    },
                ],
            },
        },
        {
            targetGroupArn: testAwsLbTargetGroup.id,
            type: "forward",
        },
    ],
});
import pulumi
import pulumi_aws as aws

test = aws.lb.Listener("test",
    load_balancer_arn=test_aws_lb["id"],
    protocol="HTTPS",
    port=443,
    ssl_policy="ELBSecurityPolicy-2016-08",
    certificate_arn=test_aws_iam_server_certificate["arn"],
    default_actions=[
        {
            "type": "jwt-validation",
            "jwt_validation": {
                "issuer": "https://example.com",
                "jwks_endpoint": "https://example.com/.well-known/jwks.json",
                "additional_claims": [
                    {
                        "format": "string-array",
                        "name": "claim_name1",
                        "values": [
                            "value1",
                            "value2",
                        ],
                    },
                    {
                        "format": "single-string",
                        "name": "claim_name2",
                        "values": ["value1"],
                    },
                ],
            },
        },
        {
            "target_group_arn": test_aws_lb_target_group["id"],
            "type": "forward",
        },
    ])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lb"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := lb.NewListener(ctx, "test", &lb.ListenerArgs{
			LoadBalancerArn: pulumi.Any(testAwsLb.Id),
			Protocol:        pulumi.String("HTTPS"),
			Port:            pulumi.Int(443),
			SslPolicy:       pulumi.String("ELBSecurityPolicy-2016-08"),
			CertificateArn:  pulumi.Any(testAwsIamServerCertificate.Arn),
			DefaultActions: lb.ListenerDefaultActionArray{
				&lb.ListenerDefaultActionArgs{
					Type: pulumi.String("jwt-validation"),
					JwtValidation: &lb.ListenerDefaultActionJwtValidationArgs{
						Issuer:       pulumi.String("https://example.com"),
						JwksEndpoint: pulumi.String("https://example.com/.well-known/jwks.json"),
						AdditionalClaims: lb.ListenerDefaultActionJwtValidationAdditionalClaimArray{
							&lb.ListenerDefaultActionJwtValidationAdditionalClaimArgs{
								Format: pulumi.String("string-array"),
								Name:   pulumi.String("claim_name1"),
								Values: pulumi.StringArray{
									pulumi.String("value1"),
									pulumi.String("value2"),
								},
							},
							&lb.ListenerDefaultActionJwtValidationAdditionalClaimArgs{
								Format: pulumi.String("single-string"),
								Name:   pulumi.String("claim_name2"),
								Values: pulumi.StringArray{
									pulumi.String("value1"),
								},
							},
						},
					},
				},
				&lb.ListenerDefaultActionArgs{
					TargetGroupArn: pulumi.Any(testAwsLbTargetGroup.Id),
					Type:           pulumi.String("forward"),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var test = new Aws.LB.Listener("test", new()
    {
        LoadBalancerArn = testAwsLb.Id,
        Protocol = "HTTPS",
        Port = 443,
        SslPolicy = "ELBSecurityPolicy-2016-08",
        CertificateArn = testAwsIamServerCertificate.Arn,
        DefaultActions = new[]
        {
            new Aws.LB.Inputs.ListenerDefaultActionArgs
            {
                Type = "jwt-validation",
                JwtValidation = new Aws.LB.Inputs.ListenerDefaultActionJwtValidationArgs
                {
                    Issuer = "https://example.com",
                    JwksEndpoint = "https://example.com/.well-known/jwks.json",
                    AdditionalClaims = new[]
                    {
                        new Aws.LB.Inputs.ListenerDefaultActionJwtValidationAdditionalClaimArgs
                        {
                            Format = "string-array",
                            Name = "claim_name1",
                            Values = new[]
                            {
                                "value1",
                                "value2",
                            },
                        },
                        new Aws.LB.Inputs.ListenerDefaultActionJwtValidationAdditionalClaimArgs
                        {
                            Format = "single-string",
                            Name = "claim_name2",
                            Values = new[]
                            {
                                "value1",
                            },
                        },
                    },
                },
            },
            new Aws.LB.Inputs.ListenerDefaultActionArgs
            {
                TargetGroupArn = testAwsLbTargetGroup.Id,
                Type = "forward",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lb.Listener;
import com.pulumi.aws.lb.ListenerArgs;
import com.pulumi.aws.lb.inputs.ListenerDefaultActionArgs;
import com.pulumi.aws.lb.inputs.ListenerDefaultActionJwtValidationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var test = new Listener("test", ListenerArgs.builder()
            .loadBalancerArn(testAwsLb.id())
            .protocol("HTTPS")
            .port(443)
            .sslPolicy("ELBSecurityPolicy-2016-08")
            .certificateArn(testAwsIamServerCertificate.arn())
            .defaultActions(            
                ListenerDefaultActionArgs.builder()
                    .type("jwt-validation")
                    .jwtValidation(ListenerDefaultActionJwtValidationArgs.builder()
                        .issuer("https://example.com")
                        .jwksEndpoint("https://example.com/.well-known/jwks.json")
                        .additionalClaims(                        
                            ListenerDefaultActionJwtValidationAdditionalClaimArgs.builder()
                                .format("string-array")
                                .name("claim_name1")
                                .values(                                
                                    "value1",
                                    "value2")
                                .build(),
                            ListenerDefaultActionJwtValidationAdditionalClaimArgs.builder()
                                .format("single-string")
                                .name("claim_name2")
                                .values("value1")
                                .build())
                        .build())
                    .build(),
                ListenerDefaultActionArgs.builder()
                    .targetGroupArn(testAwsLbTargetGroup.id())
                    .type("forward")
                    .build())
            .build());

    }
}
resources:
  test:
    type: aws:lb:Listener
    properties:
      loadBalancerArn: ${testAwsLb.id}
      protocol: HTTPS
      port: '443'
      sslPolicy: ELBSecurityPolicy-2016-08
      certificateArn: ${testAwsIamServerCertificate.arn}
      defaultActions:
        - type: jwt-validation
          jwtValidation:
            issuer: https://example.com
            jwksEndpoint: https://example.com/.well-known/jwks.json
            additionalClaims:
              - format: string-array
                name: claim_name1
                values:
                  - value1
                  - value2
              - format: single-string
                name: claim_name2
                values:
                  - value1
        - targetGroupArn: ${testAwsLbTargetGroup.id}
          type: forward

The jwt-validation action verifies tokens against the issuer’s JWKS endpoint. The additionalClaims array specifies required claims and their expected values. Tokens must contain matching claims to pass validation. Failed validation blocks the request before it reaches your application.

Require client certificates with mutual TLS

High-security environments require clients to present certificates for mutual authentication, verifying both server and client identity.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.lb.LoadBalancer("example", {loadBalancerType: "application"});
const exampleTargetGroup = new aws.lb.TargetGroup("example", {});
const exampleListener = new aws.lb.Listener("example", {
    loadBalancerArn: example.id,
    defaultActions: [{
        targetGroupArn: exampleTargetGroup.id,
        type: "forward",
    }],
    mutualAuthentication: {
        mode: "verify",
        trustStoreArn: "...",
    },
});
import pulumi
import pulumi_aws as aws

example = aws.lb.LoadBalancer("example", load_balancer_type="application")
example_target_group = aws.lb.TargetGroup("example")
example_listener = aws.lb.Listener("example",
    load_balancer_arn=example.id,
    default_actions=[{
        "target_group_arn": example_target_group.id,
        "type": "forward",
    }],
    mutual_authentication={
        "mode": "verify",
        "trust_store_arn": "...",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lb"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := lb.NewLoadBalancer(ctx, "example", &lb.LoadBalancerArgs{
			LoadBalancerType: pulumi.String("application"),
		})
		if err != nil {
			return err
		}
		exampleTargetGroup, err := lb.NewTargetGroup(ctx, "example", nil)
		if err != nil {
			return err
		}
		_, err = lb.NewListener(ctx, "example", &lb.ListenerArgs{
			LoadBalancerArn: example.ID(),
			DefaultActions: lb.ListenerDefaultActionArray{
				&lb.ListenerDefaultActionArgs{
					TargetGroupArn: exampleTargetGroup.ID(),
					Type:           pulumi.String("forward"),
				},
			},
			MutualAuthentication: &lb.ListenerMutualAuthenticationArgs{
				Mode:          pulumi.String("verify"),
				TrustStoreArn: pulumi.String("..."),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.LB.LoadBalancer("example", new()
    {
        LoadBalancerType = "application",
    });

    var exampleTargetGroup = new Aws.LB.TargetGroup("example");

    var exampleListener = new Aws.LB.Listener("example", new()
    {
        LoadBalancerArn = example.Id,
        DefaultActions = new[]
        {
            new Aws.LB.Inputs.ListenerDefaultActionArgs
            {
                TargetGroupArn = exampleTargetGroup.Id,
                Type = "forward",
            },
        },
        MutualAuthentication = new Aws.LB.Inputs.ListenerMutualAuthenticationArgs
        {
            Mode = "verify",
            TrustStoreArn = "...",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lb.LoadBalancer;
import com.pulumi.aws.lb.LoadBalancerArgs;
import com.pulumi.aws.lb.TargetGroup;
import com.pulumi.aws.lb.Listener;
import com.pulumi.aws.lb.ListenerArgs;
import com.pulumi.aws.lb.inputs.ListenerDefaultActionArgs;
import com.pulumi.aws.lb.inputs.ListenerMutualAuthenticationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new LoadBalancer("example", LoadBalancerArgs.builder()
            .loadBalancerType("application")
            .build());

        var exampleTargetGroup = new TargetGroup("exampleTargetGroup");

        var exampleListener = new Listener("exampleListener", ListenerArgs.builder()
            .loadBalancerArn(example.id())
            .defaultActions(ListenerDefaultActionArgs.builder()
                .targetGroupArn(exampleTargetGroup.id())
                .type("forward")
                .build())
            .mutualAuthentication(ListenerMutualAuthenticationArgs.builder()
                .mode("verify")
                .trustStoreArn("...")
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:lb:LoadBalancer
    properties:
      loadBalancerType: application
  exampleTargetGroup:
    type: aws:lb:TargetGroup
    name: example
  exampleListener:
    type: aws:lb:Listener
    name: example
    properties:
      loadBalancerArn: ${example.id}
      defaultActions:
        - targetGroupArn: ${exampleTargetGroup.id}
          type: forward
      mutualAuthentication:
        mode: verify
        trustStoreArn: '...'

The mutualAuthentication block enables client certificate validation. Setting mode to “verify” requires clients to present valid certificates from the trust store. The trustStoreArn points to an S3-backed trust store containing allowed certificate authorities. Requests without valid certificates are rejected at the load balancer.

Beyond these examples

These snippets focus on specific listener-level features: traffic routing and weighted distribution, authentication (Cognito, OIDC, JWT), and HTTP-to-HTTPS redirection and mutual TLS. They’re intentionally minimal rather than full load balancing solutions.

The examples reference pre-existing infrastructure such as load balancers and target groups, TLS certificates and trust stores, and Cognito User Pools or OIDC providers. They focus on configuring the listener rather than provisioning everything around it.

To keep things focused, common listener patterns are omitted, including:

  • Listener rules for path-based or host-based routing
  • Fixed-response actions for maintenance pages
  • ALPN policies for protocol negotiation
  • CORS and security headers (routing* properties)
  • TCP idle timeout tuning (tcpIdleTimeoutSeconds)

These omissions are intentional: the goal is to illustrate how each listener feature is wired, not provide drop-in load balancing modules. See the ALB Listener resource reference for all available configuration options.

Let's configure AWS Application Load Balancer Listeners

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Protocol & Load Balancer Types
What protocols are supported for each load balancer type?
Application Load Balancers support HTTP and HTTPS (default: HTTP). Network Load Balancers support TCP, TLS, UDP, TCP_UDP, QUIC, and TCP_QUIC. Gateway Load Balancers don’t use the protocol property for listeners.
Why can't I use QUIC or UDP protocols with my load balancer?
QUIC and TCP_QUIC protocols aren’t valid if security groups are configured or dual-stack mode is enabled. UDP and TCP_UDP aren’t valid if dual-stack mode is enabled on your Network Load Balancer.
Can I specify a port for Gateway Load Balancer listeners?
No, the port property isn’t valid for Gateway Load Balancers. Only Application and Network Load Balancers require a port specification.
SSL/TLS & Certificates
When is an SSL certificate required for my listener?
An SSL certificate is required when the protocol is HTTPS. You must provide exactly one certificate via certificateArn. For additional certificates, use the aws.lb.ListenerCertificate resource.
What's the default SSL policy and when do I need to specify one?
The default SSL policy is ELBSecurityPolicy-2016-08. You must specify an sslPolicy if your protocol is HTTPS or TLS, though the default will be used if you don’t provide one.
How do I configure ALPN for TLS listeners?
Set the alpnPolicy property when your protocol is TLS. Valid values are HTTP1Only, HTTP2Only, HTTP2Optional, HTTP2Preferred, and None.
Actions & Routing
What action types can I configure for my listener?
You can configure six action types: forward (route to target groups), redirect (redirect requests), fixed-response (return static content), authenticate-cognito (Cognito authentication), authenticate-oidc (OIDC authentication), and jwt-validation (JWT token validation).
How do I implement blue/green deployments with weighted routing?
Use a forward action with a forward block containing multiple targetGroups. Each target group has an arn and weight property (e.g., blue with weight 100, green with weight 0 for initial deployment).
How do I redirect HTTP traffic to HTTPS?
Configure a redirect action with port set to "443", protocol set to "HTTPS", and statusCode set to "HTTP_301" for permanent redirects.
Can I return a static response without a backend target group?
Yes, use a fixed-response action with contentType (e.g., "text/plain"), messageBody, and statusCode properties to return static content directly from the load balancer.
Authentication & Security
What authentication methods can I add to my listener?
You can add three authentication methods: authenticate-cognito (AWS Cognito user pools), authenticate-oidc (OpenID Connect providers), and jwt-validation (JWT token validation). Chain authentication actions with a forward action to protect your backend.
How do I enable mutual TLS authentication?
Configure the mutualAuthentication block with mode set to "verify" and provide a trustStoreArn pointing to your trust store containing client CA certificates.
Advanced Configuration
Can I customize HTTP request and response headers?
Yes, but only for Application Load Balancers with HTTP or HTTPS protocols. You can modify mTLS client certificate headers, TLS cipher suite headers, and CORS headers using the routingHttpRequest* and routingHttpResponse* properties. These aren’t supported for Network or Gateway Load Balancers.
What's the TCP idle timeout and when can I configure it?
The default TCP idle timeout is 350 seconds. You can configure tcpIdleTimeoutSeconds (range: 60-6000) only when the protocol is TCP on Network Load Balancers or with Gateway Load Balancers. It’s not supported for Application Load Balancers.
What properties can't be changed after creating a listener?
The loadBalancerArn property is immutable. Changing the load balancer requires recreating the listener.

Using a different cloud?

Explore networking guides for other cloud providers: