From 8d975c1b44648c88f397a8b67ed63049ac8c2b61 Mon Sep 17 00:00:00 2001 From: Howard Enos Date: Thu, 16 Apr 2026 19:43:58 -0700 Subject: [PATCH] import: ingested 160 files from C:\Users\howar\Clients Howard's personal MSP client documentation folder imported into shared ClaudeTools repo via /import command. Scope: Clients (structured MSP docs under clients//docs/): - anaise (NEW) - 13 files - cascades-tucson - 47 files merged (existing had only reports/) - dataforth - 18 files merged (alongside incident reports) - instrumental-music-center - 14 files merged - khalsa (NEW) - 22 files, multi-site (camden, river) - kittle (NEW) - 16 files incl. fix-pdf-preview, gpo-intranet-zone - lens-auto-brokerage (NEW) - 3 files (name matches SOPS vault) - _client_template - 13-file scaffold for new clients MSP tooling (projects/msp-tools/): - msp-audit-scripts/ - server_audit.ps1, workstation_audit.ps1, README - utilities/ - clean_printer_ports, win11_upgrade, screenconnect-toolbox-commands Credential handling: - Extracted 1 inline password (Anaise DESKTOP-O8GF4SD / david) to SOPS vault: clients/anaise/desktop-o8gf4sd.sops.yaml - Redacted overview.md with vault reference pattern - Scanned all 160 files for keys/tokens/connection strings - no other credentials found Skipped: - Cascades/.claude/settings.local.json (per-machine config) - Source-root CLAUDE.md (personal, claudetools has its own) - scripts/server_audit.ps1 and workstation_audit.ps1 at source root (identical duplicates of msp-audit-scripts versions) Memory updates: - reference_client_docs_structure.md (layout, conventions, active list) - reference_msp_audit_scripts.md (locations, ScreenConnect 80-char rule) Session log: session-logs/2026-04-16-howard-client-docs-import.md Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/memory/MEMORY.md | 2 + .../memory/reference_client_docs_structure.md | 33 + .claude/memory/reference_msp_audit_scripts.md | 29 + clients/_client_template/cloud/azure.md | 28 + clients/_client_template/cloud/m365.md | 52 + clients/_client_template/issues/log.md | 19 + clients/_client_template/network/dhcp.md | 31 + clients/_client_template/network/dns.md | 33 + clients/_client_template/network/firewall.md | 47 + clients/_client_template/network/topology.md | 43 + clients/_client_template/network/vlans.md | 21 + clients/_client_template/overview.md | 31 + clients/_client_template/rmm/rmm.md | 34 + .../_client_template/security/antivirus.md | 26 + clients/_client_template/security/backup.md | 34 + .../servers/server_template.md | 49 + clients/anaise/docs/cloud/azure.md | 28 + clients/anaise/docs/cloud/m365.md | 52 + clients/anaise/docs/issues/log.md | 19 + clients/anaise/docs/network/dhcp.md | 31 + clients/anaise/docs/network/dns.md | 33 + clients/anaise/docs/network/firewall.md | 47 + clients/anaise/docs/network/topology.md | 43 + clients/anaise/docs/network/vlans.md | 21 + clients/anaise/docs/overview.md | 26 + clients/anaise/docs/rmm/rmm.md | 34 + clients/anaise/docs/security/antivirus.md | 26 + clients/anaise/docs/security/backup.md | 34 + .../anaise/docs/servers/server_template.md | 49 + clients/cascades-tucson/docs/billing-log.md | 488 ++++ clients/cascades-tucson/docs/cloud/azure.md | 16 + clients/cascades-tucson/docs/cloud/m365.md | 285 +++ .../docs/issues/audit-findings-2026-03-20.md | 276 ++ clients/cascades-tucson/docs/issues/log.md | 618 +++++ .../cascades-tucson/docs/migration/README.md | 122 + .../docs/migration/phase0-safety-net.md | 65 + .../docs/migration/phase1-network.md | 180 ++ .../docs/migration/phase2-server-prep.md | 266 ++ .../docs/migration/phase3-domain-join.md | 93 + .../docs/migration/phase4-synology.md | 77 + .../docs/migration/phase5-hardening.md | 129 + .../scripts/debloat-business-pcs.ps1 | 129 + .../scripts/phase0-export-configs.ps1 | 161 ++ .../scripts/phase0-remote-checks.ps1 | 135 + .../migration/scripts/phase2-ad-setup.ps1 | 316 +++ .../migration/scripts/phase2-dns-cleanup.ps1 | 123 + .../migration/scripts/phase2-file-shares.ps1 | 233 ++ .../migration/scripts/phase2-ou-cleanup.ps1 | 354 +++ .../migration/scripts/phase2-print-server.ps1 | 123 + .../scripts/phase2-sync-synology.ps1 | 147 ++ .../migration/scripts/phase3-join-domain.ps1 | 151 ++ .../scripts/phase3-post-join-verify.ps1 | 142 ++ .../scripts/phase3-pre-join-verify.ps1 | 121 + .../scripts/phase4-archive-synology.ps1 | 71 + .../docs/migration/session3-2026-03-07.md | 280 ++ .../docs/migration/step3-switch-ports.md | 85 + .../docs/migration/step7-server-move.md | 65 + clients/cascades-tucson/docs/network/dhcp.md | 84 + clients/cascades-tucson/docs/network/dns.md | 135 + .../cascades-tucson/docs/network/firewall.md | 279 ++ .../cascades-tucson/docs/network/topology.md | 173 ++ clients/cascades-tucson/docs/network/vlans.md | 81 + clients/cascades-tucson/docs/network/wifi.md | 68 + clients/cascades-tucson/docs/overview.md | 109 + clients/cascades-tucson/docs/printers.md | 74 + .../docs/proposals/m365-premium-upgrade.md | 151 ++ clients/cascades-tucson/docs/rmm/rmm.md | 25 + .../docs/security/antivirus.md | 66 + .../cascades-tucson/docs/security/backup.md | 85 + .../cascades-tucson/docs/security/hipaa.md | 107 + clients/cascades-tucson/docs/security/mdm.md | 193 ++ .../docs/servers/active-directory.md | 329 +++ .../cascades-tucson/docs/servers/cs-server.md | 299 +++ .../docs/servers/server_template.md | 49 + clients/cascades-tucson/docs/workstations.md | 382 +++ clients/dataforth/docs/active-directory.md | 109 + clients/dataforth/docs/billing-log.md | 56 + clients/dataforth/docs/cloud/azure.md | 14 + clients/dataforth/docs/cloud/m365.md | 31 + clients/dataforth/docs/issues/log.md | 61 + clients/dataforth/docs/manufacturing.md | 95 + clients/dataforth/docs/network/dhcp.md | 11 + clients/dataforth/docs/network/dns.md | 26 + clients/dataforth/docs/network/firewall.md | 23 + clients/dataforth/docs/network/topology.md | 36 + clients/dataforth/docs/network/vlans.md | 15 + clients/dataforth/docs/overview.md | 72 + clients/dataforth/docs/rmm/rmm.md | 19 + clients/dataforth/docs/security/antivirus.md | 19 + clients/dataforth/docs/security/backup.md | 32 + clients/dataforth/docs/servers/3cx.md | 11 + clients/dataforth/docs/servers/ad1.md | 29 + clients/dataforth/docs/servers/ad2.md | 84 + clients/dataforth/docs/servers/d2testnas.md | 27 + clients/dataforth/docs/servers/df-hyperv-b.md | 11 + clients/dataforth/docs/servers/files-d1.md | 16 + clients/dataforth/docs/servers/sage-sql.md | 16 + clients/dataforth/docs/workstations.md | 81 + .../docs/billing-log.md | 33 + .../docs/cloud/azure.md | 28 + .../docs/cloud/m365.md | 52 + .../docs/issues/log.md | 19 + .../docs/network/dhcp.md | 31 + .../docs/network/dns.md | 33 + .../docs/network/firewall.md | 47 + .../docs/network/topology.md | 43 + .../docs/network/vlans.md | 21 + .../docs/overview.md | 56 + .../instrumental-music-center/docs/rmm/rmm.md | 34 + .../docs/security/antivirus.md | 26 + .../docs/security/backup.md | 34 + .../docs/servers/server_template.md | 49 + clients/khalsa/docs/apple-domain-join.md | 71 + clients/khalsa/docs/cloud/azure.md | 28 + clients/khalsa/docs/cloud/m365.md | 52 + clients/khalsa/docs/issues/log.md | 19 + clients/khalsa/docs/network/README.md | 8 + clients/khalsa/docs/network/camden/dhcp.md | 31 + clients/khalsa/docs/network/camden/dns.md | 33 + .../khalsa/docs/network/camden/firewall.md | 47 + .../khalsa/docs/network/camden/topology.md | 43 + clients/khalsa/docs/network/camden/vlans.md | 21 + clients/khalsa/docs/network/river/dhcp.md | 31 + clients/khalsa/docs/network/river/dns.md | 33 + clients/khalsa/docs/network/river/firewall.md | 47 + clients/khalsa/docs/network/river/topology.md | 43 + clients/khalsa/docs/network/river/vlans.md | 21 + clients/khalsa/docs/overview.md | 42 + clients/khalsa/docs/rmm/rmm.md | 34 + clients/khalsa/docs/security/antivirus.md | 26 + clients/khalsa/docs/security/backup.md | 34 + .../docs/servers/camden/server_template.md | 49 + .../docs/servers/river/server_template.md | 49 + .../khalsa/docs/servers/server_template.md | 49 + clients/kittle/docs/cloud/azure.md | 28 + clients/kittle/docs/cloud/m365.md | 52 + clients/kittle/docs/issues/log.md | 148 ++ clients/kittle/docs/network/dhcp.md | 46 + clients/kittle/docs/network/dns.md | 41 + clients/kittle/docs/network/firewall.md | 47 + clients/kittle/docs/network/topology.md | 87 + clients/kittle/docs/network/vlans.md | 21 + clients/kittle/docs/overview.md | 93 + clients/kittle/docs/rmm/rmm.md | 34 + .../kittle/docs/scripts/fix-pdf-preview.ps1 | 61 + .../kittle/docs/scripts/gpo-intranet-zone.ps1 | 68 + clients/kittle/docs/security/antivirus.md | 26 + clients/kittle/docs/security/backup.md | 34 + clients/kittle/docs/servers/server.md | 120 + .../kittle/docs/servers/server_template.md | 49 + .../lens-auto-brokerage/docs/billing-log.md | 41 + .../lens-auto-brokerage/docs/issues/log.md | 6 + clients/lens-auto-brokerage/docs/overview.md | 26 + .../msp-tools/msp-audit-scripts/README.md | 46 + .../msp-audit-scripts/server_audit.ps1 | 2271 +++++++++++++++++ .../msp-audit-scripts/workstation_audit.ps1 | 1158 +++++++++ .../utilities/clean_printer_ports.ps1 | 47 + .../screenconnect-toolbox-commands.txt | 184 ++ .../msp-tools/utilities/win11_upgrade.ps1 | 74 + .../2026-04-16-howard-client-docs-import.md | 88 + 160 files changed, 16002 insertions(+) create mode 100644 .claude/memory/reference_client_docs_structure.md create mode 100644 .claude/memory/reference_msp_audit_scripts.md create mode 100644 clients/_client_template/cloud/azure.md create mode 100644 clients/_client_template/cloud/m365.md create mode 100644 clients/_client_template/issues/log.md create mode 100644 clients/_client_template/network/dhcp.md create mode 100644 clients/_client_template/network/dns.md create mode 100644 clients/_client_template/network/firewall.md create mode 100644 clients/_client_template/network/topology.md create mode 100644 clients/_client_template/network/vlans.md create mode 100644 clients/_client_template/overview.md create mode 100644 clients/_client_template/rmm/rmm.md create mode 100644 clients/_client_template/security/antivirus.md create mode 100644 clients/_client_template/security/backup.md create mode 100644 clients/_client_template/servers/server_template.md create mode 100644 clients/anaise/docs/cloud/azure.md create mode 100644 clients/anaise/docs/cloud/m365.md create mode 100644 clients/anaise/docs/issues/log.md create mode 100644 clients/anaise/docs/network/dhcp.md create mode 100644 clients/anaise/docs/network/dns.md create mode 100644 clients/anaise/docs/network/firewall.md create mode 100644 clients/anaise/docs/network/topology.md create mode 100644 clients/anaise/docs/network/vlans.md create mode 100644 clients/anaise/docs/overview.md create mode 100644 clients/anaise/docs/rmm/rmm.md create mode 100644 clients/anaise/docs/security/antivirus.md create mode 100644 clients/anaise/docs/security/backup.md create mode 100644 clients/anaise/docs/servers/server_template.md create mode 100644 clients/cascades-tucson/docs/billing-log.md create mode 100644 clients/cascades-tucson/docs/cloud/azure.md create mode 100644 clients/cascades-tucson/docs/cloud/m365.md create mode 100644 clients/cascades-tucson/docs/issues/audit-findings-2026-03-20.md create mode 100644 clients/cascades-tucson/docs/issues/log.md create mode 100644 clients/cascades-tucson/docs/migration/README.md create mode 100644 clients/cascades-tucson/docs/migration/phase0-safety-net.md create mode 100644 clients/cascades-tucson/docs/migration/phase1-network.md create mode 100644 clients/cascades-tucson/docs/migration/phase2-server-prep.md create mode 100644 clients/cascades-tucson/docs/migration/phase3-domain-join.md create mode 100644 clients/cascades-tucson/docs/migration/phase4-synology.md create mode 100644 clients/cascades-tucson/docs/migration/phase5-hardening.md create mode 100644 clients/cascades-tucson/docs/migration/scripts/debloat-business-pcs.ps1 create mode 100644 clients/cascades-tucson/docs/migration/scripts/phase0-export-configs.ps1 create mode 100644 clients/cascades-tucson/docs/migration/scripts/phase0-remote-checks.ps1 create mode 100644 clients/cascades-tucson/docs/migration/scripts/phase2-ad-setup.ps1 create mode 100644 clients/cascades-tucson/docs/migration/scripts/phase2-dns-cleanup.ps1 create mode 100644 clients/cascades-tucson/docs/migration/scripts/phase2-file-shares.ps1 create mode 100644 clients/cascades-tucson/docs/migration/scripts/phase2-ou-cleanup.ps1 create mode 100644 clients/cascades-tucson/docs/migration/scripts/phase2-print-server.ps1 create mode 100644 clients/cascades-tucson/docs/migration/scripts/phase2-sync-synology.ps1 create mode 100644 clients/cascades-tucson/docs/migration/scripts/phase3-join-domain.ps1 create mode 100644 clients/cascades-tucson/docs/migration/scripts/phase3-post-join-verify.ps1 create mode 100644 clients/cascades-tucson/docs/migration/scripts/phase3-pre-join-verify.ps1 create mode 100644 clients/cascades-tucson/docs/migration/scripts/phase4-archive-synology.ps1 create mode 100644 clients/cascades-tucson/docs/migration/session3-2026-03-07.md create mode 100644 clients/cascades-tucson/docs/migration/step3-switch-ports.md create mode 100644 clients/cascades-tucson/docs/migration/step7-server-move.md create mode 100644 clients/cascades-tucson/docs/network/dhcp.md create mode 100644 clients/cascades-tucson/docs/network/dns.md create mode 100644 clients/cascades-tucson/docs/network/firewall.md create mode 100644 clients/cascades-tucson/docs/network/topology.md create mode 100644 clients/cascades-tucson/docs/network/vlans.md create mode 100644 clients/cascades-tucson/docs/network/wifi.md create mode 100644 clients/cascades-tucson/docs/overview.md create mode 100644 clients/cascades-tucson/docs/printers.md create mode 100644 clients/cascades-tucson/docs/proposals/m365-premium-upgrade.md create mode 100644 clients/cascades-tucson/docs/rmm/rmm.md create mode 100644 clients/cascades-tucson/docs/security/antivirus.md create mode 100644 clients/cascades-tucson/docs/security/backup.md create mode 100644 clients/cascades-tucson/docs/security/hipaa.md create mode 100644 clients/cascades-tucson/docs/security/mdm.md create mode 100644 clients/cascades-tucson/docs/servers/active-directory.md create mode 100644 clients/cascades-tucson/docs/servers/cs-server.md create mode 100644 clients/cascades-tucson/docs/servers/server_template.md create mode 100644 clients/cascades-tucson/docs/workstations.md create mode 100644 clients/dataforth/docs/active-directory.md create mode 100644 clients/dataforth/docs/billing-log.md create mode 100644 clients/dataforth/docs/cloud/azure.md create mode 100644 clients/dataforth/docs/cloud/m365.md create mode 100644 clients/dataforth/docs/issues/log.md create mode 100644 clients/dataforth/docs/manufacturing.md create mode 100644 clients/dataforth/docs/network/dhcp.md create mode 100644 clients/dataforth/docs/network/dns.md create mode 100644 clients/dataforth/docs/network/firewall.md create mode 100644 clients/dataforth/docs/network/topology.md create mode 100644 clients/dataforth/docs/network/vlans.md create mode 100644 clients/dataforth/docs/overview.md create mode 100644 clients/dataforth/docs/rmm/rmm.md create mode 100644 clients/dataforth/docs/security/antivirus.md create mode 100644 clients/dataforth/docs/security/backup.md create mode 100644 clients/dataforth/docs/servers/3cx.md create mode 100644 clients/dataforth/docs/servers/ad1.md create mode 100644 clients/dataforth/docs/servers/ad2.md create mode 100644 clients/dataforth/docs/servers/d2testnas.md create mode 100644 clients/dataforth/docs/servers/df-hyperv-b.md create mode 100644 clients/dataforth/docs/servers/files-d1.md create mode 100644 clients/dataforth/docs/servers/sage-sql.md create mode 100644 clients/dataforth/docs/workstations.md create mode 100644 clients/instrumental-music-center/docs/billing-log.md create mode 100644 clients/instrumental-music-center/docs/cloud/azure.md create mode 100644 clients/instrumental-music-center/docs/cloud/m365.md create mode 100644 clients/instrumental-music-center/docs/issues/log.md create mode 100644 clients/instrumental-music-center/docs/network/dhcp.md create mode 100644 clients/instrumental-music-center/docs/network/dns.md create mode 100644 clients/instrumental-music-center/docs/network/firewall.md create mode 100644 clients/instrumental-music-center/docs/network/topology.md create mode 100644 clients/instrumental-music-center/docs/network/vlans.md create mode 100644 clients/instrumental-music-center/docs/overview.md create mode 100644 clients/instrumental-music-center/docs/rmm/rmm.md create mode 100644 clients/instrumental-music-center/docs/security/antivirus.md create mode 100644 clients/instrumental-music-center/docs/security/backup.md create mode 100644 clients/instrumental-music-center/docs/servers/server_template.md create mode 100644 clients/khalsa/docs/apple-domain-join.md create mode 100644 clients/khalsa/docs/cloud/azure.md create mode 100644 clients/khalsa/docs/cloud/m365.md create mode 100644 clients/khalsa/docs/issues/log.md create mode 100644 clients/khalsa/docs/network/README.md create mode 100644 clients/khalsa/docs/network/camden/dhcp.md create mode 100644 clients/khalsa/docs/network/camden/dns.md create mode 100644 clients/khalsa/docs/network/camden/firewall.md create mode 100644 clients/khalsa/docs/network/camden/topology.md create mode 100644 clients/khalsa/docs/network/camden/vlans.md create mode 100644 clients/khalsa/docs/network/river/dhcp.md create mode 100644 clients/khalsa/docs/network/river/dns.md create mode 100644 clients/khalsa/docs/network/river/firewall.md create mode 100644 clients/khalsa/docs/network/river/topology.md create mode 100644 clients/khalsa/docs/network/river/vlans.md create mode 100644 clients/khalsa/docs/overview.md create mode 100644 clients/khalsa/docs/rmm/rmm.md create mode 100644 clients/khalsa/docs/security/antivirus.md create mode 100644 clients/khalsa/docs/security/backup.md create mode 100644 clients/khalsa/docs/servers/camden/server_template.md create mode 100644 clients/khalsa/docs/servers/river/server_template.md create mode 100644 clients/khalsa/docs/servers/server_template.md create mode 100644 clients/kittle/docs/cloud/azure.md create mode 100644 clients/kittle/docs/cloud/m365.md create mode 100644 clients/kittle/docs/issues/log.md create mode 100644 clients/kittle/docs/network/dhcp.md create mode 100644 clients/kittle/docs/network/dns.md create mode 100644 clients/kittle/docs/network/firewall.md create mode 100644 clients/kittle/docs/network/topology.md create mode 100644 clients/kittle/docs/network/vlans.md create mode 100644 clients/kittle/docs/overview.md create mode 100644 clients/kittle/docs/rmm/rmm.md create mode 100644 clients/kittle/docs/scripts/fix-pdf-preview.ps1 create mode 100644 clients/kittle/docs/scripts/gpo-intranet-zone.ps1 create mode 100644 clients/kittle/docs/security/antivirus.md create mode 100644 clients/kittle/docs/security/backup.md create mode 100644 clients/kittle/docs/servers/server.md create mode 100644 clients/kittle/docs/servers/server_template.md create mode 100644 clients/lens-auto-brokerage/docs/billing-log.md create mode 100644 clients/lens-auto-brokerage/docs/issues/log.md create mode 100644 clients/lens-auto-brokerage/docs/overview.md create mode 100644 projects/msp-tools/msp-audit-scripts/README.md create mode 100644 projects/msp-tools/msp-audit-scripts/server_audit.ps1 create mode 100644 projects/msp-tools/msp-audit-scripts/workstation_audit.ps1 create mode 100644 projects/msp-tools/utilities/clean_printer_ports.ps1 create mode 100644 projects/msp-tools/utilities/screenconnect-toolbox-commands.txt create mode 100644 projects/msp-tools/utilities/win11_upgrade.ps1 create mode 100644 session-logs/2026-04-16-howard-client-docs-import.md diff --git a/.claude/memory/MEMORY.md b/.claude/memory/MEMORY.md index 523afc5..8a4c388 100644 --- a/.claude/memory/MEMORY.md +++ b/.claude/memory/MEMORY.md @@ -10,6 +10,8 @@ - [Matomo Analytics](reference_matomo_analytics.md) - Self-hosted analytics at analytics.azcomputerguru.com, site IDs, tracking for all 3 sites - [Dataforth Contact - AJ](reference_dataforth_contact.md) - AJ at Dataforth, dataforthgit@ email forwarding to him - [TickTick Integration](reference_ticktick_integration.md) - OAuth API integration, MCP server, SOPS vault creds, project/task CRUD +- [Client Docs Structure](reference_client_docs_structure.md) - clients//docs/ layout (overview, network, servers, cloud, security, rmm, issues). Template at clients/_client_template/. +- [MSP Audit Scripts](reference_msp_audit_scripts.md) - server_audit.ps1 / workstation_audit.ps1 at projects/msp-tools/msp-audit-scripts/. ScreenConnect 80-char rule. ## Users - [Howard Enos](user_howard.md) — Mike's brother, technician, full trust/access. Known machine: ACG-TECH03L. diff --git a/.claude/memory/reference_client_docs_structure.md b/.claude/memory/reference_client_docs_structure.md new file mode 100644 index 0000000..5034342 --- /dev/null +++ b/.claude/memory/reference_client_docs_structure.md @@ -0,0 +1,33 @@ +--- +name: Client Documentation Structure +description: Howard's MSP client docs live under clients//docs/ with a standard subfolder layout (overview, network, servers, cloud, security, rmm, issues). Template at clients/_client_template/. +type: reference +--- + +Each active client has structured Markdown documentation under `clients//docs/`: + +| File / Folder | Purpose | +|---|---| +| `overview.md` | Company info, contacts, environment summary, device counts | +| `network/topology.md` | Switches, APs, cabling, interconnects | +| `network/vlans.md` | VLAN table, subnets, inter-VLAN routing | +| `network/dns.md` | DNS servers, zones, records, forwarders | +| `network/dhcp.md` | Scopes, reservations, relay config | +| `network/firewall.md` | Rules, NAT, VPN, interfaces | +| `network/wifi.md` | SSIDs, security, AP assignments | +| `servers/.md` | Per-server docs (use `server_template.md`) | +| `cloud/m365.md` | Tenant, licensing, Exchange, Entra ID | +| `cloud/azure.md` | Subscriptions, VMs, networking | +| `security/antivirus.md` | EDR/AV product, deployment status | +| `security/backup.md` | Backup jobs, targets, DR plan | +| `rmm/rmm.md` | RMM product, agent counts, patch policy | +| `issues/log.md` | Historical incident log with root causes | +| `billing-log.md` | Per-client billing / work log | + +Clients currently documented (imported 2026-04-16 from Howard's `C:\Users\howar\Clients`): +anaise, cascades-tucson, dataforth, instrumental-music-center, khalsa, kittle, lens-auto-brokerage. + +Credentials NEVER go inline in these docs — reference SOPS vault instead: +`clients//.sops.yaml` field path. + +The template at `clients/_client_template/` is the scaffold for new clients. diff --git a/.claude/memory/reference_msp_audit_scripts.md b/.claude/memory/reference_msp_audit_scripts.md new file mode 100644 index 0000000..bd58df4 --- /dev/null +++ b/.claude/memory/reference_msp_audit_scripts.md @@ -0,0 +1,29 @@ +--- +name: MSP Audit Scripts +description: server_audit.ps1 and workstation_audit.ps1 for on-demand auditing via ScreenConnect Toolbox. Also hosted on GitHub (Howweird/msp-audit-scripts) for remote fetch. +type: reference +--- + +Location in claudetools: `projects/msp-tools/msp-audit-scripts/`. + +Scripts: +- `server_audit.ps1` — Full server + AD + security audit, outputs JSON to `C:\Temp\`. +- `workstation_audit.ps1` — Full workstation audit, outputs JSON to `C:\Temp\`. +- `README.md` — Usage notes. + +Remote fetch URL pattern (for ScreenConnect Toolbox): +``` +https://raw.githubusercontent.com/Howweird/msp-audit-scripts/master/server_audit.ps1 +https://raw.githubusercontent.com/Howweird/msp-audit-scripts/master/workstation_audit.ps1 +``` + +ScreenConnect Toolbox PowerShell rules (IMPORTANT): +- No line may exceed 80 chars — Toolbox silently truncates long lines +- Store long URLs/paths in variables first +- Use multi-line try/catch blocks, never single-line +- Paste whole scripts as one command — no inline comments between blocks + +Utility scripts also at `projects/msp-tools/utilities/`: +- `clean_printer_ports.ps1` +- `win11_upgrade.ps1` +- `screenconnect-toolbox-commands.txt` (saved Toolbox one-liners) diff --git a/clients/_client_template/cloud/azure.md b/clients/_client_template/cloud/azure.md new file mode 100644 index 0000000..4c7e869 --- /dev/null +++ b/clients/_client_template/cloud/azure.md @@ -0,0 +1,28 @@ +# Azure / Cloud Services + +## Azure Subscription +- Subscription Name: +- Subscription ID: +- Resource Group(s): +- Region: +- Monthly Spend (approx): + +## Virtual Machines +| VM Name | Size | OS | IP | Purpose | +|---------------|------------|------------|------------|-----------------| +| | | | | | + +## Networking +- Virtual Network: +- Address Space: +- Subnets: +- VPN Gateway to On-Prem: Yes/No +- ExpressRoute: Yes/No + +## Other Cloud Services + +| Service | Purpose | Admin URL | Notes | +|-----------------|------------------|------------------|-----------------| +| | | | | + +## Notes diff --git a/clients/_client_template/cloud/m365.md b/clients/_client_template/cloud/m365.md new file mode 100644 index 0000000..dc32af2 --- /dev/null +++ b/clients/_client_template/cloud/m365.md @@ -0,0 +1,52 @@ +# Microsoft 365 + +## Tenant Info +- Tenant Name: +- Tenant ID: +- Primary Domain: +- Admin Portal URL: https://admin.microsoft.com + +## Licensing +| License Type | Quantity | Assigned | Available | +|--------------------------|----------|----------|-----------| +| Microsoft 365 Business Basic | | | | +| Microsoft 365 Business Standard | | | | +| Microsoft 365 Business Premium | | | | +| Exchange Online Plan 1/2 | | | | +| Other | | | | + +## Exchange Online +- Mail Domain(s): +- MX Record Points To: +- SPF Record: +- DKIM Enabled: Yes/No +- DMARC Policy: +- Shared Mailboxes: +- Distribution Groups: +- Mail Flow Rules: Yes/No (describe below) + +## SharePoint / OneDrive +- SharePoint Sites: +- External Sharing: Enabled/Disabled +- OneDrive Storage Limit: + +## Teams +- Teams Phone System: Yes/No +- Calling Plan / Direct Routing: +- Auto Attendant: + +## Entra ID (Azure AD) +- Hybrid Joined: Yes/No +- Azure AD Connect Server: +- Sync Schedule: +- Password Hash Sync: Yes/No +- MFA Enforced: Yes/No +- Conditional Access Policies: + +## Security +- Defender for Office 365: Yes/No +- Safe Links: Yes/No +- Safe Attachments: Yes/No +- Audit Log Retention: + +## Notes diff --git a/clients/_client_template/issues/log.md b/clients/_client_template/issues/log.md new file mode 100644 index 0000000..dd4b53e --- /dev/null +++ b/clients/_client_template/issues/log.md @@ -0,0 +1,19 @@ +# Issue Log + +Record past issues and their resolutions here. This helps the AI learn from historical +troubleshooting and avoid repeating failed approaches. + +## Template + +### [DATE] - [Brief Description] +- **Reported By:** +- **Severity:** Low / Medium / High / Critical +- **Symptoms:** +- **Root Cause:** +- **Resolution:** +- **Time to Resolve:** +- **Lessons Learned:** + +--- + + diff --git a/clients/_client_template/network/dhcp.md b/clients/_client_template/network/dhcp.md new file mode 100644 index 0000000..dc7ad3f --- /dev/null +++ b/clients/_client_template/network/dhcp.md @@ -0,0 +1,31 @@ +# DHCP Configuration + +## DHCP Server +- Server Name: +- Server IP: +- Failover Partner: + +## Scopes + +### Scope - [VLAN Name] +- Subnet: +- Range Start: +- Range End: +- Subnet Mask: +- Default Gateway: +- DNS Servers: +- Lease Duration: +- Exclusions: + + + +## Reservations +| Device Name | MAC Address | IP Address | Scope | Notes | +|-----------------|-------------------|-----------------|---------------|---------------| +| | | | | | + +## DHCP Relay +- Relay agents configured on: +- Helper address: + +## Notes diff --git a/clients/_client_template/network/dns.md b/clients/_client_template/network/dns.md new file mode 100644 index 0000000..7bf8186 --- /dev/null +++ b/clients/_client_template/network/dns.md @@ -0,0 +1,33 @@ +# DNS Configuration + +## Internal DNS Servers +| Server Name | IP Address | Role | +|-------------|-----------|-------------------| +| | | Primary | +| | | Secondary | + +## DNS Forwarders +- Forwarder 1: +- Forwarder 2: + +## Conditional Forwarders +| Domain | Forward To | Purpose | +|----------------------|-----------------|-------------------| +| | | | + +## Key DNS Records +| Record Type | Name | Value | Notes | +|-------------|------------------|------------------|------------------| +| A | | | | +| CNAME | | | | +| MX | | | | +| TXT | | | | + +## External DNS +- Registrar: +- Hosted At: +- Primary Domain: +- Management URL: + +## Notes + diff --git a/clients/_client_template/network/firewall.md b/clients/_client_template/network/firewall.md new file mode 100644 index 0000000..21d8c8e --- /dev/null +++ b/clients/_client_template/network/firewall.md @@ -0,0 +1,47 @@ +# Firewall Configuration + +## Device Info +- Vendor/Model: +- Firmware Version: +- Management IP: +- Management URL: +- HA Pair: Yes/No +- License Expiry: + +## Interfaces +| Interface | Zone | IP Address | VLAN | Description | +|-----------|-----------|-----------------|------|-------------------| +| WAN1 | WAN | | | Primary Internet | +| WAN2 | WAN | | | Backup Internet | +| LAN | LAN | | | | +| DMZ | DMZ | | | | + +## NAT Rules +| Name | Source | Destination | Port(s) | NAT To | +|-------------------|---------------|----------------|-------------|-----------------| +| | | | | | + +## Key Firewall Policies +| Name | Source Zone | Dest Zone | Service | Action | Notes | +|-------------------|--------------|---------------|-------------|--------|--------| +| | | | | | | + +## VPN +### Site-to-Site VPNs +| Peer Name | Peer IP | Local Subnet | Remote Subnet | Status | +|-------------------|--------------|----------------|---------------|--------| +| | | | | | + +### SSL/Client VPN +- Enabled: Yes/No +- Portal URL: +- Auth Method: +- IP Pool: +- Split Tunnel: Yes/No + +## Content Filtering +- Web Filter Profile: +- App Control Profile: +- DNS Filter: + +## Notes diff --git a/clients/_client_template/network/topology.md b/clients/_client_template/network/topology.md new file mode 100644 index 0000000..740cf09 --- /dev/null +++ b/clients/_client_template/network/topology.md @@ -0,0 +1,43 @@ +# Network Topology + +## Internet Connection +- ISP: +- Circuit Type: +- Speed (Down/Up): +- Public IP: +- Gateway: +- Modem Model: + +## Core Switch +- Model: +- IP Address: +- Management URL: +- Firmware Version: +- Location: + +## Additional Switches + +### Switch - [Name/Location] +- Model: +- IP Address: +- Port Count: +- PoE: Yes/No +- Uplink To: + +## Wireless +- Controller Model: +- Controller IP: +- Number of APs: +- AP Model(s): + +### Access Points + +- AP Name: +- Location: +- IP Address: +- Connected Switch/Port: + +## WAN / SD-WAN +- SD-WAN Vendor: +- Number of Sites: +- Hub Site: diff --git a/clients/_client_template/network/vlans.md b/clients/_client_template/network/vlans.md new file mode 100644 index 0000000..475f778 --- /dev/null +++ b/clients/_client_template/network/vlans.md @@ -0,0 +1,21 @@ +# VLANs + +## VLAN Table + +| VLAN ID | Name | Subnet | Gateway | DHCP Scope | Purpose | +|---------|---------------|-----------------|-----------------|------------------|------------------------| +| 1 | Default | | | | | +| 10 | Management | | | | Network devices | +| 20 | Servers | | | | Server infrastructure | +| 30 | Workstations | | | | End user devices | +| 40 | VoIP | | | | Phone system | +| 50 | WiFi-Corp | | | | Corporate wireless | +| 60 | WiFi-Guest | | | | Guest wireless | +| 100 | Security | | | | Cameras / access ctrl | + +## Inter-VLAN Routing +- Performed by: +- Routing device IP: + +## VLAN Notes + diff --git a/clients/_client_template/overview.md b/clients/_client_template/overview.md new file mode 100644 index 0000000..54e0d83 --- /dev/null +++ b/clients/_client_template/overview.md @@ -0,0 +1,31 @@ +# Client Overview + +## Company Name + + +## Primary Contact +- Name: +- Phone: +- Email: + +## IT Contact +- Name: +- Phone: +- Email: + +## Contract Details +- Service Level: +- Hours Covered: +- Contract Renewal Date: + +## Environment Summary +- Total Users: +- Total Locations: +- Domain Name: +- Primary Site Address: +- RMM Agent Count: +- Workstation Count: +- Server Count: + +## Notes + diff --git a/clients/_client_template/rmm/rmm.md b/clients/_client_template/rmm/rmm.md new file mode 100644 index 0000000..819596b --- /dev/null +++ b/clients/_client_template/rmm/rmm.md @@ -0,0 +1,34 @@ +# RMM / Monitoring + +## RMM Solution +- Product: +- Console URL: +- Agent Version: + +## Agent Deployment +- Total Devices: +- Servers Monitored: +- Workstations Monitored: +- Network Devices Monitored: + +## Monitoring Policies +| Policy Name | Applies To | Alert Condition | Action | +|-------------------|----------------|-------------------------|---------------| +| Disk Space | All Servers | < 10% free | Alert + Ticket| +| CPU | All Servers | > 90% for 15 min | Alert | +| Service Monitor | All Servers | | | +| Backup Monitor | | | | +| Offline Alert | All Agents | Offline > 30 min | Alert | + +## Patch Management +- Patch Policy: +- Patch Window: +- Auto-approve: Yes/No +- Exclusions: + +## Scripting / Automation +| Script Name | Schedule | Purpose | +|---------------------|-------------|--------------------------| +| | | | + +## Notes diff --git a/clients/_client_template/security/antivirus.md b/clients/_client_template/security/antivirus.md new file mode 100644 index 0000000..d495dfc --- /dev/null +++ b/clients/_client_template/security/antivirus.md @@ -0,0 +1,26 @@ +# Endpoint Security / Antivirus + +## Solution +- Product: +- Console URL: +- License Count: +- License Expiry: +- Managed By: + +## Policy +- Real-time Protection: Yes/No +- Scheduled Scans: (frequency) +- Exclusions: + +## Deployment Status +- Total Endpoints: +- Protected: +- Missing Agent: +- Out of Date: + +## EDR / XDR +- EDR Enabled: Yes/No +- Product: +- Console URL: + +## Notes diff --git a/clients/_client_template/security/backup.md b/clients/_client_template/security/backup.md new file mode 100644 index 0000000..4ed13a4 --- /dev/null +++ b/clients/_client_template/security/backup.md @@ -0,0 +1,34 @@ +# Backup and Disaster Recovery + +## Backup Solution +- Product: +- Console URL: +- License/Subscription: + +## Backup Targets +| Target Name | Type | Location | Capacity | Encrypted | +|----------------|----------------|-----------------|--------------|-----------| +| | Local NAS | | | Yes/No | +| | Cloud | | | Yes/No | +| | Offsite | | | Yes/No | + +## Backup Jobs +| Job Name | Source | Target | Schedule | Retention | Status | +|-----------------|-------------------|------------|---------------|-------------|--------| +| | | | | | | + +## M365 Backup +- M365 Backup Product: +- Exchange Backed Up: Yes/No +- SharePoint Backed Up: Yes/No +- OneDrive Backed Up: Yes/No +- Teams Backed Up: Yes/No + +## Disaster Recovery Plan +- RTO Target: +- RPO Target: +- DR Site: +- Last DR Test Date: +- DR Test Result: + +## Notes diff --git a/clients/_client_template/servers/server_template.md b/clients/_client_template/servers/server_template.md new file mode 100644 index 0000000..d35ab32 --- /dev/null +++ b/clients/_client_template/servers/server_template.md @@ -0,0 +1,49 @@ +# Server: [SERVER NAME] + +## General Info +- Hostname: +- IP Address: +- OS: +- OS Version: +- Physical / Virtual: +- Host (if virtual): +- Location: +- Last Patched: + +## Hardware (if physical) +- Make/Model: +- CPU: +- RAM: +- Storage: +- Warranty Expiry: + +## Roles and Services + +- [ ] Domain Controller +- [ ] DNS Server +- [ ] DHCP Server +- [ ] File Server +- [ ] Print Server +- [ ] Application Server +- [ ] Database Server +- [ ] Backup Target +- [ ] RDS / Terminal Server +- [ ] Hyper-V Host + +## Shares (if file server) +| Share Name | Path | Permissions Group | Notes | +|---------------|-------------------|---------------------|----------------| +| | | | | + +## Applications Installed +| Application | Version | Purpose | License | +|-------------------|------------|----------------------|---------------| +| | | | | + +## Backup +- Backup Method: +- Backup Schedule: +- Backup Target: +- Last Verified Restore: + +## Notes diff --git a/clients/anaise/docs/cloud/azure.md b/clients/anaise/docs/cloud/azure.md new file mode 100644 index 0000000..4c7e869 --- /dev/null +++ b/clients/anaise/docs/cloud/azure.md @@ -0,0 +1,28 @@ +# Azure / Cloud Services + +## Azure Subscription +- Subscription Name: +- Subscription ID: +- Resource Group(s): +- Region: +- Monthly Spend (approx): + +## Virtual Machines +| VM Name | Size | OS | IP | Purpose | +|---------------|------------|------------|------------|-----------------| +| | | | | | + +## Networking +- Virtual Network: +- Address Space: +- Subnets: +- VPN Gateway to On-Prem: Yes/No +- ExpressRoute: Yes/No + +## Other Cloud Services + +| Service | Purpose | Admin URL | Notes | +|-----------------|------------------|------------------|-----------------| +| | | | | + +## Notes diff --git a/clients/anaise/docs/cloud/m365.md b/clients/anaise/docs/cloud/m365.md new file mode 100644 index 0000000..dc32af2 --- /dev/null +++ b/clients/anaise/docs/cloud/m365.md @@ -0,0 +1,52 @@ +# Microsoft 365 + +## Tenant Info +- Tenant Name: +- Tenant ID: +- Primary Domain: +- Admin Portal URL: https://admin.microsoft.com + +## Licensing +| License Type | Quantity | Assigned | Available | +|--------------------------|----------|----------|-----------| +| Microsoft 365 Business Basic | | | | +| Microsoft 365 Business Standard | | | | +| Microsoft 365 Business Premium | | | | +| Exchange Online Plan 1/2 | | | | +| Other | | | | + +## Exchange Online +- Mail Domain(s): +- MX Record Points To: +- SPF Record: +- DKIM Enabled: Yes/No +- DMARC Policy: +- Shared Mailboxes: +- Distribution Groups: +- Mail Flow Rules: Yes/No (describe below) + +## SharePoint / OneDrive +- SharePoint Sites: +- External Sharing: Enabled/Disabled +- OneDrive Storage Limit: + +## Teams +- Teams Phone System: Yes/No +- Calling Plan / Direct Routing: +- Auto Attendant: + +## Entra ID (Azure AD) +- Hybrid Joined: Yes/No +- Azure AD Connect Server: +- Sync Schedule: +- Password Hash Sync: Yes/No +- MFA Enforced: Yes/No +- Conditional Access Policies: + +## Security +- Defender for Office 365: Yes/No +- Safe Links: Yes/No +- Safe Attachments: Yes/No +- Audit Log Retention: + +## Notes diff --git a/clients/anaise/docs/issues/log.md b/clients/anaise/docs/issues/log.md new file mode 100644 index 0000000..dd4b53e --- /dev/null +++ b/clients/anaise/docs/issues/log.md @@ -0,0 +1,19 @@ +# Issue Log + +Record past issues and their resolutions here. This helps the AI learn from historical +troubleshooting and avoid repeating failed approaches. + +## Template + +### [DATE] - [Brief Description] +- **Reported By:** +- **Severity:** Low / Medium / High / Critical +- **Symptoms:** +- **Root Cause:** +- **Resolution:** +- **Time to Resolve:** +- **Lessons Learned:** + +--- + + diff --git a/clients/anaise/docs/network/dhcp.md b/clients/anaise/docs/network/dhcp.md new file mode 100644 index 0000000..dc7ad3f --- /dev/null +++ b/clients/anaise/docs/network/dhcp.md @@ -0,0 +1,31 @@ +# DHCP Configuration + +## DHCP Server +- Server Name: +- Server IP: +- Failover Partner: + +## Scopes + +### Scope - [VLAN Name] +- Subnet: +- Range Start: +- Range End: +- Subnet Mask: +- Default Gateway: +- DNS Servers: +- Lease Duration: +- Exclusions: + + + +## Reservations +| Device Name | MAC Address | IP Address | Scope | Notes | +|-----------------|-------------------|-----------------|---------------|---------------| +| | | | | | + +## DHCP Relay +- Relay agents configured on: +- Helper address: + +## Notes diff --git a/clients/anaise/docs/network/dns.md b/clients/anaise/docs/network/dns.md new file mode 100644 index 0000000..7bf8186 --- /dev/null +++ b/clients/anaise/docs/network/dns.md @@ -0,0 +1,33 @@ +# DNS Configuration + +## Internal DNS Servers +| Server Name | IP Address | Role | +|-------------|-----------|-------------------| +| | | Primary | +| | | Secondary | + +## DNS Forwarders +- Forwarder 1: +- Forwarder 2: + +## Conditional Forwarders +| Domain | Forward To | Purpose | +|----------------------|-----------------|-------------------| +| | | | + +## Key DNS Records +| Record Type | Name | Value | Notes | +|-------------|------------------|------------------|------------------| +| A | | | | +| CNAME | | | | +| MX | | | | +| TXT | | | | + +## External DNS +- Registrar: +- Hosted At: +- Primary Domain: +- Management URL: + +## Notes + diff --git a/clients/anaise/docs/network/firewall.md b/clients/anaise/docs/network/firewall.md new file mode 100644 index 0000000..21d8c8e --- /dev/null +++ b/clients/anaise/docs/network/firewall.md @@ -0,0 +1,47 @@ +# Firewall Configuration + +## Device Info +- Vendor/Model: +- Firmware Version: +- Management IP: +- Management URL: +- HA Pair: Yes/No +- License Expiry: + +## Interfaces +| Interface | Zone | IP Address | VLAN | Description | +|-----------|-----------|-----------------|------|-------------------| +| WAN1 | WAN | | | Primary Internet | +| WAN2 | WAN | | | Backup Internet | +| LAN | LAN | | | | +| DMZ | DMZ | | | | + +## NAT Rules +| Name | Source | Destination | Port(s) | NAT To | +|-------------------|---------------|----------------|-------------|-----------------| +| | | | | | + +## Key Firewall Policies +| Name | Source Zone | Dest Zone | Service | Action | Notes | +|-------------------|--------------|---------------|-------------|--------|--------| +| | | | | | | + +## VPN +### Site-to-Site VPNs +| Peer Name | Peer IP | Local Subnet | Remote Subnet | Status | +|-------------------|--------------|----------------|---------------|--------| +| | | | | | + +### SSL/Client VPN +- Enabled: Yes/No +- Portal URL: +- Auth Method: +- IP Pool: +- Split Tunnel: Yes/No + +## Content Filtering +- Web Filter Profile: +- App Control Profile: +- DNS Filter: + +## Notes diff --git a/clients/anaise/docs/network/topology.md b/clients/anaise/docs/network/topology.md new file mode 100644 index 0000000..740cf09 --- /dev/null +++ b/clients/anaise/docs/network/topology.md @@ -0,0 +1,43 @@ +# Network Topology + +## Internet Connection +- ISP: +- Circuit Type: +- Speed (Down/Up): +- Public IP: +- Gateway: +- Modem Model: + +## Core Switch +- Model: +- IP Address: +- Management URL: +- Firmware Version: +- Location: + +## Additional Switches + +### Switch - [Name/Location] +- Model: +- IP Address: +- Port Count: +- PoE: Yes/No +- Uplink To: + +## Wireless +- Controller Model: +- Controller IP: +- Number of APs: +- AP Model(s): + +### Access Points + +- AP Name: +- Location: +- IP Address: +- Connected Switch/Port: + +## WAN / SD-WAN +- SD-WAN Vendor: +- Number of Sites: +- Hub Site: diff --git a/clients/anaise/docs/network/vlans.md b/clients/anaise/docs/network/vlans.md new file mode 100644 index 0000000..475f778 --- /dev/null +++ b/clients/anaise/docs/network/vlans.md @@ -0,0 +1,21 @@ +# VLANs + +## VLAN Table + +| VLAN ID | Name | Subnet | Gateway | DHCP Scope | Purpose | +|---------|---------------|-----------------|-----------------|------------------|------------------------| +| 1 | Default | | | | | +| 10 | Management | | | | Network devices | +| 20 | Servers | | | | Server infrastructure | +| 30 | Workstations | | | | End user devices | +| 40 | VoIP | | | | Phone system | +| 50 | WiFi-Corp | | | | Corporate wireless | +| 60 | WiFi-Guest | | | | Guest wireless | +| 100 | Security | | | | Cameras / access ctrl | + +## Inter-VLAN Routing +- Performed by: +- Routing device IP: + +## VLAN Notes + diff --git a/clients/anaise/docs/overview.md b/clients/anaise/docs/overview.md new file mode 100644 index 0000000..746b635 --- /dev/null +++ b/clients/anaise/docs/overview.md @@ -0,0 +1,26 @@ +# Client Overview + +## Company Name +Anaise + +## Primary Contact +- Name: David +- Email: anaisedavid.office@gmail.com + +## Workstations + +| Machine | Username | OS | Notes | +|---------|----------|-----|-------| +| DESKTOP-O8GF4SD | david | | | + +## Credentials + +Stored in SOPS vault. Do not put plaintext passwords in this file. + +| Machine | Username | Vault Reference | +|---------|----------|-----------------| +| DESKTOP-O8GF4SD | david | `clients/anaise/desktop-o8gf4sd.sops.yaml` → `credentials.password` | + +Retrieve with: `bash C:/vault/scripts/vault.sh get-field clients/anaise/desktop-o8gf4sd.sops.yaml credentials.password` + +## Notes diff --git a/clients/anaise/docs/rmm/rmm.md b/clients/anaise/docs/rmm/rmm.md new file mode 100644 index 0000000..819596b --- /dev/null +++ b/clients/anaise/docs/rmm/rmm.md @@ -0,0 +1,34 @@ +# RMM / Monitoring + +## RMM Solution +- Product: +- Console URL: +- Agent Version: + +## Agent Deployment +- Total Devices: +- Servers Monitored: +- Workstations Monitored: +- Network Devices Monitored: + +## Monitoring Policies +| Policy Name | Applies To | Alert Condition | Action | +|-------------------|----------------|-------------------------|---------------| +| Disk Space | All Servers | < 10% free | Alert + Ticket| +| CPU | All Servers | > 90% for 15 min | Alert | +| Service Monitor | All Servers | | | +| Backup Monitor | | | | +| Offline Alert | All Agents | Offline > 30 min | Alert | + +## Patch Management +- Patch Policy: +- Patch Window: +- Auto-approve: Yes/No +- Exclusions: + +## Scripting / Automation +| Script Name | Schedule | Purpose | +|---------------------|-------------|--------------------------| +| | | | + +## Notes diff --git a/clients/anaise/docs/security/antivirus.md b/clients/anaise/docs/security/antivirus.md new file mode 100644 index 0000000..d495dfc --- /dev/null +++ b/clients/anaise/docs/security/antivirus.md @@ -0,0 +1,26 @@ +# Endpoint Security / Antivirus + +## Solution +- Product: +- Console URL: +- License Count: +- License Expiry: +- Managed By: + +## Policy +- Real-time Protection: Yes/No +- Scheduled Scans: (frequency) +- Exclusions: + +## Deployment Status +- Total Endpoints: +- Protected: +- Missing Agent: +- Out of Date: + +## EDR / XDR +- EDR Enabled: Yes/No +- Product: +- Console URL: + +## Notes diff --git a/clients/anaise/docs/security/backup.md b/clients/anaise/docs/security/backup.md new file mode 100644 index 0000000..4ed13a4 --- /dev/null +++ b/clients/anaise/docs/security/backup.md @@ -0,0 +1,34 @@ +# Backup and Disaster Recovery + +## Backup Solution +- Product: +- Console URL: +- License/Subscription: + +## Backup Targets +| Target Name | Type | Location | Capacity | Encrypted | +|----------------|----------------|-----------------|--------------|-----------| +| | Local NAS | | | Yes/No | +| | Cloud | | | Yes/No | +| | Offsite | | | Yes/No | + +## Backup Jobs +| Job Name | Source | Target | Schedule | Retention | Status | +|-----------------|-------------------|------------|---------------|-------------|--------| +| | | | | | | + +## M365 Backup +- M365 Backup Product: +- Exchange Backed Up: Yes/No +- SharePoint Backed Up: Yes/No +- OneDrive Backed Up: Yes/No +- Teams Backed Up: Yes/No + +## Disaster Recovery Plan +- RTO Target: +- RPO Target: +- DR Site: +- Last DR Test Date: +- DR Test Result: + +## Notes diff --git a/clients/anaise/docs/servers/server_template.md b/clients/anaise/docs/servers/server_template.md new file mode 100644 index 0000000..d35ab32 --- /dev/null +++ b/clients/anaise/docs/servers/server_template.md @@ -0,0 +1,49 @@ +# Server: [SERVER NAME] + +## General Info +- Hostname: +- IP Address: +- OS: +- OS Version: +- Physical / Virtual: +- Host (if virtual): +- Location: +- Last Patched: + +## Hardware (if physical) +- Make/Model: +- CPU: +- RAM: +- Storage: +- Warranty Expiry: + +## Roles and Services + +- [ ] Domain Controller +- [ ] DNS Server +- [ ] DHCP Server +- [ ] File Server +- [ ] Print Server +- [ ] Application Server +- [ ] Database Server +- [ ] Backup Target +- [ ] RDS / Terminal Server +- [ ] Hyper-V Host + +## Shares (if file server) +| Share Name | Path | Permissions Group | Notes | +|---------------|-------------------|---------------------|----------------| +| | | | | + +## Applications Installed +| Application | Version | Purpose | License | +|-------------------|------------|----------------------|---------------| +| | | | | + +## Backup +- Backup Method: +- Backup Schedule: +- Backup Target: +- Last Verified Restore: + +## Notes diff --git a/clients/cascades-tucson/docs/billing-log.md b/clients/cascades-tucson/docs/billing-log.md new file mode 100644 index 0000000..177053a --- /dev/null +++ b/clients/cascades-tucson/docs/billing-log.md @@ -0,0 +1,488 @@ +# Cascades — Work Log / Billing Record + +## Session 1 — 2026-03-06 (Remote) + +**Focus:** Initial audit, data gathering, documentation buildout + +| Time | Task | Details | +|------|------|---------| +| | Initial server audit | Gathered systeminfo, AD users/computers/groups, DNS records, installed software, Hyper-V VMs, listening ports, disk info from CS-SERVER | +| | Network audit | Reviewed pfSense config (interfaces, firewall rules, VLANs, DHCP), UniFi APs/switches/SSIDs | +| | ARP/DHCP dump | Captured 802 ARP entries, 624 DHCP leases, identified all devices on network | +| | Printer inventory | Documented all printers with IPs, MACs, models, status | +| | Workstation inventory | Documented all PCs on INTERNAL and LAN with MACs, status, domain join state | +| | MDIRECTOR-PC audit | Gathered OS info (Win10 Home), users, network config via ScreenConnect | +| | Synology audit | Documented shares, storage capacity, permission report | +| | Full documentation buildout | Created/updated all .md files: overview, network/*, servers/*, security/*, migration/* | +| | Migration plan | Created phased migration plan with runbooks and PowerShell scripts | +| | CLAUDE.md | Created repo-level guidance file for AI tooling | + +--- + +## Session 2 — 2026-03-06 (Remote) + +**Focus:** Guest WiFi isolation, DNS fixes, security hardening + +| Time | Task | Details | +|------|------|---------| +| | Guest WiFi isolation | Created VLAN 50 on pfSense (igc1.50, 10.0.50.1/24), DHCP scope, 4 firewall rules, UniFi Guest network, reassigned Guest SSID | +| | ~~RFC1918 alias~~ | ~~Created firewall alias~~ **CORRECTION (Session 6):** Never actually created. Using built-in `_private4_` alias instead. | +| | CS-SERVER DNS client fix | Changed DNS servers from pfSense+8.8.8.8 to 127.0.0.1+192.168.0.1, verified | +| | Stale DNS cleanup | Removed 9 stale records, added 3 correct records (@ → 192.168.2.254, DomainDnsZones, ForestDnsZones) | +| | pfSense domain overrides | Added cascades.local + _msdcs.cascades.local → 192.168.2.254 | +| | Reverse lookup zones | Created 5 zones (0/1/2/3.168.192 + 20.0.10.in-addr.arpa) | +| | DNS scavenging | Enabled server-level scavenging (7-day), zone aging on cascades.local | +| | Documentation updates | Updated all affected .md files to reflect changes | + +--- + +## Session 3 — 2026-03-07 (Remote) + +**Focus:** Backup setup, config exports, quick fixes, network diagnostics + +| Time | Task | Details | +|------|------|---------| +| | CS-SERVER DNS forwarder verified | Confirmed forwarder is 192.168.0.1 (item G) | +| | CS-SERVER timezone fixed | Changed from Pacific to Arizona (UTC-07:00, no DST) to match pfSense | +| | Room 218 DHCP fixed | Changed range end from 10.2.18.2 to 10.2.18.14 in pfSense | +| | Room 130 firewall rule deleted | Removed disabled TCP PASS rule from Room130 interface | +| | pfSense config exported | Downloaded XML config (with and without RRD data), saved to D:\Shares\IT\Backups\pfSense\ | +| | Synology Active Backup for Business | Installed on Synology — **BLOCKED: requires Btrfs, NAS is ext4.** Cannot use ABB. Will use Windows Server Backup instead. | +| | Synology Drive Client | Reinstalled on CS-SERVER, configured live sync to D:\Shares\Main (all Synology shares) | +| | Synology share audit | Enumerated shares via SMB: homes (228 GB), Public (50 GB), SalesDept (13 GB), Server (2 GB), Management (1.4 GB), chat (0), home (0). Total ~294 GB. 4 shares (Activities, pacs, Sandra Fish, web) not visible via SMB. | +| | ARP flapping investigation | Analyzed pfSense ARP logs, found 5 IP conflicts | +| | LG TV ARP conflict fixed | TV was dual-connected (WiFi + ethernet). Disabled ethernet port on 1st Floor USW Port 18. Flapping resolved. | +| | Brother printer conflict identified | 192.168.2.53 — printer dual-connected (WiFi + ethernet). Needs onsite fix. | +| | Minor ARP conflicts triaged | Room 307, Room 130, iPhone MAC randomization — low priority, noted for onsite | +| | AD/DNS/Permissions exported | Exported users, computers, groups, domain admins, DNS records, zones, forwarders, SMB shares, GPOs to D:\Shares\IT\Backups\ | +| | AD export analysis | Identified: 3 non-IT users in Domain Admins, 12 accounts to remove, 3 undocumented GPOs from Dec 2025, most users never logged in | +| | GPO report export + analysis | Exported full GPO report (Get-GPOReport -All). Reviewed all 6 GPOs: 3 Dec 2025 GPOs (CopyRoomPrinter, Nurses-Kiosk, MemCareMedTechPrinter) are completely empty — no settings, no links. Found account lockout disabled (threshold=0) in Default Domain Policy. | +| | Session planning | Created session3 runbook, phase0-remote-checks.ps1 script | +| | Documentation updates | Updated issue log (6 issues resolved), AD docs, backup docs, migration docs, session log | + +--- + +## Session 4 — 2026-03-07 (Remote) + +**Focus:** AD OU structure cleanup planning + script creation + +| Time | Task | Details | +|------|------|---------| +| | AD OU structure audit | Identified 10 duplicate root-level department OUs, 3 empty root OUs (Managment, MemCare, Sales), 20 misplaced accounts in CN=Users | +| | phase2-ou-cleanup.ps1 | Created script: audit root OUs (confirm empty + no GP links), delete 13 root-level OUs, delete/disable stale CN=Users accounts, flag Lupe.Sanchez duplicate | +| | phase2-ad-setup.ps1 updated | Added prerequisite note for OU cleanup, CS-QB exclusion comment | +| | active-directory.md updated | Added current vs target OU structure, CN=Users placement plan, 4 new issues (root OUs, CN=Users, CN=Computers, Lupe.Sanchez) | +| | Issue log updated | Added 2 issues: root-level OU junk, Lupe.Sanchez duplicate | + +--- + +## Session 5 — 2026-03-08 (Remote) + +**Focus:** M365 tenant audit, AD↔M365 identity mapping, shared workstation GPO design + +| Time | Task | Details | +|------|------|---------| +| | M365 tenant documented | Tenant: cascadestucson.com, ID: 207fa277-..., domain: cascadestucson.com, admin: Sandra Fish (admin@NETORGFT4257522.onmicrosoft.com) | +| | User export analysis | Exported 51 M365 users, cross-referenced against 46 AD accounts. Built full AD↔M365 mapping. | +| | Identity mapping | 24 AD accounts matched to M365. 13 AD users have no M365. 2 M365 users (nick pavloff, Kristiana Dowse) not in AD. | +| | License audit | Business Standard 34/34 (0 available). 12 role-based accounts wasting licenses (~$150/mo). Entra ID P2 (1, Sandra Fish). | +| | Shared mailbox audit | 4 shared mailboxes: 3 former employees (Anna Pitzlin, Jeff Bristol, Nela Durut-Azizi) + Fax Cascades | +| | External guest audit | 6 guest accounts: 3 personal emails (jensen, dupras, rossini), 2 Howard accounts (1 typo "howaed"), 1 external partner (Debora Morris) | +| | Name mismatch found | Tamra Johnson (AD) → tamra.matthews@ (M365) — married name not updated in AD | +| | Shared workstation GPO | Added SharedComputers OU to phase2-ad-setup.ps1, GPO 6 design to phase2-server-prep.md, updated AD target OU tree | +| | cloud/m365.md | Fully populated from blank template — tenant info, licensing, full AD↔M365 mapping, shared mailboxes, issues | +| | 11 new issues logged | License exhaustion, role-account waste, Tamra name mismatch, 13 unmapped AD users, nick pavloff, Kristiana Dowse, Sandra Fish admin, former employee mailboxes, howaed typo, no Entra Connect | + +--- + +## Session 6 — 2026-03-09 (Remote + Onsite Data) + +**Focus:** Onsite data entry, printer inventory, AD quick fixes + +| Time | Task | Details | +|------|------|---------| +| | Printer inventory update | Full onsite printer data entered — 15 printers documented with models, SNs, IPs, users, locations. Resolved 6 previously unidentified printers. | +| | Name changes documented | Tamra.Johnson→Matthews, Alyssa.Shestko→Brooks confirmed. Michelle.Shestko→Brooks pending. Updated all docs and scripts. | +| | **Remove Monica.Ramirez from Domain Admins (IMPLEMENTED)** | Removed disabled account from DA group | +| | **Delete 3 empty GPOs (IMPLEMENTED)** | Deleted CopyRoomPrinter, Nurses-Kiosk, MemCareMedTechPrinter — all empty, no links | +| | **Fix account lockout policy (IMPLEMENTED)** | Set lockout threshold to 5 attempts, 30 min duration/observation window | +| | **Rename QuickBooks group (IMPLEMENTED)** | Fixed "Quickboosk acccess" → "QuickBooks Access" | +| | **pfSense aliases created** | Server_IPs (192.168.2.254), NAS_IP (192.168.0.120) created. Printer_IPs, AD_Ports, Print_Ports created then removed — not needed. | +| | Firewall strategy revised | Original plan: scoped INTERNAL→LAN rules for each resource. Revised: move all PCs and printers to INTERNAL VLAN 20 (same subnet), then lock down after migration. Simpler, fewer rules needed. | +| | RFC1918 alias correction | Documented as created in Session 2 but was never actually created. Using built-in `_private4_` alias instead. | +| | **ASSISTNURSE-PC upgraded to Win11 Pro (IMPLEMENTED)** | Upgraded from Windows Home to Windows 11 Pro using product key — enables domain join | + +--- + +## Session 7 — 2026-03-11 (Onsite) + +**Focus:** Quick wins — Guest WiFi test, kitchen thermal printer inventory, printer doc corrections + +| Time | Task | Details | +|------|------|---------| +| | **Guest WiFi isolation tested (VERIFIED)** | Connected to Guest SSID, got 10.0.50.x IP. Fixed DHCP: changed DNS to 8.8.8.8/1.1.1.1, cleared domain name (was cascades.local). Internet works, cannot ping CS-SERVER or access shares — isolation confirmed. | +| | **Guest DHCP DNS fix (IMPLEMENTED)** | GUEST DHCP scope was handing out pfSense DNS + cascades.local domain. Blocked by firewall rules (block all private IPs). Changed to public DNS 8.8.8.8/1.1.1.1, cleared domain name. | +| | **Kitchen thermal printer inventory (DONE)** | 2 printers: Bistro — Epson TM-T88VII (M371A) at 192.168.2.207, Kitchen cooks — Epson TM-U220IIB (M384B) at 10.0.20.225. Both ethernet, both receive orders from 9 iPads. | +| | **"Port 8 Epson" mystery resolved** | Previously unaccounted 192.168.2.207 is the Bistro thermal printer | +| | **MemCare printer corrections** | Room 615 printer (192.168.2.53) is WiFi-only with static IP, NOT dual-connected. MemCare Reception needs dummy switch replaced with UniFi. Added room numbers (615, 603). | +| | **Nick Pavloff clarification** | M365 account is for Synology admin only. Plan: change Synology admin email to another account, then delete Nick's M365 to free license. | +| | **Bistro dummy switch identified** | Bistro has a non-managed switch splitting connection for thermal printer, CC, and other devices. Plan: replace with UniFi switch, set ports to VLAN 20 (CSCNet). Same situation as MemCare reception. | +| | **Bistro printer VLAN move planned** | Bistro Epson TM-T88VII (192.168.2.207) to be moved to CSCNet (VLAN 20) once UniFi switch installed. Test iPad printing after move — cooks printer already on CSCNet (10.0.20.225) so iPads likely already route there. | + +--- + +## Onsite / Remote — Migration Tasks + +### PC Migration (Phase 1.4) — Move to CSCNet WiFi +Connect each PC to CSCNet, forget CSC ENT, verify connectivity. + +| PC | Current IP | User(s) | Status | +|----|-----------|---------|--------| +| RECEPTIONIST-PC | 192.168.2.17 | CJ, Christina, Kyla, Tiffany | [ ] | +| RECEPTIONIST-PC (2nd) | 192.168.3.187 | Receptionist | [ ] | +| ASSISTMAN-PC | 192.168.2.38 | Assistant Manager | [ ] | +| ASSISTNURSE-PC | 192.168.2.153 | Assist Nurse | [ ] WiFi — upgraded to Win11 Pro, move to CSCNet later | +| NURSESTATION-PC | 192.168.3.135 | Nurse Station | [ ] | +| MEMRECEPT-PC | 192.168.3.41 | MemCare Reception | [ ] | +| ANN-PC | 192.168.3.252 | Ann | [ ] | +| MDIRECTOR-PC | 192.168.3.20 | Shelby Trozzi | [ ] Needs Pro upgrade first | +| DESKTOP-LPOPV30 | 192.168.2.250 | Unknown | [ ] | +| DESKTOP-U2DHAP0 | 192.168.3.37 | Unknown | [ ] | +| DESKTOP-TRCIEJA | 192.168.3.93 | Unknown | [ ] | +| DESKTOP-DLTAGOI | 192.168.3.133 | Unknown | [ ] | +| DESKTOP-ROK7VNM | 192.168.3.148 | Unknown | [ ] | +| DESKTOP-MD6UQI3 | 192.168.3.208 | Unknown | [ ] | + +### Printer Migration (Phase 1.5) — Change switch port to VLAN 20 +Requires: identify switch port, change VLAN, DHCP reservation, update PCs. + +| Printer | Current IP | Users | Status | +|---------|-----------|-------|--------| +| Chef Brother | 192.168.3.88 | Chef | [ ] | +| Kitchen Manager Canon | 192.168.3.232 | Alyssa | [ ] | +| Meredith's Canon | 192.168.2.67 | Meredith | [ ] | +| MemCare Director Canon | 192.168.3.52 | Shelby | [ ] | +| MemCare Nurse Brother | 192.168.2.53 | MemCare nurses | [ ] | +| Room 103 Brother | 192.168.2.145 | Ashley, Christina | [ ] | +| Room 132 Canon | 192.168.3.211 | Sharon, Susan | [ ] | +| Room 217 Sales Brother | 192.168.3.44 | Sales team | [ ] | +| Room 206 Bizhub | 192.168.1.138 | Health Services | [ ] | +| Accounting Canon | 192.168.3.227 | Lauren | [ ] | +| Front Desk Epson | 192.168.2.147 | 4 users | [ ] | +| Copy Room Canon | 192.168.2.230 | Everyone | [ ] **LAST** | +| MemCare Reception Epson | — | MemCare Recept | [ ] Needs hardwire first | + +### Other Onsite Tasks + +| Task | Details | +|------|---------| +| ~~Test Guest WiFi isolation~~ | ~~Connect to Guest SSID, verify 10.0.50.x IP, no LAN access~~ **DONE 2026-03-11** | +| Identify unknown devices | DESKTOP-1ISF081, DESKTOP-KQSL232, DESKTOP-VAVKCIM | +| User-to-machine mapping | Document who uses each PC for GPO targeting | +| MDIRECTOR-PC Pro upgrade | Install Windows 10 Pro upgrade key | +| SALES4-PC status | Locate or confirm decommissioned | +| Two RECEPTIONIST-PCs | Determine which is primary | +| 9 offline APs | Check PoE, cables, re-adopt | +| Room 307 ARP conflict | Check if still occurring | + +--- + +## Outstanding Work — Prioritized + +### Priority 1: CRITICAL +- [ ] **Set up backup** — Windows Server Backup to Synology SMB share (ABB blocked by ext4) +- [x] ~~**Remove Monica.Ramirez from Domain Admins**~~ — DONE 2026-03-09 + +### Priority 2: HIGH (security) +- [x] ~~Create firewall aliases~~ — Server_IPs and NAS_IP created. Others not needed (printers moving to INTERNAL VLAN). DONE 2026-03-09 +- [ ] Replace INTERNAL firewall rules — **deferred until after all devices migrated to VLAN 20** +- [ ] Disable floating rule #4 + add scoped room internet rule — **deferred until post-migration** +- [x] ~~Remove Meredith.Kuhn and John.Trozzi from Domain Admins~~ — DONE 2026-04-13 +- [x] ~~Review 3 undocumented GPOs~~ — REVIEWED: all 3 are empty (no settings, no links). Delete in Phase 2.2. +- [x] ~~Delete 3 empty GPOs (CopyRoomPrinter, Nurses-Kiosk, MemCareMedTechPrinter)~~ — DONE 2026-03-09 +- [x] ~~Fix account lockout policy~~ — Set to 5 attempts / 30 min lockout — DONE 2026-03-09 + +### Priority 3: MEDIUM (cleanup) +- [ ] Delete VLAN 10 from UniFi +- [x] ~~Disable/delete 12 stale AD accounts~~ — DONE 2026-04-13 (13 accounts deleted) +- [ ] Remove unused server roles (NPS, RDS) +- [ ] Create DHCP reservation for LG TV WiFi MAC (e0:85:4d:4d:f0:3e → 192.168.2.148) +- [x] ~~Fix Brother printer dual-connection (onsite)~~ — NOT an issue. 192.168.2.53 is WiFi-only with static IP. DONE 2026-03-11 + +### Priority 4: Phase 2+ (AD/server prep) +- [x] ~~**Run phase2-ou-cleanup.ps1** — audit + delete 13 root-level OUs, clean CN=Users accounts~~ — DONE 2026-04-13 (manual commands) +- [x] ~~**Run phase2-ad-setup.ps1** — security fixes, Workstations OU (incl. Shared PCs), security groups, computer moves~~ — Partially DONE 2026-04-13 (Workstations OU created, DA cleaned, UPNs updated. Security groups + computer moves still pending) +- [ ] Set up file share permissions on CS-SERVER +- [ ] Create GPOs (drive maps, printers, security baseline, updates, folder redirection, shared workstation) +- [ ] Domain-join non-domain machines +- [ ] Synology retirement + backup-only repurpose + +### Priority 5: M365 Cleanup +- [ ] **Convert 12 role-based accounts to shared mailboxes** — accounting@, frontdesk@, hr@, security@, memcarereceptionist@, boadmin@, accountingassistant@, Training@, Kitchenipad@, medtech@, nurse@, transportation@. Frees ~12 licenses (~$150/mo) +- [ ] **Delete nick pavloff M365 account** — account was only for Synology admin. Change Synology admin email to another account first, then delete to free license. +- [x] ~~**Update Tamra.Johnson → Tamra.Matthews in AD**~~ — DONE 2026-04-13 +- [ ] **Delete Kristiana Dowse M365 account** — HR confirmed not current employee (2026-03-10). Frees 1 license. +- [ ] **Delete "howaed" guest account** — typo duplicate of howard@azcomputerguru.com +- [ ] **Delete Anna Pitzlin & Nela Durut-Azizi shared mailboxes** — HR confirmed OK to delete (were forwarded to Meredith, no longer needed). Jeff Bristol still pending. +- [ ] **Review Sandra Fish global admin** — previous owner still holds the only global admin. Create break-glass admin? +- [ ] **Install Entra Connect** — planned for CS-SERVER, AD cleanup complete, UPNs updated. Blocked on: M365 shared mailbox conversions +- [ ] **Determine if AD users need M365** — HR confirmed all current employees (2026-03-10). Roles: Front Desk/Courtesy Patrol, MC Front Desk, Transportation, Housekeeping. Do they need email? Free licenses first via role account cleanup. + +### Priority 6: Audit Findings (2026-03-10) + +**Doc fixes:** +- [x] Fix Room 206 printers in `phase2-print-server.ps1` — Added Bizhub C368 + 206 Nurse Station Brother as separate entries — DONE +- [x] Fix `firewall.md` post-migration rules — changed "RFC1918" to `_private4_` — DONE +- [x] Fix `dhcp.md` Room 218 — marked as FIXED 2026-03-07 — DONE +- [x] Fix `dhcp.md` printer 192.168.2.53 — updated to online with MAC — DONE +- [x] Fix `step3-switch-ports.md` — Added Bizhub C368 + 206 Nurse Station — DONE +- [x] Fix RFC1918 alias entry in Session 2 billing record — corrected — DONE +- [x] Standardize "MemCare MedTech" printer naming across all docs — DONE + +**Resolved with Howard's input:** +- [x] ~~**Duplicate Alyssa accounts**~~ — Resolved: Alyssa.Shestko renamed to Alyssa.Brooks, lowercase duplicate deleted — DONE 2026-04-13 +- [x] **SALES4-PC** — Active, used by Tamra Matthews. Was just offline during audit. Updated overview.md. — DONE +- [x] **Azure docs** — No Azure services. M365 + GoDaddy web hosting only. Updated `cloud/azure.md`. — DONE + +**Needs onsite / separate session:** +- [ ] M365 email audit — SPF, DKIM, DMARC, MX records all TBD +- [ ] Synology shares "pacs" and "web" — purpose unknown (may contain PHI) +- [ ] CS-SERVER ports 5504, 6783, 8019 — unidentified listeners +- [ ] Room 339 interface — may be disabled in pfSense +- [ ] 9 offline APs — need physical investigation +- [x] ~~**Kitchen thermal printer inventory**~~ — 2 printers: Bistro TM-T88VII (192.168.2.207), Kitchen TM-U220IIB (10.0.20.225). DONE 2026-03-11 +- [ ] **Verify ALIS BAA** — ask management if signed BAA exists with go-alis.com +- [ ] **Sign Microsoft BAA** — M365 Admin → Settings → Org Settings → Security & Privacy → HIPAA BAA +- [ ] **Enable MFA** — Security Defaults in Entra ID (free, 5 min to enable) + +### Onsite Visit Additions (from M365 audit) +- [ ] Identify shared workstation computer names for GPO 6 targeting +- [ ] Confirm nick pavloff's department and PC assignment +- [x] ~~Ask about Kristiana Dowse — current or former?~~ HR confirmed DELETE (2026-03-10) +- [ ] Map user-to-shared-PC rotation matrix for shared mailbox permissions + +--- + +## Session 8 — 2026-03-20 (Remote) + +**Focus:** Audit script deployment, GitHub hosting, ScreenConnect Toolbox setup + +| Time | Task | Details | +|------|------|---------| +| | Audit script updates | Removed .txt transcript output (JSON only), added hostname to filenames (HOSTNAME_audit_DATE.json) | +| | Script self-relaunch fix | Changed `-Verb RunAs` to `-NoNewWindow -WindowStyle Hidden` for silent ScreenConnect execution | +| | GitHub repo created | Created public repo `Howweird/msp-audit-scripts` with server_audit.ps1, workstation_audit.ps1, README.md | +| | ScreenConnect Toolbox commands | Built commands for: server audit, workstation audit, clear C:\Temp. Documented ScreenConnect 80-char line limit. | +| | ScreenConnect line-wrapping fix | Discovered ScreenConnect silently truncates long lines (~120 chars). Rewrote all commands with URLs in variables, short lines. Added rules to CLAUDE.md. | + +--- + +## Session 9 — 2026-03-20/21/22 (Remote) + +**Focus:** Full fleet audit, security remediation, Windows upgrades + +| Time | Task | Details | +|------|------|---------| +| | **Full fleet audit** | Ran server + workstation audits on 19 machines (1 server, 18 workstations) via ScreenConnect Toolbox | +| | **Workstation inventory created** | Created `cascades/workstations.md` — full hardware, OS, users, software, security findings for all 18 workstations | +| | **Documentation updates** | Updated cs-server.md (security findings, disk usage, software, share permissions), active-directory.md (functional levels, new users, login activity), antivirus.md (deployment status for all 19 endpoints), hipaa.md (11 new gaps), overview.md (workstation table with audit data) | +| | **Master issue tracker** | Built combined issue tracker (42 items) merging audit findings with all prior issue log entries, organized by severity | +| | **Pro key applied to 4 machines** | ANN-PC, DESKTOP-DLTAGOI, MAINTENANCE-PC, MDIRECTOR-PC — Win 11 Home → Pro via changepk ScreenConnect command | +| | **RDP disabled on 2 machines** | ASSISTMAN-PC and DESKTOP-U2DHAP0 — were exposed without NLA | +| | **AD Recycle Bin enabled** | Was off — deleted objects were unrecoverable | +| | **MachineAccountQuota set to 0** | Was 10 — any domain user could join machines | +| | **RestrictAnonymous set to 1** | Was 0 — null sessions allowed on CS-SERVER | +| | **Stale printer ports cleaned** | Ran cleanup script on all 18 workstations — removed orphan TCP/IP ports | +| | **AutoPatch + Win 11 upgrade pushed** | Created PSWindowsUpdate scheduled tasks on 15 machines (overnight, auto-stop 5AM). Skipped CS-SERVER, RECEPTIONIST-PC, MEMRECEPT-PC | +| | **Win 11 upgrade assistant** | Pushed to eligible Win 10 machines: DESKTOP-LPOPV30, NURSESTATION-PC, LAPTOP-DRQ5L558, LAPTOP-E0STJJE8. Also 25H2 upgrade for CRYSTAL-PC, DESKTOP-U2DHAP0, LAPTOP2 | +| | **ScreenConnect Toolbox expanded** | Added commands for: auto-patch, auto-patch+upgrade, stop updates at 5AM, Pro key push, stale printer port cleanup | +| | **Network analysis** | Identified DNS misconfiguration (15 machines pointing to pfSense instead of CS-SERVER), cross-subnet routing issues, printer port IP mismatches | +| | **DirecTV VLAN issue documented** | Older DirecTV boxes can't connect to VLAN networks — must join CSC ENT first for update, then move to CSCNet | +| | **Pro key documented** | Volume license key added to root CLAUDE.md with usage log tracking requirement | + +--- + +## Session 10 — 2026-04-13 (Onsite + Remote) + +**Focus:** Workstation upgrades, domain joins, printer setup, AD cleanup, Entra Connect planning, MDM planning + +### Workstation Upgrades & Domain Joins +| Task | Details | +|------|---------| +| **DESKTOP-DLTAGOI — Pro upgrade + domain join** | Upgraded Win 11 Home → Pro (manual key — PowerShell method caused Enterprise). Joined to cascades.local. | +| **DESKTOP-DLTAGOI — User setup** | Created domain user Sharon.Edwards (Life Enrichment Assistant). Removed local accounts: casadmin201, rootadmin, local "Sharon Edwards". Disabled system accounts. | +| **DESKTOP-DLTAGOI — Printer cleanup** | Removed all Brother printers. Added Copy Room printer manually. | +| **DESKTOP-ROK7VNM — Pro upgrade + domain join** | New machine (not in previous audit). Upgraded to Pro (manual key). Joined to cascades.local. | +| **DESKTOP-ROK7VNM — User setup** | Created domain user Susan.Hicks (Life Enrichment Director). Removed local accounts: casadmin201, nick, SusanH, Megan Wicker. | +| **MAINTENANCE-PC — Pro upgrade** | Upgraded Win 11 Home → Pro (manual key). Domain join pending. | +| **MAINTENANCE-PC — Disk cleanup** | Cleared SoftwareDistribution, temp files, DISM component cleanup, deleted nick user profile. | +| **Pro key issue documented** | PowerShell `changepk` method from Session 9 caused Enterprise edition on some machines. Manual key entry through Settings is the correct method. | + +### Printer Work +| Task | Details | +|------|---------| +| **Room 132 Canon MF741CDW — Factory reset** | Printer was locked out (System Manager ID/PIN unknown). Factory reset successful. | +| **Room 132 Canon — Moved to INTERNAL VLAN** | Connected to CSCNet WiFi, set static IP 10.0.20.94. Previously was 192.168.3.211 on LAN. | +| **Print server planning** | Planned GPO-based printer deployment via CS-SERVER print server. Print Services role check needed. Naming convention: Floor-Room-Model (e.g. 1F-132-RecRoom-Canon). | + +### AD Cleanup (on CS-SERVER) +| Task | Details | +|------|---------| +| **Deleted 13 stale accounts** | Anna.Pitzlin, Nela.Durut-Azizi, Jodi.Ramstack, Monica.Ramirez (disabled/former). Haris.Durut, Nuria.Diaz, Cathy.Reece, Kelly.Wallace, Isabella.Islas, ann.dery (not on HR roster). alyssa.brooks (lowercase duplicate). Lupe.Sanchez (duplicate of Guadalupe). jeff.bristol (replaced by Lauren). | +| **Renamed 5 accounts** | Tamra.Johnson → Tamra.Matthews, Alyssa.Shestko → Alyssa.Brooks, Guadalupe.Sanchez → Lupe.Sanchez, strozzi → Shelby.Trozzi, Christopher.Holik → Christopher.Holick | +| **Removed non-IT from Domain Admins** | Removed Meredith.Kuhn and John.Trozzi. Only Administrator and sysadmin remain. | +| **Deleted root-level duplicate OUs** | 13 empty root-level OUs (confirmed already deleted from previous session). | +| **Created Workstations OU** | OU=Workstations with sub-OUs: Staff PCs, Shared PCs. | +| **Added UPN suffix** | Added cascadestucson.com as UPN suffix to AD forest. | +| **Updated all 33 user UPNs** | Changed from @cascades.local to @cascadestucson.com for Entra Connect SSO readiness. | +| **Created Kyla.QuickTiffany account** | New Resident Services Receptionist. Placed in OU=Resident Services. | +| **Full HR roster imported** | All 32 employees documented with positions, departments, and shared email group assignments. | + +### Print Server & GPO Setup +| Task | Details | +|------|---------| +| **Removed Roaming share** | Deleted D:\Roaming and SMB share — unused, replaced by Folder Redirection | +| **Created homes share** | D:\Homes shared as \\CS-SERVER\homes — Domain Admins full, Domain Users change. For Folder Redirection. | +| **RecRoom Canon added to print server** | Added printer port TCP_10.0.20.94, shared as "RecRoom-Canon" using Canon Generic Plus PCL6 driver | +| **CSC - Life Enrichment Printers GPO** | Created and linked to OU=Life Enrichment. RecRoom Canon deployed via Print Management (per user). | +| **CSC - Folder Redirection GPO** | Created and linked to OU=Departments. GPMC Folder Redirection extension broken on CS-SERVER — fdeploy.ini not being created. Worked around using GP Preferences > Registry to set shell folder paths (Desktop, Documents, Downloads → \\CS-SERVER\homes\%USERNAME%\). | +| **Folder Redirection verified** | Tested with Sharon.Edwards — Desktop redirects to \\CS-SERVER\homes\sharon.edwards\Desktop. Documents and Downloads also configured. | +| **Moved 6 PCs to Staff PCs OU** | ACCT2-PC, CRYSTAL-PC, DESKTOP-H6QHRR7, DESKTOP-1ISF081, DESKTOP-DLTAGOI, DESKTOP-ROK7VNM moved to OU=Staff PCs,OU=Workstations. CS-QB left in CN=Computers. | +| **Data migration slow** | Robocopy to server limited by Sharon's 72 Mbps WiFi (~8 MB/s). Server storage is two PERC RAID virtual disks (300GB C: + 1.1TB D:), likely spinning SAS. Consider SSD upgrade + hardwiring PCs for speed. | + +### Planning & Documentation +| Task | Details | +|------|---------| +| **Entra Connect SSO plan** | Documented full plan in cloud/m365.md — prerequisites, install steps, sync scope. Enables single sign-on: AD login → Office/Edge/Outlook auto-activate. | +| **M365 license optimization** | Planned conversion of 12 role-based accounts to shared mailboxes. 10 staff (drivers, receptionists, courtesy patrol) get AD + SSO but no paid license. Saves ~$137.50/month (11 licenses freed). | +| **ManageEngine MDM** | Account created. Will manage employee Android phones (HIPAA compliance) + 9 kitchen iPads (lockdown/kiosk mode). Created security/mdm.md. | +| **Len's Auto Brokerage (LAB)** | New client folder created. Documented lab-server (Server 2008 SP2, EOL) and DESKTOP-BMBTQLI (HPE MicroServer Gen10 Plus v2, current server). RDP troubleshooting on Server 2008 — CredSSP incompatibility. | + +### Billing Summary — Session 10 +| Category | Items | +|----------|-------| +| Workstation upgrades (Pro key + domain join) | 3 machines (DLTAGOI, ROK7VNM, MAINTENANCE-PC) | +| User setup + local account cleanup | 3 machines | +| Printer reset + VLAN move + print server | 1 printer factory reset, moved to INTERNAL VLAN, added to print server, deployed via GPO | +| AD cleanup | 13 accounts deleted, 5 renamed, 2 removed from Domain Admins, OU cleanup, UPN migration, 1 new account created | +| GPO setup | 2 GPOs created (Life Enrichment Printers, Folder Redirection). Folder Redirection working via GP Preferences workaround. | +| File server setup | Homes share created, Roaming share removed, 6 PCs moved to Staff PCs OU | +| Infrastructure planning | Entra Connect SSO, M365 license optimization, MDM setup | +| New client setup | Len's Auto Brokerage — folder + initial docs + RDP troubleshooting | + +### Session 10b — 2026-04-14 (Remote + Onsite) + +**Focus:** Continued Life Enrichment setup, GPO troubleshooting, OneDrive cleanup + +| Task | Details | +|------|---------| +| **Narrowed Folder Redirection GPO** | Moved link from OU=Departments to OU=Life Enrichment only. Roll out dept by dept. | +| **Susan.Hicks OneDrive cleanup** | ProfWiz migrated old SusanH profile with OneDrive folder redirection. Fixed shell folders (Desktop, Documents, Downloads, Videos, Pictures, Attachments) back to local %USERPROFILE% paths. Uninstalled OneDrive. | +| **Printer GPO troubleshooting** | Print Management "Deploy with Group Policy" not saving to SYSVOL (same broken GPMC issue as Folder Redirection). Fixed using GP Preferences > Shared Printer instead — \\CS-SERVER\RecRoom-Canon. Printers.xml confirmed in SYSVOL. | +| **Susan data migration** | Robocopy of Susan's data to \\CS-SERVER\homes in progress — slow due to WiFi. | + +### Session 10c — 2026-04-14 (Remote) + +**Focus:** M365 admin cleanup, MDM planning, ALIS SSO research, proposal + +| Task | Details | +|------|---------| +| **Sandra Fish admin removed** | Revoked global admin, blocked sign-in, removed P2 license. sysadmin@cascadestucson.com is now sole global admin. | +| **Entra P2 license freed** | 1 P2 license available for Conditional Access testing when ready. | +| **ALIS SSO confirmed** | ALIS supports Microsoft Entra SSO (Azure AD / Office 365). Requires App Registration in Azure Portal + ALIS App Store config. Users must have matching email in ALIS and Entra. | +| **M365 Business Premium proposal** | Created formal proposal at cascades/proposals/m365-premium-upgrade.md. Net savings of $56.50/mo after shared mailbox cleanup. Covers Intune, Conditional Access, Defender, DLP. | +| **MDM plan documented** | Full 7-phase ManageEngine MDM rollout plan in security/mdm.md. 25 shared Android phones + 9 kitchen iPads. | +| **Folder Redirection GPO narrowed** | Moved from OU=Departments to OU=Life Enrichment only. Roll out dept by dept. | +| **Susan Hicks OneDrive cleanup** | Fixed shell folders pointing to old OneDrive paths after ProfWiz migration. Uninstalled OneDrive. | + +### Session 10d — 2026-04-14 (Remote, extended diagnostic — inconclusive) + +**Focus:** Try to make Folder Redirection work natively and retire the GP Preferences Registry hack. + +| Task | Details | +|------|---------| +| **SYSVOL health verified** | `dcdiag /test:sysvolcheck` passed, SYSVOL permissions correct, writable as admin | +| **FR extension registration confirmed** | `gPCUserExtensionNames` on the old GPO correctly lists `{25537BA6-77A8-11D2-9B6C-0000F8080861}` (FR CSE) | +| **NTFS on D:\Homes hardened** | Removed `BUILTIN\Users ReadAndExecute` inheritance to subfolders/files — was allowing cross-user read of redirected PHI (HIPAA violation). Scoped to "This folder only". CREATOR OWNER Full Control still inherits so each user owns their own home folder. | +| **First diagnosis (WRONG)** | Initially thought GPMC on CS-SERVER was writing FR config to the wrong location (`User\Documents & Settings\fdeploy1.ini` with `FullPath=` + `Flags=1231`). Hypothesized a broken legacy ADMX template. | +| **RSAT installed + tested** | Installed RSAT GPMC on Sharon.Edwards' Win11 PC (`Add-WindowsCapability -Online -Name "Rsat.GroupPolicy.Management.Tools~~~~0.0.1.0"`). Recreated `CSC - Folder Redirection (LE)` GPO from RSAT. | +| **First diagnosis disproven** | RSAT wrote to the **same path** as CS-SERVER's GPMC (`User\Documents & Settings\fdeploy1.ini` with `FullPath=`). Two independent tools writing identical files = that IS the correct modern format. The "Documents & Settings" subfolder and `FullPath=` syntax are NOT legacy — they're normal modern FR layout. The original GPO was broken simply because the save was incomplete (empty `fdeploy.ini`, stub `fdeploy1.ini` with `Flags=4` and no FullPath). | +| **New GPO linked, old unlinked** | `CSC - Folder Redirection (LE)` linked to OU=Life Enrichment; `CSC - Folder Redirection` unlinked from OU=Life Enrichment (GPO itself kept as 1-week rollback). | +| **FR refuses to commit on Sharon** | At Sharon's logon, FR CSE fires, logs event **1006 "Documents has to be redirected"** with correct path+flags, logs event **1001 "extension finished"**. **No event 1013 (success), no error events.** `User Shell Folders\Personal` stays at `C:\Users\Sharon Edwards\Documents`. Multiple logon cycles don't help. `gpupdate /force` doesn't help. Permissions verified (Sharon has FullControl, write test succeeds). Target path reachable. FR history key (`HKCU\...\History\{25537BA6-...}`) still references OLD unlinked GPO; key is SYSTEM-protected, can't clear from user context. | +| **Investigation parked** | Howard wants to avoid the registry hack as the answer. Captured leading hypothesis + research search terms in plan file `C:\Users\howar\.claude\plans\immutable-imagining-spring.md`. | +| **Documented** | Revised `servers/cs-server.md` "Known Admin Issues" section to correct the earlier wrong theories and accurately describe the silent-no-commit symptom. | + +### Where We Left Off (2026-04-14 — Session end, investigation parked) + +**Leading hypothesis (needs confirmation via research):** +The FR policy has "Grant user exclusive rights" enabled (Flags=1231 bit 0x1). When the target folder `\\CS-SERVER\homes\sharon.edwards\Documents` already exists with a non-Sharon owner (sysadmin created it during the original registry-hack migration, and we re-created it manually during tonight's diagnostic), FR can't rewrite the folder's ACL to Sharon-only. Documented FR quirk: logs intent via 1006, silently aborts without logging to Operational channel. This matches our exact fingerprint (1006 fires, 1013 never fires, zero errors). + +**Fast sanity-check for next session (read-only):** +```powershell +(Get-Acl "D:\Homes\sharon.edwards\Documents").Owner +``` +If owner is anything other than `CASCADES\sharon.edwards`, hypothesis strongly supported. + +**Search terms Howard will research:** +1. Primary: `Folder Redirection "has to be redirected" event 1006 no 1013 silent no error` +2. Hypothesis-driven: `Folder Redirection "Grant the user exclusive rights" existing folder silently fails ownership` +3. Fallback: `Folder Redirection Windows 10 event 1001 finished but folder not redirected registry` + +**If hypothesis confirmed — next steps:** +1. `takeown /F "D:\Homes\sharon.edwards\Documents" /A` then `icacls ... /setowner "CASCADES\sharon.edwards" /T` +2. Clear FR history from elevated context via `HKU\` +3. Sharon log off + on, verify event 1013 fires and Documents redirects +4. If successful, script this across all LE users' homes folders + +**If hypothesis wrong — secondary paths to try:** +- Enable FR verbose debug logging (`HKLM\...\Diagnostics\FdeployDebugLevel=0x10`), read `%windir%\debug\usermode\fdeploy.log` for the real skip reason +- Test FR on a brand-new user with no profile history to rule out profile corruption +- If still blocked, fall back to GP Preferences Registry for Documents (as already deployed for Desktop) — documented workaround, not the end state + +**Current Sharon state (unchanged tonight):** +- Desktop: `\\CS-SERVER\homes\Sharon.Edwards\Desktop` (working, via original registry hack — no FR involvement) +- Documents: `C:\Users\Sharon Edwards\Documents` (local, FR failed to redirect) +- Downloads: `C:\Users\Sharon Edwards\Downloads` (local) + +**Phase D HIPAA hardening (still pending, after FR is working):** +- `Set-SmbShare -Name homes -EncryptData $true -Force` (SMB encryption in transit) +- Enable file access auditing on D:\Homes (§164.312(b) Audit Controls) +- VSS + daily shadow copies on D: (§164.308(a)(7) Contingency Plan) +- Backup D:\Homes to Synology via Windows Server Backup + +**Phase D HIPAA hardening** (after FR is working): +- `Set-SmbShare -Name homes -EncryptData $true -Force` (SMB encryption in transit) +- Enable file access auditing on D:\Homes (§164.312(b) Audit Controls) +- VSS + daily shadow copies on D: (§164.308(a)(7) Contingency Plan) +- Backup D:\Homes to Synology via Windows Server Backup +- Manually set NTFS permissions on D:\Homes (commands ready, not yet run): + - CREATOR OWNER: full access to own folder only + - Domain Users: can create subfolder, cannot access others + - Domain Admins: full access + - Lock down existing sharon.edwards and susan.hicks folders + +**D:\Homes NTFS permissions (not yet run):** +``` +icacls D:\Homes /inheritance:d +icacls D:\Homes /remove "BUILTIN\Users" +icacls D:\Homes /grant "CASCADES\Domain Admins:(OI)(CI)F" +icacls D:\Homes /grant "CREATOR OWNER:(OI)(CI)F" +icacls D:\Homes /grant "CASCADES\Domain Users:(CI)(AD)(RD)" +``` + +**Data migration script ready (not yet run):** +- Copy-only test version (robocopy /L for dry run, remove /L for real copy) +- Move version (robocopy /MOVE) for production +- Run on each user's machine while logged in as them + +**Other pending:** +- **Printer GPO:** RecRoom Canon added via GP Preferences. Needs gpupdate + re-login test on Sharon/Susan machines. +- **Copy Room printer:** Not yet added to print server or GPO. +- **MAINTENANCE-PC:** Pro upgraded, domain join + local account cleanup still pending. +- **ANN-PC, MDIRECTOR-PC:** Check for Enterprise edition from PowerShell Pro key push. +- **M365:** Sandra removed. Shared mailbox conversions pending. Entra Connect pending. Sign BAA. 23 licensed users confirmed. +- **MDM:** ManageEngine Phase 1 tenant setup in progress. 25 shared Android phones + 9 kitchen iPads. +- **ALIS SSO:** Confirmed Entra support. Needs App Registration in Azure Portal. +- **Business Premium proposal:** cascades/proposals/m365-premium-upgrade.md — net -$56.50/mo. +- **Len's:** RDP to Server 2008 still failing (CredSSP). +- **Server storage:** Likely spinning SAS in Dell R610 — evaluate SSD upgrade. diff --git a/clients/cascades-tucson/docs/cloud/azure.md b/clients/cascades-tucson/docs/cloud/azure.md new file mode 100644 index 0000000..7bd9ebe --- /dev/null +++ b/clients/cascades-tucson/docs/cloud/azure.md @@ -0,0 +1,16 @@ +# Azure / Cloud Services + +## Azure Subscription +- **No Azure services in use.** Cascades uses M365 only — no Azure VMs, networking, or cloud infrastructure. + +## Other Cloud Services + +| Service | Purpose | Notes | +|---------|---------|-------| +| Microsoft 365 Business Standard | Email, Office apps, OneDrive | See `cloud/m365.md` | +| GoDaddy | Web hosting | cascadestucson.com website hosting | + +## Notes +- Cascades is a senior living / assisted living facility (not classified as medical, but has health services staff) +- No Azure AD Connect / Entra Connect — AD and M365 are separate identity systems +- No cloud backup configured — see `security/backup.md` diff --git a/clients/cascades-tucson/docs/cloud/m365.md b/clients/cascades-tucson/docs/cloud/m365.md new file mode 100644 index 0000000..0364ee8 --- /dev/null +++ b/clients/cascades-tucson/docs/cloud/m365.md @@ -0,0 +1,285 @@ +# Microsoft 365 + +## Tenant Info +- Tenant Name: cascadestucson.com +- Tenant ID: 207fa277-e9d8-4eb7-ada1-1064d2221498 +- Primary Domain: cascadestucson.com +- onmicrosoft Domain: NETORGFT4257522.onmicrosoft.com +- Admin Portal URL: https://admin.microsoft.com +- Global Admin: sysadmin@cascadestucson.com (Howard Enos, MSP) +- Former Admin: admin@NETORGFT4257522.onmicrosoft.com (Sandra Fish — previous director, removed 2026-04-14: global admin revoked, sign-in blocked, P2 license removed) +- DirSync / Entra Connect: **Not configured** (all accounts cloud-only) — **PLANNED: Install Entra Connect for SSO** +- HIPAA BAA: **Not signed** — required since email may contain PHI +- MFA: **Not enabled** — Security Defaults not configured + +## Licensing + +| License Type | Total | Assigned | Available | +|---|---|---|---| +| Microsoft 365 Business Standard | 34 | 34 | 0 | +| Microsoft Entra ID P2 | 1 | 0 | 1 (unassigned — was Sandra Fish, available for testing) | +| Microsoft Power Automate Free | 10000 | 2 | 9998 | +| Microsoft Stream Trial | 1000000 | 0 | 1000000 | +| Exchange Online Essentials | — | 4 | — | + +**Note:** Business Standard is fully allocated (34/34, 0 available). Any new hires require purchasing additional licenses. + +## AD ↔ M365 Account Mapping + +### Matched Accounts (AD user → M365 mailbox) + +| AD SamAccountName | M365 UPN | License | Notes | +|---|---|---|---| +| howard | dax.howard@cascadestucson.com | Business Standard | Alias: cara.lespron@ (reused mailbox from former employee) | +| sysadmin | sysadmin@cascadestucson.com | Power Automate Free | Display: "Computer Guru Support" — no mailbox license | +| Meredith.Kuhn | meredith.kuhn@cascadestucson.com | Business Standard | | +| John.Trozzi | john.trozzi@cascadestucson.com | Business Standard | | +| Lupe.Sanchez | lupe.sanchez@cascadestucson.com | Business Standard | | +| Megan.Hiatt | megan.hiatt@cascadestucson.com | Business Standard | | +| Crystal.Rodriguez | crystal.rodriguez@cascadestucson.com | Business Standard | Alias: crystal.suszek@ | +| Tamra.Johnson | tamra.matthews@cascadestucson.com | Business Standard | **Rename AD to Tamra.Matthews** — M365 already correct. Alias: tamra.johnson@ still works | +| Lois.Lane | lois.lane@cascadestucson.com | Business Standard | | +| Christina.DuPras | christina.dupras@cascadestucson.com | Business Standard | | +| Christine.Nyanzunda | christine.nyanzunda@cascadestucson.com | Business Standard | M365 last name: "Nyanzuda" (typo — AD has Nyanzunda) | +| Susan.Hicks | susan.hicks@cascadestucson.com | Business Standard | | +| Ashley.Jensen | ashley.jensen@cascadestucson.com | Business Standard + Power Automate Free | Alias: ashley.jenson@ | +| Veronica.Feller | veronica.feller@cascadestucson.com | Business Standard | | +| JD.Martin | jd.martin@cascadestucson.com | Business Standard | | +| alyssa.brooks | alyssa.brooks@cascadestucson.com | Business Standard | | +| Matt.Brooks | matthew.brooks@cascadestucson.com | Business Standard | AD: Matt, M365: Matthew | +| Ramon.Castaneda | ramon.castaneda@cascadestucson.com | Business Standard | Aliases: ramon.castanada@, ramon.casteneda@ (typos kept as aliases) | +| Sharon.Edwards | sharon.edwards@cascadestucson.com | Business Standard | | +| britney.thompson | Britney.Thompson@cascadestucson.com | Business Standard + Exchange Online Essentials | | +| ann.dery | ann.dery@cascadestucson.com | Business Standard | | +| strozzi (Shelby Trozzi) | Shelby.Trozzi@cascadestucson.com | Business Standard + Exchange Online Essentials | AD username doesn't match M365 format | +| karen.rossini | karen.rossini@cascadestucson.com | Business Standard | | +| lauren.hasselman | lauren.hasselman@cascadestucson.com | Business Standard | Created 2026-02-26 (recent hire, replaced Jeff Bristol) | +| Allison.Reibschied | Allison.Reibschied@cascadestucson.com | Business Standard | Accounting Assistant (new hire 2026-03) | + +### AD Accounts with NO M365 Match + +| AD SamAccountName | Type | Action Needed | +|---|---|---| +| Administrator | Built-in | None needed | +| localadmin | Admin | None needed | +| Sebastian.Leon | User | Front Desk/Courtesy Patrol — needs M365 account if they use email | +| Michelle.Shestko | User | MC Front Desk — keep as Shestko. Needs M365 account if they use email | +| Alyssa.Shestko (now Alyssa Brooks) | User | **Rename to Alyssa.Brooks in AD.** This is the real account. M365 already alyssa.brooks@. Duplicate lowercase `alyssa.brooks` in CN=Users to be deleted. | +| Guadalupe.Sanchez | User | Housekeeping — already has M365 as lupe.sanchez@cascadestucson.com | +| Sheldon.Gardfrey | User | Front Desk/Courtesy Patrol — needs M365 if they use email | +| Cathy.Kingston | User | Front Desk/Courtesy Patrol — needs M365 if they use email | +| Shontiel.Nunn | User | Transferring soon — keep for now | +| Ray.Rai | User | Front Desk/Courtesy Patrol — needs M365 if they use email | +| Richard.Adams | User | Transportation — needs M365 if they use email | +| Julian.Crim | User | Transportation — needs M365 if they use email | +| Christopher.Holik | User | Transportation — needs M365 if they use email | +| QBDataServiceUser34 | Service | None needed | +| Culinary | Shared/Generic | None needed (AD shared account) | +| Receptionist | Shared/Generic | Maps to frontdesk@cascadestucson.com? | +| saleshare | Shared/Generic | None needed | +| directoryshare | Shared/Generic | None needed | + +### M365 Accounts with NO AD Match + +#### Real users (need AD accounts created or are new hires) + +| M365 Display Name | UPN | License | Notes | +|---|---|---|---| +| Kristiana Dowse | kristiana.dowse@cascadestucson.com | Business Standard | **DELETE** — HR confirmed not current employee. Remove license + delete account | +| nick pavloff | nick.pavloff@cascadestucson.com | Business Standard | Created 2026-03-07 — **new hire**, needs AD account | + +#### Role-Based Accounts — Convert to Shared Mailboxes (saves ~$125/mo) + +All of these are currently licensed user accounts. Convert to **shared mailboxes** (free) and remove licenses. Then assign members from AD-synced accounts. + +| M365 Display Name | UPN | Current License | Action | Members (after conversion) | +|---|---|---|---|---| +| Accounting Dept. | accounting@cascadestucson.com | Business Standard | Convert to shared | Ashley.Jensen, lauren.hasselman | +| Accounting Assistant | accountingassistant@cascadestucson.com | Business Standard | Convert to shared | Allison.Reibschied | +| Bookkeeping Office | boadmin@cascadestucson.com | Business Standard | Convert to shared | TBD | +| Front Desk | frontdesk@cascadestucson.com | Business Standard | Convert to shared | Cathy.Kingston, Shontiel.Nunn, Kyla.QuickTiffany, Sebastian.Leon, Sheldon.Gardfrey, Ray.Rai | +| Human Resources | hr@cascadestucson.com | Business Standard | Convert to shared | Meredith.Kuhn | +| MemCare Receptionist | memcarereceptionist@cascadestucson.com | Business Standard | Convert to shared | Michelle.Shestko, Matt.Brooks | +| Security Cascades | security@cascadestucson.com | Business Standard | Convert to shared | TBD | +| Training | Training@cascadestucson.com | Business Standard | Convert to shared | TBD | +| Nurse | nurse@cascadestucson.com | Exchange Online Essentials | Convert to shared | Lois.Lane, Karen.Rossini, britney.thompson | +| medtech | medtech@cascadestucson.com | Exchange Online Essentials | Convert to shared | TBD | +| transportation | transportation@cascadestucson.com | Exchange Online Essentials | Convert to shared | Richard.Adams, Julian.Crim, Christopher.Holick | +| AppleID | Kitchenipad@cascadestucson.com | Unlicensed | Keep as-is | Device account. Alias: ipad@ | + +#### Courtesy Patrol Shared Mailbox (NEW) +- Create: **courtesypatrol@cascadestucson.com** as shared mailbox +- Members: Sebastian.Leon, Sheldon.Gardfrey + +### License Plan After Cleanup + +#### Full Business Standard License (own mailbox + Office apps) +Staff with `first.last@cascadestucson.com` personal mailboxes: +| Employee | UPN | +|----------|-----| +| Howard Dax | dax.howard@ | +| Meredith Kuhn | meredith.kuhn@ | +| John Trozzi | john.trozzi@ | +| Megan Hiatt | megan.hiatt@ | +| Crystal Rodriguez | crystal.rodriguez@ | +| Tamra Matthews | tamra.matthews@ | +| Lois Lane | lois.lane@ | +| Christina DuPras | christina.dupras@ | +| Christine Nyanzunda | christine.nyanzunda@ | +| Susan Hicks | susan.hicks@ | +| Ashley Jensen | ashley.jensen@ | +| Veronica Feller | veronica.feller@ | +| JD Martin | jd.martin@ | +| Alyssa Brooks | alyssa.brooks@ | +| Matt Brooks | matthew.brooks@ | +| Ramon Castaneda | ramon.castaneda@ | +| Sharon Edwards | sharon.edwards@ | +| Britney Thompson | britney.thompson@ | +| Shelby Trozzi | shelby.trozzi@ | +| Karen Rossini | karen.rossini@ | +| Guadalupe Sanchez | lupe.sanchez@ | +| Lauren Hasselman | lauren.hasselman@ | +| Allison Reibschied | allison.reibschied@ | +**Total: 23 licenses** + +#### No License — Shared Mailbox Access Only (browser via SSO) +AD account + Entra sync, no M365 license. Access shared mailboxes via outlook.office.com. +| Employee | Position | Shared Mailbox Access | +|----------|----------|----------------------| +| Sebastian Leon | Courtesy Patrol | Frontdesk@, Courtesypatrol@ | +| Sheldon Gardfrey | Courtesy Patrol | Frontdesk@, Courtesypatrol@ | +| Cathy Kingston | Receptionist | Frontdesk@ | +| Shontiel Nunn | Receptionist | Frontdesk@ | +| Kyla Quick Tiffany | Receptionist | Frontdesk@ | +| Ray Rai | Courtesy Patrol | Frontdesk@ | +| Richard Adams | Driver | Transportation@ | +| Julian Crim | Driver | Transportation@ | +| Christopher Holick | Driver | Transportation@ | +| Michelle Shestko | MC Receptionist | Memcarereceptionist@ | +**Total: 10 users, 0 licenses** + +#### License Savings +- Current: 34 Business Standard (all allocated) +- After cleanup: 23 Business Standard needed +- **11 licenses freed** (~$137.50/month saved) + +#### External guest accounts + +| Display Name | Source | Notes | +|---|---|---| +| a.r.jensen018 | a.r.jensen018@gmail.com | Ashley Jensen's personal? | +| Debora Morris | deboram@teepasnow.com | External partner | +| duprasc2002 | duprasc2002@yahoo.com | Christina DuPras personal? Created 2026-03-04 | +| howaed | howaed@azcomputerguru.com | **Typo** of howard — delete | +| howard | howard@azcomputerguru.com | Howard (MSP) external account | +| karenrossini7 | karenrossini7@gmail.com | Karen Rossini's personal? | + +#### Blocked / former employee accounts in M365 + +| Display Name | UPN | Sign-in Blocked | Notes | +|---|---|---|---| +| Jeff Bristol | jeff.bristol@cascadestucson.com | Yes | Former employee — unlicensed, shared mailbox exists | +| Nela Durut-Azizi | nela.durut-azizi@cascadestucson.com | Yes | Former employee — unlicensed, shared mailbox exists | +| Stephanie Devin | Stephanie.Devin@cascadestucson.com | Yes | Former? Unlicensed, blocked | + +#### Tenant admin + +| Display Name | UPN | License | Notes | +|---|---|---|---| +| cascadestucson.com (Sandra Fish) | admin@NETORGFT4257522.onmicrosoft.com | **Unlicensed** (P2 removed) | **BLOCKED** — Former director. Global admin revoked, sign-in blocked 2026-04-14. Delete when ready. | + +## Shared Mailboxes + +| Name | Email | Notes | +|---|---|---| +| Anna Pitzlin | anna.pitzlin@cascadestucson.com | **Former employee** — was forwarded to Meredith, HR says DELETE | +| Fax Cascades | fax@cascadestucson.com | Fax-to-email service | +| Jeff Bristol | jeff.bristol@cascadestucson.com | **Former employee** — sign-in blocked, keep for mail forwarding? | +| Nela Durut-Azizi | nela.durut-azizi@cascadestucson.com | **Former employee** — was forwarded to Meredith, HR says DELETE | + +## Exchange Online +- Mail Domain(s): cascadestucson.com +- MX Record Points To: TBD (check DNS) +- SPF Record: TBD +- DKIM Enabled: TBD +- DMARC Policy: TBD +- Distribution Groups: TBD (6 groups shown in tenant summary) +- Mail Flow Rules: TBD + +## Entra ID (Azure AD) +- Hybrid Joined: **No** — DirSync not enabled on any account — **PLANNED: Entra Connect install on CS-SERVER** +- Azure AD Connect Server: None (planned: CS-SERVER) +- MFA Enforced: TBD +- Conditional Access Policies: TBD +- Total Users: 51 (24 licensed individual, 12 generic/role, 6 external guests, 4 blocked/former, 1 admin, 4 shared mailboxes) +- Total Devices: 88 + +## Entra Connect — SSO Setup Plan + +### What It Does +Syncs AD accounts to M365/Entra ID. Users log into Windows with their AD account and Office/Edge/Outlook auto-sign-in with their M365 identity. Single sign-on, one password. + +### Prerequisites (MUST complete before install) +1. **AD account cleanup** — all the renames, deletions, and duplicate fixes MUST be done first. Entra Connect syncs what's in AD, so AD must be clean. + - [ ] Rename Tamra.Johnson → Tamra.Matthews + - [ ] Rename Alyssa.Shestko → Alyssa.Brooks + delete lowercase duplicate `alyssa.brooks` + - [ ] Rename strozzi → Shelby.Trozzi (match M365 UPN) + - [ ] Fix Christopher.Holik → Christopher.Holick (HR spelling) + - [ ] Create account for Kyla Quick Tiffany (Resident Services Receptionist) + - [ ] Delete confirmed former employees (Anna.Pitzlin, Nela.Durut-Azizi, Jodi.Ramstack, Monica.Ramirez) + - [ ] Disable/delete non-current accounts (Haris.Durut, Nuria.Diaz, Cathy.Reece, Kelly.Wallace, Isabella.Islas, ann.dery, alyssa.brooks lowercase) + - [ ] Fix Matt.Brooks vs matthew.brooks@ UPN mismatch +2. **UPN suffix** — Add `cascadestucson.com` as UPN suffix in AD so AD usernames match M365 emails +3. **M365 role-based accounts** — Convert to shared mailboxes BEFORE sync to avoid sync conflicts +4. **Kristiana Dowse** — Delete from M365 before sync +5. **Verify CS-SERVER meets requirements** — Server 2016+, .NET 4.7.2+, SQL Express (installs with Entra Connect) + +### Install Steps +1. Add UPN suffix `cascadestucson.com` to AD (AD Domains and Trusts) +2. Update all synced users' UPN to `firstname.lastname@cascadestucson.com` +3. Download Entra Connect from Entra admin center +4. Install on CS-SERVER +5. Choose **Password Hash Sync** (simplest, most reliable) +6. Scope sync to `OU=Departments` only (exclude service accounts, shared accounts, computers) +7. Enable **Seamless SSO** +8. Test with one user before full sync + +### What Gets Synced +- All user accounts in OU=Departments → Entra ID +- Passwords hash-synced (user keeps same password for AD + M365) +- NOT synced: computer accounts, service accounts, shared/generic accounts (Culinary, Receptionist, saleshare, directoryshare) +- **All synced users get Entra ID accounts** but NOT all get licenses +- Licensed users (23): personal mailbox + Office apps +- Unlicensed users (10): SSO sign-in to shared mailboxes via browser only — no Office install, no personal mailbox + +### What Changes for Users +- Log into Windows → Office, Outlook, Edge, OneDrive auto-sign-in +- One password for everything (change in AD, M365 follows) +- MFA can be enforced via Entra Conditional Access after sync + +### Risks +- If AD is dirty (duplicates, mismatches), sync will create duplicate M365 accounts or fail +- Shared/generic accounts (Culinary, Receptionist) should NOT sync — exclude from scope +- Must coordinate: once sync is on, AD becomes the source of truth for identity + +## Issues Found + +1. **0 licenses available** — Business Standard is 34/34. Cannot add new users without purchasing more. +2. **Tamra Johnson → Matthews name mismatch** — M365 updated to married name, AD still says Johnson. Update AD to match. +3. **13 AD users have no M365 account** — May not need email (hourly staff?) but verify onsite. +4. **12 generic/role-based M365 accounts eating licenses** — accounting@, frontdesk@, hr@, etc. each consume a Business Standard license ($12.50/mo). Should convert to shared mailboxes (free) if nobody logs into them directly. +5. **"howaed" external guest** — Typo duplicate of howard. Delete. +6. **3 former employee shared mailboxes** — Anna Pitzlin, Jeff Bristol, Nela Durut-Azizi. Decide: keep for mail history, forward, or delete. +7. **Sandra Fish is global admin** — Previous owner/manager. Verify she should still have admin access. +8. **cara.lespron@ alias on Howard's mailbox** — Former employee's mailbox was repurposed. Remove alias if no longer needed. +9. **Kristiana Dowse** — Licensed in M365 but not in AD. Verify: current employee or former? +10. **nick pavloff** — Created 2026-03-07 (yesterday). New hire — needs AD account. +11. **sysadmin has no mailbox license** — Only Power Automate Free. May need Exchange if used for email. +12. **No Microsoft BAA signed** — M365 email may contain PHI (resident data). HIPAA §164.308(b)(1) requires a Business Associate Agreement with Microsoft. Sign via M365 Admin Center → Settings → Org Settings → Security & Privacy → HIPAA BAA. +13. **No MFA enabled** — No Security Defaults or Conditional Access configured. HIPAA §164.312(d) requires person authentication. Enable Security Defaults at minimum (free). + +## Notes + +- Previous MSP/admin created many role-based accounts as regular licensed users instead of shared mailboxes. This wastes licenses. +- No Entra Connect / hybrid join — AD and M365 are completely separate identity systems. Users have different passwords for each. +- Shared workstation plan (GPO 6) needs: reception shared mailbox created, tenant domain is cascadestucson.com. diff --git a/clients/cascades-tucson/docs/issues/audit-findings-2026-03-20.md b/clients/cascades-tucson/docs/issues/audit-findings-2026-03-20.md new file mode 100644 index 0000000..32b2525 --- /dev/null +++ b/clients/cascades-tucson/docs/issues/audit-findings-2026-03-20.md @@ -0,0 +1,276 @@ +# Cascades Master Issue Tracker + +Combined from fleet audit (2026-03-20) and all prior issue log entries. + +## Work Log + +### 2026-03-26 +- [x] MAINTENANCE-PC: Uninstalled OneDrive (corrupt Telemetry.dll causing entry point error on boot, user doesn't use it) + +### 2026-03-25 +- [x] MAINTENANCE-PC: Disabled Wi-Fi power saving + Fast Startup (was dropping Wi-Fi after idle) + +### 2026-03-21 +- [x] Enabled AD Recycle Bin +- [x] Set MachineAccountQuota = 0 +- [x] Set RestrictAnonymous = 1 on CS-SERVER +- [x] Ran stale printer port cleanup on all machines + +### 2026-03-20 (evening) +- [x] Ran audits on all 19 machines, built full documentation +- [x] Pro key applied: ANN-PC, DESKTOP-DLTAGOI, MAINTENANCE-PC, MDIRECTOR-PC +- [x] RDP disabled: ASSISTMAN-PC, DESKTOP-U2DHAP0 +- [x] AutoPatch + Win 11 upgrade tasks pushed to 15 machines (overnight, stops 5AM) + - **Skipped:** CS-SERVER (server), RECEPTIONIST-PC (front desk), MEMRECEPT-PC (front desk + ancient hw) + - After Win 11 upgrade completes on LAPTOP-DRQ5L558 and LAPTOP-E0STJJE8, push Pro key + +--- + +## CRITICAL + +- [ ] **1. No backup — anywhere** + - CS-SERVER has no backup. It is the ONLY DC. If it dies, everything is gone. + - Synology ABB blocked (ext4, needs Btrfs) — use Windows Server Backup to Synology SMB share instead + - No M365 backup either + - HIPAA §164.308(a)(7) + +- [ ] **2. CS-SERVER hardware — 2009 Dell R610, extreme failure risk** + - 16+ years old. Single server runs: DC, DNS, DHCP, File Server, Hyper-V (VoIP), RDS, IIS, NPS + - No second DC — if hardware fails, AD, DNS, shares, phones ALL go down + - Plan: migrate to new hardware, add second DC + +- [ ] **3. Windows Updates — 6 machines critically behind** + - [~] DESKTOP-LPOPV30 — 13 months behind — AutoPatch running overnight + - [~] LAPTOP2 — 8 months — AutoPatch running overnight + - [~] CRYSTAL-PC — 5 months — AutoPatch running overnight + - [ ] MEMRECEPT-PC — 4 months — skipped (front desk, ancient hw) + - [~] ASSISTMAN-PC — 3 months — AutoPatch running overnight + - [~] DESKTOP-KQSL232 — 3 months — AutoPatch running overnight + - RECEPTIONIST-PC also skipped — needs update run scheduled + +- [x] **4. Windows Home → Pro upgrades** *(2026-03-20)* + - [x] ANN-PC, DESKTOP-DLTAGOI, MAINTENANCE-PC, MDIRECTOR-PC — done via changepk + - [ ] LAPTOP-DRQ5L558, LAPTOP-E0STJJE8 — pending Win 11 upgrade first + - [ ] MEMRECEPT-PC — replace machine instead + +- [ ] **5. Shared accounts with NO PASSWORD accessing PHI** + - [ ] NURSESTATION-PC: "Nurses" — accesses ALIS (medical records) + - [ ] MEMRECEPT-PC: "memfrtdesk" — MemCare front desk + - [ ] RECEPTIONIST-PC: "Front Desk" — mapped drives to CS-SERVER + - [ ] DESKTOP-KQSL232: Lois Lane — PasswordRequired=False + - Also: AD shared accounts (Culinary, Receptionist, saleshare, directoryshare) need replacement — Phase 5 + - HIPAA §164.312(a)(2)(i) + +- [ ] **6. No audit logging** + - CS-SERVER: Object Access auditing completely disabled — cannot track PHI access + - Synology NAS: stores PHI with no access auditing (ext4 can't support it) — migrate to CS-SERVER NTFS + - HIPAA §164.312(b) + +- [ ] **7. Expired SSL certificate on CS-SERVER (2025-04-02)** + - Self-signed cert expired ~1 year ago, causing Schannel TLS errors + +- [ ] **8. Floating firewall rule #4 passes ALL IPv4 traffic** + - Breaks room-to-room VLAN isolation — residents can reach staff VLAN, servers, other rooms + - Planned: Phase 1.3 — replace with scoped rules + - HIPAA §164.312(e)(1) + +## HIGH + +- [ ] **9. Remove non-IT staff from Domain Admins** + - [ ] Meredith.Kuhn — never logged in, never set password + - [ ] John.Trozzi — never logged in, never set password + +- [ ] **10. AD password issues** + - [ ] 23 accounts never set a password (null PasswordLastSet) + - [ ] 10 accounts with PasswordNeverExpires (Lois.Lane, strozzi, Culinary, Receptionist, howard, directoryshare, etc.) + - [ ] Password min length only 7 — increase to 12 in Default Domain Policy + - [ ] krbtgt password 569 days old (last set 2024-08-28) — rotate + +- [ ] **11. No screen lock on ANY machine** + - Zero machines have inactivity timeout or screensaver lock + - Staff walk away from ALIS sessions, shared drives, email — all open + - Push via GPO (domain) or registry (non-domain) + +- [ ] **12. BitLocker broken/missing fleet-wide** + - [ ] Fix protection OFF (encrypted but no protectors): ACCT2-PC, LAPTOP2, RECEPTIONIST-PC + - [ ] Enable on remaining 13 machines (requires Pro — do after upgrades) + - Only 2 of 18 working correctly (DESKTOP-LPOPV30, DESKTOP-U2DHAP0) + +- [ ] **13. No LAPS — same local admin password on every machine** + - Compromise one = compromise all. Deploy Windows LAPS. + +- [ ] **14. Share permissions wide open on CS-SERVER** + - [ ] Culinary: Everyone=FullControl → SG-Culinary-RW + - [ ] directoryshare: Everyone=FullControl → SG-Directory-RW + - [ ] Roaming: Domain Users=FullControl → restrict + - [ ] Shares (parent): Everyone=FullControl NTFS → restrict + - Security groups already designed — apply during Phase 2 + +- [ ] **15. M365 — no MFA, no BAA signed** + - [ ] Enable Security Defaults (MFA) in Entra ID — free, 5 minutes + - [ ] Sign Microsoft HIPAA BAA in M365 Admin Center + - [ ] Verify BAA with ALIS (go-alis.com) — ask management + - HIPAA §164.312(d) and §164.308(b)(1) + +- [ ] **16. M365 licenses full (34/34) — 12 role accounts wasting licenses** + - Convert role-based accounts to shared mailboxes (~$150/mo savings) + - Frees licenses for actual employees + - Delete: Kristiana Dowse (HR confirmed), "howaed" typo guest account + - Delete shared mailboxes: Anna Pitzlin, Nela Durut-Azizi (HR confirmed OK) + - Sandra Fish still global admin — create break-glass admin, remove her access + +- [ ] **17. QuickBooks installed on Domain Controller** + - QB Pro 2024 running on CS-SERVER with DB listener on port 6600 + - Should be on dedicated workstation or VM — increases DC attack surface + +- [ ] **18. Account lockout was disabled (fixed threshold, but GPO not fully deployed)** + - Default Domain Policy: 5 attempts / 30 min (fixed 2026-03-09) + - But most PCs aren't domain-joined so policy doesn't apply to them yet + +- [x] **19. RDP without NLA** *(2026-03-20)* + - [x] ASSISTMAN-PC — disabled + - [x] DESKTOP-U2DHAP0 — disabled + +- [ ] **20. TightVNC on MEMRECEPT-PC + old MSP remote access tools everywhere** + - [ ] TightVNC on MEMRECEPT-PC — unauthorized remote access, no-password machine + - [ ] Splashtop Streamer — ALL 19 machines + - [ ] Datto RMM — CS-SERVER at minimum + - [ ] N-able Take Control — some machines + - [ ] RemotePC — ASSISTMAN-PC, CHEF-PC, DESKTOP-U2DHAP0 + - [ ] TeamViewer — ANN-PC + - [ ] GoTo Opener — ANN-PC, MDIRECTOR-PC, DESKTOP-H6QHRR7 + +- [ ] **21. AV conflicts on multiple machines** + - [ ] RECEPTIONIST-PC: Bitdefender + Datto AV both running + - [ ] LAPTOP-E0STJJE8: McAfee LiveSafe + Datto AV + - [ ] MDIRECTOR-PC: COMODO AV disabled (stale, remove) + - [ ] CHEF-PC: Norton Security Scan (bloatware) + +- [ ] **22. RDS licensing expired ~17 months ago** + - RDS roles installed (Connection Broker, Session Host, Web Access) but no CALs + - Decide: purchase CALs or remove RDS roles + +## MEDIUM + +- [ ] **23. Most PCs not domain-joined (15 of 18)** + - Only 3 joined: ACCT2-PC, CRYSTAL-PC, DESKTOP-H6QHRR7 + - No GPOs apply to the other 15 (password policy, screen lock, BitLocker, drive maps) + - Domain join planned Phase 3, needs Pro on all machines first + +- [ ] **24. Network — machines on wrong subnets** + - LAPTOP-DRQ5L558 on Guest WiFi (10.0.50.x) — no internal access at all + - Many machines on old LAN (192.168.2-3.x) instead of INTERNAL (10.0.20.x) + - Most non-domain machines DNS points to pfSense (192.168.0.1) not CS-SERVER — adds latency for AD lookups + +- [ ] **25. AD OU cleanup — 13 junk root-level OUs** + - 10 duplicate department OUs + Managment (misspelled) + MemCare + Sales + - 20 accounts in CN=Users need placement + - Scripts ready: phase2-ou-cleanup.ps1, phase2-ad-setup.ps1 + +- [ ] **26. Delete confirmed former employee AD accounts** + - Disabled: Anna.Pitzlin, Nela.Durut-Azizi, Jodi.Ramstack, Monica.Ramirez, Jeff.Bristol + - Enabled but gone: Haris.Durut, Nuria.Diaz, Cathy.Reece, Kelly.Wallace, Isabella.Islas, ann.dery, alyssa.brooks (duplicate) + - Lupe.Sanchez — possible duplicate of Guadalupe.Sanchez (verify onsite) + +- [ ] **27. AD ↔ M365 identity issues** + - Tamra.Johnson AD account needs rename to Tamra.Matthews (M365 already correct) + - nick pavloff has M365 but no AD account + - AD and M365 fully separate (no Entra Connect) — evaluate after Phase 3 + - 13 AD users have no M365 account (hourly staff — determine if they need email) + +- [ ] **28. Hardware problems** + - [ ] MEMRECEPT-PC: Pentium E5500, 6GB RAM, 100Mbps NIC — replace entirely + - [ ] MDIRECTOR-PC: only 3.9 GB RAM — upgrade or replace + - [ ] MAINTENANCE-PC: disk 85% full (34.6 GB free) — clean up + - [ ] ASSISTMAN-PC: 7 local admin accounts — clean up to 3 + +- [ ] **29. Kitchen iPads not isolated** + - 9 iPads on INTERNAL VLAN with full access to staff resources + - Food service only, NOT medical — restrict to kitchen thermal printers only + - Needs firewall rules restricting iPad MACs to printer IPs + internet + +- [ ] **30. 9 offline UniFi APs — coverage gaps** + - APs on floors 1-4 offline, some on wrong IP ranges (192.168.6-7.x) + - Need physical visit to check power, cables, re-adopt + +- [ ] **31. Printer issues** + - [ ] 206 Health Services Brother printer drops WiFi (192.168.1.138) — wire it or fix signal + - [ ] Brother printer 192.168.2.53 dual-connected (WiFi + ethernet, ARP flapping) — disable one + - [ ] Bizhub C368 location and status unknown — find it onsite + - [ ] Room 405 long print lag — investigate onsite + +- [ ] **32. LDAP Channel Binding not configured on CS-SERVER** + +- [ ] **33. Stale DNS records** + - 192.168.2.59 and 192.168.0.5 in GC (old DCs?) + - DESKTOP-1ISF081 has AAAA but no A record + +- [ ] **34. UniFi VLAN 10 "CSC Internal Network" mismatch** + - UniFi has VLAN 10 but pfSense uses VLAN 20 for INTERNAL — VLAN 10 may be orphaned + +- [ ] **35. Room 339 pfSense interface possibly disabled** + - Missing `` tag — verify if room is occupied + +- [ ] **36. SSH on pfSense — verify hardening** + - Confirm key-based auth only, restricted to management VLAN + +- [ ] **37. Synology shares still mapped directly from MDIRECTOR-PC** + - H: \\cascadesds\homes, M: \\cascadesds\Management, P: \\cascadesds\Public + - Should point to CS-SERVER after migration + +- [ ] **38. Lauren Hasselman needs Sales share access** + - Replaced Jeff Bristol as Business Office Director, permissions not granted + +## LOW + +- [ ] **39. Remove stale user profiles from workstations** + - DESKTOP-LPOPV30: Haris Durut, Jodi Ramstack, Nela + - NURSESTATION-PC: Adella Clark (2021), April Hughes (2020) + - MAINTENANCE-PC: nick (2024-08), John Trozzi (disabled) + - ASSISTMAN-PC: Cecil Rinker, "DO NOT USE" + +- [ ] **40. CS-SERVER cleanup** + - [ ] Remove AutomationManagerAgent orphan service (file not found) + - [ ] Delete "Synology Sync machine" VM (off, not needed) + - [ ] Remove unused DHCP role + - [ ] Decide on Power Options GPO (unlinked) — keep or delete + - [ ] Remove 3 empty GPOs (CopyRoomPrinter, Nurses-Kiosk, MemCareMedTechPrinter) — if not already deleted + +- [ ] **41. GPOs not deployed yet (Phase 2.5-2.6)** + - CSC - Drive Mappings, CSC - Printer Deployment, CSC - Security Baseline, CSC - Windows Update, CSC - Folder Redirection, CSC - Shared Workstation + - Blocked on: domain join (Phase 3) + +- [ ] **42. RMM documentation missing** + - rmm/rmm.md is blank — document agent counts, monitoring, patch policies + +## COMPLETED + +- [x] RDP disabled on ASSISTMAN-PC + DESKTOP-U2DHAP0 *(2026-03-20)* +- [x] Pro key applied: ANN-PC, DESKTOP-DLTAGOI, MAINTENANCE-PC, MDIRECTOR-PC *(2026-03-20)* +- [x] Orphan printer ports cleaned on all machines *(2026-03-21)* +- [x] AD Recycle Bin enabled *(2026-03-21)* +- [x] MachineAccountQuota set to 0 *(2026-03-21)* +- [x] RestrictAnonymous set to 1 *(2026-03-21)* +- [x] Guest WiFi isolated to VLAN 50 *(2026-03-06)* +- [x] DNS scavenging enabled, stale records cleaned *(2026-03-06)* +- [x] Reverse DNS zones created *(2026-03-06)* +- [x] Room 218 DHCP scope fixed *(2026-03-07)* +- [x] Room 130 disabled firewall rule deleted *(2026-03-07)* +- [x] CS-SERVER timezone fixed to Arizona *(2026-03-07)* +- [x] LG TV ARP flapping fixed (ethernet disabled) *(2026-03-07)* +- [x] Account lockout set to 5/30 *(2026-03-09)* +- [x] Monica.Ramirez removed from Domain Admins *(2026-03-09)* +- [x] 3 empty GPOs reviewed (CopyRoomPrinter, Nurses-Kiosk, MemCareMedTechPrinter) *(2026-03-07)* + +## Known Issues / Workarounds + +- **Older DirecTV boxes cannot connect to VLAN networks (CSCNet)** + - Must first join CSC ENT (non-VLAN), receive a software update, then they can join CSCNet + - Affects resident room DirecTV boxes — discovered 2026-03-22 + +- **Synology NAS is ext4 — cannot use Active Backup for Business** + - Use Windows Server Backup to Synology SMB share instead + - Cannot convert without wiping volume + +- **changepk.exe fails on Win 10 Home with Pro for Workstations key (error 0x80070490)** + - Must upgrade to Win 11 first, then apply key — or use key during ISO setup diff --git a/clients/cascades-tucson/docs/issues/log.md b/clients/cascades-tucson/docs/issues/log.md new file mode 100644 index 0000000..6293773 --- /dev/null +++ b/clients/cascades-tucson/docs/issues/log.md @@ -0,0 +1,618 @@ +# Issue Log + +Record past issues and their resolutions here. This helps the AI learn from historical +troubleshooting and avoid repeating failed approaches. + +--- + +### 2026-03-04 - CS-SERVER on 16-Year-Old Dell R610 — Imminent Hardware Failure Risk +- **Reported By:** AI audit of server systeminfo +- **Severity:** Critical +- **Symptoms:** CS-SERVER (192.168.2.254) is the PRIMARY DOMAIN CONTROLLER for cascades.local running on a Dell PowerEdge R610 (~2009 vintage). This single server hosts AD DS, DNS, DHCP, File Server, Hyper-V, RDS, IIS, and NPS. If this hardware fails — and at 16+ years old it is statistically overdue — ALL services go down simultaneously with NO backup and NO second domain controller. +- **Root Cause:** Legacy hardware never replaced. No redundancy or DR plan implemented. +- **Resolution:** OPEN — Priority actions: + 1. IMMEDIATE: Configure Synology Active Backup for Business to back up C: and D: nightly + 2. URGENT: Deploy a second domain controller (VM on Synology, or new hardware, or Azure AD DS) + 3. Plan full server migration to modern hardware (Dell PowerEdge T350/R350 or similar) + 4. Separate roles across multiple servers/VMs to reduce blast radius +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - No Backup Solution for Any System +- **Reported By:** AI audit +- **Severity:** Critical +- **Symptoms:** No backup exists for CS-SERVER, workstations, or M365 data. The only DC has no backup. File shares on D: (1.09TB, ~470GB used) are unprotected. Ransomware, hardware failure, or accidental deletion = permanent data loss. +- **Root Cause:** Backup solution never implemented. +- **Resolution:** PLANNED — **Migration Phase 0.1 + Phase 4.4** + 1. Synology Active Backup for Business (free) → back up CS-SERVER C: and D: to Synology NAS (Phase 0) + 2. Configure Hyper Backup on Synology → replicate to Backblaze B2 or Wasabi for offsite copy (Phase 4) + 3. Add M365 backup (Datto SaaS Protection, Veeam, or Acronis) (future) +- **Time to Resolve:** Phase 0 (Day 1 evening) +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - Single Domain Controller — No AD Redundancy +- **Reported By:** AI audit of server roles +- **Severity:** Critical +- **Symptoms:** CS-SERVER is the only domain controller for cascades.local. No secondary DC exists. AD DS, DNS for domain resolution, and DHCP (Windows) all depend on this single machine. +- **Root Cause:** Second DC was never deployed. +- **Resolution:** OPEN — Deploy a second DC. Options: + 1. VM on Synology (if it supports VMs) running Server 2019/2022 as DC + 2. New physical server (budget permitting) + 3. Azure AD DS or a cloud-hosted DC with site-to-site VPN +- **Time to Resolve:** Pending — DC promotion takes ~1 hour once hardware/VM is ready +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - DHCP Conflict Check (pfSense + Windows DHCP) +- **Reported By:** AI audit of pfSense config + server roles +- **Severity:** ~~Medium~~ **RESOLVED** +- **Symptoms:** Both pfSense and CS-SERVER have DHCP server roles installed. +- **Root Cause:** Windows DHCP role is installed but has ZERO scopes configured. pfSense handles all DHCP for all VLANs. No conflict. +- **Resolution:** RESOLVED — `Get-DhcpServerv4Scope` returned no results. pfSense is the sole DHCP server. Windows DHCP role could optionally be uninstalled to reduce confusion. +- **Time to Resolve:** Resolved 2026-03-04 +- **Lessons Learned:** Role installed ≠ role active. Always verify scopes. + +--- + +### 2026-03-04 - CS-QB VoIP VM Running on Failing Hardware +- **Reported By:** AI audit of Hyper-V VMs +- **Severity:** Medium +- **Symptoms:** CS-QB VM (VoIP server, 2.35 GB RAM, IP 192.168.2.228) runs on CS-SERVER. VoIP is not MSP-managed, but the VM infrastructure IS. If CS-SERVER hardware fails, the phone system goes down with it. +- **Root Cause:** VoIP VM placed on the only server, which is on 16-year-old hardware. +- **Resolution:** OPEN — When CS-SERVER is migrated to new hardware, ensure CS-QB VM is migrated or relocated. Coordinate with VoIP provider since they require static IPs. +- **Time to Resolve:** Pending — tied to server migration project +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - Synology Sync Machine VM is Powered Off +- **Reported By:** AI audit of Hyper-V VMs +- **Severity:** ~~Low~~ **RESOLVED** +- **Symptoms:** A VM called "Synology Sync machine" exists on CS-SERVER but is powered off. +- **Root Cause:** VM was set up "just in case" but was never used. +- **Resolution:** RESOLVED — Confirmed by Howard: VM does nothing, safe to delete. Scheduled for deletion in **Migration Phase 5.2**. +- **Time to Resolve:** Resolved 2026-03-04 (deletion in Phase 5) +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - CS-SERVER Timezone Mismatch with pfSense +- **Reported By:** AI audit +- **Severity:** ~~Low~~ **RESOLVED** +- **Symptoms:** CS-SERVER is set to Pacific Time (UTC-8, observes DST). pfSense is set to America/Phoenix (UTC-7, does NOT observe DST). During DST (March-November), these are the same time. Outside DST, they differ by 1 hour. This can cause Kerberos authentication issues, log correlation problems, and GPO scheduling mismatches. +- **Root Cause:** Inconsistent timezone configuration. +- **Resolution:** RESOLVED 2026-03-07 — Set CS-SERVER to `(UTC-07:00) Arizona` via `Set-TimeZone -Id "US Mountain Standard Time"`. +- **Time to Resolve:** Resolved 2026-03-07 +- **Lessons Learned:** Arizona does not observe DST — use "US Mountain Standard Time" not "Mountain Standard Time". + +--- + +### 2026-03-04 - Floating Firewall Rule #4 Passes ALL IPv4 Traffic +- **Reported By:** AI audit of pfSense config +- **Severity:** High +- **Symptoms:** Floating rule #4 (PASS, any interface, IPv4, any source, any destination) overrides per-interface rules. Breaks room-to-room isolation. Residents can potentially reach staff VLAN (10.0.20.0/24), management interfaces, VoIP phones, and other rooms. +- **Root Cause:** Overly permissive floating rule — likely added as a quick fix or during initial setup and never scoped down. +- **Resolution:** PLANNED — **Migration Phase 1.3.** Disable floating rule #4, replace with: ResidentsGroup → ! RFC1918 (rooms internet only). Replace INTERNAL rules with scoped AD/SMB/Print/ICMP allows. See `migration/phase1-network.md`. +- **Time to Resolve:** Phase 1 (Day 1 evening) +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - 9 Offline Access Points +- **Reported By:** AI audit of UniFi AP data +- **Severity:** Medium +- **Symptoms:** 9 APs showing offline in UniFi controller. Coverage gaps on floors 1-4. +- **Affected APs:** + - 108 (192.168.6.127) — wrong IP range, was mesh uplink + - 121 (192.168.2.184) — mesh uplink + - 128 (192.168.2.95) — no uplink detected + - 204 (192.168.7.243) — wrong IP range, no uplink + - 335 (192.168.2.206) — no uplink + - 406 (192.168.2.4) — no uplink + - 441 (192.168.2.200) — no uplink + - 450 (192.168.6.207) — wrong IP range, no uplink + - 4th Floor Atrium (192.168.3.28) — no uplink +- **Root Cause:** Unknown — could be power loss, cable failure, or adoption issues. APs 108, 204, and 450 have IPs in 192.168.6.x/7.x which are outside the LAN DHCP range (192.168.2.x-3.x), suggesting they may have been misconfigured or adopted to wrong network. +- **Resolution:** OPEN — Visit each AP physically. Check PoE power, cable, and switch port status. Re-adopt APs with wrong IPs. Prioritize 4th Floor Atrium (common area coverage). +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - Room 218 DHCP Scope Misconfigured (Single IP) +- **Reported By:** AI audit of pfSense config +- **Severity:** ~~Medium~~ **RESOLVED** +- **Symptoms:** Room 218 DHCP scope range is 10.2.18.2 to 10.2.18.2. Only 1 IP can be leased. Any second device in the room will fail to get an address. +- **Root Cause:** Typo or error in pfSense DHCP configuration. End of range should be 10.2.18.14 (matching the /28 pattern used by all other rooms). +- **Resolution:** RESOLVED 2026-03-07 — Range end changed to 10.2.18.14. +- **Time to Resolve:** Resolved 2026-03-07 +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - Room 339 Interface Possibly Disabled +- **Reported By:** AI audit of pfSense config +- **Severity:** Medium +- **Symptoms:** Room 339 VLAN interface (igc1.339, opt129) is missing the `` tag in the pfSense config. Room may have no network connectivity. +- **Root Cause:** Unknown — may be intentional (vacant room) or a config oversight. +- **Resolution:** OPEN — Verify if room 339 is occupied. If yes, enable the interface in pfSense under Interfaces > Room339. If intentionally disabled, document the reason. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - 206 Health Services Printer Drops WiFi +- **Reported By:** Staff / printer doc +- **Severity:** Medium +- **Symptoms:** Brother MFC-L8900CDW at 192.168.1.138 will not stay connected to wireless. Repeated disconnections. +- **Root Cause:** Likely combination of: Brother deep sleep mode dropping WiFi, possible weak signal from nearest AP, and/or DHCP lease issues. IP is in 192.168.1.x range (valid within /22 LAN but unusual compared to most devices on 192.168.2.x-3.x). +- **Resolution:** OPEN — Recommended fix priority: + 1. Wire the printer (run ethernet to nearest switch port) + 2. If wiring not possible: set DHCP reservation, disable deep sleep on printer, verify AP signal strength in room 206 +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - Bizhub C368 Location and Status Unknown +- **Reported By:** AI audit of printer inventory +- **Severity:** Low +- **Symptoms:** A Bizhub C368 is listed in the printer inventory but has no IP address, no location, and no status documented. Since printers are rentals, this unit needs to be accounted for. +- **Root Cause:** Incomplete documentation. +- **Resolution:** OPEN — Locate the Bizhub C368 physically. Document its IP, location, and connection type. Verify it's on the rental tracking list. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - Room 130 Disabled Firewall Rule +- **Reported By:** AI audit of pfSense config +- **Severity:** ~~Low~~ **RESOLVED** +- **Symptoms:** Room 130 has a disabled TCP PASS rule in pfSense. Dead rules add clutter and confusion to the firewall config. +- **Root Cause:** Likely a test rule that was disabled and never removed. +- **Resolution:** RESOLVED 2026-03-07 — Disabled rule deleted from Room130 interface. +- **Time to Resolve:** Resolved 2026-03-07 +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - SSH Enabled on pfSense — Verify Hardening +- **Reported By:** AI audit of pfSense config +- **Severity:** Low +- **Symptoms:** SSH is enabled on pfSense. Not inherently a problem but should be hardened. +- **Root Cause:** N/A — feature is intentionally enabled for remote management. +- **Resolution:** OPEN — Verify: key-based auth only (no password auth), SSH access restricted to management VLAN or VPN only, not exposed on WAN. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-04 - RMM Data Not Documented +- **Reported By:** AI audit +- **Severity:** Low +- **Symptoms:** The RMM documentation is blank. No visibility into what devices are monitored, what alerts are configured, or what patching policies are in place. +- **Root Cause:** Documentation gap. +- **Resolution:** OPEN — Fill in rmm/rmm.md with the RMM product, agent counts, monitoring policies, and patch schedule. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-05 - No Group Policy Configured +- **Reported By:** AI audit of AD +- **Severity:** High +- **Symptoms:** `gpresult /r` shows no RSoP data on CRYSTAL-PC. No GPOs are linked to any OU. No password policy, no account lockout policy, no security baselines, no drive mappings — only Windows defaults apply. +- **Root Cause:** GPOs were never created or configured. +- **Resolution:** PLANNED — **Migration Phase 2.5 + 3.2.** Create 4 GPOs (Security Baseline, Drive Mappings, Printer Deployment, Windows Update). Link after first successful domain join. See `migration/phase2-server-prep.md`. +- **Time to Resolve:** Phase 2-3 (Day 2-3) +- **Lessons Learned:** N/A + +--- + +### 2026-03-05 - RDS Licensing Not Configured — Compliance Risk +- **Reported By:** AI audit of CS-SERVER +- **Severity:** High +- **Symptoms:** `Get-RDLicenseConfiguration` returns Mode=NotConfigured, no license servers. RDS roles (Connection Broker, Session Host, Web Access) are installed. Server was installed 8/4/2024 — the 120-day grace period expired ~17 months ago. +- **Root Cause:** RDS CALs were never purchased or configured. +- **Resolution:** PLANNED — **Migration Phase 5.4.** Check if anyone uses RDS. If yes → purchase CALs. If no → uninstall RDS role. +- **Time to Resolve:** Phase 5 (Day 4) +- **Lessons Learned:** N/A + +--- + +### 2026-03-05 - Only 6 Computers Domain-Joined (4+ Staff PCs Missing) +- **Reported By:** AI audit of AD computers +- **Severity:** Medium +- **Symptoms:** `Get-ADComputer` returns only 6 objects (CS-SERVER, CS-QB, CRYSTAL-PC, ACCT2-PC, DESKTOP-H6QHRR7, DESKTOP-1ISF081). Known staff PCs SALES4-PC, CHEF-PC, MDIRECTOR-PC, and DESKTOP-KQSL232 are NOT in AD. These machines are on the network but not domain-joined. +- **Root Cause:** PCs were likely set up as workgroup machines and never joined to the domain. +- **Resolution:** PLANNED — **Migration Phase 3.** Join in order: DESKTOP-KQSL232 → CHEF-PC → SALES4-PC → MDIRECTOR-PC. See `migration/phase3-domain-join.md` and scripts. +- **Time to Resolve:** Phase 3 (Day 3) +- **Lessons Learned:** N/A + +--- + +### 2026-03-05 - Shared/Generic AD Accounts in Use +- **Reported By:** AI audit of AD users +- **Severity:** Medium +- **Symptoms:** Three shared/generic accounts exist and are enabled: Culinary, Receptionist, saleshare. Shared accounts cannot be audited to a specific person — if something is deleted or accessed inappropriately, there's no way to trace who did it. +- **Root Cause:** Created for convenience instead of individual accounts. +- **Resolution:** PLANNED — **Migration Phase 5.3.** Replace with individual user accounts + security group membership. +- **Time to Resolve:** Phase 5 (Day 4) +- **Lessons Learned:** N/A + +--- + +### 2026-03-05 - 5 Stale Disabled AD Accounts +- **Reported By:** AI audit of AD users +- **Severity:** Low +- **Symptoms:** 5 disabled user accounts (Anna.Pitzlin, Nela.Durut-Azizi, Jodi.Ramstack, Monica.Ramirez, Jeff.Bristol) are still in AD. Disabled accounts with remaining permissions or group memberships can be a security risk if re-enabled. +- **Root Cause:** Former employees disabled but not removed. +- **Resolution:** PLANNED — **Migration Phase 2.2.** Delete after client confirmation. See `migration/scripts/phase2-ad-setup.ps1` ($DeleteStaleAccounts flag). +- **Time to Resolve:** Phase 2 (Day 2) +- **Lessons Learned:** N/A + +--- + +### 2026-03-05 - QuickBooks Pro 2024 Installed on Domain Controller +- **Reported By:** AI audit of installed software +- **Severity:** High +- **Symptoms:** QuickBooks Pro 2024 (v34.0) is installed directly on CS-SERVER, the primary domain controller. QuickBooks Database Server is listening on port 6600. Business applications should never run on a DC — they increase attack surface, consume resources needed for AD services, and complicate server migration. +- **Root Cause:** QuickBooks was installed on the only available server rather than a dedicated workstation or VM. +- **Resolution:** OPEN — Migrate QuickBooks to a dedicated workstation or VM. The QuickBooks database files on D:\Shares can still be accessed via network share. QuickBooks Desktop can run in multi-user mode with the database on a file share. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-05 - Stale DNS Records in cascades.local Zone +- **Reported By:** AI audit of Windows DNS +- **Severity:** ~~Medium~~ **RESOLVED** +- **Symptoms:** Multiple DNS A records in the cascades.local zone point to incorrect or outdated IPs: + - cascades.local @ → 192.168.0.5 and 192.168.2.59 (DC is actually at 192.168.2.254) + - CRYSTAL-PC → 192.168.5.115 (should be 10.0.20.205) + - CS-QB → 192.168.5.29 (should be 192.168.2.228) + - DESKTOP-1ISF081 → 192.168.5.30 (192.168.5.x is not a documented subnet) + - DomainDnsZones/ForestDnsZones → 192.168.0.5 and 192.168.2.59 +- **Root Cause:** Dynamic DNS registrations from old IPs were never cleaned up. DNS scavenging was not configured. +- **Resolution:** RESOLVED 2026-03-06 — All stale records removed. Correct @ record added (192.168.2.254). DomainDnsZones/ForestDnsZones fixed. DNS scavenging enabled (7-day interval). First scavenge available 3/13/2026. +- **Time to Resolve:** Resolved 2026-03-06 +- **Lessons Learned:** Enable DNS scavenging on all AD-integrated zones. Set aging on zone + server-level scavenging. + +--- + +### 2026-03-05 - No Reverse DNS Lookup Zones +- **Reported By:** AI audit of Windows DNS +- **Severity:** ~~Low~~ **RESOLVED** +- **Symptoms:** No reverse lookup zones exist for production subnets (192.168.0.0/22, 10.0.20.0/24). Only auto-created placeholder zones (0, 127, 255). PTR lookups will fail for all internal hosts. +- **Root Cause:** Reverse zones were never created. +- **Resolution:** RESOLVED 2026-03-06 — 5 reverse lookup zones created: 0.168.192, 1.168.192, 2.168.192, 3.168.192.in-addr.arpa + 20.0.10.in-addr.arpa. +- **Time to Resolve:** Resolved 2026-03-06 +- **Lessons Learned:** N/A + +--- + +### 2026-03-05 - Guest WiFi on Same Network as Servers — No Isolation +- **Reported By:** AI audit of UniFi WiFi config +- **Severity:** ~~High~~ **RESOLVED** +- **Symptoms:** The "Guest" SSID is mapped to the Native Network (Default LAN, 192.168.0.0/22). Guest users land on the same L2/L3 network as CS-SERVER (DC, 192.168.2.254), Synology NAS (192.168.0.120), pfSense management (192.168.0.1), and all LAN infrastructure. +- **Root Cause:** Guest SSID was configured on the default/native network instead of an isolated VLAN. +- **Resolution:** RESOLVED 2026-03-06 — Guest VLAN 50 created (10.0.50.0/24, igc1.50 GUEST interface). DHCP scope 10.0.50.50-239. Four firewall rules (block LAN, block 10.x, block 172.x, pass internet). Guest SSID reassigned to VLAN 50 in UniFi. **Needs onsite test to confirm isolation.** +- **Time to Resolve:** Resolved 2026-03-06 (pending onsite verification) +- **Lessons Learned:** Always isolate guest WiFi on a dedicated VLAN with explicit deny rules to RFC1918. + +--- + +### 2026-03-05 - UniFi VLAN 10 "CSC Internal Network" Mismatch +- **Reported By:** AI audit of UniFi WiFi config +- **Severity:** Low +- **Symptoms:** UniFi defines "CSC Internal Network" as VLAN 10, but pfSense has the INTERNAL staff interface on VLAN 20 (igc1.20, 10.0.20.0/24). UniFi also has "Internal" on VLAN 20 (correct). VLAN 10 may be orphaned or cause confusion. +- **Root Cause:** Likely a leftover from initial setup or a mislabeled network. +- **Resolution:** PLANNED — **Migration Phase 1.5.** Verify unused, delete from UniFi. +- **Time to Resolve:** Phase 1 (Day 1 evening) +- **Lessons Learned:** N/A + +--- + +### 2026-03-07 - Non-IT Staff in Domain Admins Group +- **Reported By:** AD export analysis (2026-03-07) +- **Severity:** High +- **Symptoms:** Domain Admins group contains 5 members. Meredith.Kuhn (administrative staff) and John.Trozzi (maintenance) are Domain Admins. Monica.Ramirez (DISABLED account) is still in Domain Admins. Non-IT users with DA privileges can accidentally or intentionally modify AD, GPOs, DNS, or any domain-joined system. +- **Root Cause:** Overly permissive group membership, likely set up for convenience. +- **Resolution:** PLANNED — Phase 2.2. Remove Monica.Ramirez, Meredith.Kuhn, and John.Trozzi from Domain Admins. Only Administrator and sysadmin should remain. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-07 - 3 Undocumented GPOs Found (Dec 2025) +- **Reported By:** AD export analysis (2026-03-07) +- **Severity:** ~~Medium~~ **REVIEWED** +- **Symptoms:** Three GPOs exist that were not previously documented: CopyRoomPrinter (Dec 10, 2025), Nurses-Kiosk (Dec 15, 2025), MemCareMedTechPrinter (Dec 11, 2025). Unknown who created them, what settings they contain, and whether they are linked to any OU. +- **Root Cause:** Created by previous MSP or sysadmin account, not documented. +- **Resolution:** REVIEWED 2026-03-07 — GPO report exported and analyzed. **All 3 GPOs are completely empty** — no computer or user configuration settings defined, not linked to any OU. Safe to delete with zero impact. Delete during Phase 2.2 AD cleanup. +- **Time to Resolve:** Reviewed 2026-03-07, deletion pending Phase 2.2 +- **Lessons Learned:** N/A + +--- + +### 2026-03-07 - Account Lockout Policy Disabled (Default Domain Policy) +- **Reported By:** GPO report analysis (2026-03-07) +- **Severity:** High +- **Symptoms:** Default Domain Policy has account lockout threshold set to 0 (disabled). This means unlimited failed password attempts are allowed with no lockout, enabling brute-force attacks against any AD account. +- **Root Cause:** Default Domain Policy was never hardened — only basic password complexity was configured. +- **Resolution:** PLANNED — Set account lockout threshold to 5 invalid attempts, lockout duration 30 minutes, reset counter after 30 minutes. Will be applied via the planned "CSC - Security Baseline" GPO in Phase 2.6. +- **Time to Resolve:** Pending Phase 2.6 +- **Lessons Learned:** N/A + +--- + +### 2026-03-07 - Synology NAS Uses ext4 — Active Backup for Business Blocked +- **Reported By:** Backup setup attempt (2026-03-07) +- **Severity:** Medium +- **Symptoms:** Synology Active Backup for Business requires Btrfs filesystem. Synology Volume 1 is formatted as ext4. Cannot convert without wiping the volume. +- **Root Cause:** NAS was set up with ext4 (older default) rather than Btrfs. +- **Resolution:** PLANNED — Use Windows Server Backup on CS-SERVER to back up to a Synology SMB share instead. Alternative: Veeam Backup Free Edition. +- **Time to Resolve:** Pending — next session +- **Lessons Learned:** Always verify NAS filesystem before planning backup solutions. ABB requires Btrfs. + +--- + +### 2026-03-07 - ARP Flapping: LG TV Dual-Connected (WiFi + Ethernet) +- **Reported By:** ARP log analysis +- **Severity:** ~~High~~ **RESOLVED** +- **Symptoms:** 192.168.2.148 and 192.168.2.152 flapping every 30-60 seconds between MACs `58:96:0a:c6:1e:49` (ethernet) and `e0:85:4d:4d:f0:3e` (WiFi). Hundreds of ARP "moved from" entries daily. Constant since at least Feb 8. +- **Root Cause:** LG webOS TV connected via both ethernet (1st Floor USW Port 18) and WiFi (CSC ENT, 5 GHz) simultaneously. Both interfaces claimed the same IP, causing pfSense ARP table to flip between MACs. +- **Resolution:** RESOLVED 2026-03-07 — Disabled ethernet port (1st Floor USW Port 18) in UniFi. TV now WiFi-only. DHCP reservation recommended for WiFi MAC `e0:85:4d:4d:f0:3e` at 192.168.2.148. +- **Time to Resolve:** Resolved 2026-03-07 +- **Lessons Learned:** Check for dual-homed devices (WiFi + ethernet) when ARP flapping is detected. Smart TVs often auto-connect to both. + +--- + +### 2026-03-07 - ARP Flapping: Brother Printer at 192.168.2.53 Dual-Connected +- **Reported By:** ARP log analysis +- **Severity:** Medium +- **Symptoms:** 192.168.2.53 flapping between MACs `c8:a3:e8:a2:dd:9e` (ethernet, MemCare switch Port 3) and `80:3f:5d:72:7b:ee` (WiFi, CSC ENT 2.4 GHz). Hostname BRWC8A3E8A2DD9E confirms Brother printer. +- **Root Cause:** Brother printer connected via both ethernet and WiFi simultaneously, same as the LG TV issue. +- **Resolution:** OPEN — Needs onsite visit to determine whether to keep WiFi or ethernet connection. Disable the other. +- **Time to Resolve:** Pending onsite visit +- **Lessons Learned:** N/A + +--- + +### 2026-03-07 - Minor ARP Flapping in Resident Rooms (307, 130) and iPhone MAC Randomization +- **Reported By:** ARP log analysis +- **Severity:** Low +- **Symptoms:** Occasional ARP flapping: Room 307 (10.3.7.7, evenings), Room 130 (10.1.30.8, Feb 22 only), and 192.168.3.115 (iPhone, ~6 AM daily). Room 130 and 307 devices no longer on network. iPhone flapping caused by iOS private MAC randomization. +- **Root Cause:** Room conflicts likely transient DHCP collisions from resident devices. iPhone conflict is normal iOS behavior. +- **Resolution:** No action required. Room 307 noted for onsite check. Room 130 devices gone. iPhone MAC randomization is cosmetic. +- **Time to Resolve:** N/A +- **Lessons Learned:** N/A + +--- + +### 2026-03-07 - AD OU Structure: 13 Junk Root-Level OUs + CN=Users Misplacement +- **Reported By:** AD export analysis (2026-03-07) +- **Severity:** Medium +- **Symptoms:** 10 duplicate department OUs exist at root level AND under Departments OU. 3 additional empty OUs (Managment [misspelled], MemCare, Sales) at root. 20 user accounts sitting in the default CN=Users container instead of proper department OUs. 5 computers in default CN=Computers instead of a Workstations OU. +- **Root Cause:** Previous admin created OUs at root level, then also under Departments. Accounts were created in the default container and never moved. +- **Resolution:** PLANNED — Phase 2.1. Scripts created: + 1. `phase2-ou-cleanup.ps1` — Audit root OUs (confirm empty), delete 13 root-level OUs, delete/disable stale accounts in CN=Users, flag Lupe.Sanchez for review + 2. `phase2-ad-setup.ps1` — Create Workstations OU, move 4 staff PCs from CN=Computers +- **Time to Resolve:** Pending — run on CS-SERVER via ScreenConnect +- **Lessons Learned:** N/A + +--- + +### 2026-03-07 - Lupe.Sanchez: Possible Duplicate of Guadalupe.Sanchez +- **Reported By:** AD export analysis (2026-03-07) +- **Severity:** Low +- **Symptoms:** Lupe.Sanchez exists in CN=Users (enabled). Guadalupe.Sanchez already exists in Departments\Housekeeping. "Lupe" is a common nickname for "Guadalupe". One account may be a duplicate. +- **Root Cause:** Unknown — may have been created by different admins at different times. +- **Resolution:** OPEN — Flag for client review onsite. Check which account is actively used, delete the duplicate. +- **Time to Resolve:** Pending onsite visit +- **Lessons Learned:** N/A + +--- + +### 2026-03-08 - M365 Licenses Fully Allocated (0 Available) +- **Reported By:** M365 admin portal audit (2026-03-08) +- **Severity:** High +- **Symptoms:** Microsoft 365 Business Standard is 34/34 with 0 available. Cannot add new users (e.g., nick pavloff, new hire created 2026-03-07) without purchasing additional licenses. +- **Root Cause:** 12 generic/role-based accounts (accounting@, frontdesk@, hr@, security@, memcarereceptionist@, boadmin@, accountingassistant@, Training@, etc.) each consume a full Business Standard license ($12.50/mo) instead of being shared mailboxes (free). +- **Resolution:** PLANNED — Convert role-based accounts to shared mailboxes in Exchange Online. This frees up ~12 licenses (~$150/mo savings). Shared mailboxes don't require a license and still receive/send email. Users access them via permissions instead of logging in directly. +- **Time to Resolve:** Pending — next M365 cleanup session +- **Lessons Learned:** N/A + +--- + +### 2026-03-08 - 12 Role-Based M365 Accounts Wasting Licenses +- **Reported By:** M365 user export analysis (2026-03-08) +- **Severity:** Medium +- **Symptoms:** 12 generic/role-based accounts are set up as regular licensed users instead of shared mailboxes: accounting@, accountingassistant@, boadmin@, frontdesk@, hr@, memcarereceptionist@, security@, Training@, Kitchenipad@, medtech@, nurse@, transportation@. Each consumes a Business Standard or Exchange Online Essentials license. +- **Root Cause:** Previous admin created role accounts as regular users rather than shared mailboxes. +- **Resolution:** PLANNED — For each: convert to shared mailbox (`Set-Mailbox -Type Shared`), remove license, grant FullAccess permission to appropriate users. No data loss — mailbox contents are preserved during conversion. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-08 - AD ↔ M365 Identity Mismatch: Tamra Johnson → Matthews +- **Reported By:** M365/AD cross-reference (2026-03-08) +- **Severity:** Medium +- **Symptoms:** AD account is Tamra.Johnson. M365 UPN is tamra.matthews@cascadestucson.com (name changed, likely married). The tamra.johnson@ alias still works for email but the display name and UPN are mismatched between AD and M365. +- **Root Cause:** Name change updated in M365 but not in AD. +- **Resolution:** PLANNED — Update AD: rename Tamra.Johnson to Tamra.Matthews (Set-ADUser, Rename-ADObject). Coordinate timing — may affect drive mappings, profile path, etc. if already domain-joined. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-08 - 13 AD Users Have No M365 Account +- **Reported By:** M365/AD cross-reference (2026-03-08) +- **Severity:** Low +- **Symptoms:** 13 AD user accounts have no matching M365 account. These users cannot receive email at cascadestucson.com. +- **Root Cause:** Hourly/floor staff (front desk, courtesy patrol, transportation) given AD accounts but not M365 licenses. +- **Resolution:** CONFIRMED 2026-03-10 — HR verified all are current employees. Roles: Front Desk/Courtesy Patrol (Sebastian.Leon, Sheldon.Gardfrey, Cathy.Kingston, Ray.Rai), MC Front Desk (Michelle.Shestko), Transportation (Richard.Adams, Julian.Crim, Christopher.Holik), Housekeeping (Guadalupe.Sanchez — already has M365 as lupe.sanchez@). Shontiel.Nunn transferring soon. Alyssa.Brooks already has M365. Still need to determine if remaining users need email. +- **Time to Resolve:** Pending — determine email needs, free licenses via role account cleanup first +- **Lessons Learned:** N/A + +--- + +### 2026-03-08 - nick pavloff: New M365 User, No AD Account +- **Reported By:** M365 user export (2026-03-08) +- **Severity:** Medium +- **Symptoms:** nick.pavloff@cascadestucson.com was created 2026-03-07 with a Business Standard license. No matching AD account exists. User cannot log into domain-joined PCs or access file shares. +- **Root Cause:** New hire added to M365 but AD account not yet created. +- **Resolution:** PLANNED — Create AD account (Nick.Pavloff) in appropriate department OU. Determine department and group membership. Add to domain join list if they have a dedicated PC. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-08 - Kristiana Dowse: M365 Licensed, Not in AD +- **Reported By:** M365 user export (2026-03-08) +- **Severity:** Low +- **Symptoms:** kristiana.dowse@cascadestucson.com has a Business Standard license (created 2022-03-02, password never changed from default). No matching AD account exists. Unknown if current employee or former. +- **Root Cause:** Former employee whose M365 was never cleaned up. +- **Resolution:** CONFIRMED 2026-03-10 — HR confirmed DELETE. Block sign-in, remove license, delete account. Frees 1 Business Standard license. +- **Time to Resolve:** Pending — next M365 cleanup session +- **Lessons Learned:** N/A + +--- + +### 2026-03-08 - Sandra Fish Still Global Admin on M365 Tenant +- **Reported By:** M365 admin portal audit (2026-03-08) +- **Severity:** Medium +- **Symptoms:** admin@NETORGFT4257522.onmicrosoft.com (Sandra Fish) holds the Entra ID P2 license and appears to be the original/primary global admin. Sandra Fish is listed as previous owner/manager. +- **Root Cause:** Tenant was created under Sandra Fish's account and admin access was never transitioned. +- **Resolution:** OPEN — Verify with Howard: should Sandra Fish retain global admin? If she is no longer involved, create a new break-glass admin account, transfer global admin, and downgrade or remove her access. +- **Time to Resolve:** Pending client discussion +- **Lessons Learned:** N/A + +--- + +### 2026-03-08 - 3 Former Employee Shared Mailboxes Still Active +- **Reported By:** M365 admin portal (2026-03-08) +- **Severity:** Low +- **Symptoms:** Shared mailboxes exist for Anna Pitzlin, Jeff Bristol, and Nela Durut-Azizi — all confirmed former employees. Jeff Bristol and Nela Durut-Azizi have sign-in blocked. Anna Pitzlin's is unlicensed. +- **Root Cause:** Accounts were converted to shared mailboxes (or created as such) but never cleaned up. +- **Resolution:** CONFIRMED 2026-03-10 — HR says Anna and Nela were forwarded to Meredith but can now be deleted. Jeff Bristol still pending (Lauren Hasselman replaced him — may need forwarding). +- **Time to Resolve:** Pending — next M365 cleanup session +- **Lessons Learned:** N/A + +--- + +### 2026-03-08 - "howaed" Typo Guest Account in M365 +- **Reported By:** M365 user export (2026-03-08) +- **Severity:** Low +- **Symptoms:** External guest account howaed@azcomputerguru.com exists alongside the correct howard@azcomputerguru.com. The "howaed" account is a typo. +- **Root Cause:** Typo when inviting external guest. +- **Resolution:** PLANNED — Delete the howaed@azcomputerguru.com guest account from Entra ID. +- **Time to Resolve:** Quick fix — next M365 session +- **Lessons Learned:** N/A + +--- + +### 2026-03-08 - AD and M365 Are Completely Separate Identity Systems +- **Reported By:** M365/AD cross-reference (2026-03-08) +- **Severity:** Medium +- **Symptoms:** DirSync/Entra Connect is not configured. AD accounts and M365 accounts are completely independent — users have different passwords, no SSO, and no automatic provisioning. Changes in one system (name changes, new hires, terminations) must be manually replicated to the other. +- **Root Cause:** Entra Connect was never set up. +- **Resolution:** OPEN — Evaluate Entra Connect for hybrid identity. Consider: does the environment benefit from SSO? With only 6 domain-joined PCs currently, the value is limited. Revisit after Phase 3 domain joins bring more PCs online. For now, document the manual mapping in `cloud/m365.md`. +- **Time to Resolve:** Pending — evaluate after Phase 3 +- **Lessons Learned:** N/A + +--- + +### 2026-03-10 - Synology NAS Stores PHI Without Audit Controls +- **Reported By:** HIPAA compliance review (2026-03-10) +- **Severity:** Critical +- **Symptoms:** Synology NAS (cascadesDS, 192.168.0.120) stores PHI (resident/facility data). Filesystem is ext4 — cannot enable Windows audit logging. Access via SMB but no read/write audit trail. HIPAA §164.312(b) requires audit controls on PHI storage. +- **Root Cause:** Synology was never designed with compliance in mind. ext4 cannot support NTFS audit logging. +- **Resolution:** PLANNED — Migration Phase 4: Move all Synology shares to CS-SERVER NTFS volumes. Enable Advanced Audit Logging on PHI shares. Retire Synology from PHI scope (becomes backup target only). +- **Time to Resolve:** Phase 4 +- **Lessons Learned:** PHI storage must support audit logging. NTFS + AD group permissions provide compliance-grade access control. + +--- + +### 2026-03-10 - No Microsoft BAA Signed for M365 +- **Reported By:** HIPAA compliance review (2026-03-10) +- **Severity:** High +- **Symptoms:** M365 email (cascadestucson.com) may contain PHI in messages and attachments. No Business Associate Agreement (BAA) has been signed with Microsoft. HIPAA §164.308(b)(1) requires BAA with all business associates who handle PHI. +- **Root Cause:** BAA never signed by previous MSP or management. +- **Resolution:** PLANNED — Sign Microsoft HIPAA BAA via M365 Admin Center → Settings → Org Settings → Security & Privacy. Also verify BAA exists with ALIS (go-alis.com). +- **Time to Resolve:** Quick — can be done in M365 admin portal +- **Lessons Learned:** N/A + +--- + +### 2026-03-10 - No MFA Enabled on M365 +- **Reported By:** HIPAA compliance review (2026-03-10) +- **Severity:** High +- **Symptoms:** No MFA (Security Defaults or Conditional Access) configured on M365 tenant. Staff accounts that access ALIS and email with PHI are protected only by passwords. HIPAA §164.312(d) requires person authentication controls. +- **Root Cause:** MFA never enabled by previous MSP. +- **Resolution:** PLANNED — Enable Security Defaults in Entra ID (free). All users will be prompted to set up MFA on next login. +- **Time to Resolve:** Quick — 5 minutes to enable, users set up on next login +- **Lessons Learned:** N/A + +--- + +### 2026-03-10 - Kitchen iPads and Thermal Printers Not Inventoried or Isolated +- **Reported By:** HIPAA compliance review (2026-03-10) +- **Severity:** Medium +- **Symptoms:** 9 kitchen iPads are on INTERNAL VLAN (10.0.20.x) with full access to staff resources. They are food-service only (NOT medical — used for taking food orders). Kitchen thermal receipt printers exist but are not inventoried (count, IP, model unknown). iPads should be restricted to kitchen printers only. +- **Root Cause:** Kitchen operations not segmented from staff network. +- **Resolution:** PLANNED — Phase 1.1b: + 1. Onsite: inventory kitchen thermal printers (count, model, IP, MAC, connection type) + 2. Create firewall rules restricting kitchen iPad MACs to kitchen printer IPs only + 3. Block iPad access to servers, NAS, and staff resources + 4. Allow internet (80/443) for app updates +- **Time to Resolve:** Pending onsite visit +- **Lessons Learned:** Non-PHI devices should still be isolated to prevent lateral movement into PHI networks. + +--- + +### 2026-03-10 - ALIS BAA Status Unknown +- **Reported By:** HIPAA compliance review (2026-03-10) +- **Severity:** Medium +- **Symptoms:** Nurses and medtechs access clinical records via ALIS (go-alis.com) through web browsers. ALIS is a cloud SaaS handling PHI. HIPAA §164.308(b)(1) requires a BAA with ALIS as a business associate. Unknown if Cascades has a signed BAA with ALIS. +- **Root Cause:** Documentation gap — BAA status never verified. +- **Resolution:** OPEN — Ask management if a BAA exists with ALIS. If not, one must be executed before continuing to use the service. +- **Time to Resolve:** Pending management verification +- **Lessons Learned:** N/A + +--- + +### 2026-03-19 - Lauren Hasselman Needs Sales Share Access +- **Reported By:** Howard +- **Severity:** Low +- **Symptoms:** Lauren Hasselman (Business Office Director) does not have access to the Sales share on CS-SERVER. +- **Root Cause:** Permissions not granted when Lauren replaced Jeff Bristol. +- **Resolution:** OPEN — Grant Lauren access to \\CS-SERVER\Sales share. Add her AD account to the appropriate security group or add NTFS permissions directly. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-19 - Long Print Lag in Room 405 +- **Reported By:** Howard (staff report) +- **Severity:** Medium +- **Symptoms:** Resident room 405 experiencing long delays when printing. Specific printer unknown — likely a resident room printer. +- **Root Cause:** Unknown — could be WiFi signal, printer sleep mode, DNS resolution delay, or network path issue through per-room VLAN routing. +- **Resolution:** OPEN — Investigate onsite: + 1. Identify which printer room 405 is printing to + 2. Check network path (VLAN 10.4.5.0/28 → printer IP) + 3. Test ping/latency from room AP to printer + 4. Check printer sleep/power save settings + 5. Verify DNS resolution if printing by hostname +- **Time to Resolve:** Pending onsite visit +- **Lessons Learned:** N/A + +--- + + diff --git a/clients/cascades-tucson/docs/migration/README.md b/clients/cascades-tucson/docs/migration/README.md new file mode 100644 index 0000000..2475d38 --- /dev/null +++ b/clients/cascades-tucson/docs/migration/README.md @@ -0,0 +1,122 @@ +# Cascades Network Migration — Revised Operational Plan + +## Context + +Cascades senior living facility (236 rooms, 6 floors). New MSP takeover from previous company that left environment non-compliant. **Core mission: HIPAA remediation and compliance.** Synology NAS stores PHI, nurses/medtechs access clinical records via ALIS (cloud), and email may contain resident data. See `security/hipaa.md` for full gap analysis. + +Single 16-year-old server (CS-SERVER, 192.168.2.254) on LAN (192.168.0.0/22) running all roles. Staff PCs currently on WiFi (INTERNAL VLAN 20, 10.0.20.0/24). Printers on LAN. No backups, no GPOs, wide-open firewall, 4 PCs not domain-joined. + +**Revised approach:** Network first. Move all devices to INTERNAL VLAN 20, then lock down. Server and printers move to INTERNAL **last** — no disruption during transition. + +**Transitional state:** Machines on INTERNAL (10.0.20.0/24) → pfSense firewall bridges → CS-SERVER + printers on LAN (192.168.0.0/22). Everything works cross-subnet until we move the server. + +**HIPAA drives every phase:** Backup (Phase 0) → network isolation (Phase 1) → access control + encryption (Phase 2) → centralized management (Phase 3) → PHI migration with audit trails (Phase 4) → shared account elimination (Phase 5). + +--- + +## Schedule + +| Session | Steps | Est. Time | Impact | +|---------|-------|-----------|--------| +| Session 1 (evening) | 1 + 2 | ~3-4 hours | Backup + firewall changes during low usage | +| Session 2 (coordinated) | 3 | ~2-3 hours | Brief disruption per machine during port change | +| Session 3 (business hours) | 4 | ~4-6 hours | No user impact — server-side only | +| Session 4 (coordinated) | 5 | ~4-6 hours | Brief disruption per machine during domain join | +| Session 5 (business hours) | 6 + 8 | ~4-5 hours | Synology cutover + hardening | +| Session 6 (TBD) | 7 | ~3-4 hours | Server/printer IP changes — schedule when stable | + +**Total: ~20-28 hours across 6 sessions** + +--- + +## Steps + +| Step | Description | Runbook | Scripts | +|------|-------------|---------|---------| +| 1 | Emergency Backup | [phase0-safety-net.md](phase0-safety-net.md) | [phase0-export-configs.ps1](scripts/phase0-export-configs.ps1), [phase0-remote-checks.ps1](scripts/phase0-remote-checks.ps1) | +| 2 | Firewall & VLAN Setup | [phase1-network.md](phase1-network.md) | Manual (pfSense/UniFi web UI) | +| 3 | Identify & Move Switch Ports | [step3-switch-ports.md](step3-switch-ports.md) | Manual (UniFi web UI + on-site) | +| 4 | Server Preparation — AD & Shares | [phase2-server-prep.md](phase2-server-prep.md) | [phase2-dns-cleanup.ps1](scripts/phase2-dns-cleanup.ps1), [phase2-ad-setup.ps1](scripts/phase2-ad-setup.ps1), [phase2-sync-synology.ps1](scripts/phase2-sync-synology.ps1), [phase2-file-shares.ps1](scripts/phase2-file-shares.ps1), [phase2-print-server.ps1](scripts/phase2-print-server.ps1) | +| 5 | Domain Join | [phase3-domain-join.md](phase3-domain-join.md) | [phase3-pre-join-verify.ps1](scripts/phase3-pre-join-verify.ps1), [phase3-join-domain.ps1](scripts/phase3-join-domain.ps1), [phase3-post-join-verify.ps1](scripts/phase3-post-join-verify.ps1) | +| 6 | Synology Transition | [phase4-synology.md](phase4-synology.md) | [phase4-archive-synology.ps1](scripts/phase4-archive-synology.ps1) | +| 7 | Move Server & Printers to INTERNAL | [step7-server-move.md](step7-server-move.md) | Manual | +| 8 | Hardening & Cleanup | [phase5-hardening.md](phase5-hardening.md) | Manual + documentation updates | + +--- + +## Session Log + +| Session | Date | Focus | Status | +|---------|------|-------|--------| +| 1 | 2026-03-06 | Initial audit, data gathering, documentation buildout | Done | +| 2 | 2026-03-06 | Guest WiFi isolation, DNS fixes, firewall aliases | Done | +| 3 | 2026-03-07 | Backup setup, config exports, quick fixes | [session3-2026-03-07.md](session3-2026-03-07.md) | +| 4 | TBD | Firewall aliases, INTERNAL rules, floating rule #4 | Planned | +| 5 | TBD (onsite) | Test isolation, gather device info, Pro upgrade | Planned | + +--- + +## On-Site Tasks (separate trip) + +| Task | Why | +|------|-----| +| Fix 9 offline APs | Physical access to check PoE, cables, re-adopt | +| Wire 206 printer (ethernet) | Cable run | +| Locate Bizhub C368 | Physical walkthrough | +| Get printer MAC addresses | If not in pfSense ARP/DHCP table | +| Verify switch port assignments | Physical trace if UniFi doesn't show clearly | + +--- + +## Information Still Needed + +1. **Switch port mappings** — Which switch port is each hardwired workstation plugged into? Check UniFi → Clients or trace physically. Only CHEF-PC (USW Lite 8 Port 7) is known. +2. **DESKTOP-1ISF081 IP and location** — What IP does it have and where is it physically? +3. **MDIRECTOR-PC** — Confirm it should move to INTERNAL or stay on LAN (MemCare Director's machine, currently at 192.168.3.20) +4. **Printer MAC addresses** — Need for DHCP reservations if not already in pfSense ARP table +5. **Step 7 decision** — Move CS-SERVER to INTERNAL, dual-home it, or leave on LAN permanently? + +--- + +## Rollback Procedures + +Each step has a rollback section. Key rollbacks: +- **Step 2:** Re-enable floating rule #4, revert Guest SSID, restore pfSense XML backup +- **Step 3 (per machine):** Revert switch port to native VLAN +- **Step 4:** Unlink GPOs from GPMC. DNS records exported in Step 1. +- **Step 5 (per machine):** Log in with MSPAdmin local account, `Remove-Computer -UnjoinDomainCredential (Get-Credential) -Restart` +- **Step 6:** Rename archive folder back to SynologyDrive +- **Step 7:** Revert printer/server IPs, restore firewall rules + +--- + +## Verification + +After each step, confirm: +- **Step 2:** INTERNAL machines can reach server + printers through firewall +- **Step 3:** Hardwired machines on INTERNAL get correct IPs, reach server + printers +- **Step 4:** All shares/groups/GPOs created correctly on CS-SERVER +- **Step 5:** Domain-joined machines get GPOs, drive mappings, printers automatically +- **Step 6:** Users can access all files via mapped drives (no more Synology Drive Client) +- **Step 7:** Server/printers accessible on new IPs from all machines +- **Step 8:** Endpoint security deployed, old accounts/shares cleaned up + +--- + +## Issues Resolved + +| Issue | Resolution | +|-------|-----------| +| Floating rule #4 passes all IPv4 | Replaced with scoped rules | +| Guest WiFi on server LAN | Isolated to VLAN 50 | +| No GPOs configured | Security baseline, drives, printers, updates, folder redirection | +| 4 PCs not domain-joined | All joined | +| No backup | Synology ABB + offsite | +| Shared/generic AD accounts | Replaced with individual accounts | +| Stale DNS records | Cleaned up, scavenging enabled | +| Room 218 DHCP (single IP) | Range end fixed | +| Timezone mismatch | Both set to America/Phoenix | +| Room 130 dead firewall rule | Deleted | +| VLAN 10 mismatch | Deleted from UniFi | +| 5 stale disabled AD accounts | Deleted | +| Synology Sync VM | Deleted from Hyper-V | diff --git a/clients/cascades-tucson/docs/migration/phase0-safety-net.md b/clients/cascades-tucson/docs/migration/phase0-safety-net.md new file mode 100644 index 0000000..d7e48bb --- /dev/null +++ b/clients/cascades-tucson/docs/migration/phase0-safety-net.md @@ -0,0 +1,65 @@ +# Step 1: Emergency Backup (~1 hour, remote) + +**Must complete before touching anything.** HIPAA §164.308(a)(7) requires backup and disaster recovery for all PHI. No backup = non-compliance. Synology NAS and CS-SERVER both contain PHI. + +--- + +## 1.1 — Set up Synology Active Backup for Business + +See `session3-2026-03-07.md` Step 1 for detailed walkthrough. + +1. Log into Synology DSM at `https://192.168.0.120:5001` +2. Install "Active Backup for Business" from Package Center (free with Synology) +3. Install ABB agent on CS-SERVER via ScreenConnect (download from DSM → ABB → Physical Server → Add Device) +4. Create backup task: + - Source: CS-SERVER (192.168.2.254), entire machine (C: + D:) + - Destination: Synology Volume 1 (~540 GB free, expect ~300 GB after compression) + - Schedule: Nightly at 2:00 AM + - Retention: 7 daily + 4 weekly + - Compression + transfer encryption enabled +5. Run first backup manually +6. **Verify it completes successfully before proceeding** + +**Storage note:** ~592 GB raw data, but ABB compression typically achieves 40-60% reduction. Should fit with room for incrementals. **HIPAA critical:** PHI backup must succeed before any other migration work begins. + +## 1.2 — Export pfSense config + +1. Open pfSense web UI (192.168.0.1) +2. Diagnostics → Backup & Restore → Download configuration as XML +3. Save to `D:\Shares\IT\Backups\pfSense\` on CS-SERVER + +## 1.3 — Export configuration snapshots + +Run on CS-SERVER via ScreenConnect: + +```powershell +# Script: scripts/phase0-export-configs.ps1 +# See script for full commands +``` + +Exports: AD users/computers/groups, DNS records, NPS/RADIUS config, file share permissions (SMB + NTFS), GPO reports. + +All exports saved to `D:\Shares\IT\Backups\`. + +## 1.4 — Quick remote health checks + +Run `scripts/phase0-remote-checks.ps1` on CS-SERVER via ScreenConnect. Checks: +- Disk SMART health via Dell OpenManage +- Unknown listening ports (5504, 6783, 8019) +- IIS website audit +- DNS forwarder verification +- General server health (memory, disk, uptime) + +## Checklist + +- [ ] Active Backup for Business installed and first backup completed +- [ ] pfSense XML backup saved +- [ ] AD export CSVs in `D:\Shares\IT\Backups\AD\` +- [ ] DNS export in `D:\Shares\IT\Backups\DNS\` +- [ ] NPS export in `D:\Shares\IT\Backups\NPS\` +- [ ] Permissions exports in `D:\Shares\IT\Backups\Permissions\` +- [ ] Remote health checks completed (disk, ports, IIS, DNS forwarder) + +## Rollback + +Step 1 is read-only/additive. Nothing to roll back. diff --git a/clients/cascades-tucson/docs/migration/phase1-network.md b/clients/cascades-tucson/docs/migration/phase1-network.md new file mode 100644 index 0000000..4d1ce51 --- /dev/null +++ b/clients/cascades-tucson/docs/migration/phase1-network.md @@ -0,0 +1,180 @@ +# Phase 1: Network Migration — Move All Devices to INTERNAL VLAN 20 + +**Goal:** Consolidate all staff PCs and printers onto INTERNAL VLAN 20 (10.0.20.x / CSCNet WiFi). During migration, old permissive rules keep both networks talking. After migration, lock down with scoped rules. + +--- + +## Current State (as of 2026-03-09) + +- Staff PCs: mix of CSCNet WiFi (INTERNAL, 10.0.20.x) and CSC ENT / wired (LAN, 192.168.x.x) +- Printers: all wired on LAN (192.168.x.x) except accounting assistant (10.0.20.220) and 206 nurse station (10.0.20.69) +- CS-SERVER: 192.168.2.254 (LAN) — stays on LAN +- Synology: 192.168.0.120 (LAN) — stays on LAN +- Old permissive rules (INTERNAL→LAN pass-all, floating rule #4) allow all traffic between networks + +## pfSense Aliases (created 2026-03-09) + +| Alias | Type | Members | Status | +|-------|------|---------|--------| +| `Server_IPs` | Host(s) | 192.168.2.254 | Created | +| `NAS_IP` | Host(s) | 192.168.0.120 | Created | + +Built-in `_private4_` alias (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) will be used instead of custom RFC1918. + +--- + +## ~~Phase 1.1 — Guest VLAN~~ DONE 2026-03-06 + +All completed: +- [x] pfSense: VLAN 50, GUEST interface (10.0.50.1/24), DHCP scope (10.0.50.50–239) +- [x] pfSense: 4 firewall rules (block LAN, block 10.x, block 172.x, pass internet) +- [x] UniFi: Guest network created (VLAN 50, third-party gateway) +- [x] UniFi: Guest SSID reassigned from Default to Guest network +- [ ] **Onsite test needed:** Verify guest gets 10.0.50.x IP, internet works, cannot reach 192.168.x.x or 10.0.20.x + +--- + +## ~~Phase 1.2 — DNS forwarding~~ DONE 2026-03-06 + +- [x] pfSense domain overrides: `cascades.local` → 192.168.2.254, `_msdcs.cascades.local` → 192.168.2.254 +- [x] CS-SERVER DNS client: fixed to 127.0.0.1, 192.168.0.1 +- [x] Stale DNS records removed, correct records added +- [x] Reverse lookup zones created (5 zones) +- [x] DNS scavenging enabled (7-day) +- [x] CS-SERVER DNS forwarder confirmed as 192.168.0.1 + +--- + +## ~~Phase 1.3 — Quick fixes~~ MOSTLY DONE + +- [x] Room 218 DHCP range fixed — DONE 2026-03-07 +- [x] Room 130 stale rule deleted — DONE 2026-03-07 +- [x] CS-SERVER timezone fixed — DONE 2026-03-07 +- [ ] UniFi: Delete unused VLAN 10 ("CSC Internal Network") + +--- + +## Phase 1.4 — Migrate Staff PCs to CSCNet (INTERNAL VLAN 20) + +**Do first** — PCs are easy to move (just connect to CSCNet WiFi). No downtime, no re-IPing needed. + +### PCs Currently on LAN (need to move to CSCNet WiFi) + +| PC | Current IP | User(s) | Priority | Notes | +|----|-----------|---------|----------|-------| +| RECEPTIONIST-PC | 192.168.2.17 | CJ, Christina, Kyla, Tiffany | Medium | Front desk — high traffic | +| RECEPTIONIST-PC (2nd) | 192.168.3.187 | Receptionist | Low | Determine if still in use | +| ASSISTMAN-PC | 192.168.2.38 | Assistant Manager | Low | | +| ASSISTNURSE-PC | 192.168.2.153 | Assist Nurse | Low | | +| NURSESTATION-PC | 192.168.3.135 | Nurse Station | Low | | +| MEMRECEPT-PC | 192.168.3.41 | MemCare Reception | Low | | +| ANN-PC | 192.168.3.252 | Ann | Low | | +| MDIRECTOR-PC | 192.168.3.20 | Shelby Trozzi | Low | Win10 Home — needs Pro upgrade first for domain join | +| DESKTOP-LPOPV30 | 192.168.2.250 | Unknown | Low | | +| DESKTOP-U2DHAP0 | 192.168.3.37 | Unknown | Low | | +| DESKTOP-TRCIEJA | 192.168.3.93 | Unknown | Low | | +| DESKTOP-DLTAGOI | 192.168.3.133 | Unknown | Low | | +| DESKTOP-ROK7VNM | 192.168.3.148 | Unknown | Low | | +| DESKTOP-MD6UQI3 | 192.168.3.208 | Unknown | Low | | + +### PCs Already on INTERNAL (no action needed) + +| PC | IP | User(s) | +|----|-----|---------| +| CRYSTAL-PC | 10.0.20.205 | Crystal Rodriguez | +| ACCT2-PC | 10.0.20.209 | Accounting | +| CHEF-PC | 10.0.20.232 | Chef/Kitchen | +| DESKTOP-H6QHRR7 | 10.0.20.235 | Unknown | +| DESKTOP-KQSL232 | 10.0.20.227 | Unknown | +| DESKTOP-VAVKCIM | 10.0.20.239 | Unknown | + +### Process for each PC (WiFi move) +1. Connect PC to **CSCNet** WiFi (if not already) +2. Forget/remove **CSC ENT** WiFi profile +3. Verify PC gets 10.0.20.x IP +4. Verify can reach CS-SERVER (`ping 192.168.2.254`) +5. Verify can reach printers (still on LAN — works due to permissive rules) +6. Verify internet works + +--- + +## Phase 1.5 — Migrate Printers to INTERNAL VLAN 20 + +**Do after PCs** — requires changing UniFi switch port VLAN, printers get new IPs, must update printer config on all PCs. + +### Printer Migration Order (least impact first) + +| Order | Printer | Current IP | Switch Port | Users | Impact | +|-------|---------|-----------|-------------|-------|--------| +| 1 | Chef Brother | 192.168.3.88 | TBD | Chef | 1 user | +| 2 | Kitchen Manager Canon | 192.168.3.232 | TBD | Alyssa | 1 user | +| 3 | Meredith's Canon | 192.168.2.67 | TBD | Meredith | 1 user | +| 4 | MemCare Director Canon | 192.168.3.52 | TBD | Shelby | 1 user | +| 5 | MemCare MedTech Brother | 192.168.2.53 | TBD | MemCare MedTechs | Low | +| 6 | Room 103 Brother | 192.168.2.145 | TBD | Ashley, Christina | 2 users | +| 7 | Room 132 Canon | 192.168.3.211 | TBD | Sharon, Susan | 2 users | +| 8 | Room 217 Sales Brother | 192.168.3.44 | TBD | Sales team | ~4 users | +| 9 | Room 206 Bizhub | 192.168.1.138 | TBD | Health Services | Medium | +| 10 | Accounting Canon | 192.168.3.227 | TBD | Lauren | Accounting — careful | +| 11 | Front Desk Epson | 192.168.2.147 | TBD | 4 users | High traffic | +| 12 | Copy Room Canon | 192.168.2.230 | 1st Floor USW Port 45 | Everyone | **LAST — highest impact** | + +### Already on INTERNAL (no action needed) + +| Printer | IP | Notes | +|---------|-----|-------| +| Accounting Assistant Brother | 10.0.20.220 | Already on INTERNAL | +| 206 Nurse Station Brother | 10.0.20.69 | Fax only, already on INTERNAL | + +### MemCare Reception Epson — needs hardwire first, then assign to VLAN 20 + +### Process for each printer +1. Identify switch port in UniFi +2. Change port VLAN/network to INTERNAL (VLAN 20) +3. Printer gets new 10.0.20.x IP via DHCP +4. Create DHCP reservation on pfSense for new IP +5. Update printer IP on all user PCs that print to it +6. Test print from each user + +--- + +## Phase 1.6 — Lock Down (AFTER all devices migrated) + +Only do this after all PCs and printers are on INTERNAL VLAN 20. + +### Replace INTERNAL rules + +Delete old "INTERNAL to LAN PASS" rule. Replace with: + +| # | Action | Proto | Source | Dest | Ports | Description | +|---|--------|-------|--------|------|-------|-------------| +| 1 | PASS | TCP/UDP | INTERNAL net | Server_IPs | 53,88,135,389,445,464,636,3268,3269,5985,9389 | AD/DNS/SMB to DC | +| 2 | PASS | TCP | INTERNAL net | Server_IPs | 3389 | RDP to server | +| 3 | PASS | TCP | INTERNAL net | NAS_IP | 445,5000,5001 | Synology access | +| 4 | PASS | ICMP | INTERNAL net | LAN net | any | Ping diagnostics | +| 5 | BLOCK | IPv4 | INTERNAL net | _private4_ | any | Block other private (LOG) | +| 6 | PASS | IPv4 | INTERNAL net | any | any | Internet access | + +### Disable floating rule #4 + +Replace with scoped room rule: +- PASS | ResidentsGroup | IPv4 | any → ! _private4_ | "Rooms internet only" + +**DISABLE only — don't delete. Rollback: re-enable.** + +### Delete LAN rule #1 + +Remove "INTERNAL net to LAN net via WAN_Group" — no longer needed. + +### Retire CSC ENT SSID + +After all devices confirmed on CSCNet, remove CSC ENT SSID from UniFi. + +--- + +## Rollback + +1. Re-enable floating rule #4 +2. Re-enable old INTERNAL→LAN pass rule +3. Reconnect devices to CSC ENT if needed +4. Restore pfSense XML backup (Diagnostics → Backup & Restore) diff --git a/clients/cascades-tucson/docs/migration/phase2-server-prep.md b/clients/cascades-tucson/docs/migration/phase2-server-prep.md new file mode 100644 index 0000000..ad1a705 --- /dev/null +++ b/clients/cascades-tucson/docs/migration/phase2-server-prep.md @@ -0,0 +1,266 @@ +# Step 4: Server Preparation — AD & Shares (~4-6 hours, remote via ScreenConnect) + +**No user impact — server-side work only.** + +--- + +## 4a — DNS cleanup + +Run `scripts/phase2-dns-cleanup.ps1` on CS-SERVER. + +Actions: +- Remove stale A records pointing to old IPs (192.168.0.5, 192.168.2.59, etc.) +- Fix DomainDnsZones/ForestDnsZones records to point to 192.168.2.254 +- Enable DNS scavenging (7-day interval) +- Enable aging on cascades.local zone +- Create reverse lookup zones for 192.168.0.0/22 and 10.0.20.0/24 + +**Verify after:** `nslookup cs-server.cascades.local` should return only 192.168.2.254. + +--- + +## 4b — AD cleanup + +Run `scripts/phase2-ad-setup.ps1` on CS-SERVER. + +### Security fixes (immediate) +- Remove disabled Monica.Ramirez from **Domain Admins** group +- Disable Haris.Durut (still enabled, no longer employed) +- Rename "Quickboosk acccess" group → "QuickBooks Access" +- Add lauren.hasselman to QuickBooks Access (replaced Jeff Bristol) + +### OU cleanup +- Fix misspelled OU: "Managment" → "Management" +- Create OU structure: `Workstations\Staff PCs` + +### Security groups (created and populated with members) + +| Group | Members | +|-------|---------| +| SG-Management-RW | Meredith.Kuhn, Ashley.Jensen, Megan.Hiatt, Crystal.Rodriguez, Tamra.Matthews, britney.thompson, Veronica.Feller, strozzi, Alyssa.Brooks, lauren.hasselman | +| SG-Sales-RW | Megan.Hiatt, Crystal.Rodriguez, Tamra.Matthews | +| SG-Server-RW | Ashley.Jensen, britney.thompson, Christina.DuPras, Veronica.Feller, Meredith.Kuhn | +| SG-Chat-RW | Ashley.Jensen, britney.thompson, Veronica.Feller | +| SG-Culinary-RW | JD.Martin, Ramon.Castaneda, Alyssa.Brooks | +| SG-IT-RW | howard, sysadmin | +| SG-Receptionist-RW | Cathy.Kingston, Shontiel.Nunn, Ray.Rai, Sebastian.Leon, Michelle.Shestko | +| SG-Directory-RW | Cathy.Kingston, Shontiel.Nunn, Christina.DuPras | +| SG-AllShares-RO | (populated as needed) | + +### Account removals (client confirmed) + +**Already disabled — delete:** +- Anna.Pitzlin, Nela.Durut-Azizi, Jodi.Ramstack, Monica.Ramirez, jeff.bristol + +**Still enabled — disable + delete (not in HR list, former employees):** +- Haris.Durut, Nuria.Diaz, Cathy.Reece, Kelly.Wallace, alyssa.brooks, Isabella.Islas, ann.dery + +**Keep:** lauren.hasselman (took over Bristol's role as Business Office Director) + +### Move computers to Workstations OU +- CRYSTAL-PC, ACCT2-PC, DESKTOP-H6QHRR7, DESKTOP-1ISF081 + +--- + +## 4c — Sync data from Synology NAS — DONE 2026-03-07 + +~~Run `scripts/phase2-sync-synology.ps1` on CS-SERVER.~~ + +**Synology Drive Client** reinstalled on CS-SERVER and configured for live sync to `D:\Shares\Main`. All Synology shares sync continuously to this location. + +**Sync method:** Synology Drive Client (live, ongoing sync) +**Sync destination:** `D:\Shares\Main` on CS-SERVER +**Direction:** Synology → CS-SERVER (one-way) + +### Synology shares visible via SMB (2026-03-07 audit) + +| Synology Share | Size | Syncing to `D:\Shares\Main` | +|---|---|---| +| `homes` | 228.14 GB | Yes | +| `Public` | 50.15 GB | Yes | +| `SalesDept` | 12.61 GB | Yes | +| `Server` | 1.84 GB | Yes | +| `Management` | 1.4 GB | Yes | +| `chat` | 0 GB (empty) | Yes | +| `home` | 0 GB (empty) | Yes | + +**Total:** ~294 GB syncing. D: had 635 GB free — sufficient. + +### Shares NOT visible via SMB + +| Synology Share | Notes | +|---|---| +| `Activities` | Not shared via SMB — may be internal Synology folder or removed | +| `pacs` | Not shared via SMB — verify on Synology DSM | +| `Sandra Fish` | Not shared via SMB — verify on Synology DSM | +| `web` | Not shared via SMB — verify on Synology DSM | + +**Note:** Synology uses local accounts (not AD-joined). Authenticated from CS-SERVER via `net use \\192.168.0.120 /user:admin`. + +Already on CS-SERVER (no sync needed): Culinary, IT, Receptionist, directoryshare + +`SaleShare` is the old/duplicate folder — ignore it. `SalesDept` is the real one. + +--- + +## 4d — Set up file share permissions (HIPAA: access control + audit) + +Run `scripts/phase2-file-shares.ps1` on CS-SERVER (AFTER sync completes). + +Creates SMB shares for synced folders and sets NTFS permissions matching Synology access. **HIPAA §164.312(b):** After shares are created, enable Advanced Audit Logging on all PHI-containing shares (Management, Server, homes) to track read/write/delete operations. + +| Share | NTFS Access | SMB Share | +|---|---|---| +| Management | SG-Management-RW = Modify | `\\CS-SERVER\Management` | +| SalesDept | SG-Sales-RW = Modify | `\\CS-SERVER\SalesDept` | +| Server | SG-Server-RW = Modify | `\\CS-SERVER\Server` | +| chat | SG-Chat-RW = Modify | `\\CS-SERVER\chat` | +| Public | Authenticated Users = Modify | `\\CS-SERVER\Public` | +| Culinary | SG-Culinary-RW = Modify | `\\CS-SERVER\Culinary` (exists) | +| IT | SG-IT-RW = Modify | `\\CS-SERVER\IT` (exists) | +| Receptionist | SG-Receptionist-RW = Modify | `\\CS-SERVER\Receptionist` (exists) | +| directoryshare | SG-Directory-RW = Modify | `\\CS-SERVER\directoryshare` (exists) | +| homes | CREATOR OWNER = Full (subfolder) | `\\CS-SERVER\homes` (ABE enabled) | + +All shares also get: Domain Admins = Full Control, SYSTEM = Full Control. + +--- + +## 4e — Set up print server + +Run `scripts/phase2-print-server.ps1` on CS-SERVER. + +Creates TCP/IP printer ports for each printer. Drivers must be downloaded separately from manufacturer websites and installed manually, then the script can create the shared printers. + +| Printer | IP | Driver Source | +|---------|-----|---------------| +| Front Desk - Epson ET-5800 | 192.168.2.147 | epson.com | +| Business Office - Canon MF455DW | 192.168.3.227 | canon.com | +| Marketing - Brother MFC-L8900CDW | 192.168.2.21 | brother.com | +| 206 Health - Brother MFC-L8900CDW | 192.168.1.138 | brother.com | +| MemCare MedTech - Brother MFC-L8900CDW | 192.168.2.53 | brother.com | +| MemCare Director - Canon MF451CDW | 192.168.3.52 | canon.com | +| Kitchen Printer | 192.168.0.121 | _TBD_ | + +**Test:** Print a test page from CS-SERVER to each shared printer before deploying via GPO. + +--- + +## 4f — Create GPOs (DO NOT link yet) + +### GPO 1: "CSC - Drive Mappings" +User Configuration → Preferences → Drive Maps + +| Drive | Path | Targeting | +|-------|------|-----------| +| S: | `\\CS-SERVER\Shares` | All domain users | +| M: | `\\CS-SERVER\Management` | SG-Management-RW members | +| T: | `\\CS-SERVER\SalesDept` | SG-Sales-RW members | +| K: | `\\CS-SERVER\Culinary` | SG-Culinary-RW members | +| I: | `\\CS-SERVER\IT` | SG-IT-RW members | +| R: | `\\CS-SERVER\Receptionist` | SG-Receptionist-RW members | +| P: | `\\CS-SERVER\Public` | All domain users | + +Use Item-Level Targeting → Security Group for department-specific drives. + +### GPO 2: "CSC - Printer Deployment" +Computer Configuration → Preferences → Printers + +Deploy shared printers with Item-Level Targeting by computer name or security group: +- `\\CS-SERVER\FrontDesk-Epson` → Front desk PCs +- `\\CS-SERVER\BizOffice-Canon` → Accounting PCs +- `\\CS-SERVER\Marketing-Brother` → Marketing group +- (etc.) + +### GPO 3: "CSC - Security Baseline" +Computer Configuration → Security Settings + +| Setting | Value | +|---------|-------| +| Min password length | 12 characters | +| Password complexity | Enabled | +| Max password age | 90 days | +| Password history | 10 | +| Account lockout threshold | 5 attempts | +| Account lockout duration | 30 minutes | +| Lockout counter reset | 30 minutes | +| Screen lock timeout | 15 minutes | +| Windows Firewall | All profiles ON | + +### GPO 4: "CSC - Windows Update" +Computer Configuration → Admin Templates → Windows Update + +- Auto download + scheduled install +- Schedule: Sundays at 3:00 AM +- No auto-restart with logged-on users + +### GPO 6: "CSC - Shared Workstation" +Linked to: `OU=Shared PCs,OU=Workstations,DC=cascades,DC=local` + +Applies only to machines in the Shared PCs OU. Uses Item-Level Targeting by computer name so different shared PCs get different printers/drives/mailboxes. + +**User Configuration → Preferences → Drive Maps** + +| Drive | Path | ILT | +|-------|------|-----| +| R: | `\\CS-SERVER\Receptionist` | Computer Name = [TBD — front desk PC] | + +**User Configuration → Preferences → Printers** + +| Printer | Set Default | ILT | +|---------|-------------|-----| +| `\\CS-SERVER\FrontDesk-Epson` | Yes | Computer Name = [TBD — front desk PC] | + +**User Configuration → Admin Templates → Microsoft Outlook 2016 → Account Settings → Exchange** + +| Setting | Value | +|---------|-------| +| Cached Exchange Mode | Disabled (forces Online Mode) | + +Online mode prevents shared mailbox data from caching on every shared PC — important when multiple users rotate through the same machine. + +**User Configuration → Preferences → Registry** + +Auto-mount shared mailbox via registry (only on targeted shared PCs): + +| Key Path | Value | Data | ILT | +|----------|-------|------|-----| +| `HKCU\Software\Microsoft\Office\16.0\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676\00000002` | `001f6610` | `reception@[tenant-domain]` | Computer Name = [TBD] | + +> **Note:** The registry path above is a placeholder — the exact profile key varies per installation. The shared mailbox SMTP address depends on the M365 tenant domain (Step 3 blocker). Computer names will be filled in after the onsite visit identifies which PCs are shared. + +**Onsite TODO (before this GPO is functional):** +- [ ] Identify shared workstation computer names and roles +- [ ] Move shared PCs into `OU=Shared PCs,OU=Workstations` +- [ ] Fill in ILT computer names above +- [ ] Create shared mailbox in M365 (see Step 4 in shared workstation plan) +- [ ] Document user-to-shared-PC rotation matrix +- [ ] Test: log into shared PC → verify R: drive, default printer, and shared mailbox appear +- [ ] Test: log into normal PC → verify reception drive/printer/mailbox do NOT appear + +--- + +### GPO 5: "CSC - Folder Redirection" +User Configuration → Policies → Windows Settings → Folder Redirection + +| Folder | Redirect to | Setting | +|--------|------------|---------| +| Desktop | `\\CS-SERVER\homes\%username%\Desktop` | Basic, create folder | +| Documents | `\\CS-SERVER\homes\%username%\Documents` | Basic, create folder | +| Downloads | `\\CS-SERVER\homes\%username%\Downloads` | Basic, create folder | + +Settings: +- Grant user exclusive rights: **Yes** +- Move contents to new location: **Yes** (first time only) +- Policy removal: Leave contents +- Also applies to Windows 10/11 + +This replaces roaming profiles — user data lives on the server but profiles stay local (fast logon, no corruption issues). + +--- + +## Rollback + +- Unlink any GPO from GPMC — takes effect at next `gpupdate` +- DNS records were exported in Step 1 +- AD changes can be reverted from Step 1 exports diff --git a/clients/cascades-tucson/docs/migration/phase3-domain-join.md b/clients/cascades-tucson/docs/migration/phase3-domain-join.md new file mode 100644 index 0000000..1db3f3a --- /dev/null +++ b/clients/cascades-tucson/docs/migration/phase3-domain-join.md @@ -0,0 +1,93 @@ +# Step 5: Domain Join (~1-2 hours per machine, remote via ScreenConnect) + +--- + +## 5a — Pre-join verification (once, from any INTERNAL machine) + +Run `scripts/phase3-pre-join-verify.ps1` or manually: + +```cmd +nslookup cs-server.cascades.local +nslookup _ldap._tcp.cascades.local +ping 192.168.2.254 +net view \\192.168.2.254 +``` + +**All must succeed.** If they don't, fix DNS/firewall (Step 2) before proceeding. + +--- + +## 5b — Join non-domain machines (one at a time) + +**Order:** DESKTOP-KQSL232 → CHEF-PC → SALES4-PC → MDIRECTOR-PC (least critical first) + +### Pre-requisites per machine + +| Machine | Blocker | Action Needed | +|---------|---------|---------------| +| DESKTOP-KQSL232 | None known | Verify OS edition supports domain join | +| CHEF-PC | None known | Verify OS edition supports domain join | +| SALES4-PC | **Not on network** (absent from ARP/DHCP as of 2026-03-06) | Locate machine, verify powered on | +| MDIRECTOR-PC | **Windows 10 Home** — cannot domain-join | Upgrade to Pro first (key available). Users: Anna Pitzlin, Shelby Trozzi, localadmin. No Desktop/Documents data to migrate. MAC: 98:ee:cb:9d:8a:81 | + +For each machine, run `scripts/phase3-join-domain.ps1` via ScreenConnect: + +### Per machine: + +1. **Document current state** (automated by script) + - systeminfo, ipconfig, printers, mapped drives saved to `C:\IT-Migration\` + +2. **Create local admin backup account** + - Localadmin local admin account (for rollback access) + +3. **Verify DNS resolves AD** + - Must resolve `cs-server.cascades.local` + - If fails: check DHCP DNS settings (should get 192.168.0.1 via DHCP) + +4. **Join domain** + - `Add-Computer -DomainName cascades.local -OUPath "OU=Staff PCs,OU=Workstations,DC=cascades,DC=local"` + - Automatic reboot + +5. **Post-reboot verification** (run `scripts/phase3-post-join-verify.ps1`): + - [ ] `gpresult /r` — GPOs applied? + - [ ] `\\CS-SERVER\Shares` accessible? + - [ ] Mapped drives appear (S:, department drive)? + - [ ] Printers auto-installed? + - [ ] Print test page works? + - [ ] Internet works? + - [ ] `nltest /dsgetdc:cascades.local` returns CS-SERVER? + +6. **Migrate user profile if needed** + - Copy local Desktop/Documents to network share + - Or use ForensiT User Profile Wizard (free) to migrate local → domain profile + +--- + +## 5c — Link GPOs (after first successful join) + +1. Link "CSC - Security Baseline" → domain root +2. Link "CSC - Drive Mappings" → user OUs (Departments, Management, Sales, MemCare) +3. Link "CSC - Printer Deployment" → OU=Workstations +4. Link "CSC - Windows Update" → domain root +5. Link "CSC - Folder Redirection" → user OUs + +Run `gpupdate /force` on first machine, verify everything works. + +--- + +## 5d — Update existing domain machines + +Run on CRYSTAL-PC, ACCT2-PC, DESKTOP-H6QHRR7, DESKTOP-1ISF081: + +```powershell +gpupdate /force +# Verify drive mappings and printers appeared +``` + +--- + +## Rollback (per machine) + +1. Log in with `Localadmin` local account +2. Run: `Remove-Computer -UnjoinDomainCredential (Get-Credential) -Restart` +3. Machine returns to workgroup mode with local accounts intact diff --git a/clients/cascades-tucson/docs/migration/phase4-synology.md b/clients/cascades-tucson/docs/migration/phase4-synology.md new file mode 100644 index 0000000..c94eb20 --- /dev/null +++ b/clients/cascades-tucson/docs/migration/phase4-synology.md @@ -0,0 +1,77 @@ +# Step 6: Synology Transition (~2 hours, remote) + +--- + +## 6.1 — Verify drive mappings + +On each machine via ScreenConnect: + +```powershell +net use +``` + +Confirm mapped drives (S:, department drives) point to `\\CS-SERVER\...` and files are accessible. + +**Do not proceed until all users have working drive mappings.** + +--- + +## 6.2 — Remove Synology Drive Client + +On each machine via ScreenConnect: + +1. Right-click Synology Drive Client in system tray → Quit +2. Settings → Apps → Synology Drive Client → Uninstall +3. Verify user can access files via mapped drives (not Synology) + +Or via PowerShell: +```powershell +# Find and uninstall Synology Drive Client +$synology = Get-WmiObject Win32_Product | Where-Object { $_.Name -like "*Synology Drive*" } +if ($synology) { + $synology.Uninstall() + Write-Host "Synology Drive Client uninstalled" +} else { + Write-Host "Synology Drive Client not found" +} +``` + +--- + +## 6.3 — Disable Synology Drive Server sync + +1. Log into Synology DSM at `https://192.168.0.120:5001` +2. Open Synology Drive Admin Console +3. Disable all sync tasks +4. Optionally disable Synology Drive Server package (don't uninstall yet) + +--- + +## 6.4 — Repurpose Synology as backup-only + +1. Verify Active Backup for Business is backing up CS-SERVER nightly (set up in Step 1) +2. Configure offsite backup: + - Install **Hyper Backup** from Package Center + - Create task → Backblaze B2 or Wasabi (~$3/mo for offsite copy) + - Schedule: daily after ABB completes (e.g., 5:00 AM) + - Retention: 30 daily + 12 monthly + +--- + +## 6.5 — Archive SynologyDrive folder + +Run `scripts/phase4-archive-synology.ps1` on CS-SERVER: + +```powershell +Rename-Item "D:\Shares\SynologyDrive" "D:\Shares\_SynologyDrive_ARCHIVE_DeleteAfter30Days" +``` + +**Do NOT delete immediately** — keep 30 days as safety net. Set a calendar reminder to delete after 30 days. + +--- + +## Rollback + +- Rename archive folder back: `Rename-Item "D:\Shares\_SynologyDrive_ARCHIVE_DeleteAfter30Days" "D:\Shares\SynologyDrive"` +- Re-enable Synology Drive Server sync +- Reinstall Synology Drive Client on workstations diff --git a/clients/cascades-tucson/docs/migration/phase5-hardening.md b/clients/cascades-tucson/docs/migration/phase5-hardening.md new file mode 100644 index 0000000..0a7891c --- /dev/null +++ b/clients/cascades-tucson/docs/migration/phase5-hardening.md @@ -0,0 +1,129 @@ +# Step 8: Hardening & Cleanup (~3-4 hours, remote) + +--- + +## 8.1 — Deploy endpoint security + +Via SyncroRMM, deploy to all endpoints: +- **SentinelOne** — EDR agent +- **Huntress** — threat detection + +Then uninstall Datto EDR from all machines. + +Verify on each machine: +```powershell +Get-Service SentinelAgent, HuntressAgent -ErrorAction SilentlyContinue | Select-Object Name, Status +``` + +--- + +## 8.2 — Delete Synology Sync VM + +On CS-SERVER: +1. Open Hyper-V Manager +2. Shut down the Synology Sync VM (if running) +3. Delete the VM and its virtual hard disks +4. Reclaim disk space + +```powershell +# Check for VMs +Get-VM | Select-Object Name, State, Path +# Stop and remove (adjust name as needed) +# Stop-VM -Name "SynologySync" -Force +# Remove-VM -Name "SynologySync" -Force +# Then manually delete the VHD files from the VM path +``` + +--- + +## 8.3 — Address shared accounts + +Replace these shared/generic AD accounts with individual user accounts: +- **Culinary** → JD.Martin, Ramon.Castaneda, Alyssa.Brooks already in SG-Culinary-RW +- **Receptionist** → Cathy.Kingston, Shontiel.Nunn, Ray.Rai, Sebastian.Leon, Michelle.Shestko already in SG-Receptionist-RW +- **saleshare** → Megan.Hiatt, Crystal.Rodriguez, Tamra.Matthews already in SG-Sales-RW +- **directoryshare** → Cathy.Kingston, Shontiel.Nunn, Christina.DuPras already in SG-Directory-RW + +Steps: +1. Verify individual users can access their shares via security group membership +2. Have users log in with their own accounts, confirm access works +3. Disable shared accounts (don't delete immediately) +4. Delete shared accounts after 30 days + +### Clean up old shares/folders + +- **D:\Roaming** — old roaming profiles attempt, never completed. Remove SMB share and delete folder. +- **D:\Shares\SaleShare** — old/duplicate sales folder. Verify no unique data, then remove SMB share. + +```powershell +# Remove old Roaming share +Remove-SmbShare -Name "Roaming" -Force +# Remove old SaleShare (verify empty/duplicate first) +# Remove-SmbShare -Name "SaleShare" -Force +``` + +--- + +## 8.4 — RDS decision + +Check if anyone uses Remote Desktop Services: +```powershell +# Check active RDS sessions +quser /server:CS-SERVER + +# Check RDS configuration +Get-RDServer -ErrorAction SilentlyContinue +Get-RDSessionCollection -ErrorAction SilentlyContinue +``` + +**If RDS is used:** Purchase proper CALs (grace period expired ~17 months ago). +**If RDS is not used:** Remove the RDS role: +```powershell +Remove-WindowsFeature -Name RDS-RD-Server -Restart +``` + +--- + +## 8.5 — Update documentation + +Update Cascades knowledge base at `C:\Users\howar\Clients\Cascades\`: +- [ ] `network/firewall.md` — new rules, Guest VLAN, aliases +- [ ] `network/vlans.md` — add VLAN 50 (Guest) +- [ ] `network/dns.md` — cleaned records, scavenging, reverse zones +- [ ] `network/wifi.md` — Guest SSID on VLAN 50 +- [ ] `network/dhcp.md` — reservations +- [ ] `servers/active-directory.md` — new OUs, security groups, GPOs, all PCs joined +- [ ] `servers/cs-server.md` — print server role, GPOs +- [ ] `security/backup.md` — Synology ABB + offsite +- [ ] `security/antivirus.md` — SentinelOne + Huntress deployed +- [ ] `printers.md` — centralized print server, share names +- [ ] `issues/log.md` — close resolved issues + +--- + +## 8.6 — Re-ingest knowledge base + +```cmd +cd C:\Users\howar\Clients\MSP-AI +msp-ingest.bat Cascades --clear +``` + +--- + +## 8.7 — Close resolved issues + +Update `issues/log.md` to mark these as resolved: +- Floating rule #4 +- Guest WiFi on server LAN +- No GPOs +- 4 PCs not domain-joined +- No backup +- Shared accounts +- Stale DNS records +- Room 218 DHCP +- Timezone mismatch +- RDS licensing +- Room 130 dead rule +- VLAN 10 mismatch +- Stale disabled AD accounts +- Synology Sync VM diff --git a/clients/cascades-tucson/docs/migration/scripts/debloat-business-pcs.ps1 b/clients/cascades-tucson/docs/migration/scripts/debloat-business-pcs.ps1 new file mode 100644 index 0000000..56a7aee --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/debloat-business-pcs.ps1 @@ -0,0 +1,129 @@ +# Win11Debloat — Business PC App Removal Script +# For Cascades senior living staff machines +# Removes games, social media, entertainment, and other non-business apps +# Keeps: Calculator, Notepad, Snipping Tool, Photos, Paint, Camera, Store, +# Sticky Notes, Alarms, OneDrive, Outlook, Teams, Office Hub, +# Windows Terminal, Remote Desktop, Quick Assist +# +# Usage (run as Administrator in PowerShell): +# +# Option 1 — One-liner (downloads and runs automatically): +# & ([scriptblock]::Create((irm "https://debloat.raphi.re/"))) -RemoveApps -Apps "Microsoft.GamingApp, Microsoft.XboxApp, Microsoft.XboxGameOverlay, Microsoft.XboxGamingOverlay, Microsoft.XboxSpeechToTextOverlay, Microsoft.Xbox.TCUI, Microsoft.MicrosoftSolitaireCollection, king.com.CandyCrushSaga, king.com.CandyCrushSodaSaga, king.com.BubbleWitch3Saga, Asphalt8Airborne, CaesarsSlotsFreeCasino, COOKINGFEVER, DisneyMagicKingdoms, FarmVille2CountryEscape, HiddenCity, MarchofEmpires, RoyalRevolt, Facebook, Instagram, TikTok, Twitter, LinkedInforWindows, XING, Viber, Netflix, Spotify, Disney, HULULLC.HULUPLUS, AmazonVideo.PrimeVideo, SlingTV, PandoraMediaInc, iHeartRadio, TuneInRadio, Plex, Shazam, Flipboard, Microsoft.BingNews, Microsoft.News, Microsoft.BingWeather, Microsoft.BingSports, Microsoft.BingFinance, Microsoft.BingFoodAndDrink, Microsoft.BingHealthAndFitness, Microsoft.BingTranslator, Microsoft.BingTravel, Clipchamp.Clipchamp, Microsoft.Copilot, Microsoft.Windows.AIHub, Microsoft.549981C3F5F10, Microsoft.MicrosoftJournal, Microsoft.Whiteboard, Microsoft.Office.Sway, Microsoft.PowerAutomateDesktop, Microsoft.MicrosoftPowerBIForWindows, Microsoft.StartExperiencesApp, Microsoft.3DBuilder, Microsoft.Microsoft3DViewer, Microsoft.MSPaint, Microsoft.Print3D, Microsoft.MixedReality.Portal, Amazon.com.Amazon, Microsoft.WindowsFeedbackHub, Microsoft.Getstarted, Microsoft.M365Companions, Microsoft.YourPhone, Microsoft.BingSearch, Microsoft.ZuneVideo, Microsoft.SkypeApp, Microsoft.People, Microsoft.Messaging, Microsoft.OneConnect, Microsoft.NetworkSpeedTest, AutodeskSketchBook, DrawboardPDF, Duolingo-LearnLanguagesforFree, NYTCrossword, OneCalendar, PhototasticCollage, PicsArt-PhotoStudio, PolarrPhotoEditorAcademicEdition, WinZipUniversal, Wunderlist, AdobeSystemsIncorporated.AdobePhotoshopExpress, ActiproSoftwareLLC, CyberLinkMediaSuiteEssentials, fitbit, Sidia.LiveWallpaper" +# +# Option 2 — From downloaded copy (if Win11Debloat is already extracted): +# cd C:\path\to\Win11Debloat +# Set-ExecutionPolicy Unrestricted -Scope Process -Force +# .\Win11Debloat.ps1 -RemoveApps -Apps "" +# +# Apps not present on the machine are silently skipped. +# Source: https://github.com/Raphire/Win11Debloat/wiki/App-Removal + +# --- App list (one per line for readability) --- +$apps = @( + # Gaming / Xbox + "Microsoft.GamingApp" + "Microsoft.XboxApp" + "Microsoft.XboxGameOverlay" + "Microsoft.XboxGamingOverlay" + "Microsoft.XboxSpeechToTextOverlay" + "Microsoft.Xbox.TCUI" + "Microsoft.MicrosoftSolitaireCollection" + + # Bloatware games + "king.com.CandyCrushSaga" + "king.com.CandyCrushSodaSaga" + "king.com.BubbleWitch3Saga" + "Asphalt8Airborne" + "CaesarsSlotsFreeCasino" + "COOKINGFEVER" + "DisneyMagicKingdoms" + "FarmVille2CountryEscape" + "HiddenCity" + "MarchofEmpires" + "RoyalRevolt" + + # Social media + "Facebook" + "Instagram" + "TikTok" + "Twitter" + "LinkedInforWindows" + "XING" + "Viber" + + # Entertainment / streaming + "Netflix" + "Spotify" + "Disney" + "HULULLC.HULUPLUS" + "AmazonVideo.PrimeVideo" + "SlingTV" + "PandoraMediaInc" + "iHeartRadio" + "TuneInRadio" + "Plex" + "Shazam" + "Flipboard" + + # Bing / News / Weather + "Microsoft.BingNews" + "Microsoft.News" + "Microsoft.BingWeather" + "Microsoft.BingSports" + "Microsoft.BingFinance" + "Microsoft.BingFoodAndDrink" + "Microsoft.BingHealthAndFitness" + "Microsoft.BingTranslator" + "Microsoft.BingTravel" + + # Unused Microsoft apps + "Clipchamp.Clipchamp" + "Microsoft.Copilot" + "Microsoft.Windows.AIHub" + "Microsoft.549981C3F5F10" # Cortana + "Microsoft.MicrosoftJournal" + "Microsoft.Whiteboard" + "Microsoft.Office.Sway" + "Microsoft.PowerAutomateDesktop" + "Microsoft.MicrosoftPowerBIForWindows" + "Microsoft.StartExperiencesApp" # Widgets + "Microsoft.WindowsFeedbackHub" + "Microsoft.Getstarted" # Get Started / Tips + "Microsoft.M365Companions" + "Microsoft.YourPhone" # Phone Link + "Microsoft.BingSearch" + "Microsoft.ZuneVideo" # Movies & TV + "Microsoft.SkypeApp" + "Microsoft.People" + "Microsoft.Messaging" + "Microsoft.OneConnect" + "Microsoft.NetworkSpeedTest" + + # 3D / Creative (not needed) + "Microsoft.3DBuilder" + "Microsoft.Microsoft3DViewer" + "Microsoft.MSPaint" # Paint 3D (not regular Paint) + "Microsoft.Print3D" + "Microsoft.MixedReality.Portal" + + # Third-party bloat + "Amazon.com.Amazon" + "AutodeskSketchBook" + "DrawboardPDF" + "Duolingo-LearnLanguagesforFree" + "NYTCrossword" + "OneCalendar" + "PhototasticCollage" + "PicsArt-PhotoStudio" + "PolarrPhotoEditorAcademicEdition" + "WinZipUniversal" + "Wunderlist" + "AdobeSystemsIncorporated.AdobePhotoshopExpress" + "ActiproSoftwareLLC" + "CyberLinkMediaSuiteEssentials" + "fitbit" + "Sidia.LiveWallpaper" +) -join ", " + +# --- Run via one-liner (download + execute) --- +& ([scriptblock]::Create((irm "https://debloat.raphi.re/"))) -RemoveApps -Apps $apps diff --git a/clients/cascades-tucson/docs/migration/scripts/phase0-export-configs.ps1 b/clients/cascades-tucson/docs/migration/scripts/phase0-export-configs.ps1 new file mode 100644 index 0000000..5679827 --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/phase0-export-configs.ps1 @@ -0,0 +1,161 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + Phase 0: Export all configuration snapshots from CS-SERVER before migration. +.DESCRIPTION + Exports AD users/computers/groups, DNS records, NPS config, and file share + permissions to D:\Shares\IT\Backups\. Run on CS-SERVER via ScreenConnect. +.NOTES + Run BEFORE making any changes. This is the safety net. +#> + +$BackupRoot = "D:\Shares\IT\Backups" +$Timestamp = Get-Date -Format "yyyy-MM-dd_HHmm" + +Write-Host "=== Phase 0: Configuration Export ===" -ForegroundColor Cyan +Write-Host "Timestamp: $Timestamp" +Write-Host "" + +# --- Create backup directories --- +$dirs = @("AD", "DNS", "NPS", "Permissions", "pfSense", "SynologyDrive-Audit") +foreach ($d in $dirs) { + New-Item -Path "$BackupRoot\$d" -ItemType Directory -Force | Out-Null +} +Write-Host "[OK] Backup directories created at $BackupRoot" -ForegroundColor Green + +# --- AD Export --- +Write-Host "`n--- Exporting Active Directory ---" -ForegroundColor Yellow + +try { + Import-Module ActiveDirectory -ErrorAction Stop + + Get-ADUser -Filter * -Properties * | + Export-Csv "$BackupRoot\AD\AD-Users_$Timestamp.csv" -NoTypeInformation + Write-Host "[OK] AD Users exported" -ForegroundColor Green + + Get-ADComputer -Filter * -Properties * | + Export-Csv "$BackupRoot\AD\AD-Computers_$Timestamp.csv" -NoTypeInformation + Write-Host "[OK] AD Computers exported" -ForegroundColor Green + + Get-ADGroup -Filter * | ForEach-Object { + $g = $_ + Get-ADGroupMember $g -ErrorAction SilentlyContinue | + Select-Object @{N='Group';E={$g.Name}}, Name, SamAccountName + } | Export-Csv "$BackupRoot\AD\AD-Groups_$Timestamp.csv" -NoTypeInformation + Write-Host "[OK] AD Groups exported" -ForegroundColor Green + + Get-ADOrganizationalUnit -Filter * -Properties * | + Export-Csv "$BackupRoot\AD\AD-OUs_$Timestamp.csv" -NoTypeInformation + Write-Host "[OK] AD OUs exported" -ForegroundColor Green +} +catch { + Write-Host "[ERROR] AD export failed: $_" -ForegroundColor Red +} + +# --- DNS Export --- +Write-Host "`n--- Exporting DNS ---" -ForegroundColor Yellow + +try { + Import-Module DnsServer -ErrorAction Stop + + Get-DnsServerResourceRecord -ZoneName "cascades.local" | + Export-Csv "$BackupRoot\DNS\DNS-Records_$Timestamp.csv" -NoTypeInformation + Write-Host "[OK] DNS records exported" -ForegroundColor Green + + Get-DnsServerZone | + Export-Csv "$BackupRoot\DNS\DNS-Zones_$Timestamp.csv" -NoTypeInformation + Write-Host "[OK] DNS zones exported" -ForegroundColor Green + + Get-DnsServerForwarder | + Export-Csv "$BackupRoot\DNS\DNS-Forwarders_$Timestamp.csv" -NoTypeInformation + Write-Host "[OK] DNS forwarders exported" -ForegroundColor Green +} +catch { + Write-Host "[ERROR] DNS export failed: $_" -ForegroundColor Red +} + +# --- NPS/RADIUS Export --- +Write-Host "`n--- Exporting NPS/RADIUS ---" -ForegroundColor Yellow + +try { + $npsFile = "$BackupRoot\NPS\nps-config_$Timestamp.xml" + $result = netsh nps export filename="$npsFile" exportPSK=YES 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Host "[OK] NPS config exported" -ForegroundColor Green + } else { + Write-Host "[WARN] NPS export returned: $result" -ForegroundColor Yellow + } +} +catch { + Write-Host "[WARN] NPS export failed (may not be installed): $_" -ForegroundColor Yellow +} + +# --- File Share Permissions Export --- +Write-Host "`n--- Exporting File Share Permissions ---" -ForegroundColor Yellow + +try { + $shares = Get-SmbShare | Where-Object { $_.Path -like "D:\*" } + foreach ($s in $shares) { + $shareName = $s.Name -replace '[\\/:*?"<>|]', '_' + + # NTFS permissions via icacls + icacls $s.Path /save "$BackupRoot\Permissions\${shareName}-icacls_$Timestamp.txt" /T /C 2>$null + Write-Host " [OK] NTFS permissions for $($s.Name)" -ForegroundColor Green + + # SMB share-level permissions + Get-SmbShareAccess -Name $s.Name | + Export-Csv "$BackupRoot\Permissions\${shareName}-SMB_$Timestamp.csv" -NoTypeInformation + Write-Host " [OK] SMB permissions for $($s.Name)" -ForegroundColor Green + } + + # Also export the share list itself + Get-SmbShare | Select-Object Name, Path, Description, CurrentUsers | + Export-Csv "$BackupRoot\Permissions\AllShares_$Timestamp.csv" -NoTypeInformation + Write-Host "[OK] Share list exported" -ForegroundColor Green +} +catch { + Write-Host "[ERROR] Share permissions export failed: $_" -ForegroundColor Red +} + +# --- SynologyDrive Audit --- +Write-Host "`n--- Auditing SynologyDrive ---" -ForegroundColor Yellow + +if (Test-Path "D:\Shares\SynologyDrive") { + Get-ChildItem "D:\Shares\SynologyDrive" -Depth 2 | + Select-Object FullName, Length, LastWriteTime, PSIsContainer | + Export-Csv "$BackupRoot\SynologyDrive-Audit\SynologyDrive-Contents_$Timestamp.csv" -NoTypeInformation + Write-Host "[OK] SynologyDrive structure exported" -ForegroundColor Green + + # Summary + $stats = Get-ChildItem "D:\Shares\SynologyDrive" -Recurse -File -ErrorAction SilentlyContinue + Write-Host " Total files: $($stats.Count)" -ForegroundColor Cyan + Write-Host " Total size: $([math]::Round(($stats | Measure-Object Length -Sum).Sum / 1GB, 2)) GB" -ForegroundColor Cyan +} else { + Write-Host "[WARN] D:\Shares\SynologyDrive not found" -ForegroundColor Yellow +} + +# --- GPO Export --- +Write-Host "`n--- Exporting GPO state ---" -ForegroundColor Yellow + +try { + New-Item -Path "$BackupRoot\GPO" -ItemType Directory -Force | Out-Null + Get-GPO -All | ForEach-Object { + $_ | Get-GPOReport -ReportType XML -Path "$BackupRoot\GPO\$($_.DisplayName)_$Timestamp.xml" + } + Get-GPO -All | Select-Object DisplayName, Id, GpoStatus, CreationTime, ModificationTime | + Export-Csv "$BackupRoot\GPO\GPO-List_$Timestamp.csv" -NoTypeInformation + Write-Host "[OK] GPO reports exported" -ForegroundColor Green +} +catch { + Write-Host "[WARN] GPO export failed: $_" -ForegroundColor Yellow +} + +# --- Summary --- +Write-Host "`n=== Export Complete ===" -ForegroundColor Cyan +Write-Host "All backups saved to: $BackupRoot" -ForegroundColor Green +Write-Host "" +Write-Host "Next steps:" -ForegroundColor Yellow +Write-Host " 1. Download pfSense config XML manually (Diagnostics > Backup & Restore)" +Write-Host " 2. Save it to $BackupRoot\pfSense\" +Write-Host " 3. Review SynologyDrive audit output" +Write-Host " 4. Proceed to Phase 1 (Network Foundation)" diff --git a/clients/cascades-tucson/docs/migration/scripts/phase0-remote-checks.ps1 b/clients/cascades-tucson/docs/migration/scripts/phase0-remote-checks.ps1 new file mode 100644 index 0000000..4598602 --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/phase0-remote-checks.ps1 @@ -0,0 +1,135 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + Phase 0: Quick remote health checks on CS-SERVER. +.DESCRIPTION + Checks disk health (Dell OpenManage), identifies unknown listening ports, + audits IIS websites, and reports general server health. + Run on CS-SERVER via ScreenConnect. +.NOTES + Run during Phase 0 / Session 3 (2026-03-07) while backup is in progress. +#> + +Write-Host "=== CS-SERVER Remote Health Checks ===" -ForegroundColor Cyan +Write-Host "Timestamp: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" +Write-Host "" + +# --- Disk Health (Dell OpenManage) --- +Write-Host "--- Disk Health ---" -ForegroundColor Yellow + +$omreport = "C:\Program Files\Dell\SysMgt\oma\bin\omreport.exe" +if (Test-Path $omreport) { + Write-Host "[Physical Disks]" -ForegroundColor Cyan + & $omreport storage pdisk controller=0 + + Write-Host "`n[Virtual Disks]" -ForegroundColor Cyan + & $omreport storage vdisk controller=0 + + Write-Host "`n[Controller Battery]" -ForegroundColor Cyan + & $omreport storage battery controller=0 +} else { + Write-Host "[WARN] Dell OpenManage CLI not found at: $omreport" -ForegroundColor Yellow + Write-Host "Try these alternatives:" -ForegroundColor Yellow + Write-Host " - OpenManage web UI: https://192.168.2.254:1311" + + # Try alternate paths + $altPaths = @( + "C:\Program Files (x86)\Dell\SysMgt\oma\bin\omreport.exe", + "C:\Program Files\Dell\OpenManage\oma\bin\omreport.exe" + ) + foreach ($p in $altPaths) { + if (Test-Path $p) { + Write-Host " - Found at: $p" -ForegroundColor Green + } + } + + # Fallback: Windows disk health + Write-Host "`n[Windows Disk Health - Fallback]" -ForegroundColor Cyan + Get-PhysicalDisk | Select-Object DeviceId, FriendlyName, MediaType, Size, HealthStatus, OperationalStatus | Format-Table -AutoSize + Get-Volume | Where-Object { $_.DriveLetter } | Select-Object DriveLetter, FileSystemLabel, FileSystem, @{N='SizeGB';E={[math]::Round($_.Size/1GB,1)}}, @{N='FreeGB';E={[math]::Round($_.SizeRemaining/1GB,1)}}, HealthStatus | Format-Table -AutoSize +} + +# --- Unknown Listening Ports --- +Write-Host "`n--- Unknown Port Identification ---" -ForegroundColor Yellow + +$unknownPorts = @(5504, 6783, 8019) +foreach ($port in $unknownPorts) { + $conns = Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue + if ($conns) { + foreach ($conn in $conns) { + $proc = Get-Process -Id $conn.OwningProcess -ErrorAction SilentlyContinue + $svc = Get-CimInstance Win32_Service | Where-Object { $_.ProcessId -eq $conn.OwningProcess } | Select-Object -First 1 + Write-Host "Port $port`:" -ForegroundColor Green -NoNewline + Write-Host " PID=$($conn.OwningProcess), Process=$($proc.ProcessName), Service=$($svc.Name)" + if ($proc.Path) { Write-Host " Path: $($proc.Path)" } + Write-Host " Local: $($conn.LocalAddress):$($conn.LocalPort) State: $($conn.State)" + } + } else { + Write-Host "Port $port`: No active listener" -ForegroundColor Yellow + } +} + +# --- IIS Websites --- +Write-Host "`n--- IIS Websites ---" -ForegroundColor Yellow + +try { + Import-Module WebAdministration -ErrorAction Stop + $sites = Get-Website + if ($sites) { + foreach ($site in $sites) { + Write-Host "Site: $($site.Name)" -ForegroundColor Green + Write-Host " State: $($site.State)" + Write-Host " Path: $($site.PhysicalPath)" + Write-Host " Bindings: $(($site.bindings.Collection | ForEach-Object { $_.bindingInformation }) -join ', ')" + Write-Host " App Pool: $($site.applicationPool)" + } + } else { + Write-Host "No websites configured in IIS" -ForegroundColor Yellow + } +} catch { + Write-Host "[WARN] WebAdministration module not available: $_" -ForegroundColor Yellow +} + +# --- General Server Health --- +Write-Host "`n--- Server Health Summary ---" -ForegroundColor Yellow + +$os = Get-CimInstance Win32_OperatingSystem +$uptime = (Get-Date) - $os.LastBootUpTime +Write-Host "Hostname: $env:COMPUTERNAME" +Write-Host "OS: $($os.Caption) $($os.Version)" +Write-Host "Last Boot: $($os.LastBootUpTime) (uptime: $($uptime.Days)d $($uptime.Hours)h $($uptime.Minutes)m)" +Write-Host "Timezone: $(Get-TimeZone | Select-Object -ExpandProperty DisplayName)" + +# Memory +$totalMemGB = [math]::Round($os.TotalVisibleMemorySize / 1MB, 1) +$freeMemGB = [math]::Round($os.FreePhysicalMemory / 1MB, 1) +$usedMemGB = [math]::Round(($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / 1MB, 1) +Write-Host "Memory: ${usedMemGB} GB used / ${totalMemGB} GB total (${freeMemGB} GB free)" + +# Disk space +Write-Host "`nDisk Space:" +Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Used -gt 0 } | ForEach-Object { + $totalGB = [math]::Round(($_.Used + $_.Free) / 1GB, 1) + $usedGB = [math]::Round($_.Used / 1GB, 1) + $freeGB = [math]::Round($_.Free / 1GB, 1) + $pct = [math]::Round($_.Used / ($_.Used + $_.Free) * 100, 0) + Write-Host " $($_.Name): ${usedGB} GB / ${totalGB} GB (${freeGB} GB free, ${pct}% used)" +} + +# --- DNS Forwarder Verification --- +Write-Host "`n--- DNS Forwarder Verification ---" -ForegroundColor Yellow + +try { + $fwd = Get-DnsServerForwarder + Write-Host "DNS Forwarders:" -ForegroundColor Green + $fwd.IPAddress | ForEach-Object { Write-Host " $_" } + if ($fwd.IPAddress -contains "192.168.0.1") { + Write-Host " [OK] pfSense (192.168.0.1) is configured as forwarder" -ForegroundColor Green + } else { + Write-Host " [WARN] pfSense (192.168.0.1) is NOT a forwarder!" -ForegroundColor Red + } +} catch { + Write-Host "[WARN] Could not check DNS forwarders: $_" -ForegroundColor Yellow +} + +Write-Host "`n=== Checks Complete ===" -ForegroundColor Cyan diff --git a/clients/cascades-tucson/docs/migration/scripts/phase2-ad-setup.ps1 b/clients/cascades-tucson/docs/migration/scripts/phase2-ad-setup.ps1 new file mode 100644 index 0000000..ce39e9f --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/phase2-ad-setup.ps1 @@ -0,0 +1,316 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + Phase 2.2: AD cleanup and preparation on CS-SERVER. +.DESCRIPTION + Fixes OU naming, creates Workstations OU, creates security groups with members, + removes stale/former accounts, fixes group issues, and moves computers. + Run on CS-SERVER via ScreenConnect. +.NOTES + PREREQUISITE: Run phase2-ou-cleanup.ps1 FIRST to remove duplicate root-level OUs. + Client has confirmed all account removals. + Set $DeleteAccounts = $true when ready to execute deletions. +#> + +Import-Module ActiveDirectory -ErrorAction Stop + +# --- SAFETY FLAG --- +$DeleteAccounts = $false + +$Domain = "DC=cascades,DC=local" + +Write-Host "=== Phase 2.2: AD Cleanup & Preparation ===" -ForegroundColor Cyan +Write-Host "" + +# ============================================================ +# STEP 1: Fix immediate security issues +# ============================================================ +Write-Host "--- Fixing Security Issues ---" -ForegroundColor Yellow + +# Remove disabled Monica.Ramirez from Domain Admins +try { + $monica = Get-ADUser -Identity "Monica.Ramirez" -ErrorAction SilentlyContinue + if ($monica) { + Remove-ADGroupMember -Identity "Domain Admins" -Members "Monica.Ramirez" -Confirm:$false -ErrorAction Stop + Write-Host " [OK] Removed Monica.Ramirez from Domain Admins" -ForegroundColor Green + } +} +catch { + Write-Host " [SKIP] Monica.Ramirez not in Domain Admins or not found" -ForegroundColor DarkGray +} + +# Disable Haris.Durut (currently enabled, no longer employed) +try { + Disable-ADAccount -Identity "Haris.Durut" -ErrorAction Stop + Write-Host " [OK] Disabled Haris.Durut" -ForegroundColor Green +} +catch { + Write-Host " [SKIP] Haris.Durut already disabled or not found" -ForegroundColor DarkGray +} + +# ============================================================ +# STEP 2: Fix misspelled OU +# ============================================================ +Write-Host "`n--- Fixing misspelled OU ---" -ForegroundColor Yellow + +try { + $mgmtOU = Get-ADOrganizationalUnit -Filter 'Name -eq "Managment"' -ErrorAction SilentlyContinue + if ($mgmtOU) { + Rename-ADOrganizationalUnit -Identity $mgmtOU.DistinguishedName -NewName "Management" + Write-Host " [OK] Renamed 'Managment' -> 'Management'" -ForegroundColor Green + } else { + Write-Host " [SKIP] 'Managment' OU not found (already renamed?)" -ForegroundColor DarkGray + } +} +catch { + Write-Host " [ERROR] Failed to rename OU: $_" -ForegroundColor Red +} + +# Fix "Quickboosk acccess" group name typo +try { + $qbGroup = Get-ADGroup -Filter 'Name -eq "Quickboosk acccess"' -ErrorAction SilentlyContinue + if ($qbGroup) { + Rename-ADObject -Identity $qbGroup.DistinguishedName -NewName "QuickBooks Access" + Set-ADGroup -Identity $qbGroup.DistinguishedName -DisplayName "QuickBooks Access" + Write-Host " [OK] Renamed 'Quickboosk acccess' -> 'QuickBooks Access'" -ForegroundColor Green + } else { + Write-Host " [SKIP] 'Quickboosk acccess' group not found" -ForegroundColor DarkGray + } +} +catch { + Write-Host " [ERROR] Failed to rename QuickBooks group: $_" -ForegroundColor Red +} + +# Add lauren.hasselman to QuickBooks Access (she replaced Jeff Bristol) +try { + $qbGroupName = "QuickBooks Access" + # Try both names in case rename hasn't run yet + $qb = Get-ADGroup -Filter "Name -eq '$qbGroupName'" -ErrorAction SilentlyContinue + if (-not $qb) { $qb = Get-ADGroup -Filter 'Name -eq "Quickboosk acccess"' -ErrorAction SilentlyContinue } + if ($qb) { + Add-ADGroupMember -Identity $qb -Members "lauren.hasselman" -ErrorAction Stop + Write-Host " [OK] Added lauren.hasselman to $($qb.Name)" -ForegroundColor Green + } +} +catch { + Write-Host " [SKIP] lauren.hasselman already in QuickBooks group or error: $_" -ForegroundColor DarkGray +} + +# ============================================================ +# STEP 3: Create Workstations OU +# ============================================================ +Write-Host "`n--- Creating Workstations OU ---" -ForegroundColor Yellow + +$ous = @( + @{ Name = "Workstations"; Path = $Domain } + @{ Name = "Staff PCs"; Path = "OU=Workstations,$Domain" } + @{ Name = "Shared PCs"; Path = "OU=Workstations,$Domain" } +) + +foreach ($ou in $ous) { + try { + $existing = Get-ADOrganizationalUnit -Filter "Name -eq '$($ou.Name)'" -SearchBase $ou.Path -SearchScope OneLevel -ErrorAction SilentlyContinue + if (-not $existing) { + New-ADOrganizationalUnit -Name $ou.Name -Path $ou.Path -ProtectedFromAccidentalDeletion $true + Write-Host " [OK] Created OU=$($ou.Name) in $($ou.Path)" -ForegroundColor Green + } else { + Write-Host " [SKIP] OU=$($ou.Name) already exists" -ForegroundColor DarkGray + } + } + catch { + Write-Host " [ERROR] Failed to create $($ou.Name): $_" -ForegroundColor Red + } +} + +# ============================================================ +# STEP 4: Create security groups and populate members +# ============================================================ +Write-Host "`n--- Creating & Populating Security Groups ---" -ForegroundColor Yellow + +# Group definitions: Name -> array of AD SamAccountNames +$groupDefs = @{ + "SG-Management-RW" = @( + "Meredith.Kuhn", "Ashley.Jensen", "Megan.Hiatt", "Crystal.Rodriguez", + "Tamra.Matthews", "britney.thompson", "Veronica.Feller", "strozzi", + "Alyssa.Brooks", "lauren.hasselman" + ) + "SG-Sales-RW" = @( + "Megan.Hiatt", "Crystal.Rodriguez", "Tamra.Matthews" + ) + "SG-Server-RW" = @( + "Ashley.Jensen", "britney.thompson", "Christina.DuPras", + "Veronica.Feller", "Meredith.Kuhn" + ) + "SG-Chat-RW" = @( + "Ashley.Jensen", "britney.thompson", "Veronica.Feller" + ) + "SG-Culinary-RW" = @( + "JD.Martin", "Ramon.Castaneda", "Alyssa.Brooks" + ) + "SG-IT-RW" = @( + "howard", "sysadmin" + ) + "SG-Receptionist-RW" = @( + "Cathy.Kingston", "Shontiel.Nunn", "Ray.Rai", + "Sebastian.Leon", "Michelle.Shestko" + ) + "SG-Directory-RW" = @( + "Cathy.Kingston", "Shontiel.Nunn", "Christina.DuPras" + ) + "SG-Public-RW" = @() # Empty — will use "Authenticated Users" at share level + "SG-AllShares-RO" = @() # Populated as needed +} + +foreach ($g in $groupDefs.Keys) { + # Create group if it doesn't exist + try { + $existing = Get-ADGroup -Filter "Name -eq '$g'" -ErrorAction SilentlyContinue + if (-not $existing) { + New-ADGroup -Name $g -GroupScope DomainLocal -GroupCategory Security -Path $Domain ` + -Description "File share access - created during migration" + Write-Host " [OK] Created group: $g" -ForegroundColor Green + } else { + Write-Host " [SKIP] Group $g already exists" -ForegroundColor DarkGray + } + } + catch { + Write-Host " [ERROR] Failed to create $g : $_" -ForegroundColor Red + continue + } + + # Add members + foreach ($member in $groupDefs[$g]) { + try { + Add-ADGroupMember -Identity $g -Members $member -ErrorAction Stop + Write-Host " [OK] Added $member to $g" -ForegroundColor Green + } + catch { + Write-Host " [SKIP] $member -> $g (already member or not found)" -ForegroundColor DarkGray + } + } +} + +# ============================================================ +# STEP 5: Delete stale and former employee accounts +# ============================================================ +Write-Host "`n--- Removing Stale & Former Employee Accounts ---" -ForegroundColor Yellow + +# Already disabled — delete +$disabledToDelete = @( + "Anna.Pitzlin", + "Nela.Durut-Azizi", + "Jodi.Ramstack", + "Monica.Ramirez", + "jeff.bristol" +) + +# Currently enabled — disable first, then delete (former employees not in HR) +$enabledToRemove = @( + "Haris.Durut", + "Nuria.Diaz", + "Cathy.Reece", + "Kelly.Wallace", + "alyssa.brooks", + "Isabella.Islas", + "ann.dery" +) + +if ($DeleteAccounts) { + Write-Host " Deleting disabled accounts..." -ForegroundColor Yellow + foreach ($acct in $disabledToDelete) { + try { + Remove-ADUser -Identity $acct -Confirm:$false -ErrorAction Stop + Write-Host " [OK] Deleted: $acct" -ForegroundColor Green + } + catch { + Write-Host " [SKIP] $acct not found: $_" -ForegroundColor DarkGray + } + } + + Write-Host " Disabling and deleting former employees..." -ForegroundColor Yellow + foreach ($acct in $enabledToRemove) { + try { + Disable-ADAccount -Identity $acct -ErrorAction SilentlyContinue + Remove-ADUser -Identity $acct -Confirm:$false -ErrorAction Stop + Write-Host " [OK] Disabled + Deleted: $acct" -ForegroundColor Green + } + catch { + Write-Host " [SKIP] $acct not found: $_" -ForegroundColor DarkGray + } + } +} else { + Write-Host " [WARN] Deletion SKIPPED - set `$DeleteAccounts = `$true to execute" -ForegroundColor Yellow + Write-Host "" + Write-Host " Disabled accounts to delete:" -ForegroundColor Yellow + foreach ($acct in $disabledToDelete) { Write-Host " - $acct" -ForegroundColor DarkGray } + Write-Host " Enabled accounts to disable + delete (NOT in HR):" -ForegroundColor Yellow + foreach ($acct in $enabledToRemove) { Write-Host " - $acct" -ForegroundColor DarkGray } +} + +# ============================================================ +# STEP 6: Remove shared/generic accounts from active use +# ============================================================ +Write-Host "`n--- Shared Account Cleanup ---" -ForegroundColor Yellow + +$sharedAccounts = @("Culinary", "Receptionist", "saleshare", "directoryshare") +Write-Host " The following shared accounts should be replaced with individual logins:" -ForegroundColor Yellow +foreach ($acct in $sharedAccounts) { + try { + $user = Get-ADUser -Identity $acct -Properties Enabled -ErrorAction SilentlyContinue + if ($user -and $user.Enabled) { + Write-Host " $acct — ENABLED (disable after users have individual accounts)" -ForegroundColor Yellow + } + } + catch { + Write-Host " $acct — not found" -ForegroundColor DarkGray + } +} + +# ============================================================ +# STEP 7: Move computers to Workstations OU +# ============================================================ +Write-Host "`n--- Moving Computers to Workstations OU ---" -ForegroundColor Yellow + +$targetOU = "OU=Staff PCs,OU=Workstations,$Domain" +# Note: CS-QB stays in CN=Computers — it's a Hyper-V VM (VoIP server), not a staff PC +$computers = @("CRYSTAL-PC", "ACCT2-PC", "DESKTOP-H6QHRR7", "DESKTOP-1ISF081") + +foreach ($pc in $computers) { + try { + $comp = Get-ADComputer -Identity $pc -ErrorAction SilentlyContinue + if ($comp) { + if ($comp.DistinguishedName -notlike "*$targetOU*") { + Move-ADObject -Identity $comp.DistinguishedName -TargetPath $targetOU + Write-Host " [OK] Moved $pc to Staff PCs OU" -ForegroundColor Green + } else { + Write-Host " [SKIP] $pc already in Staff PCs OU" -ForegroundColor DarkGray + } + } else { + Write-Host " [SKIP] $pc not found in AD" -ForegroundColor DarkGray + } + } + catch { + Write-Host " [ERROR] Failed to move $pc : $_" -ForegroundColor Red + } +} + +# ============================================================ +# SUMMARY +# ============================================================ +Write-Host "`n=== AD Setup Summary ===" -ForegroundColor Cyan + +Write-Host "`nOU Structure:" -ForegroundColor Yellow +Get-ADOrganizationalUnit -Filter * | Select-Object Name, DistinguishedName | Format-Table -AutoSize -Wrap + +Write-Host "Security Groups & Members:" -ForegroundColor Yellow +Get-ADGroup -Filter 'Name -like "SG-*"' | ForEach-Object { + $members = (Get-ADGroupMember $_ -ErrorAction SilentlyContinue | Select-Object -Expand Name) -join ", " + Write-Host " $($_.Name): $members" -ForegroundColor Cyan +} + +Write-Host "`nEnabled User Count:" -ForegroundColor Yellow +$enabled = (Get-ADUser -Filter 'Enabled -eq $true').Count +Write-Host " $enabled enabled accounts" -ForegroundColor Cyan + +Write-Host "`n=== AD Cleanup Complete ===" -ForegroundColor Cyan +Write-Host "Next: Run phase2-sync-synology.ps1 to sync data from NAS" -ForegroundColor Green diff --git a/clients/cascades-tucson/docs/migration/scripts/phase2-dns-cleanup.ps1 b/clients/cascades-tucson/docs/migration/scripts/phase2-dns-cleanup.ps1 new file mode 100644 index 0000000..ddfaefc --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/phase2-dns-cleanup.ps1 @@ -0,0 +1,123 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + Phase 2.1: DNS cleanup on CS-SERVER. +.DESCRIPTION + Removes stale DNS records, fixes DomainDnsZones/ForestDnsZones, + enables scavenging, and creates reverse lookup zones. + Run on CS-SERVER via ScreenConnect. +#> + +Import-Module DnsServer -ErrorAction Stop +Import-Module ActiveDirectory -ErrorAction Stop + +$Zone = "cascades.local" + +Write-Host "=== Phase 2.1: DNS Cleanup ===" -ForegroundColor Cyan +Write-Host "" + +# --- Remove stale A records --- +Write-Host "--- Removing stale A records ---" -ForegroundColor Yellow + +$staleRecords = @( + @{ Name = "@"; IP = "192.168.0.5" } + @{ Name = "@"; IP = "192.168.2.59" } + @{ Name = "CRYSTAL-PC"; IP = "192.168.5.115" } + @{ Name = "CS-QB"; IP = "192.168.5.29" } + @{ Name = "DESKTOP-1ISF081"; IP = "192.168.5.30" } + @{ Name = "DomainDnsZones"; IP = "192.168.0.5" } + @{ Name = "DomainDnsZones"; IP = "192.168.2.59" } + @{ Name = "ForestDnsZones"; IP = "192.168.0.5" } + @{ Name = "ForestDnsZones"; IP = "192.168.2.59" } +) + +foreach ($rec in $staleRecords) { + try { + Remove-DnsServerResourceRecord -ZoneName $Zone -RRType "A" -Name $rec.Name -RecordData $rec.IP -Force -ErrorAction Stop + Write-Host " [OK] Removed $($rec.Name) -> $($rec.IP)" -ForegroundColor Green + } + catch { + Write-Host " [SKIP] $($rec.Name) -> $($rec.IP) not found or already removed" -ForegroundColor DarkGray + } +} + +# --- Fix DomainDnsZones/ForestDnsZones --- +Write-Host "`n--- Fixing DomainDnsZones/ForestDnsZones ---" -ForegroundColor Yellow + +try { + Add-DnsServerResourceRecordA -ZoneName $Zone -Name "DomainDnsZones" -IPv4Address "192.168.2.254" -ErrorAction Stop + Write-Host " [OK] Added DomainDnsZones -> 192.168.2.254" -ForegroundColor Green +} +catch { + Write-Host " [SKIP] DomainDnsZones -> 192.168.2.254 already exists" -ForegroundColor DarkGray +} + +try { + Add-DnsServerResourceRecordA -ZoneName $Zone -Name "ForestDnsZones" -IPv4Address "192.168.2.254" -ErrorAction Stop + Write-Host " [OK] Added ForestDnsZones -> 192.168.2.254" -ForegroundColor Green +} +catch { + Write-Host " [SKIP] ForestDnsZones -> 192.168.2.254 already exists" -ForegroundColor DarkGray +} + +# --- Enable scavenging --- +Write-Host "`n--- Enabling DNS Scavenging ---" -ForegroundColor Yellow + +try { + Set-DnsServerScavenging -ScavengingState $true -ScavengingInterval 7.00:00:00 -ErrorAction Stop + Write-Host " [OK] Server-level scavenging enabled (7-day interval)" -ForegroundColor Green +} +catch { + Write-Host " [ERROR] Failed to enable scavenging: $_" -ForegroundColor Red +} + +try { + Set-DnsServerZoneAging -Name $Zone -Aging $true -ErrorAction Stop + Write-Host " [OK] Zone aging enabled on $Zone" -ForegroundColor Green +} +catch { + Write-Host " [ERROR] Failed to enable zone aging: $_" -ForegroundColor Red +} + +# --- Create reverse lookup zones --- +Write-Host "`n--- Creating Reverse Lookup Zones ---" -ForegroundColor Yellow + +# 192.168.0.0/22 - covers 192.168.0.x through 192.168.3.x +# /22 means we need individual /24 reverse zones for each subnet +$reverseSubnets = @("192.168.0.0/24", "192.168.1.0/24", "192.168.2.0/24", "192.168.3.0/24") +foreach ($subnet in $reverseSubnets) { + try { + Add-DnsServerPrimaryZone -NetworkId $subnet -ReplicationScope "Domain" -DynamicUpdate "Secure" -ErrorAction Stop + Write-Host " [OK] Created reverse zone for $subnet" -ForegroundColor Green + } + catch { + Write-Host " [SKIP] Reverse zone for $subnet already exists or failed: $_" -ForegroundColor DarkGray + } +} + +# 10.0.20.0/24 - INTERNAL VLAN +try { + Add-DnsServerPrimaryZone -NetworkId "10.0.20.0/24" -ReplicationScope "Domain" -DynamicUpdate "Secure" -ErrorAction Stop + Write-Host " [OK] Created reverse zone for 10.0.20.0/24" -ForegroundColor Green +} +catch { + Write-Host " [SKIP] Reverse zone for 10.0.20.0/24 already exists or failed: $_" -ForegroundColor DarkGray +} + +# --- Verify --- +Write-Host "`n--- Verification ---" -ForegroundColor Yellow + +Write-Host "`nCurrent A records for zone root:" -ForegroundColor Cyan +Get-DnsServerResourceRecord -ZoneName $Zone -Name "@" -RRType "A" | Format-Table -AutoSize + +Write-Host "DomainDnsZones records:" -ForegroundColor Cyan +Get-DnsServerResourceRecord -ZoneName $Zone -Name "DomainDnsZones" -RRType "A" | Format-Table -AutoSize + +Write-Host "ForestDnsZones records:" -ForegroundColor Cyan +Get-DnsServerResourceRecord -ZoneName $Zone -Name "ForestDnsZones" -RRType "A" | Format-Table -AutoSize + +Write-Host "Reverse lookup zones:" -ForegroundColor Cyan +Get-DnsServerZone | Where-Object { $_.IsReverseLookupZone } | Format-Table ZoneName, ZoneType, DynamicUpdate -AutoSize + +Write-Host "`n=== DNS Cleanup Complete ===" -ForegroundColor Cyan +Write-Host "Next: Run phase2-ad-setup.ps1" -ForegroundColor Green diff --git a/clients/cascades-tucson/docs/migration/scripts/phase2-file-shares.ps1 b/clients/cascades-tucson/docs/migration/scripts/phase2-file-shares.ps1 new file mode 100644 index 0000000..56aab2d --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/phase2-file-shares.ps1 @@ -0,0 +1,233 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + Phase 2.4: Set up file share permissions on CS-SERVER. +.DESCRIPTION + Creates SMB shares for folders synced from Synology and sets NTFS + permissions using security groups from phase2-ad-setup.ps1. + Run on CS-SERVER via ScreenConnect AFTER phase2-sync-synology.ps1. +.NOTES + Permissions mapped from Synology NAS permission report. + Existing shares (Culinary, IT, Receptionist, directoryshare) are updated. + New shares (Management, SalesDept, Server, chat, Public, homes) are created. + SaleShare (old) is left alone — SalesDept is the real folder. +#> + +Import-Module ActiveDirectory -ErrorAction Stop + +Write-Host "=== Phase 2.4: File Share Permissions ===" -ForegroundColor Cyan +Write-Host "" + +$DestRoot = "D:\Shares" + +# --- Share definitions --- +# Each entry: share Name, folder Path, security group for RW, description +$shares = @( + # Synology-sourced shares (new on CS-SERVER) + @{ + Name = "Management" + Path = "$DestRoot\Management" + Group = "CASCADES\SG-Management-RW" + Desc = "Management share (from Synology)" + }, + @{ + Name = "SalesDept" + Path = "$DestRoot\SalesDept" + Group = "CASCADES\SG-Sales-RW" + Desc = "Sales department (from Synology)" + }, + @{ + Name = "Server" + Path = "$DestRoot\Server" + Group = "CASCADES\SG-Server-RW" + Desc = "Server/office share (from Synology)" + }, + @{ + Name = "chat" + Path = "$DestRoot\chat" + Group = "CASCADES\SG-Chat-RW" + Desc = "Chat share (from Synology)" + }, + @{ + Name = "Public" + Path = "$DestRoot\Public" + Group = $null # Everyone gets access + Desc = "Public share (all users)" + }, + # Existing shares on CS-SERVER (update permissions) + @{ + Name = "Culinary" + Path = "$DestRoot\Culinary" + Group = "CASCADES\SG-Culinary-RW" + Desc = "Culinary department" + }, + @{ + Name = "IT" + Path = "$DestRoot\IT" + Group = "CASCADES\SG-IT-RW" + Desc = "IT department" + }, + @{ + Name = "Receptionist" + Path = "$DestRoot\Receptionist" + Group = "CASCADES\SG-Receptionist-RW" + Desc = "Receptionist/front desk" + }, + @{ + Name = "directoryshare" + Path = "$DestRoot\directoryshare" + Group = "CASCADES\SG-Directory-RW" + Desc = "Directory share" + } +) + +# --- Process each share --- +foreach ($s in $shares) { + Write-Host "`n--- $($s.Name) ---" -ForegroundColor Yellow + + # Check path exists + if (-not (Test-Path $s.Path)) { + Write-Host " [SKIP] Path not found: $($s.Path) — run phase2-sync-synology.ps1 first" -ForegroundColor Yellow + continue + } + + # Set NTFS permissions + try { + $acl = Get-Acl $s.Path + $acl.SetAccessRuleProtection($true, $false) # Break inheritance + + # SYSTEM: Full Control + $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule( + "SYSTEM", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow" + ))) + + # Domain Admins: Full Control + $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule( + "CASCADES\Domain Admins", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow" + ))) + + if ($s.Group) { + # Specific group: Modify + $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule( + $s.Group, "Modify", "ContainerInherit,ObjectInherit", "None", "Allow" + ))) + + # Read-only group + $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule( + "CASCADES\SG-AllShares-RO", "ReadAndExecute", "ContainerInherit,ObjectInherit", "None", "Allow" + ))) + } else { + # Public share: Authenticated Users get Modify + $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule( + "Authenticated Users", "Modify", "ContainerInherit,ObjectInherit", "None", "Allow" + ))) + } + + Set-Acl $s.Path $acl + Write-Host " [OK] NTFS permissions set" -ForegroundColor Green + } + catch { + Write-Host " [ERROR] NTFS permissions failed: $_" -ForegroundColor Red + continue + } + + # Create or update SMB share + try { + $existingShare = Get-SmbShare -Name $s.Name -ErrorAction SilentlyContinue + if (-not $existingShare) { + New-SmbShare -Name $s.Name -Path $s.Path -FullAccess "Authenticated Users" -Description $s.Desc + Write-Host " [OK] Created SMB share: \\CS-SERVER\$($s.Name)" -ForegroundColor Green + } else { + Grant-SmbShareAccess -Name $s.Name -AccountName "Authenticated Users" -AccessRight Full -Force | Out-Null + Write-Host " [OK] Updated SMB share: \\CS-SERVER\$($s.Name)" -ForegroundColor Green + } + } + catch { + Write-Host " [ERROR] SMB share failed: $_" -ForegroundColor Red + } +} + +# --- Set up homes share for folder redirection --- +Write-Host "`n--- homes (Folder Redirection) ---" -ForegroundColor Yellow + +$homesPath = "$DestRoot\homes" +if (Test-Path $homesPath) { + # Special permissions for homes: users create their own folders + try { + $acl = Get-Acl $homesPath + $acl.SetAccessRuleProtection($true, $false) + + # SYSTEM: Full Control + $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule( + "SYSTEM", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow" + ))) + + # Domain Admins: Full Control + $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule( + "CASCADES\Domain Admins", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow" + ))) + + # Authenticated Users: Create folders only (at root level) + # This lets folder redirection auto-create user folders + $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule( + "Authenticated Users", "CreateDirectories", "ThisFolder", "None", "Allow" + ))) + $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule( + "Authenticated Users", "ListDirectory", "ThisFolder", "None", "Allow" + ))) + + # CREATOR OWNER: Full Control on subfolders/files only + # This gives each user full control of their own folder + $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule( + "CREATOR OWNER", "FullControl", "ContainerInherit,ObjectInherit", "InheritOnly", "Allow" + ))) + + Set-Acl $homesPath $acl + Write-Host " [OK] NTFS permissions set (folder redirection ready)" -ForegroundColor Green + } + catch { + Write-Host " [ERROR] homes NTFS permissions failed: $_" -ForegroundColor Red + } + + # Create SMB share + try { + $existingShare = Get-SmbShare -Name "homes" -ErrorAction SilentlyContinue + if (-not $existingShare) { + New-SmbShare -Name "homes" -Path $homesPath -FullAccess "Authenticated Users" ` + -Description "User home folders (folder redirection)" ` + -FolderEnumerationMode AccessBased # ABE: users only see their own folder + Write-Host " [OK] Created SMB share: \\CS-SERVER\homes (ABE enabled)" -ForegroundColor Green + } else { + Write-Host " [SKIP] homes share already exists" -ForegroundColor DarkGray + } + } + catch { + Write-Host " [ERROR] homes SMB share failed: $_" -ForegroundColor Red + } +} else { + Write-Host " [SKIP] $homesPath not found — run phase2-sync-synology.ps1 first" -ForegroundColor Yellow +} + +# --- Update main Shares share --- +Write-Host "`n--- Updating main Shares ---" -ForegroundColor Yellow +try { + $sharesShare = Get-SmbShare -Name "Shares" -ErrorAction SilentlyContinue + if ($sharesShare) { + Grant-SmbShareAccess -Name "Shares" -AccountName "Authenticated Users" -AccessRight Full -Force | Out-Null + Write-Host " [OK] Updated Shares SMB permissions" -ForegroundColor Green + } +} +catch { + Write-Host " [WARN] Could not update Shares: $_" -ForegroundColor Yellow +} + +# --- Summary --- +Write-Host "`n=== File Share Summary ===" -ForegroundColor Cyan + +Write-Host "`nAll SMB Shares:" -ForegroundColor Yellow +Get-SmbShare | Where-Object { $_.Path -like "D:\*" } | + Select-Object Name, Path, Description | + Format-Table -AutoSize -Wrap + +Write-Host "=== File Share Setup Complete ===" -ForegroundColor Cyan +Write-Host "Next: Run phase2-print-server.ps1" -ForegroundColor Green diff --git a/clients/cascades-tucson/docs/migration/scripts/phase2-ou-cleanup.ps1 b/clients/cascades-tucson/docs/migration/scripts/phase2-ou-cleanup.ps1 new file mode 100644 index 0000000..497a8a7 --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/phase2-ou-cleanup.ps1 @@ -0,0 +1,354 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + Phase 2.1: OU Structure Cleanup on CS-SERVER. +.DESCRIPTION + Audits and removes duplicate/empty root-level OUs, fixes misspelling, + handles CN=Users account cleanup. Run BEFORE phase2-ad-setup.ps1. + Run on CS-SERVER via ScreenConnect. +.NOTES + Step 1: Read-only audit (always runs) + Step 2: Delete duplicate root OUs (requires $DeleteOUs = $true) + Step 3: Delete empty Managment/MemCare/Sales OUs (requires $DeleteOUs = $true) + Step 4: Delete/disable stale accounts in CN=Users (requires $DeleteAccounts = $true) + Step 5: Flag Lupe.Sanchez for review +#> + +Import-Module ActiveDirectory -ErrorAction Stop + +# --- SAFETY FLAGS --- +$DeleteOUs = $false # Set $true to delete empty root-level OUs +$DeleteAccounts = $false # Set $true to delete/disable stale accounts in CN=Users + +$Domain = "DC=cascades,DC=local" + +Write-Host "=== Phase 2.1: OU Structure Cleanup ===" -ForegroundColor Cyan +Write-Host "" + +# ============================================================ +# STEP 1: Audit root-level duplicate OUs (READ-ONLY) +# ============================================================ +Write-Host "--- Step 1: Auditing Root-Level Duplicate OUs ---" -ForegroundColor Yellow +Write-Host "These OUs exist at root AND under Departments. Root copies should be empty." -ForegroundColor DarkGray +Write-Host "" + +$rootDuplicateOUs = @( + "OU=Administrative,$Domain", + "OU=Care-Assisted Living,$Domain", + "OU=Care-Memorycare,$Domain", + "OU=Culinary,$Domain", + "OU=Housekeeping,$Domain", + "OU=Life Enrichment,$Domain", + "OU=Maintenance,$Domain", + "OU=Marketing,$Domain", + "OU=Resident Services,$Domain", + "OU=Transportation,$Domain" +) + +$allEmpty = $true +$ouResults = @{} + +foreach ($ou in $rootDuplicateOUs) { + $ouName = ($ou -split ',')[0] -replace 'OU=','' + try { + $objects = Get-ADObject -SearchBase $ou -SearchScope OneLevel -Filter * -ErrorAction Stop + $props = Get-ADOrganizationalUnit $ou -Properties gPLink, ProtectedFromAccidentalDeletion -ErrorAction Stop + + Write-Host " === $ouName (root) ===" -ForegroundColor White + if ($objects) { + $allEmpty = $false + $ouResults[$ou] = "HAS_OBJECTS" + foreach ($obj in $objects) { + Write-Host " $($obj.ObjectClass): $($obj.Name)" -ForegroundColor Red + } + } else { + $ouResults[$ou] = "EMPTY" + Write-Host " Empty" -ForegroundColor Green + } + + $gpLink = if ($props.gPLink) { $props.gPLink } else { "(none)" } + Write-Host " GPLink: $gpLink" -ForegroundColor DarkGray + Write-Host " Protected: $($props.ProtectedFromAccidentalDeletion)" -ForegroundColor DarkGray + Write-Host "" + } + catch { + Write-Host " === $ouName (root) ===" -ForegroundColor White + Write-Host " NOT FOUND — may already be deleted" -ForegroundColor DarkGray + $ouResults[$ou] = "NOT_FOUND" + Write-Host "" + } +} + +# Also audit Managment, MemCare, Sales +Write-Host "--- Auditing Managment, MemCare, Sales Root OUs ---" -ForegroundColor Yellow +Write-Host "" + +$miscRootOUs = @( + "OU=Managment,$Domain", + "OU=MemCare,$Domain", + "OU=Sales,$Domain" +) + +foreach ($ou in $miscRootOUs) { + $ouName = ($ou -split ',')[0] -replace 'OU=','' + try { + $objects = Get-ADObject -SearchBase $ou -SearchScope OneLevel -Filter * -ErrorAction Stop + $props = Get-ADOrganizationalUnit $ou -Properties gPLink, ProtectedFromAccidentalDeletion -ErrorAction Stop + + Write-Host " === $ouName (root) ===" -ForegroundColor White + if ($objects) { + $allEmpty = $false + $ouResults[$ou] = "HAS_OBJECTS" + foreach ($obj in $objects) { + Write-Host " $($obj.ObjectClass): $($obj.Name)" -ForegroundColor Red + } + } else { + $ouResults[$ou] = "EMPTY" + Write-Host " Empty" -ForegroundColor Green + } + + $gpLink = if ($props.gPLink) { $props.gPLink } else { "(none)" } + Write-Host " GPLink: $gpLink" -ForegroundColor DarkGray + Write-Host " Protected: $($props.ProtectedFromAccidentalDeletion)" -ForegroundColor DarkGray + Write-Host "" + } + catch { + Write-Host " === $ouName (root) ===" -ForegroundColor White + Write-Host " NOT FOUND — may already be deleted" -ForegroundColor DarkGray + $ouResults[$ou] = "NOT_FOUND" + Write-Host "" + } +} + +if ($allEmpty) { + Write-Host " All audited OUs are EMPTY — safe to delete." -ForegroundColor Green +} else { + Write-Host " WARNING: Some OUs contain objects! Review output above before deleting." -ForegroundColor Red +} +Write-Host "" + +# ============================================================ +# STEP 2: Delete root-level duplicate OUs (if empty) +# ============================================================ +Write-Host "--- Step 2: Delete Root-Level Duplicate Department OUs ---" -ForegroundColor Yellow + +if ($DeleteOUs) { + foreach ($ou in $rootDuplicateOUs) { + $ouName = ($ou -split ',')[0] -replace 'OU=','' + if ($ouResults[$ou] -eq "EMPTY") { + try { + # Remove accidental deletion protection + Set-ADOrganizationalUnit $ou -ProtectedFromAccidentalDeletion $false -ErrorAction Stop + # Delete the OU + Remove-ADOrganizationalUnit $ou -Confirm:$false -ErrorAction Stop + Write-Host " [OK] Deleted root-level OU: $ouName" -ForegroundColor Green + } + catch { + Write-Host " [ERROR] Failed to delete $ouName : $_" -ForegroundColor Red + } + } + elseif ($ouResults[$ou] -eq "HAS_OBJECTS") { + Write-Host " [SKIP] $ouName has objects — move them to Departments\$ouName first!" -ForegroundColor Red + } + elseif ($ouResults[$ou] -eq "NOT_FOUND") { + Write-Host " [SKIP] $ouName already deleted" -ForegroundColor DarkGray + } + } +} else { + Write-Host " [WARN] Deletion SKIPPED — set `$DeleteOUs = `$true after reviewing audit output" -ForegroundColor Yellow +} +Write-Host "" + +# ============================================================ +# STEP 3: Delete empty Managment, MemCare, Sales root OUs +# ============================================================ +Write-Host "--- Step 3: Delete Managment, MemCare, Sales Root OUs ---" -ForegroundColor Yellow + +if ($DeleteOUs) { + foreach ($ou in $miscRootOUs) { + $ouName = ($ou -split ',')[0] -replace 'OU=','' + if ($ouResults[$ou] -eq "EMPTY") { + try { + Set-ADOrganizationalUnit $ou -ProtectedFromAccidentalDeletion $false -ErrorAction Stop + Remove-ADOrganizationalUnit $ou -Confirm:$false -ErrorAction Stop + Write-Host " [OK] Deleted root-level OU: $ouName" -ForegroundColor Green + } + catch { + Write-Host " [ERROR] Failed to delete $ouName : $_" -ForegroundColor Red + } + } + elseif ($ouResults[$ou] -eq "HAS_OBJECTS") { + Write-Host " [SKIP] $ouName has objects — review and move/delete contents first!" -ForegroundColor Red + } + elseif ($ouResults[$ou] -eq "NOT_FOUND") { + Write-Host " [SKIP] $ouName already deleted" -ForegroundColor DarkGray + } + } +} else { + Write-Host " [WARN] Deletion SKIPPED — set `$DeleteOUs = `$true after reviewing audit output" -ForegroundColor Yellow +} +Write-Host "" + +# ============================================================ +# STEP 4: Handle stale accounts in CN=Users +# ============================================================ +Write-Host "--- Step 4: CN=Users Account Cleanup ---" -ForegroundColor Yellow + +# First, show what's in CN=Users +Write-Host " Current accounts in CN=Users:" -ForegroundColor DarkGray +$usersContainer = "CN=Users,$Domain" +$cnUsers = Get-ADUser -SearchBase $usersContainer -SearchScope OneLevel -Filter * -Properties Enabled, Description, LastLogonDate +foreach ($u in $cnUsers | Sort-Object Enabled, Name) { + $status = if ($u.Enabled) { "Enabled " } else { "Disabled" } + $lastLogon = if ($u.LastLogonDate) { $u.LastLogonDate.ToString("yyyy-MM-dd") } else { "Never" } + Write-Host " [$status] $($u.SamAccountName) — Last logon: $lastLogon" -ForegroundColor $(if ($u.Enabled) { "White" } else { "DarkGray" }) +} +Write-Host "" + +# Already disabled — delete immediately +$disabledToDelete = @( + "Anna.Pitzlin", + "Nela.Durut-Azizi", + "Jodi.Ramstack", + "Monica.Ramirez" +) + +# Enabled but former employees — disable then delete +$enabledToRemove = @( + "alyssa.brooks", + "ann.dery", + "Cathy.Reece", + "Haris.Durut", + "Isabella.Islas", + "Kelly.Wallace", + "Nuria.Diaz" +) + +if ($DeleteAccounts) { + Write-Host " Deleting disabled accounts from CN=Users..." -ForegroundColor Yellow + foreach ($acct in $disabledToDelete) { + try { + # Remove from all groups first (especially Domain Admins — Monica.Ramirez!) + $user = Get-ADUser $acct -Properties MemberOf -ErrorAction Stop + foreach ($group in $user.MemberOf) { + $groupName = (Get-ADGroup $group).Name + if ($groupName -ne "Domain Users") { + Remove-ADGroupMember -Identity $group -Members $acct -Confirm:$false -ErrorAction SilentlyContinue + Write-Host " [OK] Removed $acct from $groupName" -ForegroundColor Green + } + } + Remove-ADUser -Identity $acct -Confirm:$false -ErrorAction Stop + Write-Host " [OK] Deleted: $acct" -ForegroundColor Green + } + catch { + Write-Host " [SKIP] $acct not found or error: $_" -ForegroundColor DarkGray + } + } + + Write-Host "" + Write-Host " Disabling + deleting former employee accounts..." -ForegroundColor Yellow + foreach ($acct in $enabledToRemove) { + try { + # Disable first + Disable-ADAccount -Identity $acct -ErrorAction SilentlyContinue + # Remove from all groups + $user = Get-ADUser $acct -Properties MemberOf -ErrorAction Stop + foreach ($group in $user.MemberOf) { + $groupName = (Get-ADGroup $group).Name + if ($groupName -ne "Domain Users") { + Remove-ADGroupMember -Identity $group -Members $acct -Confirm:$false -ErrorAction SilentlyContinue + Write-Host " [OK] Removed $acct from $groupName" -ForegroundColor Green + } + } + Remove-ADUser -Identity $acct -Confirm:$false -ErrorAction Stop + Write-Host " [OK] Disabled + Deleted: $acct" -ForegroundColor Green + } + catch { + Write-Host " [SKIP] $acct not found or error: $_" -ForegroundColor DarkGray + } + } +} else { + Write-Host " [WARN] Account deletion SKIPPED — set `$DeleteAccounts = `$true to execute" -ForegroundColor Yellow + Write-Host "" + Write-Host " Disabled accounts to delete:" -ForegroundColor Yellow + foreach ($acct in $disabledToDelete) { Write-Host " - $acct" -ForegroundColor DarkGray } + Write-Host " Enabled accounts to disable + delete (NOT in HR):" -ForegroundColor Yellow + foreach ($acct in $enabledToRemove) { Write-Host " - $acct" -ForegroundColor DarkGray } +} +Write-Host "" + +# ============================================================ +# STEP 5: Flag Lupe.Sanchez for review +# ============================================================ +Write-Host "--- Step 5: Lupe.Sanchez Review ---" -ForegroundColor Yellow + +try { + $lupe = Get-ADUser -Identity "Lupe.Sanchez" -Properties Enabled, LastLogonDate, Description, DistinguishedName -ErrorAction Stop + $guadalupe = Get-ADUser -Identity "Guadalupe.Sanchez" -Properties Enabled, LastLogonDate, Description, DistinguishedName -ErrorAction SilentlyContinue + + Write-Host " Lupe.Sanchez:" -ForegroundColor White + Write-Host " DN: $($lupe.DistinguishedName)" -ForegroundColor DarkGray + Write-Host " Enabled: $($lupe.Enabled)" -ForegroundColor $(if ($lupe.Enabled) { "Yellow" } else { "DarkGray" }) + Write-Host " Last Logon: $($lupe.LastLogonDate)" -ForegroundColor DarkGray + Write-Host "" + + if ($guadalupe) { + Write-Host " Guadalupe.Sanchez (possible duplicate):" -ForegroundColor White + Write-Host " DN: $($guadalupe.DistinguishedName)" -ForegroundColor DarkGray + Write-Host " Enabled: $($guadalupe.Enabled)" -ForegroundColor DarkGray + Write-Host " Last Logon: $($guadalupe.LastLogonDate)" -ForegroundColor DarkGray + Write-Host "" + Write-Host " ** REVIEW: Lupe.Sanchez may be a duplicate of Guadalupe.Sanchez (Housekeeping)." -ForegroundColor Red + Write-Host " ** Both accounts exist. Check with client which to keep." -ForegroundColor Red + } +} +catch { + Write-Host " Lupe.Sanchez not found in AD" -ForegroundColor DarkGray +} +Write-Host "" + +# ============================================================ +# STEP 6: Accounts that should STAY in CN=Users +# ============================================================ +Write-Host "--- Accounts staying in CN=Users (system/service) ---" -ForegroundColor Yellow +$keepInUsers = @("Administrator", "Guest", "krbtgt", "localadmin", "sysadmin", "QBDataServiceUser34") +foreach ($acct in $keepInUsers) { + try { + $user = Get-ADUser $acct -Properties Enabled -ErrorAction SilentlyContinue + if ($user) { + $status = if ($user.Enabled) { "Enabled" } else { "Disabled" } + Write-Host " [$status] $acct — Staying in CN=Users (system/service account)" -ForegroundColor DarkGray + } + } + catch {} +} +Write-Host "" + +# Accounts needing client decision +Write-Host "--- Accounts needing client decision ---" -ForegroundColor Yellow +Write-Host " Receptionist — shared account, currently in CN=Users. Move to Departments\Resident Services?" -ForegroundColor Yellow +Write-Host " directoryshare — shared account, currently in CN=Users. Keep as service account?" -ForegroundColor Yellow +Write-Host " Lupe.Sanchez — see review above. Possible duplicate of Guadalupe.Sanchez." -ForegroundColor Yellow +Write-Host "" + +# ============================================================ +# SUMMARY: Final OU structure +# ============================================================ +Write-Host "=== Final OU Structure ===" -ForegroundColor Cyan +Write-Host "" + +$allOUs = Get-ADOrganizationalUnit -Filter * -Properties ProtectedFromAccidentalDeletion | + Sort-Object DistinguishedName | + Select-Object Name, DistinguishedName, ProtectedFromAccidentalDeletion + +foreach ($ou in $allOUs) { + # Calculate depth for indentation + $depth = ($ou.DistinguishedName -split ',' | Where-Object { $_ -match '^OU=' }).Count - 1 + $indent = " " * $depth + $protected = if ($ou.ProtectedFromAccidentalDeletion) { "" } else { " [UNPROTECTED]" } + Write-Host " $indent$($ou.Name)$protected" -ForegroundColor White +} + +Write-Host "" +Write-Host "=== OU Cleanup Complete ===" -ForegroundColor Cyan +Write-Host "Next: Run phase2-ad-setup.ps1 (security fixes, groups, computer moves)" -ForegroundColor Green diff --git a/clients/cascades-tucson/docs/migration/scripts/phase2-print-server.ps1 b/clients/cascades-tucson/docs/migration/scripts/phase2-print-server.ps1 new file mode 100644 index 0000000..3492ad9 --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/phase2-print-server.ps1 @@ -0,0 +1,123 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + Phase 2.4: Set up print server on CS-SERVER. +.DESCRIPTION + Creates TCP/IP printer ports for all managed printers. + Drivers must be installed manually before uncommenting the Add-Printer lines. + Run on CS-SERVER via ScreenConnect. +.NOTES + Download drivers from manufacturer websites FIRST: + - Epson ET-5800: https://epson.com/Support/Printers/All-In-Ones/ET-Series/Epson-ET-5800/s/SPT_C11CJ30201 + - Canon MF455DW: https://www.usa.canon.com/support/p/imageclass-mf455dw + - Canon MF451CDW: https://www.usa.canon.com/support/p/imageclass-mf451dw + - Brother MFC-L8900CDW: https://www.brother-usa.com/support/mfcl8900cdw + - Konica Minolta Bizhub C368: https://www.konicaminolta.us/en-us/support/download-centre + Install drivers on CS-SERVER, then uncomment the Add-Printer section below. +#> + +Write-Host "=== Phase 2.4: Print Server Setup ===" -ForegroundColor Cyan +Write-Host "" + +# --- Ensure Print Server feature is installed --- +Write-Host "--- Checking Print Server role ---" -ForegroundColor Yellow +$printFeature = Get-WindowsFeature -Name Print-Server -ErrorAction SilentlyContinue +if ($printFeature -and -not $printFeature.Installed) { + Install-WindowsFeature -Name Print-Server -IncludeManagementTools + Write-Host " [OK] Print Server role installed" -ForegroundColor Green +} else { + Write-Host " [OK] Print Server role already installed" -ForegroundColor Green +} + +# --- Define printers --- +$printers = @( + @{ Name = "Front Desk - Epson ET-5800"; IP = "192.168.2.147"; ShareName = "FrontDesk-Epson"; Driver = "EPSON ET-5800 Series" } + @{ Name = "Business Office - Canon MF455DW"; IP = "192.168.3.227"; ShareName = "BizOffice-Canon"; Driver = "Canon Generic Plus UFR II" } + @{ Name = "Marketing - Brother MFC-L8900CDW"; IP = "192.168.2.21"; ShareName = "Marketing-Brother"; Driver = "Brother MFC-L8900CDW series" } + @{ Name = "206 Health - Bizhub C368"; IP = "192.168.1.138"; ShareName = "Health206-Bizhub"; Driver = "KONICA MINOLTA Universal PCL" } + @{ Name = "206 Nurse Station - Brother MFC-L8900CDW"; IP = "10.0.20.69"; ShareName = "Health206-Brother"; Driver = "Brother MFC-L8900CDW series" } + @{ Name = "MemCare MedTech - Brother (model TBD)"; IP = "192.168.2.53"; ShareName = "MemCare-Brother"; Driver = "TBD" } + @{ Name = "MemCare Director - Canon MF451CDW"; IP = "192.168.3.52"; ShareName = "MemDir-Canon"; Driver = "Canon Generic Plus UFR II" } + @{ Name = "Kitchen Printer"; IP = "192.168.0.121"; ShareName = "Kitchen"; Driver = "TBD" } +) + +# --- Create TCP/IP printer ports --- +Write-Host "`n--- Creating Printer Ports ---" -ForegroundColor Yellow + +foreach ($p in $printers) { + $portName = "TCP_$($p.IP)" + try { + $existing = Get-PrinterPort -Name $portName -ErrorAction SilentlyContinue + if (-not $existing) { + Add-PrinterPort -Name $portName -PrinterHostAddress $p.IP + Write-Host " [OK] Created port: $portName ($($p.Name))" -ForegroundColor Green + } else { + Write-Host " [SKIP] Port $portName already exists" -ForegroundColor DarkGray + } + } + catch { + Write-Host " [ERROR] Failed to create port $portName : $_" -ForegroundColor Red + } +} + +# --- Test connectivity to each printer --- +Write-Host "`n--- Testing Printer Connectivity ---" -ForegroundColor Yellow + +foreach ($p in $printers) { + $result = Test-Connection -ComputerName $p.IP -Count 1 -Quiet -ErrorAction SilentlyContinue + if ($result) { + Write-Host " [OK] $($p.Name) ($($p.IP)) - reachable" -ForegroundColor Green + } else { + Write-Host " [WARN] $($p.Name) ($($p.IP)) - NOT reachable" -ForegroundColor Yellow + } +} + +# --- Add shared printers (UNCOMMENT after installing drivers) --- +<# +Write-Host "`n--- Creating Shared Printers ---" -ForegroundColor Yellow + +foreach ($p in $printers) { + if ($p.Driver -eq "TBD") { + Write-Host " [SKIP] $($p.Name) - driver not specified" -ForegroundColor Yellow + continue + } + + $portName = "TCP_$($p.IP)" + try { + $existing = Get-Printer -Name $p.Name -ErrorAction SilentlyContinue + if (-not $existing) { + Add-Printer -Name $p.Name -DriverName $p.Driver -PortName $portName -Shared -ShareName $p.ShareName -Published + Write-Host " [OK] Created printer: $($p.Name) (\\CS-SERVER\$($p.ShareName))" -ForegroundColor Green + } else { + Write-Host " [SKIP] Printer $($p.Name) already exists" -ForegroundColor DarkGray + } + } + catch { + Write-Host " [ERROR] Failed to create $($p.Name): $_" -ForegroundColor Red + Write-Host " Verify driver '$($p.Driver)' is installed: Get-PrinterDriver" -ForegroundColor Yellow + } +} +#> + +# --- Summary --- +Write-Host "`n=== Print Server Summary ===" -ForegroundColor Cyan + +Write-Host "`nPrinter Ports:" -ForegroundColor Yellow +Get-PrinterPort | Where-Object { $_.Name -like "TCP_*" } | + Select-Object Name, PrinterHostAddress | + Format-Table -AutoSize + +Write-Host "Installed Printer Drivers:" -ForegroundColor Yellow +Get-PrinterDriver | Select-Object Name | Format-Table -AutoSize + +Write-Host "Shared Printers:" -ForegroundColor Yellow +Get-Printer | Where-Object { $_.Shared } | + Select-Object Name, PortName, ShareName, DriverName | + Format-Table -AutoSize + +Write-Host "`n=== Print Server Setup Complete ===" -ForegroundColor Cyan +Write-Host "Next steps:" -ForegroundColor Green +Write-Host " 1. Download and install printer drivers (see .NOTES in script header)" +Write-Host " 2. Uncomment the 'Add shared printers' section and re-run" +Write-Host " 3. Print a test page from CS-SERVER to each printer" +Write-Host " 4. Create GPOs in GPMC (see phase2-server-prep.md section 2.5)" diff --git a/clients/cascades-tucson/docs/migration/scripts/phase2-sync-synology.ps1 b/clients/cascades-tucson/docs/migration/scripts/phase2-sync-synology.ps1 new file mode 100644 index 0000000..8819c35 --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/phase2-sync-synology.ps1 @@ -0,0 +1,147 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + Phase 2.3: Sync data from Synology NAS to CS-SERVER. +.DESCRIPTION + Uses robocopy to pull all shared folder data from the Synology NAS + (192.168.0.120) to D:\Shares on CS-SERVER. Creates missing folders + and SMB shares. Run on CS-SERVER via ScreenConnect. +.NOTES + - Synology must be accessible at \\192.168.0.120 + - Run BEFORE phase2-file-shares.ps1 (permissions are set after sync) + - First run may take hours depending on data size + - Subsequent runs are incremental (robocopy only copies changes) + - /MIR is NOT used to avoid accidental deletion — uses /E instead +#> + +$NAS = "192.168.0.120" +$DestRoot = "D:\Shares" + +Write-Host "=== Phase 2.3: Synology Data Sync ===" -ForegroundColor Cyan +Write-Host "" + +# --- Test NAS connectivity --- +Write-Host "--- Testing NAS connectivity ---" -ForegroundColor Yellow + +$ping = Test-Connection -ComputerName $NAS -Count 2 -Quiet -ErrorAction SilentlyContinue +if (-not $ping) { + Write-Host "[FAIL] Cannot reach $NAS — aborting" -ForegroundColor Red + exit 1 +} +Write-Host " [OK] $NAS is reachable" -ForegroundColor Green + +# Test SMB access +try { + $testPath = Test-Path "\\$NAS\Public" -ErrorAction Stop + if ($testPath) { + Write-Host " [OK] SMB access to \\$NAS works" -ForegroundColor Green + } else { + Write-Host " [WARN] \\$NAS\Public not accessible — may need credentials" -ForegroundColor Yellow + Write-Host " Run: net use \\$NAS /user:admin " -ForegroundColor Yellow + } +} +catch { + Write-Host " [WARN] SMB access test failed: $_" -ForegroundColor Yellow + Write-Host " You may need to map the NAS first:" -ForegroundColor Yellow + Write-Host " net use \\$NAS /user:admin " -ForegroundColor Yellow +} + +# --- Define sync jobs --- +# Source (Synology share name) -> Destination folder on CS-SERVER +$syncJobs = @( + @{ Name = "Management"; Source = "\\$NAS\Management"; Dest = "$DestRoot\Management" } + @{ Name = "SalesDept"; Source = "\\$NAS\SalesDept"; Dest = "$DestRoot\SalesDept" } + @{ Name = "Server"; Source = "\\$NAS\Server"; Dest = "$DestRoot\Server" } + @{ Name = "chat"; Source = "\\$NAS\chat"; Dest = "$DestRoot\chat" } + @{ Name = "Public"; Source = "\\$NAS\Public"; Dest = "$DestRoot\Public" } + @{ Name = "homes"; Source = "\\$NAS\homes"; Dest = "$DestRoot\homes" } +) + +# --- Create destination folders --- +Write-Host "`n--- Creating destination folders ---" -ForegroundColor Yellow + +foreach ($job in $syncJobs) { + if (-not (Test-Path $job.Dest)) { + New-Item -Path $job.Dest -ItemType Directory -Force | Out-Null + Write-Host " [OK] Created $($job.Dest)" -ForegroundColor Green + } else { + Write-Host " [SKIP] $($job.Dest) already exists" -ForegroundColor DarkGray + } +} + +# --- Sync each share --- +Write-Host "`n--- Syncing data (this may take a while) ---" -ForegroundColor Yellow +Write-Host "" + +$logDir = "$DestRoot\IT\Backups\SyncLogs" +New-Item -Path $logDir -ItemType Directory -Force | Out-Null + +foreach ($job in $syncJobs) { + Write-Host "=== Syncing: $($job.Name) ===" -ForegroundColor Cyan + Write-Host " From: $($job.Source)" -ForegroundColor DarkGray + Write-Host " To: $($job.Dest)" -ForegroundColor DarkGray + + # Check if source is accessible + if (-not (Test-Path $job.Source)) { + Write-Host " [SKIP] Source not accessible: $($job.Source)" -ForegroundColor Yellow + continue + } + + $logFile = "$logDir\sync-$($job.Name)-$(Get-Date -Format 'yyyy-MM-dd_HHmm').log" + + # Robocopy options: + # /E = copy subdirectories including empty ones + # /Z = restartable mode (resume on network interruption) + # /COPY:DAT = copy Data, Attributes, Timestamps (no permissions — we set those separately) + # /R:3 = retry 3 times on failure + # /W:5 = wait 5 seconds between retries + # /MT:8 = 8 threads for parallel copy + # /XD = exclude directories (@ prefixed Synology system dirs) + # /LOG = log to file + # /NP = no progress percentage (cleaner log) + # /TEE = output to console AND log file + + $roboArgs = @( + $job.Source, + $job.Dest, + "/E", "/Z", + "/COPY:DAT", + "/R:3", "/W:5", + "/MT:8", + "/XD", "@eaDir", "@tmp", "#recycle", "#snapshot", + "/XF", "Thumbs.db", ".DS_Store", "desktop.ini", + "/LOG:$logFile", + "/NP", "/TEE" + ) + + $startTime = Get-Date + & robocopy @roboArgs + $exitCode = $LASTEXITCODE + $elapsed = (Get-Date) - $startTime + + # Robocopy exit codes: 0=no change, 1=files copied, 2=extras, 4=mismatches, 8+=errors + if ($exitCode -lt 8) { + Write-Host " [OK] $($job.Name) synced in $([math]::Round($elapsed.TotalMinutes, 1)) minutes (exit code: $exitCode)" -ForegroundColor Green + } else { + Write-Host " [ERROR] $($job.Name) had errors (exit code: $exitCode) — check $logFile" -ForegroundColor Red + } + Write-Host "" +} + +# --- Summary --- +Write-Host "=== Sync Complete ===" -ForegroundColor Cyan +Write-Host "" +Write-Host "Folder sizes:" -ForegroundColor Yellow + +foreach ($job in $syncJobs) { + if (Test-Path $job.Dest) { + $size = (Get-ChildItem $job.Dest -Recurse -File -ErrorAction SilentlyContinue | Measure-Object Length -Sum).Sum + $sizeGB = [math]::Round($size / 1GB, 2) + $fileCount = (Get-ChildItem $job.Dest -Recurse -File -ErrorAction SilentlyContinue).Count + Write-Host " $($job.Name): $sizeGB GB ($fileCount files)" -ForegroundColor Cyan + } +} + +Write-Host "" +Write-Host "Sync logs saved to: $logDir" -ForegroundColor Green +Write-Host "Next: Run phase2-file-shares.ps1 to set permissions and create SMB shares" -ForegroundColor Green diff --git a/clients/cascades-tucson/docs/migration/scripts/phase3-join-domain.ps1 b/clients/cascades-tucson/docs/migration/scripts/phase3-join-domain.ps1 new file mode 100644 index 0000000..f15d82c --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/phase3-join-domain.ps1 @@ -0,0 +1,151 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + Phase 3.1: Domain join a workstation. +.DESCRIPTION + Documents current state, creates local admin backup, verifies DNS, + and joins the machine to cascades.local domain. + Run on each target workstation via ScreenConnect. +.NOTES + Order: DESKTOP-KQSL232 -> CHEF-PC -> SALES4-PC -> MDIRECTOR-PC + Machine will REBOOT after domain join. +#> + +param( + [string]$LocalAdminPassword = "" +) + +$DomainName = "cascades.local" +$OUPath = "OU=Staff PCs,OU=Workstations,DC=cascades,DC=local" +$MigrationDir = "C:\IT-Migration" + +Write-Host "=== Phase 3.1: Domain Join - $env:COMPUTERNAME ===" -ForegroundColor Cyan +Write-Host "" + +# --- Step 1: Document current state --- +Write-Host "--- Step 1: Documenting current state ---" -ForegroundColor Yellow + +New-Item -Path $MigrationDir -ItemType Directory -Force | Out-Null + +$infoFile = "$MigrationDir\pre-join-info_$(Get-Date -Format 'yyyy-MM-dd_HHmm').txt" +"=== Pre-Join State for $env:COMPUTERNAME ===" | Out-File $infoFile +"Timestamp: $(Get-Date)" | Out-File $infoFile -Append +"" | Out-File $infoFile -Append + +"--- SYSTEM INFO ---" | Out-File $infoFile -Append +systeminfo | Out-File $infoFile -Append + +"--- IP CONFIG ---" | Out-File $infoFile -Append +ipconfig /all | Out-File $infoFile -Append + +"--- PRINTERS ---" | Out-File $infoFile -Append +Get-Printer -ErrorAction SilentlyContinue | Format-List | Out-File $infoFile -Append + +"--- MAPPED DRIVES ---" | Out-File $infoFile -Append +net use | Out-File $infoFile -Append + +"--- LOCAL USERS ---" | Out-File $infoFile -Append +Get-LocalUser | Format-Table | Out-File $infoFile -Append + +"--- INSTALLED SOFTWARE ---" | Out-File $infoFile -Append +Get-WmiObject Win32_Product | Select-Object Name, Version | Sort-Object Name | Format-Table | Out-File $infoFile -Append + +Write-Host " [OK] State documented at $infoFile" -ForegroundColor Green + +# --- Step 2: Create local admin backup --- +Write-Host "`n--- Step 2: Creating MSPAdmin local account ---" -ForegroundColor Yellow + +$localAdmin = Get-LocalUser -Name "Localadmin" -ErrorAction SilentlyContinue +if (-not $localAdmin) { + if (-not $LocalAdminPassword) { + Write-Host " [INPUT NEEDED] Enter password for Localadmin account:" -ForegroundColor Yellow + $securePassword = Read-Host -AsSecureString + } else { + $securePassword = ConvertTo-SecureString $LocalAdminPassword -AsPlainText -Force + } + + New-LocalUser -Name "Localadmin" -Password $securePassword -PasswordNeverExpires -AccountNeverExpires -Description "Local emergency admin - migration rollback" | Out-Null + Add-LocalGroupMember -Group "Administrators" -Member "Localadmin" | Out-Null + Write-Host " [OK] Localadmin local admin created" -ForegroundColor Green +} else { + Write-Host " [SKIP] Localadmin already exists" -ForegroundColor DarkGray +} + +# --- Step 3: Verify DNS --- +Write-Host "`n--- Step 3: Verifying DNS resolution ---" -ForegroundColor Yellow + +try { + $dns = Resolve-DnsName "cs-server.cascades.local" -ErrorAction Stop + Write-Host " [OK] cs-server.cascades.local resolves to: $($dns.IPAddress -join ', ')" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] Cannot resolve cs-server.cascades.local" -ForegroundColor Red + $currentDns = (Get-DnsClientServerAddress -AddressFamily IPv4 | Where-Object { $_.ServerAddresses.Count -gt 0 }) + Write-Host " Current DNS servers:" -ForegroundColor Yellow + foreach ($adapter in $currentDns) { + Write-Host " $($adapter.InterfaceAlias): $($adapter.ServerAddresses -join ', ')" -ForegroundColor Yellow + } + Write-Host "" + Write-Host " Options:" -ForegroundColor Cyan + Write-Host " [F] Fix DNS — set primary DNS to 192.168.2.254 (CS-SERVER) and retry" -ForegroundColor Cyan + Write-Host " [A] Abort — exit and fix manually" -ForegroundColor Cyan + $choice = Read-Host " Enter F or A" + + if ($choice -eq 'F') { + # Get the active adapter (the one with a default gateway) + $activeAdapter = Get-NetIPConfiguration | Where-Object { $_.IPv4DefaultGateway -ne $null } | Select-Object -First 1 + if (-not $activeAdapter) { + Write-Host " [FAIL] No active adapter with a gateway found. Fix manually." -ForegroundColor Red + exit 1 + } + $ifIndex = $activeAdapter.InterfaceIndex + $ifName = $activeAdapter.InterfaceAlias + Write-Host " Setting DNS on '$ifName' to 192.168.2.254 (CS-SERVER)..." -ForegroundColor Yellow + Set-DnsClientServerAddress -InterfaceIndex $ifIndex -ServerAddresses @('192.168.2.254') + Clear-DnsClientCache + + # Retry resolution + Start-Sleep -Seconds 2 + try { + $dns = Resolve-DnsName "cs-server.cascades.local" -ErrorAction Stop + Write-Host " [OK] DNS fixed. cs-server.cascades.local resolves to: $($dns.IPAddress -join ', ')" -ForegroundColor Green + } + catch { + Write-Host " [FAIL] Still cannot resolve after DNS change. Check network connectivity to 192.168.2.254" -ForegroundColor Red + exit 1 + } + } + else { + Write-Host " [ABORT] Fix DNS and re-run the script." -ForegroundColor Yellow + exit 1 + } +} + +# --- Step 4: Confirm and join --- +Write-Host "`n--- Step 4: Domain Join ---" -ForegroundColor Yellow +Write-Host " Computer: $env:COMPUTERNAME" -ForegroundColor Cyan +Write-Host " Domain: $DomainName" -ForegroundColor Cyan +Write-Host " OU: $OUPath" -ForegroundColor Cyan +Write-Host "" +Write-Host " Machine will REBOOT after joining." -ForegroundColor Yellow +Write-Host "" + +$confirm = Read-Host " Type 'JOIN' to proceed (anything else to abort)" +if ($confirm -ne "JOIN") { + Write-Host " [ABORT] Domain join cancelled by user" -ForegroundColor Yellow + exit 0 +} + +try { + Write-Host " Enter domain admin credentials..." -ForegroundColor Yellow + Add-Computer -DomainName $DomainName -OUPath $OUPath -Credential (Get-Credential) -Restart -Force + Write-Host " [OK] Domain join initiated - machine is restarting..." -ForegroundColor Green +} +catch { + Write-Host " [ERROR] Domain join failed: $_" -ForegroundColor Red + Write-Host " Troubleshooting:" -ForegroundColor Yellow + Write-Host " - Verify credentials (domain admin)" -ForegroundColor Yellow + Write-Host " - Check firewall rules allow AD ports from this subnet" -ForegroundColor Yellow + Write-Host " - Run phase3-pre-join-verify.ps1 for diagnostics" -ForegroundColor Yellow + exit 1 +} diff --git a/clients/cascades-tucson/docs/migration/scripts/phase3-post-join-verify.ps1 b/clients/cascades-tucson/docs/migration/scripts/phase3-post-join-verify.ps1 new file mode 100644 index 0000000..bab9ce5 --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/phase3-post-join-verify.ps1 @@ -0,0 +1,142 @@ +<# +.SYNOPSIS + Phase 3.1 Step 5: Post-domain-join verification. +.DESCRIPTION + Verifies GPO application, drive mappings, printer deployment, and network + connectivity after a workstation has been joined to cascades.local. + Run on the joined machine after reboot, logged in with a domain account. +#> + +Write-Host "=== Phase 3: Post-Join Verification - $env:COMPUTERNAME ===" -ForegroundColor Cyan +Write-Host "Logged in as: $env:USERDOMAIN\$env:USERNAME" +Write-Host "" + +$issues = @() + +# --- Domain membership --- +Write-Host "--- Domain Membership ---" -ForegroundColor Yellow + +$cs = Get-WmiObject Win32_ComputerSystem +if ($cs.PartOfDomain) { + Write-Host " [OK] Domain: $($cs.Domain)" -ForegroundColor Green +} else { + Write-Host " [FAIL] Not joined to domain!" -ForegroundColor Red + $issues += "Not domain-joined" +} + +# --- DC locator --- +Write-Host "`n--- Domain Controller ---" -ForegroundColor Yellow + +$nltest = nltest /dsgetdc:cascades.local 2>&1 +if ($LASTEXITCODE -eq 0) { + $dcLine = $nltest | Select-String "DC:" + Write-Host " [OK] DC found: $dcLine" -ForegroundColor Green +} else { + Write-Host " [FAIL] Cannot locate domain controller" -ForegroundColor Red + $issues += "Cannot locate DC" +} + +# --- GPO --- +Write-Host "`n--- Group Policy ---" -ForegroundColor Yellow + +$gpresult = gpresult /r 2>&1 +$appliedGPOs = $gpresult | Select-String "CSC -" +if ($appliedGPOs) { + foreach ($gpo in $appliedGPOs) { + Write-Host " [OK] Applied: $($gpo.Line.Trim())" -ForegroundColor Green + } +} else { + Write-Host " [WARN] No CSC GPOs detected - may need gpupdate /force" -ForegroundColor Yellow + $issues += "No CSC GPOs applied" +} + +# --- Drive Mappings --- +Write-Host "`n--- Drive Mappings ---" -ForegroundColor Yellow + +$expectedDrives = @("S:") +$mappedDrives = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.DisplayRoot -like "\\*" } + +foreach ($d in $mappedDrives) { + Write-Host " [OK] $($d.Name): -> $($d.DisplayRoot)" -ForegroundColor Green +} + +if (-not $mappedDrives) { + Write-Host " [WARN] No mapped drives found - check GPO and logoff/logon" -ForegroundColor Yellow + $issues += "No mapped drives" +} + +# SMB access test +try { + $testPath = Test-Path "\\192.168.2.254\Shares" -ErrorAction Stop + if ($testPath) { + Write-Host " [OK] \\CS-SERVER\Shares accessible" -ForegroundColor Green + } else { + Write-Host " [FAIL] \\CS-SERVER\Shares not accessible" -ForegroundColor Red + $issues += "Cannot access \\CS-SERVER\Shares" + } +} +catch { + Write-Host " [FAIL] SMB access error: $_" -ForegroundColor Red + $issues += "SMB access error" +} + +# --- Printers --- +Write-Host "`n--- Printers ---" -ForegroundColor Yellow + +$printers = Get-Printer -ErrorAction SilentlyContinue +$networkPrinters = $printers | Where-Object { $_.Type -eq "Connection" } + +if ($networkPrinters) { + foreach ($p in $networkPrinters) { + Write-Host " [OK] $($p.Name) ($($p.PortName))" -ForegroundColor Green + } +} else { + Write-Host " [WARN] No network printers deployed - check GPO" -ForegroundColor Yellow + $issues += "No network printers" +} + +# --- Network --- +Write-Host "`n--- Network Connectivity ---" -ForegroundColor Yellow + +# Internet +$internet = Test-Connection -ComputerName "8.8.8.8" -Count 1 -Quiet -ErrorAction SilentlyContinue +if ($internet) { + Write-Host " [OK] Internet: working" -ForegroundColor Green +} else { + Write-Host " [FAIL] Internet: NOT working" -ForegroundColor Red + $issues += "No internet" +} + +# DNS +try { + $dns = Resolve-DnsName "cs-server.cascades.local" -ErrorAction Stop + Write-Host " [OK] DNS: cs-server.cascades.local -> $($dns.IPAddress -join ', ')" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] DNS: cannot resolve cs-server.cascades.local" -ForegroundColor Red + $issues += "DNS resolution failed" +} + +# Ping DC +$ping = Test-Connection -ComputerName "192.168.2.254" -Count 1 -Quiet -ErrorAction SilentlyContinue +if ($ping) { + Write-Host " [OK] Ping CS-SERVER: reachable" -ForegroundColor Green +} else { + Write-Host " [WARN] Ping CS-SERVER: no response (ICMP may be filtered)" -ForegroundColor Yellow +} + +# --- Summary --- +Write-Host "`n========================================" -ForegroundColor Cyan +if ($issues.Count -eq 0) { + Write-Host "ALL CHECKS PASSED" -ForegroundColor Green +} else { + Write-Host "ISSUES FOUND ($($issues.Count)):" -ForegroundColor Red + foreach ($i in $issues) { + Write-Host " - $i" -ForegroundColor Red + } + Write-Host "`nTroubleshooting:" -ForegroundColor Yellow + Write-Host " - Run: gpupdate /force" -ForegroundColor Yellow + Write-Host " - Log off and log on again (for user-level GPOs)" -ForegroundColor Yellow + Write-Host " - Check: gpresult /r (for GPO details)" -ForegroundColor Yellow +} +Write-Host "========================================" -ForegroundColor Cyan diff --git a/clients/cascades-tucson/docs/migration/scripts/phase3-pre-join-verify.ps1 b/clients/cascades-tucson/docs/migration/scripts/phase3-pre-join-verify.ps1 new file mode 100644 index 0000000..1d4999b --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/phase3-pre-join-verify.ps1 @@ -0,0 +1,121 @@ +<# +.SYNOPSIS + Phase 3.0: Pre-join verification from an INTERNAL VLAN machine. +.DESCRIPTION + Tests DNS resolution, network connectivity, and SMB access to CS-SERVER. + Run from any machine on INTERNAL VLAN (10.0.20.0/24) before domain joining. + ALL tests must pass before proceeding with domain join. +#> + +Write-Host "=== Phase 3.0: Pre-Join Verification ===" -ForegroundColor Cyan +Write-Host "Running from: $env:COMPUTERNAME ($((Get-NetIPAddress -AddressFamily IPv4 | Where-Object {$_.IPAddress -notlike '127.*'}).IPAddress -join ', '))" +Write-Host "" + +$allPassed = $true + +# --- DNS Resolution --- +Write-Host "--- DNS Tests ---" -ForegroundColor Yellow + +$dnsTests = @( + @{ Name = "cs-server.cascades.local"; Expected = "192.168.2.254" } + @{ Name = "_ldap._tcp.cascades.local"; Expected = "" } +) + +foreach ($test in $dnsTests) { + try { + $result = Resolve-DnsName $test.Name -ErrorAction Stop + if ($test.Expected -and $result.IPAddress -notcontains $test.Expected) { + Write-Host " [WARN] $($test.Name) resolved but not to $($test.Expected): $($result.IPAddress -join ', ')" -ForegroundColor Yellow + } else { + Write-Host " [OK] $($test.Name) resolved: $($result.IPAddress -join ', ')" -ForegroundColor Green + } + } + catch { + Write-Host " [FAIL] $($test.Name) - DNS resolution failed" -ForegroundColor Red + $allPassed = $false + } +} + +# --- Network Connectivity --- +Write-Host "`n--- Network Connectivity ---" -ForegroundColor Yellow + +$pingTargets = @( + @{ Name = "CS-SERVER"; IP = "192.168.2.254" } + @{ Name = "pfSense"; IP = "192.168.0.1" } +) + +foreach ($target in $pingTargets) { + $result = Test-Connection -ComputerName $target.IP -Count 2 -Quiet -ErrorAction SilentlyContinue + if ($result) { + Write-Host " [OK] $($target.Name) ($($target.IP)) - reachable" -ForegroundColor Green + } else { + Write-Host " [FAIL] $($target.Name) ($($target.IP)) - NOT reachable" -ForegroundColor Red + $allPassed = $false + } +} + +# --- Port Connectivity --- +Write-Host "`n--- Port Connectivity to CS-SERVER ---" -ForegroundColor Yellow + +$ports = @( + @{ Port = 53; Desc = "DNS" } + @{ Port = 88; Desc = "Kerberos" } + @{ Port = 135; Desc = "RPC" } + @{ Port = 389; Desc = "LDAP" } + @{ Port = 445; Desc = "SMB" } + @{ Port = 636; Desc = "LDAPS" } + @{ Port = 3268; Desc = "Global Catalog" } +) + +foreach ($p in $ports) { + try { + $result = Test-NetConnection -ComputerName "192.168.2.254" -Port $p.Port -WarningAction SilentlyContinue -ErrorAction SilentlyContinue + if ($result.TcpTestSucceeded) { + Write-Host " [OK] Port $($p.Port) ($($p.Desc)) - open" -ForegroundColor Green + } else { + Write-Host " [FAIL] Port $($p.Port) ($($p.Desc)) - CLOSED/FILTERED" -ForegroundColor Red + $allPassed = $false + } + } + catch { + Write-Host " [FAIL] Port $($p.Port) ($($p.Desc)) - test failed" -ForegroundColor Red + $allPassed = $false + } +} + +# --- SMB Access --- +Write-Host "`n--- SMB Share Access ---" -ForegroundColor Yellow + +try { + $shares = net view \\192.168.2.254 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Host " [OK] net view \\192.168.2.254 succeeded" -ForegroundColor Green + } else { + Write-Host " [FAIL] net view \\192.168.2.254 failed: $shares" -ForegroundColor Red + $allPassed = $false + } +} +catch { + Write-Host " [FAIL] SMB access test failed: $_" -ForegroundColor Red + $allPassed = $false +} + +# --- Internet --- +Write-Host "`n--- Internet Access ---" -ForegroundColor Yellow + +$internet = Test-Connection -ComputerName "8.8.8.8" -Count 1 -Quiet -ErrorAction SilentlyContinue +if ($internet) { + Write-Host " [OK] Internet connectivity works" -ForegroundColor Green +} else { + Write-Host " [WARN] No internet connectivity" -ForegroundColor Yellow +} + +# --- Result --- +Write-Host "`n========================================" -ForegroundColor Cyan +if ($allPassed) { + Write-Host "ALL TESTS PASSED - Safe to proceed with domain join" -ForegroundColor Green +} else { + Write-Host "SOME TESTS FAILED - Fix issues before domain joining" -ForegroundColor Red + Write-Host "Check firewall rules (Phase 1.3) and DNS (Phase 1.4)" -ForegroundColor Yellow +} +Write-Host "========================================" -ForegroundColor Cyan diff --git a/clients/cascades-tucson/docs/migration/scripts/phase4-archive-synology.ps1 b/clients/cascades-tucson/docs/migration/scripts/phase4-archive-synology.ps1 new file mode 100644 index 0000000..4c9983c --- /dev/null +++ b/clients/cascades-tucson/docs/migration/scripts/phase4-archive-synology.ps1 @@ -0,0 +1,71 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + Phase 4.5: Archive the SynologyDrive folder on CS-SERVER. +.DESCRIPTION + Renames the SynologyDrive share folder to indicate it's archived and + should be deleted after 30 days. Run ONLY after all users are confirmed + working with mapped drives to CS-SERVER shares. +#> + +$SourcePath = "D:\Shares\SynologyDrive" +$ArchiveName = "_SynologyDrive_ARCHIVE_DeleteAfter30Days" +$ArchivePath = "D:\Shares\$ArchiveName" +$DeleteDate = (Get-Date).AddDays(30).ToString("yyyy-MM-dd") + +Write-Host "=== Phase 4.5: Archive SynologyDrive ===" -ForegroundColor Cyan +Write-Host "" + +if (-not (Test-Path $SourcePath)) { + Write-Host "[SKIP] $SourcePath does not exist (already archived?)" -ForegroundColor Yellow + if (Test-Path $ArchivePath) { + Write-Host " Archive exists at: $ArchivePath" -ForegroundColor DarkGray + } + exit 0 +} + +# Check if there's an SMB share pointing to SynologyDrive +$share = Get-SmbShare | Where-Object { $_.Path -eq $SourcePath } -ErrorAction SilentlyContinue +if ($share) { + Write-Host "Removing SMB share: $($share.Name)" -ForegroundColor Yellow + Remove-SmbShare -Name $share.Name -Force + Write-Host " [OK] SMB share removed" -ForegroundColor Green +} + +# Get size info +$stats = Get-ChildItem $SourcePath -Recurse -File -ErrorAction SilentlyContinue +$sizeGB = [math]::Round(($stats | Measure-Object Length -Sum).Sum / 1GB, 2) +Write-Host "" +Write-Host "SynologyDrive stats:" -ForegroundColor Cyan +Write-Host " Files: $($stats.Count)" -ForegroundColor Cyan +Write-Host " Size: $sizeGB GB" -ForegroundColor Cyan +Write-Host "" + +# Rename +Write-Host "Archiving..." -ForegroundColor Yellow +try { + Rename-Item $SourcePath $ArchiveName + Write-Host "[OK] Renamed to: $ArchivePath" -ForegroundColor Green + Write-Host "" + Write-Host "IMPORTANT:" -ForegroundColor Yellow + Write-Host " Safe to delete after: $DeleteDate" -ForegroundColor Yellow + Write-Host " Set a calendar reminder!" -ForegroundColor Yellow + + # Create a readme in the archive + @" +THIS FOLDER IS ARCHIVED +======================== +Archived on: $(Get-Date -Format "yyyy-MM-dd HH:mm") +Safe to delete after: $DeleteDate +Reason: Migrated to CS-SERVER mapped drives (Phase 4 of network migration) + +If you need to restore: + Rename-Item "$ArchivePath" "SynologyDrive" +"@ | Out-File "$ArchivePath\_ARCHIVE_README.txt" + +} +catch { + Write-Host "[ERROR] Failed to archive: $_" -ForegroundColor Red +} + +Write-Host "`n=== Archive Complete ===" -ForegroundColor Cyan diff --git a/clients/cascades-tucson/docs/migration/session3-2026-03-07.md b/clients/cascades-tucson/docs/migration/session3-2026-03-07.md new file mode 100644 index 0000000..6aca956 --- /dev/null +++ b/clients/cascades-tucson/docs/migration/session3-2026-03-07.md @@ -0,0 +1,280 @@ +# Session 3 — 2026-03-07: Backup Setup + Quick Wins + +**Focus:** Priority 1 (backup/safety net) + quick remote fixes + +--- + +## Pre-Session Checklist + +- [ ] Howard has Synology DSM credentials ready +- [ ] ScreenConnect access to CS-SERVER confirmed +- [ ] pfSense web UI accessible + +--- + +## Step 1: Set Up Synology Active Backup for Business (~30 min) + +### 1a. Install Active Backup for Business + +1. Log into Synology DSM at `https://192.168.0.120:5001` +2. Open **Package Center** → search "Active Backup for Business" +3. Install (free with Synology, no license key needed) +4. Open Active Backup for Business from main menu + +### 1b. Install ABB Agent on CS-SERVER + +Via ScreenConnect on CS-SERVER: + +1. Open browser on CS-SERVER, go to `https://192.168.0.120:5001` +2. Log into DSM → Active Backup for Business → **Physical Server** tab +3. Click **Add Device** → download the Windows agent installer +4. Run installer on CS-SERVER — it will ask for: + - Synology NAS address: `192.168.0.120` + - DSM admin credentials +5. Once agent connects, CS-SERVER should appear in the device list + +### 1c. Create Backup Task + +| Setting | Value | +|---------|-------| +| Source | CS-SERVER (entire machine — C: + D:) | +| Destination | Synology Volume 1 | +| Schedule | Daily at 2:00 AM | +| Retention | 7 daily + 4 weekly | +| Compression | Enabled | +| Transfer encryption | Enabled | + +### 1d. Storage Check BEFORE Running First Backup + +```powershell +# Run on CS-SERVER to get actual data sizes +$cUsed = (Get-PSDrive C).Used / 1GB +$dUsed = (Get-PSDrive D).Used / 1GB +Write-Host "C: drive used: $([math]::Round($cUsed, 1)) GB" +Write-Host "D: drive used: $([math]::Round($dUsed, 1)) GB" +Write-Host "Total data: $([math]::Round($cUsed + $dUsed, 1)) GB" +``` + +**Storage concern:** Synology has ~540 GB free. CS-SERVER has ~137 GB on C: + ~455 GB on D: = ~592 GB total. First full backup may NOT fit if we include everything. + +**Options if space is tight:** +- Exclude pagefile.sys, hiberfil.sys, temp folders (ABB usually does this automatically) +- Exclude `C:\Windows\Temp`, `C:\Users\*\AppData\Local\Temp` +- Back up D: only (has the critical data — shares, Roaming profiles) +- Check if ABB uses dedup/compression (it does — expect 40-60% compression) + +### 1e. Run First Backup + +1. Click **Back Up Now** in the ABB console +2. Monitor progress — first full backup of ~460 GB over Gigabit LAN should take 1-2 hours +3. Verify backup starts successfully, note estimated completion time +4. Can continue with other steps while backup runs + +### 1f. Verify + +- [ ] ABB agent installed and connected on CS-SERVER +- [ ] Backup task created with correct schedule/retention +- [ ] First backup started successfully +- [ ] Estimated completion time noted: ____________ + +--- + +## Step 2: Export pfSense Config XML (~2 min) + +1. Open pfSense web UI → `https://192.168.0.1` +2. Navigate to **Diagnostics → Backup & Restore** +3. Click **Download configuration as XML** +4. Save file locally, then copy to CS-SERVER: + +```powershell +# On CS-SERVER, create the directory +New-Item -Path "D:\Shares\IT\Backups\pfSense" -ItemType Directory -Force +``` + +5. Upload the XML to `D:\Shares\IT\Backups\pfSense\pfsense-config-2026-03-07.xml` + +- [ ] pfSense XML saved to CS-SERVER + +--- + +## Step 3: Export AD/DNS/Permissions Snapshots (~10 min) + +Run on CS-SERVER via ScreenConnect: + +```powershell +# Use the existing script +Set-Location "D:\Shares\IT" +# If script is available on the server: +# .\phase0-export-configs.ps1 + +# Or run inline: +$BackupRoot = "D:\Shares\IT\Backups" +$Timestamp = Get-Date -Format "yyyy-MM-dd_HHmm" + +# Create directories +"AD", "DNS", "Permissions", "GPO" | ForEach-Object { + New-Item -Path "$BackupRoot\$_" -ItemType Directory -Force | Out-Null +} + +# AD exports +Import-Module ActiveDirectory +Get-ADUser -Filter * -Properties * | Export-Csv "$BackupRoot\AD\users_$Timestamp.csv" -NoTypeInformation +Get-ADComputer -Filter * -Properties * | Export-Csv "$BackupRoot\AD\computers_$Timestamp.csv" -NoTypeInformation +Get-ADGroup -Filter * -Properties * | Export-Csv "$BackupRoot\AD\groups_$Timestamp.csv" -NoTypeInformation +Get-ADGroupMember -Identity "Domain Admins" | Export-Csv "$BackupRoot\AD\domain-admins_$Timestamp.csv" -NoTypeInformation + +# DNS export +Import-Module DnsServer +Get-DnsServerResourceRecord -ZoneName "cascades.local" | Export-Csv "$BackupRoot\DNS\cascades-local-records_$Timestamp.csv" -NoTypeInformation +Get-DnsServerZone | Export-Csv "$BackupRoot\DNS\zones_$Timestamp.csv" -NoTypeInformation + +# DNS forwarder check (also verifies item G) +Get-DnsServerForwarder | Out-File "$BackupRoot\DNS\forwarders_$Timestamp.txt" +Write-Host "--- DNS Forwarder Check (should show 192.168.0.1) ---" +Get-DnsServerForwarder | Format-List + +# File share permissions +Get-SmbShare | Export-Csv "$BackupRoot\Permissions\smb-shares_$Timestamp.csv" -NoTypeInformation +Get-SmbShare | Where-Object { $_.Path -like "D:\*" } | ForEach-Object { + Get-SmbShareAccess -Name $_.Name | Out-File "$BackupRoot\Permissions\$($_.Name)-access_$Timestamp.txt" +} + +# GPO report +Get-GPO -All | Export-Csv "$BackupRoot\AD\gpos_$Timestamp.csv" -NoTypeInformation + +Write-Host "`nAll exports saved to $BackupRoot" -ForegroundColor Green +``` + +- [ ] AD exports completed +- [ ] DNS exports completed (including forwarder check) +- [ ] Permissions exports completed +- [ ] GPO report exported +- [ ] DNS forwarder confirmed as 192.168.0.1 (item G): ____________ + +--- + +## Step 4: Quick Remote Checks (~5 min) + +Run on CS-SERVER while backup is in progress: + +```powershell +# === DISK HEALTH CHECK === +# Try Dell OpenManage CLI +$omreport = "C:\Program Files\Dell\SysMgt\oma\bin\omreport.exe" +if (Test-Path $omreport) { + Write-Host "=== DISK HEALTH (OpenManage) ===" -ForegroundColor Cyan + & $omreport storage pdisk controller=0 +} else { + Write-Host "[WARN] Dell OpenManage CLI not found at expected path" -ForegroundColor Yellow + Write-Host "Try OpenManage web UI at https://192.168.2.254:1311" +} + +# === UNKNOWN LISTENING PORTS === +Write-Host "`n=== UNKNOWN PORT IDENTIFICATION ===" -ForegroundColor Cyan + +@(5504, 6783, 8019) | ForEach-Object { + $port = $_ + $conn = Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue + if ($conn) { + $proc = Get-Process -Id $conn.OwningProcess -ErrorAction SilentlyContinue + Write-Host "Port $port -> PID $($conn.OwningProcess) -> $($proc.ProcessName) ($($proc.Path))" -ForegroundColor Green + } else { + Write-Host "Port $port -> No active listener" -ForegroundColor Yellow + } +} + +# === IIS CHECK === +Write-Host "`n=== IIS WEBSITES ===" -ForegroundColor Cyan +try { + Import-Module WebAdministration -ErrorAction Stop + Get-Website | Format-Table Name, State, PhysicalPath, @{N='Bindings';E={$_.bindings.Collection.bindingInformation}} -AutoSize +} catch { + Write-Host "[WARN] WebAdministration module not available: $_" -ForegroundColor Yellow +} + +# === SERVER UPTIME & GENERAL HEALTH === +Write-Host "`n=== SERVER HEALTH ===" -ForegroundColor Cyan +$os = Get-CimInstance Win32_OperatingSystem +Write-Host "Uptime: $((Get-Date) - $os.LastBootUpTime)" +Write-Host "Memory: $([math]::Round(($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / 1MB, 1)) GB used / $([math]::Round($os.TotalVisibleMemorySize / 1MB, 1)) GB total" +Get-PSDrive C, D | ForEach-Object { + Write-Host "$($_.Name): $([math]::Round($_.Used/1GB,1)) GB used / $([math]::Round(($_.Used+$_.Free)/1GB,1)) GB total ($([math]::Round($_.Free/1GB,1)) GB free)" +} +``` + +Record results: +- [ ] Disk health status: ____________ +- [ ] Port 5504 is: ____________ +- [ ] Port 6783 is: ____________ +- [ ] Port 8019 is: ____________ +- [ ] IIS serving: ____________ + +--- + +## Step 5: Quick Fixes (if time permits, ~10 min) + +### 5a. Fix Room 218 DHCP (Item H) + +pfSense UI → Services → DHCP Server → Room218 +- Change **Range End** from `10.2.18.2` to `10.2.18.14` +- Save → Apply Changes + +- [ ] Room 218 DHCP range fixed + +### 5b. Delete Room 130 Firewall Rule (Item I) + +pfSense UI → Firewall → Rules → Room130 +- Delete the disabled TCP PASS rule +- Apply Changes + +- [ ] Room 130 dead rule deleted + +### 5c. Set CS-SERVER Timezone (Item K) + +```powershell +# Check current timezone +Get-TimeZone + +# Set to Arizona (UTC-07:00, no DST — matches pfSense) +Set-TimeZone -Id "US Mountain Standard Time" + +# Verify +Get-TimeZone +# Should show: (UTC-07:00) Arizona +``` + +- [ ] CS-SERVER timezone set to Arizona + +--- + +## Post-Session Summary + +### Completed This Session +- [ ] Synology Active Backup for Business installed and first backup running +- [ ] pfSense config XML exported +- [ ] AD/DNS/Permissions snapshots exported +- [ ] DNS forwarder verified (item G) +- [ ] Disk health checked +- [ ] Unknown ports identified +- [ ] IIS purpose documented +- [ ] Room 218 DHCP fixed (item H) +- [ ] Room 130 rule deleted (item I) +- [ ] CS-SERVER timezone fixed (item K) + +### Next Session Plan +1. Create firewall aliases (item D) — ~15 min +2. Replace INTERNAL firewall rules (item E) +3. Disable floating rule #4 + add room internet rule (item F) +4. Delete VLAN 10 from UniFi (item J) + +### Information Gathered +| Item | Finding | +|------|---------| +| DNS Forwarder | | +| Disk Health | | +| Port 5504 | | +| Port 6783 | | +| Port 8019 | | +| IIS Purpose | | +| Backup Status | | +| Storage Remaining | | diff --git a/clients/cascades-tucson/docs/migration/step3-switch-ports.md b/clients/cascades-tucson/docs/migration/step3-switch-ports.md new file mode 100644 index 0000000..34db645 --- /dev/null +++ b/clients/cascades-tucson/docs/migration/step3-switch-ports.md @@ -0,0 +1,85 @@ +# Step 3: Identify & Move Switch Ports (~1-2 hours, UniFi web UI + on-site) + +--- + +## 3a — Identify which switch port each hardwired machine is on + +**Need to determine for each machine:** + +| Machine | Current IP | Target IP | Known Port? | +|---------|-----------|-----------|-------------| +| CRYSTAL-PC | 10.0.20.205 | 10.0.20.205 | Unknown — find in UniFi | +| ACCT2-PC | 10.0.20.209 | 10.0.20.209 | Unknown — find in UniFi | +| DESKTOP-H6QHRR7 | 10.0.20.235 | 10.0.20.235 | Unknown — find in UniFi | +| DESKTOP-1ISF081 | Unknown | TBD | Unknown — find in UniFi | +| SALES4-PC | 10.0.20.203 | 10.0.20.203 | Unknown — find in UniFi | +| CHEF-PC | 10.0.20.232 | 10.0.20.232 | USW Lite 8, Port 7 (already INTERNAL) | +| MDIRECTOR-PC | 192.168.3.20 | 10.0.20.x (TBD) | Unknown — find in UniFi | +| DESKTOP-KQSL232 | 10.0.20.227 | 10.0.20.227 | Unknown — find in UniFi | + +**How to find ports:** UniFi → Clients → find each machine by hostname or MAC → check which switch/port it's connected to. Or: check each switch's port list for connected clients. + +--- + +## 3b — Create DHCP reservations + +### INTERNAL scope (pfSense → Services → DHCP Server → INTERNAL) + +| Machine | MAC | IP | +|---------|-----|-----| +| SALES4-PC | (get from UniFi/ARP) | 10.0.20.203 | +| CRYSTAL-PC | (get from UniFi/ARP) | 10.0.20.205 | +| ACCT2-PC | (get from UniFi/ARP) | 10.0.20.209 | +| DESKTOP-KQSL232 | (get from UniFi/ARP) | 10.0.20.227 | +| CHEF-PC | (get from UniFi/ARP) | 10.0.20.232 | +| DESKTOP-H6QHRR7 | (get from UniFi/ARP) | 10.0.20.235 | +| MDIRECTOR-PC | (get from UniFi/ARP) | 10.0.20.240 | +| DESKTOP-1ISF081 | (get from UniFi/ARP) | 10.0.20.241 | + +### LAN scope (pfSense → Services → DHCP Server → LAN) + +Create reservations for all printers (get MACs from pfSense ARP table): + +| Device | IP | MAC | +|--------|-----|-----| +| Front Desk Epson ET-5800 | 192.168.2.147 | _get from ARP_ | +| Business Office Canon MF455DW | 192.168.3.227 | _get from ARP_ | +| Marketing Brother MFC-L8900CDW | 192.168.2.21 | _get from ARP_ | +| 206 Health Services Bizhub C368 | 192.168.1.138 | 00:20:6b:b3:4a:55 | +| 206 Nurse Station Brother MFC-L8900CDW | 10.0.20.69 | Already on INTERNAL | +| MemCare MedTech Brother | 192.168.2.53 | _get from ARP_ | +| MemCare Director Canon MF451CDW | 192.168.3.52 | _get from ARP_ | +| Kitchen printer | 192.168.0.121 | _get from ARP_ | +| Epson (USW Port 8) | 192.168.2.207 | _get from ARP_ | +| Canon (USW Port 45) | 192.168.2.230 | _get from ARP_ | +| Printer-80A423 (Lite 8 Port 2) | 192.168.2.202 | _get from ARP_ | + +--- + +## 3c — Change switch port VLAN assignments + +For each hardwired workstation port identified in 3a: + +1. UniFi → Devices → select switch → Ports → select port +2. Change Native VLAN to "INTERNAL" (VLAN 20) +3. Machine will get new DHCP lease on 10.0.20.0/24 + +**Do one machine at a time.** Verify it can reach the server and printers after each change. If it can't, revert the port to native VLAN. + +--- + +## 3d — Test each moved machine + +After each port change: + +- [ ] Machine gets 10.0.20.x IP +- [ ] `nslookup cs-server.cascades.local` → 192.168.2.254 +- [ ] `\\192.168.2.254\Shares` accessible +- [ ] Can print to LAN printers +- [ ] Internet works + +--- + +## Rollback + +Revert the switch port to native VLAN (Default) in UniFi. Machine will get a LAN IP via DHCP and return to previous state. diff --git a/clients/cascades-tucson/docs/migration/step7-server-move.md b/clients/cascades-tucson/docs/migration/step7-server-move.md new file mode 100644 index 0000000..f90c33a --- /dev/null +++ b/clients/cascades-tucson/docs/migration/step7-server-move.md @@ -0,0 +1,65 @@ +# Step 7: Move Server & Printers to INTERNAL (LAST) + +**This is the final network change.** Only after everything is stable on the transitional setup. + +--- + +## 7a — Move printers to INTERNAL + +For each printer: + +1. Change switch port from native VLAN to VLAN 20 (INTERNAL) in UniFi +2. Set static IP in 10.0.20.x range (or keep LAN IP if reconfiguring server to LAN) +3. Update printer IP in CS-SERVER print server +4. Update pfSense alias `Printer_IPs` with new IPs +5. Test printing from all machines + +**Do one printer at a time.** Verify printing works before moving the next one. + +--- + +## 7b — Move CS-SERVER to INTERNAL (or re-address) + +Options (decide closer to the time): + +### Option A: Change CS-SERVER IP to 10.0.20.254 +- Update NIC to 10.0.20.254/24, gateway 10.0.20.1 +- Update DNS records (cascades.local zone) +- Update all GPOs referencing \\CS-SERVER (drive maps, printers, folder redirection) +- Update pfSense domain overrides +- Update DHCP DNS settings +- Most disruptive, but cleanest result + +### Option B: Dual-home CS-SERVER +- Add a second NIC on INTERNAL (10.0.20.254) +- Keep existing LAN NIC (192.168.2.254) +- Less disruption, but dual-homed DCs can cause issues +- Need to configure DNS binding order correctly + +### Option C: Leave as-is +- Server stays on LAN (192.168.2.254) permanently +- Firewall bridging continues to work +- Simplest, no disruption +- Fine if firewall performance is adequate + +--- + +## 7c — Clean up firewall rules + +After server/printers move (if choosing Option A or B): + +- Remove INTERNAL → LAN bridging rules (no longer needed if everything is on INTERNAL) +- Remove NAS_IP alias rule (if Synology is backup-only and on LAN) +- Simplify to standard default-deny with internet access + +If choosing Option C, keep the bridging rules as-is. + +--- + +## Rollback + +- Revert printer switch ports to native VLAN +- Revert printer static IPs to LAN addresses +- Update print server ports back to LAN IPs +- Revert CS-SERVER NIC configuration (if changed) +- Restore pfSense aliases diff --git a/clients/cascades-tucson/docs/network/dhcp.md b/clients/cascades-tucson/docs/network/dhcp.md new file mode 100644 index 0000000..f45e80a --- /dev/null +++ b/clients/cascades-tucson/docs/network/dhcp.md @@ -0,0 +1,84 @@ +# DHCP Configuration + +## DHCP Server +- Server: pfSense (pfsense.cascades.local) +- Server IP: 192.168.0.1 + +## Scopes + +### LAN Scope +- Interface: LAN (192.168.0.0/22) +- Range: 192.168.2.2 - 192.168.3.254 +- Enabled: Yes +- DHCP Option 43: Configured (UniFi controller discovery) + +### INTERNAL (VLAN 20) Scope +- Interface: INTERNAL (10.0.20.0/24) +- Range: 10.0.20.50 - 10.0.20.239 +- Enabled: Yes +- DNS Server: 192.168.0.1 + +### GUEST (VLAN 50) Scope — ADDED 2026-03-06 +- Interface: GUEST (10.0.50.0/24) +- Range: 10.0.50.50 - 10.0.50.239 +- Enabled: Yes +- DNS Server: 10.0.50.1 + +### 999GuruTestNet Scope +- Interface: 999GuruTestNet (10.0.99.0/28) +- Range: 10.0.99.2 - 10.0.99.14 +- Enabled: Yes +- Domain: 99.cascades.local +- DNS Server: 10.0.99.1 + +### Room VLAN DHCP Scopes (All Rooms) +Every room VLAN has DHCP enabled with a consistent pattern: +- Subnet: /28 per room +- Range: x.x.x.2 - x.x.x.14 (13 usable IPs per room) +- DNS: Defaults to pfSense interface IP (gateway) +- No static mappings + +~~**Known Issue:** Room218 DHCP scope~~ **FIXED 2026-03-07** — Range end changed from 10.2.18.2 to 10.2.18.14 + +## Migration Plan — DHCP Changes (Phase 1.2) + +### ~~New: GUEST Scope~~ — DONE 2026-03-06 + +Guest DHCP scope created (see GUEST scope above). + +### LAN Static Mappings (DHCP Reservations) + +| Device | MAC | IP | Purpose | +|--------|-----|-----|---------| +| Front Desk Epson ET-5800 | dc:cd:2f:82:2b:7a | 192.168.2.147 | Printer | +| Business Office Canon MF455DW | 80:a5:89:f6:71:9b | 192.168.3.227 | Printer | +| Marketing Brother MFC-L8900CDW | — (not on network) | 192.168.2.21 | Printer | +| 206 Health Services Brother | 00:20:6b:b3:4a:55 | 192.168.1.138 | Printer | +| MemCare MedTech Brother | c8:a3:e8:a2:dd:93 | 192.168.2.53 | Printer — online, dual-connected (WiFi+ethernet, needs fix) | +| MemCare Director Canon MF451CDW | 20:0b:74:b2:29:08 | 192.168.3.52 | Printer | +| Kitchen printer | — (not on network) | 192.168.0.121 | Printer | +| Epson (USW Port 8) | dc:cd:2f:22:09:69 | 192.168.2.207 | Printer | +| Canon (USW Port 45) | 74:bf:c0:fd:7a:64 | 192.168.2.230 | Printer | +| Printer-80A423 (Lite 8 Port 2) | f8:25:51:80:a4:23 | 192.168.2.202 | Printer | +| CS-QB VM | 00:15:5d:02:3b:02 | 192.168.2.228 | VoIP server (Hyper-V) | +| MDIRECTOR-PC | 98:ee:cb:9d:8a:81 | 192.168.3.20 | MemCare Director staff PC | + +### INTERNAL Static Mappings + +| Device | MAC | IP | +|--------|-----|-----| +| SALES4-PC | — (not on network) | 10.0.20.203 | +| CRYSTAL-PC | f0:09:0d:0d:fc:a7 | 10.0.20.205 | +| ACCT2-PC | 98:8d:46:f1:2d:c2 | 10.0.20.209 | +| DESKTOP-KQSL232 | c8:ff:28:64:8a:9f | 10.0.20.227 | +| CHEF-PC | 98:ee:cb:9d:8a:84 | 10.0.20.232 | +| DESKTOP-H6QHRR7 | f0:09:0d:0d:fe:e9 | 10.0.20.235 | + +### Fix: Room 218 + +Change DHCP range end from `10.2.18.2` to `10.2.18.14`. + +See `migration/phase1-network.md` for full steps. + +## DHCP Relay +- Not configured (pfSense serves DHCP directly on all interfaces) diff --git a/clients/cascades-tucson/docs/network/dns.md b/clients/cascades-tucson/docs/network/dns.md new file mode 100644 index 0000000..2128427 --- /dev/null +++ b/clients/cascades-tucson/docs/network/dns.md @@ -0,0 +1,135 @@ +# DNS Configuration + +## Internal DNS Server (Unbound Resolver) +- Server: pfSense (pfsense.cascades.local) +- Server IP: 192.168.0.1 +- DNSSEC: Enabled +- Prefetch: Enabled +- Active Interface: All +- Outgoing Interface: WAN + +## DNS Forwarders (System DNS) +- Forwarder 1: 8.8.8.8 (Google) +- Forwarder 2: 1.1.1.1 (Cloudflare) + +## Cache Settings +- Message Cache Size: 512 +- Max TTL: 86400 (24 hours) +- Min TTL: 0 +- Infra Host TTL: 900 +- Infra Cache Hosts: 10000 + +## DHCP Integration +- Register DHCP leases in DNS: Yes +- Register DHCP static mappings: Yes + +## Host Overrides +| Hostname | Domain | IP Address | Aliases | +|-------------|-----------------|----------------|---------------------------| +| cascadesds | cascades.local | 192.168.0.120 | synology.cascades.local | + +## Windows DNS Server (AD-Integrated) +- Server: CS-SERVER (192.168.2.254) +- Required for: Active Directory domain resolution, SRV records, Kerberos, LDAP + +### DNS Zones +| Zone | Type | AD-Integrated | Auto-Created | Notes | +|------|------|---------------|-------------|-------| +| cascades.local | Primary | Yes | No | Main AD zone | +| _msdcs.cascades.local | Primary | Yes | No | AD metadata zone | +| 0.in-addr.arpa | Primary | No | Yes | Auto-created reverse | +| 127.in-addr.arpa | Primary | No | Yes | Auto-created reverse | +| 255.in-addr.arpa | Primary | No | Yes | Auto-created reverse | +| TrustAnchors | Primary | Yes | No | DNSSEC trust anchors | + +**NOTE: No real reverse lookup zones exist** for any production subnet (192.168.0.0/22, 10.0.20.0/24, room VLANs). Only auto-created placeholder zones. + +### Key DNS Records (cascades.local zone) +| Hostname | Type | IP / Data | Timestamp | Notes | +|----------|------|-----------|-----------|-------| +| @ (cascades.local) | A | 192.168.0.5 | 3/25/2025 | **STALE — not current DC IP** | +| @ (cascades.local) | A | 192.168.2.59 | 9/22/2024 | **STALE — not current DC IP** | +| cs-server | A | 192.168.2.254 | Static | Correct DC record | +| ACCT2-PC | A | 10.0.20.209 | 3/2/2026 | Current | +| CRYSTAL-PC | A | 192.168.5.115 | 3/27/2025 | **STALE — should be 10.0.20.205** | +| CS-QB | A | 192.168.5.29 | 3/27/2025 | **STALE — should be 192.168.2.228** | +| DESKTOP-1ISF081 | A | 192.168.5.30 | 3/27/2025 | **192.168.5.x not a documented subnet** | +| DESKTOP-H6QHRR7 | A | 10.0.20.235 | 3/2/2026 | Current | +| Cascades-Probe | A | 192.168.3.155 | 4/23/2025 | Monitoring probe? | +| Probe | A | 192.168.5.160 | 3/14/2025 | Monitoring probe? | +| DomainDnsZones | A | 192.168.0.5 | 3/25/2025 | **STALE** | +| DomainDnsZones | A | 192.168.2.59 | 9/22/2024 | **STALE** | +| ForestDnsZones | A | 192.168.0.5 | 3/25/2025 | **STALE** | +| ForestDnsZones | A | 192.168.2.59 | 9/22/2024 | **STALE** | + +### AD SRV Records (all point to cs-server.cascades.local) +- _gc._tcp (Global Catalog, port 3268) +- _kerberos._tcp (Kerberos, port 88) +- _kpasswd._tcp (Kerberos password, port 464) +- _ldap._tcp (LDAP, port 389) +- All registered 8/28/2024 — normal for single-DC environment + +### DNS Issues — Status +1. ~~**Stale @ records**~~ — **FIXED 2026-03-06.** Removed old 192.168.0.5 and 192.168.2.59. Added correct 192.168.2.254. +2. ~~**Stale computer records**~~ — **FIXED 2026-03-06.** Removed CRYSTAL-PC (192.168.5.115), CS-QB (192.168.5.29), DESKTOP-1ISF081 (192.168.5.30). +3. ~~**No reverse lookup zones**~~ — **FIXED 2026-03-06.** Created 5 reverse zones covering LAN /22 and INTERNAL. +4. ~~**DomainDnsZones/ForestDnsZones stale**~~ — **FIXED 2026-03-06.** Removed old IPs, added 192.168.2.254. + +## DNS Architecture (pfSense + Windows DNS) +- **pfSense Unbound** (192.168.0.1): Primary DNS resolver for all clients. Forwards external queries to 8.8.8.8 / 1.1.1.1. Registers DHCP leases. +- **Windows DNS** (192.168.2.254): Authoritative for cascades.local zone. Required for AD SRV records, Kerberos, LDAP lookups. +- **Forwarding relationship:** Needs verification — pfSense should forward cascades.local queries to 192.168.2.254, and Windows DNS should forward external queries to pfSense or directly to internet resolvers. +- Domain-joined PCs likely use 192.168.2.254 as DNS (per server's own config) or 192.168.0.1 (per DHCP). + +## Migration Plan — DNS Changes (Phase 1.4 + 2.1) + +See `migration/phase2-server-prep.md` and `migration/scripts/phase2-dns-cleanup.ps1`. + +### pfSense Domain Overrides (Phase 1.4) — DONE 2026-03-06 + +| Domain | Forward to | Purpose | Status | +|--------|-----------|---------|--------| +| `cascades.local` | 192.168.2.254 | AD domain resolution | ✅ Added | +| `_msdcs.cascades.local` | 192.168.2.254 | AD metadata zone | ✅ Added | + +### CS-SERVER DNS Client Fix (Phase 1.4) — DONE 2026-03-06 + +~~CS-SERVER used pfSense (192.168.0.1) + 8.8.8.8 as DNS.~~ Fixed: now uses `127.0.0.1, 192.168.0.1`. Verified — both `cs-server.cascades.local` and `google.com` resolve correctly through localhost. + +### CS-SERVER Forwarder Fix (Phase 1.4) + +Set Windows DNS forwarder to `192.168.0.1` (pfSense) for external resolution. **TODO: Verify this is set.** + +### Stale Record Cleanup (Phase 2.1) — DONE 2026-03-06 + +All stale records removed and correct records added: +- ~~cascades.local @ → 192.168.0.5, 192.168.2.59~~ Removed. Added correct: @ → 192.168.2.254 +- ~~CRYSTAL-PC → 192.168.5.115~~ Removed (will re-register correct IP via DHCP) +- ~~CS-QB → 192.168.5.29~~ Removed (will re-register correct IP via DHCP) +- ~~DESKTOP-1ISF081 → 192.168.5.30~~ Removed +- ~~DomainDnsZones → 192.168.0.5, 192.168.2.59~~ Removed. Added correct: → 192.168.2.254 +- ~~ForestDnsZones → 192.168.0.5, 192.168.2.59~~ Removed. Added correct: → 192.168.2.254 + +### Enable Scavenging (Phase 2.1) — DONE 2026-03-06 + +- Server-level scavenging: enabled, 7-day interval ✅ +- Zone aging on cascades.local: enabled ✅ +- First scavenge available: 3/13/2026 (14-day aging window from enable date) + +### Create Reverse Lookup Zones (Phase 2.1) — DONE 2026-03-06 + +All 5 reverse zones created (AD-integrated, Domain replication scope): +- 0.168.192.in-addr.arpa ✅ +- 1.168.192.in-addr.arpa ✅ +- 2.168.192.in-addr.arpa ✅ +- 3.168.192.in-addr.arpa ✅ +- 20.0.10.in-addr.arpa ✅ + +## External DNS +- Not documented yet (registrar, hosted DNS, etc.) + +## Notes +- pfSense Unbound serves as the DNS resolver for all VLANs +- Room VLANs use their gateway (pfSense interface IP) as DNS server +- INTERNAL VLAN uses 192.168.0.1 explicitly as DNS +- 999GuruTestNet uses 10.0.99.1 as DNS diff --git a/clients/cascades-tucson/docs/network/firewall.md b/clients/cascades-tucson/docs/network/firewall.md new file mode 100644 index 0000000..3e33790 --- /dev/null +++ b/clients/cascades-tucson/docs/network/firewall.md @@ -0,0 +1,279 @@ +# Firewall Configuration + +## Device Info +- Vendor/Model: Netgate pfSense +- Firmware Version: 24.0 +- Hostname: pfsense.cascades.local +- Management IP: 192.168.0.1 (LAN), 184.191.143.62 (WAN) +- Management URL: https://192.168.0.1 +- HA Pair: No +- SSH: Enabled +- Timezone: America/Phoenix +- System DNS: 8.8.8.8, 1.1.1.1 +- Crypto Hardware: AES-NI + Cryptodev +- NIC Driver: igc (Intel i225/i226 series) + +## Physical Interfaces +| Interface | NIC | Zone/Name | IP Address | Subnet | Notes | +|-----------|--------|----------------|--------------------|--------|--------------------------------| +| igc0 | WAN | WAN | 184.191.143.62 | /30 | Primary Internet (static) | +| igc1 | LAN | LAN | 192.168.0.1 | /22 | Management / main LAN | +| igc1.20 | opt238 | INTERNAL | 10.0.20.1 | /24 | Infrastructure VLAN 20 | +| igc1.50 | GUEST | GUEST | 10.0.50.1 | /24 | Guest WiFi VLAN (added 2026-03-06) | +| igc1.999 | opt1 | 999GuruTestNet | 10.0.99.1 | /28 | Test/lab network | +| igc3 | opt240 | WANCOAX | DHCP | -- | Secondary WAN (coax backup) | + +## Gateways +| Name | Interface | Address | Protocol | Notes | +|--------------|-----------|-----------------|----------|---------------------------| +| WANGW | wan | 184.191.143.61 | IPv4 | **DEFAULT GATEWAY** | +| WANCOAX_DHCP | opt240 | dynamic | IPv4 | Backup WAN, monitor 8.8.8.8 | + +## Gateway Group: WAN_Group +- Members: WAN_DHCP (Tier 1) + WANCOAX_DHCP (Tier 1) +- Mode: Load-balance / failover +- Trigger: Download loss + latency + +## Room VLAN Scheme +Each room gets its own VLAN and /28 subnet. Pattern: `10.[floor].[room_number].0/28`, gateway at `.1`. + +### Floor 1 (VLANs 101-149) +| Room | VLAN | Subnet | Gateway | +|------|----------|--------------------|---------------| +| 101 | igc1.101 | 10.1.1.0/28 | 10.1.1.1 | +| 102 | igc1.102 | 10.1.2.0/28 | 10.1.2.1 | +| 103 | igc1.103 | 10.1.3.0/28 | 10.1.3.1 | +| 104 | igc1.104 | 10.1.4.0/28 | 10.1.4.1 | +| 105 | igc1.105 | 10.1.5.0/28 | 10.1.5.1 | +| 106 | igc1.106 | 10.1.6.0/28 | 10.1.6.1 | +| 107 | igc1.107 | 10.1.7.0/28 | 10.1.7.1 | +| 108 | igc1.108 | 10.1.8.0/28 | 10.1.8.1 | +| 109 | igc1.109 | 10.1.9.0/28 | 10.1.9.1 | +| 110 | igc1.110 | 10.1.10.0/28 | 10.1.10.1 | +| 111 | igc1.111 | 10.1.11.0/28 | 10.1.11.1 | +| 112 | igc1.112 | 10.1.12.0/28 | 10.1.12.1 | +| 115 | igc1.115 | 10.1.15.0/28 | 10.1.15.1 | +| 116 | igc1.116 | 10.1.16.0/28 | 10.1.16.1 | +| 117 | igc1.117 | 10.1.17.0/28 | 10.1.17.1 | +| 118 | igc1.118 | 10.1.18.0/28 | 10.1.18.1 | +| 119 | igc1.119 | 10.1.19.0/28 | 10.1.19.1 | +| 120 | igc1.120 | 10.1.20.0/28 | 10.1.20.1 | +| 121 | igc1.121 | 10.1.21.0/28 | 10.1.21.1 | +| 122 | igc1.122 | 10.1.22.0/28 | 10.1.22.1 | +| 123 | igc1.123 | 10.1.23.0/28 | 10.1.23.1 | +| 124 | igc1.124 | 10.1.24.0/28 | 10.1.24.1 | +| 125 | igc1.125 | 10.1.25.0/28 | 10.1.25.1 | +| 126 | igc1.126 | 10.1.26.0/28 | 10.1.26.1 | +| 127 | igc1.127 | 10.1.27.0/28 | 10.1.27.1 | +| 128 | igc1.128 | 10.1.28.0/28 | 10.1.28.1 | +| 129 | igc1.129 | 10.1.29.0/28 | 10.1.29.1 | +| 130 | igc1.130 | 10.1.30.0/28 | 10.1.30.1 | +| 131 | igc1.131 | 10.1.31.0/28 | 10.1.31.1 | +| 132 | igc1.132 | 10.1.32.0/28 | 10.1.32.1 | +| 133 | igc1.133 | 10.1.33.0/28 | 10.1.33.1 | +| 134 | igc1.134 | 10.1.34.0/28 | 10.1.34.1 | +| 135 | igc1.135 | 10.1.35.0/28 | 10.1.35.1 | +| 136 | igc1.136 | 10.1.36.0/28 | 10.1.36.1 | +| 137 | igc1.137 | 10.1.37.0/28 | 10.1.37.1 | +| 138 | igc1.138 | 10.1.38.0/28 | 10.1.38.1 | +| 140 | igc1.140 | 10.1.40.0/28 | 10.1.40.1 | +| 142 | igc1.142 | 10.1.42.0/28 | 10.1.42.1 | +| 143 | igc1.143 | 10.1.43.0/28 | 10.1.43.1 | +| 144 | igc1.144 | 10.1.44.0/28 | 10.1.44.1 | +| 145 | igc1.145 | 10.1.45.0/28 | 10.1.45.1 | +| 146 | igc1.146 | 10.1.46.0/28 | 10.1.46.1 | +| 147 | igc1.147 | 10.1.47.0/28 | 10.1.47.1 | +| 148 | igc1.148 | 10.1.48.0/28 | 10.1.48.1 | +| 149 | igc1.149 | 10.1.49.0/28 | 10.1.49.1 | + +Missing rooms on Floor 1: 113, 114, 139, 141 + +### Floor 2 (VLANs 201-249) +Same pattern: `10.2.[room].0/28` +Rooms: 201-212, 215-238, 240-249 +Missing: 213, 214, 239 + +### Floor 3 (VLANs 301-350) +Pattern: `10.3.[room].0/28` +Rooms: 301-312, 315-350 +Missing: 313, 314 +Note: Room339 interface exists but may NOT be enabled + +### Floor 4 (VLANs 401-449) +Pattern: `10.4.[room].0/28` +Rooms: 401-412, 415-449 +Missing: 413, 414 + +### Floor 5 (VLANs 501-522) +Pattern: `10.5.[room].0/28` +Rooms: 501-512, 514-522 +Missing: 513 + +### Floor 6 (VLANs 603-631) +Pattern: `10.6.[room].0/28` +Rooms: 603-631 +Missing: 601, 602 + +## Firewall Rules + +### Floating Rules (apply to all/multiple interfaces) +| # | Action | Interface | Protocol | Source | Destination | Description | +|---|--------|----------------|-----------|---------------|-------------|----------------------------------| +| 1 | PASS | openvpn | IPv4 | any | any | OpenVPN pass-all | +| 2 | PASS | any | ICMP | any | any | Allow all ICMP | +| 3 | PASS | All_Networks | TCP/UDP | any | any:53 | All Networks DNS Allow | +| 4 | PASS | any | IPv4 | any | any | Allow all IPv4 (permissive) | +| 5 | BLOCK | wan | IPv4+IPv6 | NOT lanip | (self) | Block external access to firewall| + +### WAN Rules +| # | Action | Protocol | Source | Destination | Port | Description | +|---|--------|----------|-----------------|-------------|------|--------------------------| +| 1 | PASS | UDP | any | wanip | 1194 | OpenVPN IT Staff | +| 2 | BLOCK | IPv4 | NOT All_Networks| (self) | any | Block ext access to FW | + +### LAN Rules +| # | Action | Protocol | Source | Destination | Gateway | Description | +|---|--------|----------|-------------|-------------|-----------|--------------------------| +| 1 | PASS | IPv4 | INTERNAL net| LAN net | WAN_Group | INTERNAL to LAN via WAN_Group | +| 2 | PASS | IPv4 | LAN net | any | WAN_Group | Default LAN to any | +| 3 | PASS | IPv6 | LAN net | any | -- | Default LAN IPv6 to any | + +### INTERNAL (VLAN 20) Rules +| # | Action | Protocol | Source | Destination | Description | +|---|--------|----------|---------------|-------------|--------------------------| +| 1 | PASS | IPv4 | INTERNAL net | LAN net | INTERNAL to LAN access | + +### GUEST (VLAN 50) Rules — ADDED 2026-03-06 +| # | Action | Protocol | Source | Destination | Description | +|---|--------|----------|--------|-------------|-------------| +| 1 | BLOCK | IPv4 | GUEST subnet | 192.168.0.0/22 | Block Guest to LAN | +| 2 | BLOCK | IPv4 | GUEST subnet | 10.0.0.0/8 | Block Guest to private 10.x | +| 3 | BLOCK | IPv4 | GUEST subnet | 172.16.0.0/12 | Block Guest to private 172.x | +| 4 | PASS | IPv4 | GUEST subnet | any | Guest internet access | + +### Room130 Rules +| # | Action | Protocol | Notes | +|---|----------|----------|--------------------| +| 1 | PASS | TCP | **DISABLED** | + +## NAT +- Port Forwards: None +- Outbound NAT: Automatic mode (480 auto-generated rules covering all subnets) + +## VPN - OpenVPN Server +| Setting | Value | +|----------------------|------------------------------------| +| Description | IT Staff | +| Mode | TLS + User Auth (server_tls_user) | +| Auth Backend | Local Database | +| Protocol | UDP4 | +| Listen Port | 1194 | +| Interface | WAN | +| Tunnel Network | 192.168.10.0/28 | +| Pushed Local Network | 192.168.0.0/22 | +| Pushed DNS Server | 192.168.0.1 | +| CA | CascadesVPN 25 | +| Ciphers | AES-256-GCM, AES-128-GCM, CHACHA20-POLY1305 | +| DH Length | 2048 | +| Digest | SHA256 | +| Topology | Subnet | +| Client-to-Client | Yes | +| Compression | Not allowed | +| Keepalive | 10s / 60s timeout | +| Inactive Timeout | 300s | + +## Interface Groups +| Group Name | Members | Purpose | +|-------------------|-----------------------------------------|----------------------------| +| ResidentsGroup | All room interfaces (opt2-opt237) | All resident room VLANs | +| All_Networks | LAN + opt1-opt238 | Every internal interface | +| Wan_Group_Inter | wan + opt240 | Both WAN interfaces | + +## pfSense Users +| Username | Role | Group | +|-----------|---------|--------| +| admin | System Admin | admins | +| Howard | User | admins | +| sysadmin | User | admins | +| rturner | User | -- | + +## Migration Plan — Firewall Changes (Phase 1.3) + +See `migration/phase1-network.md` for full runbook. + +### Aliases Created (on pfSense as of 2026-03-09) + +| Alias | Type | Members | Status | +|-------|------|---------|--------| +| `Server_IPs` | Host(s) | 192.168.2.254 | **CREATED** | +| `NAS_IP` | Host(s) | 192.168.0.120 | **CREATED** | + +**Deleted (not needed):** `Printer_IPs`, `AD_Ports`, `Print_Ports` — printers moving to INTERNAL VLAN (same subnet as PCs, no firewall rules needed between them). `RFC1918` not created — using built-in `_private4_` alias instead. + +### Migration Approach (revised 2026-03-09) + +Instead of building scoped INTERNAL→LAN rules for a transitional state, the plan is: +1. Move staff PCs to CSCNet WiFi (INTERNAL VLAN 20, 10.0.20.x) +2. Move printer switch ports to VLAN 20 — printers get new 10.0.20.x IPs +3. During migration, old permissive rules keep both networks talking freely +4. After all devices migrated: create scoped INTERNAL → server-only rules, then lock down + +### Post-Migration INTERNAL Rules (to create after all devices on VLAN 20) + +| # | Action | Protocol | Source | Destination | Dest Port | Description | +|---|--------|----------|--------|-------------|-----------|-------------| +| 1 | PASS | TCP/UDP | INTERNAL net | Server_IPs | 53,88,135,389,445,464,636,3268,3269,5985,9389 | AD/DNS/SMB to DC | +| 2 | PASS | TCP | INTERNAL net | Server_IPs | 3389 | RDP to server | +| 3 | PASS | TCP | INTERNAL net | NAS_IP | 445,5000,5001 | Synology access | +| 4 | PASS | ICMP | INTERNAL net | LAN net | any | Ping diagnostics | +| 5 | BLOCK | IPv4 | INTERNAL net | _private4_ | any | Block other private (LOG) | +| 6 | PASS | IPv4 | INTERNAL net | any | any | Internet access | + +### New GUEST VLAN Rules (Phase 1.1) + +| # | Action | Source | Destination | Description | +|---|--------|--------|-------------|-------------| +| 1 | BLOCK | GUEST net | 192.168.0.0/22 | Block Guest to LAN | +| 2 | BLOCK | GUEST net | 10.0.0.0/8 | Block Guest to private | +| 3 | BLOCK | GUEST net | 172.16.0.0/12 | Block Guest to private | +| 4 | PASS | GUEST net | any | Guest internet | + +### Floating Rule #4 Change + +Replace "PASS any/any on ANY interface" with: +- PASS | ResidentsGroup | IPv4 | any → ! _private4_ | "Rooms internet only" + +**Rollback:** Re-enable old floating rule #4 (disable first, don't delete). + +### Kitchen iPad Isolation (Phase 1.1b — after thermal printer inventory) + +Kitchen iPads (9 units) are food-service only — NOT medical. Restrict to kitchen thermal printers only to prevent lateral movement into PHI networks. + +| # | Action | Source | Dest | Description | +|---|--------|--------|------|-------------| +| 1 | BLOCK | Kitchen_iPads | Server_IPs | Block kitchen to servers | +| 2 | BLOCK | Kitchen_iPads | NAS_IP | Block kitchen to NAS | +| 3 | PASS | Kitchen_iPads | Kitchen_Printers | Allow kitchen to thermal printers | +| 4 | PASS | Kitchen_iPads | any (80,443) | Allow internet for app updates | + +**Blocked on:** Kitchen thermal printer inventory (need IPs/MACs from onsite visit). Kitchen_iPads alias needs MAC addresses of all 9 iPads. + +### CSC ENT → CSCNet Migration (LAN → INTERNAL coexistence) + +Many staff machines are still on CSC ENT (native LAN, 192.168.0.0/22). During migration, devices on LAN must be able to reach devices on INTERNAL (10.0.20.0/24) by name and IP, and vice versa. The existing LAN rule "INTERNAL to LAN" handles INTERNAL→LAN. Need to verify LAN→INTERNAL routing works (LAN devices reaching 10.0.20.x). Once all devices are migrated to CSCNet/INTERNAL, CSC ENT SSID can be removed. + +### Quick Fixes +- Delete Room 130 disabled rule +- Delete "INTERNAL net to LAN net PASS" from LAN rules + +## Notes +- This is a large multi-tenant residential property (6 floors, ~236 rooms) +- Each room is isolated on its own /28 VLAN (14 usable IPs per room) +- Floating rule #4 passes ALL IPv4 on any interface - very permissive (to be replaced) +- No port forwards configured +- No IPsec VPN +- No static routes +- `RFC1918` alias was NOT created (documented in error). Using built-in `_private4_` alias instead. +- `Server_IPs` and `NAS_IP` aliases created 2026-03-09. `Printer_IPs`, `AD_Ports`, `Print_Ports` created then deleted — not needed since printers are moving to INTERNAL VLAN. +- Room339 may not be enabled (missing enable tag) +- ~~Room218 DHCP scope misconfigured~~ **FIXED 2026-03-07** — range end changed to 10.2.18.14 diff --git a/clients/cascades-tucson/docs/network/topology.md b/clients/cascades-tucson/docs/network/topology.md new file mode 100644 index 0000000..c4834c7 --- /dev/null +++ b/clients/cascades-tucson/docs/network/topology.md @@ -0,0 +1,173 @@ +# Network Topology + +## Internet Connections +### Primary WAN +- ISP: (not documented in config) +- Interface: igc0 +- IP Address: 184.191.143.62/30 +- Gateway: 184.191.143.61 +- Type: Static + +### Secondary WAN (WANCOAX) +- Interface: igc3 +- IP Address: DHCP +- Type: Coax backup +- Monitor: 8.8.8.8 +- Failover: Part of WAN_Group (Tier 1 with primary) + +## Switches + +### 1st Floor USW (Core) +- Model: UniFi USW (48-port PoE) +- MAC: 28:70:4e:dc:59:8d +- IP Address: 192.168.3.155 +- Uplink: GbE +- Location: 1st Floor +- Clients: 10 +- SFP+ 1: -> Switch 2nd Floor (192.168.2.193) +- SFP+ 4: -> Switch MemCare (192.168.2.215) +- Notable ports: + - Port 8: Epson printer (192.168.2.207) + - Port 36: USW-16-PoE VoIP switch + - Port 40: Synology NAS (192.168.0.120) + - Port 41: AP 103 + - Port 45: Canon printer (192.168.2.230) + - Port 48: CS-QB (192.168.2.228) + +### Switch 2nd Floor +- Model: USW-Pro-24-PoE (UniFi Gen 2, 10G, 400W) — **PENDING REPLACEMENT** +- Previous: UniFi 24-port PoE (MAC: 0c:ea:14:3b:a5:88) +- IP Address: 192.168.2.193 +- Uplink: SFP+ (GbE) to 1st Floor USW +- UPS: CyberPower CP500PFCRM1U (500VA/300W, 1U rackmount) +- SFP 1: -> 3rd Floor switch +- SFP 2: -> 1st Floor USW (192.168.3.155) + +### Switch 3rd Floor +- Model: USW-Pro-24-PoE (UniFi Gen 2, 10G, 400W) — **PENDING REPLACEMENT** +- Previous: UniFi 24-port PoE (same model as floors 2/4/old MemCare) +- Test unit: USW Pro Max 16 PoE (MAC: 28:70:4e:32:59:24, IP: 192.168.3.134) — to be removed +- IP Address: 192.168.3.134 +- Uplink: SFP (GbE) +- UPS: CyberPower CP500PFCRM1U (500VA/300W, 1U rackmount) +- SFP+ 1: -> Switch 4th Floor +- SFP+ 2: -> Switch 2nd Floor + +### Switch 4th Floor +- Model: USW-Pro-24-PoE (UniFi Gen 2, 10G, 400W) — **PENDING REPLACEMENT** +- Previous: UniFi 24-port PoE (MAC: 0c:ea:14:3b:a9:a2) +- IP Address: 192.168.3.65 +- Uplink: SFP+ (GbE) to 3rd Floor switch +- UPS: CyberPower CP500PFCRM1U (500VA/300W, 1U rackmount) +- SFP 2: -> 3rd Floor switch + +### Spare Switches (powered off) +- 3x UniFi 24-port PoE (original floor 2, 3, 4 switches) +- 1x USW Pro Max 16 PoE (3rd floor test unit, MAC: 28:70:4e:32:59:24) +- Status: Powered off, available as spares if needed + +### Switch MemCare +- Model: USW-Pro-24-PoE (UniFi Gen 2, 10G, 400W) — **REPLACED 2026-04-07** +- Previous: UniFi 24-port PoE (MAC: 0c:ea:14:3b:b2:08) +- IP Address: 192.168.2.215 +- Uplink: SFP+ (GbE) to 1st Floor USW +- Clients: 9 +- UPS: CyberPower CP500PFCRM1U (500VA/300W, 1U rackmount) +- Notable: Serves memory care wing (5th/6th floor APs, dining, nurse station) +- Installed via UniFi Device Replacement — settings imported from old switch + +### USW Lite 8 PoE +- Model: UniFi USW Lite 8 PoE +- MAC: f4:e2:c6:57:27:87 +- IP Address: 192.168.3.214 +- Location: MemCare/Kitchen area +- Port 1: Dining Room AP +- Port 2: Printer (192.168.2.202) +- Port 3: Kitchen AP +- Port 7: CHEF-PC (INTERNAL VLAN, 10.0.20.232) +- Port 8: Uplink to Switch MemCare + +### USW-16-PoE (VoIP Switch) +- Model: UniFi USW-16-PoE +- MAC: d8:b3:70:21:94:5f +- IP Address: 192.168.3.223 +- Location: 1st Floor (connected to Port 36 of 1st Floor USW) +- Clients: 9 +- Ports 1-8: AudioCodes VoIP phones (ACL_xxxxx) +- Port 15: Uplink to 1st Floor USW +- Port 16: Vertical-Remote (192.168.2.180) + +## Switch Interconnect Topology +``` +1st Floor USW (Core - 48 port) +├── SFP+ 1 ──> Switch 2nd Floor (24 port) +│ ├── SFP 1 ──> 3rd Floor USW Pro Max 16 PoE +│ │ └── SFP+ 1 ──> Switch 4th Floor (24 port) +│ └── SFP 2 ──> 1st Floor USW (loop/redundancy) +├── SFP+ 4 ──> Switch MemCare (24 port) +│ └── Port 15 ──> USW Lite 8 PoE +└── Port 36 ──> USW-16-PoE (VoIP, 16 port) +``` + +## Wireless Access Points (82 total) + +### AP Summary by Floor +| Floor | APs | Offline | Models | Notes | +|-------|-----|---------|--------|-------| +| 1 | 16 | 3 (108, 121, 128) | U6-Lite, U7 Pro | Includes Rec Room, Kitchen, Dining | +| 2 | 13 | 1 (204) | U6-Lite, U7 Pro | Includes 2nd Floor Atrium | +| 3 | 13 | 1 (335) | U6-Lite, U7 Pro, U6 Pro | Includes 3rd Floor Atrium | +| 4 | 10 | 3 (406, 441, 450, 4th Fl Atrium) | Various | | +| 5 | 2 | 0 | U6-Lite | 505, 517 | +| 6 | 3 | 0 | U6-Lite, U7 Pro | 608, 615, 622 | +| Common | 6 | 0 | Various | Dining, Kitchen, MemCare areas | +| Special | 1 | 0 | - | CC Bridge (mesh) | + +### Offline APs (Needs Attention) +| AP Name | MAC | Last IP | Uplink | Notes | +|---------|-----|---------|--------|-------| +| 108 | 0c:ea:14:3e:55:c6 | 192.168.6.127 | Mesh | Wrong IP range (192.168.6.x) | +| 121 | 0c:ea:14:3e:5e:ae | 192.168.2.184 | Mesh | | +| 128 | 0c:ea:14:1b:2e:d1 | 192.168.2.95 | - | No uplink | +| 204 | 0c:ea:14:3e:5d:42 | 192.168.7.243 | - | Wrong IP range (192.168.7.x) | +| 335 | 0c:ea:14:3e:54:5a | 192.168.2.206 | - | | +| 406 | 0c:ea:14:36:aa:01 | 192.168.2.4 | - | | +| 441 | 0c:ea:14:3e:5e:32 | 192.168.2.200 | - | | +| 450 | 0c:ea:14:36:72:ad | 192.168.6.207 | - | Wrong IP range (192.168.6.x) | +| 4th Floor Atrium | 0c:ea:14:36:b3:61 | 192.168.3.28 | - | | + +### Common Area APs +| AP Name | MAC | IP Address | Uplink | Clients | Location | +|---------|-----|-----------|--------|---------|----------| +| Dining Room | 0c:ea:14:36:85:89 | 192.168.2.177 | GbE | 26 | Main dining | +| Kitchen | 0c:ea:14:36:af:91 | 192.168.3.73 | GbE | 9 | Kitchen | +| Memcare Nurse Station | 0c:ea:14:3e:62:3a | 192.168.3.129 | GbE | 8 | MemCare wing | +| Memcare TV Room | 0c:ea:14:3e:56:16 | 192.168.2.14 | GbE | 7 | MemCare TV room | +| Memcare Piano | 0c:ea:14:3e:57:fe | 192.168.2.188 | GbE | 1 | MemCare piano area | +| CC Bridge | 0c:ea:14:36:13:45 | 192.168.2.237 | Mesh | 7 | Bridge/connector | +| 2nd Floor Atrium | 0c:ea:14:3e:58:5e | 192.168.3.215 | GbE | 18 | 2nd floor common | +| 3rd Floor Atrium | 0c:ea:14:3e:63:be | 192.168.3.138 | GbE | 8 | 3rd floor common | + +## Key Infrastructure Devices +| Device | IP Address | MAC | Location | Notes | +|--------|-----------|-----|----------|-------| +| pfSense Firewall | 192.168.0.1 | 00:f1:f5:34:b3:4a | Server room | Primary gateway | +| CS-SERVER | 192.168.2.254 | 00:22:19:60:50:db | Server room | DC, Hyper-V host (Dell R610) | +| CS-SERVER iDRAC | 192.168.2.65 | 00:22:19:60:50:e3 | Server room | Dell out-of-band management | +| Synology NAS (cascadesds) | 192.168.0.120 | 00:11:32:a7:94:10 | 1st Floor USW Port 40 | synology.cascades.local | +| CS-QB (Hyper-V VM) | 192.168.2.228 | 00:15:5d:02:3b:02 | 1st Floor USW Port 48 | VoIP server | +| Vertical-Remote | 192.168.2.180 | e4:e7:49:52:3a:06 | USW-16-PoE Port 16 | VoIP management | +| NurseAssist | 192.168.3.254 | a8:6d:aa:51:d6:55 | — | Nurse call system? | + +## VoIP Phones (AudioCodes) +All on USW-16-PoE, ports 1-8: +| Device | IP Address | Port | +|--------|-----------|------| +| ACL_14325765 | 192.168.3.1 | Port 1 | +| ACL_14827614 | 192.168.2.143 | Port 2 | +| ACL_14865060 | 192.168.3.185 | Port 3 | +| ACL_14761438 | 192.168.2.142 | Port 4 | +| ACL_14761360 | 192.168.2.29 | Port 5 | +| ACL_14761310 | 192.168.3.192 | Port 6 | +| ACL_14761385 | 192.168.3.174 | Port 7 | +| ACL_14761495 | 192.168.3.102 | Port 8 | diff --git a/clients/cascades-tucson/docs/network/vlans.md b/clients/cascades-tucson/docs/network/vlans.md new file mode 100644 index 0000000..9c9d2e8 --- /dev/null +++ b/clients/cascades-tucson/docs/network/vlans.md @@ -0,0 +1,81 @@ +# VLANs + +## VLAN Summary + +| VLAN ID | Name | Subnet | Gateway | Interface | Purpose | +|---------|----------------|------------------|---------------|-----------|----------------------------| +| Native | LAN | 192.168.0.0/22 | 192.168.0.1 | igc1 | Management / main LAN | +| 20 | INTERNAL | 10.0.20.0/24 | 10.0.20.1 | igc1.20 | Infrastructure devices | +| 999 | 999GuruTestNet | 10.0.99.0/28 | 10.0.99.1 | igc1.999 | Test/lab network | + +## Room VLANs + +Each room gets its own VLAN with a /28 subnet (14 usable IPs). All on igc1 trunk. + +**Addressing Pattern:** `10.[floor].[room_number].0/28` with gateway at `.1` + +### Floor 1 (44 rooms) +Rooms: 101-112, 115-138, 140, 142-149 +Missing rooms (no VLAN): 113, 114, 139, 141 +Example: Room 101 = VLAN 101, subnet 10.1.1.0/28, gateway 10.1.1.1 + +### Floor 2 (46 rooms) +Rooms: 201-212, 215-238, 240-249 +Missing: 213, 214, 239 +Example: Room 201 = VLAN 201, subnet 10.2.1.0/28, gateway 10.2.1.1 + +### Floor 3 (48 rooms) +Rooms: 301-312, 315-350 +Missing: 313, 314 +Note: Room339 may not be enabled +Example: Room 301 = VLAN 301, subnet 10.3.1.0/28, gateway 10.3.1.1 + +### Floor 4 (47 rooms) +Rooms: 401-412, 415-449 +Missing: 413, 414 +Example: Room 401 = VLAN 401, subnet 10.4.1.0/28, gateway 10.4.1.1 + +### Floor 5 (21 rooms) +Rooms: 501-512, 514-522 +Missing: 513 +Example: Room 501 = VLAN 501, subnet 10.5.1.0/28, gateway 10.5.1.1 + +### Floor 6 (29 rooms) +Rooms: 603-631 +Missing: 601, 602 +Example: Room 603 = VLAN 603, subnet 10.6.3.0/28, gateway 10.6.3.1 + +**Total room VLANs: ~236** + +## Inter-VLAN Routing +- Performed by: pfSense (pfsense.cascades.local) +- All inter-VLAN routing handled by the firewall + +## Interface Groups +| Group Name | Members | Purpose | +|-------------------|--------------------------------------|----------------------------| +| ResidentsGroup | All room interfaces (opt2-opt237) | All resident room VLANs | +| All_Networks | LAN + opt1-opt238 | Every internal interface | +| Wan_Group_Inter | wan + opt240 (WANCOAX) | Both WAN interfaces | + +## Migration Plan — VLAN Changes (Phase 1.1) + +### New: VLAN 50 — Guest WiFi + +| VLAN ID | Name | Subnet | Gateway | Interface | Purpose | +|---------|------|--------|---------|-----------|---------| +| 50 | GUEST | 10.0.50.0/24 | 10.0.50.1 | igc1.50 | Isolated guest WiFi (internet only) | + +- DHCP: 10.0.50.50 - 10.0.50.239, DNS 10.0.50.1 +- Firewall: block all RFC1918, pass to internet only +- Guest SSID reassigned from Default LAN to this VLAN +- See `migration/phase1-network.md` for full setup + +### Remove: VLAN 10 — CSC Internal Network + +VLAN 10 "CSC Internal Network" in UniFi appears orphaned (pfSense uses VLAN 20 for INTERNAL). Verify unused and delete from UniFi. + +## Notes +- Guest isolation: Each room is on its own /28, rooms cannot communicate with each other +- Floating firewall rule passes all IPv4 - rooms CAN reach the internet (to be replaced with scoped rules) +- DHCP range per room: x.x.x.2 through x.x.x.14 (13 addresses) diff --git a/clients/cascades-tucson/docs/network/wifi.md b/clients/cascades-tucson/docs/network/wifi.md new file mode 100644 index 0000000..d4e138b --- /dev/null +++ b/clients/cascades-tucson/docs/network/wifi.md @@ -0,0 +1,68 @@ +# WiFi Configuration (UniFi) + +## SSIDs (3) +| SSID | Network Assignment | AP Group | Bands | Security | Purpose | +|------|-------------------|----------|-------|----------|---------| +| **CSCNet** | 238 Networks (per-room VLANs) | All APs | 2.4 + 5 GHz | WPA2 | Primary SSID — residents + staff. VLAN assignment handled at UniFi controller level (per-AP network mapping), NOT via RADIUS/NPS. NPS on CS-SERVER has only default deny policies, no RADIUS clients, and no VLAN attributes configured. | +| **CSC ENT** | Native Network (Default LAN, 192.168.0.0/22) | All APs | 2.4 + 5 GHz | WPA2 | Legacy staff WiFi — many machines still on this SSID. Must keep functional (LAN access to servers/printers) until all devices migrate to CSCNet (INTERNAL VLAN). Remove after migration complete. | +| **Guest** | Guest (VLAN 50, 10.0.50.0/24) | All APs | 2.4 + 5 GHz | WPA2 | Guest WiFi — isolated from all internal networks (moved from Default LAN 2026-03-06) | + +## UniFi Network Definitions + +### Infrastructure Networks +| Network Name | VLAN ID | Gateway | Subnet | Notes | +|-------------|---------|---------|--------|-------| +| Default | 1 (native) | Third-party (pfSense) | 192.168.0.0/22 | Main LAN — servers, infra, APs | +| Guest | **50** | Third-party (pfSense) | 10.0.50.0/24 | Guest WiFi isolation (added 2026-03-06) | +| CSC Internal Network | **10** | Third-party (pfSense) | - | **Mismatch: pfSense has INTERNAL on VLAN 20, not 10** | +| Internal | **20** | Third-party (pfSense) | - | Staff VLAN (10.0.20.0/24) — matches pfSense | +| 999 - Test | 999 | Third-party (pfSense) | - | GuruTestNet | + +### Room VLANs (238 total) +All room VLANs are defined in UniFi as "Third-party Gateway" networks. VLAN IDs match room numbers. + +**Floor 1 (44):** 101-149 (missing: 113, 114, 139, 141) +**Floor 2 (46):** 201-249 (missing: 213, 214, 239) +**Floor 3 (48):** 301-350 (missing: 313, 314) +**Floor 4 (47):** 401-449 (missing: 413, 414) +**Floor 5 — MemCare (21):** 501-522 (missing: 513) +**Floor 6 — MemCare (29):** 603-631 + +## Issues + +### ~~1. Guest WiFi on Native LAN — NO ISOLATION (High)~~ FIXED 2026-03-06 +Guest SSID moved to VLAN 50 (10.0.50.0/24) with internet-only firewall rules. All RFC1918 ranges blocked. DHCP scope: 10.0.50.50–10.0.50.239 (190 addresses). **Needs onsite testing to verify isolation.** + +### 2. CSC Internal Network VLAN Mismatch (Medium) +UniFi defines "CSC Internal Network" as VLAN 10, but pfSense has the INTERNAL interface on VLAN 20 (igc1.20, 10.0.20.0/24). UniFi also has "Internal" on VLAN 20 (correct). The VLAN 10 network may be unused/orphaned, or it could cause tagging issues if any port or SSID references it. + +**Fix:** Verify if VLAN 10 is used anywhere. If not, delete "CSC Internal Network" from UniFi to avoid confusion. + +### 3. All SSIDs Use WPA2 Only (Low) +WPA3 is not enabled on any SSID. WPA2 is acceptable but WPA3-transitional mode would improve security for newer devices while maintaining compatibility. + +### 4. Kitchen iPads Not Restricted (Medium — Security) +9 kitchen iPads are on INTERNAL VLAN (10.0.20.x) with full access to staff resources. They are food-service only (NOT medical) — used for taking orders and printing to kitchen thermal receipt printers. They should be restricted to kitchen printer access only to prevent lateral movement into PHI networks if a device is compromised. + +**Fix:** Create firewall rules restricting kitchen iPad MACs to kitchen thermal printer IPs only. Block access to staff VLAN, servers, and Synology. Allow internet for app updates. See `security/hipaa.md`. + +### 5. No Band Steering or Separate SSIDs (Low) +All SSIDs broadcast on both 2.4 and 5 GHz. Band steering should be enabled (if not already) to push capable devices to 5 GHz for better performance, especially in high-density areas like the Dining Room. + +## Migration Plan — WiFi Changes (Phase 1.1) + +### Guest SSID → VLAN 50 + +The Guest SSID will be reassigned from the Default (native LAN) network to a new Guest network on VLAN 50 (10.0.50.0/24). This isolates guest traffic from all internal resources. + +**UniFi changes:** +1. Create "Guest" network: VLAN 50, third-party gateway +2. Change Guest SSID network assignment: Default → Guest (VLAN 50) + +**Note:** Guest WiFi will briefly disconnect during SSID reassignment. + +### Delete CSC Internal Network (VLAN 10) + +After verifying VLAN 10 is not referenced by any port profile or SSID, delete "CSC Internal Network" from UniFi to avoid confusion with the correct "Internal" network on VLAN 20. + +See `migration/phase1-network.md` for full steps. diff --git a/clients/cascades-tucson/docs/overview.md b/clients/cascades-tucson/docs/overview.md new file mode 100644 index 0000000..0fee992 --- /dev/null +++ b/clients/cascades-tucson/docs/overview.md @@ -0,0 +1,109 @@ +# Client Overview + +## Company Name +Cascades - Senior Living / Assisted Living Facility + +## IT Contact +- Name: Howard (MSP) + +## Environment Summary +- Total Rooms: ~236 (6 floors) +- Domain Name: cascades.local +- Primary Site: Single building, 6 floors + MemCare wing +- ISP: Cox Fiber (primary) + Cox Coax (backup) +- Firewall: pfSense 24.0 +- Network: Full UniFi switching + wireless +- Server: CS-SERVER (192.168.2.254, RAID) +- NAS: Synology cascadesds (192.168.0.120) +- VoIP: AudioCodes phones (8 units) — not MSP-managed, but infra must stay static +- RMM: SyncroRMM (migrating from Datto RMM) +- Remote Access: ScreenConnect (all machines) +- Antivirus: Datto EDR (current, migrating away) +- Backup: **NONE — needs implementation** +- Total UniFi Devices: 82 (APs + switches) +- Total Client Devices: ~677 + +## Building Layout +- Floor 1: Rooms 101-149 (44 rooms) + common areas (Dining, Kitchen, Rec Room) +- Floor 2: Rooms 201-249 (46 rooms) + Atrium +- Floor 3: Rooms 301-350 (48 rooms) + Atrium +- Floor 4: Rooms 401-449 (47 rooms) + Atrium +- Floor 5: Rooms 501-522 (21 rooms) - MemCare +- Floor 6: Rooms 603-631 (29 rooms) - MemCare +- MemCare Wing: Nurse Station, TV Room, Piano Area, Director office + +## Staff Workstations (audit 2026-03-20) + +Full inventory with hardware, OS, security status: see `workstations.md` + +### Summary (19 machines audited: 1 server + 18 workstations) +| PC Name | User/Role | IP | OS | Domain | BitLocker | Updates | +|---------|-----------|-----|-----|--------|-----------|---------| +| **CS-SERVER** | DC/File/Hyper-V | 192.168.2.254 | Server 2019 | cascades.local | n/a | Current | +| ACCT2-PC | Stephanie (Accounting) | 10.0.20.209 | Win 11 Pro WS | cascades.local | Encrypted, OFF | Feb 2026 | +| ANN-PC | Christina / Ann Dery | 192.168.3.252 | **Win 11 Home** | WORKGROUP | None | Current | +| ASSISTMAN-PC | MeredithK (Asst Mgr) | 192.168.2.38 | Win 10 Pro | WORKGROUP | None | **3mo behind** | +| CHEF-PC | Ramon/Michael (Kitchen) | 10.0.20.232 | Win 11 Pro | WORKGROUP | None | Feb 2026 | +| CRYSTAL-PC | Crystal Rodriguez | 10.0.20.205 | Win 11 Pro | cascades.local | None | **5mo behind** | +| DESKTOP-DLTAGOI | Sharon Edwards | 192.168.3.133 | **Win 11 Home** | WORKGROUP | None | Current | +| DESKTOP-H6QHRR7 | Sylvia Cuen | 10.0.20.235 | Win 11 Pro WS | cascades.local | None | Feb 2026 | +| DESKTOP-KQSL232 | Lois Lane | 10.0.20.227 | Win 10 Pro | WORKGROUP | None | **3mo behind** | +| DESKTOP-LPOPV30 | Karen Rossini | 192.168.2.250 | Win 10 Pro | WORKGROUP | **ON** | **13mo behind!** | +| DESKTOP-U2DHAP0 | Ashley (Accounting) | 192.168.3.37 | Win 11 Pro | WORKGROUP | **ON** | Feb 2026 | +| LAPTOP-DRQ5L558 | User (generic) | **10.0.50.141 (Guest!)** | **Win 10 Home** | WORKGROUP | None | Current | +| LAPTOP-E0STJJE8 | User (generic) | 10.0.20.200 | **Win 10 Home** | WORKGROUP | None | Current | +| LAPTOP2 | Training2 | 192.168.2.118 | Win 11 Pro | WORKGROUP | Encrypted, OFF | **8mo behind** | +| MAINTENANCE-PC | Bruce Miller | 192.168.3.156 | **Win 11 Home** | WORKGROUP | None | Current | +| MDIRECTOR-PC | Shelby Trozzi (MemCare Dir) | 10.0.20.71 | **Win 11 Home** | WORKGROUP | None | Current | +| MEMRECEPT-PC | memfrtdesk (MemCare Recept) | 192.168.3.41 | **Win 10 Home** | WORKGROUP | None | **4mo behind** | +| NURSESTATION-PC | Nurses (shared, no pwd) | 192.168.3.135 | Win 10 Pro WS | WORKGROUP | None | Current | +| RECEPTIONIST-PC | Front Desk (shared, no pwd) | 192.168.2.17 | Win 11 Pro | WORKGROUP | Encrypted, OFF | Feb 2026 | + +**Not audited (offline):** SALES4-PC, ASSISTNURSE-PC, DESKTOP-VAVKCIM, DESKTOP-TRCIEJA, DESKTOP-ROK7VNM, DESKTOP-MD6UQI3 + +Also on INTERNAL: 9 kitchen iPads (food order taking only, NOT medical — restrict to kitchen thermal printers), LG TV (10.0.20.234), staff phones + +## Synology NAS (cascadesds) — Shared Folders +| Share | Volume | Notes | +|-------|--------|-------| +| Activities | Volume 1 | | +| chat | Volume 1 | Synced to CS-SERVER | +| homes | Volume 1 | User home dirs, synced to CS-SERVER | +| Management | Volume 1 | Synced to CS-SERVER | +| pacs | Volume 1 | **Not in migration plan — verify purpose** | +| Public | Volume 1 | Synced to CS-SERVER | +| SalesDept | Volume 1 | Synced to CS-SERVER | +| Sandra Fish | Volume 1 | **Personal folder — verify if still needed** | +| Server | Volume 1 | Synced to CS-SERVER | +| web | Volume 1 | **Not in migration plan — verify purpose** | + +Storage: 371.6 GB used / 540.7 GB free (Volume 1, healthy) + +## Ring Security Cameras (8 units, on LAN) +| Device | IP | MAC | +|--------|-----|-----| +| RingStickUpCam | 192.168.2.61 | 90:48:6c:a8:d9:fb | +| RingStickUpCam | 192.168.2.129 | 5c:47:5e:40:e2:82 | +| RingStickUpCam | 192.168.2.252 | ac:9f:c3:86:5a:f4 | +| RingDoorbell | 192.168.3.49 | 90:48:6c:80:6a:f7 | +| RingStickUpCam | 192.168.3.95 | 90:48:6c:a9:14:2f | +| RingStickUpCam | 192.168.3.162 | ac:9f:c3:80:89:c2 | +| RingStickUpCam | 192.168.3.178 | 64:9a:63:1b:d7:0b | +| Ring | 192.168.3.233 | 90:48:6c:3a:dd:3e | + +## HIPAA Compliance +- **Primary project objective:** Get Cascades secure and HIPAA compliant (taken over from previous MSP) +- Clinical records: ALIS (cloud SaaS, https://www.go-alis.com/) — accessed via browser +- Local PHI: Synology NAS (cascadesDS) — migrating to CS-SERVER for proper access control + audit +- Kitchen iPads: NOT medical — food order taking only, need access to thermal receipt printers only +- Full compliance plan and gap analysis: `security/hipaa.md` + +## Notes +- Residential senior living facility with per-room VLAN isolation +- MemCare (Memory Care) wing spans floors 5-6 with dedicated networking +- Staff devices use INTERNAL VLAN (10.0.20.0/24) via CSCNet WiFi SSID +- Residents get isolated /28 VLANs per room +- Dining Room AP is busiest (26 clients) - heavy iPad usage for residents +- ~626 resident devices on room VLANs (123 DirecTV, 75 phones, 27 Apple devices) +- NurseAssist device at 192.168.3.254 (a8:6d:aa:51:d6:55) +- iDRAC for CS-SERVER at 192.168.2.65 (00:22:19:60:50:e3) diff --git a/clients/cascades-tucson/docs/printers.md b/clients/cascades-tucson/docs/printers.md new file mode 100644 index 0000000..ca65a54 --- /dev/null +++ b/clients/cascades-tucson/docs/printers.md @@ -0,0 +1,74 @@ +# Printers (Rental Units - Monitor and Track) + +## Printer Inventory + +| # | Location | IP Address | MAC/Node | Model | SN | Users | Connection | Status | +|---|----------|-----------|----------|-------|----|-------|-----------|--------| +| 1 | Front Desk | 192.168.2.147 | dc:cd:2f:82:2b:7a (epson822b74) | Epson ET-5800 | — | CJ (Cathy Kingston), Christina DuPras, Kyla, Tiffany | Network | Online | +| 2 | Accounting Assistant (Room 103 area) | 10.0.20.220 | — | Brother MFC-L8900CDW | U64646-B0J498444 | Lauren Hasselman, Allison Reibschied | Network | Online | +| 3 | Accounting | 192.168.3.227 | 80:a5:89:f6:71:9b (canonfb04b5) | Canon imageClass MF455DW | 3RD05874 | Lauren Hasselman | Network | Online | +| 4 | Room 103 | 192.168.2.145 | 5c:ea:1d:4e:96:af | Brother MFC-9340CDW | U63481D8J826812 | Ashley (Jensen), Christina (DuPras) | Network | Online | +| 5 | Room 132 (Rec Room) | 10.0.20.94 | — | Canon Color imageClass MF741CDW | — | Sharon Edwards (Life Education Asst), Susan Hicks | WiFi (INTERNAL VLAN) | Online — factory reset 2026-04-13 | +| 6 | Copy Room | 192.168.2.230 | 74:bf:c0:fd:7a:64 (canonfd7a64) | Canon imageRunner C478iF | — | Shared (all staff) | Wired | Online | +| 7 | Room 217 (Marketing) | 192.168.3.44 | 2c:9c:58:28:ec:9e (brw2c9c5828ec9e) | Brother MFC-L8900CDW | — | All of Sales | Network | Online | +| 8 | 206 Nurse Station | 10.0.20.69 | brw283a4d1ad571 | Brother MFC-L8900CDW | — | Whoever sits at NURSESTATION-PC | Network (fax, internet only) | Online | +| 9 | Room 206 (large printer) | 192.168.1.138 | 00:20:6b:b3:4a:55 | Konica Minolta Bizhub C368 | A7PV011016305 | Health Services | Network | Online | +| 10 | Kitchen Manager | 192.168.3.232 | — | Canon imageClass MFC743CDW | — | Alyssa (Brooks) | Network | Online | +| 11 | Chef | 192.168.3.88 | — | Brother MFC-9330CDW | — | Chef | Network | Online | +| 12 | MemCare Reception | — | — | Epson ET-5800 | — | MemCare Receptionist | Pending — need UniFi switch to replace dummy switch | Offline | +| 13 | MemCare Room 615 | 192.168.2.53 | c8:a3:e8:a2:dd:93 (brwc8a3e8a2dd93) | Brother (model TBD) | — | MedTechs, Nurses | WiFi (static IP) | Online | +| 14 | Meredith's Office | 192.168.2.67 | — | Canon imageClass MF743CDW | — | Meredith (Kuhn) | Network | Online | +| 15 | MemCare Director (Room 603) | 192.168.3.52 | 20:0b:74:b2:29:08 | Canon Color imageClass MF751CDW | — | Shelby Trozzi | Network | Online | + +## Previously Unidentified — Now Resolved + +| Old Entry | Resolved To | +|-----------|-------------| +| 192.168.2.145 (Location TBD) | Room 103 — Brother MFC-9340CDW (Ashley/Christina) | +| 192.168.3.44 (Location TBD) | Room 217 — Sales Brother printer | +| 192.168.2.53 (MemCare MedTech, missing from ARP) | MemCare MedTech printer — now online | +| 192.168.1.138 (206 Health Services, won't stay connected) | Room 206 — Bizhub C368 (was misidentified as Brother) | +| Kitchen (unknown model) | Kitchen Manager — Canon imageClass MFC743CDW at 192.168.3.232 (new IP) | +| Bizhub C368 (Location TBD) | Room 206 at 192.168.1.138 | + +## No Longer On Network / Unaccounted + +| Old Entry | IP | Notes | +|-----------|-----|-------| +| Marketing Brother MFC-L8900CDW | 192.168.2.21 | Not found onsite — may be decommissioned or moved. The accounting assistant printer at 10.0.20.220 is now the MFC-L8900CDW. | +| ~~Port 8 Epson~~ | ~~192.168.2.207~~ | **RESOLVED** — Kitchen Bistro thermal printer (Epson TM-T88VII) | +| Printer-80A423 (USW Lite 8 Port 2) | 192.168.2.202 | Unknown — verify onsite | +| Brother at 192.168.3.10 | 192.168.3.10 | Offline — verify onsite | + +## Known Issues +- **MemCare Reception Epson ET-5800**: Needs to be hardwired (currently not connected to network) +- **MemCare Room 615 printer** (192.168.2.53): WiFi only with static IP (not dual-connected as previously thought). No ethernet fix needed. +- **206 Nurse Station Brother** (10.0.20.69): Connected for fax/internet only — not used as network print share +- **4 printers unaccounted** — see "No Longer On Network" table above + +## Kitchen Thermal Receipt Printers + +Kitchen uses 2 thermal receipt printers for food order taking. Connected to 9 kitchen iPads. **Not HIPAA in scope** — food service only, no PHI. + +| # | Location | IP | MAC | Model | Connection | Status | +|---|----------|-----|-----|-------|-----------|--------| +| 1 | Bistro (specialty kitchen) | 192.168.2.207 | dc:cd:2f:22:09:69 | Epson TM-T88VII (M371A) | Ethernet | Online | +| 2 | Kitchen (back, cooks) | 10.0.20.225 | dc:cd:2f:f4:4e:e7 | Epson TM-U220IIB (M384B) | Ethernet | Online | + +**Note:** Kitchen iPads (9) should be restricted to these 2 printers only via firewall rules (see `security/hipaa.md`) + +## Resident Room Printers (found in DHCP) +| Room | IP | MAC | Hostname | +|------|-----|-----|----------| +| 146 (Floor 1) | 10.1.46.7 | — | Epson | +| 237 (Floor 2) | 10.2.37.9 | 38:1a:52:f6:5c:53 | EPSONF65C53 | +| 204 (Floor 2) | 10.2.4.8 | — | HP | +| 328 (Floor 3) | 10.3.28.14 | 88:51:fb:20:17:eb | HP181FE8 | +| 447 (Floor 4) | 10.4.47.3 | — | Brother | + +## Notes +- These printers are RENTAL units — must be monitored and tracked +- Jeff Bristol replaced by Lauren Hasselman as Business Office Director +- Printers on INTERNAL VLAN (10.0.20.x) or LAN (192.168.x.x) are accessible by staff via CSCNet +- Front Desk printer confirmed online (was offline on 2026-03-06, now working) +- Room 206 large printer was misidentified as Brother MFC-L8900CDW — it's actually a Konica Minolta Bizhub C368 diff --git a/clients/cascades-tucson/docs/proposals/m365-premium-upgrade.md b/clients/cascades-tucson/docs/proposals/m365-premium-upgrade.md new file mode 100644 index 0000000..29df5fb --- /dev/null +++ b/clients/cascades-tucson/docs/proposals/m365-premium-upgrade.md @@ -0,0 +1,151 @@ +# Proposal: Microsoft 365 Business Premium Upgrade +## Cascades Tucson — Shared Phone Deployment & Security Upgrade + +**Prepared by:** Howard Enos, MSP +**Date:** April 14, 2026 + +--- + +## The Problem + +Cascades is deploying 25 shared phones so employees can access ALIS (medical records) and email without relying on shared computers. This improves HIPAA compliance by giving each employee their own login. + +However, the current Microsoft 365 plan creates several challenges: + +1. **Every time an employee picks up a phone**, they must manually log into ALIS, Outlook, and other apps — each with separate MFA prompts +2. **No way to automatically clear data** when one employee hands a phone to another — risk of PHI exposure +3. **No way to skip MFA on trusted devices** — employees get frustrated, find workarounds, or share logins (HIPAA violation) +4. **No centralized phone management** — no remote wipe, no app enforcement, no compliance monitoring + +--- + +## The Solution: Microsoft 365 Business Premium + +Upgrading from Business Standard to Business Premium unlocks: + +### 1. Shared Phone Mode (Microsoft Intune) +- Employee picks up any phone and taps **Sign In** +- Enters their username and password (same as their PC login) +- **ALIS, Outlook, and Edge auto-sign-in** — no separate logins needed +- When done, employee taps **Sign Out** — phone wipes all data automatically +- Next employee gets a clean phone. **User switch takes ~30 seconds.** + +### 2. Skip MFA on Company Devices (Conditional Access) +- Managed phones on the Cascades network are automatically trusted +- **No MFA prompts** for employees using company phones at work +- MFA still required if someone tries to log in from an unknown device or location +- ALIS uses Microsoft Entra as its identity provider — **one sign-in covers everything** + +### 3. Full HIPAA Audit Trail +- Every sign-in logged: who, which device, when, where +- Compliance dashboard shows which devices meet security requirements +- Automatic alerts for non-compliant devices + +### 4. Additional Security (included at no extra cost) +| Feature | What it does | +|---------|-------------| +| **Microsoft Defender for Business** | Advanced threat protection on all PCs and phones | +| **Data Loss Prevention (DLP)** | Prevents PHI from being emailed or shared outside the organization | +| **Remote Wipe** | Instantly wipe a lost or stolen phone from the admin portal | +| **App Management** | Push required apps, block unauthorized apps, force updates | +| **Conditional Access** | Location and device-based security policies | + +--- + +## What Changes for Employees + +| Today | After Upgrade | +|-------|--------------| +| Pick up phone, manually log into each app | Pick up phone, sign in once — everything works | +| MFA prompt every time (SMS code from personal phone) | No MFA on company phones at work | +| Previous user's data may still be on the phone | Auto-wipe on sign out — clean phone every time | +| Log into ALIS separately with username + password + MFA | ALIS auto-signs in via SSO — no extra steps | +| 2-5 minutes to get working on a phone | ~30 seconds | + +--- + +## Cost Breakdown + +### Current Monthly Cost +| Item | Users | Rate | Monthly | +|------|-------|------|---------| +| M365 Business Standard | 34 | $12.50 | $425.00 | +| **Total** | | | **$425.00** | + +### Proposed Monthly Cost +| Item | Users | Rate | Monthly | +|------|-------|------|---------| +| M365 Business Premium | 23 | $22.00 | $506.00 | +| Unlicensed users (shared mailbox only) | 10 | $0.00 | $0.00 | +| **Total** | | | **$506.00** | + +### Savings from Cleanup (already planned) +| Item | Monthly Savings | +|------|----------------| +| Convert 11 role-based accounts to free shared mailboxes | -$137.50 | +| (accounting@, frontdesk@, hr@, security@, transportation@, etc.) | | + +### Net Impact +| | Monthly | +|--|---------| +| New cost | $506.00 | +| Current cost | $425.00 | +| Difference | +$81.00 | +| Shared mailbox savings | -$137.50 | +| **Net change** | **-$56.50 (savings)** | + +**The upgrade actually saves $56.50/month after the shared mailbox cleanup.** + +--- + +## What's Included vs. What's Replaced + +| Current (separate cost) | Premium (included) | +|------------------------|-------------------| +| ManageEngine MDM (phone management) | Microsoft Intune (replaces ManageEngine) | +| No email security | Microsoft Defender for Business | +| No data loss prevention | DLP policies | +| No conditional access | Conditional Access | +| Manual MFA every login | Smart MFA (skip on trusted devices) | +| No device compliance | Compliance dashboard + auto-remediation | + +--- + +## Implementation Timeline + +| Phase | Task | Timeframe | +|-------|------|-----------| +| 1 | Upgrade licenses in M365 admin center | 1 day | +| 2 | Convert role-based accounts to shared mailboxes | 1 day | +| 3 | Install Entra Connect on server (SSO) | 1 day | +| 4 | Configure Conditional Access policies | 1 day | +| 5 | Set up Intune + Shared Device Mode | 1-2 days | +| 6 | Enroll 2 test phones | 1 day | +| 7 | Roll out remaining 23 phones | 2-3 days | +| 8 | Enroll 9 kitchen iPads | 1 day | +| **Total** | | **~2 weeks** | + +--- + +## HIPAA Compliance Improvements + +| HIPAA Requirement | Current State | After Upgrade | +|------------------|--------------|--------------| +| §164.312(a)(2)(i) — Unique User ID | Shared accounts on PCs, no phone identity | Every employee signs in with their own account on every device | +| §164.312(d) — Person Authentication | Basic MFA, employees share logins to avoid it | SSO + Conditional Access — easy to use, hard to bypass | +| §164.312(b) — Audit Controls | Minimal logging | Full sign-in logs: who, when, which device, what was accessed | +| §164.310(d)(1) — Device and Media Controls | No phone management | Remote wipe, encryption enforcement, compliance monitoring | +| §164.312(e)(1) — Transmission Security | No DLP | DLP prevents PHI from leaving the organization via email | +| §164.312(a)(2)(iv) — Encryption | Not enforced on phones | Intune enforces device encryption on all managed phones | + +--- + +## Recommendation + +Upgrade to Microsoft 365 Business Premium. It solves the shared phone problem, simplifies employee experience, strengthens HIPAA compliance, and **actually saves money** after the planned shared mailbox cleanup. + +The alternative — keeping Business Standard with a separate MDM product — costs more in admin time, provides a worse employee experience, and leaves security gaps that Business Premium closes out of the box. + +--- + +*Prepared by Howard Enos — MSP IT Services* diff --git a/clients/cascades-tucson/docs/rmm/rmm.md b/clients/cascades-tucson/docs/rmm/rmm.md new file mode 100644 index 0000000..a15b2e5 --- /dev/null +++ b/clients/cascades-tucson/docs/rmm/rmm.md @@ -0,0 +1,25 @@ +# RMM / Monitoring + +## RMM Solution +- Product: SyncroRMM (primary, migrating to) +- Previous RMM: Datto RMM (migrating away from) +- Console URL: https://cascades.syncromsp.com (verify exact URL) +- Migration Status: In progress — transitioning from Datto RMM to Syncro + +## Remote Access +- Product: ScreenConnect (ConnectWise Control) +- Deployed: All machines + +## Agent Deployment +- Total Devices: TBD (verify in Syncro console) +- Servers Monitored: TBD +- Workstations Monitored: TBD +- Network Devices Monitored: TBD + +## Migration Notes +- Datto RMM agents may still be installed on some machines alongside Syncro +- Once migration is complete, remove all Datto RMM agents +- ScreenConnect is independent and stays regardless of RMM platform + +## Notes +- Syncro also provides access to Bitdefender GravityZone and Emsisoft for antivirus (see security/antivirus.md for AV decision) diff --git a/clients/cascades-tucson/docs/security/antivirus.md b/clients/cascades-tucson/docs/security/antivirus.md new file mode 100644 index 0000000..190f349 --- /dev/null +++ b/clients/cascades-tucson/docs/security/antivirus.md @@ -0,0 +1,66 @@ +# Endpoint Security / Antivirus + +## Current State (In Transition) +- Current Product: Datto EDR (part of Datto RMM suite) +- Status: **Migrating away** — Datto RMM being replaced by SyncroRMM +- Datto EDR will need to be replaced when migration completes +- **HIPAA:** §164.308(a)(5) requires security awareness and §164.312(a) requires access control. EDR/AV is a critical control for protecting PHI on staff workstations that access ALIS and file shares. + +## Available Options Through Syncro +- Bitdefender GravityZone — available, Howard does NOT prefer this +- Emsisoft — available through Syncro + +## Recommended: Huntress + SentinelOne (via Syncro) +See notes section for full recommendation. + +## Deployment Status (audit 2026-03-20) +- Total Endpoints: 19 (1 server + 18 workstations) +- **Datto AV:** 17 machines (enabled and up to date on most) +- **Bitdefender + Datto AV (conflict):** RECEPTIONIST-PC — dual AV running +- **COMODO AV (disabled):** MDIRECTOR-PC — Windows Defender active instead +- **McAfee LiveSafe (bloatware):** LAPTOP-E0STJJE8 — conflicts with Datto +- **Malwarebytes (alongside Datto):** CRYSTAL-PC, MAINTENANCE-PC +- **Windows Defender active:** MDIRECTOR-PC (only machine using Defender as primary) + +### Issues +| Machine | Issue | +|---------|-------| +| RECEPTIONIST-PC | Bitdefender + Datto AV both running — pick one | +| LAPTOP-E0STJJE8 | McAfee LiveSafe + WebAdvisor installed — remove | +| MDIRECTOR-PC | COMODO AV disabled, stale — remove | +| LAPTOP-DRQ5L558 | Multiple Datto AV instances, mixed enabled/disabled | +| LAPTOP-E0STJJE8 | Multiple Datto AV instances, mixed enabled/disabled | + +### Previous MSP Software (on ALL machines — remove) +- Splashtop Streamer — on every machine +- Datto RMM agent — on CS-SERVER (at minimum) +- N-able Take Control — on some machines (stopped/stuck services) + +## Notes +### Antivirus Recommendation for Syncro Integration + +**Best option: Huntress + SentinelOne** + +**SentinelOne (Singularity)** +- Native Syncro integration (built-in, deploy from Syncro) +- Full autonomous EDR — detects AND responds without human intervention +- Rollback capability (ransomware recovery) +- Consistently top-rated in independent AV tests +- Per-agent MSP pricing available +- Much stronger detection engine than Bitdefender GZ or Emsisoft + +**Huntress (Managed Threat Detection)** +- Native Syncro integration +- Managed by Huntress SOC team — they investigate alerts FOR you +- Catches what traditional AV misses (persistent footholds, LOLbins, lateral movement) +- Lightweight agent runs alongside any AV +- Built specifically for MSPs +- 24/7 human threat hunters review detections before alerting you + +**Why both?** +- SentinelOne = prevention + automated response (replaces Datto EDR) +- Huntress = detection + managed investigation (adds a layer Datto EDR never had) +- Together they cover the full kill chain with minimal MSP effort +- Both have one-click deploy through Syncro + +**If only one:** SentinelOne alone is a strong standalone choice and integrates directly with Syncro's policy management. It's a significant upgrade over Datto EDR, Bitdefender GZ, and Emsisoft in both detection quality and automation. diff --git a/clients/cascades-tucson/docs/security/backup.md b/clients/cascades-tucson/docs/security/backup.md new file mode 100644 index 0000000..4685d4d --- /dev/null +++ b/clients/cascades-tucson/docs/security/backup.md @@ -0,0 +1,85 @@ +# Backup and Disaster Recovery + +## Backup Solution +- Product: **NONE CURRENTLY** — implementation planned as Phase 0 of network migration (Session 3, 2026-03-07) +- Priority: **CRITICAL** — no backups means no recovery from ransomware, hardware failure, or accidental deletion +- **HIPAA:** §164.308(a)(7) requires contingency plan including backup. Synology NAS and CS-SERVER both store PHI. No backup = regulatory violation. +- See `migration/session3-2026-03-07.md` for detailed setup steps + +## Migration Plan — Backup Implementation (Phase 0.1 + Phase 4.4) + +See `migration/phase0-safety-net.md`. + +### Phase 0.1: Synology Active Backup for Business + +| Setting | Value | +|---------|-------| +| Product | Synology Active Backup for Business (free) | +| Target | Synology NAS (192.168.0.120), Volume 1 | +| Source | CS-SERVER C: and D: drives (entire machine) | +| Agent | ABB Windows agent on CS-SERVER | +| Schedule | Nightly at 2:00 AM | +| Retention | 7 daily + 4 weekly | +| Compression | Enabled | +| Transfer Encryption | Enabled | + +#### Storage Capacity Analysis + +| Item | Size | +|------|------| +| Synology Volume 1 free space | ~540 GB | +| CS-SERVER C: used | ~137 GB | +| CS-SERVER D: used | ~455 GB | +| Total data to back up | ~592 GB | +| Expected after ABB compression (40-60%) | ~240-355 GB | +| Estimated remaining after first backup | ~185-300 GB | + +ABB automatically excludes pagefile, hibernation file, and temp files. With compression and dedup, first full backup should fit. Incrementals will be small (daily changes are minimal). Monitor after first backup. + +### Phase 4.4: Offsite Backup + +| Setting | Value | +|---------|-------| +| Product | Synology Hyper Backup | +| Target | Backblaze B2 or Wasabi (~$3/mo) | +| Schedule | Daily after ABB completes (e.g., 5:00 AM) | +| Retention | 30 daily + 12 monthly | + +## Available Backup Targets +| Target Name | Type | Location | Details | +|----------------|--------------|-----------|----------------------| +| Synology NAS | Local NAS | On-site | cascadesds / synology.cascades.local, IP: 192.168.0.120 | +| CS-SERVER | Server RAID | On-site | 192.168.2.254, has RAID storage | + +## Backup Jobs +- None configured (Phase 0 will establish first backup) + +## M365 Backup +- M365 Backup Product: None +- Exchange Backed Up: No +- SharePoint Backed Up: No +- OneDrive Backed Up: No +- Teams Backed Up: No + +## Disaster Recovery Plan +- RTO Target: Not defined +- RPO Target: Not defined +- DR Site: None +- Last DR Test Date: N/A + +## Notes +### Backup Implementation Recommendations + +**For servers/workstations (on-prem):** +- Synology Active Backup for Business — free with the Synology, backs up Windows PCs and servers to the NAS +- Or Datto BCDR / Axcient x360Recover for full BDR with cloud replication + +**For M365:** +- Datto SaaS Protection, Veeam Backup for M365, or Acronis — protects Exchange, SharePoint, OneDrive, Teams + +**Minimum viable backup plan (HIPAA required):** +1. Enable Synology Active Backup for Business (free, already have the hardware) ← Phase 0 +2. Back up CS-SERVER and critical workstations to the Synology nightly ← Phase 0 +3. Add an M365 backup solution for email/SharePoint (email may contain PHI) +4. Configure Synology Hyper Backup to replicate critical data to a cloud target ← Phase 4 +5. After Phase 4: enable NTFS audit logging on PHI shares migrated from Synology diff --git a/clients/cascades-tucson/docs/security/hipaa.md b/clients/cascades-tucson/docs/security/hipaa.md new file mode 100644 index 0000000..bda2a63 --- /dev/null +++ b/clients/cascades-tucson/docs/security/hipaa.md @@ -0,0 +1,107 @@ +# HIPAA Compliance — Cascades + +## Why HIPAA Applies + +Cascades is an assisted living facility with health services staff (nurses, medtechs, health services director). They handle Protected Health Information (PHI) through: + +1. **ALIS** (https://www.go-alis.com/) — cloud-hosted clinical/medical records system, accessed via web browser on staff PCs +2. **Synology NAS (cascadesDS)** — stores resident/facility data locally that falls under HIPAA +3. **CS-SERVER file shares** — migration target for Synology data; will become the primary secured storage +4. **M365 email** — staff may send/receive resident-related information via cascadestucson.com email + +## Project Mission + +Cascades was taken over from a previous MSP that left the environment insecure and non-compliant. The core objective of the migration project is to **get Cascades secure and HIPAA compliant**. Every migration phase ties back to this goal. + +## Current HIPAA Gaps + +| # | Gap | Severity | HIPAA Rule | Migration Phase | +|---|-----|----------|------------|-----------------| +| 1 | **No backup exists** | Critical | §164.308(a)(7) — Contingency Plan | Phase 0 (WSB → Synology) + Phase 4 (offsite) | +| 2 | **Synology stores PHI with no access auditing** | Critical | §164.312(b) — Audit Controls | Phase 4 (move to CS-SERVER with NTFS audit) | +| 3 | **Shared accounts** (Receptionist, Culinary, saleshare, directoryshare) | High | §164.312(a)(2)(i) — Unique User ID | Phase 5 (replace with individual accounts) | +| 4 | **No MFA on M365** | High | §164.312(d) — Person Authentication | Can enable now (Security Defaults, free) | +| 5 | **No disk encryption (BitLocker)** | High | §164.312(a)(2)(iv) — Encryption | Phase 2.6 GPO (free with Windows Pro) | +| 6 | **Permissive floating firewall rule** | High | §164.312(e)(1) — Transmission Security | Phase 1.6 (post-migration lockdown) | +| 7 | **Non-IT staff in Domain Admins** | High | §164.312(a)(1) — Access Control | Phase 2.2 (remove Meredith.Kuhn, John.Trozzi) | +| 8 | **Most PCs not domain-joined** | Medium | §164.308(a)(3) — Workforce Security | Phase 3 (domain join all staff PCs) | +| 9 | **No GPOs enforced** (password policy, screen lock) | Medium | §164.308(a)(5) — Security Awareness | Phase 2.6 (Security Baseline GPO) | +| 10 | **Kitchen iPads on same VLAN as staff PCs** | Medium | §164.312(e)(1) — Transmission Security | Restrict iPads to kitchen printers only | +| 11 | **ALIS browser access on shared PCs** | Medium | §164.312(d) — Person Authentication | Phase 5 (individual logins, no shared accounts) | +| 12 | **No BAA verified with ALIS** | Medium | §164.308(b)(1) — Business Associates | Verify with management | +| 13 | **No BAA with Microsoft (M365)** | Medium | §164.308(b)(1) — Business Associates | Sign Microsoft BAA via M365 admin | +| 14 | **Sandra Fish still global admin** | Low | §164.308(a)(3) — Workforce Security | Create break-glass admin, remove Sandra | +| 15 | **No M365 backup** | Low | §164.308(a)(7) — Contingency Plan | Future — Veeam Backup for M365 | + +## How Migration Phases Address HIPAA + +| Phase | What It Does | HIPAA Controls Addressed | +|-------|-------------|------------------------| +| Phase 0 — Safety Net | Windows Server Backup → Synology SMB share | Backup, contingency plan | +| Phase 1 — Network | VLAN migration, firewall lockdown, guest isolation | Transmission security, access control | +| Phase 2 — Server Prep | AD cleanup, security groups, GPOs (BitLocker, passwords, screen lock) | Access control, audit, encryption, unique user ID | +| Phase 3 — Domain Join | All staff PCs under centralized management | Workforce security, device management | +| Phase 4 — Synology Retirement | Move data to CS-SERVER with NTFS permissions + audit logging | Audit controls, access control, integrity | +| Phase 5 — Hardening | Remove shared accounts, RDS cleanup, final lockdown | Unique user ID, person authentication | + +## Systems and PHI Flow + +``` +Nurses/MedTechs (staff PCs) + │ + ├──► ALIS (cloud, go-alis.com) — clinical/medical records + │ └── ALIS responsible for their own HIPAA compliance + BAA + │ + ├──► Synology NAS (cascadesDS, 192.168.0.120) — resident/facility data (MOVING TO CS-SERVER) + │ + ├──► CS-SERVER (192.168.2.254) — file shares, AD, DNS (migration target) + │ + └──► M365 (cascadestucson.com) — email, may contain PHI in messages/attachments +``` + +## Non-PHI Systems (out of HIPAA scope) + +| System | Purpose | Notes | +|--------|---------|-------| +| Kitchen iPads (9 units) | Food order taking | No PHI — only need access to kitchen thermal receipt printers. **Managed via ManageEngine MDM** | +| Kitchen thermal printers | Receipt printing | Bistro (TM-T88VII, 192.168.2.207) + Kitchen (TM-U220IIB, 10.0.20.225) | +| Resident room VLANs | Resident personal devices (TVs, phones) | No PHI — isolated /28 per room | +| Ring cameras (8 units) | Security cameras | No PHI | +| GoDaddy | Website hosting (cascadestucson.com) | Public website, no PHI | + +## New Findings from Audit (2026-03-20) + +| # | Gap | Severity | HIPAA Rule | Notes | +|---|-----|----------|------------|-------| +| 16 | **3 shared accounts with no password** (Nurses, memfrtdesk, Front Desk) — these PCs access ALIS | Critical | §164.312(a)(2)(i) — Unique User ID | NURSESTATION-PC, MEMRECEPT-PC, RECEPTIONIST-PC | +| 17 | **No audit logging on CS-SERVER** (Object Access = No Auditing) | Critical | §164.312(b) — Audit Controls | Cannot track who accessed PHI files | +| 18 | **13 months without Windows updates** on DESKTOP-LPOPV30 | High | §164.308(a)(1) — Security Management | 6 machines 3+ months behind | +| 19 | **Expired SSL certificate** on CS-SERVER (2025-04-02) | High | §164.312(e)(1) — Transmission Security | Causes Schannel errors | +| 20 | **krbtgt password 569 days old** | High | §164.312(a)(1) — Access Control | Should rotate every 180 days | +| 21 | **RDP without NLA** on ASSISTMAN-PC, DESKTOP-U2DHAP0 | High | §164.312(e)(1) — Transmission Security | Credential exposure risk | +| 22 | **TightVNC on MEMRECEPT-PC** | High | §164.312(a)(1) — Access Control | Unauthorized remote access tool | +| 23 | **No LAPS** — same local admin password on all machines | Medium | §164.312(a)(1) — Access Control | Lateral movement risk | +| 24 | **RestrictAnonymous = 0** on CS-SERVER | Medium | §164.312(a)(1) — Access Control | Null sessions allowed | +| 25 | **Protected Users group empty** | Medium | §164.312(a)(1) — Access Control | Admin accounts not protected | +| 26 | **Share permissions: Everyone=FullControl** on multiple shares | Medium | §164.312(a)(1) — Access Control | Culinary, directoryshare, Roaming | + +## Quick Wins (Free, Can Do Now) + +1. **Enable MFA on M365** — Security Defaults in Entra ID (free, takes 5 minutes) +2. **Sign Microsoft BAA** — M365 Admin Center → Settings → Org Settings → Security & Privacy → HIPAA BAA +3. **Verify ALIS BAA** — Ask management if they have a signed BAA with go-alis.com +4. **BitLocker GPO** — Enable via Security Baseline GPO once PCs are domain-joined (Phase 2.6) + +## Recommendations (Paid) + +| Service | Why | Cost | Priority | +|---------|-----|------|----------| +| Veeam Backup for M365 | Protect email/OneDrive containing PHI | ~$2-4/user/mo | Medium | +| Business Premium upgrade | DLP (prevent PHI in outbound email), Defender, Conditional Access | +$10/user/mo (~$340/mo net after shared mailbox savings) | Low — most gaps covered by free controls | + +## Notes + +- Cascades is assisted living, not a hospital — but nurses and medtechs handle PHI, making HIPAA applicable +- Previous MSP left the environment non-compliant — this project is a remediation effort +- ALIS handles the heavy clinical data in the cloud — local HIPAA focus is on access control, backup, encryption, and audit trails +- Kitchen area (iPads, thermal printers) is out of HIPAA scope — food service only diff --git a/clients/cascades-tucson/docs/security/mdm.md b/clients/cascades-tucson/docs/security/mdm.md new file mode 100644 index 0000000..e2452bb --- /dev/null +++ b/clients/cascades-tucson/docs/security/mdm.md @@ -0,0 +1,193 @@ +# Mobile Device Management — Cascades + +## Product +- **Platform:** ManageEngine Mobile Device Manager Plus +- **URL:** https://mdm.manageengine.com/ +- **Account:** Created (setup pending) +- **Future consideration:** Microsoft Intune Shared Device Mode (requires Business Premium upgrade, ~+$10/user/mo). Enables per-user sign-in/sign-out with automatic data wipe. Better HIPAA audit trail at device level. Revisit when budget allows. + +## Device Inventory +- **25 Android phones** — shared among employees (rotation model) +- **9 Kitchen iPads** — food service only, no PHI +- **Mode:** Device Owner (fully managed), shared device, no OS-level users +- **Kiosk:** Multi-app kiosk mode + +## Phase 0 — Baseline Decision + +| Setting | Value | +|---------|-------| +| Devices | Android (Zero-touch supported) | +| Mode | Device Owner (fully managed) | +| Usage | Shared device (no OS-level users) | +| Control | Kiosk mode (multi-app) | +| HIPAA audit trail | Application layer (ALIS login, browser sign-in) — not device level | + +## Phase 1 — Prep MDM Environment + +### 1.1 Configure MDM Tenant +- [ ] Set organization name (Cascades) +- [ ] Create admin accounts +- [ ] Configure email/SMS notification settings + +### 1.2 Create Device Groups +| Group | Purpose | +|-------|---------| +| Cascades-Shared-Phones | 25 employee phones | +| Cascades-Kitchen-iPads | 9 kitchen iPads | +| Cascades-Test-Devices | 1-2 test devices | + +### 1.3 Upload Apps to App Repository +- [ ] ALIS (EHR / medical records — go-alis.com, browser-based) +- [ ] Secure browser (if needed beyond Chrome) +- [ ] Microsoft Authenticator (if MFA required) +- [ ] Outlook (for shared mailbox access via SSO — future) + +### 1.4 Build Baseline Policies + +#### Security Policy +- Passcode required (6+ digits) +- Auto-lock: 2-5 minutes +- Encryption: ON +- Disable: + - USB file transfer + - Unknown app installs + - Developer options + +#### Restrictions Policy +- Disable: + - Camera (if required by compliance) + - Bluetooth (optional) + - Screen capture +- Block personal Google accounts + +#### App Policy +- Silent install required apps +- Force updates +- Prevent uninstall + +#### Data Protection Policy +- Clear app data on logout (if supported) +- Disable copy/paste between apps +- Block cloud backups + +#### Kiosk Profile (CRITICAL) +Multi-app kiosk mode — allow ONLY: +- Medical app (ALIS via browser) +- Browser (limited) +- Settings (optional, limited) + +This turns the phone into a work terminal. + +## Phase 2 — Zero-Touch Enrollment + +### 2.1 Register with Android Zero-Touch +- URL: https://enterprise.google.com/android/zero-touch/ +- [ ] Link reseller (Verizon, AT&T, etc.) +- [ ] Add ManageEngine as EMM provider +- [ ] Use ManageEngine's EMM config + +### 2.2 Create Zero-Touch Configuration +In Zero-touch portal: +- EMM: ManageEngine +- Enrollment profile: Fully managed device, Device Owner mode +- Auto-assign to all 25 devices + +### 2.3 Link Zero-Touch to ManageEngine +- [ ] Go to Enrollment > Android > Zero-touch in MDM +- [ ] Paste configuration details + +**Result:** Phone powers on > connects to WiFi > auto-enrolls into ManageEngine > gets policies + apps + kiosk mode. No manual setup per device. + +## Phase 3 — Device Staging + +When phones arrive: +1. Unbox +2. Power on +3. Connect to WiFi + +**Automatic:** +- Device contacts Google +- Pulls Zero-touch config +- Enrolls into ManageEngine +- Receives: policies, apps, kiosk mode + +No manual setup needed per device. + +## Phase 4 — Testing (DO NOT SKIP) + +Test with 1-2 devices first: +- [ ] Auto enrollment works +- [ ] Apps install correctly +- [ ] Kiosk locks properly +- [ ] Cannot exit kiosk +- [ ] No personal account access +- [ ] Device wipes correctly from MDM +- [ ] ALIS login/logout works per user +- [ ] Browser doesn't save passwords or cookies + +## Phase 5 — HIPAA Workflow + +### 5.1 App Login Behavior +- Require unique user login to ALIS +- MFA if possible +- Auto logout after 5-10 min idle + +### 5.2 Session Control +- Browser: disable saved passwords, clear cookies on exit +- Apps: disable offline storage if possible + +### 5.3 Physical Device Labels +Label each phone: "Cascades Device 01" through "Cascades Device 25" +- Helps auditing + troubleshooting + +## Phase 6 — Monitoring & Control + +In ManageEngine MDM: +- Track: device compliance, app usage, last check-in, security status +- Enable: remote lock, remote wipe, lost mode + +## Phase 7 — Ongoing Maintenance + +| Frequency | Task | +|-----------|------| +| Weekly | Check compliance dashboard, review failed devices | +| Monthly | Update apps, review security policies | +| As needed | Remote wipe lost/stolen, add/remove apps | + +## Kitchen iPads (9 units) + +Separate from phones — food service only, no PHI. + +### Policies +- Kiosk/lockdown mode (food ordering app only) +- Restrict to kitchen thermal printers only (Bistro 192.168.2.207, Kitchen 10.0.20.225) +- No browser/email/app store access +- WiFi profile: CSCNet (INTERNAL VLAN 20) only + +### Enrollment +- [ ] Create iOS/iPadOS enrollment profile +- [ ] Apple DEP or manual enrollment (iPads may not support zero-touch without Apple Business Manager) + +## Future Upgrades +| Upgrade | Benefit | Requires | +|---------|---------|----------| +| SSO Integration (Entra ID) | Faster logins, better audit trails | Entra Connect (planned) | +| Microsoft Intune Shared Device Mode | Per-user sign-in/sign-out with auto data wipe | Business Premium (~+$10/user/mo) | +| Per-app VPN | Encrypt only medical app traffic | VPN gateway | +| Audit logging | Track who logged in from which device | App-level or Intune | + +## Common Mistakes to Avoid +- Skipping kiosk mode +- Allowing Google accounts +- Not enforcing auto logout +- Testing on all 25 at once +- Letting users store data locally + +## Setup Status +- [ ] Phase 1 — MDM tenant setup +- [ ] Phase 2 — Zero-touch enrollment +- [ ] Phase 3 — Device staging +- [ ] Phase 4 — Testing (1-2 devices) +- [ ] Phase 5 — HIPAA workflow +- [ ] Phase 6 — Monitoring enabled +- [ ] Phase 7 — Ongoing maintenance schedule diff --git a/clients/cascades-tucson/docs/servers/active-directory.md b/clients/cascades-tucson/docs/servers/active-directory.md new file mode 100644 index 0000000..3cccd37 --- /dev/null +++ b/clients/cascades-tucson/docs/servers/active-directory.md @@ -0,0 +1,329 @@ +# Active Directory — cascades.local + +## Domain Info (audit 2026-03-20) +- Domain: cascades.local (NetBIOS: CASCADES) +- Forest Functional Level: Windows2016Forest +- Domain Functional Level: Windows2016Domain +- Domain Controllers: CS-SERVER (192.168.2.254) — **ONLY DC** (all FSMO roles) +- Sites: Default-First-Site-Name +- No trusts configured + +## AD Users (42 total — 40 enabled, 2 disabled) — cleaned 2026-04-13 + +**New since last doc update:** Allison Reibschied (2026-03-13), Lauren Hasselman (2026-02-26) + +### Enabled Accounts — HR Roster (updated 2026-04-13) +| Name | SamAccountName | Position | Department | Shared Email | Notes | +|------|---------------|----------|------------|-------------|-------| +| Administrator | Administrator | — | — | — | Built-in | +| localadmin | localadmin | — | — | — | Local admin | +| Sysadmin | sysadmin | — | — | — | System admin | +| Howard Dax | howard | Home Office | Administrative | first.last@ | MSP technician | +| Meredith Kuhn | Meredith.Kuhn | Executive Director | Administrative | first.last@ | | +| John Trozzi | John.Trozzi | Maintenance Director | Maintenance | first.last@ | PC: MAINTENANCE-PC | +| Lupe Sanchez | Lupe.Sanchez | Housekeeping Director | Housekeeping | first.last@ | Renamed from Guadalupe.Sanchez, duplicate deleted (2026-04-13) | +| Megan Hiatt | Megan.Hiatt | Sales Director | Marketing | first.last@, Sales@ | | +| Crystal Rodriguez | Crystal.Rodriguez | Sales Associate | Marketing | first.last@, Sales@ | PC: CRYSTAL-PC | +| Tamra Matthews | Tamra.Matthews | Move-In Coordinator | Marketing | first.last@ | Renamed from Tamra.Johnson (2026-04-13) | +| Lois Lane | Lois.Lane | Health Services Director | Care, Assisted Living | first.last@, Nurses@ | | +| Christina DuPras | Christina.DuPras | Resident Services Director | Resident Services | first.last@ | | +| Christine Nyanzunda | Christine.Nyanzunda | Memory Care Admin Assistant | Care, Memory Care | first.last@ | | +| Susan Hicks | Susan.Hicks | Life Enrichment Director | Life Enrichment | first.last@ | PC: DESKTOP-ROK7VNM | +| Ashley Jensen | Ashley.Jensen | Assistant Executive Director | Administrative | first.last@, Accounting@ | | +| Veronica Feller | Veronica.Feller | Care, Assisted Living Aide | Care, Assisted Living | first.last@ | | +| Sebastian Leon | Sebastian.Leon | RS Courtesy Patrol | Resident Services | Frontdesk@, Courtesypatrol@ | | +| JD Martin | JD.Martin | Culinary Director | Culinary | first.last@ | | +| Alyssa Brooks | Alyssa.Brooks | Dining Manager | Culinary | first.last@ | Renamed from Alyssa.Shestko, duplicate deleted (2026-04-13) | +| Matt Brooks | Matt.Brooks | Memory Care Receptionist | Maintenance | first.last@ | Dept says Maintenance (HR data) | +| Ramon Castaneda | Ramon.Castaneda | Kitchen Manager | Culinary | first.last@ | | +| Michelle Shestko | Michelle.Shestko | Resident Services Receptionist | Resident Services | MC Front Desk | | +| Sharon Edwards | Sharon.Edwards | Life Enrichment Assistant | Life Enrichment | first.last@ | PC: DESKTOP-DLTAGOI | +| Britney Thompson | britney.thompson | Memory Care Nurse | Care, Assisted Living | first.last@, Nurses@ | lowercase SamAccountName | +| Shelby Trozzi | Shelby.Trozzi | Memory Care Director | Care, Memory Care | first.last@ | Renamed from strozzi (2026-04-13) | +| Karen Rossini | karen.rossini | Health Services Manager | Care, Assisted Living | first.last@, Nurses@ | lowercase SamAccountName | +| Sheldon Gardfrey | Sheldon.Gardfrey | RS Courtesy Patrol | Resident Services | Frontdesk@, Courtesypatrol@ | | +| Cathy Kingston | Cathy.Kingston | Resident Services Receptionist | Resident Services | Frontdesk@ | | +| Shontiel Nunn | Shontiel.Nunn | Resident Services Receptionist | Resident Services | Frontdesk@ | | +| Ray Rai | Ray.Rai | RS Courtesy Patrol | Resident Services | Frontdesk@ | | +| Richard Adams | Richard.Adams | Driver | Transportation | Transportation@ | | +| Julian Crim | Julian.Crim | Driver | Transportation | Transportation@ | | +| Christopher Holick | Christopher.Holick | Driver | Transportation | Transportation@ | Fixed from Holik (2026-04-13) | +| Lauren Hasselman | lauren.hasselman | Business Office Director | Administrative | first.last@, Accounting@ | Replaced Jeff Bristol. lowercase SamAccountName | +| Allison Reibschied | Allison.Reibschied | Accounting Assistant | Administrative | first.last@ | Added 2026-03-13. PC: ACCT2-PC | +| QBDataServiceUser34 | QBDataServiceUser34 | — | — | — | QuickBooks service account | +| Culinary | Culinary | — | — | — | Generic department account — replace Phase 5 | +| RECEPTIONIST | Receptionist | — | — | — | Generic role account — replace Phase 5 | +| saleshare | saleshare | — | — | — | Shared sales resource — replace Phase 5 | +| directoryshare | directoryshare | — | — | — | Shared directory resource — replace Phase 5 | + +### Not in AD — Needs Account Created +| Name | Position | Department | Shared Email | Notes | +|------|----------|------------|-------------|-------| +| Kyla Quick Tiffany | Resident Services Receptionist | Resident Services | Frontdesk@ | New — needs AD + M365 account | + +### Accounts Deleted (2026-04-13 cleanup) +Anna.Pitzlin, Nela.Durut-Azizi, Jodi.Ramstack, Monica.Ramirez, Haris.Durut, Nuria.Diaz, Cathy.Reece, Kelly.Wallace, Isabella.Islas, ann.dery, alyssa.brooks (duplicate), Lupe.Sanchez (duplicate), jeff.bristol + +### Disabled Accounts (2) — cleaned 2026-04-13 +| Name | SamAccountName | Notes | +|------|---------------|-------| +| Guest | Guest | Built-in — correct to leave disabled | +| krbtgt | krbtgt | Built-in Kerberos — correct to leave disabled. **Password 569+ days old — needs rotation** | + +## Domain-Joined Computers (8) + +### OU=Domain Controllers +| Computer | Role | +|----------|------| +| CS-SERVER | Primary DC, File Server, Hyper-V host | + +### CN=Computers (default) +| Computer | Role | +|----------|------| +| CS-QB | Hyper-V VM — VoIP server | + +### OU=Staff PCs,OU=Workstations (moved 2026-04-13) +| Computer | User | Role | +|----------|------|------| +| ACCT2-PC | Allison Reibschied | Accounting | +| CRYSTAL-PC | Crystal Rodriguez | Sales Associate | +| DESKTOP-H6QHRR7 | Sylvia Cuen | Staff workstation | +| DESKTOP-1ISF081 | TBD | Unknown — needs identification | +| DESKTOP-DLTAGOI | Sharon Edwards | Life Enrichment Assistant | +| DESKTOP-ROK7VNM | Susan Hicks | Life Enrichment Director | + +### Missing from AD (listed in overview but NOT domain-joined) +- **SALES4-PC** — Sales workstation (10.0.20.203) — NOT in AD +- **CHEF-PC** — Kitchen workstation (10.0.20.232) — NOT in AD +- **MDIRECTOR-PC** — MemCare Director (192.168.3.20) — NOT in AD +- **DESKTOP-KQSL232** — Unknown (10.0.20.227) — NOT in AD + +These 4 machines are on the network but not domain-joined. They may be workgroup machines or were never joined to the domain. + +## Organizational Units + +### Current State (pre-cleanup) +``` +cascades.local +├── Builtin (system) +├── Computers (default container) ← 5 PCs here: ACCT2-PC, CRYSTAL-PC, CS-QB, DESKTOP-1ISF081, DESKTOP-H6QHRR7 +├── Users (default container) ← 20 accounts dumped here (system + stale + needs placement) +├── Domain Controllers +│ └── CS-SERVER +├── Managment ← MISSPELLED, empty — DELETE +├── Sales ← empty — DELETE +├── MemCare ← empty — DELETE +├── Administrative ← ROOT DUPLICATE of Departments\Administrative — DELETE +├── Care-Assisted Living ← ROOT DUPLICATE — DELETE +├── Care-Memorycare ← ROOT DUPLICATE — DELETE +├── Culinary ← ROOT DUPLICATE — DELETE +├── Housekeeping ← ROOT DUPLICATE — DELETE +├── Life Enrichment ← ROOT DUPLICATE — DELETE +├── Maintenance ← ROOT DUPLICATE — DELETE +├── Marketing ← ROOT DUPLICATE — DELETE +├── Resident Services ← ROOT DUPLICATE — DELETE +├── Transportation ← ROOT DUPLICATE — DELETE +└── Departments + ├── Administrative (6 users) + ├── Care-Assisted Living (4 users) + │ └── Nurses (sub-OU, empty) + ├── Care-Memorycare (2 users) + ├── Culinary (4 users) + ├── Housekeeping (1 user) + ├── Life Enrichment (2 users) + ├── Maintenance (2 users) + ├── Marketing (4 users) + ├── Resident Services (7 users) + └── Transportation (3 users) +``` + +### Target State (after cleanup — Phase 2.1 + 2.2) +``` +cascades.local +├── Builtin (system) +├── Computers (default container) ← CS-QB stays here (VM, not staff PC) +├── Users (default container) ← system/service accounts only +├── Domain Controllers +│ └── CS-SERVER +├── Workstations ← NEW +│ ├── Staff PCs ← NEW — CRYSTAL-PC, ACCT2-PC, DESKTOP-H6QHRR7, DESKTOP-1ISF081, DESKTOP-DLTAGOI, DESKTOP-ROK7VNM +│ └── Shared PCs ← NEW — shared/rotation workstations (GPO: CSC - Shared Workstation) +└── Departments + ├── Administrative (6 users) + ├── Care-Assisted Living (4 users) + │ └── Nurses (sub-OU) + ├── Care-Memorycare (2 users) + ├── Culinary (4 users) + ├── Housekeeping (1 user) + ├── Life Enrichment (2 users) + ├── Maintenance (2 users) + ├── Marketing (4 users) + ├── Resident Services (7 users) + └── Transportation (3 users) +``` + +### Cleanup Scripts +- `migration/scripts/phase2-ou-cleanup.ps1` — Audit + delete 13 root-level OUs, handle CN=Users accounts +- `migration/scripts/phase2-ad-setup.ps1` — Security fixes, create Workstations OU, security groups, move computers + +## Group Policy (as of 2026-03-07 export) + +GPOs exist but effectiveness is limited since most PCs aren't domain-joined. + +| GPO | Created | Modified | Settings | Notes | +|-----|---------|----------|----------|-------| +| Default Domain Policy | Aug 2024 | Mar 2026 | Password: 7-char min, 42-day max, complexity on, 24 history. **Lockout: 5 attempts / 30 min** (fixed 2026-03-09). Kerberos defaults. | OK | +| Default Domain Controllers Policy | Aug 2024 | Oct 2024 | IIS app pool audit rights, print operator driver loading. Standard. | OK | +| Power Options | Jul 2025 | Jul 2025 | "Cascades Default" power plan: never sleep/hibernate, display off 15 min (plugged in) / 10 min (battery), password on wake. | Reasonable — keep | +| ~~CopyRoomPrinter~~ | Dec 2025 | Dec 2025 | EMPTY | **DELETED 2026-03-09** | +| ~~Nurses-Kiosk~~ | Dec 2025 | Dec 2025 | EMPTY | **DELETED 2026-03-09** | +| ~~MemCareMedTechPrinter~~ | Dec 2025 | Dec 2025 | EMPTY | **DELETED 2026-03-09** | + +**GPO Review (2026-03-07):** All 3 Dec 2025 GPOs are completely empty shells — no computer or user settings, not linked to any OU. Safe to delete with zero impact. The Default Domain Policy has account lockout disabled (threshold = 0), allowing unlimited password brute-force attempts — this needs to be fixed in the security baseline GPO. + +## RDS Licensing +- **Mode: NotConfigured** +- **License Servers: None** +- RDS roles are installed on CS-SERVER (Connection Broker, Session Host, Web Access) but licensing is NOT configured. +- **Compliance risk:** Windows Server allows a 120-day grace period for RDS without licensing. After that, connections may be refused. Since the server was installed 8/4/2024 (~19 months ago), the grace period has long expired. RDS may be running in non-compliant mode. + +## Existing AD Groups (Custom) + +| Group | Members | Notes | +|-------|---------|-------| +| QuickBooks Access | Meredith.Kuhn, Megan.Hiatt, Ashley.Jensen, lauren.hasselman | Renamed from "Quickboosk acccess" on 2026-03-09 | +| Roaming | (empty) | Old roaming profile attempt — unused | +| MemoryCareDepartment | (empty) | Never populated | +| KitchenAdmin | (empty) | Never populated | + +## Migration Plan — AD Changes (Phase 2.2 + 2.6 + 3) + +See `migration/phase2-server-prep.md` and `migration/scripts/phase2-ad-setup.ps1`. + +### Security Fixes (immediate) +- Remove disabled Monica.Ramirez from **Domain Admins** (security risk) +- Disable Haris.Durut (still enabled, not employed) +- Fix "Quickboosk acccess" → "QuickBooks Access" +- Add lauren.hasselman to QuickBooks Access (replaced Jeff Bristol) + +### OU Changes +- **DELETE 10 root-level duplicate OUs** (Administrative, Care-Assisted Living, Care-Memorycare, Culinary, Housekeeping, Life Enrichment, Maintenance, Marketing, Resident Services, Transportation) — duplicates of Departments sub-OUs +- **DELETE 3 empty root-level OUs** (Managment, MemCare, Sales) — unused +- Create: `OU=Workstations,DC=cascades,DC=local` +- Create: `OU=Staff PCs,OU=Workstations,DC=cascades,DC=local` + +### Security Groups (created with members from Synology permission mapping) + +| Group | Members | +|-------|---------| +| SG-Management-RW | Meredith.Kuhn, Ashley.Jensen, Megan.Hiatt, Crystal.Rodriguez, Tamra.Matthews, britney.thompson, Veronica.Feller, strozzi, Alyssa.Brooks, lauren.hasselman | +| SG-Sales-RW | Megan.Hiatt, Crystal.Rodriguez, Tamra.Matthews | +| SG-Server-RW | Ashley.Jensen, britney.thompson, Christina.DuPras, Veronica.Feller, Meredith.Kuhn | +| SG-Chat-RW | Ashley.Jensen, britney.thompson, Veronica.Feller | +| SG-Culinary-RW | JD.Martin, Ramon.Castaneda, Alyssa.Brooks | +| SG-IT-RW | howard, sysadmin | +| SG-Receptionist-RW | Cathy.Kingston, Shontiel.Nunn, Ray.Rai, Sebastian.Leon, Michelle.Shestko | +| SG-Directory-RW | Cathy.Kingston, Shontiel.Nunn, Christina.DuPras | +| SG-AllShares-RO | (populated as needed) | + +### Account Removals (client confirmed) + +**Already disabled — delete:** Anna.Pitzlin, Nela.Durut-Azizi, Jodi.Ramstack, Monica.Ramirez, jeff.bristol + +**Enabled but not in HR — disable + delete:** Haris.Durut, Nuria.Diaz, Cathy.Reece, Kelly.Wallace, alyssa.brooks, Isabella.Islas, ann.dery + +**Keep:** lauren.hasselman (replaced Bristol as Business Office Director) + +### CN=Users — HR Verified (2026-03-10) + +HR (Meredith) responded. All accounts resolved: + +| Account | Enabled | Last Logon | Action | +|---------|---------|-----------|--------| +| Lupe.Sanchez | Yes | Never | **Keep** — confirmed same person as Guadalupe.Sanchez (M365: lupe.sanchez@). Merge or delete duplicate | +| Receptionist | Yes | 2/22/2026 | Shared account — keep until Phase 5 replacement | +| directoryshare | Yes | 2/26/2026 | Shared/service account — keep until Phase 5 replacement | + +**Confirmed DELETE by HR:** +- Anna.Pitzlin (disabled) — was forwarded to Meredith, OK to delete now +- Nela.Durut-Azizi (disabled) — was forwarded to Meredith, OK to delete now +- Jodi.Ramstack (disabled) +- Monica.Ramirez (disabled, already removed from Domain Admins) +- Kristiana.Dowse — M365 only, not in AD. Delete M365 account + remove license + +**Already confirmed for removal (not current employees, never logged in):** +Haris.Durut, Nuria.Diaz, Cathy.Reece, Kelly.Wallace, Isabella.Islas, ann.dery, alyssa.brooks (lowercase duplicate) + +**System/service accounts staying in CN=Users:** +Administrator, Guest, krbtgt, localadmin, sysadmin, QBDataServiceUser34 + +### Domain Join (Phase 3) +Join these PCs to cascades.local in OU=Staff PCs,OU=Workstations: +- DESKTOP-KQSL232 (first) +- CHEF-PC +- SALES4-PC +- MDIRECTOR-PC (last) + +### GPOs to Create (Phase 2.6) +1. **CSC - Drive Mappings** — S:, M:, T:, K:, I:, R:, P: with item-level targeting +2. **CSC - Printer Deployment** — Deploy printers by OU/group targeting (Life Enrichment first: 1F-132-RecRoom-Canon + CopyRoom) +3. **CSC - Security Baseline** — 12-char passwords, complexity, lockout 5/30, screen lock 15 min +4. **CSC - Windows Update** — Auto download, Sundays 3 AM, no auto-restart +5. **CSC - Folder Redirection** — Desktop, Documents, Downloads → `\\CS-SERVER\homes\%username%\` +6. **CSC - Shared Workstation** — Linked to Shared PCs OU; ILT by computer name for reception drive (R:), front desk printer, Outlook online mode, shared mailbox auto-mount. Blocked on: M365 tenant details, onsite PC identification. + +### Entra Connect (Phase 2.7 — NEW) +- Install Entra Connect on CS-SERVER for AD → M365 sync + SSO +- **BLOCKED ON:** AD cleanup (renames, deletions, duplicate resolution) must complete first +- See `cloud/m365.md` → "Entra Connect — SSO Setup Plan" for full prerequisites and steps +- Enables: single sign-on, one password, auto Office/Edge activation per user, roaming experience without roaming profiles + +### Shared Account Replacement (Phase 5) +Replace Culinary, Receptionist, saleshare, directoryshare with security group access. + +## Domain Admins (from 2026-03-07 export) + +| Account | Status | Action Needed | +|---------|--------|---------------| +| Administrator | Enabled | OK (built-in) | +| Meredith.Kuhn | Enabled | **REMOVE** — administrative staff, not IT | +| John.Trozzi | Enabled | **REMOVE** — maintenance, not IT | +| ~~Monica.Ramirez~~ | **Disabled** | **REMOVED 2026-03-09** | +| sysadmin | Enabled | OK (IT account) | + +## Login Activity (audit 2026-03-20) + +Only 12 of 49 enabled accounts have ever logged in. Most staff have never used their AD accounts because their PCs aren't domain-joined. + +| Account | Last Logon | Notes | +|---------|-----------|-------| +| sysadmin | 2026-03-16 | | +| QBDataServiceUser34 | 2026-03-14 | QuickBooks service | +| Allison.Reibschied | 2026-03-13 | **NEW** — Administrative | +| lauren.hasselman | 2026-03-12 | Business Office Director | +| Administrator | 2026-03-11 | | +| Receptionist | 2026-03-11 | Shared account | +| directoryshare | 2026-03-10 | Shared account | +| localadmin | 2026-03-09 | | +| Crystal.Rodriguez | 2026-03-09 | CRYSTAL-PC | +| Culinary | 2026-02-20 | Shared account | +| saleshare | 2025-12-08 | Shared account | +| Christina.DuPras | 2026-01-06 | | +| Monica.Ramirez | 2024-11-04 | **Disabled** | + +**37 enabled accounts have NEVER logged in** — most have never set a password either. + +## Issues Found +1. **Only 6 computers domain-joined** — At least 4 known staff PCs are NOT in AD. (Migration Phase 3 will fix) +2. **3 GPOs from Dec 2025 undocumented** — CopyRoomPrinter, Nurses-Kiosk, MemCareMedTechPrinter. Need to review settings and linkage. Previous MSP or sysadmin created these. +3. **RDS licensing not configured** — Compliance risk, grace period expired ~17 months ago. (Phase 5 decision) +4. **12 accounts to remove** — 5 disabled + 7 former employees still enabled. (Phase 2.1/2.2) +5. **4 shared/generic accounts** (Culinary, Receptionist, saleshare, directoryshare) — To be replaced. (Phase 5) +6. **Monica.Ramirez (disabled) still in Domain Admins** — Security risk, fix immediately. (Phase 2.2) +7. **Meredith.Kuhn and John.Trozzi in Domain Admins** — Non-IT staff should not be DAs. (Phase 2.2) +8. **"Managment" OU misspelled** — To be deleted (empty). (Phase 2.1) +9. **"Quickboosk acccess" group typo** — To be fixed. (Phase 2.2) +10. **13 junk root-level OUs** — 10 duplicate department OUs + Managment + MemCare + Sales, all empty. Delete in Phase 2.1. +11. **20 accounts in CN=Users** — Mix of system, stale, and misplaced. Clean up in Phase 2.1. +12. **5 computers in CN=Computers** — Move 4 staff PCs to Workstations OU. CS-QB stays. (Phase 2.2) +13. **Lupe.Sanchez** — In CN=Users, possible duplicate of Guadalupe.Sanchez (Housekeeping). Flag for onsite review. diff --git a/clients/cascades-tucson/docs/servers/cs-server.md b/clients/cascades-tucson/docs/servers/cs-server.md new file mode 100644 index 0000000..337363d --- /dev/null +++ b/clients/cascades-tucson/docs/servers/cs-server.md @@ -0,0 +1,299 @@ +# Server: CS-SERVER + +## General Info +- Hostname: CS-SERVER +- IP Address: 192.168.2.254 +- Subnet Mask: 255.255.252.0 (/22) +- Default Gateway: 192.168.0.1 (pfSense) +- DNS Servers: 127.0.0.1 (itself), 192.168.0.1 (pfSense) — **FIXED 2026-03-06** +- OS: Microsoft Windows Server 2019 Standard +- OS Version: 10.0.17763 Build 17763 +- OS Configuration: **Primary Domain Controller** +- Domain: cascades.local +- Physical / Virtual: Physical (Hyper-V host) +- Location: Server room (1st Floor) +- Original Install Date: 8/4/2024 +- Last Boot: 3/14/2026 (uptime 5.9 days as of audit) +- Timezone: (UTC-07:00) Arizona — **FIXED 2026-03-07** (was Pacific Time) + +## Hardware +- Make/Model: **Dell PowerEdge R610** (CRITICAL: ~2009 era hardware, 16+ years old) +- BIOS: Dell Inc. 6.6.0, 5/22/2018 +- CPU: 2x Intel Xeon (Nehalem, Model 26) @ 2261 MHz +- RAM: 48 GB (36.9 GB available) +- System Type: x64-based PC + +## Storage + +### RAID Controller +- Controller: Dell SAS 6/iR Integrated (Embedded) +- Type: Hardware RAID (basic, no battery-backed cache) + +### Physical Disks (5 total) +| Disk ID | Model | Bus | Size | Mfg Year | Serial | Role | Status | +|---------|-------|-----|------|----------|--------|------|--------| +| 0:0:0 | Seagate ST1200MM0088 | SAS | 1.12 TB | 2016 wk52 | Z400WHK8 | RAID (D: Shares) | OK | +| 0:0:1 | Seagate ST1200MM0088 | SAS | 1.12 TB | 2015 wk43 | S400RL2N | RAID (D: Shares) | OK | +| 0:0:2 | Hitachi HTS545032B9A300 | SATA | 297.5 GB | Unknown | 100602PB... | RAID (C: OS) | OK | +| 0:0:3 | WD WD3200BEVT-75ZCT2 | SATA | 297.5 GB | Unknown | WD-WXEX... | RAID (C: OS) | OK | +| 1:0:4 | Seagate ST1200MM0088 | SAS | 1.12 TB | 2016 wk52 | Z400WHML | **Global Hot Spare** | Ready | + +**Failure Predicted on any disk: No** (all currently healthy) + +### Disk Concerns +- Disks 0:0:2 and 0:0:3 are **consumer-grade SATA laptop drives** (Hitachi 2.5" and WD Scorpio Blue) running the OS RAID. These are NOT enterprise drives. They have no manufacture date — likely very old. +- Disks 0:0:0 and 0:0:1 are enterprise SAS drives from 2015-2016 — now 9-10 years old. +- Disk 1:0:4 is a **global hot spare** (SAS, 1.12TB) — good, this will auto-rebuild if a SAS drive fails in the D: array. However, there is NO hot spare for the C: SATA array. +- The SAS 6/iR controller is very basic — does NOT have a battery-backed write cache. + +### Logical Volumes +| Drive | Label | Filesystem | Size | Free | Used | Backed By | +|-------|-------|------------|------|------|------|-----------| +| C: | (OS) | NTFS | 296.9 GB | 152.1 GB | 49% | 2x SATA laptop drives (RAID 1 likely) | +| D: | Shares | NTFS | 1.09 TB | 584.6 GB | 48% | 2x SAS enterprise drives (RAID 1 likely) + hot spare | +| - | Recovery | NTFS | 499 MB | 482.8 MB | | | + +## Network Interfaces +| NIC | Description | MAC | Status | IP | +|-----|------------|-----|--------|-----| +| Ethernet | QLogic BCM5709C #37 | 00:22:19:60:50:E1 | **Disconnected** | - | +| Ethernet 2 | QLogic BCM5709C #35 | 00:22:19:60:50:DD | **Disconnected** | - | +| Ethernet 3 | QLogic BCM5709C (used by Hyper-V vSwitch) | 00:22:19:60:50:DB | Active (underlying) | - | +| Ethernet 4 | QLogic BCM5709C #36 | 00:22:19:60:50:DF | **Disconnected** | - | +| vEthernet | Hyper-V Virtual Adapter | 00:22:19:60:50:DB | **Active** | 192.168.2.254 | + +Only 1 of 4 physical NICs is in use (via Hyper-V virtual switch). 3 NICs disconnected. + +## Roles and Services (Installed) +- [x] **Active Directory Domain Services** (Primary DC) +- [x] **DHCP Server** +- [x] **DNS Server** +- [x] **File Server** (D:\Shares) +- [x] **Hyper-V** (Hypervisor) +- [x] **Remote Desktop Services** (Connection Broker, Session Host, Web Access) — **NOT USED, can be removed** +- [x] **IIS Web Server** (ASP.NET 4.7, Windows Auth) +- [x] **Network Policy and Access Services (NPS/RADIUS)** — **NOT USED, no RADIUS clients configured, can be removed** +- [x] Group Policy Management +- [x] RSAT Tools + +## Hyper-V Virtual Machines +| VM Name | State | RAM Assigned | Uptime | Status | Notes | +|---------|-------|-------------|--------|--------|-------| +| CS-QB | **Running** | 2.35 GB | 7d 10h | Normal | VoIP server — seen on network at 192.168.2.228 | +| Synology Sync machine | Off | 0 | - | Normal | NOT USED — safe to delete, was set up "just in case" | +| VM-TEMPLATE | Off | 0 | - | Normal | Template VM for cloning | + +- The host's single active NIC is shared via a Hyper-V virtual switch +- CS-QB is the VoIP-related VM (explains the "CS-QB" device on 1st Floor USW Port 48) +- Synology Sync machine can be removed to free disk space +- RAID health: Windows reports virtual disks as healthy, but actual physical drive health is behind the Dell PERC controller. Need Dell OpenManage or `omreport` / `perccli64` to check real disk SMART status + +## SMB File Shares (audit 2026-03-20) +| Share Name | Path | SMB Perms | NTFS Notes | +|-----------|------|-----------|------------| +| Shares | D:\Shares | Everyone=Read, Admins=Full, Domain Computers=Read | **Everyone=FullControl** (too permissive) | +| Culinary | D:\Shares\Culinary | Admins=Full, **Everyone=Full** | Everyone=FullControl — needs restriction | +| directoryshare | D:\Shares\directoryshare | **Everyone=Full** | Everyone=FullControl — needs restriction | +| IT | D:\Shares\IT | Admins=Full, Everyone=Full | Everyone=ReadAndExecute only (OK) | +| Receptionist | D:\Shares\Receptionist | Admins=Full, Everyone=Full | Receptionist=Full, admins only | +| SaleShare | D:\Shares\SaleShare | Everyone=Read, saleshare=Full | saleshare=Full | +| Roaming | D:\Roaming | Admins=Full, Everyone=Full | **Domain Users=FullControl** (too broad) | +| RDVirtualDesktopTemplate | C:\RDVirtualDesktopTemplate | Network Service/Admins/RDS=Full | Standard | +| MemCare Director Printer | (printer share) | Everyone=Full | Printer share | +| MemCare MedTech Printer | (printer share) | Everyone=Full | Printer share | +| NETLOGON | C:\Windows\SYSVOL\...\SCRIPTS | Everyone=Read | Standard | +| SYSVOL | C:\Windows\SYSVOL\sysvol | Everyone=Read, Admins=Full | Standard | +| ADMIN$, C$, D$ | (system) | Admin only | Default admin shares | + +## Synology Sync +- **Synology Drive Client** installed on CS-SERVER (2026-03-07) +- Syncing all Synology shares to `D:\Shares\Main` +- Live continuous sync (Synology → CS-SERVER) +- Previous install was removed and reinstalled due to incomplete sync config + +## Installed Software (audit 2026-03-20) +| Software | Version | Notes | +|----------|---------|-------| +| **QuickBooks Pro 2024** | 34.0.4008.3401 | **Should NOT be on a DC** | +| Synology Drive Client | 7.5.0.16085 | Live sync from Synology NAS to D:\Shares\Main | +| Datto EDR Agent | 3.17.1.4720 | Remove when migrating to new AV | +| Datto RMM | 4.4.10748 | Previous RMM — remove | +| Dell EMC OpenManage | 9.3.0 | Server management — keep | +| Google Chrome | 146.0.7680.154 | | +| Mozilla Firefox | 148.0.2 | | +| PuTTY | 0.83 | SSH client | +| Everything | 1.4.1.1026 | File search tool | +| KPAX Agent | 3.1.2409 | Print management | +| ScreenConnect Client | 26.1.18.9566 | Remote access | +| Splashtop Streamer | 3.8.0.4 | Previous MSP — remove | +| Syncro | 1.0.199.18369 | Current RMM | +| WinRAR | 7.20 | | +| .NET 6.0.x runtimes | Various | Required by apps | +| VC++ 2013 & 2022 Redist | Various | Runtime dependencies | + +## Security Findings (audit 2026-03-20) + +### Certificates +- **EXPIRED:** CN=CS-SERVER.cascades.local self-signed cert expired **2025-04-02** — causes Schannel errors + +### Password Policy +- Min length: **7** (should be 12+) +- Complexity: enabled +- Max age: 42 days | Min age: 1 day | History: 24 +- Lockout: 5 attempts / 30 min + +### Critical Security Issues +| Finding | Status | +|---------|--------| +| **No LAPS deployed** (legacy or Windows LAPS) | Not configured | +| **MachineAccountQuota = 10** | Any user can join 10 machines | +| **AD Recycle Bin NOT enabled** | Deleted objects not recoverable | +| **10 accounts with PasswordNeverExpires** | Administrator, localadmin, Lois.Lane, sysadmin, QBDataServiceUser34, Culinary, Receptionist, howard, directoryshare, strozzi | +| **23 accounts never set a password** | PasswordLastSet = null | +| **Protected Users group EMPTY** | No accounts protected against credential theft | +| **RestrictAnonymous = 0** | Null sessions allowed (should be 1+) | +| **LDAP Channel Binding not configured** | Should be enabled | +| **krbtgt password age: 569 days** | Last set 2024-08-28, should rotate every 180 days | +| **No screen lock policy** | No inactivity timeout configured | +| **Object Access auditing DISABLED** | No file/registry auditing (HIPAA requirement) | +| **TLS 1.0/1.1 and SSL not explicitly disabled** | All at OS default | +| **Credential Guard NOT running** | VBS running but CG not enabled | +| **AutomationManagerAgent service** | Stopped — file not found (orphan) | +| **Windows Server Backup not installed** | Feature not present | +| **NPS PowerShell module missing** | Role installed but cmdlets unavailable | + +### Firewall +- All 3 profiles (Domain, Private, Public) enabled +- Default inbound/outbound: NotConfigured +- 341 rules total (206 inbound, 135 outbound) — ALL Allow, no Block rules +- No logging enabled +- Notable inbound: RDP (3389), HTTP/HTTPS (80/443), AD/DNS, QuickBooks (8019, 64214), Dell OpenManage (1311), Syncro/Splashtop/Datto (any port) + +### Domain Admins — Never Logged In +- **Meredith.Kuhn:** Domain Admin, never logged in, never set a password +- **John.Trozzi:** Domain Admin, never logged in, never set a password + +## Listening Ports (Key Services) +| Port | Protocol | Service | Notes | +|------|----------|---------|-------| +| 53 | TCP | DNS | On 192.168.2.254 + localhost | +| 80/443 | TCP | IIS (HTTP/HTTPS) | Bound to 0.0.0.0 — exposed to all interfaces | +| 88 | TCP | Kerberos | AD authentication | +| 135 | TCP | RPC Endpoint Mapper | Standard Windows | +| 389/636 | TCP | LDAP/LDAPS | AD directory services | +| 445 | TCP | SMB | File shares | +| 464 | TCP | Kerberos kpasswd | Password changes | +| 1311 | TCP | Dell OpenManage Web | Management console | +| 2179 | TCP | Hyper-V VMMS | VM management | +| 3268/3269 | TCP | Global Catalog / GC SSL | AD Global Catalog | +| 3387 | TCP | RDP (alternate) | Second RDP listener (RDS?) | +| 3389 | TCP | RDP | Remote Desktop | +| 5357 | TCP | WSDAPI | Web Services Discovery | +| 5504 | TCP | Unknown | **Needs identification** | +| 5985 | TCP | WinRM | PowerShell remoting | +| 6600 | TCP | QuickBooks DB Server | On 192.168.2.254 + link-local IPv6 | +| 6783 | TCP | Unknown | **Needs identification** | +| 8019 | TCP | Unknown | **Needs identification** | +| 9389 | TCP | AD Web Services | AD management | + +## Hotfixes Installed (16) +KB5066137, KB4486153, KB4539571, KB4577586, KB4589208, KB5005112, KB5075904, KB5040563, KB5050110, KB5058525, KB5062800, KB5065765, KB5066585, KB5070248, KB5074222, KB5075903 + +## Related Devices +- MAC 00:22:19:60:50:E1 seen on 1st Floor USW Port 44 (PoE OFF) +- MAC 00:22:19:60:50:DB seen on 1st Floor USW Port 48 as "CS-QB" (192.168.2.228) — this is the Hyper-V vSwitch NIC + +## Backup +- **NONE — NO BACKUP EXISTS FOR THIS SERVER** +- **HIPAA VIOLATION:** §164.308(a)(7) requires backup of all PHI. CS-SERVER stores PHI (synced from Synology + file shares). +- This server is the ONLY domain controller +- If this server dies, Active Directory, DNS, DHCP, file shares, and RDS are ALL lost + +## Known Admin Issues + +### Folder Redirection CSE silently declines to commit — ROOT CAUSE UNKNOWN +**Investigated 2026-04-14. Not fully resolved — currently using manual registry fix as workaround.** + +**Symptoms:** +- GPO `CSC - Folder Redirection (LE)` is correctly configured (verified via SYSVOL inspection and RSAT on a Win11 PC — both wrote identical, valid `fdeploy1.ini` with `FullPath=\\CS-SERVER\homes\%USERNAME%\Documents` and `Flags=1231`) +- GPO is correctly linked to `OU=Life Enrichment`, old `CSC - Folder Redirection` GPO has been unlinked +- `gpresult /r /scope:user` on a Life Enrichment user (Sharon.Edwards) confirms the new GPO is in "Applied Group Policy Objects" +- At logon, FR client-side extension fires, reads config, and **logs event 1006 "Folder Documents has to be redirected" with correct target path** +- Extension then logs event 1001 "finished" — **no error events, no event 1013 (success)** +- `HKCU\...\User Shell Folders\Personal` is never updated, Documents stays at `C:\Users\\Documents` +- Multiple logon cycles, `gpupdate /force`, clearing GP cache do not resolve + +**What we verified is NOT the cause:** +- ❌ GPMC on CS-SERVER writing "broken" files — false. Both CS-SERVER GPMC and Win11 RSAT write identical, valid modern format (`User\Documents & Settings\fdeploy1.ini` with `FullPath=` IS the correct modern location; earlier claim this was "legacy XP format" was wrong) +- ❌ SYSVOL ACL problems — SYSVOL writable, `dcdiag /test:sysvolcheck` passes +- ❌ NTFS permission issues — user has FullControl on their `\\CS-SERVER\homes\` folder, write test succeeds +- ❌ Extension registration — `gPCUserExtensionNames` correctly lists `{25537BA6-77A8-11D2-9B6C-0000F8080861}` +- ❌ Target path unreachable — manual `New-Item` and `Test-Path` succeed +- ❌ User OU placement — user is in OU=Life Enrichment, GPO applies per gpresult + +**Suspected causes (to investigate later):** +- Stale entry in `HKCU\Software\Microsoft\Windows\CurrentVersion\Group Policy\History\{25537BA6-77A8-11D2-9B6C-0000F8080861}` referencing the old unlinked GPO. Key is SYSTEM-protected — can't delete from user context, need elevated sysadmin access via HKU\\ +- Profile corruption from prior failed redirection attempts / GP Preferences registry hack +- Competing shell folder state (e.g., `Desktop` is already at UNC via the original registry hack, `Personal` is local — mixed state may confuse FR) + +**Current workaround (works reliably):** +Set the User Shell Folders registry values directly — same pattern as the original GP Preferences Registry hack: +```powershell +Set-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" ` + -Name "Personal" ` + -Value "\\CS-SERVER\homes\\Documents" -Type ExpandString +``` +Then `robocopy /MOVE` local content to server, sign out, sign in. This works; FR does not. + +**Admin console note:** RSAT GPMC on a Win11 Pro domain-joined workstation is now installed on ASSISTNURSE-PC (and Sharon Edwards' PC during testing). All future GPO edits should happen from RSAT — best practice regardless of this issue. + +**Next debug steps (not tonight — fresh eyes needed):** +1. Test FR on a **brand new user** (no profile history) in OU=Life Enrichment to rule out profile-level corruption +2. If new user works → profile issue, proceed with per-user manual fix + eventual profile reset +3. If new user also fails → policy or CSE issue, dig into FR debug logging (`fdeploy.log` verbose mode, enable via registry) +4. Clear `Group Policy\History\{25537BA6...}` key from elevated sysadmin via `HKU\` path to rule out stale history + +--- + +## CRITICAL ISSUES (all impact HIPAA compliance) + +### 1. Hardware Age — EXTREME FAILURE RISK +The Dell PowerEdge R610 was released in 2009. This server is 16+ years old. PERC RAID controllers, power supplies, and disks from this era have very high failure rates. This is the single most critical risk at Cascades. + +### 2. Single Domain Controller — No Redundancy +CS-SERVER is the ONLY DC for cascades.local. If it fails: +- No Active Directory authentication +- No DNS resolution (for domain-joined devices) +- No DHCP (if clients use this DHCP) +- No file shares +- No RDS sessions +- Complete business disruption + +### 3. No Backup +There is no backup solution. The Synology NAS at 192.168.0.120 is available as a target but is not configured to back up this server. + +### 4. DHCP — RESOLVED (No Conflict) +Windows DHCP role is installed but has NO scopes configured. pfSense handles all DHCP. No conflict exists. The DHCP role could be uninstalled to reduce confusion, but it's not causing harm. + +### 5. DNS Client Misconfiguration — FIXED (2026-03-06) +~~CS-SERVER pointed to pfSense (192.168.0.1) + 8.8.8.8 as its DNS servers.~~ Fixed: now uses `127.0.0.1` (itself) as primary, `192.168.0.1` (pfSense) as secondary. Verified working — both AD and external resolution resolve correctly through localhost. + +### 7. CS-QB VM — VoIP Server on Ancient Hardware +The CS-QB VM (2.35 GB RAM, running) is the VoIP server at 192.168.2.228. VoIP was noted as not MSP-managed, but the VM runs on CS-SERVER. If CS-SERVER hardware fails, phones go down too. + +### 8. Synology Sync Machine VM is OFF +A VM called "Synology Sync machine" exists but is powered off. Unclear if this is intentional or if it should be running to sync data to the Synology NAS. Needs investigation. + +### 6. Too Many Roles on One Box +This single server runs: DC, DNS, DHCP, File Server, Hyper-V, RDS, IIS, NPS. Any one role failure or resource contention affects everything. + +## TODO (Priority Order) +- [ ] **IMMEDIATE: Set up backup** — Synology Active Backup for Business → back up C: and D: nightly +- [ ] **IMMEDIATE: Document VMs** — Run `Get-VM` to see what's running in Hyper-V +- [ ] **URGENT: Plan DC migration** — This hardware WILL fail. Plan migration to new hardware or cloud +- [ ] **URGENT: Add second DC** — Deploy a second DC (VM or cloud) for AD redundancy +- [ ] Check RAID controller type and health: `Get-StorageSubSystem`, or Dell OpenManage +- [ ] Verify DHCP scope split between pfSense and CS-SERVER +- [ ] Document DNS forwarding chain (pfSense <-> Windows DNS) +- [ ] Check RDS licensing status +- [ ] List Hyper-V VMs and their purposes diff --git a/clients/cascades-tucson/docs/servers/server_template.md b/clients/cascades-tucson/docs/servers/server_template.md new file mode 100644 index 0000000..d35ab32 --- /dev/null +++ b/clients/cascades-tucson/docs/servers/server_template.md @@ -0,0 +1,49 @@ +# Server: [SERVER NAME] + +## General Info +- Hostname: +- IP Address: +- OS: +- OS Version: +- Physical / Virtual: +- Host (if virtual): +- Location: +- Last Patched: + +## Hardware (if physical) +- Make/Model: +- CPU: +- RAM: +- Storage: +- Warranty Expiry: + +## Roles and Services + +- [ ] Domain Controller +- [ ] DNS Server +- [ ] DHCP Server +- [ ] File Server +- [ ] Print Server +- [ ] Application Server +- [ ] Database Server +- [ ] Backup Target +- [ ] RDS / Terminal Server +- [ ] Hyper-V Host + +## Shares (if file server) +| Share Name | Path | Permissions Group | Notes | +|---------------|-------------------|---------------------|----------------| +| | | | | + +## Applications Installed +| Application | Version | Purpose | License | +|-------------------|------------|----------------------|---------------| +| | | | | + +## Backup +- Backup Method: +- Backup Schedule: +- Backup Target: +- Last Verified Restore: + +## Notes diff --git a/clients/cascades-tucson/docs/workstations.md b/clients/cascades-tucson/docs/workstations.md new file mode 100644 index 0000000..672c182 --- /dev/null +++ b/clients/cascades-tucson/docs/workstations.md @@ -0,0 +1,382 @@ +# Workstation Inventory — Cascades + +Last audited: 2026-03-20 + +## Fleet Summary + +| Metric | Count | +|--------|-------| +| Total workstations | 19 | +| Domain-joined | 5 | +| Not domain-joined | 14 | +| Windows 11 Pro | 8 | +| Windows 11 Home (cannot domain join) | 3 | +| Windows 10 Pro | 3 | +| Windows 10 Home (cannot domain join) | 3 | +| Win 10 Pro for Workstations | 1 | +| Win 11 Pro for Workstations | 1 | +| BitLocker encrypted + protection ON | 2 | +| BitLocker encrypted, protection OFF | 3 | +| No BitLocker | 13 | +| Updates current (within 30 days) | 10 | +| Updates critically behind (3+ months) | 6 | + +## All Workstations + +### ACCT2-PC +- **OS:** Windows 11 Pro for Workstations 25H2 (Build 26200) +- **Domain:** cascades.local (joined) +- **Hardware:** Acer Aspire C24-963 AIO | Serial: DQBF6AA001040003223000 +- **CPU:** Intel i3-1005G1 (2C/4T) | **RAM:** 7.8 GB +- **Disk:** C: 118.4 GB / 53 GB free (55%) — SanDisk SSD +- **Network:** Wi-Fi @ 10.0.20.209/24 (STATIC) | DNS: 192.168.2.254 | MAC: 98:8D:46:F1:2D:C2 +- **BitLocker:** Encrypted, **Protection OFF** — no key protectors +- **AV:** Datto AV (enabled, up to date) +- **Updates:** Last KB5077181 2026-02-21 +- **Local Admins:** Administrator, Localadmin, CASCADES\Allison.Reibschied, CASCADES\Domain Admins +- **Users:** Localadmin, Stephanie +- **Software:** M365 Apps, Brother MFC-L8900CDW, Epson ES-580W, Synology Drive Client, Chrome +- **Printers:** CopyRoom iR-ADV C478 (WSD), Brother MFC-L8900CDW (10.0.20.220) +- **Issues:** BitLocker protection OFF, no screen lock + +### ANN-PC +- **OS:** Windows 11 Home 24H2 (Build 26100) — **cannot domain join** +- **Domain:** WORKGROUP (not joined) +- **Hardware:** Lenovo F0EW005TUS AIO | Serial: MP1Z9DZY +- **CPU:** AMD Ryzen 3 4300U (4C/4T) | **RAM:** 7.4 GB +- **Disk:** C: 237.2 GB / 71.9 GB free (70%) — Samsung SSD +- **Network:** Wi-Fi @ 192.168.3.252/22 (DHCP) | DNS: 192.168.0.1 | MAC: CC:6B:1E:11:F0:7F +- **BitLocker:** Not enabled +- **AV:** Datto AV (enabled) +- **Updates:** Last KB5083532 2026-03-11 (current) +- **Local Admins:** Administrator, christina, localadmin, rootadmin +- **Users:** christina (active), Ann Dery (last 2024-06-11) +- **Software:** M365 Apps, Adobe Acrobat, Kofax Power PDF, Brother MFC-9330/9340CDW, Epson ET-5800, Synology Drive, TeamViewer, Firefox, Chrome, Splashtop, Syncro +- **Printers:** Brother MFC-L8900CDW (WSD), Brother MFC-9340CDW (WSD), Brother MFC-9330CDW (WSD) +- **Printer ports:** 10.0.20.221, 192.168.2.126, 192.168.2.135, 192.168.0.145, 192.168.0.170 +- **Issues:** Win Home (needs Pro), no BitLocker, no screen lock, TeamViewer installed (old MSP?) + +### ASSISTMAN-PC +- **OS:** Windows 10 Pro 22H2 (Build 19045) +- **Domain:** WORKGROUP (not joined, workgroup name is "CASCADES") +- **Hardware:** Lenovo 10K3000BUS AIO | Serial: P901KGLQ +- **CPU:** Intel i5-6200U (2C/4T) | **RAM:** 11.9 GB +- **Disk:** C: 222.3 GB / 53 GB free (76% — getting full) — SATA SSD +- **Network:** Wi-Fi @ 192.168.2.38/22 (DHCP) | DNS: 192.168.0.1 | MAC: EE:80:75:AE:49:E3 +- **BitLocker:** Not enabled +- **AV:** Datto AV (enabled) +- **Updates:** Last KB5072653 **2025-12-20 — 3 MONTHS BEHIND** +- **Local Admins:** Administrator, CasAdmin201, Dax Howard, "DO NOT USE", localadmin, Meredith Kuhn, MeredithK (7 admins!) +- **Users:** MeredithK (active), CasAdmin201, Cecil Rinker (old), "DO NOT USE" (disabled) +- **Software:** M365 Apps, Adobe Acrobat, Adobe Creative Cloud, QuickBooks Pro 2024, Canon MF741C/743C, Brother MFC-9330CDW, Dropbox, RemotePC, Synology Drive/Chat, Firefox, Chrome, Splashtop, Syncro +- **Mapped Drives:** H: \\192.168.0.120\homes, M: \\192.168.0.120\Management, Q: \\cs-server\Quickbooks, S: \\192.168.0.120\SalesDept, Z: \\cs-server\directoryshare +- **Printers:** Canon MF741C/743C (WSD+USB), Canon copy room (WSD), Brother MFC-9330CDW, ABS PDF, Adobe PDF +- **Issues:** 3 months behind on updates, **RDP enabled WITHOUT NLA**, 7 local admins, 76% disk used, no BitLocker, no screen lock + +### CHEF-PC +- **OS:** Windows 11 Pro 25H2 (Build 26200) +- **Domain:** WORKGROUP (not joined, workgroup name is "CASCADES") +- **Hardware:** Acer Aspire C24-865 AIO | Serial: DQBBUAA0049100D70B3000 +- **CPU:** Intel i5-8250U (4C/8T) | **RAM:** 11.9 GB +- **Disk:** C: 222.3 GB / 102.5 GB free (54%) — Patriot P210 512GB SSD (only 222GB partition on 476GB disk) +- **Network:** Ethernet @ 10.0.20.232/24 (DHCP) | DNS: 192.168.0.1 | MAC: 98:EE:CB:9D:8A:84 | 1 Gbps (hardwired) +- **BitLocker:** Not enabled +- **AV:** Datto AV (enabled) +- **Updates:** Last KB5077181 2026-02-21 +- **Local Admins:** Administrator, CasAdmin201, localadmin +- **Users:** CasAdmin201, Ramon Castaneda (active), Michael Sabia (active), Ordering (old) +- **Software:** M365 Apps, Brother MFC-9330CDW, Canon MF731C/733C, Dropbox, UniFi, RemotePC, Synology Drive, Firefox, Splashtop, Syncro +- **Mapped Drives:** None +- **Printers:** CopyRoom (Canon, port 192.168.2.230), Chef Printer (Brother MFC-9330CDW USB) +- **Issues:** RDP enabled, no BitLocker, no screen lock, Norton Security Scan (old bloatware), partition only uses half the disk + +### CRYSTAL-PC +- **OS:** Windows 11 Pro 23H2 (Build 22631) +- **Domain:** cascades.local (joined) +- **Hardware:** Acer Aspire C27-962 AIO | Serial: DQBDPAA003037009206B01 +- **CPU:** Intel i5-1035G1 (4C/8T) | **RAM:** 15.8 GB +- **Disk:** C: 231.6 GB / 61.8 GB free (73%) — WDC SSD +- **Network:** Wi-Fi @ 10.0.20.205/24 (DHCP) | DNS: 192.168.0.1 | MAC: F0:09:0D:0D:FC:A7 (TP-Link USB) +- **BitLocker:** Not enabled +- **AV:** Datto AV + Malwarebytes 5.5.1 +- **Updates:** Last KB5066133 **2025-10-18 — 5 MONTHS BEHIND** +- **Local Admins:** CASCADES\Administrator, CASCADES\Domain Admins, Administrator, localadmin, rootadmin +- **Users:** localadmin, rootadmin (domain-joined, logs in via AD) +- **Software:** M365 Apps, Adobe Acrobat, Adobe Creative Cloud, Canon MF741C/743C, Malwarebytes, Synology Drive/Chat/Assistant, Zoom, Firefox, Chrome, Splashtop +- **Printers:** Sales Printer (Brother, port 192.168.3.44), iR-ADV C478 Copy Room (WSD) +- **Orphan printer ports:** 0.0.0.0, 192.168.0.140, 192.168.2.139, 192.168.2.230, 192.168.2.60, 192.168.45.109 +- **Issues:** **5 months behind on updates**, no BitLocker, no screen lock, many orphan printer ports + +### DESKTOP-DLTAGOI +- **OS:** Windows 11 Pro 25H2 (Build 26200) — **upgraded 2026-04-13** +- **Domain:** cascades.local (joined 2026-04-13) +- **Hardware:** Acer Aspire C24-865 AIO | Serial: DQBBUAA0048510AC273000 +- **CPU:** Intel i5-8250U (4C/8T) | **RAM:** 11.9 GB +- **Disk:** C: 237.6 GB / 105.4 GB free (56%) — SPCC SSD +- **Network:** Wi-Fi @ 192.168.3.133/22 (DHCP) | DNS: 192.168.0.1 | MAC: A0:A4:C5:7A:83:16 | 72.2 Mbps (low!) +- **BitLocker:** Not enabled +- **AV:** Datto AV (enabled) +- **Updates:** Last KB5079473 2026-03-11 (current) +- **Local Admins:** localadmin +- **Local Users Removed:** casadmin201, rootadmin, local "Sharon Edwards" (2026-04-13) +- **Domain User:** CASCADES\Sharon.Edwards (Life Education Assistant) +- **Software:** M365 Apps, Adobe Acrobat, Canon MF741C/743C, Google Drive, Chrome, Splashtop, Syncro +- **Printers:** CopyRoom iR-ADV C478 (manual IP), Brother printers removed (2026-04-13) +- **Issues:** No BitLocker, no screen lock, slow WiFi (72 Mbps). +- **Nearby Printer:** Rec Room (Room 132) Canon MF741CDW @ 10.0.20.94 (INTERNAL VLAN, WiFi) + +### DESKTOP-ROK7VNM +- **OS:** Windows 11 Pro (upgraded 2026-04-13, manual key) +- **Domain:** cascades.local (joined 2026-04-13) +- **Hardware:** TBD — needs audit +- **Network:** TBD +- **BitLocker:** TBD +- **AV:** TBD +- **Local Admins:** localadmin (others TBD) +- **Domain User:** CASCADES\Susan.Hicks (Life Enrichment Director) +- **Printers:** TBD — needs Rec Room Canon MF741CDW (10.0.20.94) + Copy Room +- **Issues:** Needs full audit + +### DESKTOP-H6QHRR7 +- **OS:** Windows 11 Pro for Workstations 25H2 (Build 26200) +- **Domain:** cascades.local (joined) +- **Hardware:** Lenovo 11QE003VGP Desktop | Serial: GM01H11N +- **CPU:** Intel i5-10400 (6C/12T) | **RAM:** 15.7 GB +- **Disks:** C: 476 GB / 309 GB free (35%) + D: 476.2 GB / 434.7 GB free (9%) — Intel NVMe + SPCC SSD +- **Network:** Wi-Fi @ 10.0.20.235/24 (STATIC) | DNS: 192.168.2.254 | MAC: F0:09:0D:0D:FE:E9 (TP-Link USB) +- **BitLocker:** Not enabled (C: or D:) +- **AV:** Datto AV (enabled) +- **Updates:** Last KB5077241 2026-02-25 +- **Local Admins:** CASCADES\Domain Admins, CASCADES\lauren.hasselman, Administrator, CasAdmin201, localadmin, Sylvia Cuen +- **Users:** Sylvia Cuen (active), CasAdmin201, Amber Lee (old), Daniela Arballo (old) +- **Software:** M365 Apps, Adobe Acrobat, QuickBooks Pro 2024, Canon MF450, Epson scanners, ScanLite2/TellerScan (check scanners), Zoom, Chrome, Splashtop, Syncro +- **Mapped Drives:** Z: \\192.168.0.120\Management (user: lauren.hasselman) +- **Printers:** Accounting Printer Brother (192.168.0.26), Canon MF450 (USB, shared), iR-ADV C478 (WSD), Brother HL-L2395DW/MFC-L8850CDW/MFC-L8900CDW (WSD) +- **Issues:** No BitLocker on either drive, no screen lock, lauren.hasselman is local admin (AD user) + +### DESKTOP-KQSL232 +- **OS:** Windows 10 Pro 22H2 (Build 19045) +- **Domain:** WORKGROUP (not joined) +- **Hardware:** Lenovo 10HC000CUS AIO | Serial: P9017YA3 +- **CPU:** Intel i5-6200U (2C/4T) | **RAM:** 7.9 GB +- **Disk:** C: 222.9 GB / 86 GB free (61%) — SanDisk Ultra II 240GB SSD +- **Network:** Wi-Fi @ 10.0.20.227/24 (DHCP) | DNS: 192.168.0.1 | MAC: C8:FF:28:64:8A:9F +- **BitLocker:** Not enabled +- **AV:** Datto AV (2 instances, one disabled) +- **Updates:** Last KB5072653 **2025-12-20 — 3 MONTHS BEHIND** +- **Local Admins:** Administrator, localadmin, rootadmin +- **Users:** Lois Lane (active, **no password required**), Nela Durut-Azizi (old), rootadmin +- **Software:** M365 Apps, Brother MFC-L8900CDW, Canon Generic Plus, KONICA MINOLTA, Synology Drive, Zoom, Chrome, Splashtop, Syncro +- **Printers:** KONICA MINOLTA (192.168.1.138), iR-ADV C478 (192.168.0.170), Brother MFC-L8900CDW (IP mismatch: 192.168.0.55 → 192.168.45.191) +- **Issues:** 3 months behind updates, user has no password, duplicate Datto AV, stale printer port mappings + +### DESKTOP-LPOPV30 +- **OS:** Windows 10 Pro 22H2 (Build 19045) +- **Domain:** WORKGROUP (not joined) +- **Hardware:** Lenovo 11CES04D00 AIO | Serial: MJ0H4SZQ +- **CPU:** Intel i5-10500 (6C/12T) | **RAM:** 15.7 GB +- **Disk:** C: 476.3 GB / 322.2 GB free (32%) — Samsung NVMe SSD +- **Network:** Wi-Fi @ 192.168.2.250/22 (DHCP) | DNS: 192.168.0.1 | MAC: E4:FA:C4:00:65:F1 +- **BitLocker:** Encrypted, **Protection ON** (TPM + RecoveryPassword) — GOOD +- **AV:** Datto AV (enabled) +- **Updates:** Last KB5049981 **2025-02-02 — 13 MONTHS BEHIND (CRITICAL)** +- **Local Admins:** Administrator, CasAdmin201, Karen Rossini, localadmin +- **Users:** Karen Rossini (active), Britney Thompson, CasAdmin201, Haris Durut (old), Jodi Ramstack (old), nela (old) +- **Software:** M365 Apps, Brother MFC-L8850CDW, Brother MFC-L8900CDW, Synology Drive, TP-Link drivers, Firefox, Chrome, Splashtop, Syncro +- **Printers:** Memcare Medtech Printer (Brother MFC-L8900CDW, 192.168.2.53), iR-ADV C478 (WSD), Brother MFC-L8850CDW +- **Issues:** **13 MONTHS without updates (CRITICAL)**, multiple stale user profiles from former employees + +### DESKTOP-U2DHAP0 +- **OS:** Windows 11 Pro 24H2 (Build 26100) +- **Domain:** WORKGROUP (not joined) +- **Hardware:** Lenovo 12CE009MGP AIO | Serial: MP2AKLWY +- **CPU:** 12th Gen Intel i5-12450H (8C/12T) | **RAM:** 15.7 GB +- **Disks:** C: 475.9 GB / 287.8 GB free (40%) + E: FAT32 1.9 GB USB flash +- **Network:** Wi-Fi @ 192.168.3.37/22 (DHCP) | DNS: 192.168.0.1 | MAC: E8:C8:29:6B:C1:D7 +- **BitLocker:** C: Encrypted, **Protection ON** (TPM + RecoveryPassword) — GOOD | E: not encrypted +- **AV:** Datto AV (enabled) +- **Updates:** Last KB5077869 2026-02-25 +- **Local Admins:** Administrator, Ashley, localadmin +- **Users:** Ashley (active) +- **Software:** M365 Apps, Adobe Acrobat, QuickBooks Pro 2024, Epson ET-5800, Epson check scanners (TM-S1000/S9000), ScanLite2, Canon MF741C/743C, Synology Drive/Chat, RemotePC, Chrome, Splashtop, Syncro +- **Mapped Drives:** Q: \\cs-server\Quickbooks, Y: \\cs-server\directoryshare, Z: \\EPSON833571\USBSTORAGE +- **Printers:** Canon MF741C/743C (multiple), iR-ADV C478 (WSD), HP Smart Tank 7600, Brother MFC-L8850CDW, ABS PDF, Adobe PDF +- **Issues:** **RDP enabled WITHOUT NLA**, no screen lock, USB flash drive not encrypted + +### LAPTOP-DRQ5L558 +- **OS:** Windows 10 Home 22H2 (Build 19045) — **cannot domain join** +- **Domain:** WORKGROUP (not joined) +- **Hardware:** Lenovo 81W1 Notebook | Serial: PF2L3689 +- **CPU:** AMD Ryzen 5 3500U (4C/8T) | **RAM:** 5.9 GB +- **Disk:** C: 237.2 GB / 173.3 GB free (27%) — SK Hynix NVMe SSD +- **Network:** Wi-Fi @ **10.0.50.141/24 (GUEST VLAN!)** | DNS: 8.8.8.8, 1.1.1.1 | MAC: D8:F3:BC:88:8B:E3 +- **BitLocker:** Not enabled +- **AV:** Datto AV (multiple instances, mixed state) +- **Updates:** Last KB5075039 2026-03-04 (current) +- **Local Admins:** Administrator, localadmin, rootadmin +- **Users:** User (generic account, active) +- **Software:** Chrome, Edge, ScreenConnect, Splashtop, Syncro — **no M365, no productivity apps** +- **Issues:** **On Guest WiFi (10.0.50.x) — should be internal**, Win Home, no BitLocker, no productivity software, minimal setup laptop + +### LAPTOP-E0STJJE8 +- **OS:** Windows 10 Home 22H2 (Build 19045) — **cannot domain join** +- **Domain:** WORKGROUP (not joined) +- **Hardware:** Lenovo 81W1 Notebook | Serial: PF2L3AAQ +- **CPU:** AMD Ryzen 5 3500U (4C/8T) | **RAM:** 5.9 GB +- **Disk:** C: 237.2 GB / 173.3 GB free (27%) — SK Hynix NVMe SSD +- **Network:** Wi-Fi @ 10.0.20.200/24 (DHCP) | DNS: 192.168.0.1 | MAC: DA:F3:D8:88:F9:C7 +- **BitLocker:** Not enabled +- **AV:** Datto AV (mixed) + **McAfee LiveSafe + WebAdvisor (bloatware, conflicts)** +- **Updates:** Last KB5075039 2026-03-04 (current) +- **Local Admins:** Administrator, localadmin, rootadmin +- **Users:** User (generic account, active) +- **Software:** Chrome, Edge, McAfee LiveSafe, Zoom — **no M365, no productivity apps** +- **Issues:** Win Home, McAfee conflicting with Datto AV, no BitLocker, minimal setup laptop + +### LAPTOP2 +- **OS:** Windows 11 Pro 24H2 (Build 26100) +- **Domain:** WORKGROUP (not joined) +- **Hardware:** Lenovo 82QD Notebook | Serial: PF5D2015 +- **CPU:** 12th Gen Intel i5-1235U (10C/12T) | **RAM:** 11.7 GB +- **Disk:** C: 474.7 GB / 395.4 GB free (17%) — Samsung NVMe SSD +- **Network:** Wi-Fi @ 192.168.2.118/22 (DHCP) | DNS: 192.168.0.1 | MAC: 70:08:94:93:8E:F5 +- **BitLocker:** Encrypted, **Protection OFF** — no key protectors +- **AV:** Datto AV (enabled) +- **Updates:** Last KB5063666 **2025-07-14 — 8 MONTHS BEHIND (CRITICAL)** +- **Local Admins:** Administrator, Localadmin +- **Users:** Training2 (active) +- **Software:** M365, Firefox — minimal/clean install +- **Issues:** **8 months without updates**, BitLocker protection OFF, training laptop with minimal software + +### MAINTENANCE-PC +- **OS:** Windows 11 Pro 25H2 (Build 26200) — **upgraded 2026-04-13, manual key** +- **Domain:** PENDING domain join +- **Hardware:** Lenovo F0EW005TUS AIO | Serial: MP1Z9NPA +- **CPU:** AMD Ryzen 3 4300U (4C/4T) | **RAM:** 7.4 GB +- **Disk:** C: 237.2 GB / low — cleanup in progress (SoftwareDistribution cleared, nick profile deleted, DISM cleanup run) +- **Network:** Wi-Fi @ 192.168.3.156/22 (DHCP) | DNS: 192.168.0.1 | MAC: 5C:61:99:24:E7:5F +- **BitLocker:** Not enabled +- **AV:** Datto AV + Malwarebytes 5.5.0 +- **Updates:** Last KB5079473 2026-03-11 (current) +- **Local Admins:** Administrator, casadmin201, localadmin — nick removed 2026-04-13 +- **Users:** Bruce Miller (active), casadmin201, John Trozzi (disabled) — nick profile deleted 2026-04-13 +- **Domain User:** CASCADES\John.Trozzi (Maintenance) — pending domain join +- **Software:** M365 Apps, Adobe Acrobat, 8x8 Network Utility, Brother MFC-9340CDW, Brother MFC-L2820DW, Epson ET-5800, Malwarebytes, Zoom, Splashtop, Syncro +- **Printers:** Maintenance Printer (Brother USB), KONICA MINOLTA bizhub C368 (WSD), iR-ADV C478 (WSD), Epson ET-5800 (USB), Brother MFC-L2820DW (USB) +- **Fixes Applied:** Wi-Fi power saving disabled + Fast Startup disabled (2026-03-25) — was dropping Wi-Fi after idle. OneDrive uninstalled (2026-03-26) — corrupt Telemetry.dll causing entry point error on boot, user doesn't use OneDrive. +- **TODO:** Domain join, local account cleanup (remove casadmin201, old local users), finish disk cleanup + +### MDIRECTOR-PC +- **OS:** Windows 11 Home 25H2 (Build 26200) — **cannot domain join** +- **Domain:** WORKGROUP (not joined) +- **Hardware:** Acer Aspire C24-865 AIO | Serial: DQBBUAA0049100D7043000 +- **CPU:** Intel i5-8250U (4C/8T) | **RAM: 3.9 GB (critically low)** +- **Disk:** C: 475.7 GB / 194.3 GB free (59%) — Patriot P210 512GB SSD +- **Network:** Ethernet @ 10.0.20.71/24 (DHCP) | DNS: 192.168.0.1 | MAC: 98:EE:CB:9D:8A:81 | 1 Gbps (hardwired) +- **BitLocker:** Not enabled +- **AV:** **COMODO Antivirus (DISABLED)** — Windows Defender is active instead +- **Updates:** Last KB5079473 2026-03-11 (current) +- **Local Admins:** Administrator, localadmin +- **Users:** Shelby Trozzi (active), Anna Pitzlin (old — last 2025-06-26) +- **Software:** M365 Apps, Adobe Acrobat, Brother MFC-L8850CDW, Canon MF750C, CrystalDiskInfo, Synology Drive, Firefox, Splashtop, Syncro +- **Mapped Drives:** H: \\cascadesds\homes, M: \\cascadesds\Management, P: \\cascadesds\Public (all Synology!) +- **Printers:** Memcare Director Printer (Canon UFR II USB), iR-ADV C478 (WSD), KONICA MINOLTA bizhub C368 (WSD) +- **Issues:** **Only 3.9 GB RAM**, Win Home, COMODO AV disabled (remove it), still mapped to Synology directly, no BitLocker + +### MEMRECEPT-PC +- **OS:** Windows 10 Home 22H2 (Build 19045) — **cannot domain join** +- **Domain:** WORKGROUP (not joined) +- **Hardware:** No-brand PC (no manufacturer info) | Serial: none +- **CPU:** **Pentium Dual-Core E5500 @ 2.80GHz (2C/2T) — ANCIENT** +- **RAM:** 6 GB | **NIC:** 100 Mbps only +- **Disk:** C: 237.4 GB / 110.3 GB free (54%) — ADATA SSD +- **Network:** Ethernet @ 192.168.3.41/22 (DHCP) | DNS: 192.168.0.1 | MAC: BC:AE:C5:46:83:77 | 100 Mbps +- **BitLocker:** Not enabled +- **AV:** Datto AV (enabled) +- **Updates:** Last KB5072653 **2025-11-18 — 4 MONTHS BEHIND** +- **Local Admins:** Administrator, CasAdmin201, localadmin +- **Users:** memfrtdesk (active, **no password required**), CasAdmin201, Matthew (old) +- **Software:** M365 Apps, Brother MFC-L8900CDW, Canon MF Scan, Epson ET-5800, Synology Drive, Chrome, **TightVNC 2.8.27** (security risk), Splashtop, Syncro +- **Printers:** Epson ET-5800 (USB), Brother MFC-L8900CDW (BRW283A4D1AD571) +- **Printer port mismatches:** 192.168.0.145 → 192.168.1.138, 192.168.0.55 → 192.168.45.191 +- **Issues:** **Ancient hardware** (Pentium E5500, 100Mbps NIC), 4 months behind updates, TightVNC installed (remove), user has no password, Win Home, stale printer ports + +### NURSESTATION-PC +- **OS:** Windows 10 Pro for Workstations 22H2 (Build 19045) +- **Domain:** WORKGROUP (not joined) +- **Hardware:** Acer Aspire C24-963 AIO | Serial: DQBEQAA00302904E4D3000 +- **CPU:** Intel i3-1005G1 (2C/4T) | **RAM:** 7.8 GB +- **Disk:** C: 475.8 GB / 309.2 GB free (35%) — WDC NVMe SSD +- **Network:** Wi-Fi @ 192.168.3.135/22 (DHCP) | DNS: 192.168.0.1 | MAC: 80:30:49:3D:5A:C1 +- **BitLocker:** Not enabled +- **AV:** Datto AV (enabled) +- **Updates:** Last KB5075039 2026-03-03 (current) +- **Local Admins:** Administrator, CasAdmin201, localadmin +- **Users:** **Nurses (shared account, no password required)**, CasAdmin201, Adella Clark (last 2021), April Hughes (last 2020) +- **Software:** M365 Apps, Adobe Acrobat, Brother MFC-L8900CDW, Firefox, Splashtop, Syncro +- **Printers:** Brother MFC-L8900CDW (BRW283A4D1AD571) +- **Printer port mismatches:** 192.168.0.145 → 192.168.1.138, 192.168.0.55 → 192.168.45.191 +- **Issues:** Shared "Nurses" account with no password (HIPAA violation), Pro for Workstations but not domain-joined, stale user profiles from 2020-2021, stale printer ports + +### RECEPTIONIST-PC +- **OS:** Windows 11 Pro 25H2 (Build 26200) +- **Domain:** WORKGROUP (not joined) +- **Hardware:** Lenovo 11CDS0DC00 AIO | Serial: MJ0KQHNP +- **CPU:** Intel i5-10500 (6C/12T) | **RAM:** 15.7 GB +- **Disk:** C: 475.7 GB / 342.2 GB free (28%) — Samsung NVMe SSD +- **Network:** Wi-Fi @ 192.168.2.17/22 (DHCP) | DNS: 192.168.0.1 | MAC: 98:59:7A:B0:06:58 +- **BitLocker:** Encrypted, **Protection OFF** — no key protectors +- **AV:** **Bitdefender Endpoint Security Tools + Datto AV** (both running — potential conflict) +- **Updates:** Last KB5077181 2026-02-21 +- **Local Admins:** Administrator, CasAdmin201, localadmin +- **Users:** **Front Desk (shared account, no password required)**, CasAdmin201 +- **Mapped Drives:** S: \\cs-server\Receptionist, Z: \\cs-server\directoryshare +- **Software:** M365 Apps, Adobe Acrobat, Bitdefender, Brother MFC-L8900CDW, Canon Generic Plus, Epson ET-5800, Chrome, Splashtop, Syncro +- **Printers:** Front Desk (Brother MFC-L8900CDW, BRWE86F385A28AB → 192.168.0.33), ET-5800 (network), CopyRoom (Canon, 192.168.2.219 → 192.168.2.230) +- **Issues:** Shared "Front Desk" account with no password (HIPAA violation), BitLocker protection OFF, dual AV conflict (Bitdefender + Datto), not domain-joined despite Pro license + +## Critical Issues — Fleet-Wide + +### Updates Critically Behind +| Machine | Last Update | Months Behind | +|---------|-----------|---------------| +| DESKTOP-LPOPV30 | 2025-02-02 | **13 months** | +| LAPTOP2 | 2025-07-14 | **8 months** | +| CRYSTAL-PC | 2025-10-18 | **5 months** | +| MEMRECEPT-PC | 2025-11-18 | **4 months** | +| ASSISTMAN-PC | 2025-12-20 | **3 months** | +| DESKTOP-KQSL232 | 2025-12-20 | **3 months** | + +### Shared Accounts with No Password (HIPAA violations) +| Machine | Account | Role | +|---------|---------|------| +| NURSESTATION-PC | Nurses | Nurse station | +| MEMRECEPT-PC | memfrtdesk | MemCare front desk | +| RECEPTIONIST-PC | Front Desk | Main reception | +| DESKTOP-KQSL232 | Lois Lane | User with PasswordRequired=False | + +### RDP Exposed +| Machine | NLA | +|---------|-----| +| ASSISTMAN-PC | **No NLA — critical** | +| DESKTOP-U2DHAP0 | **No NLA — critical** | +| CHEF-PC | Yes (NLA required) | + +### Hardware Replacement Needed +| Machine | Issue | +|---------|-------| +| MEMRECEPT-PC | Pentium E5500, 6GB RAM, 100Mbps NIC — ancient, needs replacement | +| MDIRECTOR-PC | Only 3.9 GB RAM — needs RAM upgrade or replacement | + +### Software to Remove (old MSP / security risks) +| Machine | Software | Risk | +|---------|----------|------| +| MEMRECEPT-PC | TightVNC 2.8.27 | Unauthorized remote access | +| LAPTOP-E0STJJE8 | McAfee LiveSafe + WebAdvisor | AV conflict with Datto | +| MDIRECTOR-PC | COMODO Antivirus (disabled) | Stale AV, should be removed | +| CHEF-PC | Norton Security Scan | Old bloatware | +| ALL machines | Splashtop Streamer | Previous MSP remnant | +| ALL machines | Syncro RMM | Previous MSP remnant (or current?) | + +### Windows Home Machines (cannot domain join without Pro upgrade) +ANN-PC, LAPTOP-DRQ5L558, LAPTOP-E0STJJE8, MAINTENANCE-PC, MDIRECTOR-PC, MEMRECEPT-PC (6 machines) diff --git a/clients/dataforth/docs/active-directory.md b/clients/dataforth/docs/active-directory.md new file mode 100644 index 0000000..e867c3d --- /dev/null +++ b/clients/dataforth/docs/active-directory.md @@ -0,0 +1,109 @@ +# Active Directory + +## Domain Info +- Domain: intranet.dataforth.com +- Forest Level: Windows Server 2016 +- Domain Level: Windows Server 2016 +- Domain Controllers: AD1 (192.168.0.27, primary), AD2 (192.168.0.6, secondary) +- FSMO Roles: All on AD1 (assumed) + +## Organizational Units +| OU | Purpose | Entra Sync | +|----|---------|------------| +| Domain Controllers | DCs | — | +| CompanyUsers | Main user OU | — | +| Azure_Users | Azure-related users | — | +| SyncedUsers | Users synced to Entra ID | Yes | +| ServiceAccounts | Service accounts | No | +| Servers | Server computer accounts | — | +| Workstations | Workstation computer accounts | — | +| DistoGroups | Distribution groups | — | + +## Active Human Users (as of 2026-04-02) +| Name | Username | Last Logon | Notes | +|------|----------|------------|-------| +| Ben Wadzinski | bwadzinski | 2026-04-01 | | +| Jacque Antar | jantar | 2026-04-01 | | +| Martin Florez | mflorez | 2026-04-02 | | +| Kevin Wackerly | kwackerly | 2026-03-30 | | +| Otto Fest | ofest | 2026-03-30 | | +| Lee Payne | lpayne | 2026-03-29 | | +| John Lehman | jlehman | 2026-03-29 | Engineering | +| Georg Haubner | ghaubner | 2026-03-27 | Engineering, has D: backup | +| Kellyn Wackerly | Kellynwackerly | 2026-03-26 | | +| Jaime Becerra | JBecerra | 2026-03-26 | | +| Angel Lopez | alopez | 2026-03-25 | | +| Dan Center | dcenter | 2026-03-23 | Operations | +| Logan Tobey | ltobey | 2026-03-23 | | +| Patricia | patricia | 2026-03-23 | | +| Peter Iliya | pIliya | 2026-03-23 | Applications Engineer | +| Sandra Schock | sSchock | 2026-03-23 | | +| Theresa Dean | tdean | 2026-03-23 | | +| Bobbi Whitson | bwhitson | 2026-03-23 | | +| Ayleen Montijo | aMontijo | 2026-03-23 | | +| Ken Hoffman | khoffman | 2026-03-10 | Also has "oemdata" account | +| Ken Hoffman | oemdata | N/A | TestDataSheetUploader author | +| Joel Lohr | jlohr | 2026-03-31 | **RETIRING — disable after 03/31** | + +## Service / System Accounts +| Username | Purpose | Notes | +|----------|---------|-------| +| sysadmin | Domain Admin | — | +| Administrator (Admin_3652) | Built-in admin | — | +| svc_testdatadb | TestDataDB service | OU=ServiceAccounts, created 2026-03-28 | +| sqluser | SQL Server service | OU=ServiceAccounts | +| MSOL_664594195fe2 | Entra ID Sync (Azure AD Connect) | — | +| ClaudeTools-ReadOnly | Read-only automation access | Purpose unclear | + +## Machine / Functional Accounts +- Assembly Stations: AS24, AS26, AS30, AS31, AS34 +- Test Stations: TS1, TS1L, TS1R, TS2L, TS2R, etc. (30+ stations) +- Manufacturing: hipot, encap, Endcap, my9 +- Label/Scanning: labelpc, scan, scand2 +- Mobile: tablet01–07, hh01–04 +- Shared: confroom, Training + +## Disabled Accounts +Alex Mitev, Annie Chin, Bill Oldham, Brian Faires, Brian Scaramella, calibration, Jerry Lopez, John Barrios, Linda D, Maria Cota, Michele Hvidsten, Mizan Rahman, Moe Naseem, Stephen Poanessa, Steve Lehman, Support Pool, William Oldham, wcarr + +## Groups +| Group | Scope | Notes | +|-------|-------|-------| +| Domain Admins | Global | Standard | +| Enterprise Admins | Universal | Forest-wide | +| Schema Admins | Universal | Schema modification | +| Administrators | DomainLocal | Local admin | +| ADSyncAdmins | DomainLocal | Azure AD Connect | +| DnsAdmins | DomainLocal | DNS management | +| Hyper-V Administrators | DomainLocal | Hyper-V | +| Key Admins | Global | Key management | +| Enterprise Key Admins | Universal | Enterprise keys | +| Storage Replica Admins | DomainLocal | Storage replication | + +**No custom security groups found** — only default/built-in groups. + +## Group Policy Objects +| GPO | Status | Last Modified | +|-----|--------|---------------| +| Default Domain Policy | AllSettingsEnabled | 2026-03-02 | +| Default Domain Controllers Policy | AllSettingsEnabled | 2025-09-30 | +| TrustedZones | AllSettingsEnabled | 2025-10-01 | +| Screenconnect | AllSettingsEnabled | 2025-10-01 | +| Profwiz | AllSettingsEnabled | 2025-10-08 | +| Mapped Drives | AllSettingsEnabled | 2025-10-09 | + +## Drive Mappings (GPO: Mapped Drives) +| Letter | Path | Purpose | +|--------|------|---------| +| B: | \\\\ad1\itsvc | IT service files | +| Q: | \\\\ad2\c-drive | AD2 C-drive share | +| S: | \\\\SAGE-SQL\sage | Sage ERP | +| T: | \\\\ad2\e-drive | AD2 E-drive share | +| W: | \\\\files-d1\sales | Sales docs | +| X: | \\\\ad2\webshare | Datasheets (For_Web) | +| Y: | \\\\files-d1\archive | Archive | + +## Action Items +- **[HIGH]** Disable jlohr account — retirement was 2026-03-31, **OVERDUE** +- Investigate ClaudeTools-ReadOnly account purpose +- Ken Hoffman has two accounts (khoffman + oemdata) — consolidate? diff --git a/clients/dataforth/docs/billing-log.md b/clients/dataforth/docs/billing-log.md new file mode 100644 index 0000000..8c221b3 --- /dev/null +++ b/clients/dataforth/docs/billing-log.md @@ -0,0 +1,56 @@ +# Dataforth — Work Log / Billing Record + +## Session 1 — 2026-04-02 (Remote — Documentation Audit) + +**Focus:** Full client documentation buildout from Mike Swanson handoff + post-incident audit + +| Time | Task | Details | +|------|------|---------| +| | Client intake & overview | Created overview.md — company info, Dan Center contact (replacing retired Joel Lohr), Mike Swanson as outgoing IT, M365 tenant 7dfa3ce8-c496-4b51-ab8d-bd3dcd78b584, ~21 human users, 6 servers, 2 ESXi + 1 Hyper-V, ~38 workstations, 64 DOS test stations | +| | Network documentation | Built topology.md, dns.md, dhcp.md, firewall.md, vlans.md for flat network (no VLANs, all Windows Firewall profiles disabled on AD2) | +| | Cloud documentation | Built m365.md + azure.md — tenant info, Entra ID Sync from OU=SyncedUsers, MFA enforcement deadline April 4, 19 users still need to register | +| | Security documentation | Built antivirus.md + backup.md | +| | RMM documentation | Documented Datto RMM + GuruRMM (azcomputerguru.com) | +| | Active Directory doc | Built active-directory.md — intranet.dataforth.com forest, Windows Server 2016 level | +| | Per-server docs (6 servers) | AD1, AD2, FILES-D1, SAGE-SQL, 3CX, DF-HYPERV-B, D2TESTNAS | +| | Workstation inventory | Built workstations.md — Engineering (~12), Manufacturing/Assembly (~14), Office/Admin (~12), 3 EOL Windows 7 (LABELPC, LABELPC2, D2-RCVG-003) | +| | Manufacturing doc | Built manufacturing.md — 64 DOS stations running QuickBASIC 4.5 ATE on MS-DOS 6.22, SMB1 via D2TESTNAS Samba proxy, TestDataDB (Node.js + SQLite on AD2:3000, 2.28M test records) | +| | Issue log buildout | Documented 2025 ransomware incident (AD2 wiped/rebuilt), 2026-03-27 DF-JOEL2 phishing compromise (Angel Raya/ScreenConnect social engineering, C2 blocked, IC3 complaint, jlohr reset) | +| | Risk inventory | Critical/High/Medium/Low risk catalog: firewall disabled on AD2, Win7 machines, AD1 at 90% disk, jlohr account overdue for disable, 28 machines not scanned, etc. | + +### Billing Summary — Session 1 +| Category | Items | +|----------|-------| +| Client onboarding / intake | Full Mike Swanson handoff documented | +| Documentation buildout | 22 files created across overview, network, cloud, security, rmm, servers, workstations, manufacturing, issues | +| Post-incident risk audit | 2025 ransomware + 2026-03-27 phishing compromise fully documented with follow-ups | + +**Time:** File timestamps span ~10:04 AM → 12:45 PM (~2.5–3 hrs) + +--- + +## Outstanding Work — Prioritized + +### Critical +- All Windows Firewall profiles disabled on AD2 — re-enable +- 3 Windows 7 machines still on network — retire or isolate +- AD1 C: drive at 90% capacity (C:\Engineering = 787 GB) — expand or clean +- AD1/AD2 on Windows Server 2016 (end of mainstream support) — plan upgrade + +### High +- Joel Lohr (jlohr) account — disable post-retirement (**OVERDUE since 2026-03-31**) +- C2 IP blocks on UDM are iptables rules only — make permanent in UniFi UI +- 28 machines offline during incident — rescan when available +- MFA enforcement (April 4) — 19 users still need to register +- No reverse DNS zone for 192.168.0.x +- Website upload mechanism broken (ASP.NET 404s) + +### Medium +- D2TESTNAS uses root SSH with password auth +- Stale/conflicting computer account IPs +- ~845K test records pending ForWeb export + +### Low +- DVD ISO mounted on AD2 D: +- ClaudeTools-ReadOnly AD account — purpose unclear +- DESKTOP-* BYOD-looking hostnames diff --git a/clients/dataforth/docs/cloud/azure.md b/clients/dataforth/docs/cloud/azure.md new file mode 100644 index 0000000..04d4e23 --- /dev/null +++ b/clients/dataforth/docs/cloud/azure.md @@ -0,0 +1,14 @@ +# Azure / Cloud Services + +## Azure +No Azure IaaS services identified. Entra ID is used for M365 sync only. + +## Other Cloud/Web Services +| Service | Purpose | Notes | +|---------|---------|-------| +| dataforth.com | Company website + test datasheet portal | Upload endpoints currently return 404 | +| legacy.dataforth.com | Legacy test data reports | /TestDataReport_Print.aspx still works, no auth required | + +## Notes +- Website upload mechanism is broken post-crypto attack — old ASP.NET endpoints return 404 +- Legacy datasheet viewer still functional but unauthenticated diff --git a/clients/dataforth/docs/cloud/m365.md b/clients/dataforth/docs/cloud/m365.md new file mode 100644 index 0000000..077491f --- /dev/null +++ b/clients/dataforth/docs/cloud/m365.md @@ -0,0 +1,31 @@ +# Microsoft 365 / Entra ID + +## Tenant Info +- Tenant ID: 7dfa3ce8-c496-4b51-ab8d-bd3dcd78b584 +- Primary Domain: dataforth.com +- Admin Portal: https://admin.microsoft.com + +## Entra ID (Azure AD) +- Hybrid Joined: Yes — Azure AD Connect +- Sync Account: MSOL_664594195fe2 +- Syncs From: OU=SyncedUsers +- Does NOT Sync: OU=ServiceAccounts +- Password Hash Sync: Unknown + +## Conditional Access Policies +Deployed 2026-03-27, **report-only until April 4, 2026**: + +| Policy | Details | +|--------|---------| +| Require MFA | Skip from office IP 67.206.163.122 | +| Block foreign sign-ins | US only, MFA-Travel-Bypass group for exceptions | +| Block legacy authentication | Blocks all legacy auth protocols | + +## MFA Status +- MFA-Ready: 19/38 users +- Need to Register: 19 users +- **Enforcement Date: April 4, 2026** + +## Notes +- MFA-Travel-Bypass is likely an Entra ID group (not on-prem AD) +- No custom security groups found in on-prem AD diff --git a/clients/dataforth/docs/issues/log.md b/clients/dataforth/docs/issues/log.md new file mode 100644 index 0000000..905bafd --- /dev/null +++ b/clients/dataforth/docs/issues/log.md @@ -0,0 +1,61 @@ +# Issue Log + +### 2025 — Crypto/Ransomware Attack +- **Severity:** Critical +- **Symptoms:** Ransomware encryption across network +- **Impact:** AD2 wiped and rebuilt. Many files lost including C:\DFWDS\, scheduled tasks, service configs. Test datasheet pipeline (DFWDS.exe, VB6) destroyed. +- **Resolution:** AD2 rebuilt. Pre-attack backup exists on HGHAUBNER D: drive. TestDataDB pipeline rebuilt 2026-03-27–29. +- **Lessons Learned:** No adequate backup existed. Flat network allowed lateral movement. + +--- + +### 2026-03-27 — DF-JOEL2 Workstation Compromise +- **Reported By:** Mike Swanson +- **Severity:** Critical +- **Target:** Joel Lohr's workstation (DF-JOEL2, 192.168.0.174) +- **Vector:** Phishing email to personal Yahoo account +- **Attacker:** "Angel Raya" via ScreenConnect social engineering +- **C2 IPs:** 80.76.49.18, 45.88.91.99 (AS399486, Virtuo, Montreal QC) +- **C2 Cloud:** instance-wlb9ga-relay.screenconnect.com +- **M365 Impact:** jlohr account compromised from Turkey/UK/Germany +- **Resolution:** + - C2 IPs blocked at UDM firewall (iptables rules — need permanent UniFi UI rules) + - 3 rogue ScreenConnect clients uninstalled + - jlohr AD password reset, M365 sessions revoked + - 32 machines scanned clean, 28 unreachable (offline) + - No lateral movement detected + - IC3 Complaint: 1c32ade367084be9acd548f23705736f + - ConnectWise Case: 03464184 + - C2 hosting SUSPENDED by provider +- **Follow-up:** Joel Lohr retired 2026-03-31. Auto-reply set to Dan Center. +- **Lessons Learned:** Personal email on work machines is a phishing vector. ScreenConnect brand used for social engineering. + +--- + +## Known Issues & Risks (from 2026-04-02 audit) + +### Critical +- All Windows Firewall profiles **DISABLED** on AD2 +- Windows 7 machines still on network (LABELPC, LABELPC2, D2-RCVG-003) +- AD1 and AD2 are Windows Server 2016 (end of mainstream support) +- AD1 C: drive at **90% capacity** (C:\Engineering = 787 GB) + +### High +- Joel Lohr account (jlohr) needs to be disabled post-retirement (March 31) — **OVERDUE** +- 28 machines not scanned during security incident (were offline) +- C2 IP blocks are iptables rules on UDM — need permanent UniFi UI rules +- No reverse DNS zone for 192.168.0.x +- MFA enforcement deadline April 4, 2026 — 19 users still need to register +- Website upload mechanism broken (old ASP.NET endpoints return 404) + +### Medium +- D2TESTNAS uses root SSH with password authentication +- Multiple DESKTOP-* computer names suggest unmanaged/BYOD devices +- ~845K test records pending ForWeb export +- Some computer accounts have stale/conflicting IP addresses +- TestDataDB Server scheduled task still exists (disabled, replaced by service) + +### Low +- DVD ISO still mounted on AD2 D: drive +- ClaudeTools-ReadOnly AD account — purpose unclear +- Multiple duplicate/old computer accounts in AD diff --git a/clients/dataforth/docs/manufacturing.md b/clients/dataforth/docs/manufacturing.md new file mode 100644 index 0000000..dfac22b --- /dev/null +++ b/clients/dataforth/docs/manufacturing.md @@ -0,0 +1,95 @@ +# Manufacturing Test Infrastructure + +## DOS Test Stations (64 total) +- Stations: TS-1 through TS-30 (plus L/R variants for dual-station setups) +- Dev/Test: TS-GURU, TS-TOM +- OS: MS-DOS 6.22 +- Software: QuickBASIC 4.5 ATE programs +- Network: SMB1 via D2TESTNAS Samba proxy +- Not domain-joined + +### Boot Sequence +AUTOEXEC.BAT v4.1 (deployed 2026-03-12): +1. `STARTNET.BAT` → Map T: (\\\\D2TESTNAS\test) and X: (\\\\D2TESTNAS\datasheets) +2. `NWTOC.BAT` → Download software updates from T:\COMMON\ProdSW +3. `CTONW.BAT` → Upload DAT files to T:\TS-XX\LOGS\ +4. `CTONWTXT.BAT` → Upload TXT datasheets to T:\STAGE\TS-XX\ +5. `menux` → Launch test menu system + +### Test Programs by Product Family +| Family | Programs | Description | +|--------|----------|-------------| +| SCM5B | TEST5B1E/2E | Voltage/current/TC/RTD modules | +| SCM5B | TST5B45B | Frequency/counter | +| SCM5B | TST5B481 | Multi-bandwidth | +| SCM5B | TST5B49B | Sample & hold | +| 8B | TEST8B1D/2D | 8B series modules | +| DSCA | KDSCOUT1/2 | Output modules | +| DSCA | TSTDIN1B/2B | Input modules | +| DSCT | TST5SCT1/2 | Transmitters | +| SCM7B | 7BMAIN4, TEST7B1C/2C/3C | 7B series modules | + +## Test Datasheet Pipeline + +### Original Pipeline (pre-crypto attack, broken) +1. QuickBASIC writes DAT (binary) + TXT (formatted) on DOS machines +2. CTONW.BAT uploads DAT to NAS, CTONWTXT.BAT uploads TXT +3. DFWDS.exe (VB6) validates/renames files, moves to X:\For_Web +4. TestDataSheetUploader (VB.NET) syncs to dataforth.com via HTTP +5. Website serves at dataforth.com/TestDataReport + +Status: Steps 3–4 broken after crypto wipe. Step 2 (CTONWTXT) was not being called. + +### New Pipeline (rebuilt 2026-03-27–29) +1. DOS machines write DAT files → NAS (via CTONW.BAT) ✓ +2. Sync-FromNAS pulls DAT to AD2 every 15 min ✓ +3. import.js parses DAT into SQLite database ✓ +4. export-datasheets.js generates exact-match TXT → X:\For_Web ✓ +5. Website upload mechanism **TBD** (old endpoints return 404) + +**Key improvement:** Datasheets generated server-side from DAT data. Eliminates need for CTONWTXT.BAT, DFWDS.exe, and DOS-side TXT transfer. + +## Model Specifications (Spec Files) +| File | Models | Family | +|------|--------|--------| +| 5BMAIN.DAT | 481 | SCM5B | +| 5B45DATA.DAT | 56 | SCM5B frequency/counter | +| DB5B48.DAT | 3 | SCM5B multi-bandwidth | +| 5B49_2.DAT | 15 | SCM5B sample & hold | +| 8BMAIN.DAT | 148 | 8B | +| DSCOUT.DAT | 23 | DSCA output | +| DSCMAIN4.DAT | 391 | DSCA input | +| SCTMAIN.DAT | 103 | DSCT transmitters | +| 7BMAIN.DAT | 276 | SCM7B | +| **Total** | **1,470+** | | + +Location: C:\Shares\testdatadb\specdata\ +Source: \\\\AD1\Engineering\ENGR\ATE\\\ + +## Webshare Layout (X: / C:\Shares\webshare on AD2) +| Path | Contents | Count | +|------|----------|-------| +| X:\For_Web\ | Validated test datasheets | ~1,058 current year | +| X:\For_Web\2011\–2025\ | Archived by year | 500K+ files total | +| X:\For_Web_PDF\ | PDF versions | ~4,773 | +| X:\Test_Datasheets\ | Incoming/staging from DFWDS | — | +| X:\Bad_Datasheets\ | Invalid files | ~18,801 | +| X:\Datasheets_Log\ | DFWDS processing logs | ~3,336 | + +## TestDataDB Statistics +| Metric | Value | +|--------|-------| +| Test Records | 2,281,524 | +| Work Orders | 33,745 (63,263 test lines) | +| Records with WO | 2,277,183 | +| ForWeb Exported | 1,435,989 | +| Pending Export | ~845K | +| Model Specs | 1,470+ | + +## Future Product Lines (not yet integrated) +| Product | Format | Location | Notes | +|---------|--------|----------|-------| +| MAQ20 | XLS (multi-sheet) | T:\ENGR\DESIGN\MAQ20 Design\Test Data | Needs K: drive move, path update | +| PWRM10 | XLS | U:\DESIGN\PWRM10...\Test Data\Final Pass Test | — | +| 10D | JSON | K:\10D\first_pass and second_pass | ~May 2026 | +| DSCMHV | — | — | New line, uses MAQ20/PWRM naming standard | diff --git a/clients/dataforth/docs/network/dhcp.md b/clients/dataforth/docs/network/dhcp.md new file mode 100644 index 0000000..22216b3 --- /dev/null +++ b/clients/dataforth/docs/network/dhcp.md @@ -0,0 +1,11 @@ +# DHCP Configuration + +## DHCP Server +- Details not captured in audit +- Likely running on UDM (192.168.0.254) or AD1 (192.168.0.27) + +## Known Static IPs +See `network/topology.md` for server IPs. All servers appear to be statically assigned on 192.168.0.0/24. + +## Notes +- DHCP scope details need to be captured from UDM or AD1 diff --git a/clients/dataforth/docs/network/dns.md b/clients/dataforth/docs/network/dns.md new file mode 100644 index 0000000..b3544ac --- /dev/null +++ b/clients/dataforth/docs/network/dns.md @@ -0,0 +1,26 @@ +# DNS Configuration + +## Internal DNS Servers +| Server Name | IP Address | Role | +|-------------|-----------|------| +| AD1 | 192.168.0.27 | Primary DNS | +| AD2 | 192.168.0.6 | Secondary DNS | + +## DNS Zones +| Zone | Type | Notes | +|------|------|-------| +| intranet.dataforth.com | Primary | Main forward lookup zone | +| _msdcs.intranet.dataforth.com | Primary | DC locator records | +| 0.in-addr.arpa | Primary | Auto-created | +| 127.in-addr.arpa | Primary | Auto-created | +| 255.in-addr.arpa | Primary | Auto-created | +| TrustAnchors | Primary | DNSSEC anchors | + +## Known Issues +- **[HIGH] No reverse lookup zone for 192.168.0.x** — PTR lookups will fail + +## External DNS +- Primary Domain: dataforth.com + +## Notes +- DNS is AD-integrated on both domain controllers diff --git a/clients/dataforth/docs/network/firewall.md b/clients/dataforth/docs/network/firewall.md new file mode 100644 index 0000000..3768913 --- /dev/null +++ b/clients/dataforth/docs/network/firewall.md @@ -0,0 +1,23 @@ +# Firewall Configuration + +## Gateway Device +- Device: UniFi Dream Machine (UDM) +- IP: 192.168.0.254 +- Public IP: 67.206.163.122 + +## Firewall Rules (UDM) +- C2 IPs blocked (iptables): 80.76.49.18, 45.88.91.99 (from 2026-03-27 incident) +- **[HIGH]** These blocks are iptables rules — need permanent UniFi UI rules + +## Windows Firewall (AD2) +| Profile | Status | +|---------|--------| +| Domain | **DISABLED** | +| Private | **DISABLED** | +| Public | **DISABLED** | + +**[CRITICAL]** All Windows Firewall profiles are disabled on AD2. + +## Notes +- No dedicated firewall appliance — UDM handles all perimeter firewall duties +- AD2 firewall was opened to HGHAUBNER D$ share on 2026-03-27 for backup access diff --git a/clients/dataforth/docs/network/topology.md b/clients/dataforth/docs/network/topology.md new file mode 100644 index 0000000..a47b2e9 --- /dev/null +++ b/clients/dataforth/docs/network/topology.md @@ -0,0 +1,36 @@ +# Network Topology + +## Internet Connection +- Public IP: 67.206.163.122 +- Gateway/Router: UniFi Dream Machine (UDM) at 192.168.0.254 + +## Network Segments +| Segment | Subnet | Purpose | +|---------|--------|---------| +| Main LAN | 192.168.0.0/24 | Servers, workstations, DOS test stations | +| Secondary | 192.168.1.x | Some workstations | +| VPN/Remote | 192.168.6.x | VPN / remote access | + +## Key Infrastructure IPs +| Device | IP | OS / Type | Role | +|--------|-----|-----------|------| +| AD1 | 192.168.0.27 | Win Server 2016 | Primary DC, DNS, WINS/NPS | +| AD2 | 192.168.0.6 | Win Server 2016 | Secondary DC, DNS, DFS, TestDataDB | +| FILES-D1 | 192.168.0.189 | Win Server 2016 | File Server | +| SAGE-SQL | 192.168.0.153 | Win Server 2016 | Sage ERP Database | +| 3CX | 192.168.0.125 | Win Server 2016 | Phone System | +| D2TESTNAS | 192.168.0.9 | Debian 13 / Samba | SMB1 proxy for DOS machines | +| ESXi-122 | 192.168.0.122 | VMware ESXi | Hypervisor | +| ESXi-124 | 192.168.0.124 | VMware ESXi | Hypervisor | +| DF-HYPERV-B | 192.168.0.123 | Win Server 2025 | Hyper-V Host | +| UDM | 192.168.0.254 | UniFi Dream Machine | Gateway/Router | +| ENG-DEV-SERVER | 192.168.0.126 | Win 11 Pro | Engineering Dev Server | + +## WINS / NPS +- Server: AD1 (192.168.0.27) +- NPS Ports: 1812/1813 + +## Notes +- Flat network — no VLANs, everything on 192.168.0.0/24 +- DOS test stations (64) use SMB1 via D2TESTNAS Samba proxy +- No dedicated firewall appliance — UDM handles routing and firewall diff --git a/clients/dataforth/docs/network/vlans.md b/clients/dataforth/docs/network/vlans.md new file mode 100644 index 0000000..5141b7a --- /dev/null +++ b/clients/dataforth/docs/network/vlans.md @@ -0,0 +1,15 @@ +# VLANs + +## Current State +**No VLANs configured.** Dataforth runs a flat network — all devices on 192.168.0.0/24. + +## Network Segments (non-VLAN) +| Segment | Subnet | Purpose | +|---------|--------|---------| +| Main LAN | 192.168.0.0/24 | Servers, workstations, DOS test stations | +| Secondary | 192.168.1.x | Some workstations | +| VPN | 192.168.6.x | VPN / remote access | + +## Notes +- Flat network is a risk — no segmentation between servers, workstations, and DOS stations +- DOS stations require SMB1 (via D2TESTNAS), which is a lateral movement risk on a flat network diff --git a/clients/dataforth/docs/overview.md b/clients/dataforth/docs/overview.md new file mode 100644 index 0000000..ea2113b --- /dev/null +++ b/clients/dataforth/docs/overview.md @@ -0,0 +1,72 @@ +# Client Overview + +## Company Name +Dataforth Corporation + +## Primary Contact +- Name: Dan Center (Operations, replacing retired Joel Lohr) +- Email: dcenter@dataforth.com + +## IT Contact +- Name: Mike Swanson (azcomputerguru.com) +- Email: mike@azcomputerguru.com + +## Address +3331 E. Hemisphere Loop, Tucson, AZ 85706 USA +Phone: (520) 741-1404 | Fax: (520) 741-0762 +Website: www.dataforth.com + +## Industry +Signal Conditioning / Data Acquisition — Manufacturing + +## Environment Summary +- Domain: intranet.dataforth.com +- Forest/Domain Level: Windows Server 2016 +- Total Active Users: ~21 human + ~5 service + 50+ machine/functional accounts +- Servers: 6 (AD1, AD2, FILES-D1, SAGE-SQL, 3CX, DF-HYPERV-B) +- Hypervisors: 2x ESXi (122, 124) + 1x Hyper-V (DF-HYPERV-B) +- Engineering Workstations: ~12 +- Manufacturing/Assembly: ~14 +- Office/Admin: ~12 +- DOS Test Stations: 64 (MS-DOS 6.22, not domain-joined) +- End-of-Life Machines: 3 (Windows 7: LABELPC, LABELPC2, D2-RCVG-003) +- M365 Tenant ID: 7dfa3ce8-c496-4b51-ab8d-bd3dcd78b584 +- Entra ID Sync: Yes (Azure AD Connect from OU=SyncedUsers) +- RMM: Datto RMM + GuruRMM (azcomputerguru.com) + +## Key Applications +- **TestDataDB**: Node.js + SQLite web app on AD2:3000 — 2.28M test records, 1,470+ model specs +- **Sage ERP**: On SAGE-SQL (192.168.0.153), mapped as S: +- **3CX Phone**: On 3CX server (192.168.0.125) — possibly inactive (last logon Oct 2025) +- **GageTrak**: Calibration tracking on DF-GAGETRAK (192.168.0.102) +- **QuickBASIC 4.5 ATE**: Automated test equipment on 64 DOS stations + +## Key Contacts + +### Dataforth Staff +| Name | Username | Role | Email | +|------|----------|------|-------| +| John Lehman | jlehman | Engineering, QB code, test specs | jlehman@dataforth.com | +| Peter Iliya | pIliya | Applications Engineer | pIliya@dataforth.com | +| Dan Center | dcenter | Operations | dcenter@dataforth.com | +| Georg Haubner | ghaubner | Engineering, has D: backup | ghaubner@dataforth.com | +| Ken Hoffman | khoffman / oemdata | TestDataSheetUploader author | — | +| Logan Tobey | ltobey | Support/Sales | ltobey@dataforth.com | +| Ben Wadzinski | bwadzinski | Engineering | — | +| Lee Payne | lpayne | Engineering | — | +| Theresa Dean | tdean | Admin | tdean@dataforth.com | +| Joel Lohr | jlohr | **RETIRED 2026-03-31** | jlohr@dataforth.com | + +### External +| Name | Role | Contact | +|------|------|---------| +| Mike Swanson | IT Consultant (azcomputerguru.com) | mike@azcomputerguru.com | +| Ginger (Quatronix) | China distributor | gy@quatronix-cn.com | +| Support Pool | Customer support | support@dataforth.com / (520) 741-1404 | + +## Notes +- Suffered a **crypto/ransomware attack in 2025** — AD2 was wiped and rebuilt, many files lost +- **DF-JOEL2 compromised 2026-03-27** via phishing — remediated, Joel retired 3/31 +- Test datasheet pipeline rebuilt 2026-03-27–29 after crypto wipe broke original VB6 tooling +- MFA enforcement deadline: **April 4, 2026** — 19/38 users still need to register +- Previous MSP/IT: Mike Swanson (azcomputerguru.com) — prepared this audit for handoff diff --git a/clients/dataforth/docs/rmm/rmm.md b/clients/dataforth/docs/rmm/rmm.md new file mode 100644 index 0000000..cea7764 --- /dev/null +++ b/clients/dataforth/docs/rmm/rmm.md @@ -0,0 +1,19 @@ +# RMM / Monitoring + +## RMM Solutions +| Product | Vendor | Notes | +|---------|--------|-------| +| Datto RMM | Datto | CagService on AD2 | +| GuruRMM | azcomputerguru.com | Agent + scheduled update/restart/rollback tasks | +| ScreenConnect | ConnectWise | Remote access client deployed on servers | + +## Scheduled RMM Tasks (AD2) +| Task | Status | Purpose | +|------|--------|---------| +| AgentBinaryUpdate | Ready | Updates RMM agent binary | +| AgentRestart | Ready | Restarts GuruRMM agent service | +| GuruRMM-Rollback | Ready | RMM rollback script | + +## Notes +- RMM is managed by previous IT consultant (Mike Swanson, azcomputerguru.com) +- Transition status TBD — may need to replace with our own RMM diff --git a/clients/dataforth/docs/security/antivirus.md b/clients/dataforth/docs/security/antivirus.md new file mode 100644 index 0000000..e5244da --- /dev/null +++ b/clients/dataforth/docs/security/antivirus.md @@ -0,0 +1,19 @@ +# Endpoint Security / Antivirus + +## Solution +- Product: Not specified in audit +- Managed By: Mike Swanson / azcomputerguru.com + +## Deployment Status +- During 2026-03-27 incident: 32 machines scanned clean +- 28 machines were unreachable (offline at time of scan) + +## Remote Access Tools +- ScreenConnect (ConnectWise) — deployed across fleet +- Datto RMM agent (CagService) +- GuruRMM Agent (azcomputerguru.com) + +## Notes +- AV/EDR product details not captured in audit — need to identify +- Post-incident scan was incomplete (28 machines missed) +- No lateral movement detected from DF-JOEL2 compromise diff --git a/clients/dataforth/docs/security/backup.md b/clients/dataforth/docs/security/backup.md new file mode 100644 index 0000000..abf837a --- /dev/null +++ b/clients/dataforth/docs/security/backup.md @@ -0,0 +1,32 @@ +# Backup and Disaster Recovery + +## Pre-Crypto Attack Backup +- Location: HGHAUBNER (192.168.0.148) D: drive +- Contents: Full backup of all visible network shares before 2025 crypto/ransomware attack +- Folders: DF C-Drive, DF E-Drive, DF Sage, DF Server Archive, DF Server Engineering, DF Server Sales, DF Staff, DF WebShare +- Access: Admin share (D$), firewall opened 2026-03-27 + +## TestDataDB Backup +- Task: TestDataDB-Backup (scheduled on AD2) +- Script: C:\Shares\testdatadb\backup-db.ps1 +- Output: C:\Shares\testdatadb\backups\ + +## VSS Shadow Copy +- Task: VSS Shadow Copy (scheduled daily at 2:00 AM on AD2) +- Target: E: drive + +## Online Backup +- Service: "Online Backup Service" running on AD2 +- Details: Unknown — needs investigation + +## M365 Backup +- Not identified + +## Disaster Recovery +- No formal DR plan documented +- RTO/RPO targets not defined + +## Notes +- Backup posture is weak — the only full backup is a pre-attack copy on a workstation's D: drive +- No verified backup of current server state, AD, or Sage ERP +- TestDataDB has its own scheduled SQLite backup diff --git a/clients/dataforth/docs/servers/3cx.md b/clients/dataforth/docs/servers/3cx.md new file mode 100644 index 0000000..31fdb55 --- /dev/null +++ b/clients/dataforth/docs/servers/3cx.md @@ -0,0 +1,11 @@ +# Server: 3CX + +## General Info +- Hostname: 3CX +- IP Address: 192.168.0.125 +- OS: Windows Server 2016 +- Role: Phone System (3CX PBX) + +## Notes +- Last logon: 2025-10-01 — **may be inactive/decommissioned** +- Needs investigation to determine if still in use diff --git a/clients/dataforth/docs/servers/ad1.md b/clients/dataforth/docs/servers/ad1.md new file mode 100644 index 0000000..f4c4140 --- /dev/null +++ b/clients/dataforth/docs/servers/ad1.md @@ -0,0 +1,29 @@ +# Server: AD1 + +## General Info +- Hostname: AD1 +- IP Address: 192.168.0.27 +- OS: Windows Server 2016 Standard +- Physical / Virtual: Unknown (likely VM on ESXi) + +## Roles and Services +- [x] Primary Domain Controller (all FSMO roles assumed) +- [x] DNS Server +- [x] WINS / NPS Server (ports 1812/1813) +- [ ] File Server (Engineering share) + +## Storage +- **C: drive at 90% full** — C:\Engineering consuming 787 GB + +## Shares +| Share Name | Path | Notes | +|-----------|------|-------| +| Engineering | C:\Engineering | 787 GB — ENGR/ATE source code and specs | +| ITSvc | C:\Shares\ITSvc | IT service files, mapped as B: | + +## Drive Mappings (from AD1) +- B: = \\ad1\itsvc + +## Known Issues +- **[CRITICAL]** C: drive at 90% capacity — Engineering folder needs to move off this DC +- Running AD DS on a server with 787 GB of engineering data is a risk diff --git a/clients/dataforth/docs/servers/ad2.md b/clients/dataforth/docs/servers/ad2.md new file mode 100644 index 0000000..acb218f --- /dev/null +++ b/clients/dataforth/docs/servers/ad2.md @@ -0,0 +1,84 @@ +# Server: AD2 + +## General Info +- Hostname: AD2 +- IP Address: 192.168.0.6 +- OS: Windows Server 2016 Standard +- Physical / Virtual: VM (VMware Tools + VGAuthService running, on ESXi) + +## Roles and Services +- [x] Secondary Domain Controller +- [x] DNS Server +- [x] DFS Replication / Namespace +- [x] File Server (multiple shares) +- [x] TestDataDB host (Node.js + SQLite on port 3000) +- [x] NAS sync (rsync every 15 min) + +## Storage +- C: drive 1 TB, 405 GB free +- E: drive (VSS shadow copy target) + +## Shares +| Share Name | Path | Mapped As | Notes | +|-----------|------|-----------|-------| +| c-drive | C:\Shares\c-drive | Q: | — | +| e-drive | C:\Shares\e-drive | T: | — | +| test | C:\Shares\test | — | NAS sync staging, DOS station data | +| webshare | C:\Shares\webshare | X: | Contains For_Web datasheets | + +## Key Applications +### TestDataDB +- Type: Node.js + SQLite web app +- URL: http://192.168.0.6:3000 +- Location: C:\Shares\testdatadb\ +- Database: C:\Shares\testdatadb\database\testdata.db (~3 GB) +- Service: `testdatadb` (Windows service, runs as INTRANET\svc_testdatadb) +- Stats: 2,281,524 test records, 33,745 work orders, 1,470+ model specs + +## Scheduled Tasks +| Task | Status | Schedule | Notes | +|------|--------|----------|-------| +| Sync-FromNAS | Ready | Every 15 min | Bidirectional rsync with D2TESTNAS | +| TestDataDB-Backup | Ready | Scheduled | SQLite DB backup | +| VSS Shadow Copy | Ready | Daily 2:00 AM | E: drive | +| ClaudeTools Log Rotation | Ready | — | Log rotation | +| AgentBinaryUpdate | Ready | — | RMM agent update | +| AgentRestart | Ready | — | GuruRMM restart | +| GuruRMM-Rollback | Ready | — | RMM rollback | +| TestDataDB Server | Disabled | — | Replaced by Windows service | +| TestDataDB_NodeServer | Disabled | — | Alternate startup (disabled) | +| BulkSync-Catchup | Disabled | — | One-time bulk sync (done) | + +## Running Services (Non-Default) +| Service | Purpose | Run As | +|---------|---------|--------| +| testdatadb | TestDataDB web app (Node.js port 3000) | INTRANET\svc_testdatadb | +| CagService | Datto RMM agent | — | +| GuruRMMAgent | GuruRMM monitoring | — | +| ScreenConnect Client | Remote access | — | +| Online Backup Service | Backup agent | — | +| VGAuthService | VMware guest auth | — | +| VMTools | VMware Tools | — | +| NTDS | AD DS | — | +| Kdc | Kerberos KDC | — | +| ADWS | AD Web Services | — | +| DFSR | DFS Replication | — | +| Dfs | DFS Namespace | — | +| ssh-agent | OpenSSH auth agent | — | + +## Windows Firewall +| Profile | Status | +|---------|--------| +| Domain | **DISABLED** | +| Private | **DISABLED** | +| Public | **DISABLED** | + +## History +- **Wiped and rebuilt after 2025 crypto/ransomware attack** +- Many files lost (C:\DFWDS\, scheduled tasks, service configs) +- TestDataDB pipeline rebuilt 2026-03-27–29 + +## Known Issues +- **[CRITICAL]** All firewall profiles disabled +- **[LOW]** DVD ISO still mounted on D: drive +- **[MEDIUM]** TestDataDB Server scheduled task still exists but disabled diff --git a/clients/dataforth/docs/servers/d2testnas.md b/clients/dataforth/docs/servers/d2testnas.md new file mode 100644 index 0000000..161150b --- /dev/null +++ b/clients/dataforth/docs/servers/d2testnas.md @@ -0,0 +1,27 @@ +# Server: D2TESTNAS + +## General Info +- Hostname: D2TESTNAS +- IP Address: 192.168.0.9 +- OS: Debian 13 Linux +- Role: SMB1 proxy for DOS test stations, rsync endpoint + +## Services +| Service | Port | Notes | +|---------|------|-------| +| Samba (SMB1) | 445 | Guest access (no password) for DOS machines | +| SSH | 22 | Root access with password auth | +| rsync daemon | 873 | Module "test" → /data/test | + +## Storage +- /data/test — Test station data +- /data/test/STAGE — TXT datasheet staging area + +## Sync +- rsync module "test" maps to /data/test +- AD2 pulls from NAS every 15 min (Sync-FromNAS scheduled task) +- Bidirectional: DAT/reports pulled to AD2, software updates pushed to NAS + +## Known Issues +- **[MEDIUM]** Root SSH with password authentication — should use key-based auth +- **[MEDIUM]** Guest Samba access (no password) — required for DOS SMB1 compatibility diff --git a/clients/dataforth/docs/servers/df-hyperv-b.md b/clients/dataforth/docs/servers/df-hyperv-b.md new file mode 100644 index 0000000..be55423 --- /dev/null +++ b/clients/dataforth/docs/servers/df-hyperv-b.md @@ -0,0 +1,11 @@ +# Server: DF-HYPERV-B + +## General Info +- Hostname: DF-HYPERV-B +- IP Address: 192.168.0.123 +- OS: Windows Server 2025 +- Role: Hyper-V Host + +## Notes +- Newest server in the environment (Server 2025) +- VM inventory not captured in audit diff --git a/clients/dataforth/docs/servers/files-d1.md b/clients/dataforth/docs/servers/files-d1.md new file mode 100644 index 0000000..e594459 --- /dev/null +++ b/clients/dataforth/docs/servers/files-d1.md @@ -0,0 +1,16 @@ +# Server: FILES-D1 + +## General Info +- Hostname: FILES-D1 +- IP Address: 192.168.0.189 +- OS: Windows Server 2016 +- Role: File Server + +## Shares +| Share Name | Mapped As | Notes | +|-----------|-----------|-------| +| sales | W: | Sales documents | +| archive | Y: | Archive storage | + +## Notes +- Primary file server for sales and archive data diff --git a/clients/dataforth/docs/servers/sage-sql.md b/clients/dataforth/docs/servers/sage-sql.md new file mode 100644 index 0000000..9f30bfb --- /dev/null +++ b/clients/dataforth/docs/servers/sage-sql.md @@ -0,0 +1,16 @@ +# Server: SAGE-SQL + +## General Info +- Hostname: SAGE-SQL +- IP Address: 192.168.0.153 +- OS: Windows Server 2016 +- Role: Sage ERP Database Server + +## Shares +| Share Name | Mapped As | Notes | +|-----------|-----------|-------| +| sage | S: | Sage ERP data | + +## Notes +- Hosts Sage ERP database +- Backup status unknown — not included in any identified backup job diff --git a/clients/dataforth/docs/workstations.md b/clients/dataforth/docs/workstations.md new file mode 100644 index 0000000..96357b1 --- /dev/null +++ b/clients/dataforth/docs/workstations.md @@ -0,0 +1,81 @@ +# Workstation Inventory + +## Engineering Workstations +| Hostname | IP | OS | User/Purpose | Last Logon | +|----------|-----|-----|-------------|------------| +| D1-ENGI-005 | 192.168.0.91 | Win 11 Pro | Engineering | — | +| D1-ENGI-006 | 192.168.0.104 | Win 11 Pro | Engineering | 2026-04-02 | +| D1-ENGI-008 | 192.168.0.116 | Win 10 Pro | Engineering | 2026-04-02 | +| D1-ENGI-010 | 192.168.0.197 | Win 11 Pro | Engineering | — | +| D1-ENGI-012 | 192.168.0.135 | Win 11 Pro | Engineering | 2026-04-02 | +| D1-ENGI-DEV2 | 192.168.0.71 | Win 11 Pro | Engineering | — | +| D1-ENGI-EMCLAB1 | 192.168.0.50 | Win 11 Pro | EMC Lab | — | +| D1-ENGI-LAB1 | 192.168.0.193 | Win 10 Pro | Engineering Lab | — | +| D1-PWRM | 192.168.0.166 | Win 11 Pro | PWRM10 test station | — | +| ENG-DEV-SERVER | 192.168.0.126 | Win 11 Pro | Engineering Dev Server | — | +| DF-LEE-I9 | 192.168.0.103 | Win 11 Pro | Lee Payne | — | +| HGHAUBNER | 192.168.0.148 | Win 11 Pro | Georg Haubner (D: has pre-attack backup) | — | + +## Manufacturing / Assembly +| Hostname | IP | OS | Purpose | Last Logon | +|----------|-----|-----|---------|------------| +| D2-AS-24 | 192.168.0.115 | Win 11 Pro | Assembly | — | +| D2-AS-26 | 192.168.0.79 | Win 11 Pro | Assembly | — | +| D2-ASSY-001 | 192.168.0.71 | Win 10 Pro | Assembly | — | +| D2-HIPOT-SURFAC | 192.168.0.121 | Win 11 Pro | Hi-pot / surface test | — | +| D2-LEETEST | 192.168.0.40 | Win 11 Pro | Lee's test station | — | +| D2-MFG-001 | 192.168.0.81 | Win 11 Pro | Manufacturing | — | +| D2-MFGR-004 | 192.168.0.90 | Win 11 Pro | Manufacturing | — | +| D2-MFGR-200 | 192.168.0.151 | Win 11 Pro | Manufacturing | — | +| QCINSPECTION | 192.168.0.84 | Win 11 Pro | QC Inspection | — | +| STATION_41 | 192.168.0.42 | Win 11 Pro | Test station | — | +| STATION20-PC | 192.168.0.129 | Win 10 Pro | Test station | — | +| STATION21 | 192.168.0.79 | Win 10 Pro | Test station | — | +| STATION43 | 192.168.0.43 | Win 10 Pro | Test station | — | +| D2-BOBBI | 192.168.0.154 | Win 11 Pro | Bobbi Whitson | — | + +## Office / Admin +| Hostname | IP | OS | Purpose | +|----------|-----|-----|---------| +| D1-CONF-002 | 192.168.0.120 | Win 11 Pro | Conference room | +| D1-CUST-003 | 192.168.0.119 | Win 11 Pro | Customer service | +| DANC0619 | 192.168.0.51 | Win 11 Pro | Dan Center | +| DFORTH-SHIP | 192.168.0.146 | Win 11 Pro | Shipping | +| DFORTH-SHIPP | 192.168.0.70 | Win 11 Pro | Shipping | +| DFASLB0519 | 192.168.0.172 | Win 10 Pro | — | +| DF-GAGETRAK | 192.168.0.102 | Win 11 Pro | Gage tracking | +| DF-D2-TRAINING- | 192.168.0.199 | Win 11 Pro | Training room | +| MY9-PC | 192.168.0.57 | Win 10 Pro | — | + +## End-of-Life (Windows 7) +| Hostname | IP | OS | Notes | +|----------|-----|-----|-------| +| LABELPC | 192.168.0.100 | **Win 7 Pro** | Label printing — EOL | +| LABELPC2 | 192.168.0.98 | **Win 7 Pro** | Label printing — EOL | +| D2-RCVG-003 | 192.168.0.47 | **Win 7 Pro** | Receiving — EOL | + +## Security Incident Machine +| Hostname | IP | OS | Notes | +|----------|-----|-----|-------| +| DF-JOEL2 | 192.168.0.174 | Win 11 Pro | Compromised 2026-03-27, remediated | + +## RMA +| Hostname | IP | OS | Notes | +|----------|-----|-----|-------| +| DATAFORTH-PC | — | — | RMA processing. Users remote in via RDP + ScreenConnect. Not in Mike's audit. | + +## Test / Special +| Hostname | IP | OS | Notes | +|----------|-----|-----|-------| +| D2-10D-TS1 | — | Win 11 Pro | 10D product test station (new) | +| TEST01 | 192.168.0.167 | Win 11 Business | — | +| TS-41 | 192.168.0.169 | Win 10 Pro | — | + +## DOS Test Stations (64 total, not domain-joined) +- Stations: TS-1 through TS-30 (plus L/R variants: TS-1L, TS-1R, TS-2L, TS-2R, etc.) +- Dev stations: TS-GURU, TS-TOM +- OS: MS-DOS 6.22 +- Software: QuickBASIC 4.5 ATE (Automated Test Equipment) +- Network: SMB1 via D2TESTNAS Samba proxy +- Drive Maps: T: = \\\\D2TESTNAS\test, X: = \\\\D2TESTNAS\datasheets +- See `manufacturing.md` for full test infrastructure details diff --git a/clients/instrumental-music-center/docs/billing-log.md b/clients/instrumental-music-center/docs/billing-log.md new file mode 100644 index 0000000..74f3305 --- /dev/null +++ b/clients/instrumental-music-center/docs/billing-log.md @@ -0,0 +1,33 @@ +# Instrumental Music Center (IMC) — Work Log / Billing Record + +## Session 1 — (date TBD, ~2026-04) — Speedway Location + +**Focus:** New workstation rollout — 3 machines installed, data migrated, domain-joined + +| Time | Task | Details | +|------|------|---------| +| | Installed 3 new workstations | Speedway location. Hostnames TBD — to be filled in. | +| | Data migration | Transferred data from 3 old machines to the 3 new ones. | +| | Domain join | Joined all 3 new machines to the domain (domain name TBD in overview). | +| | AIM user# assignment | AIM by Tritech requires per-machine user# — must be recorded for each new machine. User#s TBD — to be filled in. | + +### Still to Document +- [ ] Exact date of install +- [ ] 3 new machine hostnames +- [ ] 3 old machine hostnames being replaced +- [ ] AIM user# for each new machine +- [ ] Domain name for IMC +- [ ] Approximate hours on this job + +--- + +## Client-Wide Reference + +### Critical Software: AIM by Tritech +- https://www.tritechretail.com/topic/aim +- **Per-machine user# is required** — preserve/assign on any install, reimage, or machine swap +- Document every machine's AIM user# in `overview.md` + +### Locations +- Speedway (documented — partial) +- Other locations: TBD diff --git a/clients/instrumental-music-center/docs/cloud/azure.md b/clients/instrumental-music-center/docs/cloud/azure.md new file mode 100644 index 0000000..4c7e869 --- /dev/null +++ b/clients/instrumental-music-center/docs/cloud/azure.md @@ -0,0 +1,28 @@ +# Azure / Cloud Services + +## Azure Subscription +- Subscription Name: +- Subscription ID: +- Resource Group(s): +- Region: +- Monthly Spend (approx): + +## Virtual Machines +| VM Name | Size | OS | IP | Purpose | +|---------------|------------|------------|------------|-----------------| +| | | | | | + +## Networking +- Virtual Network: +- Address Space: +- Subnets: +- VPN Gateway to On-Prem: Yes/No +- ExpressRoute: Yes/No + +## Other Cloud Services + +| Service | Purpose | Admin URL | Notes | +|-----------------|------------------|------------------|-----------------| +| | | | | + +## Notes diff --git a/clients/instrumental-music-center/docs/cloud/m365.md b/clients/instrumental-music-center/docs/cloud/m365.md new file mode 100644 index 0000000..dc32af2 --- /dev/null +++ b/clients/instrumental-music-center/docs/cloud/m365.md @@ -0,0 +1,52 @@ +# Microsoft 365 + +## Tenant Info +- Tenant Name: +- Tenant ID: +- Primary Domain: +- Admin Portal URL: https://admin.microsoft.com + +## Licensing +| License Type | Quantity | Assigned | Available | +|--------------------------|----------|----------|-----------| +| Microsoft 365 Business Basic | | | | +| Microsoft 365 Business Standard | | | | +| Microsoft 365 Business Premium | | | | +| Exchange Online Plan 1/2 | | | | +| Other | | | | + +## Exchange Online +- Mail Domain(s): +- MX Record Points To: +- SPF Record: +- DKIM Enabled: Yes/No +- DMARC Policy: +- Shared Mailboxes: +- Distribution Groups: +- Mail Flow Rules: Yes/No (describe below) + +## SharePoint / OneDrive +- SharePoint Sites: +- External Sharing: Enabled/Disabled +- OneDrive Storage Limit: + +## Teams +- Teams Phone System: Yes/No +- Calling Plan / Direct Routing: +- Auto Attendant: + +## Entra ID (Azure AD) +- Hybrid Joined: Yes/No +- Azure AD Connect Server: +- Sync Schedule: +- Password Hash Sync: Yes/No +- MFA Enforced: Yes/No +- Conditional Access Policies: + +## Security +- Defender for Office 365: Yes/No +- Safe Links: Yes/No +- Safe Attachments: Yes/No +- Audit Log Retention: + +## Notes diff --git a/clients/instrumental-music-center/docs/issues/log.md b/clients/instrumental-music-center/docs/issues/log.md new file mode 100644 index 0000000..dd4b53e --- /dev/null +++ b/clients/instrumental-music-center/docs/issues/log.md @@ -0,0 +1,19 @@ +# Issue Log + +Record past issues and their resolutions here. This helps the AI learn from historical +troubleshooting and avoid repeating failed approaches. + +## Template + +### [DATE] - [Brief Description] +- **Reported By:** +- **Severity:** Low / Medium / High / Critical +- **Symptoms:** +- **Root Cause:** +- **Resolution:** +- **Time to Resolve:** +- **Lessons Learned:** + +--- + + diff --git a/clients/instrumental-music-center/docs/network/dhcp.md b/clients/instrumental-music-center/docs/network/dhcp.md new file mode 100644 index 0000000..dc7ad3f --- /dev/null +++ b/clients/instrumental-music-center/docs/network/dhcp.md @@ -0,0 +1,31 @@ +# DHCP Configuration + +## DHCP Server +- Server Name: +- Server IP: +- Failover Partner: + +## Scopes + +### Scope - [VLAN Name] +- Subnet: +- Range Start: +- Range End: +- Subnet Mask: +- Default Gateway: +- DNS Servers: +- Lease Duration: +- Exclusions: + + + +## Reservations +| Device Name | MAC Address | IP Address | Scope | Notes | +|-----------------|-------------------|-----------------|---------------|---------------| +| | | | | | + +## DHCP Relay +- Relay agents configured on: +- Helper address: + +## Notes diff --git a/clients/instrumental-music-center/docs/network/dns.md b/clients/instrumental-music-center/docs/network/dns.md new file mode 100644 index 0000000..7bf8186 --- /dev/null +++ b/clients/instrumental-music-center/docs/network/dns.md @@ -0,0 +1,33 @@ +# DNS Configuration + +## Internal DNS Servers +| Server Name | IP Address | Role | +|-------------|-----------|-------------------| +| | | Primary | +| | | Secondary | + +## DNS Forwarders +- Forwarder 1: +- Forwarder 2: + +## Conditional Forwarders +| Domain | Forward To | Purpose | +|----------------------|-----------------|-------------------| +| | | | + +## Key DNS Records +| Record Type | Name | Value | Notes | +|-------------|------------------|------------------|------------------| +| A | | | | +| CNAME | | | | +| MX | | | | +| TXT | | | | + +## External DNS +- Registrar: +- Hosted At: +- Primary Domain: +- Management URL: + +## Notes + diff --git a/clients/instrumental-music-center/docs/network/firewall.md b/clients/instrumental-music-center/docs/network/firewall.md new file mode 100644 index 0000000..21d8c8e --- /dev/null +++ b/clients/instrumental-music-center/docs/network/firewall.md @@ -0,0 +1,47 @@ +# Firewall Configuration + +## Device Info +- Vendor/Model: +- Firmware Version: +- Management IP: +- Management URL: +- HA Pair: Yes/No +- License Expiry: + +## Interfaces +| Interface | Zone | IP Address | VLAN | Description | +|-----------|-----------|-----------------|------|-------------------| +| WAN1 | WAN | | | Primary Internet | +| WAN2 | WAN | | | Backup Internet | +| LAN | LAN | | | | +| DMZ | DMZ | | | | + +## NAT Rules +| Name | Source | Destination | Port(s) | NAT To | +|-------------------|---------------|----------------|-------------|-----------------| +| | | | | | + +## Key Firewall Policies +| Name | Source Zone | Dest Zone | Service | Action | Notes | +|-------------------|--------------|---------------|-------------|--------|--------| +| | | | | | | + +## VPN +### Site-to-Site VPNs +| Peer Name | Peer IP | Local Subnet | Remote Subnet | Status | +|-------------------|--------------|----------------|---------------|--------| +| | | | | | + +### SSL/Client VPN +- Enabled: Yes/No +- Portal URL: +- Auth Method: +- IP Pool: +- Split Tunnel: Yes/No + +## Content Filtering +- Web Filter Profile: +- App Control Profile: +- DNS Filter: + +## Notes diff --git a/clients/instrumental-music-center/docs/network/topology.md b/clients/instrumental-music-center/docs/network/topology.md new file mode 100644 index 0000000..740cf09 --- /dev/null +++ b/clients/instrumental-music-center/docs/network/topology.md @@ -0,0 +1,43 @@ +# Network Topology + +## Internet Connection +- ISP: +- Circuit Type: +- Speed (Down/Up): +- Public IP: +- Gateway: +- Modem Model: + +## Core Switch +- Model: +- IP Address: +- Management URL: +- Firmware Version: +- Location: + +## Additional Switches + +### Switch - [Name/Location] +- Model: +- IP Address: +- Port Count: +- PoE: Yes/No +- Uplink To: + +## Wireless +- Controller Model: +- Controller IP: +- Number of APs: +- AP Model(s): + +### Access Points + +- AP Name: +- Location: +- IP Address: +- Connected Switch/Port: + +## WAN / SD-WAN +- SD-WAN Vendor: +- Number of Sites: +- Hub Site: diff --git a/clients/instrumental-music-center/docs/network/vlans.md b/clients/instrumental-music-center/docs/network/vlans.md new file mode 100644 index 0000000..475f778 --- /dev/null +++ b/clients/instrumental-music-center/docs/network/vlans.md @@ -0,0 +1,21 @@ +# VLANs + +## VLAN Table + +| VLAN ID | Name | Subnet | Gateway | DHCP Scope | Purpose | +|---------|---------------|-----------------|-----------------|------------------|------------------------| +| 1 | Default | | | | | +| 10 | Management | | | | Network devices | +| 20 | Servers | | | | Server infrastructure | +| 30 | Workstations | | | | End user devices | +| 40 | VoIP | | | | Phone system | +| 50 | WiFi-Corp | | | | Corporate wireless | +| 60 | WiFi-Guest | | | | Guest wireless | +| 100 | Security | | | | Cameras / access ctrl | + +## Inter-VLAN Routing +- Performed by: +- Routing device IP: + +## VLAN Notes + diff --git a/clients/instrumental-music-center/docs/overview.md b/clients/instrumental-music-center/docs/overview.md new file mode 100644 index 0000000..6f8031c --- /dev/null +++ b/clients/instrumental-music-center/docs/overview.md @@ -0,0 +1,56 @@ +# Client Overview + +## Company Name +Instrumental Music Center (IMC) + +## Primary Contact +- Name: +- Phone: +- Email: + +## IT Contact +- Name: +- Phone: +- Email: + +## Contract Details +- Service Level: +- Hours Covered: +- Contract Renewal Date: + +## Environment Summary +- Total Users: +- Total Locations: Multiple (only Speedway documented currently) +- Domain Name: +- RMM Agent Count: +- Workstation Count: +- Server Count: + +## Locations + +### Speedway +- Address: +- Phone: +- Primary Use: + +#### Workstations (Speedway) +3 new machines installed, data migrated from 3 old machines, joined to domain. + +| New Machine | Replaces | AIM User # | Notes | +|-------------|----------|------------|-------| +| TBD | TBD | TBD | | +| TBD | TBD | TBD | | +| TBD | TBD | TBD | | + +## Critical Software: AIM by Tritech + +**IMC uses AIM (retail POS/inventory software) by Tritech — https://www.tritechretail.com/topic/aim** + +> **When installing new machines or reinstalling Windows:** AIM requires a **user #** to be set on each machine. This is machine-specific configuration that must be preserved/assigned during any rebuild. + +- Document every machine's AIM user # in the workstation table above +- Do NOT wipe/reimage a machine without first recording its AIM user # +- When replacing a machine, the new one needs its own user # assigned + +## Notes + diff --git a/clients/instrumental-music-center/docs/rmm/rmm.md b/clients/instrumental-music-center/docs/rmm/rmm.md new file mode 100644 index 0000000..819596b --- /dev/null +++ b/clients/instrumental-music-center/docs/rmm/rmm.md @@ -0,0 +1,34 @@ +# RMM / Monitoring + +## RMM Solution +- Product: +- Console URL: +- Agent Version: + +## Agent Deployment +- Total Devices: +- Servers Monitored: +- Workstations Monitored: +- Network Devices Monitored: + +## Monitoring Policies +| Policy Name | Applies To | Alert Condition | Action | +|-------------------|----------------|-------------------------|---------------| +| Disk Space | All Servers | < 10% free | Alert + Ticket| +| CPU | All Servers | > 90% for 15 min | Alert | +| Service Monitor | All Servers | | | +| Backup Monitor | | | | +| Offline Alert | All Agents | Offline > 30 min | Alert | + +## Patch Management +- Patch Policy: +- Patch Window: +- Auto-approve: Yes/No +- Exclusions: + +## Scripting / Automation +| Script Name | Schedule | Purpose | +|---------------------|-------------|--------------------------| +| | | | + +## Notes diff --git a/clients/instrumental-music-center/docs/security/antivirus.md b/clients/instrumental-music-center/docs/security/antivirus.md new file mode 100644 index 0000000..d495dfc --- /dev/null +++ b/clients/instrumental-music-center/docs/security/antivirus.md @@ -0,0 +1,26 @@ +# Endpoint Security / Antivirus + +## Solution +- Product: +- Console URL: +- License Count: +- License Expiry: +- Managed By: + +## Policy +- Real-time Protection: Yes/No +- Scheduled Scans: (frequency) +- Exclusions: + +## Deployment Status +- Total Endpoints: +- Protected: +- Missing Agent: +- Out of Date: + +## EDR / XDR +- EDR Enabled: Yes/No +- Product: +- Console URL: + +## Notes diff --git a/clients/instrumental-music-center/docs/security/backup.md b/clients/instrumental-music-center/docs/security/backup.md new file mode 100644 index 0000000..4ed13a4 --- /dev/null +++ b/clients/instrumental-music-center/docs/security/backup.md @@ -0,0 +1,34 @@ +# Backup and Disaster Recovery + +## Backup Solution +- Product: +- Console URL: +- License/Subscription: + +## Backup Targets +| Target Name | Type | Location | Capacity | Encrypted | +|----------------|----------------|-----------------|--------------|-----------| +| | Local NAS | | | Yes/No | +| | Cloud | | | Yes/No | +| | Offsite | | | Yes/No | + +## Backup Jobs +| Job Name | Source | Target | Schedule | Retention | Status | +|-----------------|-------------------|------------|---------------|-------------|--------| +| | | | | | | + +## M365 Backup +- M365 Backup Product: +- Exchange Backed Up: Yes/No +- SharePoint Backed Up: Yes/No +- OneDrive Backed Up: Yes/No +- Teams Backed Up: Yes/No + +## Disaster Recovery Plan +- RTO Target: +- RPO Target: +- DR Site: +- Last DR Test Date: +- DR Test Result: + +## Notes diff --git a/clients/instrumental-music-center/docs/servers/server_template.md b/clients/instrumental-music-center/docs/servers/server_template.md new file mode 100644 index 0000000..d35ab32 --- /dev/null +++ b/clients/instrumental-music-center/docs/servers/server_template.md @@ -0,0 +1,49 @@ +# Server: [SERVER NAME] + +## General Info +- Hostname: +- IP Address: +- OS: +- OS Version: +- Physical / Virtual: +- Host (if virtual): +- Location: +- Last Patched: + +## Hardware (if physical) +- Make/Model: +- CPU: +- RAM: +- Storage: +- Warranty Expiry: + +## Roles and Services + +- [ ] Domain Controller +- [ ] DNS Server +- [ ] DHCP Server +- [ ] File Server +- [ ] Print Server +- [ ] Application Server +- [ ] Database Server +- [ ] Backup Target +- [ ] RDS / Terminal Server +- [ ] Hyper-V Host + +## Shares (if file server) +| Share Name | Path | Permissions Group | Notes | +|---------------|-------------------|---------------------|----------------| +| | | | | + +## Applications Installed +| Application | Version | Purpose | License | +|-------------------|------------|----------------------|---------------| +| | | | | + +## Backup +- Backup Method: +- Backup Schedule: +- Backup Target: +- Last Verified Restore: + +## Notes diff --git a/clients/khalsa/docs/apple-domain-join.md b/clients/khalsa/docs/apple-domain-join.md new file mode 100644 index 0000000..8b9fe15 --- /dev/null +++ b/clients/khalsa/docs/apple-domain-join.md @@ -0,0 +1,71 @@ +# Joining Apple Devices to khalsa.local Domain + +## Prerequisites +- Mac must be on the network and able to reach DC (TROUT at 10.11.12.254) +- DNS must resolve khalsa.local (primary DNS should be 10.11.12.254) +- Ports 88 (Kerberos), 389 (LDAP), 445 (SMB) must be open to DC +- Domain admin credentials (guru) + +## Steps (run all as localadmin) + +### 1. Verify connectivity +```bash +ping -c 2 10.11.12.254 +nc -z -w 3 10.11.12.254 389 && echo "LDAP open" || echo "LDAP closed" +nc -z -w 3 10.11.12.254 88 && echo "Kerberos open" || echo "Kerberos closed" +``` + +### 2. Join the domain +Run in Terminal (not ScreenConnect — password prompt): +```bash +sudo dsconfigad -add khalsa.local -username guru -force +``` +Enter guru's password when prompted. No output = success. + +### 3. Verify binding +```bash +dsconfigad -show +id guru +``` +Should show `uid=...(guru)` with KHALSA\Domain Admins in groups. + +### 4. Grant Domain Admins local admin rights +```bash +sudo dsconfigad -groups "KHALSA\Domain Admins" +``` + +### 5. Set default domain (so users type just username, not KHALSA\username) +```bash +sudo defaults write /Library/Preferences/com.apple.loginwindow DefaultDomain -string "KHALSA" +``` + +### 6. Reboot and test +Log in with just the domain username (e.g., `guru`) — no `KHALSA\` prefix needed. + +## Troubleshooting + +**"Connection failed to the directory server" (2100)** +- If the Mac was previously joined and the trust is broken, force remove first: + ```bash + sudo dsconfigad -remove -username guru -force + ``` + Then redo step 2. + +**"Data source (/Active Directory/KHALSA/All Domains) is not valid"** +- Directory plugin not loading. Run: + ```bash + sudo killall opendirectoryd + ``` + Wait 10 seconds, then test with `id guru`. + +**Domain user doesn't have sudo** +- Must be logged in as localadmin to run step 4. Domain Admins group must be added via `dsconfigad -groups`. + +**User has to type KHALSA\ at login** +- Step 5 wasn't applied, or needs a reboot after applying. + +## Network Info +- DC: TROUT (10.11.12.254) +- Domain: khalsa.local +- DNS: 10.11.12.254 (primary), 10.11.12.1 (secondary) +- 10.11.12.243 is a DNS server but NOT the DC diff --git a/clients/khalsa/docs/cloud/azure.md b/clients/khalsa/docs/cloud/azure.md new file mode 100644 index 0000000..4c7e869 --- /dev/null +++ b/clients/khalsa/docs/cloud/azure.md @@ -0,0 +1,28 @@ +# Azure / Cloud Services + +## Azure Subscription +- Subscription Name: +- Subscription ID: +- Resource Group(s): +- Region: +- Monthly Spend (approx): + +## Virtual Machines +| VM Name | Size | OS | IP | Purpose | +|---------------|------------|------------|------------|-----------------| +| | | | | | + +## Networking +- Virtual Network: +- Address Space: +- Subnets: +- VPN Gateway to On-Prem: Yes/No +- ExpressRoute: Yes/No + +## Other Cloud Services + +| Service | Purpose | Admin URL | Notes | +|-----------------|------------------|------------------|-----------------| +| | | | | + +## Notes diff --git a/clients/khalsa/docs/cloud/m365.md b/clients/khalsa/docs/cloud/m365.md new file mode 100644 index 0000000..dc32af2 --- /dev/null +++ b/clients/khalsa/docs/cloud/m365.md @@ -0,0 +1,52 @@ +# Microsoft 365 + +## Tenant Info +- Tenant Name: +- Tenant ID: +- Primary Domain: +- Admin Portal URL: https://admin.microsoft.com + +## Licensing +| License Type | Quantity | Assigned | Available | +|--------------------------|----------|----------|-----------| +| Microsoft 365 Business Basic | | | | +| Microsoft 365 Business Standard | | | | +| Microsoft 365 Business Premium | | | | +| Exchange Online Plan 1/2 | | | | +| Other | | | | + +## Exchange Online +- Mail Domain(s): +- MX Record Points To: +- SPF Record: +- DKIM Enabled: Yes/No +- DMARC Policy: +- Shared Mailboxes: +- Distribution Groups: +- Mail Flow Rules: Yes/No (describe below) + +## SharePoint / OneDrive +- SharePoint Sites: +- External Sharing: Enabled/Disabled +- OneDrive Storage Limit: + +## Teams +- Teams Phone System: Yes/No +- Calling Plan / Direct Routing: +- Auto Attendant: + +## Entra ID (Azure AD) +- Hybrid Joined: Yes/No +- Azure AD Connect Server: +- Sync Schedule: +- Password Hash Sync: Yes/No +- MFA Enforced: Yes/No +- Conditional Access Policies: + +## Security +- Defender for Office 365: Yes/No +- Safe Links: Yes/No +- Safe Attachments: Yes/No +- Audit Log Retention: + +## Notes diff --git a/clients/khalsa/docs/issues/log.md b/clients/khalsa/docs/issues/log.md new file mode 100644 index 0000000..dd4b53e --- /dev/null +++ b/clients/khalsa/docs/issues/log.md @@ -0,0 +1,19 @@ +# Issue Log + +Record past issues and their resolutions here. This helps the AI learn from historical +troubleshooting and avoid repeating failed approaches. + +## Template + +### [DATE] - [Brief Description] +- **Reported By:** +- **Severity:** Low / Medium / High / Critical +- **Symptoms:** +- **Root Cause:** +- **Resolution:** +- **Time to Resolve:** +- **Lessons Learned:** + +--- + + diff --git a/clients/khalsa/docs/network/README.md b/clients/khalsa/docs/network/README.md new file mode 100644 index 0000000..bd78c38 --- /dev/null +++ b/clients/khalsa/docs/network/README.md @@ -0,0 +1,8 @@ +# Khalsa Network Documentation + +Khalsa has two locations. Network docs are split by site: + +- **[Camden](camden/)** — Camden location +- **[River](river/)** — River location + +Site-to-site connectivity (VPN, SD-WAN, etc.) is documented in each site's firewall.md under the VPN section. diff --git a/clients/khalsa/docs/network/camden/dhcp.md b/clients/khalsa/docs/network/camden/dhcp.md new file mode 100644 index 0000000..dde06e6 --- /dev/null +++ b/clients/khalsa/docs/network/camden/dhcp.md @@ -0,0 +1,31 @@ +# DHCP Configuration — Camden + +## DHCP Server +- Server Name: +- Server IP: +- Failover Partner: + +## Scopes + +### Scope - [VLAN Name] +- Subnet: +- Range Start: +- Range End: +- Subnet Mask: +- Default Gateway: +- DNS Servers: +- Lease Duration: +- Exclusions: + + + +## Reservations +| Device Name | MAC Address | IP Address | Scope | Notes | +|-----------------|-------------------|-----------------|---------------|---------------| +| | | | | | + +## DHCP Relay +- Relay agents configured on: +- Helper address: + +## Notes diff --git a/clients/khalsa/docs/network/camden/dns.md b/clients/khalsa/docs/network/camden/dns.md new file mode 100644 index 0000000..9fc77c9 --- /dev/null +++ b/clients/khalsa/docs/network/camden/dns.md @@ -0,0 +1,33 @@ +# DNS Configuration — Camden + +## Internal DNS Servers +| Server Name | IP Address | Role | +|-------------|-----------|-------------------| +| | | Primary | +| | | Secondary | + +## DNS Forwarders +- Forwarder 1: +- Forwarder 2: + +## Conditional Forwarders +| Domain | Forward To | Purpose | +|----------------------|-----------------|-------------------| +| | | | + +## Key DNS Records +| Record Type | Name | Value | Notes | +|-------------|------------------|------------------|------------------| +| A | | | | +| CNAME | | | | +| MX | | | | +| TXT | | | | + +## External DNS +- Registrar: +- Hosted At: +- Primary Domain: +- Management URL: + +## Notes + diff --git a/clients/khalsa/docs/network/camden/firewall.md b/clients/khalsa/docs/network/camden/firewall.md new file mode 100644 index 0000000..cf40d15 --- /dev/null +++ b/clients/khalsa/docs/network/camden/firewall.md @@ -0,0 +1,47 @@ +# Firewall Configuration — Camden + +## Device Info +- Vendor/Model: +- Firmware Version: +- Management IP: +- Management URL: +- HA Pair: Yes/No +- License Expiry: + +## Interfaces +| Interface | Zone | IP Address | VLAN | Description | +|-----------|-----------|-----------------|------|-------------------| +| WAN1 | WAN | | | Primary Internet | +| WAN2 | WAN | | | Backup Internet | +| LAN | LAN | | | | +| DMZ | DMZ | | | | + +## NAT Rules +| Name | Source | Destination | Port(s) | NAT To | +|-------------------|---------------|----------------|-------------|-----------------| +| | | | | | + +## Key Firewall Policies +| Name | Source Zone | Dest Zone | Service | Action | Notes | +|-------------------|--------------|---------------|-------------|--------|--------| +| | | | | | | + +## VPN +### Site-to-Site VPNs +| Peer Name | Peer IP | Local Subnet | Remote Subnet | Status | +|-------------------|--------------|----------------|---------------|--------| +| | | | | | + +### SSL/Client VPN +- Enabled: Yes/No +- Portal URL: +- Auth Method: +- IP Pool: +- Split Tunnel: Yes/No + +## Content Filtering +- Web Filter Profile: +- App Control Profile: +- DNS Filter: + +## Notes diff --git a/clients/khalsa/docs/network/camden/topology.md b/clients/khalsa/docs/network/camden/topology.md new file mode 100644 index 0000000..ac42896 --- /dev/null +++ b/clients/khalsa/docs/network/camden/topology.md @@ -0,0 +1,43 @@ +# Network Topology — Camden + +## Internet Connection +- ISP: +- Circuit Type: +- Speed (Down/Up): +- Public IP: +- Gateway: +- Modem Model: + +## Core Switch +- Model: +- IP Address: +- Management URL: +- Firmware Version: +- Location: + +## Additional Switches + +### Switch - [Name/Location] +- Model: +- IP Address: +- Port Count: +- PoE: Yes/No +- Uplink To: + +## Wireless +- Controller Model: +- Controller IP: +- Number of APs: +- AP Model(s): + +### Access Points + +- AP Name: +- Location: +- IP Address: +- Connected Switch/Port: + +## WAN / SD-WAN +- SD-WAN Vendor: +- Number of Sites: +- Hub Site: diff --git a/clients/khalsa/docs/network/camden/vlans.md b/clients/khalsa/docs/network/camden/vlans.md new file mode 100644 index 0000000..d7deaf0 --- /dev/null +++ b/clients/khalsa/docs/network/camden/vlans.md @@ -0,0 +1,21 @@ +# VLANs — Camden + +## VLAN Table + +| VLAN ID | Name | Subnet | Gateway | DHCP Scope | Purpose | +|---------|---------------|-----------------|-----------------|------------------|------------------------| +| 1 | Default | | | | | +| 10 | Management | | | | Network devices | +| 20 | Servers | | | | Server infrastructure | +| 30 | Workstations | | | | End user devices | +| 40 | VoIP | | | | Phone system | +| 50 | WiFi-Corp | | | | Corporate wireless | +| 60 | WiFi-Guest | | | | Guest wireless | +| 100 | Security | | | | Cameras / access ctrl | + +## Inter-VLAN Routing +- Performed by: +- Routing device IP: + +## VLAN Notes + diff --git a/clients/khalsa/docs/network/river/dhcp.md b/clients/khalsa/docs/network/river/dhcp.md new file mode 100644 index 0000000..2f47957 --- /dev/null +++ b/clients/khalsa/docs/network/river/dhcp.md @@ -0,0 +1,31 @@ +# DHCP Configuration — River + +## DHCP Server +- Server Name: +- Server IP: +- Failover Partner: + +## Scopes + +### Scope - [VLAN Name] +- Subnet: +- Range Start: +- Range End: +- Subnet Mask: +- Default Gateway: +- DNS Servers: +- Lease Duration: +- Exclusions: + + + +## Reservations +| Device Name | MAC Address | IP Address | Scope | Notes | +|-----------------|-------------------|-----------------|---------------|---------------| +| | | | | | + +## DHCP Relay +- Relay agents configured on: +- Helper address: + +## Notes diff --git a/clients/khalsa/docs/network/river/dns.md b/clients/khalsa/docs/network/river/dns.md new file mode 100644 index 0000000..7558715 --- /dev/null +++ b/clients/khalsa/docs/network/river/dns.md @@ -0,0 +1,33 @@ +# DNS Configuration — River + +## Internal DNS Servers +| Server Name | IP Address | Role | +|-------------|-----------|-------------------| +| | | Primary | +| | | Secondary | + +## DNS Forwarders +- Forwarder 1: +- Forwarder 2: + +## Conditional Forwarders +| Domain | Forward To | Purpose | +|----------------------|-----------------|-------------------| +| | | | + +## Key DNS Records +| Record Type | Name | Value | Notes | +|-------------|------------------|------------------|------------------| +| A | | | | +| CNAME | | | | +| MX | | | | +| TXT | | | | + +## External DNS +- Registrar: +- Hosted At: +- Primary Domain: +- Management URL: + +## Notes + diff --git a/clients/khalsa/docs/network/river/firewall.md b/clients/khalsa/docs/network/river/firewall.md new file mode 100644 index 0000000..4315c7a --- /dev/null +++ b/clients/khalsa/docs/network/river/firewall.md @@ -0,0 +1,47 @@ +# Firewall Configuration — River + +## Device Info +- Vendor/Model: +- Firmware Version: +- Management IP: +- Management URL: +- HA Pair: Yes/No +- License Expiry: + +## Interfaces +| Interface | Zone | IP Address | VLAN | Description | +|-----------|-----------|-----------------|------|-------------------| +| WAN1 | WAN | | | Primary Internet | +| WAN2 | WAN | | | Backup Internet | +| LAN | LAN | | | | +| DMZ | DMZ | | | | + +## NAT Rules +| Name | Source | Destination | Port(s) | NAT To | +|-------------------|---------------|----------------|-------------|-----------------| +| | | | | | + +## Key Firewall Policies +| Name | Source Zone | Dest Zone | Service | Action | Notes | +|-------------------|--------------|---------------|-------------|--------|--------| +| | | | | | | + +## VPN +### Site-to-Site VPNs +| Peer Name | Peer IP | Local Subnet | Remote Subnet | Status | +|-------------------|--------------|----------------|---------------|--------| +| | | | | | + +### SSL/Client VPN +- Enabled: Yes/No +- Portal URL: +- Auth Method: +- IP Pool: +- Split Tunnel: Yes/No + +## Content Filtering +- Web Filter Profile: +- App Control Profile: +- DNS Filter: + +## Notes diff --git a/clients/khalsa/docs/network/river/topology.md b/clients/khalsa/docs/network/river/topology.md new file mode 100644 index 0000000..e05ab17 --- /dev/null +++ b/clients/khalsa/docs/network/river/topology.md @@ -0,0 +1,43 @@ +# Network Topology — River + +## Internet Connection +- ISP: +- Circuit Type: +- Speed (Down/Up): +- Public IP: +- Gateway: +- Modem Model: + +## Core Switch +- Model: +- IP Address: +- Management URL: +- Firmware Version: +- Location: + +## Additional Switches + +### Switch - [Name/Location] +- Model: +- IP Address: +- Port Count: +- PoE: Yes/No +- Uplink To: + +## Wireless +- Controller Model: +- Controller IP: +- Number of APs: +- AP Model(s): + +### Access Points + +- AP Name: +- Location: +- IP Address: +- Connected Switch/Port: + +## WAN / SD-WAN +- SD-WAN Vendor: +- Number of Sites: +- Hub Site: diff --git a/clients/khalsa/docs/network/river/vlans.md b/clients/khalsa/docs/network/river/vlans.md new file mode 100644 index 0000000..c27c871 --- /dev/null +++ b/clients/khalsa/docs/network/river/vlans.md @@ -0,0 +1,21 @@ +# VLANs — River + +## VLAN Table + +| VLAN ID | Name | Subnet | Gateway | DHCP Scope | Purpose | +|---------|---------------|-----------------|-----------------|------------------|------------------------| +| 1 | Default | | | | | +| 10 | Management | | | | Network devices | +| 20 | Servers | | | | Server infrastructure | +| 30 | Workstations | | | | End user devices | +| 40 | VoIP | | | | Phone system | +| 50 | WiFi-Corp | | | | Corporate wireless | +| 60 | WiFi-Guest | | | | Guest wireless | +| 100 | Security | | | | Cameras / access ctrl | + +## Inter-VLAN Routing +- Performed by: +- Routing device IP: + +## VLAN Notes + diff --git a/clients/khalsa/docs/overview.md b/clients/khalsa/docs/overview.md new file mode 100644 index 0000000..0892de0 --- /dev/null +++ b/clients/khalsa/docs/overview.md @@ -0,0 +1,42 @@ +# Client Overview + +## Company Name +Khalsa + +## Primary Contact +- Name: +- Phone: +- Email: + +## IT Contact +- Name: +- Phone: +- Email: + +## Contract Details +- Service Level: +- Hours Covered: +- Contract Renewal Date: + +## Environment Summary +- Total Users: +- Total Locations: 2 +- Domain Name: +- RMM Agent Count: +- Workstation Count: +- Server Count: + +## Locations + +### Camden +- Address: +- Phone: +- Primary Use: + +### River +- Address: +- Phone: +- Primary Use: + +## Notes + diff --git a/clients/khalsa/docs/rmm/rmm.md b/clients/khalsa/docs/rmm/rmm.md new file mode 100644 index 0000000..819596b --- /dev/null +++ b/clients/khalsa/docs/rmm/rmm.md @@ -0,0 +1,34 @@ +# RMM / Monitoring + +## RMM Solution +- Product: +- Console URL: +- Agent Version: + +## Agent Deployment +- Total Devices: +- Servers Monitored: +- Workstations Monitored: +- Network Devices Monitored: + +## Monitoring Policies +| Policy Name | Applies To | Alert Condition | Action | +|-------------------|----------------|-------------------------|---------------| +| Disk Space | All Servers | < 10% free | Alert + Ticket| +| CPU | All Servers | > 90% for 15 min | Alert | +| Service Monitor | All Servers | | | +| Backup Monitor | | | | +| Offline Alert | All Agents | Offline > 30 min | Alert | + +## Patch Management +- Patch Policy: +- Patch Window: +- Auto-approve: Yes/No +- Exclusions: + +## Scripting / Automation +| Script Name | Schedule | Purpose | +|---------------------|-------------|--------------------------| +| | | | + +## Notes diff --git a/clients/khalsa/docs/security/antivirus.md b/clients/khalsa/docs/security/antivirus.md new file mode 100644 index 0000000..d495dfc --- /dev/null +++ b/clients/khalsa/docs/security/antivirus.md @@ -0,0 +1,26 @@ +# Endpoint Security / Antivirus + +## Solution +- Product: +- Console URL: +- License Count: +- License Expiry: +- Managed By: + +## Policy +- Real-time Protection: Yes/No +- Scheduled Scans: (frequency) +- Exclusions: + +## Deployment Status +- Total Endpoints: +- Protected: +- Missing Agent: +- Out of Date: + +## EDR / XDR +- EDR Enabled: Yes/No +- Product: +- Console URL: + +## Notes diff --git a/clients/khalsa/docs/security/backup.md b/clients/khalsa/docs/security/backup.md new file mode 100644 index 0000000..4ed13a4 --- /dev/null +++ b/clients/khalsa/docs/security/backup.md @@ -0,0 +1,34 @@ +# Backup and Disaster Recovery + +## Backup Solution +- Product: +- Console URL: +- License/Subscription: + +## Backup Targets +| Target Name | Type | Location | Capacity | Encrypted | +|----------------|----------------|-----------------|--------------|-----------| +| | Local NAS | | | Yes/No | +| | Cloud | | | Yes/No | +| | Offsite | | | Yes/No | + +## Backup Jobs +| Job Name | Source | Target | Schedule | Retention | Status | +|-----------------|-------------------|------------|---------------|-------------|--------| +| | | | | | | + +## M365 Backup +- M365 Backup Product: +- Exchange Backed Up: Yes/No +- SharePoint Backed Up: Yes/No +- OneDrive Backed Up: Yes/No +- Teams Backed Up: Yes/No + +## Disaster Recovery Plan +- RTO Target: +- RPO Target: +- DR Site: +- Last DR Test Date: +- DR Test Result: + +## Notes diff --git a/clients/khalsa/docs/servers/camden/server_template.md b/clients/khalsa/docs/servers/camden/server_template.md new file mode 100644 index 0000000..d35ab32 --- /dev/null +++ b/clients/khalsa/docs/servers/camden/server_template.md @@ -0,0 +1,49 @@ +# Server: [SERVER NAME] + +## General Info +- Hostname: +- IP Address: +- OS: +- OS Version: +- Physical / Virtual: +- Host (if virtual): +- Location: +- Last Patched: + +## Hardware (if physical) +- Make/Model: +- CPU: +- RAM: +- Storage: +- Warranty Expiry: + +## Roles and Services + +- [ ] Domain Controller +- [ ] DNS Server +- [ ] DHCP Server +- [ ] File Server +- [ ] Print Server +- [ ] Application Server +- [ ] Database Server +- [ ] Backup Target +- [ ] RDS / Terminal Server +- [ ] Hyper-V Host + +## Shares (if file server) +| Share Name | Path | Permissions Group | Notes | +|---------------|-------------------|---------------------|----------------| +| | | | | + +## Applications Installed +| Application | Version | Purpose | License | +|-------------------|------------|----------------------|---------------| +| | | | | + +## Backup +- Backup Method: +- Backup Schedule: +- Backup Target: +- Last Verified Restore: + +## Notes diff --git a/clients/khalsa/docs/servers/river/server_template.md b/clients/khalsa/docs/servers/river/server_template.md new file mode 100644 index 0000000..d35ab32 --- /dev/null +++ b/clients/khalsa/docs/servers/river/server_template.md @@ -0,0 +1,49 @@ +# Server: [SERVER NAME] + +## General Info +- Hostname: +- IP Address: +- OS: +- OS Version: +- Physical / Virtual: +- Host (if virtual): +- Location: +- Last Patched: + +## Hardware (if physical) +- Make/Model: +- CPU: +- RAM: +- Storage: +- Warranty Expiry: + +## Roles and Services + +- [ ] Domain Controller +- [ ] DNS Server +- [ ] DHCP Server +- [ ] File Server +- [ ] Print Server +- [ ] Application Server +- [ ] Database Server +- [ ] Backup Target +- [ ] RDS / Terminal Server +- [ ] Hyper-V Host + +## Shares (if file server) +| Share Name | Path | Permissions Group | Notes | +|---------------|-------------------|---------------------|----------------| +| | | | | + +## Applications Installed +| Application | Version | Purpose | License | +|-------------------|------------|----------------------|---------------| +| | | | | + +## Backup +- Backup Method: +- Backup Schedule: +- Backup Target: +- Last Verified Restore: + +## Notes diff --git a/clients/khalsa/docs/servers/server_template.md b/clients/khalsa/docs/servers/server_template.md new file mode 100644 index 0000000..d35ab32 --- /dev/null +++ b/clients/khalsa/docs/servers/server_template.md @@ -0,0 +1,49 @@ +# Server: [SERVER NAME] + +## General Info +- Hostname: +- IP Address: +- OS: +- OS Version: +- Physical / Virtual: +- Host (if virtual): +- Location: +- Last Patched: + +## Hardware (if physical) +- Make/Model: +- CPU: +- RAM: +- Storage: +- Warranty Expiry: + +## Roles and Services + +- [ ] Domain Controller +- [ ] DNS Server +- [ ] DHCP Server +- [ ] File Server +- [ ] Print Server +- [ ] Application Server +- [ ] Database Server +- [ ] Backup Target +- [ ] RDS / Terminal Server +- [ ] Hyper-V Host + +## Shares (if file server) +| Share Name | Path | Permissions Group | Notes | +|---------------|-------------------|---------------------|----------------| +| | | | | + +## Applications Installed +| Application | Version | Purpose | License | +|-------------------|------------|----------------------|---------------| +| | | | | + +## Backup +- Backup Method: +- Backup Schedule: +- Backup Target: +- Last Verified Restore: + +## Notes diff --git a/clients/kittle/docs/cloud/azure.md b/clients/kittle/docs/cloud/azure.md new file mode 100644 index 0000000..4c7e869 --- /dev/null +++ b/clients/kittle/docs/cloud/azure.md @@ -0,0 +1,28 @@ +# Azure / Cloud Services + +## Azure Subscription +- Subscription Name: +- Subscription ID: +- Resource Group(s): +- Region: +- Monthly Spend (approx): + +## Virtual Machines +| VM Name | Size | OS | IP | Purpose | +|---------------|------------|------------|------------|-----------------| +| | | | | | + +## Networking +- Virtual Network: +- Address Space: +- Subnets: +- VPN Gateway to On-Prem: Yes/No +- ExpressRoute: Yes/No + +## Other Cloud Services + +| Service | Purpose | Admin URL | Notes | +|-----------------|------------------|------------------|-----------------| +| | | | | + +## Notes diff --git a/clients/kittle/docs/cloud/m365.md b/clients/kittle/docs/cloud/m365.md new file mode 100644 index 0000000..dc32af2 --- /dev/null +++ b/clients/kittle/docs/cloud/m365.md @@ -0,0 +1,52 @@ +# Microsoft 365 + +## Tenant Info +- Tenant Name: +- Tenant ID: +- Primary Domain: +- Admin Portal URL: https://admin.microsoft.com + +## Licensing +| License Type | Quantity | Assigned | Available | +|--------------------------|----------|----------|-----------| +| Microsoft 365 Business Basic | | | | +| Microsoft 365 Business Standard | | | | +| Microsoft 365 Business Premium | | | | +| Exchange Online Plan 1/2 | | | | +| Other | | | | + +## Exchange Online +- Mail Domain(s): +- MX Record Points To: +- SPF Record: +- DKIM Enabled: Yes/No +- DMARC Policy: +- Shared Mailboxes: +- Distribution Groups: +- Mail Flow Rules: Yes/No (describe below) + +## SharePoint / OneDrive +- SharePoint Sites: +- External Sharing: Enabled/Disabled +- OneDrive Storage Limit: + +## Teams +- Teams Phone System: Yes/No +- Calling Plan / Direct Routing: +- Auto Attendant: + +## Entra ID (Azure AD) +- Hybrid Joined: Yes/No +- Azure AD Connect Server: +- Sync Schedule: +- Password Hash Sync: Yes/No +- MFA Enforced: Yes/No +- Conditional Access Policies: + +## Security +- Defender for Office 365: Yes/No +- Safe Links: Yes/No +- Safe Attachments: Yes/No +- Audit Log Retention: + +## Notes diff --git a/clients/kittle/docs/issues/log.md b/clients/kittle/docs/issues/log.md new file mode 100644 index 0000000..04e9b24 --- /dev/null +++ b/clients/kittle/docs/issues/log.md @@ -0,0 +1,148 @@ +# Issue Log + +Record past issues and their resolutions here. This helps the AI learn from historical +troubleshooting and avoid repeating failed approaches. + +--- + +### 2026-03-12 - Windows Server 2025 EVALUATION License — Time Bomb +- **Reported By:** Server audit +- **Severity:** Critical +- **Symptoms:** SERVER (10.0.0.5) is running Windows Server 2025 Standard as an EVALUATION install (Build 26100). Evaluation licenses expire after 180 days, after which the server shuts down every hour. This is the only domain controller for kittle.lan. +- **Root Cause:** Full license never purchased or applied during server setup. +- **Resolution:** OPEN — Purchase and apply a full Windows Server 2025 Standard license immediately. Check remaining evaluation time with `slmgr /dlv`. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-12 - No Dedicated Firewall — ISP Router Only +- **Reported By:** Server audit (ARP/network analysis) +- **Severity:** High +- **Symptoms:** The network gateway at 10.0.0.1 (MAC: 42:0f:c1:f0:e6:43) is an ISP-provided router. No dedicated firewall appliance (pfSense, SonicWall, FortiGate, etc.) exists. The ISP router provides basic NAT but likely has no stateful packet inspection, IDS/IPS, content filtering, or granular firewall rules. +- **Root Cause:** No firewall was ever deployed — the ISP router was used as-is. +- **Resolution:** OPEN — Deploy a dedicated firewall appliance. Recommended: pfSense (free), or a commercial UTM (FortiGate, SonicWall). Place it between the ISP router and the LAN switch. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-12 - No Backup Solution +- **Reported By:** Server audit +- **Severity:** Critical +- **Symptoms:** No backup solution is visible on SERVER. No Windows Server Backup, no third-party backup agent, no cloud backup. If the server fails, Active Directory, DNS, file shares (C:\Shares\Home), and QuickBooks data are permanently lost. +- **Root Cause:** Backup was never configured. +- **Resolution:** OPEN — Implement backup immediately. Options: + 1. Windows Server Backup to external USB drive or NAS + 2. Veeam Backup Free Edition + 3. Cloud backup (Backblaze B2, Wasabi, etc.) +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-12 - QuickBooks Pro 2024 Installed on Domain Controller +- **Reported By:** Server audit (installed software) +- **Severity:** High +- **Symptoms:** QuickBooks Pro 2024 (v34) is installed directly on SERVER, the primary domain controller. Business applications on a DC increase attack surface, consume resources needed for AD services, and complicate server migration. +- **Root Cause:** QuickBooks was installed on the only available server rather than a dedicated workstation. +- **Resolution:** OPEN — Migrate QuickBooks to a workstation. QuickBooks can run in multi-user mode with the database on \\SERVER\QBooks. The application itself should run on ACCOUNTING or another workstation. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-12 - DHCP Running on ISP Router Instead of Server +- **Reported By:** Server audit +- **Severity:** Medium +- **Symptoms:** DHCP is served by the ISP router at 10.0.0.1. The Windows Server DHCP role is installed but has zero scopes configured. DHCP clients may be receiving the ISP's DNS servers instead of the domain controller (10.0.0.5), which would break AD name resolution. +- **Root Cause:** DHCP was never configured on the server; ISP router default was left in place. +- **Resolution:** OPEN — Migrate DHCP to Windows Server for centralized management and correct DNS distribution. Disable DHCP on the ISP router after migration. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-12 - Role-Based AD Account Names +- **Reported By:** Server audit (AD users) +- **Severity:** Medium +- **Symptoms:** Two AD accounts use role-based names instead of individual names: "accountant" and "frontdesk". Role-based accounts cannot be audited to a specific person — if something is deleted or accessed inappropriately, there's no way to trace who did it. +- **Root Cause:** Accounts created for convenience instead of using individual names. +- **Resolution:** OPEN — Identify the actual users of these accounts. Create individual accounts (e.g., darline.cabrera for accountant). Migrate data and disable role-based accounts. +- **Time to Resolve:** Pending +- **Lessons Learned:** N/A + +--- + +### 2026-03-12 - Email Issue: Moved Emails Reappearing in Inbox +- **Reported By:** Users +- **Severity:** Medium +- **Symptoms:** Users report moving emails from Inbox to subfolders, then finding them back in the Inbox days later. Affects multiple users. +- **Root Cause:** Suspected Outlook cached mode issue. When Outlook is in Cached Exchange Mode, moves may not sync properly to the server if the OST file is corrupted or if multiple devices are accessing the same mailbox with conflicting cached states. +- **Resolution:** OPEN — Need M365 admin access to investigate further. Check: + 1. Check if Outlook is in Cached or Online mode (File > Account Settings > Account Settings > Change) + 2. Check if users access email on multiple devices (phone + PC) — moves on one device may not sync + 3. Try switching to Online mode temporarily to see if issue persists + 4. If cached mode is the culprit, delete and rebuild the OST file + 5. Check if any Outlook rules are moving mail back to Inbox +- **Time to Resolve:** Pending M365 access + investigation +- **Lessons Learned:** N/A + +--- + +### 2026-03-12 - Unknown Service on Port 8019 +- **Reported By:** Server audit (listening ports) +- **Severity:** Low +- **Symptoms:** An unidentified service is listening on TCP port 8019 on SERVER. Not a standard Windows or AD port. +- **Root Cause:** Unknown — could be QuickBooks-related, ScreenConnect, or another application. +- **Resolution:** OPEN — Run `netstat -ano | findstr 8019` to identify the PID, then `tasklist /fi "PID eq "` to identify the process. +- **Time to Resolve:** Quick — 2 minutes to identify +- **Lessons Learned:** N/A + +--- + +### 2026-03-12 - No Reverse DNS Zone for 10.0.0.x +- **Reported By:** Server audit (DNS analysis) +- **Severity:** Low +- **Symptoms:** No reverse lookup zone exists for 10.0.0.0/24. PTR lookups fail for all internal hosts. Some applications and troubleshooting tools rely on reverse DNS. +- **Root Cause:** Reverse zone was never created during AD/DNS setup. +- **Resolution:** OPEN — Create AD-integrated reverse lookup zone: 0.0.10.in-addr.arpa. Enable secure dynamic updates. +- **Time to Resolve:** Quick fix — 5 minutes +- **Lessons Learned:** N/A + +--- + +### 2026-03-12 - 4 Workstations with Generic DESKTOP-xxx Names +- **Reported By:** Server audit (AD computers) +- **Severity:** Low +- **Symptoms:** Four domain-joined computers have generic Windows-assigned names: WINDOWS-QV1B0EL, DESKTOP-R0KA2UG, DESKTOP-9B2SMD9, DESKTOP-2560Q7R. Generic names make it impossible to identify which user or role a computer belongs to without logging in. +- **Root Cause:** Computers were domain-joined without being renamed first. +- **Resolution:** OPEN — Identify the user at each workstation and rename to match (e.g., ALEXIS-PC, MARCO-PC, etc.). Rename via System Properties and reboot. +- **Time to Resolve:** Pending — need onsite visit to correlate names to users +- **Lessons Learned:** N/A + +--- + +### 2026-03-12 - File Explorer Closing When Browsing Network Shares +- **Reported By:** Users (FRONTDESK, ACCOUNTING, DESKTOP-2560Q7R/Wrex) +- **Severity:** Medium +- **Symptoms:** File Explorer windows close unexpectedly when users browse \\SERVER\Home or \\SERVER\QBooks. No crash logged in Event Viewer. Happens intermittently on 3 of 7 workstations. +- **Root Cause:** HomeFolder GPO drive maps (H: → \\server\home, Q: → \\server\qbooks) were using **Replace** action. Replace disconnects and reconnects the drive every GP refresh (~90 min), killing any open Explorer window on that path. +- **Resolution:** Changed both drive map actions from **Replace** to **Update** in the HomeFolder GPO on 2026-03-12. Update preserves existing connections. Monitoring for confirmation. +- **Time to Resolve:** Same day — awaiting user confirmation 2026-03-13 +- **Lessons Learned:** Always use **Update** (not Replace) for GPO drive maps unless there's a specific reason to tear down and recreate the mapping. + +--- + +### 2026-03-25 - FRONTDESK Folder View Keeps Changing Sort Order +- **Reported By:** User +- **Severity:** Low +- **Symptoms:** File Explorer on FRONTDESK would switch from ascending alphabetical to descending or another view when browsing mapped drives to the server. View settings would not persist. +- **Root Cause:** Windows automatic folder type discovery keeps reassigning view templates to network folders, overriding user preferences. +- **Resolution:** RESOLVED — Ran PowerShell script to clear cached folder views (Bags/BagMRU registry keys), disabled folder type auto-detection, and forced all folders to Details view sorted by Name ascending via AllFolders Shell registry key. Explorer restarted to apply. +- **Time to Resolve:** Same day +- **Lessons Learned:** "Apply to Folders" doesn't stick for mapped/network drives. Must clear Bags registry and set AllFolders default via `{5C4F28B5-F869-4E84-8E60-F11DB97C5CC7}` shell key. + +--- + + diff --git a/clients/kittle/docs/network/dhcp.md b/clients/kittle/docs/network/dhcp.md new file mode 100644 index 0000000..1676899 --- /dev/null +++ b/clients/kittle/docs/network/dhcp.md @@ -0,0 +1,46 @@ +# DHCP Configuration + +## DHCP Server +- Server Name: ISP Router +- Server IP: 10.0.0.1 +- Failover Partner: None + +**Note:** The Windows Server DHCP role is installed on SERVER (10.0.0.5) but has **zero scopes configured**. All DHCP is handled by the ISP router. + +## Scopes + +### Scope - LAN (ISP Router) +- Subnet: 10.0.0.0/24 +- Range Start: Unknown — need to check ISP router admin interface +- Range End: Unknown +- Subnet Mask: 255.255.255.0 +- Default Gateway: 10.0.0.1 +- DNS Servers: **Unknown — critical to verify** (should be 10.0.0.5 for AD) +- Lease Duration: Unknown + +## Reservations + +No reservations documented. Need to check ISP router for any existing DHCP reservations. + +| Device Name | MAC Address | IP Address | Scope | Notes | +|------------|-------------|------------|-------|-------| +| SERVER | — | 10.0.0.5 | LAN | DC — should be reserved or static | +| UniFi Switch | 0C:EA:14:8A:8D:7F | 10.0.0.122 | LAN | Should be reserved | + +## DHCP Relay +- Not applicable — single subnet, DHCP server on same segment + +## Issues +1. **DHCP on ISP router instead of server** — Less control over DHCP options (DNS, NTP, lease times). Cannot manage reservations centrally via Windows tools. ISP router may hand out ISP DNS instead of the DC's DNS (10.0.0.5), which would break AD name resolution. +2. **Windows DHCP role installed but unused** — Creates confusion. Either uninstall or migrate DHCP to the server. + +## Recommendations +1. **Migrate DHCP to Windows Server** — Provides centralized management, AD-integrated DNS updates, DHCP reservations via PowerShell, and logging. +2. **Create reservations** for: SERVER (10.0.0.5), UniFi switch (10.0.0.122), printers, and any other infrastructure. +3. **Set DNS option** — Ensure DHCP hands out 10.0.0.5 as the primary DNS server. + +## TODO +- [ ] Log into ISP router and document DHCP scope, range, DNS settings, and any reservations +- [ ] Verify what DNS servers DHCP clients receive +- [ ] Plan DHCP migration from ISP router to Windows Server +- [ ] Create DHCP reservations for infrastructure devices diff --git a/clients/kittle/docs/network/dns.md b/clients/kittle/docs/network/dns.md new file mode 100644 index 0000000..5b4b029 --- /dev/null +++ b/clients/kittle/docs/network/dns.md @@ -0,0 +1,41 @@ +# DNS Configuration + +## Windows DNS Server (AD-Integrated) +- Server: SERVER (10.0.0.5) +- Role: Primary DNS for kittle.lan domain +- DNS Client: 127.0.0.1 (correct — DC points to itself) + +## DNS Forwarders +- Forwarder 1: 10.0.0.1 (ISP router — for external resolution) + +## DNS Zones + +| Zone | Type | AD-Integrated | Notes | +|------|------|---------------|-------| +| kittle.lan | Primary | Yes | Main AD zone | +| _msdcs.kittle.lan | Primary | Yes | AD metadata zone (SRV records) | + +**No reverse lookup zone exists for 10.0.0.x** — PTR lookups will fail for all internal hosts. + +## DNS Architecture +- **Windows DNS** (10.0.0.5): Authoritative for kittle.lan. Handles AD SRV records, Kerberos, LDAP lookups. +- **ISP Router** (10.0.0.1): Acts as forwarder for external (internet) DNS resolution. +- Workstations should use 10.0.0.5 as primary DNS (the DC) so AD name resolution works correctly. +- If workstations are getting DNS from DHCP on the ISP router, they may be pointed at the ISP's DNS instead of the DC — needs verification. + +## External DNS +- Registrar: Unknown +- Primary Domain: kittlearizona.com +- Management URL: Unknown + +## Issues +1. **No reverse DNS zone** — Create 0.0.10.in-addr.arpa for PTR lookups on 10.0.0.0/24 +2. **DHCP DNS settings unknown** — ISP router handles DHCP; unclear if it hands out 10.0.0.5 as DNS or the ISP's own DNS servers. If clients don't use the DC for DNS, AD name resolution and domain joins may have issues. +3. **Single forwarder** — Only forwarding to 10.0.0.1. Consider adding a secondary forwarder (8.8.8.8 or 1.1.1.1) for redundancy if the ISP router's DNS fails. + +## TODO +- [ ] Create reverse lookup zone: 0.0.10.in-addr.arpa +- [ ] Verify what DNS server DHCP clients receive from the ISP router +- [ ] Consider adding secondary DNS forwarder for redundancy +- [ ] Enable DNS scavenging to prevent stale records +- [ ] Document external DNS (registrar, MX records, SPF/DKIM/DMARC for kittlearizona.com) diff --git a/clients/kittle/docs/network/firewall.md b/clients/kittle/docs/network/firewall.md new file mode 100644 index 0000000..21d8c8e --- /dev/null +++ b/clients/kittle/docs/network/firewall.md @@ -0,0 +1,47 @@ +# Firewall Configuration + +## Device Info +- Vendor/Model: +- Firmware Version: +- Management IP: +- Management URL: +- HA Pair: Yes/No +- License Expiry: + +## Interfaces +| Interface | Zone | IP Address | VLAN | Description | +|-----------|-----------|-----------------|------|-------------------| +| WAN1 | WAN | | | Primary Internet | +| WAN2 | WAN | | | Backup Internet | +| LAN | LAN | | | | +| DMZ | DMZ | | | | + +## NAT Rules +| Name | Source | Destination | Port(s) | NAT To | +|-------------------|---------------|----------------|-------------|-----------------| +| | | | | | + +## Key Firewall Policies +| Name | Source Zone | Dest Zone | Service | Action | Notes | +|-------------------|--------------|---------------|-------------|--------|--------| +| | | | | | | + +## VPN +### Site-to-Site VPNs +| Peer Name | Peer IP | Local Subnet | Remote Subnet | Status | +|-------------------|--------------|----------------|---------------|--------| +| | | | | | + +### SSL/Client VPN +- Enabled: Yes/No +- Portal URL: +- Auth Method: +- IP Pool: +- Split Tunnel: Yes/No + +## Content Filtering +- Web Filter Profile: +- App Control Profile: +- DNS Filter: + +## Notes diff --git a/clients/kittle/docs/network/topology.md b/clients/kittle/docs/network/topology.md new file mode 100644 index 0000000..6e3b124 --- /dev/null +++ b/clients/kittle/docs/network/topology.md @@ -0,0 +1,87 @@ +# Network Topology + +## Internet Connection +- ISP: Unknown +- Gateway: 10.0.0.1 (MAC: 42:0f:c1:f0:e6:43) +- Type: ISP router — serves as gateway, DHCP server, and only "firewall" +- **No dedicated firewall appliance** + +## Network Design +- Single flat subnet: 10.0.0.0/24 +- No VLANs +- All devices (server, workstations, printers, APs) on the same broadcast domain + +## Switches + +### UniFi USW-Lite-16-PoE +- Model: Ubiquiti USW-Lite-16-PoE +- IP Address: 10.0.0.122 +- MAC: 0C:EA:14:8A:8D:7F +- Port Count: 16 (PoE) +- Management: Self-hosted UniFi controller (managed by MSP) + +## Key Infrastructure Devices + +| Device | IP Address | MAC | Notes | +|--------|-----------|-----|-------| +| ISP Router (Gateway) | 10.0.0.1 | 42:0f:c1:f0:e6:43 | Gateway, DHCP, only firewall | +| SERVER (DC) | 10.0.0.5 | — | HPE ProLiant MicroServer Gen11 | +| UniFi Switch | 10.0.0.122 | 0C:EA:14:8A:8D:7F | USW-Lite-16-PoE | + +## ARP Table (All Observed Devices) + +| IP Address | MAC Address | Identified As | +|-----------|-------------|--------------| +| 10.0.0.1 | 42:0f:c1:f0:e6:43 | ISP Router (Gateway) | +| 10.0.0.5 | — | SERVER (DC) | +| 10.0.0.52 | 00:50:AA:54:8C:EF | Unknown | +| 10.0.0.100 | C4:5A:B1:F9:48:18 | Unknown | +| 10.0.0.105 | 92:CE:74:91:59:AD | Unknown | +| 10.0.0.106 | 5C:47:5E:7E:87:9E | Unknown | +| 10.0.0.110 | 48:25:67:D4:2B:1F | Unknown | +| 10.0.0.117 | 54:E0:19:E2:21:DD | Unknown | +| 10.0.0.120 | A8:9C:6C:58:9C:98 | Unknown | +| 10.0.0.121 | 48:25:67:D4:29:F0 | Unknown | +| 10.0.0.122 | 0C:EA:14:8A:8D:7F | UniFi USW-Lite-16-PoE | +| 10.0.0.123 | C4:5A:B1:F9:B2:9B | Unknown | +| 10.0.0.131 | 54:E0:19:E2:CF:D1 | Unknown | +| 10.0.0.132 | 48:25:67:D4:2A:FB | Unknown | +| 10.0.0.133 | C4:5A:B1:F9:66:BC | Unknown | +| 10.0.0.134 | 78:46:5C:AF:7A:EF | Unknown | +| 10.0.0.144 | 48:25:67:D4:29:3F | Unknown | +| 10.0.0.145 | 54:E0:19:E2:CB:DB | Unknown | +| 10.0.0.152 | E8:65:38:E9:45:CB | Unknown | +| 10.0.0.156 | 5A:37:74:00:8C:37 | Unknown | +| 10.0.0.161 | B0:7B:25:14:3E:F1 | Unknown | +| 10.0.0.162 | A8:9C:6C:4A:0C:78 | Unknown | +| 10.0.0.168 | 48:25:67:D4:29:57 | Unknown | +| 10.0.0.169 | A4:BB:6D:A8:F8:1B | Unknown | +| 10.0.0.171 | 76:8C:A8:6D:60:3C | Unknown | +| 10.0.0.172 | C0:BF:BE:E8:56:1D | Unknown | +| 10.0.0.184 | 22:7B:45:0B:97:9C | Unknown | +| 10.0.0.189 | 30:8D:99:A9:0B:C3 | Unknown | +| 10.0.0.192 | 48:25:67:D4:2B:0D | Unknown | +| 10.0.0.198 | 48:25:67:D4:2B:13 | Unknown | +| 10.0.0.241 | A4:BB:6D:A9:CC:B1 | Unknown | + +**Note:** 31 devices observed on the network via ARP. Many are unidentified — need MAC vendor lookups and onsite correlation to map devices to workstations, printers, phones, etc. + +## Network Diagram +``` +[Internet] + | +[ISP Router: 10.0.0.1] -- DHCP, Gateway, "Firewall" + | +[UniFi USW-Lite-16-PoE: 10.0.0.122] + | + +-- SERVER (DC): 10.0.0.5 + +-- 7 Workstations (Win11 Pro) + +-- ~20 other devices (printers, phones, etc.) +``` + +## Notes +- Flat network with no segmentation — all devices can reach all other devices +- No dedicated firewall — ISP router is the only perimeter device +- MAC 42:0f:c1:f0:e6:43 on the gateway is an unusual/randomized MAC — confirms consumer-grade ISP equipment +- Several MAC prefixes repeat (48:25:67, C4:5A:B1, 54:E0:19, A8:9C:6C) — likely same vendor, possibly UniFi APs, printers, or phones +- Onsite visit needed to correlate ARP entries to physical devices diff --git a/clients/kittle/docs/network/vlans.md b/clients/kittle/docs/network/vlans.md new file mode 100644 index 0000000..475f778 --- /dev/null +++ b/clients/kittle/docs/network/vlans.md @@ -0,0 +1,21 @@ +# VLANs + +## VLAN Table + +| VLAN ID | Name | Subnet | Gateway | DHCP Scope | Purpose | +|---------|---------------|-----------------|-----------------|------------------|------------------------| +| 1 | Default | | | | | +| 10 | Management | | | | Network devices | +| 20 | Servers | | | | Server infrastructure | +| 30 | Workstations | | | | End user devices | +| 40 | VoIP | | | | Phone system | +| 50 | WiFi-Corp | | | | Corporate wireless | +| 60 | WiFi-Guest | | | | Guest wireless | +| 100 | Security | | | | Cameras / access ctrl | + +## Inter-VLAN Routing +- Performed by: +- Routing device IP: + +## VLAN Notes + diff --git a/clients/kittle/docs/overview.md b/clients/kittle/docs/overview.md new file mode 100644 index 0000000..6090ec1 --- /dev/null +++ b/clients/kittle/docs/overview.md @@ -0,0 +1,93 @@ +# Client Overview + +## Company Name +Kittle Design & Construction LLC — General Contractor + +## Primary Contact +- Name: Ken +- Email: ken@kittlearizona.com + +## Secondary Contact +- Name: Darline Cabrera (Bookkeeper) +- Email: accounting@kittlearizona.com + +## IT Contact +- Name: Howard (Computer Guru, MSP) +- Account: sysadmin (Domain Admin) + +## Company Info +- Address: 2539 N Balboa Ave #125, Tucson, AZ 85705 +- Phone: 520.299.0404 +- Fax: 520.299.0477 +- Website: kittlearizona.com +- Industry: Construction (general contractor) + +## Environment Summary +- Domain: kittle.lan +- Total Users: 10 (8 regular + sysadmin + QBDataServiceUser34) +- Workstation Count: 7 +- Server Count: 1 (HPE ProLiant MicroServer Gen11) +- Network: Single flat 10.0.0.0/24 subnet +- Gateway/Firewall: ISP router at 10.0.0.1 (no dedicated firewall) +- Switch: UniFi USW-Lite-16-PoE at 10.0.0.122 +- Remote Access: ScreenConnect +- Key Software: QuickBooks Pro 2024 (on server) + +## AD Users + +| Name | SamAccountName | Enabled | LastLogonDate | Notes | +|------|---------------|---------|---------------|-------| +| Administrator | Administrator | True | 3/10/2026 | Domain Admin | +| Guest | Guest | False | — | Default, disabled | +| krbtgt | krbtgt | False | — | Default, disabled | +| Alexis | alexis | True | 3/2/2026 | | +| Marco | Marco | True | 3/3/2026 | | +| accountant | accountant | True | 3/5/2026 | Role-based name — should be individual | +| Ken | ken | True | 3/2/2026 | Owner | +| Front Desk | frontdesk | True | 3/6/2026 | Role-based name — should be individual | +| Lori | lori | True | 3/11/2026 | | +| Wrex | wrex | True | 3/9/2026 | | +| Computer Guru | sysadmin | True | 12/23/2025 | MSP admin, Domain Admin | +| QBDataServiceUser34 | QBDataServiceUser34 | True | 2/9/2026 | QuickBooks service account | + +**Domain Admins:** Administrator, Computer Guru (sysadmin) + +## AD Computers + +| Name | OS | LastLogonDate | Notes | +|------|-----|---------------|-------| +| SERVER | Windows Server 2025 Standard Evaluation | 3/10/2026 | DC | +| WINDOWS-QV1B0EL | Windows 11 Pro | 3/6/2026 | Generic name — needs renaming | +| DESKTOP-R0KA2UG | Windows 11 Pro | 3/11/2026 | Generic name — needs renaming | +| DESKTOP-9B2SMD9 | Windows 11 Pro | 3/6/2026 | Generic name — needs renaming | +| FRONTDESK | Windows 11 Pro | 3/9/2026 | | +| ACCOUNTING | Windows 11 Pro for Workstations | 3/9/2026 | | +| CHRISTINE-WIN10 | Windows 11 Pro | 3/9/2026 | Legacy name — OS is actually Win11 | +| DESKTOP-2560Q7R | Windows 11 Pro | 3/6/2026 | Wrex — needs renaming | + +## Group Policy Objects + +| GPO Name | Modified | Notes | +|----------|----------|-------| +| Default Domain Policy | 12/23/2025 | | +| Default Domain Controllers Policy | 2/9/2026 | | +| HomeFolder | 2/9/2026 | Home folder mapping | +| Intranet Zone - File Server | 3/20/2026 | Adds \\\\SERVER and \\\\10.0.0.5 to Local Intranet zone — fixes PDF preview on shares (Oct 2025 security update) | + +## File Shares + +| Share Name | Path | Notes | +|-----------|------|-------| +| Home | C:\Shares\Home | User home folders | +| QBooks | C:\Shares\Home\QBooks | QuickBooks data | + +## Notes +- Server is running **Windows Server 2025 EVALUATION** — will stop working after 180 days. Must purchase and activate a full license. +- QuickBooks Pro 2024 is installed directly on the domain controller. Should be migrated off the DC. +- No dedicated firewall — only the ISP router at 10.0.0.1. +- No backup solution visible. +- 4 workstations have generic DESKTOP-xxx / WINDOWS-xxx names and should be renamed to match user/role. +- 2 role-based AD accounts (accountant, frontdesk) should be replaced with individual user accounts. +- Email issue reported: users moving emails to folders and they reappear in inbox days later (likely Outlook cached mode issue). M365 access needed. +- Explorer closing issue: HomeFolder GPO drive maps were set to Replace (disconnects on GP refresh). Fixed to Update on 2026-03-12 — awaiting confirmation. +- Machine mapping: FRONTDESK=Front Desk, ACCOUNTING=accountant, CHRISTINE-WIN10=Christine, DESKTOP-2560Q7R=Wrex. Remaining unknown: WINDOWS-QV1B0EL, DESKTOP-R0KA2UG, DESKTOP-9B2SMD9. diff --git a/clients/kittle/docs/rmm/rmm.md b/clients/kittle/docs/rmm/rmm.md new file mode 100644 index 0000000..819596b --- /dev/null +++ b/clients/kittle/docs/rmm/rmm.md @@ -0,0 +1,34 @@ +# RMM / Monitoring + +## RMM Solution +- Product: +- Console URL: +- Agent Version: + +## Agent Deployment +- Total Devices: +- Servers Monitored: +- Workstations Monitored: +- Network Devices Monitored: + +## Monitoring Policies +| Policy Name | Applies To | Alert Condition | Action | +|-------------------|----------------|-------------------------|---------------| +| Disk Space | All Servers | < 10% free | Alert + Ticket| +| CPU | All Servers | > 90% for 15 min | Alert | +| Service Monitor | All Servers | | | +| Backup Monitor | | | | +| Offline Alert | All Agents | Offline > 30 min | Alert | + +## Patch Management +- Patch Policy: +- Patch Window: +- Auto-approve: Yes/No +- Exclusions: + +## Scripting / Automation +| Script Name | Schedule | Purpose | +|---------------------|-------------|--------------------------| +| | | | + +## Notes diff --git a/clients/kittle/docs/scripts/fix-pdf-preview.ps1 b/clients/kittle/docs/scripts/fix-pdf-preview.ps1 new file mode 100644 index 0000000..41c6291 --- /dev/null +++ b/clients/kittle/docs/scripts/fix-pdf-preview.ps1 @@ -0,0 +1,61 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + Adds the Kittle file server to the Local Intranet zone so PDF preview + works on network shares (blocked by Oct 2025 security update). + +.DESCRIPTION + Windows security updates from October 14, 2025 onward disable preview + for files in the "Internet Zone". UNC shares may be classified as Internet + Zone if not explicitly added to Local Intranet or Trusted Sites. + + This script adds \\SERVER and \\10.0.0.5 to the Local Intranet zone + (zone 1) via HKLM registry so it applies to all users on the machine. + + Run on WORKSTATIONS ONLY — not needed on the server. + +.NOTES + Ref: https://support.microsoft.com/en-us/topic/56d55920-6187-4aae-a4f6-102454ef61fb +#> + +$ErrorActionPreference = 'Stop' + +# Zone 1 = Local Intranet +$zone = 1 +$basePath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap' + +# Add server by hostname +$hostKey = Join-Path $basePath 'Domains\SERVER' +if (-not (Test-Path $hostKey)) { + New-Item -Path $hostKey -Force | Out-Null +} +Set-ItemProperty -Path $hostKey -Name 'file' -Value $zone -Type DWord +Write-Host "Added \\SERVER to Local Intranet zone" -ForegroundColor Green + +# Add server by IP +$ipKey = Join-Path $basePath 'Domains\10.0.0.5' +if (-not (Test-Path $ipKey)) { + New-Item -Path $ipKey -Force | Out-Null +} +Set-ItemProperty -Path $ipKey -Name 'file' -Value $zone -Type DWord +Write-Host "Added \\10.0.0.5 to Local Intranet zone" -ForegroundColor Green + +# Also add to EscDomains in case IE ESC is somehow enabled +$escBase = Join-Path $basePath 'EscDomains' +if (Test-Path $escBase) { + $escHostKey = Join-Path $escBase 'SERVER' + if (-not (Test-Path $escHostKey)) { + New-Item -Path $escHostKey -Force | Out-Null + } + Set-ItemProperty -Path $escHostKey -Name 'file' -Value $zone -Type DWord + + $escIpKey = Join-Path $escBase '10.0.0.5' + if (-not (Test-Path $escIpKey)) { + New-Item -Path $escIpKey -Force | Out-Null + } + Set-ItemProperty -Path $escIpKey -Name 'file' -Value $zone -Type DWord + Write-Host "Added to EscDomains as well" -ForegroundColor Green +} + +Write-Host "`nDone. Restart File Explorer or log off/on for changes to take effect." -ForegroundColor Cyan +Write-Host "Verify: Internet Options > Security > Local Intranet > Sites > Advanced" -ForegroundColor Cyan diff --git a/clients/kittle/docs/scripts/gpo-intranet-zone.ps1 b/clients/kittle/docs/scripts/gpo-intranet-zone.ps1 new file mode 100644 index 0000000..3fcda86 --- /dev/null +++ b/clients/kittle/docs/scripts/gpo-intranet-zone.ps1 @@ -0,0 +1,68 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + Creates a GPO that adds \\SERVER and \\10.0.0.5 to the Local Intranet zone. + Fixes PDF preview on network shares blocked by Oct 2025 security update. + +.DESCRIPTION + Uses the "Site to Zone Assignment List" policy under: + Computer Config > Admin Templates > Windows Components > Internet Explorer > + Internet Control Panel > Security Page + + Zone 1 = Local Intranet. Applies to all domain-joined machines. + +.NOTES + Run on SERVER (10.0.0.5) as Domain Admin. + Ref: https://support.microsoft.com/en-us/topic/56d55920-6187-4aae-a4f6-102454ef61fb +#> + +$ErrorActionPreference = 'Stop' +Import-Module GroupPolicy + +$gpoName = 'Intranet Zone - File Server' +$domain = 'kittle.lan' + +# Sites to add to Local Intranet (zone 1) +$sites = @( + 'file://SERVER' + 'file://10.0.0.5' + '\\SERVER' + '\\10.0.0.5' +) + +# Registry path for the Site to Zone Assignment List policy +$policyKey = 'HKLM\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMapKey' + +# Check if GPO already exists +$existing = Get-GPO -Name $gpoName -Domain $domain -ErrorAction SilentlyContinue +if ($existing) { + Write-Host "GPO '$gpoName' already exists (ID: $($existing.Id)). Updating..." -ForegroundColor Yellow +} else { + Write-Host "Creating GPO: $gpoName" -ForegroundColor Cyan + New-GPO -Name $gpoName -Domain $domain -Comment 'Adds file server to Local Intranet zone for PDF preview on shares' | Out-Null +} + +# Set each site to zone 1 (Local Intranet) +foreach ($site in $sites) { + Set-GPRegistryValue -Name $gpoName -Domain $domain ` + -Key $policyKey ` + -ValueName $site ` + -Type String ` + -Value '1' | Out-Null + Write-Host " Added: $site -> Local Intranet" -ForegroundColor Green +} + +# Link the GPO to the domain root (applies to all machines) +$linked = (Get-GPInheritance -Target $domain).GpoLinks | Where-Object { $_.DisplayName -eq $gpoName } +if (-not $linked) { + New-GPLink -Name $gpoName -Target "DC=kittle,DC=lan" -LinkEnabled Yes | Out-Null + Write-Host "`nGPO linked to $domain" -ForegroundColor Green +} else { + Write-Host "`nGPO already linked to $domain" -ForegroundColor Yellow +} + +# Summary +Write-Host "`n=== Done ===" -ForegroundColor Cyan +Write-Host "GPO '$gpoName' is active. Workstations will pick it up at next GP refresh." +Write-Host "To force now, run on workstations: gpupdate /force" -ForegroundColor Cyan +Write-Host "`nVerify: gpresult /r on a workstation should show '$gpoName' under Computer Settings" -ForegroundColor Cyan diff --git a/clients/kittle/docs/security/antivirus.md b/clients/kittle/docs/security/antivirus.md new file mode 100644 index 0000000..d495dfc --- /dev/null +++ b/clients/kittle/docs/security/antivirus.md @@ -0,0 +1,26 @@ +# Endpoint Security / Antivirus + +## Solution +- Product: +- Console URL: +- License Count: +- License Expiry: +- Managed By: + +## Policy +- Real-time Protection: Yes/No +- Scheduled Scans: (frequency) +- Exclusions: + +## Deployment Status +- Total Endpoints: +- Protected: +- Missing Agent: +- Out of Date: + +## EDR / XDR +- EDR Enabled: Yes/No +- Product: +- Console URL: + +## Notes diff --git a/clients/kittle/docs/security/backup.md b/clients/kittle/docs/security/backup.md new file mode 100644 index 0000000..4ed13a4 --- /dev/null +++ b/clients/kittle/docs/security/backup.md @@ -0,0 +1,34 @@ +# Backup and Disaster Recovery + +## Backup Solution +- Product: +- Console URL: +- License/Subscription: + +## Backup Targets +| Target Name | Type | Location | Capacity | Encrypted | +|----------------|----------------|-----------------|--------------|-----------| +| | Local NAS | | | Yes/No | +| | Cloud | | | Yes/No | +| | Offsite | | | Yes/No | + +## Backup Jobs +| Job Name | Source | Target | Schedule | Retention | Status | +|-----------------|-------------------|------------|---------------|-------------|--------| +| | | | | | | + +## M365 Backup +- M365 Backup Product: +- Exchange Backed Up: Yes/No +- SharePoint Backed Up: Yes/No +- OneDrive Backed Up: Yes/No +- Teams Backed Up: Yes/No + +## Disaster Recovery Plan +- RTO Target: +- RPO Target: +- DR Site: +- Last DR Test Date: +- DR Test Result: + +## Notes diff --git a/clients/kittle/docs/servers/server.md b/clients/kittle/docs/servers/server.md new file mode 100644 index 0000000..7f4fcd7 --- /dev/null +++ b/clients/kittle/docs/servers/server.md @@ -0,0 +1,120 @@ +# Server: SERVER + +## General Info +- Hostname: SERVER +- IP Address: 10.0.0.5 +- Subnet Mask: 255.255.255.0 (/24) +- Default Gateway: 10.0.0.1 +- DNS Servers: 127.0.0.1 (itself — correct for DC) +- OS: Microsoft Windows Server 2025 Standard **EVALUATION** +- OS Version: Build 26100 +- OS Configuration: **Primary Domain Controller** +- Domain: kittle.lan +- Physical / Virtual: Physical +- Location: Office + +## Hardware +- Make/Model: HPE ProLiant MicroServer Gen11 +- BIOS: HPE 2.22 (5/16/2025) +- CPU: Intel Xeon E-2414 (4 cores) +- RAM: 80 GB + +## Storage + +| Drive | Label | Filesystem | Size | Notes | +|-------|-------|------------|------|-------| +| C: | (OS) | NTFS | ~11 TB | Primary volume | +| (secondary) | Server2 2022_03_31 | — | ~2 TB | Secondary storage — possibly old server backup or migration data | + +## Network Interfaces +- 4x Embedded LOM ports (Port 1-4) +- Only Port 1 is active +- 3 ports unused + +## Roles and Services (Installed) +- [x] **Active Directory Domain Services** (Primary DC) +- [x] **DNS Server** +- [x] **DHCP Server** (installed but scopes are empty — DHCP runs on ISP router) +- [x] **File Server** (C:\Shares) +- [x] **Print Server** +- [x] Group Policy Management + +## SMB File Shares + +| Share Name | Path | Notes | +|-----------|------|-------| +| Home | C:\Shares\Home | User home folders | +| QBooks | C:\Shares\Home\QBooks | QuickBooks data files | +| NETLOGON | (default) | AD logon scripts | +| SYSVOL | (default) | Group Policy store | + +## Installed Software + +| Software | Version | Notes | +|----------|---------|-------| +| **QuickBooks Pro 2024** | 34 | **Should NOT be on a DC** — migrate to workstation | +| ScreenConnect | — | Remote access agent | +| Microsoft Edge | — | Browser | + +## Listening Ports (Key Services) + +| Port | Protocol | Service | Notes | +|------|----------|---------|-------| +| 53 | TCP | DNS | AD DNS server | +| 88 | TCP | Kerberos | AD authentication | +| 135 | TCP | RPC | Endpoint Mapper | +| 139 | TCP | NetBIOS | Legacy name service | +| 389 | TCP | LDAP | AD directory | +| 445 | TCP | SMB | File shares | +| 464 | TCP | Kerberos kpasswd | Password changes | +| 636 | TCP | LDAPS | LDAP over SSL | +| 3268 | TCP | Global Catalog | AD GC | +| 3269 | TCP | GC SSL | AD GC over SSL | +| 5985 | TCP | WinRM | PowerShell remoting | +| 8019 | TCP | **Unknown** | Needs identification | +| 9389 | TCP | AD Web Services | AD management | + +## DNS Configuration +- DNS Forwarders: 10.0.0.1 (ISP router) +- DNS Zones: kittle.lan, _msdcs.kittle.lan +- No reverse lookup zone for 10.0.0.x + +## Group Policy Objects + +| GPO Name | Modified | Notes | +|----------|----------|-------| +| Default Domain Policy | 12/23/2025 | | +| Default Domain Controllers Policy | 2/9/2026 | | +| HomeFolder | 2/9/2026 | Maps home folders | +| Intranet Zone - File Server | 3/20/2026 | Adds \\\\SERVER + \\\\10.0.0.5 to Local Intranet zone for PDF preview on shares | + +## Backup +- **NONE — NO BACKUP EXISTS FOR THIS SERVER** +- This server is the ONLY domain controller +- If this server dies, Active Directory, DNS, file shares, and QuickBooks data are ALL lost + +## CRITICAL ISSUES + +### 1. EVALUATION LICENSE — Time Bomb +Windows Server 2025 Standard is running as an **EVALUATION** install. Evaluation licenses expire after 180 days, after which the server will shut down every hour. A full license must be purchased and applied immediately. + +### 2. QuickBooks on the Domain Controller +QuickBooks Pro 2024 is installed directly on the DC. Business applications increase attack surface and resource contention on the DC. Should be migrated to a dedicated workstation. + +### 3. No Backup +No backup solution is configured. Total data loss if the server fails. + +### 4. DHCP Role Installed But Not Used +Windows DHCP role is installed but all scopes are empty. DHCP is handled by the ISP router at 10.0.0.1. The DHCP role could be uninstalled to reduce confusion, or properly configured to take over from the ISP router (recommended). + +### 5. Unknown Port 8019 +An unidentified service is listening on port 8019. Needs investigation. + +## TODO (Priority Order) +- [ ] **IMMEDIATE: Activate full Windows Server license** — Evaluation will expire +- [ ] **IMMEDIATE: Set up backup** — No backup exists +- [ ] **HIGH: Migrate QuickBooks off the DC** — Install on a workstation instead +- [ ] Create reverse DNS zone for 10.0.0.x +- [ ] Investigate port 8019 +- [ ] Consider moving DHCP from ISP router to server for better control +- [ ] Identify purpose of "Server2 2022_03_31" secondary volume diff --git a/clients/kittle/docs/servers/server_template.md b/clients/kittle/docs/servers/server_template.md new file mode 100644 index 0000000..d35ab32 --- /dev/null +++ b/clients/kittle/docs/servers/server_template.md @@ -0,0 +1,49 @@ +# Server: [SERVER NAME] + +## General Info +- Hostname: +- IP Address: +- OS: +- OS Version: +- Physical / Virtual: +- Host (if virtual): +- Location: +- Last Patched: + +## Hardware (if physical) +- Make/Model: +- CPU: +- RAM: +- Storage: +- Warranty Expiry: + +## Roles and Services + +- [ ] Domain Controller +- [ ] DNS Server +- [ ] DHCP Server +- [ ] File Server +- [ ] Print Server +- [ ] Application Server +- [ ] Database Server +- [ ] Backup Target +- [ ] RDS / Terminal Server +- [ ] Hyper-V Host + +## Shares (if file server) +| Share Name | Path | Permissions Group | Notes | +|---------------|-------------------|---------------------|----------------| +| | | | | + +## Applications Installed +| Application | Version | Purpose | License | +|-------------------|------------|----------------------|---------------| +| | | | | + +## Backup +- Backup Method: +- Backup Schedule: +- Backup Target: +- Last Verified Restore: + +## Notes diff --git a/clients/lens-auto-brokerage/docs/billing-log.md b/clients/lens-auto-brokerage/docs/billing-log.md new file mode 100644 index 0000000..4a3dbd3 --- /dev/null +++ b/clients/lens-auto-brokerage/docs/billing-log.md @@ -0,0 +1,41 @@ +# Len's Auto Brokerage — Work Log / Billing Record + +## Session 1 — 2026-04-13 (Remote — Client Onboarding) + +**Focus:** Initial client setup, server inventory, RDP troubleshooting + +| Time | Task | Details | +|------|------|---------| +| | Client folder created | New client folder at `Clients/lens/` with full documentation template | +| | Overview doc | Company info: Len's Auto Brokerage (LAB), WAN IP 174.77.67.237, LAN 192.168.1.0/24, RMM ScreenConnect | +| | Current server documented | DESKTOP-BMBTQLI at 192.168.1.81 — HPE ProLiant MicroServer Gen10 Plus v2, 16GB RAM, Tower, SN 3M1D1T12PD. Hostname still has DESKTOP- prefix (not yet set up as proper server). Last user Localadmin, last boot 2026-03-13. | +| | Old server documented | lab-server at 192.168.1.241 — Windows Server 2008 SP2 (6.0.6002), EOL since Jan 2020, no security updates for 6+ years. To be decommissioned. | +| | Migration plan scoped | Move Deskman + QuickBooks data from lab-server → DESKTOP-BMBTQLI | +| | RDP enabled on lab-server | Enabled 2026-04-13 to attempt remote migration work | +| | RDP troubleshooting | RDP to lab-server fails with 0x800706be — CredSSP/TLS incompatibility between modern client and Server 2008. May need legacy security-layer settings or local-console work. | +| | Issues log | Initial issues captured in `lens/issues/log.md` | + +### Billing Summary — Session 1 +| Category | Items | +|----------|-------| +| Client onboarding / documentation | Folder + overview + initial server inventory | +| Server 2008 RDP troubleshooting | CredSSP incompatibility diagnosed, still unresolved | +| Migration scoping | Deskman + QuickBooks move planned | + +--- + +## Outstanding Work — Prioritized + +### Critical +- lab-server is Server 2008 SP2 — EOL for 6+ years, MUST be retired ASAP (security + compliance risk) + +### High +- Resolve RDP to lab-server (CredSSP 0x800706be) or plan onsite console work +- Migrate Deskman from lab-server to DESKTOP-BMBTQLI +- Migrate QuickBooks data from lab-server to DESKTOP-BMBTQLI +- Rename DESKTOP-BMBTQLI to proper server hostname once it's the production server + +### Medium +- Confirm primary contact info (currently TBD) +- Document all workstations + users +- Evaluate backup strategy (none documented yet) diff --git a/clients/lens-auto-brokerage/docs/issues/log.md b/clients/lens-auto-brokerage/docs/issues/log.md new file mode 100644 index 0000000..134dba7 --- /dev/null +++ b/clients/lens-auto-brokerage/docs/issues/log.md @@ -0,0 +1,6 @@ +# Issue Log — Len's Auto Brokerage + +## 2026-04-13 — RDP Enable +- Enabled Remote Desktop via registry on Server 2008 SP2 machine +- `fDenyTSConnections` set to 0 +- Firewall rule may still need opening: `netsh advfirewall firewall set rule group="remote desktop" new enable=yes` diff --git a/clients/lens-auto-brokerage/docs/overview.md b/clients/lens-auto-brokerage/docs/overview.md new file mode 100644 index 0000000..7cc70be --- /dev/null +++ b/clients/lens-auto-brokerage/docs/overview.md @@ -0,0 +1,26 @@ +# Len's Auto Brokerage (LAB) + +## Company Info +- **Short Name:** Len's / LAB +- **Type:** Auto brokerage +- **Contact:** TBD +- **WAN IP:** 174.77.67.237 + +## Environment Summary +- Network: 192.168.1.0/24 +- RMM: ScreenConnect + +## Servers +| Hostname | IP | Hardware | OS | Notes | +|----------|-----|---------|-----|-------| +| DESKTOP-BMBTQLI | 192.168.1.81 | HPE ProLiant MicroServer Gen10 Plus v2 (SN: 3M1D1T12PD) | TBD | **Current server** — 16GB RAM, Tower. Last user: Localadmin. Last boot: 2026-03-13 | +| lab-server | 192.168.1.241 | TBD | Windows Server 2008 SP2 (6.0.6002) | **OLD SERVER — EOL.** RDP enabled 2026-04-13. To be decommissioned. | + +## Migration Plan +- Move **Deskman** from lab-server to DESKTOP-BMBTQLI +- Move **QuickBooks data** from lab-server to DESKTOP-BMBTQLI + +## Issues +- lab-server running Server 2008 SP2 — end-of-life since Jan 2020, no security updates for 6+ years +- RDP to lab-server fails with 0x800706be (CredSSP/TLS incompatibility) — may need legacy security layer settings +- DESKTOP-BMBTQLI hostname suggests it's not set up as a proper server (still has DESKTOP- prefix) diff --git a/projects/msp-tools/msp-audit-scripts/README.md b/projects/msp-tools/msp-audit-scripts/README.md new file mode 100644 index 0000000..7f81ce2 --- /dev/null +++ b/projects/msp-tools/msp-audit-scripts/README.md @@ -0,0 +1,46 @@ +# MSP Audit Scripts + +Universal Windows audit scripts for MSP use. Run via ScreenConnect Toolbox as SYSTEM. + +## Scripts + +| Script | Target | Output | +|--------|--------|--------| +| `server_audit.ps1` | Windows Server 2012-2025 | `C:\Temp\HOSTNAME_server_audit_YYYY-MM-DD.json` | +| `workstation_audit.ps1` | Windows 10/11 | `C:\Temp\HOSTNAME_workstation_audit_YYYY-MM-DD.json` | + +## ScreenConnect Toolbox Commands + +### Server Audit +```powershell +#!ps +#maxlength=500000 +#timeout=600000 +Set-ExecutionPolicy Bypass -Scope Process -Force +New-Item -Path C:\Temp -ItemType Directory -Force | Out-Null +$u = "https://raw.githubusercontent.com/Howweird/msp-audit-scripts/master/server_audit.ps1" +Invoke-WebRequest -Uri $u -OutFile "C:\Temp\server_audit.ps1" -UseBasicParsing +. C:\Temp\server_audit.ps1 +``` + +### Workstation Audit +```powershell +#!ps +#maxlength=500000 +#timeout=600000 +Set-ExecutionPolicy Bypass -Scope Process -Force +New-Item -Path C:\Temp -ItemType Directory -Force | Out-Null +$u = "https://raw.githubusercontent.com/Howweird/msp-audit-scripts/master/workstation_audit.ps1" +Invoke-WebRequest -Uri $u -OutFile "C:\Temp\workstation_audit.ps1" -UseBasicParsing +. C:\Temp\workstation_audit.ps1 +``` + +## Requirements + +- Must run as SYSTEM or local Administrator +- Output directory: `C:\Temp` (created automatically) +- No dependencies beyond built-in Windows PowerShell modules + +## Output + +- `.json` — Structured data with hostname in filename for multi-machine audits diff --git a/projects/msp-tools/msp-audit-scripts/server_audit.ps1 b/projects/msp-tools/msp-audit-scripts/server_audit.ps1 new file mode 100644 index 0000000..de92f60 --- /dev/null +++ b/projects/msp-tools/msp-audit-scripts/server_audit.ps1 @@ -0,0 +1,2271 @@ +# ========================================== +# UNIVERSAL WINDOWS SERVER AUDIT SCRIPT +# Works on Windows Server 2012 -> 2025 +# Outputs: HOSTNAME_server_audit_DATE.json to C:\Temp +# ========================================== + +# Auto-relaunch with ExecutionPolicy Bypass if needed +if ($MyInvocation.MyCommand.Path) { + $currentPolicy = Get-ExecutionPolicy -Scope Process + if ($currentPolicy -eq 'Restricted' -or $currentPolicy -eq 'AllSigned') { + Start-Process powershell.exe -ArgumentList "-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File `"$($MyInvocation.MyCommand.Path)`"" -Wait -NoNewWindow + exit + } +} + +# Verify running as admin +$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +if (-not $isAdmin) { + Write-Host "ERROR: This script must be run as Administrator." -ForegroundColor Red + exit 1 +} + +$Date = Get-Date -Format 'yyyy-MM-dd' +$OutputDir = "C:\Temp" +$Name = $env:COMPUTERNAME +$JsonFile = "$OutputDir\${Name}_server_audit_$Date.json" + +if (!(Test-Path $OutputDir)) { New-Item -ItemType Directory -Path $OutputDir | Out-Null } + +# Structured data collector +$audit = [ordered]@{ + _metadata = [ordered]@{ + ScriptVersion = "2.1" + ScriptType = "Server" + RunDate = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") + RunBy = "$env:USERDOMAIN\$env:USERNAME" + Hostname = $env:COMPUTERNAME + } + _errors = @() +} + +Write-Host "=======================================" +Write-Host " UNIVERSAL SERVER AUDIT v2.0" +Write-Host " $((Get-Date).ToString('yyyy-MM-dd HH:mm:ss'))" +Write-Host "=======================================" +Write-Host "" + +# ===================================================== +# 1. SYSTEM INFO +# ===================================================== +Write-Host "=== 1. SYSTEM INFO ===" -ForegroundColor Cyan +Write-Host " Running: Get-CimInstance Win32_OperatingSystem, Win32_ComputerSystem" +try { + $os = Get-CimInstance Win32_OperatingSystem + $cs = Get-CimInstance Win32_ComputerSystem + $uptime = (Get-Date) - $os.LastBootUpTime + + $sysInfo = [ordered]@{ + Hostname = $env:COMPUTERNAME + OS = $os.Caption + OSVersion = $os.Version + BuildNumber = $os.BuildNumber + Architecture = $os.OSArchitecture + InstallDate = $os.InstallDate.ToString("yyyy-MM-dd") + LastBoot = $os.LastBootUpTime.ToString("yyyy-MM-dd HH:mm:ss") + UptimeDays = [math]::Round($uptime.TotalDays, 1) + Domain = $cs.Domain + DomainJoined = $cs.PartOfDomain + DomainRole = switch ($cs.DomainRole) { + 0 {"Standalone Workstation"} 1 {"Member Workstation"} 2 {"Standalone Server"} + 3 {"Member Server"} 4 {"Backup DC"} 5 {"Primary DC"} default {"Unknown ($($cs.DomainRole))"} + } + TotalRAM_GB = [math]::Round($cs.TotalPhysicalMemory / 1GB, 1) + } + + $sysInfo.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + $audit.SystemInfo = $sysInfo + Write-Host " [OK] System info collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "SystemInfo"; Error = $_.Exception.Message } +} + +# ===================================================== +# 2. HARDWARE +# ===================================================== +Write-Host "" +Write-Host "=== 2. HARDWARE ===" -ForegroundColor Cyan +Write-Host " Running: Get-CimInstance Win32_ComputerSystem, Win32_Processor, Win32_BIOS, Win32_SystemEnclosure" +try { + $hw = Get-CimInstance Win32_ComputerSystem + $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 + $bios = Get-CimInstance Win32_BIOS + $encl = Get-CimInstance Win32_SystemEnclosure | Select-Object -First 1 + + $hardware = [ordered]@{ + Manufacturer = $hw.Manufacturer + Model = $hw.Model + SerialNumber = if ($bios.SerialNumber) { $bios.SerialNumber } else { $encl.SerialNumber } + CPU = $cpu.Name + CPUCores = $cpu.NumberOfCores + CPULogical = $cpu.NumberOfLogicalProcessors + RAM_GB = [math]::Round($hw.TotalPhysicalMemory / 1GB, 1) + BIOSVersion = $bios.SMBIOSBIOSVersion + BIOSDate = if ($bios.ReleaseDate) { $bios.ReleaseDate.ToString("yyyy-MM-dd") } else { "N/A" } + } + + $hardware.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + $audit.Hardware = $hardware + Write-Host " [OK] Hardware info collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Hardware"; Error = $_.Exception.Message } +} + +# ===================================================== +# 3. STORAGE +# ===================================================== +Write-Host "" +Write-Host "=== 3. STORAGE ===" -ForegroundColor Cyan + +Write-Host " Running: Get-Volume" +try { + $volumes = Get-Volume | Where-Object { $_.DriveLetter } | Select-Object DriveLetter, + FileSystemLabel, FileSystem, + @{N='SizeGB';E={[math]::Round($_.Size/1GB,1)}}, + @{N='FreeGB';E={[math]::Round($_.SizeRemaining/1GB,1)}}, + @{N='UsedPct';E={if($_.Size -gt 0){[math]::Round(($_.Size - $_.SizeRemaining)/$_.Size * 100,0)}else{0}}}, + HealthStatus + $volumes | Format-Table -AutoSize + $audit.Volumes = @($volumes | ForEach-Object { + [ordered]@{ + Drive = "$($_.DriveLetter):" + Label = $_.FileSystemLabel + FileSystem = $_.FileSystem + SizeGB = $_.SizeGB + FreeGB = $_.FreeGB + UsedPct = $_.UsedPct + Health = "$($_.HealthStatus)" + } + }) + Write-Host " [OK] Volumes collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Volumes"; Error = $_.Exception.Message } +} + +Write-Host " Running: Get-PhysicalDisk" +try { + $disks = Get-PhysicalDisk | Select-Object DeviceId, FriendlyName, MediaType, + @{N='SizeGB';E={[math]::Round($_.Size/1GB,1)}}, HealthStatus, OperationalStatus + $disks | Format-Table -AutoSize + $audit.PhysicalDisks = @($disks | ForEach-Object { + [ordered]@{ + DeviceId = "$($_.DeviceId)" + Name = $_.FriendlyName + MediaType = "$($_.MediaType)" + SizeGB = $_.SizeGB + Health = "$($_.HealthStatus)" + Status = "$($_.OperationalStatus)" + } + }) + Write-Host " [OK] Physical disks collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "PhysicalDisks"; Error = $_.Exception.Message } +} + +# ===================================================== +# 4. NETWORK CONFIG +# ===================================================== +Write-Host "" +Write-Host "=== 4. NETWORK CONFIG ===" -ForegroundColor Cyan + +Write-Host " Running: Get-NetAdapter" +try { + $adapters = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } | + Select-Object Name, InterfaceDescription, MacAddress, Status, LinkSpeed + $adapters | Format-Table -AutoSize + $audit.NetworkAdapters = @($adapters | ForEach-Object { + [ordered]@{ + Name = $_.Name + Description = $_.InterfaceDescription + MAC = $_.MacAddress + Status = "$($_.Status)" + LinkSpeed = $_.LinkSpeed + } + }) + Write-Host " [OK] Adapters collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "NetworkAdapters"; Error = $_.Exception.Message } +} + +Write-Host " Running: Get-NetIPConfiguration" +try { + $ipConfigs = Get-NetIPConfiguration | Where-Object { $_.IPv4Address } + $audit.IPConfig = @($ipConfigs | ForEach-Object { + [ordered]@{ + Interface = $_.InterfaceAlias + IPv4 = "$($_.IPv4Address.IPAddress)" + Gateway = "$($_.IPv4DefaultGateway.NextHop)" + DNS = @($_.DNSServer | Where-Object { $_.AddressFamily -eq 2 } | ForEach-Object { $_.ServerAddresses }) | Select-Object -Unique + } + }) + $ipConfigs | Format-List InterfaceAlias, IPv4Address, IPv4DefaultGateway, DnsServer + Write-Host " [OK] IP config collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "IPConfig"; Error = $_.Exception.Message } +} + +Write-Host " Running: Get-DnsClientServerAddress" +try { + Get-DnsClientServerAddress -AddressFamily IPv4 | Where-Object { $_.ServerAddresses.Count -gt 0 } | + Format-Table InterfaceAlias, ServerAddresses -AutoSize + Write-Host " [OK] DNS client config collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "DnsClient"; Error = $_.Exception.Message } +} + +# ===================================================== +# 5. ROUTING TABLE +# ===================================================== +Write-Host "" +Write-Host "=== 5. ROUTING TABLE ===" -ForegroundColor Cyan +Write-Host " Running: Get-NetRoute" +try { + Get-NetRoute -AddressFamily IPv4 | Where-Object { $_.DestinationPrefix -ne '255.255.255.255/32' } | + Sort-Object DestinationPrefix | + Format-Table DestinationPrefix, NextHop, InterfaceAlias, RouteMetric -AutoSize + Write-Host " [OK] Routes collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Routes"; Error = $_.Exception.Message } +} + +# ===================================================== +# 6. ARP TABLE +# ===================================================== +Write-Host "" +Write-Host "=== 6. ARP TABLE ===" -ForegroundColor Cyan +Write-Host " Running: Get-NetNeighbor" +try { + $arp = Get-NetNeighbor -AddressFamily IPv4 | Where-Object { $_.State -ne 'Unreachable' -and $_.State -ne 'Permanent' } | + Select-Object IPAddress, LinkLayerAddress, State, InterfaceAlias + $arp | Format-Table -AutoSize + $audit.ARPTable = @($arp | ForEach-Object { + [ordered]@{ IP = $_.IPAddress; MAC = $_.LinkLayerAddress; State = "$($_.State)"; Interface = $_.InterfaceAlias } + }) + Write-Host " [OK] ARP table collected - $($arp.Count) entries" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "ARP"; Error = $_.Exception.Message } +} + +# ===================================================== +# 7. LISTENING PORTS +# ===================================================== +Write-Host "" +Write-Host "=== 7. LISTENING PORTS ===" -ForegroundColor Cyan +Write-Host " Running: Get-NetTCPConnection -State Listen" +try { + $listeners = Get-NetTCPConnection -State Listen | ForEach-Object { + $proc = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue + [PSCustomObject]@{ + LocalAddress = $_.LocalAddress + LocalPort = $_.LocalPort + PID = $_.OwningProcess + ProcessName = $proc.ProcessName + } + } | Sort-Object LocalPort + $listeners | Format-Table -AutoSize + $audit.ListeningPorts = @($listeners | ForEach-Object { + [ordered]@{ Address = $_.LocalAddress; Port = $_.LocalPort; PID = $_.PID; Process = $_.ProcessName } + }) + Write-Host " [OK] Listening ports collected - $($listeners.Count) ports" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "ListeningPorts"; Error = $_.Exception.Message } +} + +# ===================================================== +# 8. WINDOWS FIREWALL RULES +# ===================================================== +Write-Host "" +Write-Host "=== 8. WINDOWS FIREWALL RULES (Enabled) ===" -ForegroundColor Cyan +Write-Host " Running: Get-NetFirewallRule | Get-NetFirewallPortFilter" +try { + $fwRules = Get-NetFirewallRule | Where-Object { $_.Enabled -eq 'True' } | ForEach-Object { + $portFilter = $_ | Get-NetFirewallPortFilter -ErrorAction SilentlyContinue + [PSCustomObject]@{ + DisplayName = $_.DisplayName + Direction = "$($_.Direction)" + Action = "$($_.Action)" + Protocol = $portFilter.Protocol + LocalPort = $portFilter.LocalPort + RemotePort = $portFilter.RemotePort + } + } + $fwRules | Format-Table DisplayName, Direction, Action, Protocol, LocalPort -AutoSize + $audit.FirewallRules = @($fwRules | ForEach-Object { + [ordered]@{ + Name = $_.DisplayName; Direction = $_.Direction; Action = $_.Action + Protocol = $_.Protocol; LocalPort = $_.LocalPort; RemotePort = $_.RemotePort + } + }) + Write-Host " [OK] Firewall rules collected - $($fwRules.Count) enabled rules" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "FirewallRules"; Error = $_.Exception.Message } +} + +# ===================================================== +# 9. AD DOMAIN INFO +# ===================================================== +Write-Host "" +Write-Host "=== 9. AD DOMAIN INFO ===" -ForegroundColor Cyan +Write-Host " Running: Get-ADDomain, Get-ADForest" +try { + Import-Module ActiveDirectory -ErrorAction Stop + $script:domain = Get-ADDomain + $script:forest = Get-ADForest + $domain = $script:domain + $forest = $script:forest + + $adInfo = [ordered]@{ + DomainName = $domain.DNSRoot + NetBIOSName = $domain.NetBIOSName + DomainMode = "$($domain.DomainMode)" + ForestName = $forest.Name + ForestMode = "$($forest.ForestMode)" + PDCEmulator = $domain.PDCEmulator + SchemaMaster = $forest.SchemaMaster + Sites = @($forest.Sites) + GlobalCatalogs = @($forest.GlobalCatalogs) + } + + $adInfo.GetEnumerator() | ForEach-Object { + $val = if ($_.Value -is [array]) { $_.Value -join ", " } else { $_.Value } + Write-Host " $($_.Key): $val" + } + $audit.ADDomain = $adInfo + + # Trusts + Write-Host " Running: Get-ADTrust" + try { + $trusts = Get-ADTrust -Filter * + if ($trusts) { + $trusts | Format-Table Name, Direction, TrustType, IntraForest -AutoSize + $audit.ADTrusts = @($trusts | ForEach-Object { + [ordered]@{ Name = $_.Name; Direction = "$($_.Direction)"; Type = "$($_.TrustType)"; IntraForest = $_.IntraForest } + }) + } else { + Write-Host " No trusts configured" + $audit.ADTrusts = @() + } + } + catch { + Write-Host " [FAIL] Trusts: $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "ADTrusts"; Error = $_.Exception.Message } + } + + Write-Host " [OK] AD domain info collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + Write-Host " (AD module not available - this may not be a domain controller)" -ForegroundColor Yellow + $audit._errors += @{ Section = "ADDomain"; Error = $_.Exception.Message } +} + +# ===================================================== +# 10. DOMAIN CONTROLLERS +# ===================================================== +Write-Host "" +Write-Host "=== 10. DOMAIN CONTROLLERS ===" -ForegroundColor Cyan +Write-Host " Running: Get-ADDomainController -Filter *" +try { + $dcs = Get-ADDomainController -Filter * + $dcs | Format-Table HostName, IPv4Address, Site, IsGlobalCatalog, OperatingSystem -AutoSize + $audit.DomainControllers = @($dcs | ForEach-Object { + [ordered]@{ + Hostname = $_.HostName; IP = $_.IPv4Address; Site = $_.Site + IsGC = $_.IsGlobalCatalog; OS = $_.OperatingSystem + } + }) + Write-Host " [OK] Domain controllers collected - $($dcs.Count) DCs" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "DomainControllers"; Error = $_.Exception.Message } +} + +# ===================================================== +# 11. AD OU STRUCTURE +# ===================================================== +Write-Host "" +Write-Host "=== 11. AD OU STRUCTURE ===" -ForegroundColor Cyan +Write-Host " Running: Get-ADOrganizationalUnit -Filter *" +try { + $ous = Get-ADOrganizationalUnit -Filter * -Properties Description, ProtectedFromAccidentalDeletion | + Sort-Object DistinguishedName + $ous | Format-Table Name, DistinguishedName, Description, ProtectedFromAccidentalDeletion -AutoSize -Wrap + $audit.ADOUs = @($ous | ForEach-Object { + [ordered]@{ + Name = $_.Name; DN = $_.DistinguishedName; Description = $_.Description + Protected = $_.ProtectedFromAccidentalDeletion + } + }) + Write-Host " [OK] OU structure collected - $($ous.Count) OUs" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "ADOUs"; Error = $_.Exception.Message } +} + +# ===================================================== +# 12. AD USERS +# ===================================================== +Write-Host "" +Write-Host "=== 12. AD USERS ===" -ForegroundColor Cyan +Write-Host " Running: Get-ADUser -Filter * -Properties ..." +try { + $users = Get-ADUser -Filter * -Properties LastLogonDate, Enabled, Description, + PasswordLastSet, PasswordNeverExpires, DistinguishedName, EmailAddress, WhenCreated | + Select-Object Name, SamAccountName, Enabled, LastLogonDate, PasswordLastSet, + PasswordNeverExpires, EmailAddress, Description, DistinguishedName, WhenCreated + $users | Format-Table Name, SamAccountName, Enabled, LastLogonDate, EmailAddress -AutoSize + $audit.ADUsers = @($users | ForEach-Object { + [ordered]@{ + Name = $_.Name; SAM = $_.SamAccountName; Enabled = $_.Enabled + LastLogon = if ($_.LastLogonDate) { $_.LastLogonDate.ToString("yyyy-MM-dd") } else { $null } + PasswordLastSet = if ($_.PasswordLastSet) { $_.PasswordLastSet.ToString("yyyy-MM-dd") } else { $null } + PasswordNeverExpires = $_.PasswordNeverExpires + Email = $_.EmailAddress; Description = $_.Description + OU = $_.DistinguishedName; Created = if ($_.WhenCreated) { $_.WhenCreated.ToString("yyyy-MM-dd") } else { $null } + } + }) + Write-Host " [OK] AD users collected - $($users.Count) users" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "ADUsers"; Error = $_.Exception.Message } +} + +# ===================================================== +# 13. AD COMPUTERS +# ===================================================== +Write-Host "" +Write-Host "=== 13. AD COMPUTERS ===" -ForegroundColor Cyan +Write-Host " Running: Get-ADComputer -Filter * -Properties ..." +try { + $computers = Get-ADComputer -Filter * -Properties LastLogonDate, OperatingSystem, + OperatingSystemVersion, IPv4Address, DistinguishedName, Description, WhenCreated | + Select-Object Name, OperatingSystem, OperatingSystemVersion, IPv4Address, + LastLogonDate, Description, DistinguishedName, WhenCreated + $computers | Format-Table Name, OperatingSystem, IPv4Address, LastLogonDate -AutoSize + $audit.ADComputers = @($computers | ForEach-Object { + [ordered]@{ + Name = $_.Name; OS = $_.OperatingSystem; OSVersion = $_.OperatingSystemVersion + IP = $_.IPv4Address + LastLogon = if ($_.LastLogonDate) { $_.LastLogonDate.ToString("yyyy-MM-dd") } else { $null } + Description = $_.Description; OU = $_.DistinguishedName + Created = if ($_.WhenCreated) { $_.WhenCreated.ToString("yyyy-MM-dd") } else { $null } + } + }) + Write-Host " [OK] AD computers collected - $($computers.Count) computers" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "ADComputers"; Error = $_.Exception.Message } +} + +# ===================================================== +# 14. AD GROUPS WITH MEMBERS +# ===================================================== +Write-Host "" +Write-Host "=== 14. AD GROUPS WITH MEMBERS ===" -ForegroundColor Cyan +Write-Host " Running: Get-ADGroup -Filter * + Get-ADGroupMember" +try { + $groups = Get-ADGroup -Filter * -Properties Description, Members | + Where-Object { $_.Members.Count -gt 0 -and $_.GroupCategory -eq 'Security' } | + Sort-Object Name + + $audit.ADGroups = @() + foreach ($g in $groups) { + Write-Host " Group: $($g.Name) [$($g.GroupScope)]" -ForegroundColor Yellow + try { + $members = Get-ADGroupMember $g.DistinguishedName -ErrorAction Stop | + Select-Object Name, SamAccountName, objectClass + $members | Format-Table -AutoSize + + $audit.ADGroups += [ordered]@{ + Name = $g.Name; Scope = "$($g.GroupScope)"; Category = "$($g.GroupCategory)" + Description = $g.Description + Members = @($members | ForEach-Object { [ordered]@{ Name = $_.Name; SAM = $_.SamAccountName; Type = $_.objectClass } }) + } + } + catch { + Write-Host " [FAIL] Could not enumerate members: $($_.Exception.Message)" -ForegroundColor Red + $audit.ADGroups += [ordered]@{ + Name = $g.Name; Scope = "$($g.GroupScope)"; Error = $_.Exception.Message + } + } + } + Write-Host " [OK] AD groups collected - $($groups.Count) groups with members" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "ADGroups"; Error = $_.Exception.Message } +} + +# ===================================================== +# 15. PRIVILEGED GROUPS +# ===================================================== +Write-Host "" +Write-Host "=== 15. PRIVILEGED GROUPS ===" -ForegroundColor Cyan +Write-Host " Running: Get-ADGroupMember for privileged groups" + +$privGroups = @("Domain Admins", "Enterprise Admins", "Schema Admins", "Administrators", + "Account Operators", "Backup Operators", "Server Operators") +$audit.PrivilegedGroups = @() + +foreach ($pg in $privGroups) { + try { + $members = Get-ADGroupMember $pg -ErrorAction Stop | Select-Object Name, SamAccountName, objectClass + Write-Host " $pg`:" -ForegroundColor Yellow + if ($members) { + $members | ForEach-Object { Write-Host " $($_.Name) ($($_.SamAccountName)) [$($_.objectClass)]" } + $audit.PrivilegedGroups += [ordered]@{ + Group = $pg + Members = @($members | ForEach-Object { [ordered]@{ Name = $_.Name; SAM = $_.SamAccountName; Type = $_.objectClass } }) + } + } else { + Write-Host " (empty)" + $audit.PrivilegedGroups += [ordered]@{ Group = $pg; Members = @() } + } + } + catch { + Write-Host " $pg`: [FAIL] $($_.Exception.Message)" -ForegroundColor Red + } +} +Write-Host " [OK] Privileged groups checked" -ForegroundColor Green + +# ===================================================== +# 16. GROUP POLICY +# ===================================================== +Write-Host "" +Write-Host "=== 16. GROUP POLICY ===" -ForegroundColor Cyan +Write-Host " Running: Get-GPO -All + Get-GPOReport (for links)" +try { + Import-Module GroupPolicy -ErrorAction Stop + $gpos = Get-GPO -All + + $audit.GPOs = @() + foreach ($gpo in $gpos) { + Write-Host " GPO: $($gpo.DisplayName)" -ForegroundColor Yellow + Write-Host " Status: $($gpo.GpoStatus) Modified: $($gpo.ModificationTime)" + + $links = @() + try { + [xml]$report = Get-GPOReport -Guid $gpo.Id -ReportType Xml -ErrorAction Stop + if ($report.GPO.LinksTo) { + $report.GPO.LinksTo | ForEach-Object { + Write-Host " Link: $($_.SOMPath) [Enabled: $($_.Enabled)]" + $links += [ordered]@{ Path = $_.SOMPath; Enabled = $_.Enabled } + } + } else { + Write-Host " Link: (not linked)" + } + } + catch { + Write-Host " [FAIL] Could not get links: $($_.Exception.Message)" -ForegroundColor Red + } + + $audit.GPOs += [ordered]@{ + Name = $gpo.DisplayName; Status = "$($gpo.GpoStatus)" + Created = $gpo.CreationTime.ToString("yyyy-MM-dd") + Modified = $gpo.ModificationTime.ToString("yyyy-MM-dd") + Links = $links + } + } + Write-Host " [OK] GPOs collected - $($gpos.Count) GPOs" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "GPOs"; Error = $_.Exception.Message } +} + +# ===================================================== +# 17. LOCAL USERS +# ===================================================== +Write-Host "" +Write-Host "=== 17. LOCAL USERS ===" -ForegroundColor Cyan +Write-Host " Running: Get-LocalUser" +try { + $localUsers = Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordRequired, PasswordLastSet + $localUsers | Format-Table -AutoSize + $audit.LocalUsers = @($localUsers | ForEach-Object { + [ordered]@{ + Name = $_.Name; Enabled = $_.Enabled + LastLogon = if ($_.LastLogon) { $_.LastLogon.ToString("yyyy-MM-dd") } else { $null } + PasswordRequired = $_.PasswordRequired + PasswordLastSet = if ($_.PasswordLastSet) { $_.PasswordLastSet.ToString("yyyy-MM-dd") } else { $null } + } + }) + Write-Host " [OK] Local users collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message) - trying net user fallback" -ForegroundColor Red + try { net user } catch {} + $audit._errors += @{ Section = "LocalUsers"; Error = $_.Exception.Message } +} + +# ===================================================== +# 18. LOCAL ADMINISTRATORS +# ===================================================== +Write-Host "" +Write-Host "=== 18. LOCAL ADMINISTRATORS ===" -ForegroundColor Cyan +Write-Host " Running: Get-LocalGroupMember Administrators" +try { + $localAdmins = Get-LocalGroupMember Administrators | Select-Object Name, PrincipalSource, ObjectClass + $localAdmins | Format-Table -AutoSize + $audit.LocalAdmins = @($localAdmins | ForEach-Object { + [ordered]@{ Name = $_.Name; Source = "$($_.PrincipalSource)"; Type = "$($_.ObjectClass)" } + }) + Write-Host " [OK] Local admins collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message) - trying net localgroup fallback" -ForegroundColor Red + try { net localgroup Administrators } catch {} + $audit._errors += @{ Section = "LocalAdmins"; Error = $_.Exception.Message } +} + +# ===================================================== +# 19. SHARES WITH PERMISSIONS +# ===================================================== +Write-Host "" +Write-Host "=== 19. SHARES WITH PERMISSIONS ===" -ForegroundColor Cyan +Write-Host " Running: Get-SmbShare + Get-SmbShareAccess + Get-Acl" +try { + $shares = Get-SmbShare | Where-Object { $_.Name -notlike '*$' } + $audit.Shares = @() + + foreach ($s in $shares) { + Write-Host " Share: \\$env:COMPUTERNAME\$($s.Name)" -ForegroundColor Yellow + Write-Host " Path: $($s.Path)" + Write-Host " Description: $($s.Description)" + + $shareData = [ordered]@{ + Name = $s.Name; Path = $s.Path; Description = $s.Description + SMBPermissions = @(); NTFSPermissions = @() + } + + # SMB permissions + Write-Host " SMB Permissions:" -ForegroundColor DarkGray + try { + $smbPerms = Get-SmbShareAccess -Name $s.Name + $smbPerms | ForEach-Object { + Write-Host " $($_.AccountName): $($_.AccessRight) ($($_.AccessControlType))" + $shareData.SMBPermissions += [ordered]@{ + Account = $_.AccountName; Right = "$($_.AccessRight)"; Type = "$($_.AccessControlType)" + } + } + } + catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + } + + # NTFS permissions + Write-Host " NTFS Permissions:" -ForegroundColor DarkGray + try { + if (Test-Path $s.Path) { + $acl = Get-Acl $s.Path + $acl.Access | ForEach-Object { + Write-Host " $($_.IdentityReference): $($_.FileSystemRights) ($($_.AccessControlType))" + $shareData.NTFSPermissions += [ordered]@{ + Identity = "$($_.IdentityReference)"; Rights = "$($_.FileSystemRights)" + Type = "$($_.AccessControlType)"; Inherited = $_.IsInherited + } + } + } + } + catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + } + + $audit.Shares += $shareData + } + Write-Host " [OK] Shares collected - $($shares.Count) shares" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Shares"; Error = $_.Exception.Message } +} + +# ===================================================== +# 20. INSTALLED ROLES/FEATURES +# ===================================================== +Write-Host "" +Write-Host "=== 20. INSTALLED ROLES/FEATURES ===" -ForegroundColor Cyan +Write-Host " Running: Get-WindowsFeature | Where Installed" +try { + $roles = Get-WindowsFeature | Where-Object { $_.Installed } | Select-Object Name, DisplayName + $roles | Format-Table -AutoSize + $audit.InstalledRoles = @($roles | ForEach-Object { + [ordered]@{ Name = $_.Name; DisplayName = $_.DisplayName } + }) + Write-Host " [OK] Roles collected - $($roles.Count) installed" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "InstalledRoles"; Error = $_.Exception.Message } +} + +# ===================================================== +# 21. DHCP +# ===================================================== +Write-Host "" +Write-Host "=== 21. DHCP ===" -ForegroundColor Cyan +Write-Host " Running: Get-DhcpServerv4Scope + options + reservations + leases" +try { + Import-Module DhcpServer -ErrorAction Stop + $scopes = Get-DhcpServerv4Scope + $audit.DHCP = [ordered]@{ Scopes = @() } + + # Server-level options + Write-Host " Server-level DHCP options:" -ForegroundColor Yellow + try { + $serverOpts = Get-DhcpServerv4OptionValue -ErrorAction Stop + $serverOpts | Format-Table OptionId, Name, Value -AutoSize + $audit.DHCP.ServerOptions = @($serverOpts | ForEach-Object { + [ordered]@{ OptionId = $_.OptionId; Name = $_.Name; Value = @($_.Value) } + }) + } + catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + } + + # Failover + try { + $failover = Get-DhcpServerv4Failover -ErrorAction Stop + if ($failover) { + $failover | Format-Table Name, PartnerServer, Mode, State -AutoSize + $audit.DHCP.Failover = @($failover | ForEach-Object { + [ordered]@{ Name = $_.Name; Partner = $_.PartnerServer; Mode = "$($_.Mode)"; State = "$($_.State)" } + }) + } + } + catch { Write-Host " No DHCP failover configured" -ForegroundColor DarkGray } + + foreach ($scope in $scopes) { + $scopeData = [ordered]@{ + ScopeId = "$($scope.ScopeId)"; Name = $scope.Name + SubnetMask = "$($scope.SubnetMask)"; StartRange = "$($scope.StartRange)" + EndRange = "$($scope.EndRange)"; State = "$($scope.State)" + LeaseDuration = "$($scope.LeaseDuration)" + Options = @(); Exclusions = @(); Reservations = @(); Leases = @() + } + + Write-Host " Scope: $($scope.ScopeId) ($($scope.Name))" -ForegroundColor Yellow + Write-Host " Range: $($scope.StartRange) - $($scope.EndRange), Mask: $($scope.SubnetMask), State: $($scope.State)" + + # Scope options + try { + $scopeOpts = Get-DhcpServerv4OptionValue -ScopeId $scope.ScopeId -ErrorAction Stop + Write-Host " Options:" + $scopeOpts | ForEach-Object { Write-Host " $($_.OptionId) $($_.Name): $($_.Value -join ', ')" } + $scopeData.Options = @($scopeOpts | ForEach-Object { + [ordered]@{ OptionId = $_.OptionId; Name = $_.Name; Value = @($_.Value) } + }) + } + catch { Write-Host " Options: [FAIL] $($_.Exception.Message)" -ForegroundColor Red } + + # Exclusions + try { + $exclusions = Get-DhcpServerv4ExclusionRange -ScopeId $scope.ScopeId -ErrorAction Stop + if ($exclusions) { + Write-Host " Exclusions:" + $exclusions | ForEach-Object { Write-Host " $($_.StartRange) - $($_.EndRange)" } + $scopeData.Exclusions = @($exclusions | ForEach-Object { + [ordered]@{ Start = "$($_.StartRange)"; End = "$($_.EndRange)" } + }) + } + } + catch {} + + # Reservations + try { + $reservations = Get-DhcpServerv4Reservation -ScopeId $scope.ScopeId -ErrorAction Stop + if ($reservations) { + Write-Host " Reservations:" + $reservations | Format-Table IPAddress, Name, ClientId, Description -AutoSize + $scopeData.Reservations = @($reservations | ForEach-Object { + [ordered]@{ IP = "$($_.IPAddress)"; Name = $_.Name; MAC = $_.ClientId; Description = $_.Description } + }) + } + } + catch { Write-Host " Reservations: [FAIL] $($_.Exception.Message)" -ForegroundColor Red } + + # Leases + try { + $leases = Get-DhcpServerv4Lease -ScopeId $scope.ScopeId -ErrorAction Stop + if ($leases) { + Write-Host " Active Leases:" + $leases | Format-Table IPAddress, HostName, ClientId, AddressState -AutoSize + $scopeData.Leases = @($leases | ForEach-Object { + [ordered]@{ IP = "$($_.IPAddress)"; Hostname = $_.HostName; MAC = $_.ClientId; State = "$($_.AddressState)" } + }) + } + } + catch { Write-Host " Leases: [FAIL] $($_.Exception.Message)" -ForegroundColor Red } + + $audit.DHCP.Scopes += $scopeData + } + Write-Host " [OK] DHCP collected - $($scopes.Count) scopes" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + Write-Host " (DHCP role not installed or not running)" -ForegroundColor Yellow + $audit._errors += @{ Section = "DHCP"; Error = $_.Exception.Message } +} + +# ===================================================== +# 22. DNS SERVER +# ===================================================== +Write-Host "" +Write-Host "=== 22. DNS SERVER ===" -ForegroundColor Cyan +Write-Host " Running: Get-DnsServerZone, Get-DnsServerForwarder, Get-DnsServerResourceRecord" +try { + Import-Module DnsServer -ErrorAction Stop + + # Forwarders + Write-Host " Forwarders:" -ForegroundColor Yellow + $fwd = Get-DnsServerForwarder + $fwd.IPAddress | ForEach-Object { Write-Host " $_" } + $audit.DNS = [ordered]@{ + Forwarders = @($fwd.IPAddress | ForEach-Object { "$_" }) + ConditionalForwarders = @() + Zones = @() + } + + # Conditional forwarders + Write-Host " Conditional Forwarders:" -ForegroundColor Yellow + $condFwd = Get-DnsServerZone | Where-Object { $_.ZoneType -eq 'Forwarder' } + if ($condFwd) { + $condFwd | ForEach-Object { + Write-Host " $($_.ZoneName) -> $($_.MasterServers -join ', ')" + $audit.DNS.ConditionalForwarders += [ordered]@{ Zone = $_.ZoneName; ForwardTo = @($_.MasterServers | ForEach-Object { "$_" }) } + } + } else { + Write-Host " (none)" + } + + # Zones and records + Write-Host " Zones:" -ForegroundColor Yellow + $zones = Get-DnsServerZone | Where-Object { $_.ZoneType -ne 'Forwarder' } + foreach ($z in $zones) { + $zoneData = [ordered]@{ + Name = $z.ZoneName; Type = "$($z.ZoneType)"; IsReverse = $z.IsReverseLookupZone + DynamicUpdate = "$($z.DynamicUpdate)"; Records = @() + } + + Write-Host " $($z.ZoneName) [$($z.ZoneType)] Reverse=$($z.IsReverseLookupZone)" -ForegroundColor DarkGray + + # Get records for primary zones (skip if >500 records to avoid flooding) + if ($z.ZoneType -eq 'Primary' -and -not $z.IsAutoCreated) { + try { + $records = Get-DnsServerResourceRecord -ZoneName $z.ZoneName -ErrorAction Stop + if ($records.Count -le 500) { + $zoneData.Records = @($records | ForEach-Object { + [ordered]@{ + Name = $_.HostName; Type = "$($_.RecordType)"; TTL = "$($_.TimeToLive)" + Data = "$($_.RecordData.IPv4Address)$($_.RecordData.HostNameAlias)$($_.RecordData.NameServer)$($_.RecordData.DescriptiveText)$($_.RecordData.DomainName)" + } + }) + Write-Host " $($records.Count) records" + } else { + Write-Host " $($records.Count) records (too many to list, count only)" -ForegroundColor Yellow + $zoneData.RecordCount = $records.Count + } + } + catch { + Write-Host " [FAIL] Records: $($_.Exception.Message)" -ForegroundColor Red + } + } + + $audit.DNS.Zones += $zoneData + } + Write-Host " [OK] DNS collected - $($zones.Count) zones" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + Write-Host " (DNS Server role not installed)" -ForegroundColor Yellow + $audit._errors += @{ Section = "DNS"; Error = $_.Exception.Message } +} + +# ===================================================== +# 23. HYPER-V VMs +# ===================================================== +Write-Host "" +Write-Host "=== 23. HYPER-V VMs ===" -ForegroundColor Cyan +Write-Host " Running: Get-VM" +try { + $vms = Get-VM -ErrorAction Stop + if ($vms) { + $audit.HyperV = @($vms | ForEach-Object { + $vm = $_ + Write-Host " VM: $($vm.Name) [State: $($vm.State)]" -ForegroundColor Yellow + Write-Host " CPU: $($vm.ProcessorCount), Memory: $([math]::Round($vm.MemoryAssigned/1MB))MB, Gen: $($vm.Generation)" + + $netAdapters = Get-VMNetworkAdapter -VMName $vm.Name -ErrorAction SilentlyContinue + $disks = Get-VMHardDiskDrive -VMName $vm.Name -ErrorAction SilentlyContinue + + [ordered]@{ + Name = $vm.Name; State = "$($vm.State)"; CPUs = $vm.ProcessorCount + MemoryMB = [math]::Round($vm.MemoryAssigned/1MB); Generation = $vm.Generation + Networks = @($netAdapters | ForEach-Object { [ordered]@{ Switch = $_.SwitchName; MAC = $_.MacAddress; IPs = @($_.IPAddresses) } }) + Disks = @($disks | ForEach-Object { $_.Path }) + } + }) + Write-Host " [OK] Hyper-V VMs collected - $($vms.Count) VMs" -ForegroundColor Green + } else { + Write-Host " No VMs found" -ForegroundColor DarkGray + $audit.HyperV = @() + } +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + Write-Host " (Hyper-V role not installed)" -ForegroundColor Yellow + $audit._errors += @{ Section = "HyperV"; Error = $_.Exception.Message } +} + +# ===================================================== +# 24. IIS SITES +# ===================================================== +Write-Host "" +Write-Host "=== 24. IIS SITES ===" -ForegroundColor Cyan +Write-Host " Running: Get-Website" +try { + Import-Module WebAdministration -ErrorAction Stop + $sites = Get-Website + if ($sites) { + $audit.IIS = @($sites | ForEach-Object { + Write-Host " Site: $($_.Name) [State: $($_.State)]" -ForegroundColor Yellow + Write-Host " Path: $($_.PhysicalPath)" + Write-Host " Bindings: $(($_.Bindings.Collection | ForEach-Object { $_.bindingInformation }) -join ', ')" + [ordered]@{ + Name = $_.Name; State = "$($_.State)"; Path = $_.PhysicalPath + AppPool = $_.applicationPool + Bindings = @($_.Bindings.Collection | ForEach-Object { $_.bindingInformation }) + } + }) + Write-Host " [OK] IIS sites collected" -ForegroundColor Green + } else { + Write-Host " No websites configured" -ForegroundColor DarkGray + } +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + Write-Host " (IIS not installed)" -ForegroundColor Yellow + $audit._errors += @{ Section = "IIS"; Error = $_.Exception.Message } +} + +# ===================================================== +# 25. NPS / RADIUS +# ===================================================== +Write-Host "" +Write-Host "=== 25. NPS / RADIUS ===" -ForegroundColor Cyan +Write-Host " Running: Get-NpsRadiusClient, Get-NpsNetworkPolicy" +try { + Import-Module NPS -ErrorAction Stop + $clients = Get-NpsRadiusClient -ErrorAction Stop + $policies = Get-NpsNetworkPolicy -ErrorAction Stop + + $audit.NPS = [ordered]@{ + Clients = @($clients | ForEach-Object { + Write-Host " RADIUS Client: $($_.Name) ($($_.Address))" -ForegroundColor Yellow + [ordered]@{ Name = $_.Name; Address = $_.Address } + }) + Policies = @($policies | ForEach-Object { + Write-Host " Policy: $($_.Name) [Enabled: $($_.Enabled)]" -ForegroundColor Yellow + [ordered]@{ Name = $_.Name; Enabled = $_.Enabled; Order = $_.ProcessingOrder } + }) + } + Write-Host " [OK] NPS collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + Write-Host " (NPS/RADIUS not installed)" -ForegroundColor Yellow + $audit._errors += @{ Section = "NPS"; Error = $_.Exception.Message } +} + +# ===================================================== +# 26. WINDOWS SERVER BACKUP +# ===================================================== +Write-Host "" +Write-Host "=== 26. WINDOWS SERVER BACKUP ===" -ForegroundColor Cyan +Write-Host " Running: Get-WBPolicy, Get-WBSummary" +try { + Add-PSSnapin Windows.ServerBackup -ErrorAction Stop + $policy = Get-WBPolicy -ErrorAction Stop + + $wsbData = [ordered]@{ + Schedule = @(Get-WBSchedule -Policy $policy) + Volumes = @((Get-WBVolume -Policy $policy).MountPoint) + SystemState = (Get-WBSystemState -Policy $policy) + } + + try { + $target = Get-WBBackupTarget -Policy $policy + $wsbData.Target = "$($target.Label) ($($target.TargetPath))" + } + catch { $wsbData.Target = "Unknown" } + + try { + $summary = Get-WBSummary + $wsbData.LastSuccess = if ($summary.LastSuccessfulBackupTime) { $summary.LastSuccessfulBackupTime.ToString("yyyy-MM-dd HH:mm") } else { $null } + $wsbData.LastResult = "$($summary.LastBackupResultHR)" + $wsbData.NextRun = if ($summary.NextBackupTime) { $summary.NextBackupTime.ToString("yyyy-MM-dd HH:mm") } else { $null } + } + catch {} + + $wsbData.GetEnumerator() | ForEach-Object { + $val = if ($_.Value -is [array]) { $_.Value -join ", " } else { $_.Value } + Write-Host " $($_.Key): $val" + } + + $audit.WindowsServerBackup = $wsbData + Write-Host " [OK] WSB info collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + Write-Host " (Windows Server Backup not installed or no policy configured)" -ForegroundColor Yellow + $audit._errors += @{ Section = "WindowsServerBackup"; Error = $_.Exception.Message } +} + +# ===================================================== +# 27. PRINTERS +# ===================================================== +Write-Host "" +Write-Host "=== 27. PRINTERS ===" -ForegroundColor Cyan +Write-Host " Running: Get-Printer, Get-PrinterPort" +try { + $printers = Get-Printer | Select-Object Name, DriverName, PortName, Shared, ShareName, Published + $printers | Format-Table -AutoSize + + $ports = Get-PrinterPort | Where-Object { $_.Name -like 'TCP_*' -or $_.Name -like 'IP_*' } | + Select-Object Name, PrinterHostAddress, PortNumber + if ($ports) { + Write-Host " Printer Ports:" -ForegroundColor DarkGray + $ports | Format-Table -AutoSize + } + + $audit.Printers = @($printers | ForEach-Object { + [ordered]@{ + Name = $_.Name; Driver = $_.DriverName; Port = $_.PortName + Shared = $_.Shared; ShareName = $_.ShareName + } + }) + $audit.PrinterPorts = @($ports | ForEach-Object { + [ordered]@{ Name = $_.Name; IP = $_.PrinterHostAddress; Port = $_.PortNumber } + }) + Write-Host " [OK] Printers collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Printers"; Error = $_.Exception.Message } +} + +# ===================================================== +# 28. CERTIFICATES +# ===================================================== +Write-Host "" +Write-Host "=== 28. CERTIFICATES ===" -ForegroundColor Cyan +Write-Host " Running: Get-ChildItem Cert:\LocalMachine\My" +try { + $certs = Get-ChildItem Cert:\LocalMachine\My | Select-Object Subject, NotAfter, NotBefore, Issuer, Thumbprint + $certs | Format-Table Subject, NotAfter, Issuer -AutoSize + + $expiringSoon = $certs | Where-Object { $_.NotAfter -lt (Get-Date).AddDays(90) } + if ($expiringSoon) { + Write-Host " WARNING: Certificates expiring within 90 days:" -ForegroundColor Yellow + $expiringSoon | ForEach-Object { Write-Host " $($_.Subject) expires $($_.NotAfter)" -ForegroundColor Yellow } + } + + $audit.Certificates = @($certs | ForEach-Object { + [ordered]@{ + Subject = $_.Subject; Issuer = $_.Issuer; Thumbprint = $_.Thumbprint + NotBefore = $_.NotBefore.ToString("yyyy-MM-dd"); NotAfter = $_.NotAfter.ToString("yyyy-MM-dd") + ExpiringSoon = ($_.NotAfter -lt (Get-Date).AddDays(90)) + } + }) + Write-Host " [OK] Certificates collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Certificates"; Error = $_.Exception.Message } +} + +# ===================================================== +# 29. INSTALLED SOFTWARE (registry-based) +# ===================================================== +Write-Host "" +Write-Host "=== 29. INSTALLED SOFTWARE ===" -ForegroundColor Cyan +Write-Host " Running: Registry query (HKLM Uninstall keys)" +try { + $regPaths = @( + 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', + 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' + ) + $software = Get-ItemProperty $regPaths -ErrorAction SilentlyContinue | + Where-Object { $_.DisplayName } | + Select-Object DisplayName, DisplayVersion, Publisher, InstallDate | + Sort-Object DisplayName -Unique + + $software | Format-Table -AutoSize + + $audit.InstalledSoftware = @($software | ForEach-Object { + [ordered]@{ + Name = $_.DisplayName; Version = $_.DisplayVersion + Publisher = $_.Publisher; InstallDate = $_.InstallDate + } + }) + Write-Host " [OK] Software collected - $($software.Count) packages" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "InstalledSoftware"; Error = $_.Exception.Message } +} + +# ===================================================== +# 30. SERVICES (non-default running) +# ===================================================== +Write-Host "" +Write-Host "=== 30. SERVICES (non-default, running) ===" -ForegroundColor Cyan +Write-Host " Running: Get-Service | Where Running + Automatic" +try { + $services = Get-Service | Where-Object { $_.Status -eq 'Running' -and $_.StartType -eq 'Automatic' } | + Where-Object { $_.DisplayName -notmatch '^(Windows|Microsoft|Net\.|COM\+|DCOM|WMI|Plug|Task|CNG|Base|Crypto|Security Center|DHCP Client|DNS Client|Group Policy|IP Helper|Server$|Workstation$|TCP/IP|Remote Procedure|User Profile|Background|Connected|CoreMessaging|State Repository|Storage|System Events|Time Broker|User Manager|WinHTTP|Diagnostic)' } | + Select-Object Name, DisplayName, StartType | + Sort-Object DisplayName + + $services | Format-Table -AutoSize + + $audit.Services = @($services | ForEach-Object { + [ordered]@{ Name = $_.Name; DisplayName = $_.DisplayName; StartType = "$($_.StartType)" } + }) + Write-Host " [OK] Services collected - $($services.Count) non-default" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Services"; Error = $_.Exception.Message } +} + +# ===================================================== +# 31. SCHEDULED TASKS (non-Microsoft) +# ===================================================== +Write-Host "" +Write-Host "=== 31. SCHEDULED TASKS (non-Microsoft) ===" -ForegroundColor Cyan +Write-Host " Running: Get-ScheduledTask" +try { + $tasks = Get-ScheduledTask | + Where-Object { $_.Author -notlike 'Microsoft*' -and $_.State -ne 'Disabled' } | + Select-Object TaskName, State, Author, TaskPath + $tasks | Format-Table -AutoSize + + $audit.ScheduledTasks = @($tasks | ForEach-Object { + [ordered]@{ Name = $_.TaskName; State = "$($_.State)"; Author = $_.Author; Path = $_.TaskPath } + }) + Write-Host " [OK] Scheduled tasks collected - $($tasks.Count) non-Microsoft" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "ScheduledTasks"; Error = $_.Exception.Message } +} + +# ===================================================== +# 32. WINDOWS UPDATES +# ===================================================== +Write-Host "" +Write-Host "=== 32. WINDOWS UPDATES (last 20) ===" -ForegroundColor Cyan +Write-Host " Running: Get-HotFix" +try { + $updates = Get-HotFix | Sort-Object InstalledOn -Descending -ErrorAction SilentlyContinue | Select-Object -First 20 | + Select-Object HotFixID, Description, InstalledOn, InstalledBy + $updates | Format-Table -AutoSize + + $audit.WindowsUpdates = @($updates | ForEach-Object { + [ordered]@{ + KB = $_.HotFixID; Description = $_.Description + InstalledOn = if ($_.InstalledOn) { $_.InstalledOn.ToString("yyyy-MM-dd") } else { $null } + InstalledBy = $_.InstalledBy + } + }) + Write-Host " [OK] Updates collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "WindowsUpdates"; Error = $_.Exception.Message } +} + +# ===================================================== +# 33. TIME CONFIG +# ===================================================== +Write-Host "" +Write-Host "=== 33. TIME CONFIG ===" -ForegroundColor Cyan +Write-Host " Running: w32tm /query /configuration" +try { + $timeConfig = w32tm /query /configuration 2>&1 + $timeConfig | ForEach-Object { Write-Host " $_" } + Write-Host " [OK] Time config collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "TimeConfig"; Error = $_.Exception.Message } +} + +# ===================================================== +# 34. EVENT LOG ERRORS (last 14 days) +# ===================================================== +Write-Host "" +Write-Host "=== 34. EVENT LOG ERRORS (Critical/Error, last 14 days) ===" -ForegroundColor Cyan +Write-Host " Running: Get-WinEvent -FilterHashtable System log" +try { + $since = (Get-Date).AddDays(-14) + $events = Get-WinEvent -FilterHashtable @{LogName='System'; Level=1,2; StartTime=$since} -MaxEvents 30 -ErrorAction Stop | + Select-Object TimeCreated, Id, LevelDisplayName, ProviderName, @{N='Message';E={$_.Message.Substring(0, [Math]::Min(200, $_.Message.Length))}} + $events | Format-Table TimeCreated, Id, ProviderName -AutoSize + + $audit.EventLogErrors = @($events | ForEach-Object { + [ordered]@{ + Time = $_.TimeCreated.ToString("yyyy-MM-dd HH:mm:ss") + EventId = $_.Id; Level = $_.LevelDisplayName; Source = $_.ProviderName + Message = $_.Message + } + }) + Write-Host " [OK] Events collected - $($events.Count) critical/error events" -ForegroundColor Green +} +catch { + Write-Host " No critical/error events in System log (last 14 days)" -ForegroundColor Green + $audit.EventLogErrors = @() +} + +# ===================================================== +# 35. EVENT LOG WARNINGS (last 14 days) +# ===================================================== +Write-Host "" +Write-Host "=== 35. EVENT LOG WARNINGS (last 14 days) ===" -ForegroundColor Cyan +Write-Host " Running: Get-WinEvent - Warning level, last 14 days" +try { + $warnSince = (Get-Date).AddDays(-14) + $warnings = Get-WinEvent -FilterHashtable @{LogName='System'; Level=3; StartTime=$warnSince} -MaxEvents 50 -ErrorAction Stop | + Select-Object TimeCreated, Id, ProviderName, @{N='Message';E={$_.Message.Substring(0, [Math]::Min(200, $_.Message.Length))}} + Write-Host " $($warnings.Count) warning events found" + $warnings | Format-Table TimeCreated, Id, ProviderName -AutoSize + $audit.EventLogWarnings = @($warnings | ForEach-Object { + [ordered]@{ Time = $_.TimeCreated.ToString("yyyy-MM-dd HH:mm:ss"); EventId = $_.Id; Source = $_.ProviderName; Message = $_.Message } + }) + Write-Host " [OK] Event log warnings collected" -ForegroundColor Green +} +catch { + Write-Host " No warning events in System log (last 14 days)" -ForegroundColor Green + $audit.EventLogWarnings = @() +} + +# ===================================================== +# 36. EVENT LOG SETTINGS +# ===================================================== +Write-Host "" +Write-Host "=== 36. EVENT LOG SETTINGS ===" -ForegroundColor Cyan +Write-Host " Running: Get-WinEvent -ListLog" +try { + $importantLogs = @('Application', 'Security', 'System', 'Setup', 'Microsoft-Windows-PowerShell/Operational') + $audit.EventLogSettings = @() + foreach ($logName in $importantLogs) { + try { + $log = Get-WinEvent -ListLog $logName -ErrorAction Stop + $logInfo = [ordered]@{ + LogName = $log.LogName + MaxSizeKB = [math]::Round($log.MaximumSizeInBytes / 1KB) + CurrentSizeKB = [math]::Round($log.FileSize / 1KB) + RecordCount = $log.RecordCount + LogMode = "$($log.LogMode)" + IsEnabled = $log.IsEnabled + } + Write-Host " $($log.LogName): Max=$($logInfo.MaxSizeKB)KB, Current=$($logInfo.CurrentSizeKB)KB, Mode=$($log.LogMode), Records=$($log.RecordCount)" + $audit.EventLogSettings += $logInfo + } + catch { Write-Host " $logName`: not available" -ForegroundColor DarkGray } + } + + # HIPAA requires adequate log retention + $securityLog = $audit.EventLogSettings | Where-Object { $_.LogName -eq 'Security' } + if ($securityLog -and $securityLog.MaxSizeKB -lt 131072) { + Write-Host " [WARN] Security log max size is $($securityLog.MaxSizeKB)KB - recommend at least 128MB for compliance" -ForegroundColor Yellow + } + if ($securityLog -and $securityLog.LogMode -eq 'Circular') { + Write-Host " [WARN] Security log is in Circular mode - old events will be overwritten. Consider archiving for HIPAA" -ForegroundColor Yellow + } + Write-Host " [OK] Event log settings collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "EventLogSettings"; Error = $_.Exception.Message } +} + +# ===================================================== +# 37. WINDOWS FIREWALL PROFILES +# ===================================================== +Write-Host "" +Write-Host "=== 37. WINDOWS FIREWALL PROFILES ===" -ForegroundColor Cyan +Write-Host " Running: Get-NetFirewallProfile" +try { + $fwProfiles = Get-NetFirewallProfile -ErrorAction Stop + $audit.FirewallProfiles = @($fwProfiles | ForEach-Object { + $p = [ordered]@{ + Profile = "$($_.Name)"; Enabled = $_.Enabled + DefaultInboundAction = "$($_.DefaultInboundAction)" + DefaultOutboundAction = "$($_.DefaultOutboundAction)" + LogAllowed = $_.LogAllowed; LogBlocked = $_.LogBlocked + LogFileName = $_.LogFileName; LogMaxSizeKilobytes = $_.LogMaxSizeKilobytes + } + Write-Host " $($_.Name): Enabled=$($_.Enabled), Inbound=$($_.DefaultInboundAction), Outbound=$($_.DefaultOutboundAction)" + if (-not $_.Enabled) { + Write-Host " [WARN] $($_.Name) firewall profile is DISABLED" -ForegroundColor Red + } + if (-not $_.LogBlocked) { + Write-Host " [WARN] $($_.Name) blocked connections are NOT being logged" -ForegroundColor Yellow + } + $p + }) + Write-Host " [OK] Firewall profiles collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "FirewallProfiles"; Error = $_.Exception.Message } +} + +# ===================================================== +# 38. RDP SECURITY SETTINGS +# ===================================================== +Write-Host "" +Write-Host "=== 38. RDP SECURITY SETTINGS ===" -ForegroundColor Cyan +Write-Host " Running: Registry check for RDP settings" +try { + $tsReg = 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' + $rdpReg = 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' + + $rdpEnabled = (Get-ItemProperty $tsReg -ErrorAction Stop).fDenyTSConnections + $nla = (Get-ItemProperty $rdpReg -ErrorAction SilentlyContinue).UserAuthentication + $secLayer = (Get-ItemProperty $rdpReg -ErrorAction SilentlyContinue).SecurityLayer + $minEncrypt = (Get-ItemProperty $rdpReg -ErrorAction SilentlyContinue).MinEncryptionLevel + + $rdpSettings = [ordered]@{ + RDPEnabled = ($rdpEnabled -eq 0) + NLARequired = ($nla -eq 1) + SecurityLayer = switch ($secLayer) { 0 {"RDP Security"} 1 {"Negotiate"} 2 {"SSL/TLS"} default {"Unknown ($secLayer)"} } + MinEncryptionLevel = switch ($minEncrypt) { 1 {"Low"} 2 {"Client Compatible"} 3 {"High"} 4 {"FIPS"} default {"Unknown ($minEncrypt)"} } + } + $rdpSettings.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + + if ($rdpSettings.RDPEnabled -and -not $rdpSettings.NLARequired) { + Write-Host " [WARN] RDP is enabled but NLA is NOT required - credential theft risk" -ForegroundColor Red + } + if ($rdpSettings.RDPEnabled -and $secLayer -lt 2) { + Write-Host " [WARN] RDP security layer is not SSL/TLS - downgrade attack risk" -ForegroundColor Yellow + } + + $audit.RDPSettings = $rdpSettings + Write-Host " [OK] RDP settings collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "RDPSettings"; Error = $_.Exception.Message } +} + +# ===================================================== +# 39. TLS/SSL CONFIGURATION +# ===================================================== +Write-Host "" +Write-Host "=== 39. TLS/SSL CONFIGURATION ===" -ForegroundColor Cyan +Write-Host " Running: Registry check for TLS protocol versions" +try { + $tlsVersions = @('SSL 2.0', 'SSL 3.0', 'TLS 1.0', 'TLS 1.1', 'TLS 1.2', 'TLS 1.3') + $audit.TLSConfig = [ordered]@{} + + foreach ($ver in $tlsVersions) { + $serverPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\$ver\Server" + $enabled = $null + if (Test-Path $serverPath) { + $enabled = (Get-ItemProperty $serverPath -ErrorAction SilentlyContinue).Enabled + } + $status = if ($null -eq $enabled) { "OS Default" } elseif ($enabled -eq 0) { "Disabled" } else { "Enabled" } + Write-Host " $ver`: $status" + $audit.TLSConfig[$ver] = $status + + if ($ver -in @('SSL 2.0', 'SSL 3.0', 'TLS 1.0', 'TLS 1.1') -and $status -ne 'Disabled') { + Write-Host " [WARN] $ver should be explicitly DISABLED for compliance" -ForegroundColor Yellow + } + } + Write-Host " [OK] TLS config collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "TLSConfig"; Error = $_.Exception.Message } +} + +# ===================================================== +# 40. CREDENTIAL GUARD / DEVICE GUARD +# ===================================================== +Write-Host "" +Write-Host "=== 40. CREDENTIAL GUARD / DEVICE GUARD ===" -ForegroundColor Cyan +Write-Host " Running: Get-CimInstance Win32_DeviceGuard" +try { + $dg = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -ErrorAction Stop + $cgStatus = switch ($dg.SecurityServicesRunning) { + { 1 -in $_ } { "Running" } + default { "Not running" } + } + $vbsStatus = switch ($dg.VirtualizationBasedSecurityStatus) { + 0 { "Not enabled" } 1 { "Enabled but not running" } 2 { "Running" } default { "Unknown" } + } + + $guardInfo = [ordered]@{ + VBSStatus = $vbsStatus + CredentialGuard = $cgStatus + SecurityServicesConfigured = @($dg.SecurityServicesConfigured) + SecurityServicesRunning = @($dg.SecurityServicesRunning) + } + $guardInfo.GetEnumerator() | ForEach-Object { + $val = if ($_.Value -is [array]) { $_.Value -join ", " } else { $_.Value } + Write-Host " $($_.Key): $val" + } + if ($cgStatus -ne "Running") { + Write-Host " [WARN] Credential Guard is not running - pass-the-hash risk" -ForegroundColor Yellow + } + $audit.CredentialGuard = $guardInfo + Write-Host " [OK] Credential/Device Guard checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "CredentialGuard"; Error = $_.Exception.Message } +} + +# ===================================================== +# 41. UAC SETTINGS +# ===================================================== +Write-Host "" +Write-Host "=== 41. UAC SETTINGS ===" -ForegroundColor Cyan +Write-Host " Running: Registry check for UAC" +try { + $uacReg = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' -ErrorAction Stop + $uacSettings = [ordered]@{ + EnableLUA = $uacReg.EnableLUA + ConsentPromptBehaviorAdmin = switch ($uacReg.ConsentPromptBehaviorAdmin) { + 0 {"Elevate without prompting"} 1 {"Prompt for credentials on secure desktop"} + 2 {"Prompt for consent on secure desktop"} 3 {"Prompt for credentials"} + 4 {"Prompt for consent"} 5 {"Prompt for consent for non-Windows binaries"} default {"Unknown"} + } + PromptOnSecureDesktop = $uacReg.PromptOnSecureDesktop + FilterAdministratorToken = $uacReg.FilterAdministratorToken + } + $uacSettings.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + + if ($uacReg.EnableLUA -ne 1) { + Write-Host " [WARN] UAC is DISABLED - all processes run with full admin rights" -ForegroundColor Red + } + if ($uacReg.ConsentPromptBehaviorAdmin -eq 0) { + Write-Host " [WARN] Admin elevation without prompting - malware risk" -ForegroundColor Yellow + } + $audit.UACSettings = $uacSettings + Write-Host " [OK] UAC settings collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "UACSettings"; Error = $_.Exception.Message } +} + +# ===================================================== +# 42. SCREEN LOCK / INACTIVITY TIMEOUT +# ===================================================== +Write-Host "" +Write-Host "=== 42. SCREEN LOCK / INACTIVITY TIMEOUT ===" -ForegroundColor Cyan +Write-Host " Running: Registry check for screen lock policy" +try { + $screenLock = [ordered]@{} + + # Machine inactivity limit + $inactivity = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' -ErrorAction SilentlyContinue).InactivityTimeoutSecs + $screenLock.InactivityTimeoutSecs = $inactivity + if ($inactivity) { + Write-Host " Machine inactivity timeout: $inactivity seconds ($([math]::Round($inactivity/60)) minutes)" + } else { + Write-Host " Machine inactivity timeout: Not configured" + Write-Host " [WARN] No machine inactivity timeout - HIPAA requires automatic session lock" -ForegroundColor Yellow + } + + # Screensaver settings (applied via GPO) + $ssActive = (Get-ItemProperty 'HKCU:\Control Panel\Desktop' -ErrorAction SilentlyContinue).ScreenSaveActive + $ssTimeout = (Get-ItemProperty 'HKCU:\Control Panel\Desktop' -ErrorAction SilentlyContinue).ScreenSaveTimeOut + $ssSecure = (Get-ItemProperty 'HKCU:\Control Panel\Desktop' -ErrorAction SilentlyContinue).ScreenSaverIsSecure + $screenLock.ScreenSaverActive = $ssActive + $screenLock.ScreenSaverTimeout = $ssTimeout + $screenLock.ScreenSaverSecure = $ssSecure + + Write-Host " Screensaver active: $ssActive, Timeout: $ssTimeout sec, Require password: $ssSecure" + + $audit.ScreenLockPolicy = $screenLock + Write-Host " [OK] Screen lock settings collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "ScreenLock"; Error = $_.Exception.Message } +} + +# ===================================================== +# 43. USB STORAGE POLICY +# ===================================================== +Write-Host "" +Write-Host "=== 43. USB STORAGE POLICY ===" -ForegroundColor Cyan +Write-Host " Running: Registry check for USB storage restrictions" +try { + $usbStorage = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\USBSTOR' -ErrorAction Stop).Start + $usbStatus = switch ($usbStorage) { + 3 { "Enabled (normal)" } 4 { "Disabled" } default { "Unknown ($usbStorage)" } + } + Write-Host " USB Storage: $usbStatus" + + # Check for GPO-based USB restrictions + $usbGPO = Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\RemovableStorageDevices' -ErrorAction SilentlyContinue + if ($usbGPO) { + Write-Host " GPO removable storage restrictions detected" + } + + if ($usbStorage -eq 3 -and -not $usbGPO) { + Write-Host " [WARN] USB storage is unrestricted - data exfiltration risk / HIPAA concern" -ForegroundColor Yellow + } + + $audit.USBStoragePolicy = [ordered]@{ USBSTORStart = $usbStorage; Status = $usbStatus; GPORestrictions = ($null -ne $usbGPO) } + Write-Host " [OK] USB storage policy collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "USBStorage"; Error = $_.Exception.Message } +} + +# ===================================================== +# 44. FAILED LOGON ATTEMPTS (last 7 days) +# ===================================================== +Write-Host "" +Write-Host "=== 44. FAILED LOGON ATTEMPTS (last 7 days) ===" -ForegroundColor Cyan +Write-Host " Running: Get-WinEvent Security log - Event ID 4625" +$failedSince = (Get-Date).AddDays(-7) +$failedLogons = @(Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625; StartTime=$failedSince} -MaxEvents 100 -ErrorAction SilentlyContinue) +if ($failedLogons.Count -gt 0) { + # Group by target account + $grouped = $failedLogons | ForEach-Object { + $xml = [xml]$_.ToXml() + $targetUser = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text' + $sourceIP = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'IpAddress' }).'#text' + [PSCustomObject]@{ Time = $_.TimeCreated; User = $targetUser; SourceIP = $sourceIP } + } + $summary = $grouped | Group-Object User | Sort-Object Count -Descending | Select-Object Count, Name + Write-Host " $($failedLogons.Count) failed logon attempts in last 7 days:" -ForegroundColor Yellow + $summary | ForEach-Object { Write-Host " $($_.Name): $($_.Count) failures" -ForegroundColor Yellow } + + $audit.FailedLogons = [ordered]@{ + TotalCount = $failedLogons.Count + ByUser = @($summary | ForEach-Object { [ordered]@{ User = $_.Name; Count = $_.Count } }) + Recent = @($grouped | Select-Object -First 20 | ForEach-Object { + [ordered]@{ Time = $_.Time.ToString("yyyy-MM-dd HH:mm:ss"); User = $_.User; SourceIP = $_.SourceIP } + }) + } +} else { + Write-Host " No failed logon attempts" -ForegroundColor Green + $audit.FailedLogons = [ordered]@{ TotalCount = 0 } +} +Write-Host " [OK] Failed logons collected" -ForegroundColor Green + +# ===================================================== +# 45. SECURITY QUICK CHECKS +# ===================================================== +Write-Host "" +Write-Host "=== 45. SECURITY QUICK CHECKS ===" -ForegroundColor Cyan +$audit.SecurityChecks = [ordered]@{} + +# --- Password Policy --- +Write-Host " Running: Get-ADDefaultDomainPasswordPolicy" +try { + $pp = Get-ADDefaultDomainPasswordPolicy -ErrorAction Stop + $passPolicy = [ordered]@{ + MinLength = $pp.MinPasswordLength + ComplexityEnabled = $pp.ComplexityEnabled + MaxAge = "$($pp.MaxPasswordAge)" + MinAge = "$($pp.MinPasswordAge)" + HistoryCount = $pp.PasswordHistoryCount + LockoutThreshold = $pp.LockoutThreshold + LockoutDuration = "$($pp.LockoutDuration)" + LockoutWindow = "$($pp.LockoutObservationWindow)" + ReversibleEncryption = $pp.ReversibleEncryptionEnabled + } + $passPolicy.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + + # Flag weak settings + $passIssues = @() + if ($pp.MinPasswordLength -lt 12) { $passIssues += "Min password length is $($pp.MinPasswordLength) - recommend 12+" } + if (-not $pp.ComplexityEnabled) { $passIssues += "Complexity is DISABLED" } + if ($pp.LockoutThreshold -eq 0) { $passIssues += "Account lockout is DISABLED - brute force risk" } + if ($pp.ReversibleEncryptionEnabled) { $passIssues += "Reversible encryption is ENABLED - critical risk" } + if ($pp.PasswordHistoryCount -lt 12) { $passIssues += "Password history is only $($pp.PasswordHistoryCount) - recommend 12+" } + + $passPolicy.Issues = $passIssues + if ($passIssues) { + $passIssues | ForEach-Object { Write-Host " [WARN] $_" -ForegroundColor Yellow } + } + $audit.SecurityChecks.PasswordPolicy = $passPolicy + Write-Host " [OK] Password policy checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "PasswordPolicy"; Error = $_.Exception.Message } +} + +# --- Fine-Grained Password Policies --- +Write-Host " Running: Get-ADFineGrainedPasswordPolicy" +try { + $fgpp = Get-ADFineGrainedPasswordPolicy -Filter * -ErrorAction Stop + if ($fgpp) { + $audit.SecurityChecks.FineGrainedPolicies = @($fgpp | ForEach-Object { + Write-Host " FGPP: $($_.Name) - MinLen=$($_.MinPasswordLength), MaxAge=$($_.MaxPasswordAge), AppliesTo=$($_.AppliesTo -join ', ')" + [ordered]@{ + Name = $_.Name; Precedence = $_.Precedence; MinLength = $_.MinPasswordLength + MaxAge = "$($_.MaxPasswordAge)"; LockoutThreshold = $_.LockoutThreshold + AppliesTo = @($_.AppliesTo) + } + }) + Write-Host " [OK] Fine-grained policies checked" -ForegroundColor Green + } else { + Write-Host " No fine-grained password policies configured" + $audit.SecurityChecks.FineGrainedPolicies = @() + } +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "FineGrainedPolicies"; Error = $_.Exception.Message } +} + +# --- LAPS Status --- +Write-Host " Running: LAPS check" +try { + $lapsCheck = [ordered]@{ LegacyLAPS = $false; WindowsLAPS = $false } + + # Check for Legacy LAPS schema extension + $schemaNC = (Get-ADRootDSE).schemaNamingContext + $lapsSchema = Get-ADObject -SearchBase $schemaNC -Filter "Name -eq 'ms-Mcs-AdmPwd'" -ErrorAction SilentlyContinue + if ($lapsSchema) { + $lapsCheck.LegacyLAPS = $true + Write-Host " Legacy LAPS (ms-Mcs-AdmPwd): INSTALLED" -ForegroundColor Green + # Count computers with LAPS passwords + $lapsComputers = (Get-ADComputer -Filter * -Properties ms-Mcs-AdmPwd | Where-Object { $_.'ms-Mcs-AdmPwd' }).Count + $totalComputers = (Get-ADComputer -Filter *).Count + $lapsCheck.LegacyLAPSCoverage = "$lapsComputers / $totalComputers computers" + Write-Host " Coverage: $lapsComputers / $totalComputers computers have LAPS passwords" + } else { + Write-Host " Legacy LAPS: NOT INSTALLED" -ForegroundColor Yellow + } + + # Check for Windows LAPS schema + $winLapsSchema = Get-ADObject -SearchBase $schemaNC -Filter "Name -eq 'ms-LAPS-Password'" -ErrorAction SilentlyContinue + if ($winLapsSchema) { + $lapsCheck.WindowsLAPS = $true + Write-Host " Windows LAPS (ms-LAPS-Password): INSTALLED" -ForegroundColor Green + } else { + Write-Host " Windows LAPS: NOT INSTALLED" -ForegroundColor Yellow + } + + if (-not $lapsCheck.LegacyLAPS -and -not $lapsCheck.WindowsLAPS) { + Write-Host " [WARN] No LAPS deployment detected - local admin passwords are likely shared/static" -ForegroundColor Red + } + $audit.SecurityChecks.LAPS = $lapsCheck + Write-Host " [OK] LAPS checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "LAPS"; Error = $_.Exception.Message } +} + +# --- SMBv1 + SMB Signing --- +Write-Host " Running: SMB configuration check" +try { + $smbConfig = Get-SmbServerConfiguration -ErrorAction Stop + + # SMBv1 + if ($smbConfig.EnableSMB1Protocol) { + Write-Host " [WARN] SMBv1 is ENABLED - known vulnerability, should be disabled" -ForegroundColor Red + } else { + Write-Host " SMBv1: Disabled" -ForegroundColor Green + } + $audit.SecurityChecks.SMBv1Enabled = $smbConfig.EnableSMB1Protocol + + # SMB Signing + $smbSigning = [ordered]@{ + RequireSecuritySignature = $smbConfig.RequireSecuritySignature + EnableSecuritySignature = $smbConfig.EnableSecuritySignature + EncryptData = $smbConfig.EncryptData + } + $smbSigning.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + if (-not $smbConfig.RequireSecuritySignature) { + Write-Host " [WARN] SMB signing is NOT required - relay attack risk" -ForegroundColor Yellow + } + $audit.SecurityChecks.SMBSigning = $smbSigning + Write-Host " [OK] SMB config checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "SMBConfig"; Error = $_.Exception.Message } +} + +# --- Machine Account Quota --- +Write-Host " Running: Machine Account Quota check" +try { + $maq = (Get-ADObject -Identity $script:domain.DistinguishedName -Properties ms-DS-MachineAccountQuota).'ms-DS-MachineAccountQuota' + Write-Host " Machine Account Quota: $maq" + if ($maq -gt 0) { + Write-Host " [WARN] Any domain user can join up to $maq computers to the domain" -ForegroundColor Yellow + } + $audit.SecurityChecks.MachineAccountQuota = $maq + Write-Host " [OK] Machine account quota checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "MachineAccountQuota"; Error = $_.Exception.Message } +} + +# --- AD Recycle Bin --- +Write-Host " Running: AD Recycle Bin check" +try { + $recycleBin = Get-ADOptionalFeature -Filter "Name -eq 'Recycle Bin Feature'" -ErrorAction Stop + if ($recycleBin.EnabledScopes.Count -gt 0) { + Write-Host " AD Recycle Bin: ENABLED" -ForegroundColor Green + $audit.SecurityChecks.RecycleBinEnabled = $true + } else { + Write-Host " [WARN] AD Recycle Bin is NOT enabled - accidental deletions are unrecoverable" -ForegroundColor Yellow + $audit.SecurityChecks.RecycleBinEnabled = $false + } + Write-Host " [OK] Recycle Bin checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "RecycleBin"; Error = $_.Exception.Message } +} + +# --- Accounts with Password Never Expires --- +Write-Host " Running: Accounts with PasswordNeverExpires" +try { + $neverExpire = Get-ADUser -Filter { PasswordNeverExpires -eq $true -and Enabled -eq $true } -Properties PasswordNeverExpires, PasswordLastSet | + Select-Object Name, SamAccountName, PasswordLastSet + if ($neverExpire) { + Write-Host " [WARN] $($neverExpire.Count) enabled accounts have PasswordNeverExpires:" -ForegroundColor Yellow + $neverExpire | ForEach-Object { Write-Host " $($_.SamAccountName) - Password set: $($_.PasswordLastSet)" -ForegroundColor Yellow } + } else { + Write-Host " No enabled accounts with PasswordNeverExpires" -ForegroundColor Green + } + $audit.SecurityChecks.PasswordNeverExpires = @($neverExpire | ForEach-Object { + [ordered]@{ Name = $_.Name; SAM = $_.SamAccountName; PasswordLastSet = if ($_.PasswordLastSet) { $_.PasswordLastSet.ToString("yyyy-MM-dd") } else { $null } } + }) + Write-Host " [OK] PasswordNeverExpires checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "PasswordNeverExpires"; Error = $_.Exception.Message } +} + +# --- Stale Passwords (not changed in 90+ days) --- +Write-Host " Running: Stale passwords check - enabled accounts, 90+ days" +try { + $staleDate = (Get-Date).AddDays(-90) + $stalePasswords = Get-ADUser -Filter { Enabled -eq $true -and PasswordLastSet -lt $staleDate } -Properties PasswordLastSet | + Select-Object Name, SamAccountName, PasswordLastSet | Sort-Object PasswordLastSet + if ($stalePasswords) { + Write-Host " [WARN] $($stalePasswords.Count) enabled accounts have passwords older than 90 days:" -ForegroundColor Yellow + $stalePasswords | Select-Object -First 20 | ForEach-Object { + Write-Host " $($_.SamAccountName) - Last set: $($_.PasswordLastSet)" -ForegroundColor Yellow + } + if ($stalePasswords.Count -gt 20) { Write-Host " ... and $($stalePasswords.Count - 20) more" -ForegroundColor Yellow } + } else { + Write-Host " All enabled accounts have passwords changed within 90 days" -ForegroundColor Green + } + $audit.SecurityChecks.StalePasswords = @($stalePasswords | ForEach-Object { + [ordered]@{ Name = $_.Name; SAM = $_.SamAccountName; PasswordLastSet = if ($_.PasswordLastSet) { $_.PasswordLastSet.ToString("yyyy-MM-dd") } else { $null } } + }) + Write-Host " [OK] Stale passwords checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "StalePasswords"; Error = $_.Exception.Message } +} + +# --- Inactive Accounts (not logged in 90+ days) --- +Write-Host " Running: Inactive accounts check - enabled, no logon 90+ days" +try { + $inactiveDate = (Get-Date).AddDays(-90) + $inactive = Get-ADUser -Filter { Enabled -eq $true -and LastLogonDate -lt $inactiveDate } -Properties LastLogonDate | + Select-Object Name, SamAccountName, LastLogonDate | Sort-Object LastLogonDate + if ($inactive) { + Write-Host " [WARN] $($inactive.Count) enabled accounts have not logged in for 90+ days:" -ForegroundColor Yellow + $inactive | Select-Object -First 20 | ForEach-Object { + Write-Host " $($_.SamAccountName) - Last logon: $($_.LastLogonDate)" -ForegroundColor Yellow + } + if ($inactive.Count -gt 20) { Write-Host " ... and $($inactive.Count - 20) more" -ForegroundColor Yellow } + } else { + Write-Host " All enabled accounts have logged in within 90 days" -ForegroundColor Green + } + $audit.SecurityChecks.InactiveAccounts = @($inactive | ForEach-Object { + [ordered]@{ Name = $_.Name; SAM = $_.SamAccountName; LastLogon = if ($_.LastLogonDate) { $_.LastLogonDate.ToString("yyyy-MM-dd") } else { $null } } + }) + Write-Host " [OK] Inactive accounts checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "InactiveAccounts"; Error = $_.Exception.Message } +} + +# --- Locked Accounts --- +Write-Host " Running: Locked accounts check" +try { + $locked = Search-ADAccount -LockedOut | Where-Object { $_.Enabled } | Select-Object Name, SamAccountName, LastLogonDate + if ($locked) { + Write-Host " [WARN] $($locked.Count) accounts are currently LOCKED OUT:" -ForegroundColor Yellow + $locked | ForEach-Object { Write-Host " $($_.SamAccountName)" -ForegroundColor Yellow } + } else { + Write-Host " No locked accounts" -ForegroundColor Green + } + $audit.SecurityChecks.LockedAccounts = @($locked | ForEach-Object { + [ordered]@{ Name = $_.Name; SAM = $_.SamAccountName } + }) + Write-Host " [OK] Locked accounts checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "LockedAccounts"; Error = $_.Exception.Message } +} + +# --- AdminSDHolder / Protected Users --- +Write-Host " Running: Protected Users group check" +try { + $protectedMembers = Get-ADGroupMember "Protected Users" -ErrorAction Stop | Select-Object Name, SamAccountName + if ($protectedMembers) { + Write-Host " Protected Users members:" -ForegroundColor Green + $protectedMembers | ForEach-Object { Write-Host " $($_.SamAccountName)" } + } else { + Write-Host " [WARN] Protected Users group is EMPTY - admin accounts should be added" -ForegroundColor Yellow + } + $audit.SecurityChecks.ProtectedUsers = @($protectedMembers | ForEach-Object { + [ordered]@{ Name = $_.Name; SAM = $_.SamAccountName } + }) + Write-Host " [OK] Protected Users checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "ProtectedUsers"; Error = $_.Exception.Message } +} + +# --- AdminSDHolder protected accounts --- +Write-Host " Running: AdminSDHolder check" +try { + $adminSDHolder = Get-ADUser -Filter { AdminCount -eq 1 -and Enabled -eq $true } -Properties AdminCount, MemberOf | + Select-Object Name, SamAccountName + if ($adminSDHolder) { + Write-Host " Accounts with AdminCount=1 - $($adminSDHolder.Count) accounts:" -ForegroundColor Yellow + $adminSDHolder | ForEach-Object { Write-Host " $($_.SamAccountName)" } + } + $audit.SecurityChecks.AdminSDHolder = @($adminSDHolder | ForEach-Object { + [ordered]@{ Name = $_.Name; SAM = $_.SamAccountName } + }) + Write-Host " [OK] AdminSDHolder checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "AdminSDHolder"; Error = $_.Exception.Message } +} + +# --- Kerberoastable Accounts (SPNs on user accounts) --- +Write-Host " Running: Kerberoastable accounts check" +try { + $spnUsers = Get-ADUser -Filter { ServicePrincipalName -like "*" -and Enabled -eq $true } -Properties ServicePrincipalName, PasswordLastSet | + Where-Object { $_.SamAccountName -ne 'krbtgt' } | + Select-Object Name, SamAccountName, PasswordLastSet, ServicePrincipalName + if ($spnUsers) { + Write-Host " [WARN] $($spnUsers.Count) enabled user accounts have SPNs - kerberoastable:" -ForegroundColor Yellow + $spnUsers | ForEach-Object { + Write-Host " $($_.SamAccountName) - Password set: $($_.PasswordLastSet) - SPN: $($_.ServicePrincipalName[0])" -ForegroundColor Yellow + } + } else { + Write-Host " No kerberoastable user accounts found" -ForegroundColor Green + } + $audit.SecurityChecks.Kerberoastable = @($spnUsers | ForEach-Object { + [ordered]@{ + Name = $_.Name; SAM = $_.SamAccountName + PasswordLastSet = if ($_.PasswordLastSet) { $_.PasswordLastSet.ToString("yyyy-MM-dd") } else { $null } + SPNs = @($_.ServicePrincipalName) + } + }) + Write-Host " [OK] Kerberoastable checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Kerberoastable"; Error = $_.Exception.Message } +} + +# --- ASREPRoastable Accounts (PreAuth disabled) --- +Write-Host " Running: ASREPRoastable accounts check" +try { + $asrep = Get-ADUser -Filter { DoesNotRequirePreAuth -eq $true -and Enabled -eq $true } -Properties DoesNotRequirePreAuth | + Select-Object Name, SamAccountName + if ($asrep) { + Write-Host " [WARN] $($asrep.Count) accounts do NOT require Kerberos pre-auth - ASREPRoastable:" -ForegroundColor Red + $asrep | ForEach-Object { Write-Host " $($_.SamAccountName)" -ForegroundColor Red } + } else { + Write-Host " No ASREPRoastable accounts found" -ForegroundColor Green + } + $audit.SecurityChecks.ASREPRoastable = @($asrep | ForEach-Object { + [ordered]@{ Name = $_.Name; SAM = $_.SamAccountName } + }) + Write-Host " [OK] ASREPRoastable checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "ASREPRoastable"; Error = $_.Exception.Message } +} + +# --- NULL Sessions --- +Write-Host " Running: NULL session check" +try { + $lsaReg = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa' -ErrorAction Stop + $restrictAnon = $lsaReg.RestrictAnonymous + $restrictAnonSAM = $lsaReg.RestrictAnonymousSAM + $everyoneAnon = $lsaReg.EveryoneIncludesAnonymous + + $nullSession = [ordered]@{ + RestrictAnonymous = $restrictAnon + RestrictAnonymousSAM = $restrictAnonSAM + EveryoneIncludesAnonymous = $everyoneAnon + } + $nullSession.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + + if ($everyoneAnon -eq 1) { + Write-Host " [WARN] EveryoneIncludesAnonymous is ENABLED - anonymous users have Everyone access" -ForegroundColor Red + } + if ($restrictAnon -eq 0) { + Write-Host " [WARN] RestrictAnonymous is 0 - anonymous enumeration of shares/accounts possible" -ForegroundColor Yellow + } + + $audit.SecurityChecks.NullSessions = $nullSession + Write-Host " [OK] NULL sessions checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "NullSessions"; Error = $_.Exception.Message } +} + +# --- Insecure DNS Zones --- +Write-Host " Running: Insecure DNS zones check" +try { + Import-Module DnsServer -ErrorAction Stop + $insecureZones = Get-DnsServerZone | Where-Object { $_.DynamicUpdate -eq 'NonsecureAndSecure' -and -not $_.IsAutoCreated } + if ($insecureZones) { + Write-Host " [WARN] DNS zones allowing nonsecure dynamic updates:" -ForegroundColor Yellow + $insecureZones | ForEach-Object { Write-Host " $($_.ZoneName)" -ForegroundColor Yellow } + } else { + Write-Host " All DNS zones use secure-only dynamic updates" -ForegroundColor Green + } + $audit.SecurityChecks.InsecureDNSZones = @($insecureZones | ForEach-Object { $_.ZoneName }) + Write-Host " [OK] DNS zone security checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "InsecureDNSZones"; Error = $_.Exception.Message } +} + +# --- SYSVOL Password Search (GPP cpassword) --- +Write-Host " Running: SYSVOL GPP password search" +try { + $dnsDomain = if ($env:USERDNSDOMAIN) { $env:USERDNSDOMAIN } elseif ($script:domain) { $script:domain.DNSRoot } else { (Get-ADDomain).DNSRoot } + $sysvolPath = "\\$dnsDomain\SYSVOL\$dnsDomain" + $gppFiles = Get-ChildItem -Path $sysvolPath -Recurse -Include "*.xml" -ErrorAction Stop + $cpasswordFiles = @() + foreach ($file in $gppFiles) { + $content = Get-Content $file.FullName -ErrorAction SilentlyContinue + if ($content -match 'cpassword') { + $cpasswordFiles += $file.FullName + Write-Host " [CRITICAL] GPP password found in: $($file.FullName)" -ForegroundColor Red + } + } + if ($cpasswordFiles.Count -eq 0) { + Write-Host " No GPP passwords found in SYSVOL" -ForegroundColor Green + } + $audit.SecurityChecks.GPPPasswords = $cpasswordFiles + Write-Host " [OK] SYSVOL password search complete" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "GPPPasswords"; Error = $_.Exception.Message } +} + +# --- Old/EOL Operating Systems --- +Write-Host " Running: End-of-life OS check" +try { + $eolPatterns = @('Windows XP*', 'Windows Vista*', 'Windows 7*', 'Windows 8 *', 'Windows 8.1*', + 'Windows Server 2003*', 'Windows Server 2008*', 'Windows Server 2012 *', 'Windows Server 2012 R2*') + $eolComputers = @() + foreach ($pattern in $eolPatterns) { + $found = Get-ADComputer -Filter "OperatingSystem -like '$pattern' -and Enabled -eq 'True'" -Properties OperatingSystem, LastLogonDate -ErrorAction SilentlyContinue + if ($found) { $eolComputers += $found } + } + if ($eolComputers) { + Write-Host " [WARN] $($eolComputers.Count) enabled computers running END OF LIFE operating systems:" -ForegroundColor Red + $eolComputers | ForEach-Object { Write-Host " $($_.Name) - $($_.OperatingSystem) - Last logon: $($_.LastLogonDate)" -ForegroundColor Red } + } else { + Write-Host " No end-of-life operating systems detected" -ForegroundColor Green + } + $audit.SecurityChecks.EOLComputers = @($eolComputers | ForEach-Object { + [ordered]@{ Name = $_.Name; OS = $_.OperatingSystem; LastLogon = if ($_.LastLogonDate) { $_.LastLogonDate.ToString("yyyy-MM-dd") } else { $null } } + }) + Write-Host " [OK] EOL OS check complete" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "EOLComputers"; Error = $_.Exception.Message } +} + +# --- Functional Level --- +Write-Host " Running: Functional level assessment" +try { + $domainFL = if ($script:domain) { $script:domain.DomainMode } else { (Get-ADDomain).DomainMode } + $forestFL = if ($script:forest) { $script:forest.ForestMode } else { (Get-ADForest).ForestMode } + Write-Host " Domain Functional Level: $domainFL" + Write-Host " Forest Functional Level: $forestFL" + + $outdatedLevels = @('Windows2003Domain', 'Windows2008Domain', 'Windows2008R2Domain', 'Windows2012Domain', 'Windows2012R2Domain', + 'Windows2003Forest', 'Windows2008Forest', 'Windows2008R2Forest', 'Windows2012Forest', 'Windows2012R2Forest') + if ($domainFL -in $outdatedLevels -or $forestFL -in $outdatedLevels) { + Write-Host " [WARN] Functional level is outdated - limits security features like Protected Users, auth silos" -ForegroundColor Yellow + } + + $audit.SecurityChecks.FunctionalLevel = [ordered]@{ Domain = "$domainFL"; Forest = "$forestFL" } + Write-Host " [OK] Functional level checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "FunctionalLevel"; Error = $_.Exception.Message } +} + +# --- Replication Type (FRS vs DFSR) --- +Write-Host " Running: SYSVOL replication type check" +try { + $dn = if ($script:domain) { $script:domain.DistinguishedName } else { (Get-ADDomain).DistinguishedName } + $replType = (Get-ADObject "CN=DFSR-GlobalSettings,CN=System,$dn" -ErrorAction Stop) + Write-Host " SYSVOL Replication: DFSR" -ForegroundColor Green + $audit.SecurityChecks.SYSVOLReplication = "DFSR" +} +catch { + Write-Host " SYSVOL Replication: FRS (legacy - should migrate to DFSR)" -ForegroundColor Yellow + $audit.SecurityChecks.SYSVOLReplication = "FRS" +} +Write-Host " [OK] Replication type checked" -ForegroundColor Green + +# --- LDAP Signing + Channel Binding --- +Write-Host " Running: LDAP signing and channel binding check" +try { + $ntdsReg = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' -ErrorAction Stop + + $ldapSigning = $ntdsReg.LDAPServerIntegrity + $ldapSigningText = switch ($ldapSigning) { + 0 { "None (not required)" } 1 { "Require signing" } 2 { "Require signing" } default { "Unknown ($ldapSigning)" } + } + Write-Host " LDAP Server Signing: $ldapSigningText" + if ($ldapSigning -eq 0 -or $null -eq $ldapSigning) { + Write-Host " [WARN] LDAP signing is NOT required - MITM risk" -ForegroundColor Yellow + } + $audit.SecurityChecks.LDAPSigning = $ldapSigningText + + $ldapCB = $ntdsReg.LdapEnforceChannelBinding + $ldapCBText = switch ($ldapCB) { + 0 { "Never" } 1 { "When supported" } 2 { "Always" } default { "Not configured (0)" } + } + Write-Host " LDAP Channel Binding: $ldapCBText" + if ($ldapCB -ne 2) { + Write-Host " [WARN] LDAP channel binding is not set to Always - credential relay risk" -ForegroundColor Yellow + } + $audit.SecurityChecks.LDAPChannelBinding = $ldapCBText + Write-Host " [OK] LDAP settings checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "LDAPSettings"; Error = $_.Exception.Message } +} + +# --- DCs Not Owned by Domain Admins --- +Write-Host " Running: DC ownership check" +try { + $dcsNotOwned = Get-ADComputer -Filter { PrimaryGroupID -eq 516 } -Properties nTSecurityDescriptor -ErrorAction Stop | ForEach-Object { + $owner = $_.nTSecurityDescriptor.Owner + if ($owner -notmatch 'Domain Admins') { + [PSCustomObject]@{ Name = $_.Name; Owner = $owner } + } + } + if ($dcsNotOwned) { + Write-Host " [WARN] DCs not owned by Domain Admins:" -ForegroundColor Yellow + $dcsNotOwned | ForEach-Object { Write-Host " $($_.Name) owned by $($_.Owner)" -ForegroundColor Yellow } + } else { + Write-Host " All DCs owned by Domain Admins" -ForegroundColor Green + } + $audit.SecurityChecks.DCsNotOwnedByDA = @($dcsNotOwned | ForEach-Object { + [ordered]@{ Name = $_.Name; Owner = $_.Owner } + }) + Write-Host " [OK] DC ownership checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "DCOwnership"; Error = $_.Exception.Message } +} + +# --- Recent Changes (last 30 days) --- +Write-Host " Running: Recent AD changes - last 30 days" +try { + $recentDate = (Get-Date).AddDays(-30) + $newUsers = Get-ADUser -Filter { WhenCreated -gt $recentDate } -Properties WhenCreated | + Select-Object Name, SamAccountName, WhenCreated + $newGroups = Get-ADGroup -Filter { WhenCreated -gt $recentDate } -Properties WhenCreated | + Select-Object Name, WhenCreated + $newComputers = Get-ADComputer -Filter { WhenCreated -gt $recentDate } -Properties WhenCreated | + Select-Object Name, WhenCreated + + if ($newUsers) { + Write-Host " New users in last 30 days:" -ForegroundColor Yellow + $newUsers | ForEach-Object { Write-Host " $($_.SamAccountName) - Created: $($_.WhenCreated)" } + } + if ($newGroups) { + Write-Host " New groups in last 30 days:" -ForegroundColor Yellow + $newGroups | ForEach-Object { Write-Host " $($_.Name) - Created: $($_.WhenCreated)" } + } + if ($newComputers) { + Write-Host " New computers in last 30 days:" -ForegroundColor Yellow + $newComputers | ForEach-Object { Write-Host " $($_.Name) - Created: $($_.WhenCreated)" } + } + if (-not $newUsers -and -not $newGroups -and -not $newComputers) { + Write-Host " No new AD objects in the last 30 days" -ForegroundColor Green + } + + $audit.SecurityChecks.RecentChanges = [ordered]@{ + NewUsers = @($newUsers | ForEach-Object { [ordered]@{ Name = $_.Name; SAM = $_.SamAccountName; Created = $_.WhenCreated.ToString("yyyy-MM-dd") } }) + NewGroups = @($newGroups | ForEach-Object { [ordered]@{ Name = $_.Name; Created = $_.WhenCreated.ToString("yyyy-MM-dd") } }) + NewComputers = @($newComputers | ForEach-Object { [ordered]@{ Name = $_.Name; Created = $_.WhenCreated.ToString("yyyy-MM-dd") } }) + } + Write-Host " [OK] Recent changes checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "RecentChanges"; Error = $_.Exception.Message } +} + +# --- krbtgt Password Age --- +Write-Host " Running: krbtgt password age check" +try { + $krbtgt = Get-ADUser 'krbtgt' -Properties PasswordLastSet -ErrorAction Stop + $krbtgtAge = ((Get-Date) - $krbtgt.PasswordLastSet).Days + Write-Host " krbtgt password last set: $($krbtgt.PasswordLastSet) - $krbtgtAge days ago" + if ($krbtgtAge -gt 180) { + Write-Host " [WARN] krbtgt password is $krbtgtAge days old - should be rotated at least every 180 days" -ForegroundColor Yellow + } + $audit.SecurityChecks.KrbtgtPasswordAge = [ordered]@{ + LastSet = $krbtgt.PasswordLastSet.ToString("yyyy-MM-dd") + AgeDays = $krbtgtAge + } + Write-Host " [OK] krbtgt checked" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "KrbtgtAge"; Error = $_.Exception.Message } +} + +# --- Audit Policy (are logons being audited?) --- +Write-Host " Running: Audit policy check" +try { + $auditPolicy = auditpol /get /category:* 2>&1 + $auditPolicy | ForEach-Object { Write-Host " $_" } + + # Check critical audit settings + $audit.SecurityChecks.AuditPolicyRaw = @($auditPolicy | ForEach-Object { "$_".Trim() } | Where-Object { $_ }) + Write-Host " [OK] Audit policy collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "AuditPolicy"; Error = $_.Exception.Message } +} + +# --- Stopped Auto-Start Services --- +Write-Host " Running: Auto-start services that are stopped" +try { + $stoppedAuto = Get-Service | Where-Object { $_.StartType -eq 'Automatic' -and $_.Status -ne 'Running' } | + Select-Object Name, DisplayName, Status, StartType + if ($stoppedAuto) { + Write-Host " [WARN] $($stoppedAuto.Count) auto-start services are NOT running:" -ForegroundColor Yellow + $stoppedAuto | Format-Table -AutoSize + } else { + Write-Host " All auto-start services are running" -ForegroundColor Green + } + $audit.SecurityChecks.StoppedAutoServices = @($stoppedAuto | ForEach-Object { + [ordered]@{ Name = $_.Name; DisplayName = $_.DisplayName; Status = "$($_.Status)" } + }) + Write-Host " [OK] Service state check complete" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "StoppedAutoServices"; Error = $_.Exception.Message } +} + +Write-Host "" +Write-Host " === Security Summary ===" -ForegroundColor Cyan +$totalWarnings = 0 +if ($audit.SecurityChecks.PasswordPolicy.Issues) { $totalWarnings += $audit.SecurityChecks.PasswordPolicy.Issues.Count } +if ($audit.SecurityChecks.SMBv1Enabled) { $totalWarnings++ } +if ($audit.SecurityChecks.MachineAccountQuota -gt 0) { $totalWarnings++ } +if (-not $audit.SecurityChecks.RecycleBinEnabled) { $totalWarnings++ } +if ($audit.SecurityChecks.PasswordNeverExpires.Count -gt 0) { $totalWarnings++ } +if ($audit.SecurityChecks.Kerberoastable.Count -gt 0) { $totalWarnings++ } +if ($audit.SecurityChecks.ASREPRoastable.Count -gt 0) { $totalWarnings++ } +if ($audit.SecurityChecks.GPPPasswords.Count -gt 0) { $totalWarnings++ } +if ($audit.SecurityChecks.EOLComputers.Count -gt 0) { $totalWarnings++ } +if (-not $audit.SecurityChecks.LAPS.LegacyLAPS -and -not $audit.SecurityChecks.LAPS.WindowsLAPS) { $totalWarnings++ } + +if ($totalWarnings -gt 0) { + Write-Host " $totalWarnings security findings detected - review warnings above" -ForegroundColor Yellow +} else { + Write-Host " No critical security findings" -ForegroundColor Green +} + +# ===================================================== +# DONE - SAVE JSON +# ===================================================== +Write-Host "" +Write-Host "=======================================" +Write-Host " AUDIT COMPLETE" +Write-Host "=======================================" + +$errorCount = $audit._errors.Count +if ($errorCount -gt 0) { + Write-Host "Completed with $errorCount section errors (see _errors in JSON)" -ForegroundColor Yellow +} else { + Write-Host "All sections completed successfully" -ForegroundColor Green +} + +Write-Host "JSON data: $JsonFile" +Write-Host "=======================================" + +# Save JSON +$audit | ConvertTo-Json -Depth 10 | Out-File $JsonFile -Encoding UTF8 diff --git a/projects/msp-tools/msp-audit-scripts/workstation_audit.ps1 b/projects/msp-tools/msp-audit-scripts/workstation_audit.ps1 new file mode 100644 index 0000000..93ca216 --- /dev/null +++ b/projects/msp-tools/msp-audit-scripts/workstation_audit.ps1 @@ -0,0 +1,1158 @@ +# ========================================== +# UNIVERSAL WORKSTATION AUDIT SCRIPT +# Works on Windows 10 / 11 (domain or workgroup) +# Outputs: HOSTNAME_workstation_audit_DATE.json to C:\Temp +# ========================================== + +# Auto-relaunch with ExecutionPolicy Bypass if needed +if ($MyInvocation.MyCommand.Path) { + $currentPolicy = Get-ExecutionPolicy -Scope Process + if ($currentPolicy -eq 'Restricted' -or $currentPolicy -eq 'AllSigned') { + Start-Process powershell.exe -ArgumentList "-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File `"$($MyInvocation.MyCommand.Path)`"" -Wait -NoNewWindow + exit + } +} + +# Verify running as admin +$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +if (-not $isAdmin) { + Write-Host "ERROR: This script must be run as Administrator." -ForegroundColor Red + exit 1 +} + +$Date = Get-Date -Format 'yyyy-MM-dd' +$OutputDir = "C:\Temp" +$Name = $env:COMPUTERNAME +$JsonFile = "$OutputDir\${Name}_workstation_audit_$Date.json" + +if (!(Test-Path $OutputDir)) { New-Item -ItemType Directory -Path $OutputDir | Out-Null } + +# Structured data collector +$audit = [ordered]@{ + _metadata = [ordered]@{ + ScriptVersion = "1.0" + ScriptType = "Workstation" + RunDate = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") + RunBy = "$env:USERDOMAIN\$env:USERNAME" + Hostname = $env:COMPUTERNAME + } + _errors = @() +} + +Write-Host "=======================================" +Write-Host " UNIVERSAL WORKSTATION AUDIT v1.0" +Write-Host " $((Get-Date).ToString('yyyy-MM-dd HH:mm:ss'))" +Write-Host "=======================================" +Write-Host "" + +# ===================================================== +# 1. SYSTEM INFO +# ===================================================== +Write-Host "=== 1. SYSTEM INFO ===" -ForegroundColor Cyan +Write-Host " Running: Get-CimInstance Win32_OperatingSystem, Win32_ComputerSystem" +try { + $os = Get-CimInstance Win32_OperatingSystem + $cs = Get-CimInstance Win32_ComputerSystem + $uptime = (Get-Date) - $os.LastBootUpTime + + # Friendly version (e.g., 23H2) + $displayVersion = "N/A" + try { + $displayVersion = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -ErrorAction Stop).DisplayVersion + } catch {} + + $sysInfo = [ordered]@{ + Hostname = $env:COMPUTERNAME + OS = $os.Caption + OSVersion = $os.Version + DisplayVersion = $displayVersion + BuildNumber = $os.BuildNumber + Architecture = $os.OSArchitecture + InstallDate = $os.InstallDate.ToString("yyyy-MM-dd") + LastBoot = $os.LastBootUpTime.ToString("yyyy-MM-dd HH:mm:ss") + UptimeDays = [math]::Round($uptime.TotalDays, 1) + Domain = $cs.Domain + DomainJoined = $cs.PartOfDomain + CurrentUser = $cs.UserName + TotalRAM_GB = [math]::Round($cs.TotalPhysicalMemory / 1GB, 1) + } + + $sysInfo.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + $audit.SystemInfo = $sysInfo + Write-Host " [OK] System info collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "SystemInfo"; Error = $_.Exception.Message } +} + +# ===================================================== +# 2. HARDWARE +# ===================================================== +Write-Host "" +Write-Host "=== 2. HARDWARE ===" -ForegroundColor Cyan +Write-Host " Running: Get-CimInstance Win32_ComputerSystem, Win32_Processor, Win32_BIOS, Win32_SystemEnclosure" +try { + $hw = Get-CimInstance Win32_ComputerSystem + $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 + $bios = Get-CimInstance Win32_BIOS + $encl = Get-CimInstance Win32_SystemEnclosure | Select-Object -First 1 + $battery = Get-CimInstance Win32_Battery -ErrorAction SilentlyContinue + + $hardware = [ordered]@{ + Manufacturer = $hw.Manufacturer + Model = $hw.Model + SerialNumber = if ($bios.SerialNumber) { $bios.SerialNumber } else { $encl.SerialNumber } + CPU = $cpu.Name + CPUCores = $cpu.NumberOfCores + CPULogical = $cpu.NumberOfLogicalProcessors + RAM_GB = [math]::Round($hw.TotalPhysicalMemory / 1GB, 1) + BIOSVersion = $bios.SMBIOSBIOSVersion + ChassisType = switch ($encl.ChassisTypes[0]) { + 3 {"Desktop"} 4 {"Low Profile Desktop"} 5 {"Pizza Box"} 6 {"Mini Tower"} + 7 {"Tower"} 8 {"Portable"} 9 {"Laptop"} 10 {"Notebook"} 11 {"Hand Held"} + 12 {"Docking Station"} 13 {"All in One"} 14 {"Sub Notebook"} 15 {"Space-Saving"} + default {"Other ($($encl.ChassisTypes[0]))"} + } + HasBattery = ($null -ne $battery) + } + + $hardware.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + $audit.Hardware = $hardware + Write-Host " [OK] Hardware info collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Hardware"; Error = $_.Exception.Message } +} + +# ===================================================== +# 3. OS ACTIVATION STATUS +# ===================================================== +Write-Host "" +Write-Host "=== 3. OS ACTIVATION STATUS ===" -ForegroundColor Cyan +Write-Host " Running: Get-CimInstance SoftwareLicensingProduct" +try { + $lic = Get-CimInstance SoftwareLicensingProduct | + Where-Object { $_.PartialProductKey -and $_.Name -like '*Windows*' } | + Select-Object -First 1 + + $statusText = switch ($lic.LicenseStatus) { + 0 {"Unlicensed"} 1 {"Licensed"} 2 {"OOB Grace"} 3 {"OOT Grace"} + 4 {"Non-Genuine"} 5 {"Notification"} 6 {"Extended Grace"} default {"Unknown"} + } + + $activation = [ordered]@{ + ProductName = $lic.Name + LicenseStatus = $statusText + StatusCode = $lic.LicenseStatus + PartialKey = $lic.PartialProductKey + LicenseFamily = $lic.LicenseFamily + } + + $activation.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + + if ($lic.LicenseStatus -ne 1) { + Write-Host " WARNING: This machine is NOT fully licensed!" -ForegroundColor Red + } + + $audit.Activation = $activation + Write-Host " [OK] Activation status collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Activation"; Error = $_.Exception.Message } +} + +# ===================================================== +# 4. STORAGE +# ===================================================== +Write-Host "" +Write-Host "=== 4. STORAGE ===" -ForegroundColor Cyan +Write-Host " Running: Get-Volume, Get-PhysicalDisk" +try { + $volumes = Get-Volume | Where-Object { $_.DriveLetter } | Select-Object DriveLetter, + FileSystemLabel, FileSystem, + @{N='SizeGB';E={[math]::Round($_.Size/1GB,1)}}, + @{N='FreeGB';E={[math]::Round($_.SizeRemaining/1GB,1)}}, + @{N='UsedPct';E={if($_.Size -gt 0){[math]::Round(($_.Size - $_.SizeRemaining)/$_.Size * 100,0)}else{0}}}, + HealthStatus + $volumes | Format-Table -AutoSize + $audit.Volumes = @($volumes | ForEach-Object { + [ordered]@{ + Drive = "$($_.DriveLetter):"; Label = $_.FileSystemLabel; FileSystem = $_.FileSystem + SizeGB = $_.SizeGB; FreeGB = $_.FreeGB; UsedPct = $_.UsedPct; Health = "$($_.HealthStatus)" + } + }) + Write-Host " [OK] Volumes collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Volumes"; Error = $_.Exception.Message } +} + +Write-Host " Running: Get-PhysicalDisk" +try { + $disks = Get-PhysicalDisk | Select-Object DeviceId, FriendlyName, MediaType, + @{N='SizeGB';E={[math]::Round($_.Size/1GB,1)}}, HealthStatus, OperationalStatus + $disks | Format-Table -AutoSize + $audit.PhysicalDisks = @($disks | ForEach-Object { + [ordered]@{ + DeviceId = "$($_.DeviceId)"; Name = $_.FriendlyName; MediaType = "$($_.MediaType)" + SizeGB = $_.SizeGB; Health = "$($_.HealthStatus)"; Status = "$($_.OperationalStatus)" + } + }) + Write-Host " [OK] Physical disks collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] PhysicalDisk: $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "PhysicalDisks"; Error = $_.Exception.Message } +} + +# ===================================================== +# 5. BITLOCKER STATUS +# ===================================================== +Write-Host "" +Write-Host "=== 5. BITLOCKER STATUS ===" -ForegroundColor Cyan +Write-Host " Running: Get-BitLockerVolume" +try { + $bl = Get-BitLockerVolume -ErrorAction Stop + $bl | ForEach-Object { + Write-Host " $($_.MountPoint): $($_.VolumeStatus), Protection: $($_.ProtectionStatus), Method: $($_.EncryptionMethod)" + } + $audit.BitLocker = @($bl | ForEach-Object { + [ordered]@{ + MountPoint = "$($_.MountPoint)"; VolumeStatus = "$($_.VolumeStatus)" + Protection = "$($_.ProtectionStatus)"; Method = "$($_.EncryptionMethod)" + KeyProtectors = @($_.KeyProtector | ForEach-Object { "$($_.KeyProtectorType)" }) + } + }) + Write-Host " [OK] BitLocker status collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + Write-Host " Trying manage-bde fallback..." -ForegroundColor Yellow + try { + $bde = manage-bde -status 2>&1 + $bde | ForEach-Object { Write-Host " $_" } + } + catch { + Write-Host " BitLocker not available on this edition" -ForegroundColor Yellow + } + $audit._errors += @{ Section = "BitLocker"; Error = $_.Exception.Message } +} + +# ===================================================== +# 6. NETWORK CONFIG +# ===================================================== +Write-Host "" +Write-Host "=== 6. NETWORK CONFIG ===" -ForegroundColor Cyan + +Write-Host " Running: Get-NetAdapter" +try { + $adapters = Get-NetAdapter | Select-Object Name, InterfaceDescription, MacAddress, Status, LinkSpeed + $adapters | Format-Table -AutoSize + $audit.NetworkAdapters = @($adapters | ForEach-Object { + [ordered]@{ Name = $_.Name; Description = $_.InterfaceDescription; MAC = $_.MacAddress; Status = "$($_.Status)"; LinkSpeed = $_.LinkSpeed } + }) + Write-Host " [OK] Adapters collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "NetworkAdapters"; Error = $_.Exception.Message } +} + +Write-Host " Running: Get-NetIPConfiguration" +try { + $ipConfigs = Get-NetIPConfiguration | Where-Object { $_.IPv4Address } + $audit.IPConfig = @($ipConfigs | ForEach-Object { + [ordered]@{ + Interface = $_.InterfaceAlias + IPv4 = "$($_.IPv4Address.IPAddress)" + PrefixLength = $_.IPv4Address.PrefixLength + Gateway = "$($_.IPv4DefaultGateway.NextHop)" + DNS = @($_.DNSServer | Where-Object { $_.AddressFamily -eq 2 } | ForEach-Object { $_.ServerAddresses }) | Select-Object -Unique + DHCPEnabled = (Get-NetIPInterface -InterfaceAlias $_.InterfaceAlias -AddressFamily IPv4 -ErrorAction SilentlyContinue).Dhcp + } + }) + $ipConfigs | Format-List InterfaceAlias, IPv4Address, IPv4DefaultGateway, DnsServer + Write-Host " [OK] IP config collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "IPConfig"; Error = $_.Exception.Message } +} + +# ===================================================== +# 7. DOMAIN MEMBERSHIP +# ===================================================== +Write-Host "" +Write-Host "=== 7. DOMAIN MEMBERSHIP ===" -ForegroundColor Cyan +Write-Host " Running: Win32_ComputerSystem + nltest + DirectorySearcher" +try { + $cs = Get-CimInstance Win32_ComputerSystem + $domainInfo = [ordered]@{ + DomainJoined = $cs.PartOfDomain + Domain = $cs.Domain + Workgroup = if (-not $cs.PartOfDomain) { $cs.Workgroup } else { $null } + } + + if ($cs.PartOfDomain) { + # Get site + try { + $site = nltest /dsgetsite 2>&1 | Select-Object -First 1 + $domainInfo.ADSite = $site.Trim() + } catch {} + + # Get DC + try { + $dcInfo = nltest /dsgetdc:$($cs.Domain) 2>&1 + $dcLine = ($dcInfo | Select-String "DC: \\\\").Line + if ($dcLine) { $domainInfo.DC = $dcLine.Trim() } + } catch {} + + # Get computer OU via DirectorySearcher (no RSAT needed) + try { + $searcher = New-Object System.DirectoryServices.DirectorySearcher + $searcher.Filter = "(&(objectClass=computer)(cn=$env:COMPUTERNAME))" + $result = $searcher.FindOne() + if ($result) { $domainInfo.ComputerDN = "$($result.Properties['distinguishedname'][0])" } + } catch {} + } + + $domainInfo.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + $audit.DomainMembership = $domainInfo + Write-Host " [OK] Domain membership collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "DomainMembership"; Error = $_.Exception.Message } +} + +# ===================================================== +# 8. CURRENT USER INFO +# ===================================================== +Write-Host "" +Write-Host "=== 8. CURRENT USER INFO ===" -ForegroundColor Cyan +Write-Host " Running: query user, whoami /groups" +try { + $currentUser = [ordered]@{ + Username = $env:USERNAME + Domain = $env:USERDOMAIN + } + + # Logged-in sessions + Write-Host " Active sessions:" -ForegroundColor Yellow + try { + $sessions = query user 2>&1 + $sessions | ForEach-Object { Write-Host " $_" } + } catch { Write-Host " Unable to query sessions" } + + # Group memberships (of the account running the script) + Write-Host " Group memberships:" -ForegroundColor Yellow + try { + $groups = whoami /groups /fo csv 2>&1 | ConvertFrom-Csv -ErrorAction Stop + $currentUser.Groups = @($groups | ForEach-Object { $_.'Group Name' }) + $groups | ForEach-Object { Write-Host " $($_.'Group Name')" } + } catch { + Write-Host " Unable to enumerate groups" -ForegroundColor Yellow + } + + $audit.CurrentUser = $currentUser + Write-Host " [OK] User info collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "CurrentUser"; Error = $_.Exception.Message } +} + +# ===================================================== +# 9. LOCAL USERS +# ===================================================== +Write-Host "" +Write-Host "=== 9. LOCAL USERS ===" -ForegroundColor Cyan +Write-Host " Running: Get-LocalUser" +try { + $localUsers = Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordRequired, PasswordLastSet + $localUsers | Format-Table -AutoSize + $audit.LocalUsers = @($localUsers | ForEach-Object { + [ordered]@{ + Name = $_.Name; Enabled = $_.Enabled + LastLogon = if ($_.LastLogon) { $_.LastLogon.ToString("yyyy-MM-dd") } else { $null } + PasswordRequired = $_.PasswordRequired + PasswordLastSet = if ($_.PasswordLastSet) { $_.PasswordLastSet.ToString("yyyy-MM-dd") } else { $null } + } + }) + Write-Host " [OK] Local users collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message) - trying net user fallback" -ForegroundColor Red + try { net user } catch {} + $audit._errors += @{ Section = "LocalUsers"; Error = $_.Exception.Message } +} + +# ===================================================== +# 10. LOCAL ADMINISTRATORS +# ===================================================== +Write-Host "" +Write-Host "=== 10. LOCAL ADMINISTRATORS ===" -ForegroundColor Cyan +Write-Host " Running: Get-LocalGroupMember Administrators" +try { + $localAdmins = Get-LocalGroupMember Administrators | Select-Object Name, PrincipalSource, ObjectClass + $localAdmins | Format-Table -AutoSize + $audit.LocalAdmins = @($localAdmins | ForEach-Object { + [ordered]@{ Name = $_.Name; Source = "$($_.PrincipalSource)"; Type = "$($_.ObjectClass)" } + }) + Write-Host " [OK] Local admins collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message) - trying net localgroup fallback" -ForegroundColor Red + try { net localgroup Administrators } catch {} + $audit._errors += @{ Section = "LocalAdmins"; Error = $_.Exception.Message } +} + +# ===================================================== +# 11. MAPPED DRIVES +# ===================================================== +Write-Host "" +Write-Host "=== 11. MAPPED DRIVES ===" -ForegroundColor Cyan +Write-Host " Running: Get-PSDrive, net use, HKU registry" +try { + # Current session drives + $mappedDrives = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.DisplayRoot } | + Select-Object Name, @{N='UNC';E={$_.DisplayRoot}} + if ($mappedDrives) { + Write-Host " Current session drives:" -ForegroundColor Yellow + $mappedDrives | Format-Table -AutoSize + } + + # net use + Write-Host " net use output:" -ForegroundColor Yellow + net use 2>&1 | ForEach-Object { Write-Host " $_" } + + # Try to get logged-in user's persistent drives via HKU + $audit.MappedDrives = @() + try { + $loggedOnUser = (Get-CimInstance Win32_ComputerSystem).UserName + if ($loggedOnUser) { + $sid = (New-Object System.Security.Principal.NTAccount($loggedOnUser)).Translate( + [System.Security.Principal.SecurityIdentifier]).Value + try { + New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS -ErrorAction Stop | Out-Null + $userDrives = Get-ItemProperty "HKU:\$sid\Network\*" -ErrorAction SilentlyContinue + if ($userDrives) { + Write-Host " Persistent drives for $loggedOnUser`:" -ForegroundColor Yellow + $userDrives | ForEach-Object { + Write-Host " $($_.PSChildName): -> $($_.RemotePath)" + $audit.MappedDrives += [ordered]@{ Drive = "$($_.PSChildName):"; UNC = $_.RemotePath; User = $loggedOnUser } + } + } + } + finally { + Remove-PSDrive HKU -ErrorAction SilentlyContinue + } + } + } + catch { + Write-Host " Unable to read user registry for mapped drives: $($_.Exception.Message)" -ForegroundColor Yellow + } + + # Also add from PSDrive + if ($mappedDrives) { + foreach ($d in $mappedDrives) { + if (-not ($audit.MappedDrives | Where-Object { $_.Drive -eq "$($d.Name):" })) { + $audit.MappedDrives += [ordered]@{ Drive = "$($d.Name):"; UNC = $d.UNC; User = "CurrentSession" } + } + } + } + + Write-Host " [OK] Mapped drives collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "MappedDrives"; Error = $_.Exception.Message } +} + +# ===================================================== +# 12. WIFI PROFILES +# ===================================================== +Write-Host "" +Write-Host "=== 12. WIFI PROFILES ===" -ForegroundColor Cyan +Write-Host " Running: netsh wlan show profiles, netsh wlan show interfaces" +try { + Write-Host " Saved WiFi profiles:" -ForegroundColor Yellow + $profiles = netsh wlan show profiles 2>&1 + $profiles | ForEach-Object { Write-Host " $_" } + + $profileNames = @($profiles | Select-String "All User Profile\s+:\s+(.+)" | ForEach-Object { $_.Matches.Groups[1].Value.Trim() }) + + Write-Host "" + Write-Host " Current connection:" -ForegroundColor Yellow + $interfaces = netsh wlan show interfaces 2>&1 + $interfaces | ForEach-Object { Write-Host " $_" } + + $connectedSSID = ($interfaces | Select-String "SSID\s+:\s+(.+)" | Select-Object -First 1) + if ($connectedSSID) { $connectedSSID = $connectedSSID.Matches.Groups[1].Value.Trim() } + + $audit.WiFi = [ordered]@{ + SavedProfiles = $profileNames + ConnectedSSID = $connectedSSID + } + Write-Host " [OK] WiFi profiles collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + Write-Host " (No WiFi adapter or service not running)" -ForegroundColor Yellow + $audit._errors += @{ Section = "WiFi"; Error = $_.Exception.Message } +} + +# ===================================================== +# 13. ANTIVIRUS / SECURITY CENTER +# ===================================================== +Write-Host "" +Write-Host "=== 13. ANTIVIRUS / SECURITY CENTER ===" -ForegroundColor Cyan + +Write-Host " Running: Get-CimInstance root/SecurityCenter2 AntiVirusProduct" +try { + $avProducts = Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct -ErrorAction Stop + $audit.Antivirus = @($avProducts | ForEach-Object { + $state = '{0:X6}' -f $_.productState + $enabled = if ($state.Substring(2,2) -eq '10') { $true } else { $false } + $upToDate = if ($state.Substring(4,2) -eq '00') { $true } else { $false } + + Write-Host " $($_.displayName): Enabled=$enabled, UpToDate=$upToDate" + + [ordered]@{ + Name = $_.displayName; Enabled = $enabled; UpToDate = $upToDate + Path = $_.pathToSignedProductExe + } + }) + Write-Host " [OK] AV products collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Antivirus"; Error = $_.Exception.Message } +} + +Write-Host " Running: Get-MpComputerStatus (Windows Defender)" +try { + $defender = Get-MpComputerStatus -ErrorAction Stop + $defenderInfo = [ordered]@{ + AMRunningMode = "$($defender.AMRunningMode)" + AntivirusEnabled = $defender.AntivirusEnabled + RealTimeProtection = $defender.RealTimeProtectionEnabled + BehaviorMonitor = $defender.BehaviorMonitorEnabled + SignatureLastUpdated = if ($defender.AntivirusSignatureLastUpdated) { $defender.AntivirusSignatureLastUpdated.ToString("yyyy-MM-dd HH:mm") } else { $null } + LastQuickScan = if ($defender.QuickScanEndTime) { $defender.QuickScanEndTime.ToString("yyyy-MM-dd HH:mm") } else { $null } + LastFullScan = if ($defender.FullScanEndTime) { $defender.FullScanEndTime.ToString("yyyy-MM-dd HH:mm") } else { $null } + } + + $defenderInfo.GetEnumerator() | ForEach-Object { Write-Host " Defender $($_.Key): $($_.Value)" } + $audit.WindowsDefender = $defenderInfo + Write-Host " [OK] Defender status collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "WindowsDefender"; Error = $_.Exception.Message } +} + +# ===================================================== +# 14. PRINTERS +# ===================================================== +Write-Host "" +Write-Host "=== 14. PRINTERS ===" -ForegroundColor Cyan +Write-Host " Running: Get-Printer, Get-PrinterPort" +try { + $printers = Get-Printer | Select-Object Name, DriverName, PortName, Type, Shared + $printers | Format-Table -AutoSize + + $ports = Get-PrinterPort | Where-Object { $_.Name -like 'TCP_*' -or $_.Name -like 'IP_*' -or $_.PrinterHostAddress } | + Select-Object Name, PrinterHostAddress, PortNumber + if ($ports) { + Write-Host " Network printer ports:" -ForegroundColor DarkGray + $ports | Format-Table -AutoSize + } + + $audit.Printers = @($printers | ForEach-Object { + [ordered]@{ Name = $_.Name; Driver = $_.DriverName; Port = $_.PortName; Type = "$($_.Type)"; Shared = $_.Shared } + }) + $audit.PrinterPorts = @($ports | ForEach-Object { + [ordered]@{ Name = $_.Name; IP = $_.PrinterHostAddress; Port = $_.PortNumber } + }) + Write-Host " [OK] Printers collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "Printers"; Error = $_.Exception.Message } +} + +# ===================================================== +# 15. INSTALLED SOFTWARE (registry-based) +# ===================================================== +Write-Host "" +Write-Host "=== 15. INSTALLED SOFTWARE ===" -ForegroundColor Cyan +Write-Host " Running: Registry query (HKLM Uninstall keys)" +try { + $regPaths = @( + 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', + 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' + ) + $software = Get-ItemProperty $regPaths -ErrorAction SilentlyContinue | + Where-Object { $_.DisplayName } | + Select-Object DisplayName, DisplayVersion, Publisher, InstallDate | + Sort-Object DisplayName -Unique + + $software | Format-Table -AutoSize + + $audit.InstalledSoftware = @($software | ForEach-Object { + [ordered]@{ Name = $_.DisplayName; Version = $_.DisplayVersion; Publisher = $_.Publisher; InstallDate = $_.InstallDate } + }) + Write-Host " [OK] Software collected - $($software.Count) packages" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "InstalledSoftware"; Error = $_.Exception.Message } +} + +# ===================================================== +# 16. STARTUP PROGRAMS +# ===================================================== +Write-Host "" +Write-Host "=== 16. STARTUP PROGRAMS ===" -ForegroundColor Cyan +Write-Host " Running: Get-CimInstance Win32_StartupCommand" +try { + $startup = Get-CimInstance Win32_StartupCommand | Select-Object Name, Command, Location, User + $startup | Format-Table -AutoSize + + $audit.StartupPrograms = @($startup | ForEach-Object { + [ordered]@{ Name = $_.Name; Command = $_.Command; Location = $_.Location; User = $_.User } + }) + Write-Host " [OK] Startup programs collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "StartupPrograms"; Error = $_.Exception.Message } +} + +# ===================================================== +# 17. WINDOWS UPDATES (last 15) +# ===================================================== +Write-Host "" +Write-Host "=== 17. WINDOWS UPDATES (last 15) ===" -ForegroundColor Cyan +Write-Host " Running: Get-HotFix" +try { + $updates = Get-HotFix | Sort-Object InstalledOn -Descending -ErrorAction SilentlyContinue | Select-Object -First 15 | + Select-Object HotFixID, Description, InstalledOn, InstalledBy + $updates | Format-Table -AutoSize + + $audit.WindowsUpdates = @($updates | ForEach-Object { + [ordered]@{ + KB = $_.HotFixID; Description = $_.Description + InstalledOn = if ($_.InstalledOn) { $_.InstalledOn.ToString("yyyy-MM-dd") } else { $null } + } + }) + Write-Host " [OK] Updates collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "WindowsUpdates"; Error = $_.Exception.Message } +} + +# ===================================================== +# 18. WINDOWS UPDATE SETTINGS +# ===================================================== +Write-Host "" +Write-Host "=== 18. WINDOWS UPDATE SETTINGS ===" -ForegroundColor Cyan +Write-Host " Running: Registry check for WSUS/AU settings" +try { + $wuSettings = [ordered]@{} + + $wu = Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -ErrorAction SilentlyContinue + if ($wu) { + $wuSettings.WUServer = $wu.WUServer + $wuSettings.WUStatusServer = $wu.WUStatusServer + Write-Host " WSUS Server: $($wu.WUServer)" + } else { + Write-Host " No WSUS configured (using Windows Update directly)" + $wuSettings.WUServer = "None (Windows Update)" + } + + $au = Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' -ErrorAction SilentlyContinue + if ($au) { + $auOption = switch ($au.AUOptions) { + 2 {"Notify before download"} 3 {"Auto download, notify install"} + 4 {"Auto download, auto install"} 5 {"Allow local admin to choose"} default {"$($au.AUOptions)"} + } + $wuSettings.AutoUpdateOption = $auOption + Write-Host " Auto Update: $auOption" + } + + $audit.WindowsUpdateSettings = $wuSettings + Write-Host " [OK] Update settings collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "WindowsUpdateSettings"; Error = $_.Exception.Message } +} + +# ===================================================== +# 19. NETWORK SHARES +# ===================================================== +Write-Host "" +Write-Host "=== 19. NETWORK SHARES (on this machine) ===" -ForegroundColor Cyan +Write-Host " Running: Get-SmbShare" +try { + $shares = Get-SmbShare | Where-Object { $_.Name -notlike '*$' } + if ($shares) { + $shares | Format-Table Name, Path, Description -AutoSize + $audit.LocalShares = @($shares | ForEach-Object { + [ordered]@{ Name = $_.Name; Path = $_.Path; Description = $_.Description } + }) + } else { + Write-Host " No user shares on this machine" + $audit.LocalShares = @() + } + Write-Host " [OK] Shares collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "LocalShares"; Error = $_.Exception.Message } +} + +# ===================================================== +# 20. POWER SETTINGS +# ===================================================== +Write-Host "" +Write-Host "=== 20. POWER SETTINGS ===" -ForegroundColor Cyan +Write-Host " Running: powercfg /getactivescheme" +try { + $powerScheme = powercfg /getactivescheme 2>&1 + Write-Host " $powerScheme" + $audit.PowerScheme = "$powerScheme".Trim() + Write-Host " [OK] Power settings collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "PowerSettings"; Error = $_.Exception.Message } +} + +# ===================================================== +# 21. EVENT LOG ERRORS (last 14 days) +# ===================================================== +Write-Host "" +Write-Host "=== 21. EVENT LOG ERRORS (last 14 days) ===" -ForegroundColor Cyan +Write-Host " Running: Get-WinEvent - Critical/Error, last 14 days" +try { + $errSince = (Get-Date).AddDays(-14) + $errors = Get-WinEvent -FilterHashtable @{LogName='System'; Level=1,2; StartTime=$errSince} -MaxEvents 30 -ErrorAction Stop | + Select-Object TimeCreated, Id, LevelDisplayName, ProviderName, @{N='Message';E={$_.Message.Substring(0, [Math]::Min(200, $_.Message.Length))}} + Write-Host " $($errors.Count) critical/error events found" + $errors | Format-Table TimeCreated, Id, ProviderName -AutoSize + $audit.EventLogErrors = @($errors | ForEach-Object { + [ordered]@{ Time = $_.TimeCreated.ToString("yyyy-MM-dd HH:mm:ss"); EventId = $_.Id; Level = $_.LevelDisplayName; Source = $_.ProviderName; Message = $_.Message } + }) + Write-Host " [OK] Event log errors collected" -ForegroundColor Green +} +catch { + Write-Host " No critical/error events in System log (last 14 days)" -ForegroundColor Green + $audit.EventLogErrors = @() +} + +# ===================================================== +# 22. EVENT LOG WARNINGS (last 14 days) +# ===================================================== +Write-Host "" +Write-Host "=== 22. EVENT LOG WARNINGS (last 14 days) ===" -ForegroundColor Cyan +Write-Host " Running: Get-WinEvent - Warning level, last 14 days" +try { + $warnSince = (Get-Date).AddDays(-14) + $warnings = Get-WinEvent -FilterHashtable @{LogName='System'; Level=3; StartTime=$warnSince} -MaxEvents 50 -ErrorAction Stop | + Select-Object TimeCreated, Id, ProviderName, @{N='Message';E={$_.Message.Substring(0, [Math]::Min(200, $_.Message.Length))}} + Write-Host " $($warnings.Count) warning events found" + $warnings | Format-Table TimeCreated, Id, ProviderName -AutoSize + $audit.EventLogWarnings = @($warnings | ForEach-Object { + [ordered]@{ Time = $_.TimeCreated.ToString("yyyy-MM-dd HH:mm:ss"); EventId = $_.Id; Source = $_.ProviderName; Message = $_.Message } + }) + Write-Host " [OK] Event log warnings collected" -ForegroundColor Green +} +catch { + Write-Host " No warning events in System log (last 14 days)" -ForegroundColor Green + $audit.EventLogWarnings = @() +} + +# ===================================================== +# 23. EVENT LOG SETTINGS +# ===================================================== +Write-Host "" +Write-Host "=== 23. EVENT LOG SETTINGS ===" -ForegroundColor Cyan +Write-Host " Running: Get-WinEvent -ListLog" +try { + $importantLogs = @('Application', 'Security', 'System', 'Setup', 'Microsoft-Windows-PowerShell/Operational') + $audit.EventLogSettings = @() + foreach ($logName in $importantLogs) { + try { + $log = Get-WinEvent -ListLog $logName -ErrorAction Stop + $logInfo = [ordered]@{ + LogName = $log.LogName + MaxSizeKB = [math]::Round($log.MaximumSizeInBytes / 1KB) + CurrentSizeKB = [math]::Round($log.FileSize / 1KB) + RecordCount = $log.RecordCount + LogMode = "$($log.LogMode)" + IsEnabled = $log.IsEnabled + } + Write-Host " $($log.LogName): Max=$($logInfo.MaxSizeKB)KB, Mode=$($log.LogMode), Records=$($log.RecordCount)" + $audit.EventLogSettings += $logInfo + } + catch { Write-Host " $logName`: not available" -ForegroundColor DarkGray } + } + Write-Host " [OK] Event log settings collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "EventLogSettings"; Error = $_.Exception.Message } +} + +# ===================================================== +# 24. WINDOWS FIREWALL PROFILES +# ===================================================== +Write-Host "" +Write-Host "=== 24. WINDOWS FIREWALL PROFILES ===" -ForegroundColor Cyan +Write-Host " Running: Get-NetFirewallProfile" +try { + $fwProfiles = Get-NetFirewallProfile -ErrorAction Stop + $audit.FirewallProfiles = @($fwProfiles | ForEach-Object { + $p = [ordered]@{ + Profile = "$($_.Name)"; Enabled = $_.Enabled + DefaultInboundAction = "$($_.DefaultInboundAction)" + DefaultOutboundAction = "$($_.DefaultOutboundAction)" + } + Write-Host " $($_.Name): Enabled=$($_.Enabled), Inbound=$($_.DefaultInboundAction), Outbound=$($_.DefaultOutboundAction)" + if (-not $_.Enabled) { + Write-Host " [WARN] $($_.Name) firewall profile is DISABLED" -ForegroundColor Red + } + $p + }) + Write-Host " [OK] Firewall profiles collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "FirewallProfiles"; Error = $_.Exception.Message } +} + +# ===================================================== +# 25. RDP SECURITY SETTINGS +# ===================================================== +Write-Host "" +Write-Host "=== 25. RDP SECURITY SETTINGS ===" -ForegroundColor Cyan +Write-Host " Running: Registry check for RDP settings" +try { + $tsReg = 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' + $rdpReg = 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' + + $rdpEnabled = (Get-ItemProperty $tsReg -ErrorAction Stop).fDenyTSConnections + $nla = (Get-ItemProperty $rdpReg -ErrorAction SilentlyContinue).UserAuthentication + + $rdpSettings = [ordered]@{ + RDPEnabled = ($rdpEnabled -eq 0) + NLARequired = ($nla -eq 1) + } + $rdpSettings.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + + if ($rdpSettings.RDPEnabled -and -not $rdpSettings.NLARequired) { + Write-Host " [WARN] RDP is enabled but NLA is NOT required" -ForegroundColor Yellow + } + + $audit.RDPSettings = $rdpSettings + Write-Host " [OK] RDP settings collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "RDPSettings"; Error = $_.Exception.Message } +} + +# ===================================================== +# 26. UAC SETTINGS +# ===================================================== +Write-Host "" +Write-Host "=== 26. UAC SETTINGS ===" -ForegroundColor Cyan +Write-Host " Running: Registry check for UAC" +try { + $uacReg = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' -ErrorAction Stop + $uacSettings = [ordered]@{ + EnableLUA = $uacReg.EnableLUA + ConsentPromptBehaviorAdmin = switch ($uacReg.ConsentPromptBehaviorAdmin) { + 0 {"Elevate without prompting"} 1 {"Prompt for credentials on secure desktop"} + 2 {"Prompt for consent on secure desktop"} 3 {"Prompt for credentials"} + 4 {"Prompt for consent"} 5 {"Prompt for consent for non-Windows binaries"} default {"Unknown"} + } + } + $uacSettings.GetEnumerator() | ForEach-Object { Write-Host " $($_.Key): $($_.Value)" } + + if ($uacReg.EnableLUA -ne 1) { + Write-Host " [WARN] UAC is DISABLED" -ForegroundColor Red + } + $audit.UACSettings = $uacSettings + Write-Host " [OK] UAC settings collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "UACSettings"; Error = $_.Exception.Message } +} + +# ===================================================== +# 27. SCREEN LOCK / INACTIVITY TIMEOUT +# ===================================================== +Write-Host "" +Write-Host "=== 27. SCREEN LOCK / INACTIVITY TIMEOUT ===" -ForegroundColor Cyan +Write-Host " Running: Registry check for screen lock policy" +try { + $screenLock = [ordered]@{} + + $inactivity = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' -ErrorAction SilentlyContinue).InactivityTimeoutSecs + $screenLock.InactivityTimeoutSecs = $inactivity + if ($inactivity) { + Write-Host " Machine inactivity timeout: $inactivity seconds" + } else { + Write-Host " Machine inactivity timeout: Not configured" + Write-Host " [WARN] No machine inactivity timeout - HIPAA requires automatic session lock" -ForegroundColor Yellow + } + + # Screensaver (may be SYSTEM context - try HKLM policy too) + $ssPolicy = Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Control Panel\Desktop' -ErrorAction SilentlyContinue + if ($ssPolicy) { + $screenLock.ScreenSaverGPO = $true + $screenLock.ScreenSaveTimeout = $ssPolicy.ScreenSaveTimeOut + $screenLock.ScreenSaverSecure = $ssPolicy.ScreenSaverIsSecure + Write-Host " Screensaver GPO: Timeout=$($ssPolicy.ScreenSaveTimeOut)sec, Password=$($ssPolicy.ScreenSaverIsSecure)" + } else { + $screenLock.ScreenSaverGPO = $false + Write-Host " No screensaver GPO detected" + } + + $audit.ScreenLockPolicy = $screenLock + Write-Host " [OK] Screen lock settings collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "ScreenLock"; Error = $_.Exception.Message } +} + +# ===================================================== +# 28. USB STORAGE POLICY +# ===================================================== +Write-Host "" +Write-Host "=== 28. USB STORAGE POLICY ===" -ForegroundColor Cyan +Write-Host " Running: Registry check for USB storage restrictions" +try { + $usbStorage = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\USBSTOR' -ErrorAction Stop).Start + $usbStatus = switch ($usbStorage) { 3 { "Enabled" } 4 { "Disabled" } default { "Unknown ($usbStorage)" } } + Write-Host " USB Storage: $usbStatus" + + $usbGPO = Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\RemovableStorageDevices' -ErrorAction SilentlyContinue + if ($usbGPO) { Write-Host " GPO removable storage restrictions detected" } + + if ($usbStorage -eq 3 -and -not $usbGPO) { + Write-Host " [WARN] USB storage is unrestricted - data exfiltration risk" -ForegroundColor Yellow + } + $audit.USBStoragePolicy = [ordered]@{ Status = $usbStatus; GPORestrictions = ($null -ne $usbGPO) } + Write-Host " [OK] USB storage policy collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "USBStorage"; Error = $_.Exception.Message } +} + +# ===================================================== +# 29. TLS/SSL CONFIGURATION +# ===================================================== +Write-Host "" +Write-Host "=== 29. TLS/SSL CONFIGURATION ===" -ForegroundColor Cyan +Write-Host " Running: Registry check for TLS protocol versions" +try { + $tlsVersions = @('SSL 2.0', 'SSL 3.0', 'TLS 1.0', 'TLS 1.1', 'TLS 1.2', 'TLS 1.3') + $audit.TLSConfig = [ordered]@{} + foreach ($ver in $tlsVersions) { + $clientPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\$ver\Client" + $enabled = $null + if (Test-Path $clientPath) { + $enabled = (Get-ItemProperty $clientPath -ErrorAction SilentlyContinue).Enabled + } + $status = if ($null -eq $enabled) { "OS Default" } elseif ($enabled -eq 0) { "Disabled" } else { "Enabled" } + Write-Host " $ver`: $status" + $audit.TLSConfig[$ver] = $status + } + Write-Host " [OK] TLS config collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "TLSConfig"; Error = $_.Exception.Message } +} + +# ===================================================== +# 30. AUTOPLAY / AUTORUN +# ===================================================== +Write-Host "" +Write-Host "=== 30. AUTOPLAY / AUTORUN ===" -ForegroundColor Cyan +Write-Host " Running: Registry check for AutoPlay/AutoRun" +try { + $autoplay = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer' -ErrorAction SilentlyContinue).NoDriveTypeAutoRun + $autorun = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer' -ErrorAction SilentlyContinue).NoAutorun + + if ($autoplay -eq 255) { + Write-Host " AutoPlay: Disabled for all drives" -ForegroundColor Green + } elseif ($autoplay) { + Write-Host " AutoPlay: Partially restricted (value: $autoplay)" -ForegroundColor Yellow + } else { + Write-Host " AutoPlay: Not restricted by policy" + Write-Host " [WARN] AutoPlay is not disabled - malware risk from USB/optical media" -ForegroundColor Yellow + } + + $audit.AutoPlay = [ordered]@{ NoDriveTypeAutoRun = $autoplay; NoAutorun = $autorun } + Write-Host " [OK] AutoPlay settings collected" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "AutoPlay"; Error = $_.Exception.Message } +} + +# ===================================================== +# 31. SERVICES (with expected state check) +# ===================================================== +Write-Host "" +Write-Host "=== 31. SERVICES (Auto start - stopped unexpectedly) ===" -ForegroundColor Cyan +Write-Host " Running: Get-Service - Auto start services that are stopped" +try { + $stoppedAuto = Get-Service | Where-Object { $_.StartType -eq 'Automatic' -and $_.Status -ne 'Running' } | + Select-Object Name, DisplayName, Status, StartType + if ($stoppedAuto) { + Write-Host " [WARN] $($stoppedAuto.Count) auto-start services are NOT running:" -ForegroundColor Yellow + $stoppedAuto | Format-Table -AutoSize + } else { + Write-Host " All auto-start services are running" -ForegroundColor Green + } + $audit.StoppedAutoServices = @($stoppedAuto | ForEach-Object { + [ordered]@{ Name = $_.Name; DisplayName = $_.DisplayName; Status = "$($_.Status)" } + }) + Write-Host " [OK] Service state check complete" -ForegroundColor Green +} +catch { + Write-Host " [FAIL] $($_.Exception.Message)" -ForegroundColor Red + $audit._errors += @{ Section = "StoppedAutoServices"; Error = $_.Exception.Message } +} + +# ===================================================== +# 32. FAILED LOGON ATTEMPTS (last 7 days) +# ===================================================== +Write-Host "" +Write-Host "=== 32. FAILED LOGON ATTEMPTS (last 7 days) ===" -ForegroundColor Cyan +Write-Host " Running: Get-WinEvent Security log - Event ID 4625" +$failedSince = (Get-Date).AddDays(-7) +$failedLogons = @(Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625; StartTime=$failedSince} -MaxEvents 50 -ErrorAction SilentlyContinue) +if ($failedLogons.Count -gt 0) { + $grouped = $failedLogons | ForEach-Object { + $xml = [xml]$_.ToXml() + $targetUser = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text' + $sourceIP = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'IpAddress' }).'#text' + [PSCustomObject]@{ Time = $_.TimeCreated; User = $targetUser; SourceIP = $sourceIP } + } + $summary = $grouped | Group-Object User | Sort-Object Count -Descending + Write-Host " $($failedLogons.Count) failed logon attempts:" -ForegroundColor Yellow + $summary | ForEach-Object { Write-Host " $($_.Name): $($_.Count) failures" -ForegroundColor Yellow } + $audit.FailedLogons = [ordered]@{ + TotalCount = $failedLogons.Count + ByUser = @($summary | ForEach-Object { [ordered]@{ User = $_.Name; Count = $_.Count } }) + } +} else { + Write-Host " No failed logon attempts" -ForegroundColor Green + $audit.FailedLogons = [ordered]@{ TotalCount = 0 } +} +Write-Host " [OK] Failed logons collected" -ForegroundColor Green + +# ===================================================== +# 33. SECURITY SUMMARY +# ===================================================== +Write-Host "" +Write-Host "=== 33. SECURITY SUMMARY ===" -ForegroundColor Cyan +$audit.SecuritySummary = @() + +# Check each finding +if ($audit.Activation -and $audit.Activation.StatusCode -ne 1) { + $audit.SecuritySummary += "OS is NOT fully licensed" + Write-Host " [WARN] OS is NOT fully licensed" -ForegroundColor Red +} +if ($audit.FirewallProfiles) { + $disabledFW = $audit.FirewallProfiles | Where-Object { -not $_.Enabled } + if ($disabledFW) { + $audit.SecuritySummary += "Windows Firewall disabled on: $($disabledFW.Profile -join ', ')" + Write-Host " [WARN] Windows Firewall disabled on: $($disabledFW.Profile -join ', ')" -ForegroundColor Red + } +} +if ($audit.BitLocker) { + $unencrypted = $audit.BitLocker | Where-Object { $_.Protection -eq 'Off' -or $_.VolumeStatus -eq 'FullyDecrypted' } + if ($unencrypted) { + $audit.SecuritySummary += "BitLocker not enabled on: $($unencrypted.MountPoint -join ', ')" + Write-Host " [WARN] BitLocker not enabled - HIPAA requires encryption at rest" -ForegroundColor Red + } +} +if (-not $audit.ScreenLockPolicy.InactivityTimeoutSecs -and -not $audit.ScreenLockPolicy.ScreenSaverGPO) { + $audit.SecuritySummary += "No screen lock / inactivity timeout configured" + Write-Host " [WARN] No screen lock configured - HIPAA requires automatic session lock" -ForegroundColor Yellow +} +if ($audit.USBStoragePolicy -and $audit.USBStoragePolicy.Status -eq 'Enabled' -and -not $audit.USBStoragePolicy.GPORestrictions) { + $audit.SecuritySummary += "USB storage unrestricted" + Write-Host " [WARN] USB storage unrestricted - data exfiltration risk" -ForegroundColor Yellow +} +if ($audit.UACSettings -and $audit.UACSettings.EnableLUA -ne 1) { + $audit.SecuritySummary += "UAC is disabled" + Write-Host " [WARN] UAC is disabled" -ForegroundColor Red +} +if ($audit.RDPSettings -and $audit.RDPSettings.RDPEnabled -and -not $audit.RDPSettings.NLARequired) { + $audit.SecuritySummary += "RDP enabled without NLA" + Write-Host " [WARN] RDP enabled without NLA" -ForegroundColor Yellow +} +if ($audit.Antivirus) { + $noAV = $audit.Antivirus | Where-Object { -not $_.Enabled } + if ($noAV) { + # Cross-reference with Get-MpComputerStatus - SecurityCenter2 is unreliable for Defender + $defenderActive = $audit.WindowsDefender -and $audit.WindowsDefender.AntivirusEnabled -and $audit.WindowsDefender.RealTimeProtection + $nonDefenderDown = $noAV | Where-Object { $_.Name -ne 'Windows Defender' } + if ($defenderActive -and -not $nonDefenderDown) { + # SecurityCenter2 false positive - Defender is actually running + } else { + $names = if ($nonDefenderDown) { $nonDefenderDown.Name -join ', ' } else { $noAV.Name -join ', ' } + $audit.SecuritySummary += "Antivirus not active: $names" + Write-Host " [WARN] Antivirus not active" -ForegroundColor Red + } + } +} +if ($audit.StoppedAutoServices.Count -gt 0) { + $audit.SecuritySummary += "$($audit.StoppedAutoServices.Count) auto-start services stopped" + Write-Host " [WARN] $($audit.StoppedAutoServices.Count) auto-start services stopped unexpectedly" -ForegroundColor Yellow +} + +if ($audit.SecuritySummary.Count -eq 0) { + Write-Host " No critical security findings" -ForegroundColor Green +} else { + Write-Host " $($audit.SecuritySummary.Count) findings detected - review above" -ForegroundColor Yellow +} + +# ===================================================== +# DONE - SAVE JSON +# ===================================================== +Write-Host "" +Write-Host "=======================================" +Write-Host " WORKSTATION AUDIT COMPLETE" +Write-Host "=======================================" + +$errorCount = $audit._errors.Count +if ($errorCount -gt 0) { + Write-Host "Completed with $errorCount section errors (see _errors in JSON)" -ForegroundColor Yellow +} else { + Write-Host "All sections completed successfully" -ForegroundColor Green +} + +Write-Host "JSON data: $JsonFile" +Write-Host "=======================================" + +# Save JSON +$audit | ConvertTo-Json -Depth 10 | Out-File $JsonFile -Encoding UTF8 diff --git a/projects/msp-tools/utilities/clean_printer_ports.ps1 b/projects/msp-tools/utilities/clean_printer_ports.ps1 new file mode 100644 index 0000000..a93b895 --- /dev/null +++ b/projects/msp-tools/utilities/clean_printer_ports.ps1 @@ -0,0 +1,47 @@ +# ========================================== +# CLEAN STALE PRINTER PORTS +# Removes unused TCP/IP ports, reports dead ones still in use +# ========================================== + +$ports = Get-PrinterPort -ErrorAction SilentlyContinue | + Where-Object { + $_.PortMonitor -eq "TCPMON.DLL" -or + $_.PortMonitor -eq "Standard TCP/IP Port" + } + +$printers = Get-Printer -ErrorAction SilentlyContinue + +$usedPorts = @($printers | ForEach-Object { $_.PortName }) + +$removed = 0 +$inUse = 0 +$dead = 0 + +foreach ($port in $ports) { + $name = $port.Name + $ip = $port.PrinterHostAddress + + if ($usedPorts -contains $name) { + # Port is used by a printer — test if alive + $ping = Test-Connection $ip -Count 1 -Quiet -ErrorAction SilentlyContinue + if (-not $ping) { + Write-Host "[WARN] In use but DEAD: $name ($ip)" -ForegroundColor Yellow + $dead++ + } + $inUse++ + } else { + # Port is orphaned — remove it + try { + Remove-PrinterPort -Name $name -ErrorAction Stop + Write-Host "[REMOVED] $name ($ip)" -ForegroundColor Green + $removed++ + } catch { + Write-Host "[FAIL] $name - $($_.Exception.Message)" -ForegroundColor Red + } + } +} + +Write-Host "" +Write-Host "Ports in use: $inUse" +Write-Host "Dead but in use: $dead (remove printer first)" +Write-Host "Orphans removed: $removed" diff --git a/projects/msp-tools/utilities/screenconnect-toolbox-commands.txt b/projects/msp-tools/utilities/screenconnect-toolbox-commands.txt new file mode 100644 index 0000000..6a0f924 --- /dev/null +++ b/projects/msp-tools/utilities/screenconnect-toolbox-commands.txt @@ -0,0 +1,184 @@ +== Server Audit == + +#!ps +#maxlength=500000 +#timeout=600000 +Set-ExecutionPolicy Bypass -Scope Process -Force +New-Item -Path C:\Temp -ItemType Directory -Force | Out-Null +$u = "https://raw.githubusercontent.com/Howweird/msp-audit-scripts/master/server_audit.ps1" +Invoke-WebRequest -Uri $u -OutFile "C:\Temp\server_audit.ps1" -UseBasicParsing +. C:\Temp\server_audit.ps1 + + +== Workstation Audit == + +#!ps +#maxlength=500000 +#timeout=600000 +Set-ExecutionPolicy Bypass -Scope Process -Force +New-Item -Path C:\Temp -ItemType Directory -Force | Out-Null +$u = "https://raw.githubusercontent.com/Howweird/msp-audit-scripts/master/workstation_audit.ps1" +Invoke-WebRequest -Uri $u -OutFile "C:\Temp\workstation_audit.ps1" -UseBasicParsing +. C:\Temp\workstation_audit.ps1 + + + +== Auto-Patch + Win 11 Upgrade == + +#!ps +#maxlength=10000 +#timeout=300000 +New-Item -Path C:\Temp -ItemType Directory -Force | Out-Null +Install-PackageProvider -Name NuGet -Force -Confirm:$false +Install-Module PSWindowsUpdate -Force -Confirm:$false +$cmd1 = "Import-Module PSWindowsUpdate; Install-WindowsUpdate -AcceptAll -AutoReboot" +$a1 = "-ExecutionPolicy Bypass -Command `"$cmd1`"" +$action1 = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $a1 +$trigger1 = New-ScheduledTaskTrigger -AtStartup +$set = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries +$pri = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest +Register-ScheduledTask -TaskName "AutoPatch" -Action $action1 -Trigger $trigger1 -Settings $set -Principal $pri -Force +$u = "https://go.microsoft.com/fwlink/?linkid=2171764" +$f = "C:\Temp\Win11Upgrade.exe" +Invoke-WebRequest -Uri $u -OutFile $f -UseBasicParsing +reg add HKLM\SYSTEM\Setup\MoSetup /v AllowUpgradesWithUnsupportedTPMOrCPU /t REG_DWORD /d 1 /f | Out-Null +$action2 = New-ScheduledTaskAction -Execute $f -Argument "/quietinstall /skipeula /auto upgrade /copylogs C:\Temp" +$trigger2 = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(5) +Register-ScheduledTask -TaskName "Win11Upgrade" -Action $action2 -Trigger $trigger2 -Settings $set -Principal $pri -Force +Start-ScheduledTask -TaskName "AutoPatch" +Write-Host "[OK] AutoPatch + Win11Upgrade tasks created and started" + + +== Auto-Patch Only (no Win 11 upgrade) == + +#!ps +#maxlength=10000 +#timeout=300000 +Install-PackageProvider -Name NuGet -Force -Confirm:$false +Install-Module PSWindowsUpdate -Force -Confirm:$false +$cmd = "Import-Module PSWindowsUpdate; Install-WindowsUpdate -AcceptAll -AutoReboot" +$a = "-ExecutionPolicy Bypass -Command `"$cmd`"" +$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $a +$trigger = New-ScheduledTaskTrigger -AtStartup +$set = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries +$pri = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest +Register-ScheduledTask -TaskName "AutoPatch" -Action $action -Trigger $trigger -Settings $set -Principal $pri -Force +Start-ScheduledTask -TaskName "AutoPatch" +Write-Host "[OK] AutoPatch task created and started" + + +== Stop Updates + Cleanup at 5AM == + +#!ps +#maxlength=10000 +#timeout=60000 +$cmd = @' +Stop-Process -Name "powershell" -Force -ErrorAction SilentlyContinue +Stop-Process -Name "Windows10UpgraderApp" -Force -ErrorAction SilentlyContinue +Unregister-ScheduledTask -TaskName "AutoPatch" -Confirm:$false -ErrorAction SilentlyContinue +Unregister-ScheduledTask -TaskName "Win11Upgrade" -Confirm:$false -ErrorAction SilentlyContinue +Unregister-ScheduledTask -TaskName "StopUpdates5AM" -Confirm:$false -ErrorAction SilentlyContinue +'@ +$scriptPath = "C:\Temp\StopUpdates.ps1" +$cmd | Out-File $scriptPath -Encoding utf8 +$a = "-ExecutionPolicy Bypass -File `"$scriptPath`"" +$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $a +$tomorrow = (Get-Date).Date.AddDays(1).AddHours(5) +$trigger = New-ScheduledTaskTrigger -Once -At $tomorrow +$set = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries +$pri = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest +Register-ScheduledTask -TaskName "StopUpdates5AM" -Action $action -Trigger $trigger -Settings $set -Principal $pri -Force +Write-Host "[OK] Updates will stop at 5AM tomorrow ($tomorrow)" + + +== Upgrade to Pro == + +#!ps +#maxlength=10000 +#timeout=120000 +$key = "BP2XJ-CGNYY-7K8TK-GWXXJ-HMJ4K" +changepk.exe /ProductKey $key +Write-Host "[OK] Pro key applied to $env:COMPUTERNAME" + + +== Clean Stale Printer Ports == + +#!ps +#maxlength=50000 +#timeout=120000 +$ports = Get-PrinterPort -ErrorAction SilentlyContinue | + Where-Object { + $_.PortMonitor -eq "TCPMON.DLL" -or + $_.PortMonitor -eq "Standard TCP/IP Port" + } +$printers = Get-Printer -ErrorAction SilentlyContinue +$used = @($printers | ForEach-Object { $_.PortName }) +$removed = 0; $deadCount = 0; $inUse = 0 +foreach ($port in $ports) { + $n = $port.Name + $ip = $port.PrinterHostAddress + if ($used -contains $n) { + $ping = Test-Connection $ip -Count 1 -Quiet -ErrorAction SilentlyContinue + if (-not $ping) { + Write-Host "[WARN] In use but DEAD: $n ($ip)" -ForegroundColor Yellow + $deadCount++ + } + $inUse++ + } else { + try { + Remove-PrinterPort -Name $n -ErrorAction Stop + Write-Host "[REMOVED] $n ($ip)" -ForegroundColor Green + $removed++ + } catch { + Write-Host "[FAIL] $n - $($_.Exception.Message)" -ForegroundColor Red + } + } +} +Write-Host "" +Write-Host "Ports in use: $inUse" +Write-Host "Dead but in use: $deadCount (remove printer first)" +Write-Host "Orphans removed: $removed" + + +== Fix Wi-Fi Disconnect on Idle == + +#!ps +#maxlength=100000 +#timeout=60000 + +$wifi = Get-PnpDevice -Class Net | + Where-Object { + $_.Status -eq 'OK' -and + $_.FriendlyName -match + 'Wi-Fi|Wireless|WLAN' -and + $_.FriendlyName -notmatch + 'Virtual|Direct' + } +if ($wifi) { + $id = $wifi.InstanceId + $p = "HKLM:\SYSTEM\CurrentControlSet" + $p += "\Enum\$id" + Set-ItemProperty $p ` + -Name "PnPCapabilities" ` + -Value 24 -Type DWord + $n = $wifi.FriendlyName + Write-Host "Wi-Fi power saving off: $n" +} else { + Write-Host "No Wi-Fi adapter found!" +} +$r = "HKLM:\SYSTEM\CurrentControlSet" +$r += "\Control\Session Manager\Power" +Set-ItemProperty $r ` + -Name "HiberbootEnabled" ` + -Value 0 -Type DWord +Write-Host "Fast Startup disabled." +Write-Host "Reboot needed to apply." + + +== Clear C:\Temp == + +#!ps +#maxlength=10000 +#timeout=30000 +Remove-Item -Path "C:\Temp\*" -Recurse -Force +Write-Host "[OK] C:\Temp cleared" -ForegroundColor Green diff --git a/projects/msp-tools/utilities/win11_upgrade.ps1 b/projects/msp-tools/utilities/win11_upgrade.ps1 new file mode 100644 index 0000000..586794e --- /dev/null +++ b/projects/msp-tools/utilities/win11_upgrade.ps1 @@ -0,0 +1,74 @@ +# ================================ +# Fleet Windows 11 Upgrade Script +# ================================ + +$logFile = "C:\Temp\Win11Upgrade.log" +$dir = "C:\Temp" +New-Item -ItemType Directory -Path $dir -Force | Out-Null + +function Write-Log { + param ([string]$msg) + $time = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") + "$time - $msg" | Out-File -FilePath $logFile -Append -Encoding utf8 + Write-Host $msg +} + +Write-Log "===== Starting Upgrade Script =====" + +# Get OS info (fast) +$os = Get-CimInstance Win32_OperatingSystem +$build = [int]$os.BuildNumber +Write-Log "OS: $($os.Caption)" +Write-Log "Build: $build" + +# Check if upgrade is needed +$targetBuild = 26000 +if ($os.Caption -like "*Windows 11*" -and $build -ge $targetBuild) { + Write-Log "Already on latest Windows 11. Exiting." + exit 0 +} + +Write-Log "Upgrade required. Proceeding..." + +# Enable unsupported hardware bypass (safe if not needed) +Write-Log "Setting compatibility bypass registry key" +reg add HKLM\SYSTEM\Setup\MoSetup /v AllowUpgradesWithUnsupportedTPMOrCPU /t REG_DWORD /d 1 /f | Out-Null + +# Download Installation Assistant +$url = "https://go.microsoft.com/fwlink/?linkid=2171764" +$file = "$dir\Win11Upgrade.exe" + +Write-Log "Downloading Windows 11 Installation Assistant..." +try { + Invoke-WebRequest $url -OutFile $file -UseBasicParsing -ErrorAction Stop +} catch { + Write-Log "Download failed: $($_.Exception.Message)" + exit 1 +} + +if (!(Test-Path $file)) { + Write-Log "Download file not found. Exiting." + exit 1 +} + +Write-Log "Download complete" + +# Kill any previous upgrade processes +Get-Process -ErrorAction SilentlyContinue | + Where-Object { $_.Name -like "*Windows10UpgraderApp*" } | + Stop-Process -Force -ErrorAction SilentlyContinue + +# Run upgrade silently +Write-Log "Starting upgrade process (this will take 30-60+ minutes)..." +$args = "/quietinstall /skipeula /auto upgrade /copylogs $dir" +$proc = Start-Process $file -ArgumentList $args -Wait -PassThru + +Write-Log "Upgrade process exited with code: $($proc.ExitCode)" + +if ($proc.ExitCode -eq 0) { + Write-Log "Upgrade succeeded. Machine will reboot automatically." +} else { + Write-Log "Upgrade may have failed. Check $dir for logs." +} + +Write-Log "===== Script Complete =====" diff --git a/session-logs/2026-04-16-howard-client-docs-import.md b/session-logs/2026-04-16-howard-client-docs-import.md new file mode 100644 index 0000000..9b4c630 --- /dev/null +++ b/session-logs/2026-04-16-howard-client-docs-import.md @@ -0,0 +1,88 @@ +# Session Log: 2026-04-16 — Howard's Clients folder import + +## User +- **User:** Howard Enos (howard) +- **Machine:** ACG-Tech03L +- **Role:** tech + +## Summary + +First session from Howard's ACG-Tech03L workstation. Bootstrapped identity, ran /sync to pull Mike's multi-user setup work, then used the new `/import` command to ingest `C:\Users\howar\Clients\` (Howard's personal MSP documentation folder) into the shared ClaudeTools repo. + +## Bootstrap + +Created `.claude/identity.json` for Howard (howard, tech, ACG-Tech03L). Set local git config to `Howard Enos `. Set origin remote to Howard's Gitea account: `https://howard@git.azcomputerguru.com/azcomputerguru/claudetools.git`. Hostname already present in `users.json` known_machines list — no update needed. + +## Sync + +Pulled 2 commits from Mike: +- `6eaba02` — Session log: multi-user setup, audit fixes, /import command, Howard onboarding +- `f5acf9f` — Add /import command — generic folder ingestion with smart classification + +Fast-forward merge. Nothing to push. + +## Import + +Source: `C:\Users\howar\Clients\` (160 files, 1.6 MB, 8 client folders + scripts + template). + +### Client documentation copied + +| Source | Destination | Files | +|---|---|---| +| `Anaise/` | `clients/anaise/docs/` | 13 (NEW client) | +| `Cascades/` | `clients/cascades-tucson/docs/` | 47 (merge) | +| `dataforth/` | `clients/dataforth/docs/` | 18 (merge into existing) | +| `IMC/` | `clients/instrumental-music-center/docs/` | 14 (merge into existing) | +| `Khalsa/` | `clients/khalsa/docs/` | 22 (NEW client) | +| `Kittle/` | `clients/kittle/docs/` | 16 (NEW client) | +| `lens/` | `clients/lens-auto-brokerage/docs/` | 3 (NEW client; name matches vault) | +| `_client_template/` | `clients/_client_template/` | 13 (scaffold) | + +### Scripts copied + +- `scripts/msp-audit-scripts/` → `projects/msp-tools/msp-audit-scripts/` (server_audit.ps1, workstation_audit.ps1, README.md) +- `scripts/clean_printer_ports.ps1`, `win11_upgrade.ps1`, `screenconnect-toolbox-commands.txt` → `projects/msp-tools/utilities/` +- `scripts/server_audit.ps1` and `scripts/workstation_audit.ps1` at source root were **skipped** — identical duplicates of msp-audit-scripts versions (verified via `diff`). + +### Credential extraction + +Scanned all 160 files for inline passwords, API keys, SSH keys, tokens. Found one real credential: + +- **Anaise/overview.md line 20**: local workstation password `Shinseki1` for `DESKTOP-O8GF4SD / david`. + - Created SOPS vault entry at `C:/vault/clients/anaise/desktop-o8gf4sd.sops.yaml` (kind: workstation, username: david, password: Shinseki1). + - Verified decryption with `sops -d`. + - Redacted plaintext password from copied `clients/anaise/docs/overview.md` — replaced with vault reference pattern. + +Everything else flagged by the grep scan was descriptive text ("Password Policy", "krbtgt password age"), script parameters (no hardcoded defaults), or table headers — no real credentials leaked. + +Verified with `grep -rn "Shinseki" C:/claudetools/` — zero matches. Password is only in the encrypted vault. + +### Skipped + +- `Cascades/.claude/settings.local.json` — per-machine Claude config, not portable +- `CLAUDE.md` (root of source) — Howard's local CLAUDE.md for his personal Clients folder; claudetools has its own project CLAUDE.md +- Two duplicate script files (see above) + +## Vault path note + +CLAUDE.md references `D:\vault\` (Mike's setup). On Howard's ACG-Tech03L the vault is at `C:\vault\`. The `vault.sh` script works the same way. `yq` is not installed on this machine, so `vault.sh get-field` fails — use `sops -d ` directly or install yq later. + +## Memory updates + +Added two reference memories: +- `reference_client_docs_structure.md` — explains `clients//docs/` layout, subfolder conventions, template location, active client list. +- `reference_msp_audit_scripts.md` — script locations, GitHub fetch URLs, ScreenConnect Toolbox 80-char rule. + +## What's next + +- Howard should skim `ONBOARDING.md` if he hasn't already. +- When syncing from other machines, the new `clients/` subfolders and `projects/msp-tools/` scripts will propagate. +- Consider whether `clients/dataforth/docs/manufacturing.md` (new) should link to or merge with existing Dataforth project context files. +- `yq` install on ACG-Tech03L would make `vault.sh get-field` work correctly. + +## Files changed + +- Added 161 files (client docs + scripts + 2 memory files) +- Modified `clients/anaise/docs/overview.md` (password redacted) +- Modified `.claude/memory/MEMORY.md` (index updated) +- Created `C:/vault/clients/anaise/desktop-o8gf4sd.sops.yaml` (separate vault repo)