Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/c7n/resources/cloudfront.py: 42%
342 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
1# Copyright The Cloud Custodian Authors.
2# SPDX-License-Identifier: Apache-2.0
3import re
5from c7n.actions import BaseAction
6from c7n.filters import MetricsFilter, ShieldMetrics, Filter
7from c7n.manager import resources
8from c7n.query import ConfigSource, QueryResourceManager, DescribeSource, TypeInfo
9from c7n.tags import universal_augment
10from c7n.utils import local_session, merge_dict, type_schema, get_retry
11from c7n.filters import ValueFilter, WafV2FilterBase
12from .aws import shape_validate
13from c7n.exceptions import PolicyValidationError
15from c7n.resources.aws import Arn
16from c7n.resources.shield import IsShieldProtected, SetShieldProtection
17from c7n.resources.securityhub import PostFinding
20class DescribeDistribution(DescribeSource):
22 def augment(self, resources):
23 return universal_augment(self.manager, resources)
25 def get_resources(self, ids, cache=True):
26 results = []
27 distribution_ids = []
28 for i in ids:
29 # if we get cloudfront distribution arn, we pick distribution id
30 if i.startswith('arn:'):
31 distribution_ids.append(Arn.parse(i).resource)
32 else:
33 distribution_ids.append(i)
34 if distribution_ids:
35 results = super().get_resources(distribution_ids, cache)
36 return results
39@resources.register('distribution')
40class Distribution(QueryResourceManager):
42 class resource_type(TypeInfo):
43 service = 'cloudfront'
44 arn_type = 'distribution'
45 enum_spec = ('list_distributions', 'DistributionList.Items', None)
46 id = 'Id'
47 arn = 'ARN'
48 name = 'DomainName'
49 date = 'LastModifiedTime'
50 dimension = "DistributionId"
51 universal_taggable = True
52 cfn_type = config_type = "AWS::CloudFront::Distribution"
53 # Denotes this resource type exists across regions
54 global_resource = True
56 source_mapping = {
57 'describe': DescribeDistribution,
58 'config': ConfigSource
59 }
62class DescribeStreamingDistribution(DescribeSource):
64 def augment(self, resources):
65 return universal_augment(self.manager, resources)
68@resources.register('streaming-distribution')
69class StreamingDistribution(QueryResourceManager):
71 class resource_type(TypeInfo):
72 service = 'cloudfront'
73 arn_type = 'streaming-distribution'
74 enum_spec = ('list_streaming_distributions',
75 'StreamingDistributionList.Items',
76 None)
77 id = 'Id'
78 arn = 'ARN'
79 name = 'DomainName'
80 date = 'LastModifiedTime'
81 dimension = "DistributionId"
82 universal_taggable = True
83 cfn_type = config_type = "AWS::CloudFront::StreamingDistribution"
85 source_mapping = {
86 'describe': DescribeStreamingDistribution,
87 'config': ConfigSource
88 }
91Distribution.filter_registry.register('shield-metrics', ShieldMetrics)
92Distribution.filter_registry.register('shield-enabled', IsShieldProtected)
93Distribution.action_registry.register('set-shield', SetShieldProtection)
96@Distribution.filter_registry.register('metrics')
97@StreamingDistribution.filter_registry.register('metrics')
98class DistributionMetrics(MetricsFilter):
99 """Filter cloudfront distributions based on metric values
101 :example:
103 .. code-block:: yaml
105 policies:
106 - name: cloudfront-distribution-errors
107 resource: distribution
108 filters:
109 - type: metrics
110 name: Requests
111 value: 3
112 op: ge
113 """
115 def get_dimensions(self, resource):
116 return [{'Name': self.model.dimension,
117 'Value': resource[self.model.id]},
118 {'Name': 'Region', 'Value': 'Global'}]
121@Distribution.filter_registry.register('waf-enabled')
122class IsWafEnabled(Filter):
123 """Filter CloudFront distribution by waf-regional web-acl
125 :example:
127 .. code-block:: yaml
129 policies:
130 - name: filter-distribution-waf
131 resource: distribution
132 filters:
133 - type: waf-enabled
134 state: false
135 web-acl: test
136 """
137 schema = type_schema(
138 'waf-enabled', **{
139 'web-acl': {'type': 'string'},
140 'state': {'type': 'boolean'}})
142 permissions = ('waf:ListWebACLs',)
144 def process(self, resources, event=None):
145 target_acl = self.data.get('web-acl')
146 wafs = self.manager.get_resource_manager('waf').resources()
147 waf_name_id_map = {w['Name']: w['WebACLId'] for w in wafs}
148 target_acl = self.data.get('web-acl')
149 target_acl_id = waf_name_id_map.get(target_acl, target_acl)
151 if target_acl_id and target_acl_id not in waf_name_id_map.values():
152 raise ValueError("invalid web acl: %s" % (target_acl_id))
154 state = self.data.get('state', False)
155 results = []
156 for r in resources:
157 if state and target_acl_id is None and r.get('WebACLId'):
158 results.append(r)
159 elif not state and target_acl_id is None and (not r.get('WebACLId') or
160 r.get('WebACLId') not in waf_name_id_map.values()):
161 results.append(r)
162 elif state and target_acl_id and r['WebACLId'] == target_acl_id:
163 results.append(r)
164 elif not state and target_acl_id and r['WebACLId'] != target_acl_id:
165 results.append(r)
166 return results
169@Distribution.filter_registry.register('wafv2-enabled')
170class IsWafV2Enabled(WafV2FilterBase):
171 """Filter CloudFront distribution by wafv2 web-acl
173 :example:
175 .. code-block:: yaml
177 policies:
178 - name: filter-distribution-wafv2
179 description: |
180 match resources that are NOT associated with any wafV2 web-acls
181 resource: distribution
182 filters:
183 - type: wafv2-enabled
184 state: false
186 - name: filter-distribution-wafv2-specific-acl
187 description: |
188 match resources that are NOT associated with wafV2's testv2 web-acl
189 resource: distribution
190 filters:
191 - type: wafv2-enabled
192 state: false
193 web-acl: testv2
195 - name: filter-distribution-wafv2-regex
196 description: |
197 match resources that are NOT associated with specified
198 wafV2 web-acl regex
199 resource: distribution
200 filters:
201 - type: wafv2-enabled
202 state: false
203 web-acl: .*FMManagedWebACLV2-?FMS-.*
204 """
206 def get_associated_web_acl(self, resource):
207 # for WAFv2 Cloudfront stores the ARN of the WebACL even though the attribute is 'WebACLId'
208 return self.get_web_acl_by_arn(resource.get('WebACLId'), scope='CLOUDFRONT')
211class BaseDistributionConfig(ValueFilter):
212 schema = type_schema('distribution-config', rinherit=ValueFilter.schema)
213 schema_alias = False
214 annotation_key = 'c7n:distribution-config'
215 annotate = False
217 def process(self, resources, event=None):
219 self.augment([r for r in resources if self.annotation_key not in r])
220 return super().process(resources, event)
222 def __call__(self, r):
223 return super(BaseDistributionConfig, self).__call__(r[self.annotation_key])
226@Distribution.filter_registry.register('distribution-config')
227class DistributionConfig(BaseDistributionConfig):
228 """Check for Cloudfront distribution config values
230 :example:
232 .. code-block:: yaml
234 policies:
235 - name: logging-enabled
236 resource: distribution
237 filters:
238 - type: distribution-config
239 key: Logging.Enabled
240 value: False
241 """
242 permissions = ('cloudfront:GetDistributionConfig',)
244 def augment(self, resources):
245 client = local_session(self.manager.session_factory).client(
246 'cloudfront', region_name=self.manager.config.region)
248 for r in resources:
249 try:
250 r[self.annotation_key] = client.get_distribution_config(Id=r['Id']) \
251 .get('DistributionConfig')
252 except (client.exceptions.NoSuchDistribution):
253 r[self.annotation_key] = {}
254 except Exception as e:
255 self.log.warning(
256 "Exception trying to get Distribution Config: %s error: %s",
257 r['ARN'], e)
258 raise e
261@StreamingDistribution.filter_registry.register('distribution-config')
262class StreamingDistributionConfig(BaseDistributionConfig):
263 """Check for Cloudfront streaming distribution config values
265 :example:
267 .. code-block:: yaml
269 policies:
270 - name: streaming-distribution-logging-enabled
271 resource: streaming-distribution
272 filters:
273 - type: distribution-config
274 key: Logging.Enabled
275 value: true
276 """
277 permissions = ('cloudfront:GetStreamingDistributionConfig',)
279 def augment(self, resources):
281 client = local_session(self.manager.session_factory).client(
282 'cloudfront', region_name=self.manager.config.region)
284 for r in resources:
285 try:
286 r[self.annotation_key] = client.get_streaming_distribution_config(Id=r['Id']) \
287 .get('StreamingDistributionConfig')
288 except (client.exceptions.NoSuchStreamingDistribution):
289 r[self.annotation_key] = {}
290 except Exception as e:
291 self.log.warning(
292 "Exception trying to get Streaming Distribution Config: %s error: %s",
293 r['ARN'], e)
294 raise e
297@Distribution.filter_registry.register('mismatch-s3-origin')
298class MismatchS3Origin(Filter):
299 """Check for existence of S3 bucket referenced by Cloudfront,
300 and verify whether owner is different from Cloudfront account owner.
302 :example:
304 .. code-block:: yaml
306 policies:
307 - name: mismatch-s3-origin
308 resource: distribution
309 filters:
310 - type: mismatch-s3-origin
311 check_custom_origins: true
312 """
314 s3_prefix = re.compile(r'.*(?=\.s3(-.*)?(\..*-\d)?\.amazonaws.com)')
315 s3_suffix = re.compile(r'^([^.]+\.)?s3(-.*)?(\..*-\d)?\.amazonaws.com')
317 schema = type_schema(
318 'mismatch-s3-origin',
319 check_custom_origins={'type': 'boolean'})
321 permissions = ('s3:ListAllMyBuckets',)
322 retry = staticmethod(get_retry(('Throttling',)))
324 def is_s3_domain(self, x):
325 bucket_match = self.s3_prefix.match(x['DomainName'])
327 if bucket_match:
328 return bucket_match.group()
330 domain_match = self.s3_suffix.match(x['DomainName'])
332 if domain_match:
333 value = x['OriginPath']
335 if value.startswith('/'):
336 value = value.replace("/", "", 1)
338 return value
340 return None
342 def process(self, resources, event=None):
343 results = []
345 s3_client = local_session(self.manager.session_factory).client(
346 's3', region_name=self.manager.config.region)
348 buckets = {b['Name'] for b in s3_client.list_buckets()['Buckets']}
350 for r in resources:
351 r['c7n:mismatched-s3-origin'] = []
352 for x in r['Origins']['Items']:
353 if 'S3OriginConfig' in x:
354 bucket_match = self.s3_prefix.match(x['DomainName'])
355 if bucket_match:
356 target_bucket = self.s3_prefix.match(x['DomainName']).group()
357 elif 'CustomOriginConfig' in x and self.data.get('check_custom_origins'):
358 target_bucket = self.is_s3_domain(x)
360 if target_bucket is not None and target_bucket not in buckets:
361 self.log.debug("Bucket %s not found in distribution %s hosting account."
362 % (target_bucket, r['Id']))
363 r['c7n:mismatched-s3-origin'].append(target_bucket)
364 results.append(r)
366 return results
369@Distribution.action_registry.register('post-finding')
370class DistributionPostFinding(PostFinding):
372 resource_type = 'AwsCloudFrontDistribution'
374 def format_resource(self, r):
375 envelope, payload = self.format_envelope(r)
376 origins = r['Origins']
378 payload.update(self.filter_empty({
379 'DomainName': r['DomainName'],
380 "WebAclId": r.get('WebACLId'),
381 'LastModifiedTime': r['LastModifiedTime'].isoformat(),
382 'Status': r['Status'],
383 'Logging': self.filter_empty(r.get('Logging', {})),
384 'Origins': {
385 'Items': [
386 {
387 # Extract a subset of origin item keys,
388 # only if they're non-empty.
389 #
390 # The full item can be large and noisy, and
391 # empty string values (notably for OriginPath)
392 # will fail validation.
393 k: o[k]
394 for k in self.filter_empty(o)
395 if k in ('Id', 'OriginPath', 'DomainName')
396 }
397 for o in origins['Items']
398 ]
399 }
400 }))
402 return envelope
405@Distribution.action_registry.register('set-waf')
406class SetWaf(BaseAction):
407 """Enable waf protection on CloudFront distribution.
409 :example:
411 .. code-block:: yaml
413 policies:
414 - name: set-waf-for-cloudfront
415 resource: distribution
416 filters:
417 - type: waf-enabled
418 state: false
419 web-acl: test
420 actions:
421 - type: set-waf
422 state: true
423 force: true
424 web-acl: test
426 - name: disassociate-waf-associate-wafv2-cf
427 resource: distribution
428 filters:
429 - type: waf-enabled
430 state: true
431 actions:
432 - type: set-wafv2
433 state: true
434 force: true
435 web-acl: testv2
437 """
438 permissions = ('cloudfront:UpdateDistribution', 'waf:ListWebACLs')
439 schema = type_schema(
440 'set-waf', required=['web-acl'], **{
441 'web-acl': {'type': 'string'},
442 'force': {'type': 'boolean'},
443 'state': {'type': 'boolean'}})
445 retry = staticmethod(get_retry(('Throttling',)))
447 def process(self, resources):
448 wafs = self.manager.get_resource_manager('waf').resources()
449 waf_name_id_map = {w['Name']: w['WebACLId'] for w in wafs}
450 target_acl = self.data.get('web-acl')
451 target_acl_id = waf_name_id_map.get(target_acl, target_acl)
453 if target_acl_id not in waf_name_id_map.values():
454 raise ValueError("invalid web acl: %s" % (target_acl_id))
456 client = local_session(self.manager.session_factory).client(
457 'cloudfront')
458 force = self.data.get('force', False)
460 for r in resources:
461 if r.get('WebACLId') and not force:
462 continue
463 if r.get('WebACLId') == target_acl_id:
464 continue
465 result = client.get_distribution_config(Id=r['Id'])
466 config = result['DistributionConfig']
467 config['WebACLId'] = target_acl_id
468 self.retry(
469 client.update_distribution,
470 Id=r['Id'], DistributionConfig=config, IfMatch=result['ETag'])
473@Distribution.action_registry.register('set-wafv2')
474class SetWafv2(BaseAction):
475 """Enable wafv2 protection on CloudFront distribution.
477 :example:
479 .. code-block:: yaml
481 policies:
482 - name: set-wafv2-for-cloudfront
483 resource: distribution
484 filters:
485 - type: wafv2-enabled
486 state: false
487 web-acl: testv2
488 actions:
489 - type: set-wafv2
490 state: true
491 force: true
492 web-acl: testv2
494 - name: disassociate-wafv2-associate-waf-cf
495 resource: distribution
496 filters:
497 - type: wafv2-enabled
498 state: true
499 actions:
500 - type: set-waf
501 state: true
502 force: true
503 web-acl: test
505 policies:
506 - name: set-wafv2-for-cloudfront-regex
507 resource: distribution
508 filters:
509 - type: wafv2-enabled
510 state: false
511 web-acl: .*FMManagedWebACLV2-?FMS-.*
512 actions:
513 - type: set-wafv2
514 state: true
515 web-acl: FMManagedWebACLV2-?FMS-TestWebACL
516 """
517 permissions = ('cloudfront:UpdateDistribution', 'wafv2:ListWebACLs')
518 schema = type_schema(
519 'set-wafv2', **{
520 'web-acl': {'type': 'string'},
521 'force': {'type': 'boolean'},
522 'state': {'type': 'boolean'}})
524 retry = staticmethod(get_retry(('Throttling',)))
526 def process(self, resources):
527 query = {'Scope': 'CLOUDFRONT'}
528 wafs = self.manager.get_resource_manager('wafv2').resources(query, augment=False)
529 waf_name_id_map = {w['Name']: w['ARN'] for w in wafs}
530 state = self.data.get('state', True)
532 target_acl_id = ''
533 if state:
534 target_acl = self.data.get('web-acl', '')
535 target_acl_ids = [v for k, v in waf_name_id_map.items() if
536 re.match(target_acl, k)]
537 if len(target_acl_ids) != 1:
538 raise ValueError(f'{target_acl} matching to none or '
539 f'multiple webacls')
540 target_acl_id = target_acl_ids[0]
542 client = local_session(self.manager.session_factory).client('cloudfront')
543 force = self.data.get('force', False)
545 for r in resources:
546 if r.get('WebACLId') and not force:
547 continue
548 if r.get('WebACLId') == target_acl_id:
549 continue
550 result = client.get_distribution_config(Id=r['Id'])
551 config = result['DistributionConfig']
552 config['WebACLId'] = target_acl_id
553 self.retry(
554 client.update_distribution,
555 Id=r['Id'], DistributionConfig=config, IfMatch=result['ETag'])
558@Distribution.action_registry.register('disable')
559class DistributionDisableAction(BaseAction):
560 """Action to disable a Distribution
562 :example:
564 .. code-block:: yaml
566 policies:
567 - name: distribution-delete
568 resource: distribution
569 filters:
570 - type: value
571 key: CacheBehaviors.Items[].ViewerProtocolPolicy
572 value: allow-all
573 op: contains
574 actions:
575 - type: disable
576 """
577 schema = type_schema('disable')
578 permissions = ("cloudfront:GetDistributionConfig",
579 "cloudfront:UpdateDistribution",)
581 def process(self, distributions):
582 client = local_session(
583 self.manager.session_factory).client(self.manager.get_model().service)
585 for d in distributions:
586 self.process_distribution(client, d)
588 def process_distribution(self, client, distribution):
589 try:
590 res = client.get_distribution_config(
591 Id=distribution[self.manager.get_model().id])
592 res['DistributionConfig']['Enabled'] = False
593 res = client.update_distribution(
594 Id=distribution[self.manager.get_model().id],
595 IfMatch=res['ETag'],
596 DistributionConfig=res['DistributionConfig']
597 )
598 except Exception as e:
599 self.log.warning(
600 "Exception trying to disable Distribution: %s error: %s",
601 distribution['ARN'], e)
602 return
605@StreamingDistribution.action_registry.register('disable')
606class StreamingDistributionDisableAction(BaseAction):
607 """Action to disable a Streaming Distribution
609 :example:
611 .. code-block:: yaml
613 policies:
614 - name: streaming-distribution-delete
615 resource: streaming-distribution
616 filters:
617 - type: value
618 key: S3Origin.OriginAccessIdentity
619 value: ''
620 actions:
621 - type: disable
622 """
623 schema = type_schema('disable')
625 permissions = ("cloudfront:GetStreamingDistributionConfig",
626 "cloudfront:UpdateStreamingDistribution",)
628 def process(self, distributions):
629 client = local_session(
630 self.manager.session_factory).client(self.manager.get_model().service)
631 for d in distributions:
632 self.process_distribution(client, d)
634 def process_distribution(self, client, distribution):
635 try:
636 res = client.get_streaming_distribution_config(
637 Id=distribution[self.manager.get_model().id])
638 res['StreamingDistributionConfig']['Enabled'] = False
639 res = client.update_streaming_distribution(
640 Id=distribution[self.manager.get_model().id],
641 IfMatch=res['ETag'],
642 StreamingDistributionConfig=res['StreamingDistributionConfig']
643 )
644 except Exception as e:
645 self.log.warning(
646 "Exception trying to disable Distribution: %s error: %s",
647 distribution['ARN'], e)
648 return
651@Distribution.action_registry.register('set-protocols')
652class DistributionSSLAction(BaseAction):
653 """Action to set mandatory https-only on a Distribution
655 :example:
657 .. code-block:: yaml
659 policies:
660 - name: distribution-set-ssl
661 resource: distribution
662 filters:
663 - type: value
664 key: CacheBehaviors.Items[].ViewerProtocolPolicy
665 value: allow-all
666 op: contains
667 actions:
668 - type: set-protocols
669 ViewerProtocolPolicy: https-only
670 """
671 schema = {
672 'type': 'object',
673 'additionalProperties': False,
674 'properties': {
675 'type': {'enum': ['set-protocols']},
676 'OriginProtocolPolicy': {
677 'enum': ['http-only', 'match-viewer', 'https-only']
678 },
679 'OriginSslProtocols': {
680 'type': 'array',
681 'items': {'enum': ['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2']}
682 },
683 'ViewerProtocolPolicy': {
684 'enum': ['allow-all', 'https-only', 'redirect-to-https']
685 }
686 }
687 }
689 permissions = ("cloudfront:GetDistributionConfig",
690 "cloudfront:UpdateDistribution",)
692 def process(self, distributions):
693 client = local_session(self.manager.session_factory).client(
694 self.manager.get_model().service)
695 for d in distributions:
696 self.process_distribution(client, d)
698 def process_distribution(self, client, distribution):
699 try:
700 res = client.get_distribution_config(
701 Id=distribution[self.manager.get_model().id])
702 etag = res['ETag']
703 dc = res['DistributionConfig']
705 for item in dc['CacheBehaviors'].get('Items', []):
706 item['ViewerProtocolPolicy'] = self.data.get(
707 'ViewerProtocolPolicy',
708 item['ViewerProtocolPolicy'])
709 dc['DefaultCacheBehavior']['ViewerProtocolPolicy'] = self.data.get(
710 'ViewerProtocolPolicy',
711 dc['DefaultCacheBehavior']['ViewerProtocolPolicy'])
713 for item in dc['Origins'].get('Items', []):
714 if item.get('CustomOriginConfig', False):
715 item['CustomOriginConfig']['OriginProtocolPolicy'] = self.data.get(
716 'OriginProtocolPolicy',
717 item['CustomOriginConfig']['OriginProtocolPolicy'])
719 item['CustomOriginConfig']['OriginSslProtocols']['Items'] = self.data.get(
720 'OriginSslProtocols',
721 item['CustomOriginConfig']['OriginSslProtocols']['Items'])
723 item['CustomOriginConfig']['OriginSslProtocols']['Quantity'] = len(
724 item['CustomOriginConfig']['OriginSslProtocols']['Items'])
726 res = client.update_distribution(
727 Id=distribution[self.manager.get_model().id],
728 IfMatch=etag,
729 DistributionConfig=dc
730 )
731 except Exception as e:
732 self.log.warning(
733 "Exception trying to force ssl on Distribution: %s error: %s",
734 distribution['ARN'], e)
735 return
738class BaseUpdateAction(BaseAction):
739 schema = type_schema('set-attributes',
740 attributes={"type": "object"},
741 required=('attributes',))
742 schema_alias = False
744 def validate(self, config_name, shape):
745 attrs = dict(self.data.get('attributes'))
746 if attrs.get('CallerReference'):
747 raise PolicyValidationError('CallerReference field cannot be updated')
749 # Set default values for required fields if they are not present
750 attrs["CallerReference"] = ""
751 config = self.validation_config
752 updatedConfig = {**config, **attrs}
754 request = {
755 config_name: updatedConfig,
756 "Id": "sample_id",
757 "IfMatch": "sample_string",
758 }
759 return shape_validate(request, shape, 'cloudfront')
761 def process(self, distributions):
762 client = local_session(self.manager.session_factory).client(
763 self.manager.get_model().service)
764 for d in distributions:
765 self.process_distribution(client, d)
768@Distribution.action_registry.register('set-attributes')
769class DistributionUpdateAction(BaseUpdateAction):
770 """Action to update the attributes of a distribution
772 :example:
774 .. code-block:: yaml
776 policies:
777 - name: enforce-distribution-logging
778 resource: distribution
779 filters:
780 - type: value
781 key: "Logging.Enabled"
782 value: null
783 actions:
784 - type: set-attributes
785 attributes:
786 Comment: ""
787 Enabled: true
788 Logging:
789 Enabled: true
790 IncludeCookies: false
791 Bucket: 'test-enable-logging-c7n.s3.amazonaws.com'
792 Prefix: ''
793 """
794 permissions = ("cloudfront:UpdateDistribution",
795 "cloudfront:GetDistributionConfig",)
796 shape = 'UpdateDistributionRequest'
797 validation_config = {
798 'Origins': {
799 'Quantity': 0,
800 'Items': [{
801 'Id': '',
802 'DomainName': ''
803 }]
804 },
805 'DefaultCacheBehavior': {
806 'TargetOriginId': '',
807 'ForwardedValues': {
808 'QueryString': True,
809 'Cookies': {
810 'Forward': ''
811 }
812 },
813 'TrustedSigners': {
814 'Enabled': True,
815 'Quantity': 0
816 },
817 'ViewerProtocolPolicy': '',
818 'MinTTL': 0
819 },
820 'Comment': '',
821 'Enabled': False
822 }
824 def validate(self):
825 return super().validate('DistributionConfig', self.shape)
827 def process_distribution(self, client, distribution):
828 try:
829 res = client.get_distribution_config(
830 Id=distribution[self.manager.get_model().id])
831 default_config = self.validation_config
832 config = {**default_config, **res['DistributionConfig']}
834 # Recursively merge config to allow piecemeal updates of
835 # nested structures
836 updatedConfig = merge_dict(config, self.data['attributes'])
837 if config == updatedConfig:
838 return
839 res = client.update_distribution(
840 Id=distribution[self.manager.get_model().id],
841 IfMatch=res['ETag'],
842 DistributionConfig=updatedConfig
843 )
844 except (client.exceptions.NoSuchDistribution):
845 pass
846 except Exception as e:
847 self.log.warning(
848 "Exception trying to update Distribution: %s error: %s",
849 distribution['ARN'], e)
850 raise e
853StreamingDistribution.filter_registry.register('shield-enabled', IsShieldProtected)
854StreamingDistribution.action_registry.register('set-shield', SetShieldProtection)
857@StreamingDistribution.action_registry.register('set-attributes')
858class StreamingDistributionUpdateAction(BaseUpdateAction):
859 """Action to update the attributes of a distribution
861 :example:
863 .. code-block:: yaml
865 policies:
866 - name: enforce-streaming-distribution-logging
867 resource: streaming-distribution
868 filters:
869 - type: value
870 key: "Logging.Enabled"
871 value: false
872 actions:
873 - type: set-attributes
874 attributes:
875 Logging:
876 Enabled: true
877 Bucket: 'test-enable-logging-c7n.s3.amazonaws.com'
878 Prefix: ''
879 """
880 permissions = ("cloudfront:UpdateStreamingDistribution",
881 "cloudfront:GetStreamingDistributionConfig",)
882 shape = 'UpdateStreamingDistributionRequest'
883 validation_config = {
884 'S3Origin': {
885 'DomainName': 'domain_name',
886 'OriginAccessIdentity': 'origin_access_identity'
887 },
888 'TrustedSigners': {
889 'Enabled': False,
890 'Quantity': 0
891 },
892 'Comment': '',
893 'Enabled': False
894 }
896 def validate(self):
897 return super().validate('StreamingDistributionConfig', self.shape)
899 def process_distribution(self, client, streaming_distribution):
900 try:
901 res = client.get_streaming_distribution_config(
902 Id=streaming_distribution[self.manager.get_model().id])
903 default_config = self.validation_config
904 config = {**default_config, **res['StreamingDistributionConfig']}
905 updatedConfig = {**config, **self.data['attributes']}
906 if config == updatedConfig:
907 return
908 res = client.update_streaming_distribution(
909 Id=streaming_distribution[self.manager.get_model().id],
910 IfMatch=res['ETag'],
911 StreamingDistributionConfig=updatedConfig
912 )
913 except (client.exceptions.NoSuchStreamingDistribution):
914 pass
915 except Exception as e:
916 self.log.warning(
917 "Exception trying to update Streaming Distribution: %s error: %s",
918 streaming_distribution['ARN'], e)
919 raise e