sync: auto-sync from HOWARD-HOME at 2026-07-04 10:45:23
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-07-04 10:45:23
This commit is contained in:
@@ -24,11 +24,16 @@ def syncro_customer(hostname):
|
||||
c=a.get("customer") or {}
|
||||
return c.get("business_name") or c.get("fullname")
|
||||
return None
|
||||
import urllib.parse
|
||||
import urllib.parse,re
|
||||
def norm(s):
|
||||
s=(s or "").lower().strip()
|
||||
s=re.sub(r'[.,]',' ',s)
|
||||
s=re.sub(r'\b(llc|inc|incorporated|corp|corporation|company|co|ltd|the)\b',' ',s)
|
||||
return re.sub(r'\s+',' ',s).strip()
|
||||
agents=rget("/api/agents")
|
||||
staging=[a for a in agents if a.get("client_name")==STAGING_CLIENT]
|
||||
print(f"{len(staging)} agents in Staging")
|
||||
# GuruRMM client -> a target site id (prefer 'Main'/'Main Office', else first)
|
||||
# GuruRMM client -> a target site id (prefer 'Main'/'Main Office', else first); keyed by normalized name
|
||||
clients=rget("/api/clients")
|
||||
name2site={}
|
||||
for c in clients:
|
||||
@@ -36,11 +41,11 @@ for c in clients:
|
||||
sites=rget(f"/api/clients/{c['id']}/sites")
|
||||
if not sites: continue
|
||||
main=next((s for s in sites if s["name"].lower() in ("main","main office","office")), sites[0])
|
||||
name2site[c["name"].lower()]=main["id"]
|
||||
name2site[norm(c["name"])]=main["id"]
|
||||
moved=0; unmatched=[]
|
||||
for a in staging:
|
||||
cust=syncro_customer(a["hostname"])
|
||||
site=name2site.get((cust or "").lower())
|
||||
site=name2site.get(norm(cust))
|
||||
if cust and site:
|
||||
if not DRY: move(a["id"],site)
|
||||
moved+=1; print(f" {a['hostname']} -> '{cust}'")
|
||||
|
||||
64
projects/gps-rmm-audit/tools/rebuild-and-push.py
Normal file
64
projects/gps-rmm-audit/tools/rebuild-and-push.py
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
# rebuild-and-push.py — rebuild the "machines not in GuruRMM" list using the CORRECT
|
||||
# ScreenConnect online signal (ActiveConnections ProcessType==2 = guest agent connected;
|
||||
# GuestInfoUpdateTime for recency), NOT Syncro's stale last_synced_at. Then push the
|
||||
# generic STAGING installer to every machine that is ONLINE right now (it will actually
|
||||
# land instead of queuing). Machines enroll into Staging -> reassign-staging.py sorts them.
|
||||
#
|
||||
# Env: RMM, TOK (GuruRMM), SK (Syncro), SC_SECRET (ScreenConnect). --dry = no pushes.
|
||||
import json,urllib.request,ssl,os,sys,base64,datetime,urllib.parse
|
||||
sys.stdout.reconfigure(encoding='utf-8',errors='replace')
|
||||
RMM=os.environ["RMM"]; TOK=os.environ["TOK"]; SK=os.environ["SK"]; SEC=os.environ["SC_SECRET"]
|
||||
ctx=ssl.create_default_context(); DRY="--dry" in sys.argv
|
||||
SB="https://computerguru.screenconnect.com/App_Extensions/2d558935-686a-4bd0-9991-07539f5fe749/Service.ashx"
|
||||
STAGING_ONELINER="irm 'https://rmm.azcomputerguru.com/install/DARK-STORM-3150/windows'|iex"
|
||||
ENC=base64.b64encode(STAGING_ONELINER.encode("utf-16-le")).decode()
|
||||
PUSHCMD=f"powershell -NoProfile -ExecutionPolicy Bypass -EncodedCommand {ENC}"
|
||||
def scp(m,b):
|
||||
req=urllib.request.Request(f"{SB}/{m}",data=json.dumps(b).encode(),method="POST",headers={"CTRLAuthHeader":SEC,"Origin":"https://computerguru.screenconnect.com","Content-Type":"application/json"})
|
||||
try:return json.loads(urllib.request.urlopen(req,context=ctx,timeout=25).read().decode("utf-8","replace"))
|
||||
except Exception as e:return {"__err":str(e)}
|
||||
def rget(p):
|
||||
return json.loads("".join(c for c in urllib.request.urlopen(urllib.request.Request(f"{RMM}{p}",headers={"Authorization":f"Bearer {TOK}"}),context=ctx,timeout=30).read().decode("utf-8","replace") if ord(c)>=32 or c in "\t\n\r"))
|
||||
def assets(cid):
|
||||
raw=urllib.request.urlopen(urllib.request.Request(f"https://computerguru.syncromsp.com/api/v1/customer_assets?customer_id={cid}&per_page=100&api_key={SK}"),context=ctx,timeout=25).read()
|
||||
raw="".join(chr(b) for b in raw if b>=32 or b in (9,10,13))
|
||||
return [a["name"] for a in json.loads(raw).get("assets",[]) if a.get("asset_type")=="Syncro Device" and a.get("name")]
|
||||
def online(s): return any(c.get("ProcessType")==2 for c in (s.get("ActiveConnections") or []))
|
||||
def recency_days(s):
|
||||
for f in ("GuestInfoUpdateTime","LastGuestConnectedEventTime"):
|
||||
t=s.get(f)
|
||||
if t and not str(t).startswith("0001"):
|
||||
try: return (datetime.datetime.utcnow()-datetime.datetime.strptime(str(t)[:19],"%Y-%m-%dT%H:%M:%S")).days
|
||||
except: pass
|
||||
return None
|
||||
rmm=set(a["hostname"].lower() for a in rget("/api/agents") if a.get("hostname"))
|
||||
tgts=json.load(open("projects/gps-rmm-audit/targets.json"))["clients"]
|
||||
groups={"ONLINE now":[],"active <=14d":[],"stale 15-45d":[],"dead/no-session":[]}
|
||||
pushed=0; failed=0
|
||||
for t in tgts:
|
||||
for h in assets(t["cid"]):
|
||||
if h.lower() in rmm: continue
|
||||
ss=scp("GetSessionsByName",{"sessionName":h})
|
||||
if not (isinstance(ss,list) and ss): groups["dead/no-session"].append((t["client"],h,"no SC")); continue
|
||||
if len(ss)>1: continue # ambiguous shared name -> skip
|
||||
s=ss[0]; d=recency_days(s)
|
||||
if online(s):
|
||||
groups["ONLINE now"].append((t["client"],h,f"{d}d" if d is not None else "?"))
|
||||
if not DRY:
|
||||
r=scp("SendCommandToSession",[s["SessionID"],PUSHCMD])
|
||||
if isinstance(r,dict) and r.get("__err"): failed+=1
|
||||
else: pushed+=1
|
||||
elif d is not None and d<=14: groups["active <=14d"].append((t["client"],h,f"{d}d"))
|
||||
elif d is not None and d<=45: groups["stale 15-45d"].append((t["client"],h,f"{d}d"))
|
||||
else: groups["dead/no-session"].append((t["client"],h,f"{d}d" if d is not None else "old"))
|
||||
out=["# Machines not in GuruRMM — by ScreenConnect reachability (CORRECTED)","",
|
||||
"Online = ActiveConnections ProcessType==2 (guest agent connected). Recency = GuestInfoUpdateTime.",
|
||||
f"Rebuilt {datetime.date(2026,7,4)}. Staging installer pushed to all ONLINE machines (land in Staging -> reassign-staging.py).",""]
|
||||
for g,items in groups.items():
|
||||
out.append(f"## {g} ({len(items)})")
|
||||
for c,h,d in sorted(items): out.append(f"- {h} [{c}] ({d})")
|
||||
out.append("")
|
||||
open("projects/gps-rmm-audit/needs-screenconnect.md","w",encoding="utf-8").write("\n".join(out))
|
||||
print(f"ONLINE now: {len(groups['ONLINE now'])} | active<=14d: {len(groups['active <=14d'])} | stale15-45: {len(groups['stale 15-45d'])} | dead: {len(groups['dead/no-session'])}")
|
||||
print(f"Staging installer pushed: {pushed} (failed {failed}){' [DRY]' if DRY else ''}")
|
||||
Reference in New Issue
Block a user