Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/c7n/resources/cw.py: 61%

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

420 statements  

1# Copyright The Cloud Custodian Authors. 

2# SPDX-License-Identifier: Apache-2.0 

3import itertools 

4import re 

5from collections import defaultdict 

6from concurrent.futures import as_completed 

7from datetime import datetime, timedelta 

8 

9from c7n.actions import BaseAction 

10from c7n.exceptions import PolicyValidationError 

11from c7n.filters import Filter, MetricsFilter 

12from c7n.filters.core import parse_date, ValueFilter 

13from c7n.filters.iamaccess import CrossAccountAccessFilter 

14from c7n.filters.kms import KmsRelatedFilter 

15from c7n.manager import resources 

16from c7n.query import ( 

17 QueryResourceManager, 

18 TypeInfo, DescribeSource, ConfigSource, DescribeWithResourceTags) 

19from c7n.resolver import ValuesFrom 

20from c7n.tags import universal_augment 

21from c7n.utils import type_schema, local_session, chunks, get_retry, jmespath_search 

22 

23 

24class DescribeAlarm(DescribeSource): 

25 def augment(self, resources): 

26 return universal_augment(self.manager, super().augment(resources)) 

27 

28 

29@resources.register('alarm') 

30class Alarm(QueryResourceManager): 

31 class resource_type(TypeInfo): 

32 service = 'cloudwatch' 

33 arn_type = 'alarm' 

34 enum_spec = ('describe_alarms', 'MetricAlarms', None) 

35 id = 'AlarmName' 

36 arn = 'AlarmArn' 

37 filter_name = 'AlarmNames' 

38 filter_type = 'list' 

39 name = 'AlarmName' 

40 date = 'AlarmConfigurationUpdatedTimestamp' 

41 cfn_type = config_type = 'AWS::CloudWatch::Alarm' 

42 universal_taggable = object() 

43 permissions_augment = ("cloudwatch:ListTagsForResource",) 

44 

45 source_mapping = { 

46 'describe': DescribeAlarm, 

47 'config': ConfigSource 

48 } 

49 

50 retry = staticmethod(get_retry(('Throttled',))) 

51 

52 

53@Alarm.action_registry.register('delete') 

54class AlarmDelete(BaseAction): 

55 """Delete a cloudwatch alarm. 

56 

57 :example: 

58 

59 .. code-block:: yaml 

60 

61 policies: 

62 - name: cloudwatch-delete-stale-alarms 

63 resource: alarm 

64 filters: 

65 - type: value 

66 value_type: age 

67 key: StateUpdatedTimestamp 

68 value: 30 

69 op: ge 

70 - StateValue: INSUFFICIENT_DATA 

71 actions: 

72 - delete 

73 """ 

74 

75 schema = type_schema('delete') 

76 permissions = ('cloudwatch:DeleteAlarms',) 

77 

78 def process(self, resources): 

79 client = local_session( 

80 self.manager.session_factory).client('cloudwatch') 

81 

82 for resource_set in chunks(resources, size=100): 

83 self.manager.retry( 

84 client.delete_alarms, 

85 AlarmNames=[r['AlarmName'] for r in resource_set]) 

86 

87 

88@Alarm.filter_registry.register('is-composite-child') 

89class IsCompositeChild(Filter): 

90 schema = type_schema('is-composite-child', state={"type": "boolean"}) 

91 permissions = ('cloudwatch:DescribeAlarms',) 

92 

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

94 state = self.data.get("state", True) 

95 # Get the composite alarms since filtered out in enum_spec 

96 composite_alarms = self.manager.get_resource_manager("composite-alarm").resources() 

97 composite_alarm_rules = jmespath_search('[].AlarmRule', composite_alarms) 

98 

99 child_alarm_names = set() 

100 # Loop through, find child alarm names 

101 for rule in composite_alarm_rules: 

102 names = self.extract_alarm_names_from_rule(rule) 

