Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWS - streaming_distribution set-attributes action and config filter #5694

Merged
248 changes: 178 additions & 70 deletions c7n/resources/cloudfront.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,23 @@ def process(self, resources, event=None):
return results


class BaseDistributionConfig(ValueFilter):
schema = type_schema('distribution-config', rinherit=ValueFilter.schema)
schema_alias = False
annotation_key = 'c7n:distribution-config'
annotate = False

def process(self, resources, event=None):

self.augment([r for r in resources if self.annotation_key not in r])
return super().process(resources, event)

def __call__(self, r):
return super(BaseDistributionConfig, self).__call__(r[self.annotation_key])


@Distribution.filter_registry.register('distribution-config')
class DistributionConfig(ValueFilter):
class DistributionConfig(BaseDistributionConfig):
"""Check for Cloudfront distribution config values

:example:
Expand All @@ -164,22 +179,11 @@ class DistributionConfig(ValueFilter):
filters:
- type: distribution-config
key: Logging.Enabled
value: true
value: False
"""

schema = type_schema('distribution-config', rinherit=ValueFilter.schema)
schema_alias = False
permissions = ('cloudfront:GetDistributionConfig',)
annotation_key = 'c7n:distribution-config'
annotate = False

def process(self, resources, event=None):

self.augment([r for r in resources if self.annotation_key not in r])
return super().process(resources, event)

def augment(self, resources):

client = local_session(self.manager.session_factory).client(
'cloudfront', region_name=self.manager.config.region)

Expand All @@ -195,8 +199,41 @@ def augment(self, resources):
r['ARN'], e)
raise e

def __call__(self, r):
return super(DistributionConfig, self).__call__(r[self.annotation_key])

@StreamingDistribution.filter_registry.register('distribution-config')
class StreamingDistributionConfig(BaseDistributionConfig):
"""Check for Cloudfront streaming distribution config values

:example:

.. code-block:: yaml

policies:
- name: streaming-distribution-logging-enabled
resource: streaming-distribution
filters:
- type: distribution-config
key: Logging.Enabled
value: true
"""
permissions = ('cloudfront:GetStreamingDistributionConfig',)

def augment(self, resources):

client = local_session(self.manager.session_factory).client(
'cloudfront', region_name=self.manager.config.region)

for r in resources:
try:
r[self.annotation_key] = client.get_streaming_distribution_config(Id=r['Id']) \
.get('StreamingDistributionConfig')
except (client.exceptions.NoSuchStreamingDistribution):
r[self.annotation_key] = {}
except Exception as e:
self.log.warning(
"Exception trying to get Streaming Distribution Config: %s error: %s",
r['ARN'], e)
raise e


@Distribution.filter_registry.register('mismatch-s3-origin')
Expand Down Expand Up @@ -488,8 +525,38 @@ def process_distribution(self, client, distribution):
return


class BaseUpdateAction(BaseAction):
schema = type_schema('set-attributes',
attributes={"type": "object"},
required=('attributes',))
schema_alias = False

def validate(self, config_name, shape):
attrs = dict(self.data.get('attributes'))
if attrs.get('CallerReference'):
raise PolicyValidationError('CallerReference field cannot be updated')

# Set default values for required fields if they are not present
attrs["CallerReference"] = ""
config = self.validation_config
updatedConfig = {**config, **attrs}

request = {
config_name: updatedConfig,
"Id": "sample_id",
"IfMatch": "sample_string",
}
return shape_validate(request, shape, 'cloudfront')

def process(self, distributions):
client = local_session(self.manager.session_factory).client(
self.manager.get_model().service)
for d in distributions:
self.process_distribution(client, d)


@Distribution.action_registry.register('set-attributes')
class DistributionUpdateAction(BaseAction):
class DistributionUpdateAction(BaseUpdateAction):
"""Action to update the attributes of a distribution

:example:
Expand All @@ -514,81 +581,122 @@ class DistributionUpdateAction(BaseAction):
Bucket: 'test-enable-logging-c7n.s3.amazonaws.com'
Prefix: ''
"""
schema = type_schema('set-attributes',
attributes={"type": "object"},
required=('attributes',))

permissions = ("cloudfront:UpdateDistribution",
"cloudfront:GetDistributionConfig",)
shape = 'UpdateDistributionRequest'
validation_config = {
'Origins': {
'Quantity': 0,
'Items': [{
'Id': '',
'DomainName': ''
}]
},
'DefaultCacheBehavior': {
'TargetOriginId': '',
'ForwardedValues': {
'QueryString': True,
'Cookies': {
'Forward': ''
}
},
'TrustedSigners': {
'Enabled': True,
'Quantity': 0
},
'ViewerProtocolPolicy': '',
'MinTTL': 0
},
'Comment': '',
'Enabled': False
}

def validate(self):
attrs = dict(self.data.get('attributes'))
if attrs.get('CallerReference'):
raise PolicyValidationError('CallerReference field cannot be updated')

# Set default values for required fields if they are not present
attrs["CallerReference"] = ""
self.set_required_update_fields(attrs)
request = {
"DistributionConfig": attrs,
"Id": "sample_id",
"IfMatch": "sample_string",
}
return shape_validate(request, self.shape, 'cloudfront')

