The `zappa deploy` functionality for ALB infrastructure.

def deploy_lambda_alb(  self,
                            lambda_arn,
                            lambda_name,
                            alb_vpc_config,
                            timeout
                         ):
        """
        The `zappa deploy` functionality for ALB infrastructure.
        """
        if not alb_vpc_config:
            raise EnvironmentError('When creating an ALB, alb_vpc_config must be filled out in zappa_settings.')
        if 'SubnetIds' not in alb_vpc_config:
            raise EnvironmentError('When creating an ALB, you must supply two subnets in different availability zones.')
        if 'SecurityGroupIds' not in alb_vpc_config:
            alb_vpc_config["SecurityGroupIds"] = []
        if not alb_vpc_config.get('CertificateArn'):
            raise EnvironmentError('When creating an ALB, you must supply a CertificateArn for the HTTPS listener.')
        print("Deploying ALB infrastructure...")

        # Create load balancer
        # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2.html#ElasticLoadBalancingv2.Client.create_load_balancer
        kwargs = dict(
            Name=lambda_name,
            Subnets=alb_vpc_config["SubnetIds"],
            SecurityGroups=alb_vpc_config["SecurityGroupIds"],
            # TODO: Scheme can also be "internal" we need to add a new option for this.
            Scheme="internet-facing",
            # TODO: Tags might be a useful means of stock-keeping zappa-generated assets.
            #Tags=[],
            Type="application",
            # TODO: can be ipv4 or dualstack (for ipv4 and ipv6) ipv4 is required for internal Scheme.
            IpAddressType="ipv4"
        )
        response = self.elbv2_client.create_load_balancer(**kwargs)
        if not(response["LoadBalancers"]) or len(response["LoadBalancers"]) != 1:
            raise EnvironmentError("Failure to create application load balancer. Response was in unexpected format. Response was: {}".format(repr(response)))
        if response["LoadBalancers"][0]['State']['Code'] == 'failed':
            raise EnvironmentError("Failure to create application load balancer. Response reported a failed state: {}".format(response["LoadBalancers"][0]['State']['Reason']))
        load_balancer_arn = response["LoadBalancers"][0]["LoadBalancerArn"]
        load_balancer_dns = response["LoadBalancers"][0]["DNSName"]
        load_balancer_vpc = response["LoadBalancers"][0]["VpcId"]
        waiter = self.elbv2_client.get_waiter('load_balancer_available')

        # Match the lambda timeout on the load balancer.
        self.elbv2_client.modify_load_balancer_attributes(
            LoadBalancerArn=load_balancer_arn,
            Attributes=[{
                'Key': 'idle_timeout.timeout_seconds',
                'Value': str(timeout)
            }]
        )

        # Create/associate target group.
        # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2.html#ElasticLoadBalancingv2.Client.create_target_group
        kwargs = dict(
            Name=lambda_name,
            TargetType="lambda",
            # TODO: Add options for health checks
        )

        response = self.elbv2_client.create_target_group(**kwargs)
        if not(response["TargetGroups"]) or len(response["TargetGroups"]) != 1:
            raise EnvironmentError("Failure to create application load balancer target group. Response was in unexpected format. Response was: {}".format(repr(response)))
        target_group_arn = response["TargetGroups"][0]["TargetGroupArn"]

        # Enable multi-value headers by default.
        response = self.elbv2_client.modify_target_group_attributes(
            TargetGroupArn=target_group_arn,
            Attributes=[
                {
                    'Key': 'lambda.multi_value_headers.enabled',
                    'Value': 'true'
                },
            ]
        )

        # Allow execute permissions from target group to lambda.
        # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lambda.html#Lambda.Client.add_permission
        kwargs = dict(
            Action="lambda:InvokeFunction",
            FunctionName="{}:{}".format(lambda_arn, ALB_LAMBDA_ALIAS),
            Principal="elasticloadbalancing.amazonaws.com",
            SourceArn=target_group_arn,
            StatementId=lambda_name
        )
        response = self.lambda_client.add_permission(**kwargs)

        # Register target group to lambda association.
        # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2.html#ElasticLoadBalancingv2.Client.register_targets
        kwargs = dict(
            TargetGroupArn=target_group_arn,
            Targets=[{"Id": "{}:{}".format(lambda_arn, ALB_LAMBDA_ALIAS)}]
        )
        response = self.elbv2_client.register_targets(**kwargs)

        # Bind listener to load balancer with default rule to target group.
        # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2.html#ElasticLoadBalancingv2.Client.create_listener
        kwargs = dict(
            # TODO: Listeners support custom ssl certificates (Certificates). For now we leave this default.
            Certificates=[{"CertificateArn": alb_vpc_config['CertificateArn']}],
            DefaultActions=[{
                "Type": "forward",
                "TargetGroupArn": target_group_arn,
            }],
            LoadBalancerArn=load_balancer_arn,
            Protocol="HTTPS",
            # TODO: Add option for custom ports
            Port=443,
            # TODO: Listeners support custom ssl security policy (SslPolicy). For now we leave this default.
        )
        response = self.elbv2_client.create_listener(**kwargs)
        print("ALB created with DNS: {}".format(load_balancer_dns))
        print("Note it may take several minutes for load balancer to become available.")