103 child_alarm_names.update(names) 

104 

105 if state: 

106 # If we want to filter out alarms that are a child of a composite alarm 

107 return [r for r in resources if r['AlarmName'] in child_alarm_names] 

108 

109 return [r for r in resources if r['AlarmName'] not in child_alarm_names] 

110 

111 def extract_alarm_names_from_rule(self, rule): 

112 # Check alarm references (OK/ALARM/INSUFFICIENT_DATA) 

113 pattern = r"\b(?:ALARM|OK|INSUFFICIENT_DATA)\s*\(\s*([^\)]+)\s*\)" 

114 matches = re.findall(pattern, rule) 

115 return set(matches) 

116 

117 

118@resources.register('composite-alarm') 

119class CompositeAlarm(QueryResourceManager): 

120 

121 class resource_type(TypeInfo): 

122 service = 'cloudwatch' 

123 arn_type = 'alarm' 

124 enum_spec = ('describe_alarms', 'CompositeAlarms', {'AlarmTypes': ['CompositeAlarm']}) 

125 id = name = 'AlarmName' 

126 arn = 'AlarmArn' 

127 date = 'AlarmConfigurationUpdatedTimestamp' 

128 cfn_type = 'AWS::CloudWatch::CompositeAlarm' 

129 universal_taggable = object() 

130 

131 augment = universal_augment 

132 

133 retry = staticmethod(get_retry(('Throttled',))) 

134 

135 

136@CompositeAlarm.action_registry.register('delete') 

137class CompositeAlarmDelete(BaseAction): 

138 """Delete a cloudwatch composite alarm. 

139 

140 :example: 

141 

142 .. code-block:: yaml 

143 

144 policies: 

145 - name: cloudwatch-delete-composite-alarms 

146 resource: aws.composite-alarm 

147 filters: 

148 - type: value 

149 value_type: age 

150 key: StateUpdatedTimestamp 

151 value: 30 

152 op: ge 

153 - StateValue: INSUFFICIENT_DATA 

154 actions: 

155 - delete 

156 """ 

157 

158 schema = type_schema('delete') 

159 permissions = ('cloudwatch:DeleteAlarms',) 

160 

161 def process(self, resources): 

162 client = local_session( 

163 self.manager.session_factory).client('cloudwatch') 

164 

165 for resource_set in chunks(resources, size=100): 

166 self.manager.retry( 

167 client.delete_alarms, 

168 AlarmNames=[r['AlarmName'] for r in resource_set]) 

169 

170 

171@resources.register('log-group') 

172class LogGroup(QueryResourceManager): 

173 class resource_type(TypeInfo): 

174 service = 'logs' 

175 arn_type = 'log-group' 

176 enum_spec = ('describe_log_groups', 'logGroups', None) 

177 id = name = 'logGroupName' 

178 arn = 'arn' # see get-arns override re attribute usage 

179 filter_name = 'logGroupNamePrefix' 

180 filter_type = 'scalar' 

181 dimension = 'LogGroupName' 

182 date = 'creationTime' 

183 universal_taggable = True 

184 cfn_type = 'AWS::Logs::LogGroup' 

185 permissions_augment = ("logs:ListTagsForResource",) 

186 

187 augment = universal_augment 

188 

189 def get_arns(self, resources): 

190 # log group arn in resource describe has ':*' suffix, not all 

191 # apis can use that form, so normalize to standard arn. 

192 return [r['arn'][:-2] for r in resources] 

193 

194 

195@resources.register('insight-rule') 

196class InsightRule(QueryResourceManager): 

197 class resource_type(TypeInfo): 

198 service = 'cloudwatch' 

199 arn_type = 'insight-rule' 

200 enum_spec = ('describe_insight_rules', 'InsightRules', None) 

201 name = id = 'Name' 

202 universal_taggable = object() 

203 permission_augment = ('cloudWatch::ListTagsForResource',) 

