sync: auto-sync from HOWARD-HOME at 2026-07-05 09:56:12
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-07-05 09:56:12
This commit is contained in:
10
.bk.ps1
Normal file
10
.bk.ps1
Normal file
@@ -0,0 +1,10 @@
|
||||
$b='C:\ProgramData\acg-backup'
|
||||
'--- COMPLETE.txt ---'
|
||||
if (Test-Path (Join-Path $b 'COMPLETE.txt')) { Get-Content (Join-Path $b 'COMPLETE.txt') } else { 'absent' }
|
||||
'--- dir listing ---'
|
||||
Get-ChildItem $b | Select-Object Name,Length,LastWriteTime | Format-Table -AutoSize | Out-String
|
||||
'--- rclone.log tail ---'
|
||||
Get-Content (Join-Path $b 'rclone.log') -Tail 25
|
||||
'--- rclone process ---'
|
||||
$p = Get-Process rclone -ErrorAction SilentlyContinue
|
||||
if ($p) { 'RUNNING pid=' + $p.Id } else { 'not running' }
|
||||
7
.chk.ps1
Normal file
7
.chk.ps1
Normal file
@@ -0,0 +1,7 @@
|
||||
Import-Module BitsTransfer
|
||||
$j = Get-BitsTransfer -Name 'Win11ARM64ISO' -AllUsers -ErrorAction SilentlyContinue
|
||||
if ($j) { 'state=' + $j.JobState + ' bytes=' + $j.BytesTransferred + '/' + $j.BytesTotal }
|
||||
else { 'NO JOB' }
|
||||
$f = Get-Item 'C:\Users\howar\Desktop\Win11_25H2_English_Arm64_v2.iso' -ErrorAction SilentlyContinue
|
||||
if ($f) { 'file present size=' + $f.Length }
|
||||
'uptime-min: ' + [math]::Round(((Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime).TotalMinutes,0)
|
||||
11
.dl.ps1
Normal file
11
.dl.ps1
Normal file
@@ -0,0 +1,11 @@
|
||||
$url = 'https://software.download.prss.microsoft.com/dbazure/Win11_25H2_English_Arm64_v2.iso?t=d4576e55-692e-48d2-9fed-eaa2e173b5de&P1=1783356015&P2=602&P3=2&P4=xodJg8f55ONbSp7rcTMYQ7XCqe4sRi%2fvBTnMttLYGogof26ximbkVPH1AcGo4uC2lkFA9SLtNzw%2bSF2y70fjMZJDOitpvTLYZovDWjoxvl7qG10gorldh4EujtcKbVgwBswc0PQ4mw7X7Mnt4bCGzZL3Fo5iN9SZA8bch06kISLSuYQrrhNZw%2bvguRStHFL9I7qGSOfVlVdDLbPiDEpW2PX8l8FthpvdVhWCn3gZh72l3mApXWXyKAFra82WgpE79pvPky1bepT%2bnBd3fkmni6or1ZrOnfpac9sWMm3GeHdffxiRtUnT50O7CDN%2b26aF8154BD%2fT6BQgYbAN8B6aFw%3d%3d'
|
||||
$dst = 'C:\Users\howar\Desktop\Win11_25H2_English_Arm64_v2.iso'
|
||||
if (Test-Path $dst) { Remove-Item $dst -Force }
|
||||
Get-BitsTransfer -Name 'Win11ARM64ISO' -AllUsers -ErrorAction SilentlyContinue | Remove-BitsTransfer
|
||||
Import-Module BitsTransfer
|
||||
$j = Start-BitsTransfer -Source $url -Destination $dst -Asynchronous -DisplayName 'Win11ARM64ISO' -Priority Foreground
|
||||
Start-Sleep -Seconds 10
|
||||
$j = Get-BitsTransfer -Name 'Win11ARM64ISO'
|
||||
'state: ' + $j.JobState
|
||||
'bytes: ' + $j.BytesTransferred + ' / ' + $j.BytesTotal
|
||||
if ($j.JobState -eq 'Error') { $j.ErrorDescription }
|
||||
9
.ex.ps1
Normal file
9
.ex.ps1
Normal file
@@ -0,0 +1,9 @@
|
||||
$rc='C:\ProgramData\acg-backup\rclone\rclone.exe'
|
||||
foreach ($d in 'C:\Windows','C:\Program Files','C:\Program Files (x86)','C:\ProgramData','C:\Users') {
|
||||
$out = & $rc size $d --skip-links 2>$null | Select-String 'Total size'
|
||||
"$d :: $out"
|
||||
}
|
||||
'--- C:\ root files (pagefile etc) ---'
|
||||
Get-ChildItem 'C:\' -File -Force -ErrorAction SilentlyContinue | Select-Object Name,@{n='GB';e={[math]::Round($_.Length/1GB,1)}} | Format-Table -AutoSize | Out-String
|
||||
'--- D:\vault ---'
|
||||
Test-Path 'D:\vault'
|
||||
11
.recon.ps1
Normal file
11
.recon.ps1
Normal file
@@ -0,0 +1,11 @@
|
||||
$ErrorActionPreference='SilentlyContinue'
|
||||
'=== BACKUP-ISH PROCESSES ==='
|
||||
Get-Process | Where-Object { $_.ProcessName -match 'cbb|CBB|robocopy|wbengine|veeam|reflect|acronis|rclone|restic|freefilesync|syncback|disk2vhd|macrium' } | Select-Object ProcessName,Id,StartTime | Format-Table -AutoSize | Out-String
|
||||
'=== MSP360 INSTALLED ==='
|
||||
Test-Path 'C:\Program Files\Arizona Computer Guru\Online Backup\cbb.exe'
|
||||
'=== MSP360 RECENT LOGS ==='
|
||||
Get-ChildItem 'C:\ProgramData\Online Backup\Logs' -Recurse -Filter *.log | Sort-Object LastWriteTime -Descending | Select-Object -First 5 FullName,LastWriteTime,Length | Format-List | Out-String
|
||||
'=== WBADMIN ==='
|
||||
wbadmin get status | Out-String
|
||||
'=== WHO IS ON ==='
|
||||
query user | Out-String
|
||||
8
.sz.ps1
Normal file
8
.sz.ps1
Normal file
@@ -0,0 +1,8 @@
|
||||
'--- DISK USAGE ---'
|
||||
Get-PSDrive -PSProvider FileSystem | Where-Object {$_.Used} | Select-Object Name,@{n='UsedGB';e={[math]::Round($_.Used/1GB,1)}},@{n='FreeGB';e={[math]::Round($_.Free/1GB,1)}} | Format-Table -AutoSize | Out-String
|
||||
'--- FOLDERS COPIED (from log) ---'
|
||||
Select-String -Path 'C:\ProgramData\acg-backup\rclone.log' -Pattern '^(>>>|=== BACKUP)' | ForEach-Object Line
|
||||
'--- PER-RUN TRANSFER TOTALS (last stats line before each section end) ---'
|
||||
Select-String -Path 'C:\ProgramData\acg-backup\rclone.log' -Pattern 'ETA 0s' | ForEach-Object Line | Select-Object -Unique
|
||||
'--- BUCKET SIZE (rclone size, may take a while) ---'
|
||||
& 'C:\ProgramData\acg-backup\rclone\rclone.exe' size 'b2:ACG-Tech03L-Backup' --config 'C:\ProgramData\acg-backup\rclone.conf' 2>&1 | Out-String
|
||||
6
.vf.ps1
Normal file
6
.vf.ps1
Normal file
@@ -0,0 +1,6 @@
|
||||
'--- upload.ps1 ---'
|
||||
Get-Content 'C:\ProgramData\acg-backup\upload.ps1'
|
||||
'--- rclone.conf (key masked) ---'
|
||||
(Get-Content 'C:\ProgramData\acg-backup\rclone.conf') -replace '(key|pass|secret|account).*','$1 = ***'
|
||||
'--- staging dir ---'
|
||||
Get-ChildItem 'C:\ProgramData\acg-backup\staging' -ErrorAction SilentlyContinue | Select-Object Name,Length | Format-Table -AutoSize | Out-String
|
||||
20
.wd.ps1
Normal file
20
.wd.ps1
Normal file
@@ -0,0 +1,20 @@
|
||||
Import-Module BitsTransfer
|
||||
$deadline = (Get-Date).AddMinutes(50)
|
||||
while ((Get-Date) -lt $deadline) {
|
||||
$j = Get-BitsTransfer -Name 'Win11ARM64ISO' -ErrorAction SilentlyContinue
|
||||
if (-not $j) { 'JOB GONE'; break }
|
||||
if ($j.JobState -eq 'Transferred') {
|
||||
Complete-BitsTransfer -BitsJob $j
|
||||
Start-Sleep -Seconds 3
|
||||
$f = Get-Item 'C:\Users\howar\Desktop\Win11_25H2_English_Arm64_v2.iso'
|
||||
'DONE size=' + $f.Length
|
||||
break
|
||||
}
|
||||
if ($j.JobState -in 'Error','TransientError') {
|
||||
'STATE=' + $j.JobState + ' :: ' + $j.ErrorDescription
|
||||
if ($j.JobState -eq 'Error') { break }
|
||||
}
|
||||
Start-Sleep -Seconds 30
|
||||
}
|
||||
$j = Get-BitsTransfer -Name 'Win11ARM64ISO' -ErrorAction SilentlyContinue
|
||||
if ($j) { 'FINAL state=' + $j.JobState + ' bytes=' + $j.BytesTransferred + '/' + $j.BytesTotal }
|
||||
@@ -19,6 +19,10 @@ Categories (the `[type]` tag): _(none)_ = skill/command execution failure ·
|
||||
|
||||
<!-- Append entries below this line -->
|
||||
|
||||
2026-07-05 | Howard-Home | git/submodule-cwd | [friction] ran merge+push for guru-rmm from C:/claudetools (parent repo) after cd for verify.sh; harmless fail but wasted a roundtrip — cd back into the submodule before repo ops
|
||||
|
||||
2026-07-05 | Howard-Home | bash/tmp-path | [friction] redirected ssh output to /tmp/deployed-build-server.sh; blocked by block-tmp-path hook; used repo-relative ./.deployed-build-server.sh instead [ctx: ref=feedback_tmp_path_windows]
|
||||
|
||||
2026-07-05 | Howard-Home | powershell/git-commit | [friction] embedded double quotes in git commit -m here-string mangled by CommandLineToArgvW -> 'fatal: /: outside repository'; fix: git commit -F <file> [ctx: ref=feedback_windows_quote_stripping]
|
||||
|
||||
2026-07-05 | Howard-Home | git/concurrent-sessions | [friction] git add -A in shared gururmm working tree swept another session's uncommitted SPEC-030 edits into my commit (2nd collision today; f6c163b was the reverse). Fix: explicit-path git add in project repos; coord lock before multi-file work [ctx: ref=coord-locks repo=gururmm]
|
||||
|
||||
16379
projects/gps-rmm-audit/.edr-agents-now.json
Normal file
16379
projects/gps-rmm-audit/.edr-agents-now.json
Normal file
File diff suppressed because it is too large
Load Diff
63
projects/gps-rmm-audit/onsite-check-list.md
Normal file
63
projects/gps-rmm-audit/onsite-check-list.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Onsite-Check List — machines/clients with NO remote channel
|
||||
|
||||
Rule (Howard, 2026-07-05): anything we cannot reach via ScreenConnect, Syncro checkin,
|
||||
or Bitdefender goes in this group — it gets handled at the next onsite visit to that
|
||||
location (enroll GuruRMM + SC + Datto EDR while physically there, or confirm the
|
||||
machine is retired and get it pulled from GPS billing).
|
||||
|
||||
Sources: Syncro assets (authoritative inventory) vs live RMM, SC reachability from
|
||||
`needs-screenconnect.md` (rebuilt 2026-07-04), Bitdefender/EDR footprint from the
|
||||
Phase 4 matrix. "no SC" = no ScreenConnect session at all; a day count = SC session
|
||||
exists but stale that many days (kept here only when 45d+ AND no other channel).
|
||||
|
||||
Machines listed are NOT yet in GuruRMM. Anything that comes online meanwhile is
|
||||
harvested automatically by GPS-RMM-AutoEnroll — remove it from this list when it
|
||||
appears in RMM (the daily progress DM catches that).
|
||||
|
||||
## Whole clients with NO footprint anywhere (visit or call required)
|
||||
|
||||
| Client | Billed devices | Notes |
|
||||
|---|---|---|
|
||||
| Little Hearts Little Hands | 8 | Only 2 Syncro assets known (LHLH, WIN-KNVO6MUMMEM), both no-SC. Also NO-AV. |
|
||||
| Residential and Renovation Engineering | 2 | No Syncro assets with SC, no BD, no RMM org agents. EDR org/location exist, 0 agents. |
|
||||
| Janet Altschuler | 2 | Separate from JANC Excavation (Syncro 457710 vs 2248945, confirmed 2026-07-04). NO-AV. |
|
||||
| Business Services of Tucson LLC | 2 | KAT1 active in SC <=14d (autoenroll may catch); KAT no-SC. NO-AV. |
|
||||
| Marty Ryan | 1 | DESKTOP-OC2PH4I seen in SC 0d — autoenroll should catch; onsite only if it doesn't. |
|
||||
|
||||
## Special case — pending Mike's ruling
|
||||
|
||||
- **IMC-PRINTSERVER** [Instrumental Music Center] — GuruRMM agent INSTALLED and running,
|
||||
but the box drops all outbound TLS so it cannot connect. DM'd Mike 2026-07-05: by design
|
||||
or fix? If by design -> stays here (dormant agent, check on next IMC visit / gateway change).
|
||||
|
||||
## Per-client machines with no SC session (Syncro-known, unreachable remotely)
|
||||
|
||||
- **Bill Tedards** (0/2 in RMM): DESKTOP-SUFJR0J, TEDARDSLAPTOP
|
||||
- **Business Services of Tucson LLC**: KAT
|
||||
- **Curtis Plumbing** (5/6): CURTIS-003
|
||||
- **Grabb & Durando Law Office** (9/12): GND-ASUSWS, GND-JEANNETTE, HOMEPC
|
||||
- **Heieck, Sheila** (0/3): John Heieck's iMac, Sheila's iMac, Sheila's MacBook Pro (all Macs)
|
||||
- **Horseshoe Management** (5/9): HSM-RANDI, HSM-SURFACE
|
||||
- **Instrumental Music Center** (13/20): IMC-L1-GRAPHICS (stale 2025 box), PHIL;
|
||||
stale-SC 80-141d: DESKTOP-JQ0D38J, DESKTOP-URV3UGR, IMC-EVENTS, PHIL2021LAPTOP, PURCHASINGCOMP
|
||||
- **Little Hearts Little Hands**: LHLH, WIN-KNVO6MUMMEM
|
||||
- **MVAN Enterprises Inc** (0/4): CPC-chris-UZR6E, DESKTOP-I7504C5, June's MacBook Pro; MITCH-LAPTOP (139d)
|
||||
- **Mineralogical Record** (4/5): DESKTOP-3VPT017, DESKTOP-9QHSCIT
|
||||
- **Multicultural Counseling Center** (2/3): DESKTOP-S1HPLDF (65d)
|
||||
- **PUTT LAND SURVEYING, INC.** (2/7): PLS-FOUR, PLS-FRONT, PLS-LAPTOP, PLS-ONE
|
||||
- **Quantum Wealth Management**: DESKTOP-A3IVMNF
|
||||
- **Reliant Well Drilling and Pump Corporate** (1/9): Clark's iMac, DESKTOP-09H7T66,
|
||||
DESKTOP-QIGH458, DESKTOP-V2247T9, LAPTOP-8AUEP9N6, LAPTOP-EJL9PFOU, LAPTOP-FR4QN9KF,
|
||||
LAPTOP-JQMU8N9K, RELIANT-L01, RELIANT-L02, RELIANT-L03, WILCOXADMIN; RWD-ALANLAPTOP (127d)
|
||||
— 13 dead assets vs 9 billed: several likely retired; reconcile onsite/with Winter.
|
||||
- **Safesite**: 1225-LENOVO-E16 (49d), DESKTOP-QAR6D04 (142d)
|
||||
- **Stamback Septic** (3/8, B-flag billing): DEREK-LAPTOP, DESKTOP-1BS5JL4, DESKTOP-JVLQQIJ,
|
||||
SSS-SPARE, STAMBACK-JBECK, STAMBACK-NEW06 — billing says 8, reality ~2; billing review first.
|
||||
- **The Marc Group** (0/2): LAPTOP-JMUNGO (54d)
|
||||
- **The Prairie Schooner** (4/5): DESKTOP-SRUOH4R, DESKTOP-TS1P9MT
|
||||
|
||||
## Not on this list on purpose
|
||||
|
||||
- Jimmy Company (1/12) and Glaz-Tech (159 billed / ~5 real) — billing-vs-reality anomalies
|
||||
held for Winter/Mike; nothing to chase onsite until billing is ruled on.
|
||||
- Anything with an SC session active <=45d — the autoenroll loop owns those.
|
||||
139
projects/gps-rmm-audit/tools/edr-deploy.py
Normal file
139
projects/gps-rmm-audit/tools/edr-deploy.py
Normal file
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env python3
|
||||
# edr-deploy.py — throttled Bitdefender->Datto EDR rollout via GuruRMM.
|
||||
#
|
||||
# For every ONLINE Windows RMM agent belonging to a GPS client with a vaulted
|
||||
# EDR registration key, and whose hostname is not already in Datto EDR, push the
|
||||
# official Install-EDR one-liner through the RMM command API. Deliberately slow:
|
||||
# strictly sequential, poll each command to completion, fixed gap between
|
||||
# machines, retry once on transient 5xx. Glaz-Tech has no key (stays BD) and
|
||||
# non-Windows agents are skipped by design.
|
||||
#
|
||||
# Env: RMM, TOK. Files: .edr-agents-now.json (fresh `edr.py --json agents` dump).
|
||||
# Usage: edr-deploy.py --dry | --live [--gap 20]
|
||||
import json, os, sys, time, ssl, subprocess, urllib.request, re
|
||||
|
||||
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||||
RMM = os.environ["RMM"]; TOK = os.environ["TOK"]
|
||||
ctx = ssl.create_default_context()
|
||||
DRY = "--live" not in sys.argv
|
||||
GAP = int(sys.argv[sys.argv.index("--gap") + 1]) if "--gap" in sys.argv else 20
|
||||
ROOT = "projects/gps-rmm-audit"
|
||||
INSTANCE = "https://azcomp4587.infocyte.com"
|
||||
ONELINER = ('[System.Net.ServicePointManager]::SecurityProtocol = '
|
||||
'[Enum]::ToObject([System.Net.SecurityProtocolType], 3072); '
|
||||
'(new-object Net.WebClient).DownloadString('
|
||||
'"https://raw.githubusercontent.com/Infocyte/PowershellTools/master/AgentDeployment/install_huntagent.ps1")'
|
||||
' | iex; Install-EDR -URL "{u}" -RegKey {k}')
|
||||
|
||||
def api(path, body=None, tries=3):
|
||||
for i in range(tries):
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
RMM + path,
|
||||
data=json.dumps(body).encode() if body is not None else None,
|
||||
method="POST" if body is not None else "GET",
|
||||
headers={"Authorization": "Bearer " + TOK, "Content-Type": "application/json"})
|
||||
raw = urllib.request.urlopen(req, context=ctx, timeout=30).read().decode("utf-8", "replace")
|
||||
return json.loads("".join(c for c in raw if ord(c) >= 32 or c in "\t\n\r"))
|
||||
except Exception as e:
|
||||
if i == tries - 1: raise
|
||||
time.sleep(5)
|
||||
|
||||
def norm(s):
|
||||
s = re.sub(r"[^a-z0-9 ]", " ", s.lower())
|
||||
drop = {"llc", "inc", "corp", "corporate", "ltd", "co", "the", "and", "of", "office", "offices"}
|
||||
return frozenset(w for w in s.split() if w and w not in drop)
|
||||
|
||||
# --- keys from vault (decrypted in-memory, never written to disk) ---
|
||||
raw = subprocess.run(["bash", ".claude/scripts/vault.sh", "get", "msp-tools/datto-edr-regkeys.sops.yaml"],
|
||||
capture_output=True, text=True).stdout
|
||||
keys = {}
|
||||
in_creds = False
|
||||
for line in raw.splitlines():
|
||||
if line.startswith("credentials:"): in_creds = True; continue
|
||||
if in_creds:
|
||||
m = re.match(r"\s+(.+?):\s*(\S+)\s*$", line)
|
||||
if m: keys[m.group(1)] = m.group(2)
|
||||
elif line and not line.startswith(" "): break
|
||||
OVERRIDES = { # vault name -> RMM client name (where token match is not enough)
|
||||
"Tedards": "Bill Tedards",
|
||||
"Altschuler, Janet": "Janet Altschuler",
|
||||
"Residential and Renovation Eng": "Residential and Renovation Engineering",
|
||||
}
|
||||
|
||||
# --- current EDR coverage ---
|
||||
edr = json.load(open(f"{ROOT}/.edr-agents-now.json"))
|
||||
edr = edr if isinstance(edr, list) else edr.get("agents", edr.get("data", []))
|
||||
covered = set((a.get("hostname") or a.get("name") or "").lower() for a in edr)
|
||||
|
||||
# --- RMM fleet ---
|
||||
agents = api("/api/agents")
|
||||
clients = sorted(set(a.get("client_name") or "" for a in agents))
|
||||
key_by_rmm_client = {}
|
||||
for vname, k in keys.items():
|
||||
target = OVERRIDES.get(vname)
|
||||
if target and target in clients:
|
||||
key_by_rmm_client[target] = k; continue
|
||||
nv = norm(vname)
|
||||
best = [c for c in clients if norm(c) and (nv <= norm(c) or norm(c) <= nv)]
|
||||
if len(best) == 1:
|
||||
key_by_rmm_client[best[0]] = k
|
||||
elif len(best) > 1:
|
||||
exact = [c for c in best if norm(c) == nv]
|
||||
if len(exact) == 1: key_by_rmm_client[exact[0]] = k
|
||||
else: print(f"[WARNING] ambiguous key mapping for vault '{vname}': {best} — skipped")
|
||||
# zero matches = client has no RMM org yet; fine.
|
||||
|
||||
targets = []
|
||||
for a in agents:
|
||||
c = a.get("client_name") or ""
|
||||
if c not in key_by_rmm_client: continue
|
||||
if (a.get("status") or "").lower() != "online": continue
|
||||
osname = (a.get("os") or a.get("os_type") or "").lower()
|
||||
if osname and "windows" not in osname: continue
|
||||
h = (a.get("hostname") or "").lower()
|
||||
if not h or h in covered: continue
|
||||
targets.append((c, a["hostname"], a["id"]))
|
||||
targets.sort()
|
||||
|
||||
print(f"EDR deploy plan: {len(targets)} machine(s) across "
|
||||
f"{len(set(t[0] for t in targets))} client(s); gap {GAP}s; {'DRY-RUN' if DRY else 'LIVE'}")
|
||||
for c, h, aid in targets:
|
||||
print(f" {c:45s} {h}")
|
||||
if DRY or not targets:
|
||||
sys.exit(0)
|
||||
|
||||
results = []
|
||||
for n, (c, h, aid) in enumerate(targets, 1):
|
||||
cmd = ONELINER.format(u=INSTANCE, k=key_by_rmm_client[c])
|
||||
print(f"[{n}/{len(targets)}] {c} / {h} ... ", end="", flush=True)
|
||||
try:
|
||||
r = api(f"/api/agents/{aid}/command",
|
||||
{"command_type": "powershell", "command": cmd, "timeout_seconds": 300, "elevated": True})
|
||||
cid = r.get("command_id") or r.get("id")
|
||||
status, out = "timeout", ""
|
||||
for _ in range(60): # poll up to 5 min
|
||||
time.sleep(5)
|
||||
st = api(f"/api/commands/{cid}")
|
||||
status = st.get("status")
|
||||
if status not in ("running", "pending"):
|
||||
out = (st.get("stdout") or "")[-400:]
|
||||
break
|
||||
ok = status == "completed" and ("installed" in out.lower() or "success" in out.lower()
|
||||
or "already" in out.lower() or out.strip())
|
||||
print(status)
|
||||
results.append((c, h, status, out.replace("\n", " ")[:200]))
|
||||
except Exception as e:
|
||||
print(f"ERROR {e}")
|
||||
results.append((c, h, "dispatch-error", str(e)[:200]))
|
||||
time.sleep(GAP)
|
||||
|
||||
print("\n=== RESULTS ===")
|
||||
for c, h, s, o in results:
|
||||
print(f"{s:16s} {c:40s} {h}\n {o}")
|
||||
done = sum(1 for r in results if r[2] == "completed")
|
||||
print(f"\ncompleted {done}/{len(results)}")
|
||||
with open(f"{ROOT}/edr-deploy-results.md", "a", encoding="utf-8") as f:
|
||||
f.write(f"\n## Run {time.strftime('%Y-%m-%d %H:%M')} — {done}/{len(results)} completed\n")
|
||||
for c, h, s, o in results:
|
||||
f.write(f"- {s} | {c} | {h} | {o}\n")
|
||||
@@ -312,3 +312,8 @@ Deploy recipe per machine (Monday): RMM online -> POST /api/agents/:id/command p
|
||||
(RMM command); readback via listener query log or nginx access log on the RMM server
|
||||
(`/install/<code>/windows?gpsdiag=...` — CF passes the install path).
|
||||
Full detail: session-logs/2026-07/2026-07-05-howard-cpqb-fixed-imcprintserver-tls-blocked.md
|
||||
|
||||
**Onsite-check group created (2026-07-05, Howard's rule):** machines/clients with no SC,
|
||||
no Syncro checkin, and no Bitdefender go to `onsite-check-list.md` — handled at the next
|
||||
site visit, not chased remotely. IMC-PRINTSERVER pending Mike's ruling (TLS lockdown by
|
||||
design vs fix — DM sent 2026-07-05).
|
||||
|
||||
Reference in New Issue
Block a user