def set_required_update_fields(self, config):
if 'Origins' not in config:
config["Origins"] = {
"Quantity": 0,
"Items": [
{
"Id": "",
"DomainName": ""
}
],
}
if 'DefaultCacheBehavior' not in config:
config["DefaultCacheBehavior"] = {
"TargetOriginId": "",
"ForwardedValues": {
"QueryString": True,
"Cookies": {
"Forward": ""
}
},
"TrustedSigners": {
"Enabled": True,
"Quantity": 0,
},
"ViewerProtocolPolicy": "",
"MinTTL": 0
}

def process(self, distributions):
client = local_session(self.manager.session_factory).client(
self.manager.get_model().service)
for d in distributions:
self.process_distribution(client, d)
return super().validate('DistributionConfig', self.shape)

def process_distribution(self, client, distribution):
try:
res = client.get_distribution_config(
Id=distribution[self.manager.get_model().id])
config = res['DistributionConfig']
default_config = self.validation_config
config = {**default_config, **res['DistributionConfig']}
updatedConfig = {**config, **self.data['attributes']}
self.set_required_update_fields(updatedConfig)
if config == updatedConfig:
return
res = client.update_distribution(
Id=distribution[self.manager.get_model().id],
IfMatch=res['ETag'],
DistributionConfig=updatedConfig
)
except (client.exceptions.NoSuchResource, client.exceptions.NoSuchDistribution):
except (client.exceptions.NoSuchDistribution):
pass
except Exception as e:
self.log.warning(
"Exception trying to update Distribution: %s error: %s",
distribution['ARN'], e)
raise e


@StreamingDistribution.action_registry.register('set-attributes')
class StreamingDistributionUpdateAction(BaseUpdateAction):
"""Action to update the attributes of a distribution

:example:

.. code-block:: yaml

policies:
- name: enforce-streaming-distribution-logging
resource: streaming-distribution
filters:
- type: value
key: "Logging.Enabled"
value: false
actions:
- type: set-attributes
attributes:
Logging:
Enabled: true
Bucket: 'test-enable-logging-c7n.s3.amazonaws.com'
Prefix: ''
"""
permissions = ("cloudfront:UpdateStreamingDistribution",
"cloudfront:GetStreamingDistributionConfig",)
shape = 'UpdateStreamingDistributionRequest'
validation_config = {
'S3Origin': {
'DomainName': 'domain_name',
'OriginAccessIdentity': 'origin_access_identity'
},
'TrustedSigners': {
'Enabled': False,
'Quantity': 0
},
'Comment': '',
'Enabled': False
}

def validate(self):
return super().validate('StreamingDistributionConfig', self.shape)

def process_distribution(self, client, streaming_distribution):
try:
res = client.get_streaming_distribution_config(
Id=streaming_distribution[self.manager.get_model().id])
default_config = self.validation_config
config = {**default_config, **res['StreamingDistributionConfig']}
updatedConfig = {**config, **self.data['attributes']}
if config == updatedConfig:
return
res = client.update_streaming_distribution(
Id=streaming_distribution[self.manager.get_model().id],
IfMatch=res['ETag'],
StreamingDistributionConfig=updatedConfig
)
except (client.exceptions.NoSuchStreamingDistribution):
pass
except Exception as e:
self.log.warning(
"Exception trying to update Streaming Distribution: %s error: %s",
streaming_distribution['ARN'], e)
raise e
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
{
"status_code": 200,
"data": {
"ResponseMetadata": {
"RequestId": "b63bf6dd-3564-47e2-931f-34f9b4045f13",
"HTTPStatusCode": 200,
"HTTPHeaders": {
"x-amzn-requestid": "b63bf6dd-3564-47e2-931f-34f9b4045f13",
"etag": "E2LOA0W0H4PY7B",
"content-type": "text/xml",
"content-length": "2298",
"vary": "Accept-Encoding",
"date": "Fri, 28 Feb 2020 17:58:16 GMT"
},
"RetryAttempts": 0
},
"ETag": "E2LOA0W0H4PY7B",
"ResponseMetadata": {},
"ETag": "ETAPGKTC9B1VZ",
"DistributionConfig": {
"CallerReference": "1582867337153",
"CallerReference": "6441605581961",
"Comment": "",
"Enabled": true,
"Logging": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
{
"status_code": 200,
"data": {
"ResponseMetadata": {
"RequestId": "4bab1034-442e-4706-a513-bd2073dd9fe1",
"HTTPStatusCode": 200,
"HTTPHeaders": {
"x-amzn-requestid": "4bab1034-442e-4706-a513-bd2073dd9fe1",
"etag": "E51JMM89BDP7",
"content-type": "text/xml",
"content-length": "2337",
"vary": "Accept-Encoding",
"date": "Fri, 28 Feb 2020 17:58:16 GMT"
},
"RetryAttempts": 0
},
"ETag": "E51JMM89BDP7",
"ResponseMetadata": {},
"ETag": "ETAPGKTC9B1VZ",
"DistributionConfig": {
"CallerReference": "1582867337153",
"CallerReference": "6441605581961",
"Comment": "",
"Enabled": true,
"Logging": {
"Enabled": true,
"Enabled": false,
"IncludeCookies": false,
"Bucket": "test-enable-logging-c7n.s3.amazonaws.com",
"Bucket": "",
"Prefix": ""
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"status_code": 200,
"data": {
"ResponseMetadata": {},
"ETag": "E3SPE6T0EJSAA4",
"DistributionConfig": {
"CallerReference": "6441605581961",
"Comment": "",
"Enabled": true,
"Logging": {
"Enabled": true,
"IncludeCookies": false,
"Bucket": "test-logging.s3.amazonaws.com",
"Prefix": ""
}
}
}
}