204 cfn_type = 'AWS::CloudWatch::InsightRule' 

205 

206 def augment(self, rules): 

207 client = local_session(self.session_factory).client('cloudwatch') 

208 

209 def _add_tags(r): 

210 arn = self.generate_arn(r['Name']) 

211 r['Tags'] = client.list_tags_for_resource( 

212 ResourceARN=arn).get('Tags', []) 

213 return r 

214 

215 return list(map(_add_tags, rules)) 

216 

217 

218@InsightRule.action_registry.register('disable') 

219class InsightRuleDisable(BaseAction): 

220 """Disable a cloudwatch contributor insight rule. 

221 

222 :example: 

223 

224 .. code-block:: yaml 

225 

226 policies: 

227 - name: cloudwatch-disable-insight-rule 

228 resource: insight-rule 

229 filters: 

230 - type: value 

231 key: State 

232 value: ENABLED 

233 op: eq 

234 actions: 

235 - disable 

236 """ 

237 

238 schema = type_schema('disable') 

239 permissions = ('cloudwatch:DisableInsightRules',) 

240 

241 def process(self, resources): 

242 client = local_session( 

243 self.manager.session_factory).client('cloudwatch') 

244 

245 for resource_set in chunks(resources, size=100): 

246 self.manager.retry( 

247 client.disable_insight_rules, 

248 RuleNames=[r['Name'] for r in resource_set]) 

249 

250 

251@InsightRule.action_registry.register('delete') 

252class InsightRuleDelete(BaseAction): 

253 """Delete a cloudwatch contributor insight rule 

254 

255 :example: 

256 

257 .. code-block:: yaml 

258 

259 policies: 

260 - name: cloudwatch-delete-insight-rule 

261 resource: insight-rule 

262 filters: 

263 - type: value 

264 key: State 

265 value: ENABLED 

266 op: eq 

267 actions: 

268 - delete 

269 """ 

270 

271 schema = type_schema('delete') 

272 permissions = ('cloudwatch:DeleteInsightRules',) 

273 

274 def process(self, resources): 

275 client = local_session( 

276 self.manager.session_factory).client('cloudwatch') 

277 

278 for resource_set in chunks(resources, size=100): 

279 self.manager.retry( 

280 client.delete_insight_rules, 

281 RuleNames=[r['Name'] for r in resource_set]) 

282 

283 

284@LogGroup.filter_registry.register('metrics') 

285class LogGroupMetrics(MetricsFilter): 

286 

287 def get_dimensions(self, resource): 

288 return [{'Name': 'LogGroupName', 'Value': resource['logGroupName']}] 

289 

290 

291@resources.register('log-metric') 

292class LogMetric(QueryResourceManager): 

293 class resource_type(TypeInfo): 

294 service = 'logs' 

295 enum_spec = ('describe_metric_filters', 'metricFilters', None) 

296 arn = False 

297 id = name = 'filterName' 

298 date = 'creationTime' 

299 cfn_type = 'AWS::Logs::MetricFilter' 

300 

301 

302@LogMetric.filter_registry.register('alarm') 

303class LogMetricAlarmFilter(ValueFilter): 

304 """ 

305 Filter log metric filters based on associated alarms. 

306 

307 :example: 

308 

309 .. code-block:: yaml 

310 

311 policies: 

312 - name: log-metrics-with-alarms 

313 resource: aws.log-metric 

314 filters: 

315 - type: alarm 

316 key: AlarmName 

317 value: present 

318 """ 

319 

320 schema = type_schema('alarm', rinherit=ValueFilter.schema) 

321 annotation_key = 'c7n:MetricAlarms' 

322 FetchThreshold = 10 # below this number of resources, fetch alarms individually 

323 

324 def augment(self, resources): 

325 """Add alarm details to log metric filter resources 

326 

327 This includes all alarms where the metric name and namespace match 

328 a log metric filter's metric transformation. 

329 """ 

