aws_scanner.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. """
  2. AWS Resource Scanner Implementation
  3. This module implements the CloudProviderScanner interface for AWS,
  4. providing comprehensive resource scanning capabilities across all supported services.
  5. Requirements:
  6. - 4.3: Process multiple accounts/regions in parallel
  7. - 4.4: Scan all supported AWS services concurrently
  8. - 5.5: Retry with exponential backoff up to 3 times
  9. - 8.2: Record error details in task record
  10. """
  11. import boto3
  12. from botocore.exceptions import ClientError, BotoCoreError
  13. from concurrent.futures import ThreadPoolExecutor, as_completed
  14. from typing import List, Dict, Any, Optional, Callable
  15. import logging
  16. import traceback
  17. from app.scanners.base import CloudProviderScanner, ResourceData, ScanResult
  18. from app.scanners.credentials import AWSCredentialProvider, CredentialError
  19. from app.scanners.utils import retry_with_backoff
  20. logger = logging.getLogger(__name__)
  21. class AWSScanner(CloudProviderScanner):
  22. """
  23. AWS Resource Scanner implementation.
  24. Scans AWS resources across multiple regions and services using boto3,
  25. with support for parallel scanning and automatic retry with exponential backoff.
  26. """
  27. # All supported AWS services
  28. SUPPORTED_SERVICES = [
  29. 'vpc', 'subnet', 'route_table', 'internet_gateway', 'nat_gateway',
  30. 'security_group', 'vpc_endpoint', 'vpc_peering',
  31. 'customer_gateway', 'virtual_private_gateway', 'vpn_connection',
  32. 'ec2', 'elastic_ip',
  33. 'autoscaling', 'elb', 'target_group',
  34. 'rds', 'elasticache',
  35. 'eks', 'lambda', 's3', 's3_event_notification',
  36. 'cloudfront', 'route53', 'acm', 'waf',
  37. 'sns', 'cloudwatch', 'eventbridge', 'cloudtrail', 'config'
  38. ]
  39. # Global services (not region-specific)
  40. GLOBAL_SERVICES = ['cloudfront', 'route53', 'waf', 's3', 's3_event_notification', 'cloudtrail']
  41. # Maximum workers for parallel scanning
  42. MAX_WORKERS = 10
  43. def __init__(self, credential_provider: AWSCredentialProvider):
  44. """
  45. Initialize the AWS Scanner.
  46. Args:
  47. credential_provider: AWSCredentialProvider instance for authentication
  48. """
  49. self.credential_provider = credential_provider
  50. self._account_id: Optional[str] = None
  51. @property
  52. def provider_name(self) -> str:
  53. return 'AWS'
  54. @property
  55. def supported_services(self) -> List[str]:
  56. return self.SUPPORTED_SERVICES.copy()
  57. @property
  58. def global_services(self) -> List[str]:
  59. return self.GLOBAL_SERVICES.copy()
  60. def get_credentials(self, credential_config: Dict[str, Any]) -> boto3.Session:
  61. """Get boto3 Session from credential configuration"""
  62. return self.credential_provider.get_session()
  63. def list_regions(self) -> List[str]:
  64. """List all available AWS regions"""
  65. try:
  66. session = self.credential_provider.get_session(region_name='us-east-1')
  67. ec2_client = session.client('ec2')
  68. response = ec2_client.describe_regions()
  69. return [region['RegionName'] for region in response['Regions']]
  70. except Exception as e:
  71. logger.error(f"Failed to list regions: {str(e)}")
  72. # Return default regions if API call fails
  73. return [
  74. 'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2',
  75. 'eu-west-1', 'eu-west-2', 'eu-west-3', 'eu-central-1',
  76. 'ap-northeast-1', 'ap-northeast-2', 'ap-southeast-1', 'ap-southeast-2',
  77. 'ap-south-1', 'sa-east-1', 'ca-central-1'
  78. ]
  79. def validate_credentials(self) -> bool:
  80. """Validate AWS credentials"""
  81. return self.credential_provider.validate()
  82. def get_account_id(self) -> str:
  83. """Get AWS account ID"""
  84. if self._account_id:
  85. return self._account_id
  86. self._account_id = self.credential_provider.get_account_id()
  87. return self._account_id
  88. def scan_resources(
  89. self,
  90. regions: List[str],
  91. services: Optional[List[str]] = None,
  92. progress_callback: Optional[Callable[[int, int, str], None]] = None
  93. ) -> ScanResult:
  94. """
  95. Scan AWS resources across specified regions and services.
  96. Args:
  97. regions: List of regions to scan
  98. services: Optional list of services to scan (None = all)
  99. progress_callback: Optional callback for progress updates
  100. Returns:
  101. ScanResult with all discovered resources
  102. """
  103. result = ScanResult(success=True)
  104. account_id = self.get_account_id()
  105. # Determine services to scan
  106. services_to_scan = services if services else self.SUPPORTED_SERVICES
  107. # Separate global and regional services
  108. global_services = [s for s in services_to_scan if s in self.GLOBAL_SERVICES]
  109. regional_services = [s for s in services_to_scan if s not in self.GLOBAL_SERVICES]
  110. # ACM needs special handling: scan selected regions + us-east-1 (for CloudFront)
  111. # but avoid duplicate if us-east-1 is already selected
  112. acm_regions = list(regions) # Copy the regions list
  113. if 'acm' in regional_services and 'us-east-1' not in acm_regions:
  114. acm_regions.append('us-east-1')
  115. # Calculate total tasks for progress tracking
  116. # Add extra task if ACM needs us-east-1 scan
  117. acm_extra_task = 1 if ('acm' in regional_services and 'us-east-1' not in regions) else 0
  118. total_tasks = len(global_services) + (len(regional_services) * len(regions)) + acm_extra_task
  119. completed_tasks = 0
  120. # Scan global services first (only once, not per region)
  121. if global_services:
  122. logger.info(f"Scanning {len(global_services)} global services")
  123. global_results = self._scan_services_parallel(
  124. account_id=account_id,
  125. region='us-east-1', # Global services use us-east-1
  126. services=global_services,
  127. is_global=True
  128. )
  129. for service, resources in global_results['resources'].items():
  130. for resource in resources:
  131. result.add_resource(service, resource)
  132. for error in global_results['errors']:
  133. result.add_error(**error)
  134. completed_tasks += len(global_services)
  135. if progress_callback:
  136. progress_callback(completed_tasks, total_tasks, "Completed global services scan")
  137. # Scan regional services in parallel across regions
  138. if regional_services and regions:
  139. logger.info(f"Scanning {len(regional_services)} services across {len(regions)} regions")
  140. with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor:
  141. futures = {}
  142. for region in regions:
  143. future = executor.submit(
  144. self._scan_services_parallel,
  145. account_id=account_id,
  146. region=region,
  147. services=regional_services,
  148. is_global=False
  149. )
  150. futures[future] = region
  151. for future in as_completed(futures):
  152. region = futures[future]
  153. try:
  154. region_results = future.result()
  155. for service, resources in region_results['resources'].items():
  156. for resource in resources:
  157. result.add_resource(service, resource)
  158. for error in region_results['errors']:
  159. result.add_error(**error)
  160. completed_tasks += len(regional_services)
  161. if progress_callback:
  162. progress_callback(
  163. completed_tasks, total_tasks,
  164. f"Completed scan for region: {region}"
  165. )
  166. except Exception as e:
  167. logger.error(f"Failed to scan region {region}: {str(e)}")
  168. result.add_error(
  169. service='all',
  170. region=region,
  171. error=f"Region scan failed: {str(e)}",
  172. details=None
  173. )
  174. completed_tasks += len(regional_services)
  175. # Scan ACM in us-east-1 if not already included in regions
  176. if 'acm' in regional_services and 'us-east-1' not in regions:
  177. logger.info("Scanning ACM certificates in us-east-1 (for CloudFront)")
  178. acm_results = self._scan_services_parallel(
  179. account_id=account_id,
  180. region='us-east-1',
  181. services=['acm'],
  182. is_global=False
  183. )
  184. for service, resources in acm_results['resources'].items():
  185. for resource in resources:
  186. result.add_resource(service, resource)
  187. for error in acm_results['errors']:
  188. result.add_error(**error)
  189. completed_tasks += 1
  190. if progress_callback:
  191. progress_callback(completed_tasks, total_tasks, "Completed ACM scan in us-east-1")
  192. # Set metadata
  193. result.metadata = {
  194. 'account_id': account_id,
  195. 'regions_scanned': regions,
  196. 'services_scanned': services_to_scan,
  197. 'total_resources': sum(len(r) for r in result.resources.values()),
  198. 'total_errors': len(result.errors)
  199. }
  200. # Mark as failed if there were critical errors
  201. if result.errors and not result.resources:
  202. result.success = False
  203. return result
  204. def _scan_services_parallel(
  205. self,
  206. account_id: str,
  207. region: str,
  208. services: List[str],
  209. is_global: bool = False
  210. ) -> Dict[str, Any]:
  211. """
  212. Scan multiple services in parallel within a single region.
  213. Args:
  214. account_id: AWS account ID
  215. region: Region to scan
  216. services: List of services to scan
  217. is_global: Whether these are global services
  218. Returns:
  219. Dictionary with 'resources' and 'errors' keys
  220. """
  221. resources: Dict[str, List[ResourceData]] = {}
  222. errors: List[Dict[str, Any]] = []
  223. # Get session for this region
  224. try:
  225. session = self.credential_provider.get_session(region_name=region)
  226. except CredentialError as e:
  227. logger.error(f"Failed to get session for region {region}: {str(e)}")
  228. return {
  229. 'resources': {},
  230. 'errors': [{
  231. 'service': 'all',
  232. 'region': region,
  233. 'error': str(e),
  234. 'details': None
  235. }]
  236. }
  237. # Scan services in parallel
  238. with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor:
  239. futures = {}
  240. for service in services:
  241. scanner_method = self._get_scanner_method(service)
  242. if scanner_method:
  243. future = executor.submit(
  244. self._scan_service_safe,
  245. scanner_method=scanner_method,
  246. session=session,
  247. account_id=account_id,
  248. region='global' if is_global else region,
  249. service=service
  250. )
  251. futures[future] = service
  252. for future in as_completed(futures):
  253. service = futures[future]
  254. try:
  255. service_result = future.result()
  256. if service_result['resources']:
  257. resources[service] = service_result['resources']
  258. if service_result['error']:
  259. errors.append(service_result['error'])
  260. except Exception as e:
  261. logger.error(f"Unexpected error scanning {service}: {str(e)}")
  262. errors.append({
  263. 'service': service,
  264. 'region': region,
  265. 'error': str(e),
  266. 'details': None
  267. })
  268. return {'resources': resources, 'errors': errors}
  269. def _scan_service_safe(
  270. self,
  271. scanner_method: Callable,
  272. session: boto3.Session,
  273. account_id: str,
  274. region: str,
  275. service: str
  276. ) -> Dict[str, Any]:
  277. """
  278. Safely scan a single service, catching and logging errors.
  279. Requirements:
  280. - 8.2: Record error details in task record (via error dict)
  281. Returns:
  282. Dictionary with 'resources' and 'error' keys
  283. """
  284. try:
  285. resources = scanner_method(session, account_id, region)
  286. return {'resources': resources, 'error': None}
  287. except ClientError as e:
  288. error_code = e.response.get('Error', {}).get('Code', 'Unknown')
  289. error_message = e.response.get('Error', {}).get('Message', str(e))
  290. stack_trace = traceback.format_exc()
  291. logger.warning(f"Error scanning {service} in {region}: {error_code} - {error_message}")
  292. return {
  293. 'resources': [],
  294. 'error': {
  295. 'service': service,
  296. 'region': region,
  297. 'error': f"{error_code}: {error_message}",
  298. 'error_type': 'ClientError',
  299. 'details': {
  300. 'error_code': error_code,
  301. 'stack_trace': stack_trace
  302. }
  303. }
  304. }
  305. except Exception as e:
  306. stack_trace = traceback.format_exc()
  307. logger.error(f"Unexpected error scanning {service} in {region}: {str(e)}")
  308. return {
  309. 'resources': [],
  310. 'error': {
  311. 'service': service,
  312. 'region': region,
  313. 'error': str(e),
  314. 'error_type': type(e).__name__,
  315. 'details': {
  316. 'stack_trace': stack_trace
  317. }
  318. }
  319. }
  320. def _get_scanner_method(self, service: str) -> Optional[Callable]:
  321. """Get the scanner method for a specific service"""
  322. scanner_methods = {
  323. 'vpc': self._scan_vpcs,
  324. 'subnet': self._scan_subnets,
  325. 'route_table': self._scan_route_tables,
  326. 'internet_gateway': self._scan_internet_gateways,
  327. 'nat_gateway': self._scan_nat_gateways,
  328. 'security_group': self._scan_security_groups,
  329. 'vpc_endpoint': self._scan_vpc_endpoints,
  330. 'vpc_peering': self._scan_vpc_peering,
  331. 'customer_gateway': self._scan_customer_gateways,
  332. 'virtual_private_gateway': self._scan_virtual_private_gateways,
  333. 'vpn_connection': self._scan_vpn_connections,
  334. 'ec2': self._scan_ec2_instances,
  335. 'elastic_ip': self._scan_elastic_ips,
  336. 'autoscaling': self._scan_autoscaling_groups,
  337. 'elb': self._scan_load_balancers,
  338. 'target_group': self._scan_target_groups,
  339. 'rds': self._scan_rds_instances,
  340. 'elasticache': self._scan_elasticache_clusters,
  341. 'eks': self._scan_eks_clusters,
  342. 'lambda': self._scan_lambda_functions,
  343. 's3': self._scan_s3_buckets,
  344. 's3_event_notification': self._scan_s3_event_notifications,
  345. 'cloudfront': self._scan_cloudfront_distributions,
  346. 'route53': self._scan_route53_hosted_zones,
  347. 'acm': self._scan_acm_certificates,
  348. 'waf': self._scan_waf_web_acls,
  349. 'sns': self._scan_sns_topics,
  350. 'cloudwatch': self._scan_cloudwatch_log_groups,
  351. 'eventbridge': self._scan_eventbridge_rules,
  352. 'cloudtrail': self._scan_cloudtrail_trails,
  353. 'config': self._scan_config_recorders,
  354. }
  355. return scanner_methods.get(service)
  356. # Helper method to get resource name from tags
  357. def _get_name_from_tags(self, tags: List[Dict[str, str]], default: str = '') -> str:
  358. """Extract Name tag value from tags list"""
  359. if not tags:
  360. return default
  361. for tag in tags:
  362. if tag.get('Key') == 'Name':
  363. return tag.get('Value', default)
  364. return default
  365. # ==================== VPC Related Scanners ====================
  366. def _scan_vpcs(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  367. """Scan VPCs"""
  368. from app.scanners.services.vpc import VPCServiceScanner
  369. return VPCServiceScanner.scan_vpcs(session, account_id, region)
  370. def _scan_subnets(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  371. """Scan Subnets"""
  372. from app.scanners.services.vpc import VPCServiceScanner
  373. return VPCServiceScanner.scan_subnets(session, account_id, region)
  374. def _scan_route_tables(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  375. """Scan Route Tables"""
  376. from app.scanners.services.vpc import VPCServiceScanner
  377. return VPCServiceScanner.scan_route_tables(session, account_id, region)
  378. def _scan_internet_gateways(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  379. """Scan Internet Gateways"""
  380. from app.scanners.services.vpc import VPCServiceScanner
  381. return VPCServiceScanner.scan_internet_gateways(session, account_id, region)
  382. def _scan_nat_gateways(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  383. """Scan NAT Gateways"""
  384. from app.scanners.services.vpc import VPCServiceScanner
  385. return VPCServiceScanner.scan_nat_gateways(session, account_id, region)
  386. def _scan_security_groups(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  387. """Scan Security Groups"""
  388. from app.scanners.services.vpc import VPCServiceScanner
  389. return VPCServiceScanner.scan_security_groups(session, account_id, region)
  390. def _scan_vpc_endpoints(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  391. """Scan VPC Endpoints"""
  392. from app.scanners.services.vpc import VPCServiceScanner
  393. return VPCServiceScanner.scan_vpc_endpoints(session, account_id, region)
  394. def _scan_vpc_peering(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  395. """Scan VPC Peering Connections"""
  396. from app.scanners.services.vpc import VPCServiceScanner
  397. return VPCServiceScanner.scan_vpc_peering(session, account_id, region)
  398. def _scan_customer_gateways(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  399. """Scan Customer Gateways"""
  400. from app.scanners.services.vpc import VPCServiceScanner
  401. return VPCServiceScanner.scan_customer_gateways(session, account_id, region)
  402. def _scan_virtual_private_gateways(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  403. """Scan Virtual Private Gateways"""
  404. from app.scanners.services.vpc import VPCServiceScanner
  405. return VPCServiceScanner.scan_virtual_private_gateways(session, account_id, region)
  406. def _scan_vpn_connections(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  407. """Scan VPN Connections"""
  408. from app.scanners.services.vpc import VPCServiceScanner
  409. return VPCServiceScanner.scan_vpn_connections(session, account_id, region)
  410. # ==================== EC2 Related Scanners ====================
  411. def _scan_ec2_instances(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  412. """Scan EC2 Instances"""
  413. from app.scanners.services.ec2 import EC2ServiceScanner
  414. return EC2ServiceScanner.scan_ec2_instances(session, account_id, region)
  415. def _scan_elastic_ips(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  416. """Scan Elastic IPs"""
  417. from app.scanners.services.ec2 import EC2ServiceScanner
  418. return EC2ServiceScanner.scan_elastic_ips(session, account_id, region)
  419. # ==================== Auto Scaling and ELB Scanners ====================
  420. def _scan_autoscaling_groups(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  421. """Scan Auto Scaling Groups"""
  422. from app.scanners.services.elb import ELBServiceScanner
  423. return ELBServiceScanner.scan_autoscaling_groups(session, account_id, region)
  424. def _scan_load_balancers(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  425. """Scan Load Balancers"""
  426. from app.scanners.services.elb import ELBServiceScanner
  427. return ELBServiceScanner.scan_load_balancers(session, account_id, region)
  428. def _scan_target_groups(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  429. """Scan Target Groups"""
  430. from app.scanners.services.elb import ELBServiceScanner
  431. return ELBServiceScanner.scan_target_groups(session, account_id, region)
  432. # ==================== Database Service Scanners ====================
  433. def _scan_rds_instances(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  434. """Scan RDS DB Instances"""
  435. from app.scanners.services.database import DatabaseServiceScanner
  436. return DatabaseServiceScanner.scan_rds_instances(session, account_id, region)
  437. def _scan_elasticache_clusters(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  438. """Scan ElastiCache Clusters"""
  439. from app.scanners.services.database import DatabaseServiceScanner
  440. return DatabaseServiceScanner.scan_elasticache_clusters(session, account_id, region)
  441. # ==================== Compute and Storage Service Scanners ====================
  442. def _scan_eks_clusters(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  443. """Scan EKS Clusters"""
  444. from app.scanners.services.compute import ComputeServiceScanner
  445. return ComputeServiceScanner.scan_eks_clusters(session, account_id, region)
  446. def _scan_lambda_functions(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  447. """Scan Lambda Functions"""
  448. from app.scanners.services.compute import ComputeServiceScanner
  449. return ComputeServiceScanner.scan_lambda_functions(session, account_id, region)
  450. def _scan_s3_buckets(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  451. """Scan S3 Buckets"""
  452. from app.scanners.services.compute import ComputeServiceScanner
  453. return ComputeServiceScanner.scan_s3_buckets(session, account_id, region)
  454. def _scan_s3_event_notifications(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  455. """Scan S3 Event Notifications"""
  456. from app.scanners.services.compute import ComputeServiceScanner
  457. return ComputeServiceScanner.scan_s3_event_notifications(session, account_id, region)
  458. # ==================== Global Service Scanners ====================
  459. def _scan_cloudfront_distributions(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  460. """Scan CloudFront Distributions"""
  461. from app.scanners.services.global_services import GlobalServiceScanner
  462. return GlobalServiceScanner.scan_cloudfront_distributions(session, account_id, region)
  463. def _scan_route53_hosted_zones(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  464. """Scan Route 53 Hosted Zones"""
  465. from app.scanners.services.global_services import GlobalServiceScanner
  466. return GlobalServiceScanner.scan_route53_hosted_zones(session, account_id, region)
  467. def _scan_acm_certificates(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  468. """Scan ACM Certificates"""
  469. from app.scanners.services.global_services import GlobalServiceScanner
  470. return GlobalServiceScanner.scan_acm_certificates(session, account_id, region)
  471. def _scan_waf_web_acls(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  472. """Scan WAF Web ACLs"""
  473. from app.scanners.services.global_services import GlobalServiceScanner
  474. return GlobalServiceScanner.scan_waf_web_acls(session, account_id, region)
  475. # ==================== Monitoring and Management Service Scanners ====================
  476. def _scan_sns_topics(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  477. """Scan SNS Topics"""
  478. from app.scanners.services.monitoring import MonitoringServiceScanner
  479. return MonitoringServiceScanner.scan_sns_topics(session, account_id, region)
  480. def _scan_cloudwatch_log_groups(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  481. """Scan CloudWatch Log Groups"""
  482. from app.scanners.services.monitoring import MonitoringServiceScanner
  483. return MonitoringServiceScanner.scan_cloudwatch_log_groups(session, account_id, region)
  484. def _scan_eventbridge_rules(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  485. """Scan EventBridge Rules"""
  486. from app.scanners.services.monitoring import MonitoringServiceScanner
  487. return MonitoringServiceScanner.scan_eventbridge_rules(session, account_id, region)
  488. def _scan_cloudtrail_trails(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  489. """Scan CloudTrail Trails"""
  490. from app.scanners.services.monitoring import MonitoringServiceScanner
  491. return MonitoringServiceScanner.scan_cloudtrail_trails(session, account_id, region)
  492. def _scan_config_recorders(self, session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  493. """Scan AWS Config Recorders"""
  494. from app.scanners.services.monitoring import MonitoringServiceScanner
  495. return MonitoringServiceScanner.scan_config_recorders(session, account_id, region)