Understanding the log file format, naming conventions, and how to parse audit results
The AWS Security Group Auditor creates a detailed log file for each execution, capturing all findings about security group associations across your AWS account.
The log file is created in the current working directory where you run the script:
# If you run the script from /home/user/audits/cd /home/user/audits/python check_sg_usage.py# Log file will be created at:# /home/user/audits/123456789012_sg_log.txt
# Source: check_sg_usage.py:44-46with open(output_file, 'w') as log_file: print_both(f"\nVerificación de uso de grupos de seguridad para la cuenta AWS: {account_id}", log_file) # ... rest of the audit logic
Find the results section and extract unused security group IDs:
# Using grep and sedgrep "no tienen recursos asociados" 123456789012_sg_log.txt | \ sed 's/.*asociados: //' | \ tr ',' '\n' | \ sed 's/^ //g'
# Using Pythonimport rewith open('123456789012_sg_log.txt', 'r') as f: content = f.read() match = re.search(r'no tienen recursos asociados: (.+)', content) if match: unused_sgs = [sg.strip() for sg in match.group(1).split(',')] print(unused_sgs)
Convert the log file into a structured CSV format:
import csvimport redef parse_log_to_csv(log_file, output_csv): security_groups = {} current_sg = None with open(log_file, 'r') as f: for line in f: # Match security group header sg_match = re.match(r'Revisando el grupo de seguridad.*?: (sg-[a-f0-9]+) \((.+)\)', line) if sg_match: current_sg = sg_match.group(1) security_groups[current_sg] = { 'id': current_sg, 'description': sg_match.group(2), 'resources': [] } # Match resource associations elif line.startswith('\t') and current_sg: resource = line.strip() security_groups[current_sg]['resources'].append(resource) # Write to CSV with open(output_csv, 'w', newline='') as csvfile: writer = csv.writer(csvfile) writer.writerow(['Security Group ID', 'Description', 'Resource Count', 'Resources']) for sg_id, data in security_groups.items(): resources_str = '; '.join(data['resources']) writer.writerow([ data['id'], data['description'], len(data['resources']), resources_str ])parse_log_to_csv('123456789012_sg_log.txt', 'sg_audit_report.csv')
import refrom collections import Counterdef count_resources_by_service(log_file): services = Counter() with open(log_file, 'r') as f: for line in f: if line.startswith('\t'): # Extract service type from resource line if 'EC2 asociada' in line: services['EC2'] += 1 elif 'ELB Clásico' in line: services['Classic ELB'] += 1 elif 'ALB/NLB' in line: services['ALB/NLB'] += 1 elif 'RDS Asociada' in line: services['RDS'] += 1 elif 'ECS Servicio' in line: services['ECS'] += 1 elif 'EKS Clúster' in line: services['EKS'] += 1 elif 'ElastiCache' in line: services['ElastiCache'] += 1 # Add more service patterns as needed return servicesservices = count_resources_by_service('123456789012_sg_log.txt')for service, count in services.most_common(): print(f"{service}: {count}")
import boto3import redef alert_if_unused_sgs(log_file, sns_topic_arn): sns = boto3.client('sns') with open(log_file, 'r') as f: content = f.read() match = re.search(r'no tienen recursos asociados: (.+)', content) if match: unused_sgs = match.group(1) sns.publish( TopicArn=sns_topic_arn, Subject='Unused Security Groups Found', Message=f'The following security groups are unused:\n{unused_sgs}' )alert_if_unused_sgs( '123456789012_sg_log.txt', 'arn:aws:sns:us-east-1:123456789012:security-alerts')