330 

331 if len(resources) < self.FetchThreshold: 

332 client = local_session(self.manager.session_factory).client('cloudwatch') 

333 for r in resources: 

334 r[self.annotation_key] = list(itertools.chain(*( 

335 self.manager.retry( 

336 client.describe_alarms_for_metric, 

337 Namespace=t['metricNamespace'], 

338 MetricName=t['metricName'])['MetricAlarms'] 

339 for t in r.get('metricTransformations', ()) 

340 ))) 

341 else: 

342 alarms = self.manager.get_resource_manager('aws.alarm').resources() 

343 

344 # We'll be matching resources to alarms based on namespace and 

345 # metric name - this lookup table makes that smoother 

346 alarms_by_metric = defaultdict(list) 

347 for alarm in alarms: 

348 alarms_by_metric[(alarm['Namespace'], alarm['MetricName'])].append(alarm) 

349 

350 for r in resources: 

351 r[self.annotation_key] = list(itertools.chain(*( 

352 alarms_by_metric.get((t['metricNamespace'], t['metricName']), []) 

353 for t in r.get('metricTransformations', ()) 

354 ))) 

355 

356 def get_permissions(self): 

357 return [ 

358 *self.manager.get_resource_manager('aws.alarm').get_permissions(), 

359 'cloudwatch:DescribeAlarmsForMetric' 

360 ] 

361 

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

363 self.augment(resources) 

364 

365 matched = [] 

366 for r in resources: 

367 if any((self.match(alarm) for alarm in r[self.annotation_key])): 

368 matched.append(r) 

369 return matched 

370 

371 

372@LogGroup.action_registry.register('retention') 

373class Retention(BaseAction): 

374 """Action to set the retention period (in days) for CloudWatch log groups 

375 

376 :example: 

377 

378 .. code-block:: yaml 

379 

380 policies: 

381 - name: cloudwatch-set-log-group-retention 

382 resource: log-group 

383 actions: 

384 - type: retention 

385 days: 200 

386 """ 

387 

388 schema = type_schema('retention', days={'type': 'integer'}) 

389 permissions = ('logs:PutRetentionPolicy',) 

390 

391 def process(self, resources): 

392 client = local_session(self.manager.session_factory).client('logs') 

393 days = self.data['days'] 

394 for r in resources: 

395 self.manager.retry( 

396 client.put_retention_policy, 

397 logGroupName=r['logGroupName'], 

398 retentionInDays=days) 

399 

400 

401@LogGroup.action_registry.register('delete') 

402class Delete(BaseAction): 

403 """ 

404 

405 :example: 

406 

407 .. code-block:: yaml 

408 

409 policies: 

410 - name: cloudwatch-delete-stale-log-group 

411 resource: log-group 

412 filters: 

413 - type: last-write 

414 days: 182.5 

415 actions: 

416 - delete 

417 """ 

418 

419 schema = type_schema('delete') 

420 permissions = ('logs:DeleteLogGroup',) 

421 

422 def process(self, resources): 

423 client = local_session(self.manager.session_factory).client('logs') 

424 for r in resources: 

425 try: 

426 self.manager.retry( 

427 client.delete_log_group, logGroupName=r['logGroupName']) 

428 except client.exceptions.ResourceNotFoundException: 

429 continue 

430 

431 

432@LogGroup.filter_registry.register('last-write') 

433class LastWriteDays(Filter): 

434 """Filters CloudWatch log groups by last write 

435 

436 :example: 

437 

438 .. code-block:: yaml 

439 

440 policies: 

441 - name: cloudwatch-stale-groups 

442 resource: log-group 

443 filters: 

444 - type: last-write 

445 days: 60 

446 """ 

447 

448 schema = type_schema( 

449 'last-write', days={'type': 'number'}) 

450 permissions = ('logs:DescribeLogStreams',) 

451 

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

453 client = local_session(self.manager.session_factory).client('logs') 

