Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/c7n/resources/appelb.py: 46%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright The Cloud Custodian Authors.
2# SPDX-License-Identifier: Apache-2.0
3"""
4Application & Network Load Balancers
5"""
6import json
7import logging
8import re
10from botocore.exceptions import ClientError
11from collections import defaultdict
12from c7n.actions import ActionRegistry, BaseAction, ModifyVpcSecurityGroupsAction
13from c7n.exceptions import PolicyValidationError
14from c7n.filters import (
15 Filter,
16 FilterRegistry,
17 ListItemFilter,
18 MetricsFilter,
19 ValueFilter,
20 WafV2FilterBase,
21 WafClassicRegionalFilterBase
22)
23import c7n.filters.vpc as net_filters
24from c7n import tags
25from c7n.manager import resources
27from c7n.query import QueryResourceManager, DescribeSource, ConfigSource, TypeInfo
28from c7n.utils import (
29 local_session, chunks, type_schema, get_retry, set_annotation)
31from c7n.resources.aws import Arn
32from c7n.resources.shield import IsShieldProtected, SetShieldProtection
34log = logging.getLogger('custodian.app-elb')
37class DescribeAppElb(DescribeSource):
39 def get_resources(self, ids, cache=True):
40 """Support server side filtering on arns or names
41 """
42 if ids[0].startswith('arn:'):
43 params = {'LoadBalancerArns': ids}
44 else:
45 params = {'Names': ids}
46 return self.query.filter(self.manager, **params)
48 def augment(self, albs):
49 _describe_appelb_tags(
50 albs,
51 self.manager.session_factory,
52 self.manager.executor_factory,
53 self.manager.retry)
55 return albs
58class ConfigAppElb(ConfigSource):
60 def load_resource(self, item):
61 resource = super(ConfigAppElb, self).load_resource(item)
62 item_attrs = item['supplementaryConfiguration'][
63 'LoadBalancerAttributes']
64 if isinstance(item_attrs, str):
65 item_attrs = json.loads(item_attrs)
66 # Matches annotation of AppELBAttributeFilterBase filter
67 resource['Attributes'] = {
68 attr['key']: parse_attribute_value(attr['value']) for
69 attr in item_attrs}
70 return resource
73@resources.register('app-elb')
74class AppELB(QueryResourceManager):
75 """Resource manager for v2 ELBs (AKA ALBs and NLBs).
76 """
78 class resource_type(TypeInfo):
79 service = 'elbv2'
80 permission_prefix = 'elasticloadbalancing'
81 enum_spec = ('describe_load_balancers', 'LoadBalancers', None)
82 name = 'LoadBalancerName'
83 id = 'LoadBalancerArn'
84 filter_name = "Names"
85 filter_type = "list"
86 dimension = "LoadBalancer"
87 date = 'CreatedTime'
88 cfn_type = config_type = 'AWS::ElasticLoadBalancingV2::LoadBalancer'
89 arn = "LoadBalancerArn"
90 # The suffix varies by type of loadbalancer (app vs net)
91 arn_type = 'loadbalancer/app'
92 permissions_augment = ("elasticloadbalancing:DescribeTags",)
94 retry = staticmethod(get_retry(('Throttling',)))
95 source_mapping = {
96 'describe': DescribeAppElb,
97 'config': ConfigAppElb
98 }
100 @classmethod
101 def get_permissions(cls):
102 # override as the service is not the iam prefix
103 return ("elasticloadbalancing:DescribeLoadBalancers",
104 "elasticloadbalancing:DescribeLoadBalancerAttributes",
105 "elasticloadbalancing:DescribeTags")
108def _describe_appelb_tags(albs, session_factory, executor_factory, retry):
109 client = local_session(session_factory).client('elbv2')
111 def _process_tags(alb_set):
112 alb_map = {alb['LoadBalancerArn']: alb for alb in alb_set}
114 results = retry(client.describe_tags, ResourceArns=list(alb_map.keys()))
115 for tag_desc in results['TagDescriptions']:
116 if ('ResourceArn' in tag_desc and
117 tag_desc['ResourceArn'] in alb_map):
118 alb_map[tag_desc['ResourceArn']]['Tags'] = tag_desc['Tags']
120 with executor_factory(max_workers=2) as w:
121 list(w.map(_process_tags, chunks(albs, 20)))
124AppELB.filter_registry.register('tag-count', tags.TagCountFilter)
125AppELB.filter_registry.register('marked-for-op', tags.TagActionFilter)
126AppELB.filter_registry.register('shield-enabled', IsShieldProtected)
127AppELB.filter_registry.register('network-location', net_filters.NetworkLocation)
128AppELB.action_registry.register('set-shield', SetShieldProtection)
131@AppELB.filter_registry.register('metrics')
132class AppElbMetrics(MetricsFilter):
133 """Filter app/net load balancer by metric values.
135 Note application and network load balancers use different Cloud
136 Watch metrics namespaces and metric names, the custodian app-elb
137 resource returns both types of load balancer, so an additional
138 filter should be used to ensure only targeting a particular
139 type. ie. `- Type: application` or `- Type: network`
141 See available application load balancer metrics here
142 https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-cloudwatch-metrics.html
144 See available network load balancer metrics here.
145 https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-cloudwatch-metrics.html
148 For network load balancer metrics, the metrics filter requires specifying
149 the namespace parameter to the filter.
151 .. code-block:: yaml
153 policies:
154 - name: net-lb-underutilized
155 resource: app-elb
156 filters:
157 - Type: network
158 - type: metrics
159 name: ActiveFlowCount
160 namespace: AWS/NetworkELB
161 statistics: Sum
162 days: 14
163 value: 100
164 op: less-than
165 """
167 def get_dimensions(self, resource):
168 return [{
169 'Name': self.model.dimension,
170 'Value': Arn.parse(resource['LoadBalancerArn']).resource}]
173@AppELB.filter_registry.register('security-group')
174class SecurityGroupFilter(net_filters.SecurityGroupFilter):
176 RelatedIdsExpression = "SecurityGroups[]"
179@AppELB.filter_registry.register('subnet')
180class SubnetFilter(net_filters.SubnetFilter):
182 RelatedIdsExpression = "AvailabilityZones[].SubnetId"
185@AppELB.filter_registry.register('vpc')
186class VpcFilter(net_filters.VpcFilter):
188 RelatedIdsExpression = "VpcId"
191@AppELB.filter_registry.register('waf-enabled')
192class WafEnabled(WafClassicRegionalFilterBase):
193 """Filter Application LoadBalancer by waf-regional web-acl
195 :example:
197 .. code-block:: yaml
199 policies:
200 - name: filter-elb-waf-regional
201 resource: app-elb
202 filters:
203 - type: waf-enabled
204 state: false
205 web-acl: test
206 """
208 # application load balancers don't hold a reference to the associated web acl
209 # so we have to look them up via the associations on the web acl directly
210 def get_associated_web_acl(self, resource):
211 return self.get_web_acl_from_associations(
212 'APPLICATION_LOAD_BALANCER',
213 resource['LoadBalancerArn']
214 )
217@AppELB.filter_registry.register('wafv2-enabled')
218class WafV2Enabled(WafV2FilterBase):
219 """Filter Application LoadBalancer by wafv2 web-acl
221 Supports regex expression for web-acl.
222 Firewall Manager pushed WebACL's name varies by account and region.
223 Regex expression can support both local and Firewall Managed WebACL.
225 :example:
227 .. code-block:: yaml
229 policies:
230 - name: filter-wafv2-elb
231 resource: app-elb
232 filters:
233 - type: wafv2-enabled
234 state: false
235 web-acl: testv2
237 - name: filter-wafv2-elb-regex
238 resource: app-elb
239 filters:
240 - type: wafv2-enabled
241 state: false
242 web-acl: .*FMManagedWebACLV2-?FMS-.*
243 """
245 # application load balancers don't hold a reference to the associated web acl
246 # so we have to look them up via the associations on the web acl directly
247 def get_associated_web_acl(self, resource):
248 return self.get_web_acl_from_associations(
249 'APPLICATION_LOAD_BALANCER',
250 resource['LoadBalancerArn']
251 )
254@AppELB.action_registry.register('set-waf')
255class SetWaf(BaseAction):
256 """Enable wafv2 protection on Application LoadBalancer.
258 :example:
260 .. code-block:: yaml
262 policies:
263 - name: set-waf-for-elb
264 resource: app-elb
265 filters:
266 - type: waf-enabled
267 state: false
268 web-acl: test
269 actions:
270 - type: set-waf
271 state: true
272 web-acl: test
274 - name: disassociate-wafv2-associate-waf-regional-elb
275 resource: app-elb
276 filters:
277 - type: wafv2-enabled
278 state: true
279 actions:
280 - type: set-waf
281 state: true
282 web-acl: test
284 """
285 permissions = ('waf-regional:AssociateWebACL', 'waf-regional:ListWebACLs')
287 schema = type_schema(
288 'set-waf', required=['web-acl'], **{
289 'web-acl': {'type': 'string'},
290 # 'force': {'type': 'boolean'},
291 'state': {'type': 'boolean'}})
293 def validate(self):
294 found = False
295 for f in self.manager.iter_filters():
296 if isinstance(f, WafEnabled) or isinstance(f, WafV2Enabled):
297 found = True
298 break
299 if not found:
300 # try to ensure idempotent usage
301 raise PolicyValidationError(
302 "set-waf should be used in conjunction with waf-enabled or wafv2-enabled \
303 filter on %s" % (self.manager.data,))
304 return self
306 def process(self, resources):
307 wafs = self.manager.get_resource_manager('waf-regional').resources(augment=False)
308 name_id_map = {w['Name']: w['WebACLId'] for w in wafs}
309 target_acl = self.data.get('web-acl')
310 target_acl_id = name_id_map.get(target_acl, target_acl)
311 state = self.data.get('state', True)
313 if state and target_acl_id not in name_id_map.values():
314 raise ValueError("invalid web acl: %s" % (target_acl_id))
316 client = local_session(
317 self.manager.session_factory).client('waf-regional')
319 arn_key = self.manager.resource_type.id
321 # TODO implement force to reassociate.
322 # TODO investigate limits on waf association.
323 for r in resources:
324 if state:
325 client.associate_web_acl(
326 WebACLId=target_acl_id, ResourceArn=r[arn_key])
327 else:
328 client.disassociate_web_acl(
329 WebACLId=target_acl_id, ResourceArn=r[arn_key])
332@AppELB.action_registry.register('set-wafv2')
333class SetWafV2(BaseAction):
334 """Enable wafv2 protection on Application LoadBalancer.
336 Supports regex expression for web-acl
338 :example:
340 .. code-block:: yaml
342 policies:
343 - name: set-wafv2-for-elb
344 resource: app-elb
345 filters:
346 - type: wafv2-enabled
347 state: false
348 web-acl: testv2
349 actions:
350 - type: set-wafv2
351 state: true
352 web-acl: testv2
354 - name: disassociate-waf-regional-associate-wafv2-elb
355 resource: app-elb
356 filters:
357 - type: waf-enabled
358 state: true
359 actions:
360 - type: set-wafv2
361 state: true
363 policies:
364 - name: set-wafv2-for-elb-regex
365 resource: app-elb
366 filters:
367 - type: wafv2-enabled
368 state: false
369 web-acl: .*FMManagedWebACLV2-?FMS-.*
370 actions:
371 - type: set-wafv2
372 state: true
373 web-acl: FMManagedWebACLV2-?FMS-TestWebACL
375 """
376 permissions = ('wafv2:AssociateWebACL',
377 'wafv2:DisassociateWebACL',
378 'wafv2:ListWebACLs')
380 schema = type_schema(
381 'set-wafv2', **{
382 'web-acl': {'type': 'string'},
383 'state': {'type': 'boolean'}})
385 retry = staticmethod(get_retry((
386 'ThrottlingException',
387 'RequestLimitExceeded',
388 'Throttled',
389 'ThrottledException',
390 'Throttling',
391 'Client.RequestLimitExceeded')))
393 def validate(self):
394 found = False
395 for f in self.manager.iter_filters():
396 if isinstance(f, WafV2Enabled) or isinstance(f, WafEnabled):
397 found = True
398 break
399 if not found:
400 # try to ensure idempotent usage
401 raise PolicyValidationError(
402 "set-wafv2 should be used in conjunction with wafv2-enabled or waf-enabled \
403 filter on %s" % (self.manager.data,))
404 return self
406 def process(self, resources):
407 wafs = self.manager.get_resource_manager('wafv2').resources(augment=False)
408 name_id_map = {w['Name']: w['ARN'] for w in wafs}
409 state = self.data.get('state', True)
411 target_acl_id = ''
412 if state:
413 target_acl = self.data.get('web-acl', '')
414 target_acl_ids = [v for k, v in name_id_map.items() if
415 re.match(target_acl, k)]
416 if len(target_acl_ids) != 1:
417 raise ValueError(f'{target_acl} matching to none or '
418 f'multiple webacls')
419 target_acl_id = target_acl_ids[0]
421 client = local_session(
422 self.manager.session_factory).client('wafv2')
424 arn_key = self.manager.resource_type.id
426 # TODO implement force to reassociate.
427 # TODO investigate limits on waf association.
428 for r in resources:
429 if state:
430 self.retry(client.associate_web_acl,
431 WebACLArn=target_acl_id,
432 ResourceArn=r[arn_key])
433 else:
434 self.retry(client.disassociate_web_acl,
435 ResourceArn=r[arn_key])
438@AppELB.action_registry.register('set-s3-logging')
439class SetS3Logging(BaseAction):
440 """Action to enable/disable S3 logging for an application loadbalancer.
442 :example:
444 .. code-block:: yaml
446 policies:
447 - name: elbv2-test
448 resource: app-elb
449 filters:
450 - type: is-not-logging
451 actions:
452 - type: set-s3-logging
453 bucket: elbv2logtest
454 prefix: dahlogs
455 state: enabled
456 """
457 schema = type_schema(
458 'set-s3-logging',
459 state={'enum': ['enabled', 'disabled']},
460 bucket={'type': 'string'},
461 prefix={'type': 'string'},
462 required=('state',))
464 permissions = ("elasticloadbalancing:ModifyLoadBalancerAttributes",)
466 def validate(self):
467 if self.data.get('state') == 'enabled':
468 if 'bucket' not in self.data or 'prefix' not in self.data:
469 raise PolicyValidationError((
470 "alb logging enablement requires `bucket` "
471 "and `prefix` specification on %s" % (self.manager.data,)))
472 return self
474 def process(self, resources):
475 client = local_session(self.manager.session_factory).client('elbv2')
476 for elb in resources:
477 elb_arn = elb['LoadBalancerArn']
478 attributes = [{
479 'Key': 'access_logs.s3.enabled',
480 'Value': (
481 self.data.get('state') == 'enabled' and 'true' or 'value')}]
483 if self.data.get('state') == 'enabled':
484 attributes.append({
485 'Key': 'access_logs.s3.bucket',
486 'Value': self.data['bucket']})
488 prefix_template = self.data['prefix']
489 info = {t['Key']: t['Value'] for t in elb.get('Tags', ())}
490 info['DNSName'] = elb.get('DNSName', '')
491 info['AccountId'] = elb['LoadBalancerArn'].split(':')[4]
492 info['LoadBalancerName'] = elb['LoadBalancerName']
494 attributes.append({
495 'Key': 'access_logs.s3.prefix',
496 'Value': prefix_template.format(**info)})
498 self.manager.retry(
499 client.modify_load_balancer_attributes,
500 LoadBalancerArn=elb_arn, Attributes=attributes)
503@AppELB.action_registry.register('mark-for-op')
504class AppELBMarkForOpAction(tags.TagDelayedAction):
505 """Action to create a delayed action on an ELB to start at a later date
507 :example:
509 .. code-block:: yaml
511 policies:
512 - name: appelb-failed-mark-for-op
513 resource: app-elb
514 filters:
515 - "tag:custodian_elb_cleanup": absent
516 - State: failed
517 actions:
518 - type: mark-for-op
519 tag: custodian_elb_cleanup
520 msg: "AppElb failed: {op}@{action_date}"
521 op: delete
522 days: 1
523 """
525 batch_size = 1
528@AppELB.action_registry.register('tag')
529class AppELBTagAction(tags.Tag):
530 """Action to create tag/tags on an ELB
532 :example:
534 .. code-block:: yaml
536 policies:
537 - name: appelb-create-required-tag
538 resource: app-elb
539 filters:
540 - "tag:RequiredTag": absent
541 actions:
542 - type: tag
543 key: RequiredTag
544 value: RequiredValue
545 """
547 batch_size = 1
548 permissions = ("elasticloadbalancing:AddTags",)
550 def process_resource_set(self, client, resource_set, ts):
551 client.add_tags(
552 ResourceArns=[alb['LoadBalancerArn'] for alb in resource_set],
553 Tags=ts)
556@AppELB.action_registry.register('remove-tag')
557class AppELBRemoveTagAction(tags.RemoveTag):
558 """Action to remove tag/tags from an ELB
560 :example:
562 .. code-block:: yaml
564 policies:
565 - name: appelb-delete-expired-tag
566 resource: app-elb
567 filters:
568 - "tag:ExpiredTag": present
569 actions:
570 - type: remove-tag
571 tags: ["ExpiredTag"]
572 """
574 batch_size = 1
575 permissions = ("elasticloadbalancing:RemoveTags",)
577 def process_resource_set(self, client, resource_set, tag_keys):
578 client.remove_tags(
579 ResourceArns=[alb['LoadBalancerArn'] for alb in resource_set],
580 TagKeys=tag_keys)
583@AppELB.action_registry.register('delete')
584class AppELBDeleteAction(BaseAction):
585 """Action to delete an ELB
587 To avoid unwanted deletions of ELB, it is recommended to apply a filter
588 to the rule
590 :example:
592 .. code-block:: yaml
594 policies:
595 - name: appelb-delete-failed-elb
596 resource: app-elb
597 filters:
598 - State: failed
599 actions:
600 - delete
601 """
603 schema = type_schema('delete', force={'type': 'boolean'})
604 permissions = (
605 "elasticloadbalancing:DeleteLoadBalancer",
606 "elasticloadbalancing:ModifyLoadBalancerAttributes",)
608 def process(self, load_balancers):
609 client = local_session(self.manager.session_factory).client('elbv2')
610 for lb in load_balancers:
611 self.process_alb(client, lb)
613 def process_alb(self, client, alb):
614 try:
615 if self.data.get('force'):
616 client.modify_load_balancer_attributes(
617 LoadBalancerArn=alb['LoadBalancerArn'],
618 Attributes=[{
619 'Key': 'deletion_protection.enabled',
620 'Value': 'false',
621 }])
622 self.manager.retry(
623 client.delete_load_balancer, LoadBalancerArn=alb['LoadBalancerArn'])
624 except client.exceptions.LoadBalancerNotFoundException:
625 pass
626 except (
627 client.exceptions.OperationNotPermittedException,
628 client.exceptions.ResourceInUseException
629 ) as e:
630 self.log.warning(
631 "Exception trying to delete load balancer: %s error: %s",
632 alb['LoadBalancerArn'], e)
635@AppELB.action_registry.register('modify-attributes')
636class AppELBModifyAttributes(BaseAction):
637 """Modify load balancer attributes.
639 :example:
641 .. code-block:: yaml
643 policies:
644 - name: turn-on-elb-deletion-protection
645 resource: app-elb
646 filters:
647 - type: attributes
648 key: "deletion_protection.enabled"
649 value: false
650 actions:
651 - type: modify-attributes
652 attributes:
653 "deletion_protection.enabled": "true"
654 "idle_timeout.timeout_seconds": 120
655 """
656 schema = {
657 'type': 'object',
658 'additionalProperties': False,
659 'properties': {
660 'type': {
661 'enum': ['modify-attributes']},
662 'attributes': {
663 'type': 'object',
664 'additionalProperties': False,
665 'properties': {
666 'access_logs.s3.enabled': {
667 'enum': ['true', 'false', True, False]},
668 'access_logs.s3.bucket': {'type': 'string'},
669 'access_logs.s3.prefix': {'type': 'string'},
670 'deletion_protection.enabled': {
671 'enum': ['true', 'false', True, False]},
672 'idle_timeout.timeout_seconds': {'type': 'number'},
673 'routing.http.desync_mitigation_mode': {
674 'enum': ['monitor', 'defensive', 'strictest']},
675 'routing.http.drop_invalid_header_fields.enabled': {
676 'enum': ['true', 'false', True, False]},
677 'routing.http2.enabled': {
678 'enum': ['true', 'false', True, False]},
679 'load_balancing.cross_zone.enabled': {
680 'enum': ['true', 'false', True, False]},
681 },
682 },
683 },
684 }
685 permissions = ("elasticloadbalancing:ModifyLoadBalancerAttributes",)
687 def process(self, resources):
688 client = local_session(self.manager.session_factory).client('elbv2')
689 for appelb in resources:
690 self.manager.retry(
691 client.modify_load_balancer_attributes,
692 LoadBalancerArn=appelb['LoadBalancerArn'],
693 Attributes=[
694 {'Key': key, 'Value': serialize_attribute_value(value)}
695 for (key, value) in self.data['attributes'].items()
696 ],
697 ignore_err_codes=('LoadBalancerNotFoundException',),
698 )
699 return resources
702class AppELBListenerFilterBase:
703 """ Mixin base class for filters that query LB listeners.
704 """
705 permissions = ("elasticloadbalancing:DescribeListeners",)
707 def initialize(self, albs):
708 client = local_session(self.manager.session_factory).client('elbv2')
709 self.listener_map = defaultdict(list)
710 for alb in albs:
711 results = self.manager.retry(client.describe_listeners,
712 LoadBalancerArn=alb['LoadBalancerArn'],
713 ignore_err_codes=('LoadBalancerNotFoundException',))
714 self.listener_map[alb['LoadBalancerArn']] = results['Listeners']
717def parse_attribute_value(v):
718 if v.isdigit():
719 v = int(v)
720 elif v == 'true':
721 v = True
722 elif v == 'false':
723 v = False
724 return v
727def serialize_attribute_value(v):
728 if v is True:
729 return 'true'
730 elif v is False:
731 return 'false'
732 elif isinstance(v, int):
733 return str(v)
734 return v
737class AppELBAttributeFilterBase:
738 """ Mixin base class for filters that query LB attributes.
739 """
741 def initialize(self, albs):
742 client = local_session(self.manager.session_factory).client('elbv2')
744 def _process_attributes(alb):
745 if 'Attributes' not in alb:
746 alb['Attributes'] = {}
747 results = client.describe_load_balancer_attributes(
748 LoadBalancerArn=alb['LoadBalancerArn'])
749 # flatten out the list of dicts and cast
750 for pair in results['Attributes']:
751 k = pair['Key']
752 v = parse_attribute_value(pair['Value'])
753 alb['Attributes'][k] = v
755 with self.manager.executor_factory(max_workers=2) as w:
756 list(w.map(_process_attributes, albs))
759@AppELB.filter_registry.register('is-logging')
760class IsLoggingFilter(Filter, AppELBAttributeFilterBase):
761 """ Matches AppELBs that are logging to S3.
762 bucket and prefix are optional
764 :example:
766 .. code-block:: yaml
768 policies:
769 - name: alb-is-logging-test
770 resource: app-elb
771 filters:
772 - type: is-logging
774 - name: alb-is-logging-bucket-and-prefix-test
775 resource: app-elb
776 filters:
777 - type: is-logging
778 bucket: prodlogs
779 prefix: alblogs
781 """
782 permissions = ("elasticloadbalancing:DescribeLoadBalancerAttributes",)
783 schema = type_schema('is-logging',
784 bucket={'type': 'string'},
785 prefix={'type': 'string'}
786 )
788 def process(self, resources, event=None):
789 self.initialize(resources)
790 bucket_name = self.data.get('bucket', None)
791 bucket_prefix = self.data.get('prefix', None)
793 return [alb for alb in resources
794 if alb['Attributes']['access_logs.s3.enabled'] and
795 (not bucket_name or bucket_name == alb['Attributes'].get(
796 'access_logs.s3.bucket', None)) and
797 (not bucket_prefix or bucket_prefix == alb['Attributes'].get(
798 'access_logs.s3.prefix', None))
799 ]
802@AppELB.filter_registry.register('is-not-logging')
803class IsNotLoggingFilter(Filter, AppELBAttributeFilterBase):
804 """ Matches AppELBs that are NOT logging to S3.
805 or do not match the optional bucket and/or prefix.
807 :example:
809 .. code-block:: yaml
811 policies:
812 - name: alb-is-not-logging-test
813 resource: app-elb
814 filters:
815 - type: is-not-logging
817 - name: alb-is-not-logging-bucket-and-prefix-test
818 resource: app-elb
819 filters:
820 - type: is-not-logging
821 bucket: prodlogs
822 prefix: alblogs
824 """
825 permissions = ("elasticloadbalancing:DescribeLoadBalancerAttributes",)
826 schema = type_schema('is-not-logging',
827 bucket={'type': 'string'},
828 prefix={'type': 'string'}
829 )
831 def process(self, resources, event=None):
832 self.initialize(resources)
833 bucket_name = self.data.get('bucket', None)
834 bucket_prefix = self.data.get('prefix', None)
836 return [alb for alb in resources
837 if not alb['Attributes']['access_logs.s3.enabled'] or
838 (bucket_name and bucket_name != alb['Attributes'].get(
839 'access_logs.s3.bucket', None)) or
840 (bucket_prefix and bucket_prefix != alb['Attributes'].get(
841 'access_logs.s3.prefix', None))]
844@AppELB.filter_registry.register('attributes')
845class CheckAttributes(ValueFilter, AppELBAttributeFilterBase):
846 """ Value filter that allows filtering on ELBv2 attributes
848 :example:
850 .. code-block:: yaml
852 policies:
853 - name: alb-http2-enabled
854 resource: app-elb
855 filters:
856 - type: attributes
857 key: routing.http2.enabled
858 value: true
859 op: eq
860 """
861 annotate: False # no annotation from value Filter
862 permissions = ("elasticloadbalancing:DescribeLoadBalancerAttributes",)
863 schema = type_schema('attributes', rinherit=ValueFilter.schema)
864 schema_alias = False
866 def process(self, resources, event=None):
867 self.augment(resources)
868 return super().process(resources, event)
870 def augment(self, resources):
871 self.initialize(resources)
873 def __call__(self, r):
874 return super().__call__(r['Attributes'])
877class AppELBTargetGroupFilterBase:
878 """ Mixin base class for filters that query LB target groups.
879 """
881 def initialize(self, albs):
882 self.target_group_map = defaultdict(list)
883 target_groups = self.manager.get_resource_manager(
884 'app-elb-target-group').resources()
885 for target_group in target_groups:
886 for load_balancer_arn in target_group['LoadBalancerArns']:
887 self.target_group_map[load_balancer_arn].append(target_group)
890@AppELB.filter_registry.register('listener')
891class AppELBListenerFilter(ValueFilter, AppELBListenerFilterBase):
892 """Filter ALB based on matching listener attributes
894 Adding the `matched` flag will filter on previously matched listeners
896 :example:
898 .. code-block:: yaml
900 policies:
901 - name: app-elb-invalid-ciphers
902 resource: app-elb
903 filters:
904 - type: listener
905 key: Protocol
906 value: HTTPS
907 - type: listener
908 key: SslPolicy
909 value: ['ELBSecurityPolicy-TLS-1-1-2017-01','ELBSecurityPolicy-TLS-1-2-2017-01']
910 op: ni
911 matched: true
912 actions:
913 - type: modify-listener
914 sslpolicy: "ELBSecurityPolicy-TLS-1-2-2017-01"
915 """
917 schema = type_schema(
918 'listener', rinherit=ValueFilter.schema, matched={'type': 'boolean'})
919 schema_alias = False
920 permissions = ("elasticloadbalancing:DescribeLoadBalancerAttributes",)
922 def validate(self):
923 if not self.data.get('matched'):
924 return
925 listeners = list(self.manager.iter_filters())
926 found = False
927 for f in listeners[:listeners.index(self)]:
928 if not f.data.get('matched', False):
929 found = True
930 break
931 if not found:
932 raise PolicyValidationError(
933 "matched listener filter, requires preceding listener filter on %s " % (
934 self.manager.data,))
935 return self
937 def process(self, albs, event=None):
938 self.initialize(albs)
939 return super(AppELBListenerFilter, self).process(albs, event)
941 def __call__(self, alb):
942 listeners = self.listener_map[alb['LoadBalancerArn']]
943 if self.data.get('matched', False):
944 listeners = alb.pop('c7n:MatchedListeners', [])
946 found_listeners = False
947 for listener in listeners:
948 if self.match(listener):
949 set_annotation(alb, 'c7n:MatchedListeners', listener)
950 found_listeners = True
951 return found_listeners
954@AppELB.filter_registry.register('listener-rule')
955class AppELBListenerRuleFilter(ListItemFilter):
956 """Filter ALB based on listener rules (path-based, host-based routing, etc.)
958 This filter allows checking multiple attributes on listener rules,
959 enabling policies to inspect non-default routing configurations
960 for security and compliance.
962 :example:
964 Find ALBs with rules redirecting to HTTP (insecure):
966 .. code-block:: yaml
968 policies:
969 - name: alb-insecure-rule-redirects
970 resource: app-elb
971 filters:
972 - type: listener-rule
973 attrs:
974 - type: value
975 key: Actions[0].Type
976 value: redirect
977 - type: value
978 key: Actions[0].RedirectConfig.Protocol
979 value: HTTP
981 Find ALBs with rules forwarding to specific target groups:
983 .. code-block:: yaml
985 policies:
986 - name: alb-rules-to-old-target-group
987 resource: app-elb
988 filters:
989 - type: listener-rule
990 attrs:
991 - type: value
992 key: Actions[0].TargetGroupArn
993 value: "arn:aws:elasticloadbalancing:*:*:targetgroup/old-*"
994 op: glob
996 Count ALBs with more than 5 custom rules:
998 .. code-block:: yaml
1000 policies:
1001 - name: alb-many-rules
1002 resource: app-elb
1003 filters:
1004 - type: listener-rule
1005 count: 5
1006 count_op: gt
1007 """
1008 schema = type_schema(
1009 'listener-rule',
1010 attrs={'$ref': '#/definitions/filters_common/list_item_attrs'},
1011 count={'type': 'number'},
1012 count_op={'$ref': '#/definitions/filters_common/comparison_operators'}
1013 )
1014 permissions = (
1015 'elasticloadbalancing:DescribeListeners',
1016 'elasticloadbalancing:DescribeRules',
1017 )
1018 annotate_items = True
1019 item_annotation_key = 'c7n:ListenerRules'
1021 def process(self, resources, event=None):
1022 client = local_session(self.manager.session_factory).client('elbv2')
1023 rule_map = defaultdict(list)
1025 # Fetch all listeners and their rules for the ALBs
1026 for alb in resources:
1027 try:
1028 listeners_result = self.manager.retry(
1029 client.describe_listeners,
1030 LoadBalancerArn=alb['LoadBalancerArn'],
1031 ignore_err_codes=('LoadBalancerNotFoundException',)
1032 )
1033 listeners = listeners_result.get('Listeners', [])
1035 # Fetch rules for each listener
1036 for listener in listeners:
1037 try:
1038 rules_result = self.manager.retry(
1039 client.describe_rules,
1040 ListenerArn=listener['ListenerArn'],
1041 ignore_err_codes=('ListenerNotFoundException',)
1042 )
1043 rules = rules_result.get('Rules', [])
1044 # Filter out default rules (IsDefault=True)
1045 # as they're already covered by the listener filter
1046 non_default_rules = [
1047 r for r in rules if not r.get('IsDefault', False)
1048 ]
1049 rule_map[alb['LoadBalancerArn']].extend(non_default_rules)
1050 except ClientError as e:
1051 log.warning(
1052 "Failed to fetch rules for listener %s: %s",
1053 listener.get('ListenerArn'), e
1054 )
1055 except ClientError as e:
1056 log.warning(
1057 "Failed to fetch listeners for ALB %s: %s",
1058 alb.get('LoadBalancerArn'), e
1059 )
1061 # Store rules in the ALB resources
1062 for alb in resources:
1063 alb[self.item_annotation_key] = rule_map.get(
1064 alb['LoadBalancerArn'], []
1065 )
1067 return super().process(resources, event)
1069 def get_item_values(self, resource):
1070 return resource.get(self.item_annotation_key, [])
1073@AppELB.action_registry.register('modify-listener')
1074class AppELBModifyListenerPolicy(BaseAction):
1075 """Action to modify the policy for an App ELB
1077 :example:
1079 .. code-block:: yaml
1081 policies:
1082 - name: appelb-modify-listener
1083 resource: app-elb
1084 filters:
1085 - type: listener
1086 key: Protocol
1087 value: HTTP
1088 actions:
1089 - type: modify-listener
1090 protocol: HTTPS
1091 sslpolicy: "ELBSecurityPolicy-TLS-1-2-2017-01"
1092 certificate: "arn:aws:acm:region:123456789012:certificate/12345678-\
1093 1234-1234-1234-123456789012"
1094 """
1096 schema = type_schema(
1097 'modify-listener',
1098 port={'type': 'integer'},
1099 protocol={'enum': ['HTTP', 'HTTPS', 'TCP', 'TLS', 'UDP', 'TCP_UDP', 'GENEVE']},
1100 sslpolicy={'type': 'string'},
1101 certificate={'type': 'string'}
1102 )
1104 permissions = ("elasticloadbalancing:ModifyListener",)
1106 def validate(self):
1107 for f in self.manager.iter_filters():
1108 if f.type == 'listener':
1109 return self
1110 raise PolicyValidationError(
1111 "modify-listener action requires the listener filter %s" % (
1112 self.manager.data,))
1114 def process(self, load_balancers):
1115 args = {}
1116 if 'port' in self.data:
1117 args['Port'] = self.data.get('port')
1118 if 'protocol' in self.data:
1119 args['Protocol'] = self.data.get('protocol')
1120 if 'sslpolicy' in self.data:
1121 args['SslPolicy'] = self.data.get('sslpolicy')
1122 if 'certificate' in self.data:
1123 args['Certificates'] = [{'CertificateArn': self.data.get('certificate')}]
1124 client = local_session(self.manager.session_factory).client('elbv2')
1126 for alb in load_balancers:
1127 for matched_listener in alb.get('c7n:MatchedListeners', ()):
1128 client.modify_listener(
1129 ListenerArn=matched_listener['ListenerArn'],
1130 **args)
1133@AppELB.action_registry.register('modify-security-groups')
1134class AppELBModifyVpcSecurityGroups(ModifyVpcSecurityGroupsAction):
1136 permissions = ("elasticloadbalancing:SetSecurityGroups",)
1138 def process(self, albs):
1139 client = local_session(self.manager.session_factory).client('elbv2')
1140 groups = super(AppELBModifyVpcSecurityGroups, self).get_groups(albs)
1142 for idx, i in enumerate(albs):
1143 try:
1144 client.set_security_groups(
1145 LoadBalancerArn=i['LoadBalancerArn'],
1146 SecurityGroups=groups[idx])
1147 except client.exceptions.LoadBalancerNotFoundException:
1148 continue
1151@AppELB.filter_registry.register('healthcheck-protocol-mismatch')
1152class AppELBHealthCheckProtocolMismatchFilter(Filter,
1153 AppELBTargetGroupFilterBase):
1154 """Filter AppELBs with mismatched health check protocols
1156 A mismatched health check protocol is where the protocol on the target group
1157 does not match the load balancer health check protocol
1159 :example:
1161 .. code-block:: yaml
1163 policies:
1164 - name: appelb-healthcheck-mismatch
1165 resource: app-elb
1166 filters:
1167 - healthcheck-protocol-mismatch
1168 """
1170 schema = type_schema('healthcheck-protocol-mismatch')
1171 permissions = ("elasticloadbalancing:DescribeTargetGroups",)
1173 def process(self, albs, event=None):
1174 def _healthcheck_protocol_mismatch(alb):
1175 for target_group in self.target_group_map[alb['LoadBalancerArn']]:
1176 if (target_group['Protocol'] !=
1177 target_group['HealthCheckProtocol']):
1178 return True
1180 return False
1182 self.initialize(albs)
1183 return [alb for alb in albs if _healthcheck_protocol_mismatch(alb)]
1186@AppELB.filter_registry.register('target-group')
1187class AppELBTargetGroupFilter(ValueFilter, AppELBTargetGroupFilterBase):
1188 """Filter ALB based on matching target group value"""
1190 schema = type_schema('target-group', rinherit=ValueFilter.schema)
1191 schema_alias = False
1192 permissions = ("elasticloadbalancing:DescribeTargetGroups",)
1194 def process(self, albs, event=None):
1195 self.initialize(albs)
1196 return super(AppELBTargetGroupFilter, self).process(albs, event)
1198 def __call__(self, alb):
1199 target_groups = self.target_group_map[alb['LoadBalancerArn']]
1200 return self.match(target_groups)
1203@AppELB.filter_registry.register('default-vpc')
1204class AppELBDefaultVpcFilter(net_filters.DefaultVpcBase):
1205 """Filter all ELB that exist within the default vpc
1207 :example:
1209 .. code-block:: yaml
1211 policies:
1212 - name: appelb-in-default-vpc
1213 resource: app-elb
1214 filters:
1215 - default-vpc
1216 """
1218 schema = type_schema('default-vpc')
1220 def __call__(self, alb):
1221 return alb.get('VpcId') and self.match(alb.get('VpcId')) or False
1224class DescribeAppELBTargetGroup(DescribeSource):
1226 def augment(self, target_groups):
1227 client = local_session(self.manager.session_factory).client('elbv2')
1229 def _describe_target_group_health(target_group):
1230 result = self.manager.retry(client.describe_target_health,
1231 TargetGroupArn=target_group['TargetGroupArn'])
1232 target_group['TargetHealthDescriptions'] = result[
1233 'TargetHealthDescriptions']
1235 with self.manager.executor_factory(max_workers=2) as w:
1236 list(w.map(_describe_target_group_health, target_groups))
1238 _describe_target_group_tags(
1239 target_groups, self.manager.session_factory,
1240 self.manager.executor_factory, self.manager.retry)
1241 return target_groups
1244@resources.register('app-elb-target-group')
1245class AppELBTargetGroup(QueryResourceManager):
1246 """Resource manager for v2 ELB target groups.
1247 """
1249 class resource_type(TypeInfo):
1250 service = 'elbv2'
1251 arn_type = 'target-group'
1252 enum_spec = ('describe_target_groups', 'TargetGroups', None)
1253 name = 'TargetGroupName'
1254 id = 'TargetGroupArn'
1255 permission_prefix = 'elasticloadbalancing'
1256 cfn_type = config_type = 'AWS::ElasticLoadBalancingV2::TargetGroup'
1258 source_mapping = {
1259 'describe': DescribeAppELBTargetGroup,
1260 'config': ConfigSource,
1261 }
1263 filter_registry = FilterRegistry('app-elb-target-group.filters')
1264 action_registry = ActionRegistry('app-elb-target-group.actions')
1265 retry = staticmethod(get_retry(('Throttling',)))
1267 filter_registry.register('tag-count', tags.TagCountFilter)
1268 filter_registry.register('marked-for-op', tags.TagActionFilter)
1270 @classmethod
1271 def get_permissions(cls):
1272 # override as the service is not the iam prefix
1273 return ("elasticloadbalancing:DescribeTargetGroups",
1274 "elasticloadbalancing:DescribeTags")
1277def _describe_target_group_tags(target_groups, session_factory,
1278 executor_factory, retry):
1279 client = local_session(session_factory).client('elbv2')
1281 def _process_tags(target_group_set):
1282 target_group_map = {
1283 target_group['TargetGroupArn']:
1284 target_group for target_group in target_group_set
1285 }
1287 results = retry(
1288 client.describe_tags,
1289 ResourceArns=list(target_group_map.keys()))
1290 for tag_desc in results['TagDescriptions']:
1291 if ('ResourceArn' in tag_desc and
1292 tag_desc['ResourceArn'] in target_group_map):
1293 target_group_map[
1294 tag_desc['ResourceArn']
1295 ]['Tags'] = tag_desc['Tags']
1297 with executor_factory(max_workers=2) as w:
1298 list(w.map(_process_tags, chunks(target_groups, 20)))
1301@AppELBTargetGroup.action_registry.register('mark-for-op')
1302class AppELBTargetGroupMarkForOpAction(tags.TagDelayedAction):
1303 """Action to specify a delayed action on an ELB target group"""
1306@AppELBTargetGroup.action_registry.register('tag')
1307class AppELBTargetGroupTagAction(tags.Tag):
1308 """Action to create tag/tags on an ELB target group
1310 :example:
1312 .. code-block:: yaml
1314 policies:
1315 - name: appelb-targetgroup-add-required-tag
1316 resource: app-elb-target-group
1317 filters:
1318 - "tag:RequiredTag": absent
1319 actions:
1320 - type: tag
1321 key: RequiredTag
1322 value: RequiredValue
1323 """
1325 batch_size = 1
1326 permissions = ("elasticloadbalancing:AddTags",)
1328 def process_resource_set(self, client, resource_set, ts):
1329 client.add_tags(
1330 ResourceArns=[tgroup['TargetGroupArn'] for tgroup in resource_set],
1331 Tags=ts)
1334@AppELBTargetGroup.action_registry.register('remove-tag')
1335class AppELBTargetGroupRemoveTagAction(tags.RemoveTag):
1336 """Action to remove tag/tags from ELB target group
1338 :example:
1340 .. code-block:: yaml
1342 policies:
1343 - name: appelb-targetgroup-remove-expired-tag
1344 resource: app-elb-target-group
1345 filters:
1346 - "tag:ExpiredTag": present
1347 actions:
1348 - type: remove-tag
1349 tags: ["ExpiredTag"]
1350 """
1352 batch_size = 1
1353 permissions = ("elasticloadbalancing:RemoveTags",)
1355 def process_resource_set(self, client, resource_set, tag_keys):
1356 client.remove_tags(
1357 ResourceArns=[tgroup['TargetGroupArn'] for tgroup in resource_set],
1358 TagKeys=tag_keys)
1361@AppELBTargetGroup.filter_registry.register('default-vpc')
1362class AppELBTargetGroupDefaultVpcFilter(net_filters.DefaultVpcBase):
1363 """Filter all application elb target groups within the default vpc
1365 :example:
1367 .. code-block:: yaml
1369 policies:
1370 - name: appelb-targetgroups-default-vpc
1371 resource: app-elb-target-group
1372 filters:
1373 - default-vpc
1374 """
1376 schema = type_schema('default-vpc')
1378 def __call__(self, target_group):
1379 return (target_group.get('VpcId') and
1380 self.match(target_group.get('VpcId')) or False)
1383@AppELBTargetGroup.action_registry.register('delete')
1384class AppELBTargetGroupDeleteAction(BaseAction):
1385 """Action to delete ELB target group
1387 It is recommended to apply a filter to the delete policy to avoid unwanted
1388 deletion of any app elb target groups.
1390 :example:
1392 .. code-block:: yaml
1394 policies:
1395 - name: appelb-targetgroups-delete-unused
1396 resource: app-elb-target-group
1397 filters:
1398 - "tag:SomeTag": absent
1399 actions:
1400 - delete
1401 """
1403 schema = type_schema('delete')
1404 permissions = ('elasticloadbalancing:DeleteTargetGroup',)
1406 def process(self, resources):
1407 client = local_session(self.manager.session_factory).client('elbv2')
1408 for tg in resources:
1409 self.process_target_group(client, tg)
1411 def process_target_group(self, client, target_group):
1412 self.manager.retry(
1413 client.delete_target_group,
1414 TargetGroupArn=target_group['TargetGroupArn'])
1417class TargetGroupAttributeFilterBase:
1418 """ Mixin base class for filters that query Target Group attributes.
1419 """
1421 def initialize(self, tgs):
1422 client = local_session(self.manager.session_factory).client('elbv2')
1424 def _process_attributes(tg):
1425 if 'c7n:TargetGroupAttributes' not in tg:
1426 tg['c7n:TargetGroupAttributes'] = {}
1427 results = self.manager.retry(client.describe_target_group_attributes,
1428 TargetGroupArn=tg['TargetGroupArn'],
1429 ignore_err_codes=('TargetGroupNotFoundException',))
1430 # flatten out the list of dicts and cast
1431 for pair in results['Attributes']:
1432 k = pair['Key']
1433 v = parse_attribute_value(pair['Value'])
1434 tg['c7n:TargetGroupAttributes'][k] = v
1436 with self.manager.executor_factory(max_workers=2) as w:
1437 list(w.map(_process_attributes, tgs))
1440@AppELBTargetGroup.filter_registry.register('attributes')
1441class TargetGroupCheckAttributes(ValueFilter, TargetGroupAttributeFilterBase):
1442 """ Value filter that allows filtering on Target group attributes
1444 :example:
1446 .. code-block:: yaml
1448 policies:
1449 - name: target-group-check-attributes
1450 resource: app-elb-target-group
1451 filters:
1452 - type: attributes
1453 key: preserve_client_ip.enabled
1454 value: True
1455 op: eq
1456 """
1457 annotate: False # no annotation from value Filter
1458 permissions = ("elasticloadbalancing:DescribeTargetGroupAttributes",)
1459 schema = type_schema('attributes', rinherit=ValueFilter.schema)
1460 schema_alias = False
1462 def process(self, resources, event=None):
1463 self.augment(resources)
1464 return super().process(resources, event)
1466 def augment(self, resources):
1467 self.initialize(resources)
1469 def __call__(self, r):
1470 return super().__call__(r['c7n:TargetGroupAttributes'])
1473@AppELBTargetGroup.action_registry.register('modify-attributes')
1474class AppELBTargetGroupModifyAttributes(BaseAction):
1475 """Modify target group attributes.
1477 :example:
1479 .. code-block:: yaml
1481 policies:
1482 - name: modify-preserve-client-ip-enable
1483 resource: app-elb-target-group
1484 filters:
1485 - type: attributes
1486 key: "preserve_client_ip.enabled"
1487 value: False
1488 actions:
1489 - type: modify-attributes
1490 attributes:
1491 "preserve_client_ip.enabled": "true"
1492 """
1493 schema = {
1494 'type': 'object',
1495 'additionalProperties': False,
1496 'properties': {
1497 'type': {
1498 'enum': ['modify-attributes']},
1499 'attributes': {
1500 'type': 'object',
1501 'additionalProperties': False,
1502 'properties': {
1503 'proxy_protocol_v2.enabled': {
1504 'enum': ['true', 'false', True, False]},
1505 'preserve_client_ip.enabled': {
1506 'enum': ['true', 'false', True, False]},
1507 'stickiness.enabled': {
1508 'enum': ['true', 'false', True, False]},
1509 'lambda.multi_value_headers.enabled': {
1510 'enum': ['true', 'false', True, False]},
1511 'deregistration_delay.connection_termination.enabled': {
1512 'enum': ['true', 'false', True, False]},
1513 'target_group_health.unhealthy_state_routing.'
1514 'minimum_healthy_targets.count': {'type': 'number'},
1515 'target_group_health.unhealthy_state_routing.'
1516 'minimum_healthy_targets.percentage': {'type': 'string'},
1517 'deregistration_delay.timeout_seconds': {'type': 'number'},
1518 'target_group_health.dns_failover.minimum_healthy_targets.count': {
1519 'type': 'string'},
1520 'stickiness.type': {
1521 'enum': ['lb_cookie', 'app_cookie', 'source_ip',
1522 'source_ip_dest_ip', 'source_ip_dest_ip_proto']},
1523 'load_balancing.cross_zone.enabled': {
1524 'enum': ['true', 'false', True, False, 'use_load_balancer_configuration']},
1525 'target_group_health.dns_failover.minimum_healthy_targets.percentage': {
1526 'type': 'string'},
1527 'stickiness.app_cookie.cookie_name': {'type': 'string'},
1528 'stickiness.lb_cookie.duration_seconds': {'type': 'number'},
1529 'slow_start.duration_seconds': {'type': 'number'},
1530 'stickiness.app_cookie.duration_seconds': {'type': 'number'},
1531 'load_balancing.algorithm.type': {
1532 'enum': ['round_robin', 'least_outstanding_requests']},
1533 'target_failover.on_deregistration': {
1534 'enum': ['rebalance', 'no_rebalance']},
1535 'target_failover.on_unhealthy': {
1536 'enum': ['rebalance', 'no_rebalance']},
1537 },
1538 },
1539 },
1540 }
1541 permissions = ("elasticloadbalancing:ModifyTargetGroupAttributes",)
1543 def process(self, resources):
1544 client = local_session(self.manager.session_factory).client('elbv2')
1545 self.log.info(resources)
1546 for targetgroup in resources:
1547 self.manager.retry(
1548 client.modify_target_group_attributes,
1549 TargetGroupArn=targetgroup['TargetGroupArn'],
1550 Attributes=[
1551 {'Key': key, 'Value': serialize_attribute_value(value)}
1552 for (key, value) in self.data['attributes'].items()
1553 ],
1554 ignore_err_codes=('TargetGroupNotFoundException',),
1555 )
1556 return resources
1559@AppELB.action_registry.register('delete-listener')
1560class AppELBDeleteListenerAction(BaseAction):
1561 """Action to delete listeners from an Application Load Balancer.
1564 :example:
1566 .. code-block:: yaml
1568 policies:
1569 - name: delete-alb-listeners
1570 resource: app-elb
1571 filters:
1572 - type: listener
1573 key: Protocol
1574 value: HTTP
1575 actions:
1576 - type: delete-listener
1577 scope: matched
1578 """
1580 def validate(self):
1581 """Validate the delete-listener action configuration.
1583 The listener filter is only required when the action is scoped to
1584 the *matched* listeners.
1585 """
1586 scope = self.data.get('scope', 'matched')
1587 if scope == 'matched':
1588 for f in self.manager.iter_filters():
1589 if f.type == 'listener':
1590 return self
1591 raise PolicyValidationError(
1592 "delete-listener action with scope 'matched' requires the listener filter %s" %
1593 (self.manager.data,))
1595 schema = type_schema(
1596 'delete-listener',
1597 scope={'enum': ['matched']})
1598 permissions = ("elasticloadbalancing:DeleteListener",)
1600 def process(self, albs):
1601 client = local_session(self.manager.session_factory).client('elbv2')
1602 for alb in albs:
1603 listeners = alb.get('c7n:MatchedListeners')
1604 for listener in listeners:
1605 try:
1606 client.delete_listener(
1607 ListenerArn=listener['ListenerArn'])
1608 except client.exceptions.ListenerNotFoundException:
1609 continue