The `zappa undeploy` functionality for ALB infrastructure.

def undeploy_lambda_alb(self, lambda_name):
        """
        The `zappa undeploy` functionality for ALB infrastructure.
        """
        print("Undeploying ALB infrastructure...")

        # Locate and delete alb/lambda permissions
        try:
            # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lambda.html#Lambda.Client.remove_permission
            self.lambda_client.remove_permission(
                FunctionName=lambda_name,
                StatementId=lambda_name
            )
        except botocore.exceptions.ClientError as e: # pragma: no cover
            if "ResourceNotFoundException" in e.response["Error"]["Code"]:
                pass
            else:
                raise e

        # Locate and delete load balancer
        try:
            # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2.html#ElasticLoadBalancingv2.Client.describe_load_balancers
            response = self.elbv2_client.describe_load_balancers(
                Names=[lambda_name]
            )
            if not(response["LoadBalancers"]) or len(response["LoadBalancers"]) > 1:
                raise EnvironmentError("Failure to locate/delete ALB named [{}]. Response was: {}".format(lambda_name, repr(response)))
            load_balancer_arn = response["LoadBalancers"][0]["LoadBalancerArn"]
            # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2.html#ElasticLoadBalancingv2.Client.describe_listeners
            response = self.elbv2_client.describe_listeners(LoadBalancerArn=load_balancer_arn)
            if not(response["Listeners"]):
                print('No listeners found.')
            elif len(response["Listeners"]) > 1:
                raise EnvironmentError("Failure to locate/delete listener for ALB named [{}]. Response was: {}".format(lambda_name, repr(response)))
            else:
                listener_arn = response["Listeners"][0]["ListenerArn"]
                # Remove the listener. This explicit deletion of the listener seems necessary to avoid ResourceInUseExceptions when deleting target groups.
                # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2.html#ElasticLoadBalancingv2.Client.delete_listener
                response = self.elbv2_client.delete_listener(ListenerArn=listener_arn)
            # Remove the load balancer and wait for completion
            # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2.html#ElasticLoadBalancingv2.Client.delete_load_balancer
            response = self.elbv2_client.delete_load_balancer(LoadBalancerArn=load_balancer_arn)
            waiter = self.elbv2_client.get_waiter('load_balancers_deleted')
            print('Waiting for load balancer [{}] to be deleted..'.format(lambda_name))
            waiter.wait(LoadBalancerArns=[load_balancer_arn], WaiterConfig={"Delay": 3})
        except botocore.exceptions.ClientError as e: # pragma: no cover
            print(e.response["Error"]["Code"])
            if "LoadBalancerNotFound" in e.response["Error"]["Code"]:
                pass
            else:
                raise e

        # Locate and delete target group
        try:
            # Locate the lambda ARN
            # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lambda.html#Lambda.Client.get_function
            response = self.lambda_client.get_function(FunctionName=lambda_name)
            lambda_arn = response["Configuration"]["FunctionArn"]
            # Locate the target group ARN
            # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2.html#ElasticLoadBalancingv2.Client.describe_target_groups
            response = self.elbv2_client.describe_target_groups(Names=[lambda_name])
            if not(response["TargetGroups"]) or len(response["TargetGroups"]) > 1:
                raise EnvironmentError("Failure to locate/delete ALB target group named [{}]. Response was: {}".format(lambda_name, repr(response)))
            target_group_arn = response["TargetGroups"][0]["TargetGroupArn"]
            # Deregister targets and wait for completion
            self.elbv2_client.deregister_targets(
                TargetGroupArn=target_group_arn,
                Targets=[{"Id": lambda_arn}]
            )
            waiter = self.elbv2_client.get_waiter('target_deregistered')
            print('Waiting for target [{}] to be deregistered...'.format(lambda_name))
            waiter.wait(
                TargetGroupArn=target_group_arn,
                Targets=[{"Id": lambda_arn}],
                WaiterConfig={"Delay": 3}
            )
            # Remove the target group
            # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2.html#ElasticLoadBalancingv2.Client.delete_target_group
            self.elbv2_client.delete_target_group(TargetGroupArn=target_group_arn)
        except botocore.exceptions.ClientError as e: # pragma: no cover
            print(e.response["Error"]["Code"])
            if "TargetGroupNotFound" in e.response["Error"]["Code"]:
                pass
            else:
                raise e