454 self.date_threshold = parse_date(datetime.utcnow()) - timedelta( 

455 days=self.data['days']) 

456 return [r for r in resources if self.check_group(client, r)] 

457 

458 def check_group(self, client, group): 

459 streams = self.manager.retry( 

460 client.describe_log_streams, 

461 logGroupName=group['logGroupName'], 

462 orderBy='LastEventTime', 

463 descending=True, 

464 limit=3).get('logStreams') 

465 group['streams'] = streams 

466 if not streams: 

467 last_timestamp = group['creationTime'] 

468 elif 'lastIngestionTime' in streams[0]: 

469 last_timestamp = streams[0]['lastIngestionTime'] 

470 else: 

471 last_timestamp = streams[0]['creationTime'] 

472 

473 last_write = parse_date(last_timestamp) 

474 group['lastWrite'] = last_write 

475 return self.date_threshold > last_write 

476 

477 

478@LogGroup.filter_registry.register('cross-account') 

479class LogCrossAccountFilter(CrossAccountAccessFilter): 

480 schema = type_schema( 

481 'cross-account', 

482 # white list accounts 

483 whitelist_from=ValuesFrom.schema, 

484 whitelist={'type': 'array', 'items': {'type': 'string'}}) 

485 

486 permissions = ('logs:DescribeSubscriptionFilters',) 

487 

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

489 client = local_session(self.manager.session_factory).client('logs') 

490 accounts = self.get_accounts() 

491 results = [] 

492 with self.executor_factory(max_workers=1) as w: 

493 futures = [] 

494 for rset in chunks(resources, 50): 

495 futures.append( 

496 w.submit( 

497 self.process_resource_set, client, accounts, rset)) 

498 for f in as_completed(futures): 

499 if f.exception(): 

500 self.log.error( 

501 "Error checking log groups cross-account %s", 

502 f.exception()) 

503 continue 

504 results.extend(f.result()) 

505 return results 

506 

507 def process_resource_set(self, client, accounts, resources): 

508 results = [] 

509 for r in resources: 

510 found = False 

511 filters = self.manager.retry( 

512 client.describe_subscription_filters, 

513 logGroupName=r['logGroupName']).get('subscriptionFilters', ()) 

514 for f in filters: 

515 if 'destinationArn' not in f: 

516 continue 

517 account_id = f['destinationArn'].split(':', 5)[4] 

518 if account_id not in accounts: 

519 r.setdefault('c7n:CrossAccountViolations', []).append( 

520 account_id) 

521 found = True 

522 if found: 

523 results.append(r) 

524 return results 

525 

526 

527@LogGroup.filter_registry.register('subscription-filter') 

528class LogSubscriptionFilter(ValueFilter): 

529 """Filters CloudWatch log groups by subscriptions 

530 

531 :example: 

532 

533 .. code-block:: yaml 

534 

535 policies: 

536 - name: cloudwatch-groups-with-subscriptions 

537 resource: log-group 

538 filters: 

539 - type: subscription-filter 

540 key: destinationArn 

541 value: arn:aws:lambda:us-east-1:123456789876:function:forwarder 

542 """ 

543 schema = type_schema('subscription-filter', rinherit=ValueFilter.schema) 

544 annotation_key = 'c7n:SubscriptionFilters' 

545 permissions = ('logs:DescribeSubscriptionFilters',) 

546 

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

548 client = local_session(self.manager.session_factory).client('logs') 

549 results = [] 

550 for r in resources: 

551 filters = self.manager.retry( 

552 client.describe_subscription_filters, 

553 logGroupName=r['logGroupName']).get('subscriptionFilters', ()) 

554 if not any(filters): 

555 continue 

556 for f in filters: 

557 r.setdefault(self.annotation_key, []).append(f) 

558 if (len(self.data) == 1) or any((self.match(sub) for sub in r[self.annotation_key])): 

559 results.append(r) 

