1# Copyright The Cloud Custodian Authors.
2# SPDX-License-Identifier: Apache-2.0
3import json
4from botocore.exceptions import ClientError
5from concurrent.futures import as_completed
6from c7n.manager import resources, ResourceManager
7from c7n.query import QueryResourceManager, TypeInfo
8from c7n.utils import local_session, chunks, type_schema, generate_arn
9from c7n.actions import BaseAction, ActionRegistry, RemovePolicyBase
10from c7n.exceptions import PolicyValidationError
11from c7n.filters.vpc import SubnetFilter, SecurityGroupFilter
12from c7n.filters.related import RelatedResourceFilter
13from c7n.tags import universal_augment
14from c7n.filters import ValueFilter, FilterRegistry, CrossAccountAccessFilter
15from c7n import query, utils
16from c7n.resources.account import GlueCatalogEncryptionEnabled
17from c7n.filters.kms import KmsRelatedFilter
18
19
20@resources.register('glue-connection')
21class GlueConnection(QueryResourceManager):
22
23 class resource_type(TypeInfo):
24 service = 'glue'
25 enum_spec = ('get_connections', 'ConnectionList', {'HidePassword': True})
26 id = name = 'Name'
27 date = 'CreationTime'
28 arn_type = "connection"
29 cfn_type = 'AWS::Glue::Connection'
30 universal_taggable = object()
31
32 augment = universal_augment
33
34
35@GlueConnection.filter_registry.register('subnet')
36class ConnectionSubnetFilter(SubnetFilter):
37
38 RelatedIdsExpression = 'PhysicalConnectionRequirements.SubnetId'
39
40
41@GlueConnection.filter_registry.register('security-group')
42class ConnectionSecurityGroupFilter(SecurityGroupFilter):
43
44 RelatedIdsExpression = 'PhysicalConnectionRequirements.' \
45 'SecurityGroupIdList[]'
46
47
48@GlueConnection.action_registry.register('delete')
49class DeleteConnection(BaseAction):
50 """Delete a connection from the data catalog
51
52 :example:
53
54 .. code-block:: yaml
55
56 policies:
57 - name: delete-jdbc-connections
58 resource: glue-connection
59 filters:
60 - ConnectionType: JDBC
61 actions:
62 - type: delete
63 """
64 schema = type_schema('delete')
65 permissions = ('glue:DeleteConnection',)
66
67 def delete_connection(self, r):
68 client = local_session(self.manager.session_factory).client('glue')
69 try:
70 client.delete_connection(ConnectionName=r['Name'])
71 except ClientError as e:
72 if e.response['Error']['Code'] != 'EntityNotFoundException':
73 raise
74
75 def process(self, resources):
76 with self.executor_factory(max_workers=2) as w:
77 list(w.map(self.delete_connection, resources))
78
79
80@resources.register('glue-dev-endpoint')
81class GlueDevEndpoint(QueryResourceManager):
82
83 class resource_type(TypeInfo):
84 service = 'glue'
85 enum_spec = ('get_dev_endpoints', 'DevEndpoints', None)
86 id = name = 'EndpointName'
87 date = 'CreatedTimestamp'
88 arn_type = "devEndpoint"
89 universal_taggable = True
90 cfn_type = 'AWS::Glue::DevEndpoint'
91
92 augment = universal_augment
93
94
95@GlueDevEndpoint.filter_registry.register('subnet')
96class EndpointSubnetFilter(SubnetFilter):
97
98 RelatedIdsExpression = 'SubnetId'
99
100
101@GlueDevEndpoint.action_registry.register('delete')
102class DeleteDevEndpoint(BaseAction):
103 """Deletes public Glue Dev Endpoints
104
105 :example:
106
107 .. code-block:: yaml
108
109 policies:
110 - name: delete-public-dev-endpoints
111 resource: glue-dev-endpoint
112 filters:
113 - PublicAddress: present
114 actions:
115 - type: delete
116 """
117 schema = type_schema('delete')
118 permissions = ('glue:DeleteDevEndpoint',)
119
120 def delete_dev_endpoint(self, client, endpoint_set):
121 for e in endpoint_set:
122 try:
123 client.delete_dev_endpoint(EndpointName=e['EndpointName'])
124 except client.exceptions.AlreadyExistsException:
125 pass
126
127 def process(self, resources):
128 futures = []
129 client = local_session(self.manager.session_factory).client('glue')
130 with self.executor_factory(max_workers=2) as w:
131 for endpoint_set in chunks(resources, size=5):
132 futures.append(w.submit(self.delete_dev_endpoint, client, endpoint_set))
133 for f in as_completed(futures):
134 if f.exception():
135 self.log.error(
136 "Exception deleting glue dev endpoint \n %s",
137 f.exception())
138
139
140@resources.register('glue-job')
141class GlueJob(QueryResourceManager):
142
143 class resource_type(TypeInfo):
144 service = 'glue'
145 enum_spec = ('get_jobs', 'Jobs', None)
146 id = name = 'Name'
147 date = 'CreatedOn'
148 arn_type = 'job'
149 universal_taggable = True
150 cfn_type = 'AWS::Glue::Job'
151
152 permissions = ('glue:GetJobs',)
153 augment = universal_augment
154
155
156@GlueJob.action_registry.register('delete')
157class DeleteJob(BaseAction):
158
159 schema = type_schema('delete')
160 permissions = ('glue:DeleteJob',)
161
162 def process(self, resources):
163 client = local_session(self.manager.session_factory).client('glue')
164 for r in resources:
165 try:
166 client.delete_job(JobName=r['Name'])
167 except client.exceptions.EntityNotFoundException:
168 continue
169
170
171@GlueJob.action_registry.register('toggle-metrics')
172class GlueJobToggleMetrics(BaseAction):
173 """Enable or disable CloudWatch metrics for a Glue job
174
175 :example:
176
177 .. code-block:: yaml
178
179 policies:
180 - name: gluejob-enable-metrics
181 resource: glue-job
182 filters:
183 - type: value
184 key: 'DefaultArguments."--enable-metrics"'
185 value: absent
186 actions:
187 - type: toggle-metrics
188 enabled: true
189 """
190 schema = type_schema(
191 'toggle-metrics',
192 enabled={'type': 'boolean'},
193 required=['enabled'],
194 )
195 permissions = ('glue:UpdateJob',)
196
197 def prepare_params(self, r):
198 client = local_session(self.manager.session_factory).client('glue')
199 update_keys = client.meta._service_model.shape_for('JobUpdate').members
200 want_keys = set(r).intersection(update_keys) - {'AllocatedCapacity'}
201 params = {k: r[k] for k in want_keys}
202
203 # Can't specify MaxCapacity when updating/creating a job if
204 # job configuration includes WorkerType or NumberOfWorkers
205 if 'WorkerType' in params or 'NumberOfWorkers' in params:
206 del params['MaxCapacity']
207
208 # Can't specify Timeout when updating Ray jobs.
209 # Removing Timeout preserves default setting.
210 if params['Command']['Name'] == 'glueray':
211 del params['Timeout']
212
213 if self.data.get('enabled'):
214 if 'DefaultArguments' not in params:
215 params['DefaultArguments'] = {}
216 params["DefaultArguments"]["--enable-metrics"] = ""
217 else:
218 if 'DefaultArguments' in params and \
219 '--enable-metrics' in params['DefaultArguments']:
220 del params["DefaultArguments"]["--enable-metrics"]
221
222 return params
223
224 def process(self, resources):
225 client = local_session(self.manager.session_factory).client('glue')
226
227 for r in resources:
228 try:
229 job_name = r["Name"]
230 updated_resource = self.prepare_params(r)
231 client.update_job(JobName=job_name, JobUpdate=updated_resource)
232 except Exception as e:
233 self.log.error('Error updating glue job: {}'.format(e))
234
235
236@resources.register('glue-crawler')
237class GlueCrawler(QueryResourceManager):
238
239 class resource_type(TypeInfo):
240 service = 'glue'
241 enum_spec = ('get_crawlers', 'Crawlers', None)
242 id = name = 'Name'
243 date = 'CreatedOn'
244 arn_type = 'crawler'
245 state_key = 'State'
246 universal_taggable = True
247 cfn_type = 'AWS::Glue::Crawler'
248
249 augment = universal_augment
250
251
252class SecurityConfigFilter(RelatedResourceFilter):
253 """Filters glue crawlers with security configurations
254
255 :example:
256
257 .. code-block:: yaml
258
259 policies:
260 - name: need-kms-cloudwatch
261 resource: glue-crawler
262 filters:
263 - type: security-config
264 key: EncryptionConfiguration.CloudWatchEncryption.CloudWatchEncryptionMode
265 op: ne
266 value: SSE-KMS
267
268 To find resources missing any security configuration all set `missing: true` on the filter.
269 """
270
271 RelatedResource = "c7n.resources.glue.GlueSecurityConfiguration"
272 AnnotationKey = "matched-security-config"
273 RelatedIdsExpression = None
274
275 schema = type_schema(
276 'security-config',
277 missing={'type': 'boolean', 'default': False},
278 rinherit=ValueFilter.schema)
279
280 def validate(self):
281 if self.data.get('missing'):
282 return self
283 else:
284 return super(SecurityConfigFilter, self).validate()
285
286 def process(self, resources, event=None):
287 if self.data.get('missing'):
288 return [r for r in resources if self.RelatedIdsExpression not in r]
289 return super(SecurityConfigFilter, self).process(resources, event=None)
290
291
292@GlueDevEndpoint.filter_registry.register('security-config')
293class DevEndpointSecurityConfigFilter(SecurityConfigFilter):
294 RelatedIdsExpression = 'SecurityConfiguration'
295
296
297@GlueJob.filter_registry.register('security-config')
298class GlueJobSecurityConfigFilter(SecurityConfigFilter):
299 RelatedIdsExpression = 'SecurityConfiguration'
300
301
302@GlueCrawler.filter_registry.register('security-config')
303class GlueCrawlerSecurityConfigFilter(SecurityConfigFilter):
304
305 RelatedIdsExpression = 'CrawlerSecurityConfiguration'
306
307
308@GlueCrawler.action_registry.register('delete')
309class DeleteCrawler(BaseAction):
310
311 schema = type_schema('delete')
312 permissions = ('glue:DeleteCrawler',)
313 valid_origin_states = ('READY', 'FAILED')
314
315 def process(self, resources):
316 resources = self.filter_resources(resources, 'State', self.valid_origin_states)
317
318 client = local_session(self.manager.session_factory).client('glue')
319 for r in resources:
320 try:
321 client.delete_crawler(Name=r['Name'])
322 except client.exceptions.EntityNotFoundException:
323 continue
324
325
326@resources.register('glue-database')
327class GlueDatabase(QueryResourceManager):
328
329 class resource_type(TypeInfo):
330 service = 'glue'
331 enum_spec = ('get_databases', 'DatabaseList', None)
332 id = name = 'Name'
333 date = 'CreatedOn'
334 arn_type = 'database'
335 state_key = 'State'
336 cfn_type = 'AWS::Glue::Database'
337
338
339@GlueDatabase.action_registry.register('delete')
340class DeleteDatabase(BaseAction):
341
342 schema = type_schema('delete')
343 permissions = ('glue:DeleteDatabase',)
344
345 def process(self, resources):
346 client = local_session(self.manager.session_factory).client('glue')
347 for r in resources:
348 try:
349 client.delete_database(Name=r['Name'])
350 except client.exceptions.EntityNotFoundException:
351 continue
352
353
354@resources.register('glue-table')
355class GlueTable(query.ChildResourceManager):
356
357 child_source = 'describe-table'
358
359 class resource_type(TypeInfo):
360 service = 'glue'
361 parent_spec = ('glue-database', 'DatabaseName', None)
362 enum_spec = ('get_tables', 'TableList', None)
363 id = name = 'Name'
364 date = 'CreatedOn'
365 arn_type = 'table'
366
367 def get_arns(self, resources):
368 return [self.generate_arn(r['DatabaseName'] + '/' + r['Name']) for r in resources]
369
370
371@query.sources.register('describe-table')
372class DescribeTable(query.ChildDescribeSource):
373
374 def get_query(self):
375 return super(DescribeTable, self).get_query(capture_parent_id=True)
376
377 def augment(self, resources):
378 result = []
379 for parent_id, r in resources:
380 r['DatabaseName'] = parent_id
381 result.append(r)
382 return result
383
384
385@GlueTable.action_registry.register('delete')
386class DeleteTable(BaseAction):
387
388 schema = type_schema('delete')
389 permissions = ('glue:DeleteTable',)
390
391 def process(self, resources):
392 client = local_session(self.manager.session_factory).client('glue')
393 for r in resources:
394 try:
395 client.delete_table(DatabaseName=r['DatabaseName'], Name=r['Name'])
396 except client.exceptions.EntityNotFoundException:
397 continue
398
399
400@resources.register('glue-classifier')
401class GlueClassifier(QueryResourceManager):
402
403 class resource_type(TypeInfo):
404 service = 'glue'
405 enum_spec = ('get_classifiers', 'Classifiers', None)
406 id = name = 'Name'
407 date = 'CreationTime'
408 arn_type = 'classifier'
409 config_type = cfn_type = 'AWS::Glue::Classifier'
410
411
412@GlueClassifier.action_registry.register('delete')
413class DeleteClassifier(BaseAction):
414
415 schema = type_schema('delete')
416 permissions = ('glue:DeleteClassifier',)
417
418 def process(self, resources):
419 client = local_session(self.manager.session_factory).client('glue')
420 for r in resources:
421 # Extract the classifier from the resource, see below
422 # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/glue.html#Glue.Client.get_classifier
423 classifier = list(r.values())[0]
424 try:
425 client.delete_classifier(Name=classifier['Name'])
426 except client.exceptions.EntityNotFoundException:
427 continue
428
429
430@resources.register('glue-ml-transform')
431class GlueMLTransform(QueryResourceManager):
432
433 class resource_type(TypeInfo):
434 service = 'glue'
435 enum_spec = ('get_ml_transforms', 'Transforms', None)
436 name = 'Name'
437 id = 'TransformId'
438 arn_type = 'mlTransform'
439 universal_taggable = object()
440 config_type = cfn_type = 'AWS::Glue::MLTransform'
441
442 source_mapping = {'describe': query.DescribeWithResourceTags,
443 'config': query.ConfigSource}
444
445 def get_permissions(self):
446 return ('glue:GetMLTransforms',)
447
448
449@GlueMLTransform.action_registry.register('delete')
450class DeleteMLTransform(BaseAction):
451
452 schema = type_schema('delete')
453 permissions = ('glue:DeleteMLTransform',)
454
455 def process(self, resources):
456 client = local_session(self.manager.session_factory).client('glue')
457 for r in resources:
458 try:
459 client.delete_ml_transform(TransformId=r['TransformId'])
460 except client.exceptions.EntityNotFoundException:
461 continue
462
463
464@resources.register('glue-security-configuration')
465class GlueSecurityConfiguration(QueryResourceManager):
466
467 class resource_type(TypeInfo):
468 service = 'glue'
469 enum_spec = ('get_security_configurations', 'SecurityConfigurations', None)
470 id = name = 'Name'
471 arn_type = 'securityConfiguration'
472 date = 'CreatedTimeStamp'
473 cfn_type = 'AWS::Glue::SecurityConfiguration'
474
475
476@GlueSecurityConfiguration.filter_registry.register('kms-key')
477class KmsFilter(KmsRelatedFilter):
478
479 schema = type_schema(
480 'kms-key',
481 rinherit=ValueFilter.schema,
482 **{'key-type': {'type': 'string', 'enum': [
483 's3', 'cloudwatch', 'job-bookmarks', 'all']},
484 'match-resource': {'type': 'boolean'},
485 'operator': {'enum': ['and', 'or']}})
486
487 RelatedIdsExpression = ''
488
489 def __init__(self, data, manager=None):
490 super().__init__(data, manager)
491 key_type_to_related_ids = {
492 's3': 'EncryptionConfiguration.S3Encryption[].KmsKeyArn',
493 'cloudwatch': 'EncryptionConfiguration.CloudWatchEncryption.KmsKeyArn',
494 'job-bookmarks': 'EncryptionConfiguration.JobBookmarksEncryption.KmsKeyArn',
495 'all': 'EncryptionConfiguration.*[][].KmsKeyArn'
496 }
497 key_type = self.data.get('key_type', 'all')
498 self.RelatedIdsExpression = key_type_to_related_ids[key_type]
499
500
501@GlueSecurityConfiguration.action_registry.register('delete')
502class DeleteSecurityConfiguration(BaseAction):
503
504 schema = type_schema('delete')
505 permissions = ('glue:DeleteSecurityConfiguration',)
506
507 def process(self, resources):
508 client = local_session(self.manager.session_factory).client('glue')
509 for r in resources:
510 try:
511 client.delete_security_configuration(Name=r['Name'])
512 except client.exceptions.EntityNotFoundException:
513 continue
514
515
516@resources.register('glue-trigger')
517class GlueTrigger(QueryResourceManager):
518
519 class resource_type(TypeInfo):
520 service = 'glue'
521 enum_spec = ('get_triggers', 'Triggers', None)
522 id = name = 'Name'
523 arn_type = 'trigger'
524 universal_taggable = object()
525 cfn_type = 'AWS::Glue::Trigger'
526
527 augment = universal_augment
528
529
530@GlueTrigger.action_registry.register('delete')
531class DeleteTrigger(BaseAction):
532
533 schema = type_schema('delete')
534 permissions = ('glue:DeleteTrigger',)
535
536 def process(self, resources):
537 client = local_session(self.manager.session_factory).client('glue')
538 for r in resources:
539 try:
540 client.delete_trigger(Name=r['Name'])
541 except client.exceptions.EntityNotFoundException:
542 continue
543
544
545@resources.register('glue-workflow')
546class GlueWorkflow(QueryResourceManager):
547
548 class resource_type(TypeInfo):
549 service = 'glue'
550 enum_spec = ('list_workflows', 'Workflows', None)
551 detail_spec = ('get_workflow', 'Name', None, 'Workflow')
552 id = name = 'Name'
553 arn_type = 'workflow'
554 universal_taggable = object()
555 cfn_type = 'AWS::Glue::Workflow'
556
557 def augment(self, resources):
558 return universal_augment(
559 self, super(GlueWorkflow, self).augment(resources))
560
561
562@GlueWorkflow.action_registry.register('delete')
563class DeleteWorkflow(BaseAction):
564
565 schema = type_schema('delete')
566 permissions = ('glue:DeleteWorkflow',)
567
568 def process(self, resources):
569 client = local_session(self.manager.session_factory).client('glue')
570 for r in resources:
571 try:
572 client.delete_workflow(Name=r['Name'])
573 except client.exceptions.EntityNotFoundException:
574 continue
575
576
577@GlueWorkflow.filter_registry.register('security-config')
578class GlueWorkflowSecurityConfigFilter(SecurityConfigFilter):
579 RelatedIdsExpression = 'SecurityConfiguration'
580
581
582@resources.register('glue-catalog')
583class GlueDataCatalog(ResourceManager):
584
585 filter_registry = FilterRegistry('glue-catalog.filters')
586 action_registry = ActionRegistry('glue-catalog.actions')
587 retry = staticmethod(QueryResourceManager.retry)
588
589 class resource_type(query.TypeInfo):
590 service = 'glue'
591 arn_type = 'catalog'
592 id = name = 'CatalogId'
593 cfn_type = 'AWS::Glue::DataCatalogEncryptionSettings'
594
595 @classmethod
596 def get_permissions(cls):
597 return ('glue:GetDataCatalogEncryptionSettings',)
598
599 @classmethod
600 def has_arn(cls):
601 return True
602
603 def get_arns(self, resources):
604 return [self.generate_arn(res) for res in resources]
605
606 def generate_arn(self, res):
607 """
608 https://docs.aws.amazon.com/service-authorization/latest/reference/list_awsglue.html
609 One resource per region and one arn per region
610 """
611 return generate_arn(
612 service=self.resource_type.service,
613 resource=self.resource_type.arn_type,
614 region=self.config.region,
615 account_id=self.config.account_id
616 )
617
618 def get_model(self):
619 return self.resource_type
620
621 def _get_catalog_encryption_settings(self):
622 client = utils.local_session(self.session_factory).client('glue')
623 settings = client.get_data_catalog_encryption_settings()
624 settings['CatalogId'] = self.config.account_id
625 settings.pop('ResponseMetadata', None)
626 return [settings]
627
628 def resources(self):
629 return self.filter_resources(self._get_catalog_encryption_settings())
630
631 def get_resources(self, resource_ids):
632 return [{'CatalogId': self.config.account_id}]
633
634
635@GlueDataCatalog.filter_registry.register('kms-key')
636class GlueCatalogKmsFilter(KmsRelatedFilter):
637
638 schema = type_schema(
639 'kms-key',
640 rinherit=ValueFilter.schema,
641 **{'key-type': {'type': 'string', 'enum': [
642 'EncryptionAtRest', 'ConnectionPasswordEncryption']},
643 'required': ['key-type'],
644 'match-resource': {'type': 'boolean'},
645 'operator': {'enum': ['and', 'or']}})
646
647 permissions = ('glue:GetDataCatalogEncryptionSettings',)
648
649 RelatedIdsExpression = ''
650
651 def __init__(self, data, manager=None):
652 super().__init__(data, manager)
653 key_type_to_related_ids = {
654 'EncryptionAtRest': 'DataCatalogEncryptionSettings.EncryptionAtRest.SseAwsKmsKeyId',
655 'ConnectionPasswordEncryption':
656 'DataCatalogEncryptionSettings.ConnectionPasswordEncryption.AwsKmsKeyId'
657 }
658 self.RelatedIdsExpression = key_type_to_related_ids.get(self.data.get('key-type'))
659
660
661@GlueDataCatalog.action_registry.register('set-encryption')
662class GlueDataCatalogEncryption(BaseAction):
663 """Modifies glue data catalog encryption based on specified parameter
664 As per docs, we can enable catalog encryption or only password encryption,
665 not both
666
667 :example:
668
669 .. code-block:: yaml
670
671 policies:
672 - name: data-catalog-encryption
673 resource: glue-catalog
674 filters:
675 - type: value
676 key: DataCatalogEncryptionSettings.EncryptionAtRest.CatalogEncryptionMode
677 value: DISABLED
678 op: eq
679 actions:
680 - type: set-encryption
681 attributes:
682 EncryptionAtRest:
683 CatalogEncryptionMode: SSE-KMS
684 SseAwsKmsKeyId: alias/aws/glue
685 """
686
687 schema = type_schema(
688 'set-encryption',
689 required=['attributes'],
690 attributes={
691 'type': 'object',
692 'additionalProperties': False,
693 'properties': {
694 'EncryptionAtRest': {
695 'type': 'object',
696 'additionalProperties': False,
697 'required': ['CatalogEncryptionMode'],
698 'properties': {
699 'CatalogEncryptionMode': {'enum': ['DISABLED', 'SSE-KMS']},
700 'SseAwsKmsKeyId': {'type': 'string'}
701 }
702 },
703 'ConnectionPasswordEncryption': {
704 'type': 'object',
705 'additionalProperties': False,
706 'required': ['ReturnConnectionPasswordEncrypted'],
707 'properties': {
708 'ReturnConnectionPasswordEncrypted': {'type': 'boolean'},
709 'AwsKmsKeyId': {'type': 'string'}
710 }
711 }
712 }
713 }
714 )
715
716 permissions = ('glue:PutDataCatalogEncryptionSettings',)
717
718 def process(self, resources):
719 client = local_session(self.manager.session_factory).client('glue')
720 self.process_catalog_encryption(client, resources)
721
722 def process_catalog_encryption(self, client, resources):
723 # there is one glue data catalog per account
724 if 'DataCatalogEncryptionSettings' not in resources[0]:
725 resources = self.manager.resources()
726 enc_config = resources[0]['DataCatalogEncryptionSettings']
727 updated_config = {**enc_config, **self.data['attributes']}
728 if enc_config == updated_config:
729 return
730 client.put_data_catalog_encryption_settings(
731 DataCatalogEncryptionSettings=updated_config)
732
733
734@GlueDataCatalog.filter_registry.register('glue-security-config')
735class GlueCatalogEncryptionFilter(GlueCatalogEncryptionEnabled):
736 """Filter glue catalog by its glue encryption status and KMS key
737
738 :example:
739
740 .. code-block:: yaml
741
742 policies:
743 - name: glue-catalog-security-config
744 resource: aws.glue-catalog
745 filters:
746 - type: glue-security-config
747 SseAwsKmsKeyId: alias/aws/glue
748
749 """
750
751
752@GlueDataCatalog.filter_registry.register('cross-account')
753class GlueCatalogCrossAccount(CrossAccountAccessFilter):
754 """Filter glue catalog if it has cross account permissions
755
756 :example:
757
758 .. code-block:: yaml
759
760 policies:
761 - name: catalog-cross-account
762 resource: aws.glue-catalog
763 filters:
764 - type: cross-account
765
766 """
767 permissions = ('glue:GetResourcePolicy',)
768 policy_annotation = "c7n:AccessPolicy"
769
770 def get_resource_policy(self, r):
771 client = local_session(self.manager.session_factory).client('glue')
772 if self.policy_annotation in r:
773 return r[self.policy_annotation]
774 try:
775 policy = client.get_resource_policy().get('PolicyInJson')
776 except client.exceptions.EntityNotFoundException:
777 policy = {}
778 r[self.policy_annotation] = policy
779 return policy
780
781
782@GlueDataCatalog.action_registry.register('remove-statements')
783class RemovePolicyStatement(RemovePolicyBase):
784 """Action to remove policy statements from Glue Data Catalog
785
786 :example:
787
788 .. code-block:: yaml
789
790 policies:
791 - name: remove-glue-catalog-cross-account
792 resource: aws.glue-catalog
793 filters:
794 - type: cross-account
795 actions:
796 - type: remove-statements
797 statement_ids: matched
798 """
799 permissions = ('glue:PutResourcePolicy',)
800 policy_annotation = "c7n:AccessPolicy"
801
802 def validate(self):
803 for f in self.manager.iter_filters():
804 if isinstance(f, GlueCatalogCrossAccount):
805 return self
806 raise PolicyValidationError(
807 '`remove-statements` may only be used in '
808 'conjunction with `cross-account` filter on %s' % (self.manager.data,))
809
810 def process(self, resources):
811 resource = resources[0]
812 client = local_session(self.manager.session_factory).client('glue')
813 if resource.get(self.policy_annotation):
814 p = json.loads(resource[self.policy_annotation])
815 statements, found = self.process_policy(
816 p, resource, CrossAccountAccessFilter.annotation_key)
817 if not found:
818 return
819 if statements:
820 client.put_resource_policy(PolicyInJson=json.dumps(p))
821 else:
822 client.delete_resource_policy()