@@ -564,12 +564,25 @@ def get_retained_resources_after_deletion(
564564 for resource in page .get ("StackResourceSummaries" , []):
565565 status = resource .get ("ResourceStatus" )
566566 resource_type = resource .get ("ResourceType" )
567+ physical_id = resource .get ("PhysicalResourceId" )
567568
568- # Only care about resources that weren't deleted
569- if status not in ["DELETE_COMPLETE" , "DELETE_IN_PROGRESS" ]:
569+ # Special handling for log groups - CF may mark DELETE_COMPLETE but they still exist
570+ if resource_type == "AWS::Logs::LogGroup" :
571+ if physical_id and self ._log_group_exists (physical_id ):
572+ resource_info = {
573+ "logical_id" : resource .get ("LogicalResourceId" ),
574+ "physical_id" : physical_id ,
575+ "type" : resource_type ,
576+ "status" : "EXISTS" , # Override CF status
577+ "status_reason" : "Verified to exist in CloudWatch" ,
578+ "stack" : stack ,
579+ }
580+ retained_resources ["log_groups" ].append (resource_info )
581+ # For other resources, use CF status
582+ elif status not in ["DELETE_COMPLETE" , "DELETE_IN_PROGRESS" ]:
570583 resource_info = {
571584 "logical_id" : resource .get ("LogicalResourceId" ),
572- "physical_id" : resource . get ( "PhysicalResourceId" ) ,
585+ "physical_id" : physical_id ,
573586 "type" : resource_type ,
574587 "status" : status ,
575588 "status_reason" : resource .get (
@@ -583,8 +596,6 @@ def get_retained_resources_after_deletion(
583596 retained_resources ["dynamodb_tables" ].append (
584597 resource_info
585598 )
586- elif resource_type == "AWS::Logs::LogGroup" :
587- retained_resources ["log_groups" ].append (resource_info )
588599 elif resource_type == "AWS::S3::Bucket" :
589600 retained_resources ["s3_buckets" ].append (resource_info )
590601 else :
@@ -627,6 +638,31 @@ def _get_nested_stacks(self, stack_name: str) -> List[str]:
627638
628639 return nested_stacks
629640
641+ def _log_group_exists (self , log_group_name : str ) -> bool :
642+ """
643+ Check if log group actually exists in CloudWatch
644+
645+ Args:
646+ log_group_name: Log group name to check
647+
648+ Returns:
649+ True if log group exists, False otherwise
650+ """
651+ logs = boto3 .client ("logs" , region_name = self .region )
652+
653+ try :
654+ response = logs .describe_log_groups (
655+ logGroupNamePrefix = log_group_name , limit = 1
656+ )
657+ # Check if exact match exists
658+ for group in response .get ("logGroups" , []):
659+ if group ["logGroupName" ] == log_group_name :
660+ return True
661+ return False
662+ except Exception as e :
663+ logger .warning (f"Error checking log group { log_group_name } : { e } " )
664+ return False
665+
630666 def cleanup_retained_resources (self , stack_identifier : str ) -> Dict :
631667 """
632668 Delete resources that CloudFormation retained
0 commit comments