560 return results 

561 

562 

563@LogGroup.filter_registry.register('kms-key') 

564class KmsFilter(KmsRelatedFilter): 

565 RelatedIdsExpression = 'kmsKeyId' 

566 

567 

568@LogGroup.action_registry.register('set-encryption') 

569class EncryptLogGroup(BaseAction): 

570 """Encrypt/Decrypt a log group 

571 

572 :example: 

573 

574 .. code-block:: yaml 

575 

576 policies: 

577 - name: encrypt-log-group 

578 resource: log-group 

579 filters: 

580 - kmsKeyId: absent 

581 actions: 

582 - type: set-encryption 

583 kms-key: alias/mylogkey 

584 state: True 

585 

586 - name: decrypt-log-group 

587 resource: log-group 

588 filters: 

589 - kmsKeyId: kms:key:arn 

590 actions: 

591 - type: set-encryption 

592 state: False 

593 """ 

594 schema = type_schema( 

595 'set-encryption', 

596 **{'kms-key': {'type': 'string'}, 

597 'state': {'type': 'boolean'}}) 

598 permissions = ( 

599 'logs:AssociateKmsKey', 'logs:DisassociateKmsKey', 'kms:DescribeKey') 

600 

601 def validate(self): 

602 if not self.data.get('state', True): 

603 return self 

604 key = self.data.get('kms-key', '') 

605 if not key: 

606 raise ValueError('Must specify either a KMS key ARN or Alias') 

607 if 'alias/' not in key and ':key/' not in key: 

608 raise PolicyValidationError( 

609 "Invalid kms key format %s" % key) 

610 return self 

611 

612 def resolve_key(self, key): 

613 if not key: 

614 return 

615 

616 # Qualified arn for key 

617 if key.startswith('arn:') and ':key/' in key: 

618 return key 

619 

620 # Alias 

621 key = local_session( 

622 self.manager.session_factory).client( 

623 'kms').describe_key( 

624 KeyId=key)['KeyMetadata']['Arn'] 

625 return key 

626 

627 def process(self, resources): 

628 session = local_session(self.manager.session_factory) 

629 client = session.client('logs') 

630 

631 state = self.data.get('state', True) 

632 key = self.resolve_key(self.data.get('kms-key')) 

633 

634 for r in resources: 

635 try: 

636 if state: 

637 client.associate_kms_key( 

638 logGroupName=r['logGroupName'], kmsKeyId=key) 

639 else: 

640 client.disassociate_kms_key(logGroupName=r['logGroupName']) 

641 except client.exceptions.ResourceNotFoundException: 

642 continue 

643 

644 

645@LogGroup.action_registry.register('put-subscription-filter') 

646class SubscriptionFilter(BaseAction): 

647 """Create/Update a subscription filter and associate with a log group 

648 

649 :example: 

650 

651 .. code-block:: yaml 

652 

653 policies: 

654 - name: cloudwatch-put-subscription-filter 

655 resource: log-group 

656 actions: 

657 - type: put-subscription-filter 

658 filter_name: AllLambda 

659 filter_pattern: ip 

660 destination_arn: arn:aws:logs:us-east-1:1234567890:destination:lambda 

661 distribution: Random 

662 role_arn: "arn:aws:iam::{account_id}:role/testCrossAccountRole" 

663 """ 

664 schema = type_schema( 

665 'put-subscription-filter', 

666 filter_name={'type': 'string'}, 

667 filter_pattern={'type': 'string'}, 

668 destination_arn={'type': 'string'}, 

669 distribution={'enum': ['Random', 'ByLogStream']}, 

670 role_arn={'type': 'string'}, 

671 required=['filter_name', 'destination_arn']) 

672 permissions = ('logs:PutSubscriptionFilter',) 

673 

674 def process(self, resources): 

675 session = local_session(self.manager.session_factory) 

676 client = session.client('logs') 

