Skip to content

Commit

Permalink
update summary
Browse files Browse the repository at this point in the history
  • Loading branch information
badra001 committed Jan 15, 2026
1 parent d007b1b commit 5af5d3d
Showing 1 changed file with 64 additions and 28 deletions.
92 changes: 64 additions & 28 deletions local-app/python-tools/cross-organization/tag-checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
import sys
import time
import re
import os
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed
from botocore.exceptions import ClientError
from tqdm import tqdm

__version__ = "1.1.7"
__version__ = "1.1.9"

def get_args():
parser = argparse.ArgumentParser(description=f"AWS Org Tag Scanner v{__version__}")
Expand Down Expand Up @@ -70,15 +71,20 @@ def scan_account(account, management_session, role_name, partition, tag_keys, re
findings = []
global_resources = set()
global_tags_found = set()
regional_data = []
regional_metrics = []

# Bar alignment fixed with bar_width + 1
label = f"{acc_id} {alias}".ljust(bar_width + 1)
# UI Alignment: Fixed size title with lane ID padding
label = f"{acc_id} {alias}".ljust(bar_width)
pbar = tqdm(total=len(tag_keys), desc=f"Lane {lane_id:02d} | {label}",
position=lane_id, leave=False, bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt}')

for key in tag_keys:
for r in active_regions:
r_start = time.perf_counter()
r_hits = 0
r_res_found = set()
r_tags_found = set()

client = m_session.client('resourcegroupstaggingapi', region_name=r)
try:
paginator = client.get_paginator('get_resources')
Expand All @@ -92,25 +98,33 @@ def scan_account(account, management_session, role_name, partition, tag_keys, re
})
global_resources.add(arn)
global_tags_found.add(key)
r_res_found.add(arn)
r_tags_found.add(key)
r_hits += 1
except ClientError as e:
if "Throttling" in str(e): time.sleep(1)

r_elapsed = round(time.perf_counter() - r_start, 4)
r_entry = next((m for m in regional_metrics if m['region'] == r), None)
if not r_entry:
regional_metrics.append({
"region": r, "hits": r_hits, "unique_resources": len(r_res_found),
"tags_found_count": len(r_tags_found), "tags_found_list": sorted(list(r_tags_found)),
"tags_not_found_count": len(tag_keys) - len(r_tags_found), "elapsed_sec": r_elapsed
})
else:
r_entry['hits'] += r_hits
# Logic update for resource uniqueness within the region
current_tags = set(r_entry['tags_found_list']) | r_tags_found
r_entry['tags_found_list'] = sorted(list(current_tags))
r_entry['tags_found_count'] = len(current_tags)
r_entry['tags_not_found_count'] = len(tag_keys) - len(current_tags)
r_entry['elapsed_sec'] = round(r_entry['elapsed_sec'] + r_elapsed, 4)

pbar.update(1)

pbar.close()

for r in active_regions:
r_findings = [f for f in findings if f['region'] == r]
r_tags = sorted(list(set([f['tag_name'] for f in r_findings])))
r_res = set([f['arn'] for f in r_findings])
regional_data.append({
"region": r,
"hits": len(r_findings),
"unique_resources": len(r_res),
"tags_found_count": len(r_tags),
"tags_found_list": r_tags,
"tags_not_found_count": len(tag_keys) - len(r_tags)
})

metrics = {
"global": {
"hits": len(findings),
Expand All @@ -120,14 +134,16 @@ def scan_account(account, management_session, role_name, partition, tag_keys, re
"tags_not_found_count": len(tag_keys) - len(global_tags_found),
"elapsed_sec": round(time.time() - acc_start, 2)
},
"regions": regional_data
"regions": regional_metrics
}
return findings, acc_id, alias, metrics, "Success"

def main():
args = get_args()
cmd_line = " ".join(sys.argv)
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
start_overall = time.time()
start_iso = datetime.now().isoformat()
start_ts = time.time()

session = boto3.Session(profile_name=args.profile, region_name=args.region)
org = session.client('organizations', region_name=args.region)
Expand All @@ -150,15 +166,13 @@ def main():

if args.limit > 0: all_accs = all_accs[:args.limit]

# Calculate bar width for alignment
# Length of ID (12) + Space (1) + Name/Alias (max)
max_label_len = max([12 + 1 + len(a['Name']) for a in all_accs]) if all_accs else 40
# UI Alignment with +1 padding fix
max_label_len = max([12 + 1 + len(a['Name']) for a in all_accs]) + 1 if all_accs else 40

print(f"\n{'='*85}\nAWS TAG CHECKER v{__version__}\n{'='*85}")
print(f"Profile: {args.profile} | Workers: {args.max_workers} | Targets: {len(all_accs)}")

all_findings = []
summary_data = []
account_results = []
overall_pbar = tqdm(total=len(all_accs), desc="Total Org Progress", position=0)

with ThreadPoolExecutor(max_workers=args.max_workers) as executor:
Expand All @@ -170,7 +184,7 @@ def main():
res, acc_id, alias, m, status = future.result()
if status == "Success":
all_findings.extend(res)
summary_data.append({
account_results.append({
"account_id": acc_id, "alias": alias,
"global_metrics": m["global"],
"regional_metrics": m["regions"]
Expand All @@ -182,17 +196,39 @@ def main():
overall_pbar.close()
print("\n" * (args.max_workers + 1))

# Final Summary Construction
total_hits = sum(a['global_metrics']['hits'] for a in account_results)
total_res = len(set(f['arn'] for f in all_findings))
all_found_keys = set(f['tag_name'] for f in all_findings)

output_summary = {
"summary": {
"version": __version__,
"command_line": cmd_line,
"aws_accounts_scanned": len(account_results),
"execution_start": start_iso,
"execution_end": datetime.now().isoformat(),
"elapsed_sec_total": round(time.time() - start_ts, 2),
"threads": args.max_workers,
"total_hits": total_hits,
"total_unique_resources": total_res,
"total_tags_found_count": len(all_found_keys),
"total_tags_not_found_count": len(tag_keys) - len(all_found_keys)
},
"accounts": account_results
}

sum_file = f"{args.output}_summary_{ts}.json"
fin_file = f"{args.output}_findings_{ts}.csv"

with open(sum_file, 'w') as f: json.dump(summary_data, f, indent=4)
with open(sum_file, 'w') as f: json.dump(output_summary, f, indent=4)
if all_findings:
with open(fin_file, 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=all_findings[0].keys())
writer.writeheader(); writer.writerows(all_findings)

print(f"[+] Scan Complete in {round(time.time()-start_overall, 2)}s")
print(f"[+] Summary: {sum_file} | Findings: {fin_file}")
print(f"[+] Summary: {sum_file}")
print(f"[+] Findings: {fin_file}")

if __name__ == "__main__":
main()

0 comments on commit 5af5d3d

Please sign in to comment.