677 params = dict( 

678 filterName=self.data.get('filter_name'), 

679 filterPattern=self.data.get('filter_pattern', ''), 

680 destinationArn=self.data.get('destination_arn'), 

681 distribution=self.data.get('distribution', 'ByLogStream')) 

682 

683 if self.data.get('role_arn'): 

684 params['roleArn'] = self.data.get('role_arn') 

685 

686 for r in resources: 

687 client.put_subscription_filter( 

688 logGroupName=r['logGroupName'], **params) 

689 

690 

691@resources.register("cloudwatch-dashboard") 

692class CloudWatchDashboard(QueryResourceManager): 

693 class resource_type(TypeInfo): 

694 service = "cloudwatch" 

695 enum_spec = ('list_dashboards', 'DashboardEntries', None) 

696 arn_type = "dashboard" 

697 arn = "DashboardArn" 

698 id = "DashboardName" 

699 name = "DashboardName" 

700 cfn_type = "AWS::CloudWatch::Dashboard" 

701 universal_taggable = object() 

702 global_resource = True 

703 

704 source_mapping = { 

705 "describe": DescribeWithResourceTags, 

706 } 

707 

708 

709@resources.register("destination") 

710class Destination(QueryResourceManager): 

711 class resource_type(TypeInfo): 

712 service = "logs" 

713 arn = "arn" 

714 arn_separator = ":" 

715 arn_type = "destination" 

716 cfn_type = "AWS::Logs::Destination" 

717 date = "creationTime" 

718 enum_spec = ('describe_destinations', 'destinations', None) 

719 id = name = "destinationName" 

720 universal_taggable = object() 

721 

722 retry = staticmethod(get_retry(('ServiceUnavailableException', 'OperationAbortedException'))) 

723 

724 source_mapping = { 

725 "describe": DescribeWithResourceTags, 

726 } 

727 

728 

729@Destination.filter_registry.register('cross-account') 

730class DestinationCrossAccount(CrossAccountAccessFilter): 

731 

732 permissions = ('logs:DescribeDestinations',) 

733 policy_attribute = 'accessPolicy' 

734 

735 

736@Destination.action_registry.register('delete') 

737class DestinationDelete(BaseAction): 

738 """Action to delete a destination 

739 

740 :example: 

741 

742 .. code-block:: yaml 

743 

744 policies: 

745 - name: delete-destination 

746 resource: aws.destination 

747 filters: 

748 - type: cross-account 

749 actions: 

750 - delete 

751 """ 

752 schema = type_schema('delete') 

753 

754 permissions = ('logs:DeleteDestination',) 

755 

756 def process(self, resources): 

757 client = local_session(self.manager.session_factory).client('logs') 

758 for r in resources: 

759 self.manager.retry( 

760 client.delete_destination, 

761 ignore_err_codes=('ResourceNotFoundException',), 

762 destinationName=r['destinationName'], 

763 ) 

764 

765 

766@resources.register("delivery-destination") 

767class DeliveryDestination(QueryResourceManager): 

768 class resource_type(TypeInfo): 

769 service = "logs" 

770 enum_spec = ('describe_delivery_destinations', 'deliveryDestinations', None) 

771 arn_type = "delivery-destination" 

772 arn_separator = ":" 

773 arn = "arn" 

774 id = name = "name" 

775 cfn_type = "AWS::Logs::DeliveryDestination" 

776 universal_taggable = object() 

777 

778 retry = staticmethod(get_retry( 

779 ('ConflictException', 'ServiceUnavailableException', 'ThrottlingException',) 

780 )) 

781 source_mapping = { 

782 "describe": DescribeWithResourceTags, 

783 } 

784 

785 

786@DeliveryDestination.filter_registry.register('cross-account') 

787class DeliveryDestinationCrossAccount(CrossAccountAccessFilter): 

788 

789 policy_attribute = 'c7n:Policy' 

790 permissions = ('logs:GetDeliveryDestinationPolicy',) 

791 

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

793 client = local_session(self.manager.session_factory).client('logs') 

794 

795 for r in resources: 

796 resp = self.manager.retry( 

797 client.get_delivery_destination_policy, 

798 deliveryDestinationName=r['name'], 

799 ignore_err_codes=('ResourceNotFoundException',) 

800 ) 

801 r[self.policy_attribute] = resp['policy'].get('deliveryDestinationPolicy', {}) 

802 return super().process(resources) 

803 

804 

805@DeliveryDestination.action_registry.register('delete') 

806class DeliveryDestinationDelete(BaseAction): 

807 """Action to delete a delivery destination 

808 

809 :example: 

810 

811 .. code-block:: yaml 

812 

813 policies: 

814 - name: delete-delivery-destination 

815 resource: aws.delivery-destination 

816 filters: 

817 - type: value 

818 key: deliveryDestinationType 

819 value: S3 

820 actions: 

821 - delete 

822 """ 

823 schema = type_schema('delete') 

824 

825 permissions = ('logs:DeleteDeliveryDestination',) 

826 

827 def process(self, resources): 

828 client = local_session(self.manager.session_factory).client('logs') 

829 for r in resources: 

830 self.manager.retry( 

831 client.delete_delivery_destination, 

832 ignore_err_codes=('ResourceNotFoundException',), 

833 name=r['name'], 

834 ) 

835 

836 

837@resources.register('cloudwatch-synthetics') 

838class SyntheticsCanary(QueryResourceManager): 

839 """AWS CloudWatch Synthetics Canary 

840 

841 Example: 

842 .. code-block:: yaml 

843 

844 policies: 

845 - name: stop-failed-canaries 

846 resource: aws.cloudwatch-synthetics 

847 filters: 

848 - State.CurrentStatus.State: FAILED 

849 actions: 

850 - type: delete 

851 """ 

852 

853 class resource_type(TypeInfo): 

854 service = 'synthetics' 

855 id = 'Id' 

856 name = 'Name' 

857 date = 'LastModified' 

858 arn_type = 'canary' 

859 dimension = 'CanaryName' 

860 cfn_type = 'AWS::Synthetics::Canary' 

861 enum_spec = ('describe_canaries', 'Canaries', None) 

862 universal_taggable = object() 

863 

864 def augment(self, resources): 

865 for r in resources: 

866 # AWS returns tags as a dict { "Key": "Value" } 

867 # Custodian expects [{"Key": k, "Value": v}, ...] 

868 r["Tags"] = [{"Key": k, "Value": v} for k, v in r["Tags"].items()] 

869 

870 return resources 

871 

872 

873@SyntheticsCanary.action_registry.register('start') 

874class StartCanary(BaseAction): 

875 schema = type_schema('start') 

876 

877 permissions = ('synthetics:StartCanary',) 

878 

879 def process(self, resources): 

880 client = local_session(self.manager.session_factory).client('synthetics') 

881 for r in resources: 

882 client.start_canary(Name=r['Name']) 

883 

884 

885@SyntheticsCanary.action_registry.register('stop') 

886class StopCanary(BaseAction): 

887 schema = type_schema('stop') 

888 

889 permissions = ('synthetics:StopCanary',) 

890 

891 def process(self, resources): 

892 """Stop all running resources""" 

893 client = local_session(self.manager.session_factory).client('synthetics') 

894 for r in resources: 

895 client.stop_canary(Name=r['Name']) 

896 

897 

898@SyntheticsCanary.action_registry.register('delete') 

899class DeleteCanary(BaseAction): 

900 schema = type_schema('delete') 

901 

902 permissions = ('synthetics:DeleteCanary',) 

903 

904 def process(self, resources): 

905 """Delete resources""" 

906 client = local_session(self.manager.session_factory).client('synthetics') 

907 for r in resources: 

908 client.delete_canary(Name=r['Name'])