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/<name>/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) <noreply@anthropic.com>
This commit is contained in:
2026-04-16 19:43:58 -07:00
parent 251edef420
commit 121ba75fda
160 changed files with 16002 additions and 0 deletions

View File

@@ -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 |

View File

@@ -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.

View File

@@ -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.50239)
- [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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 "<same app list>"
#
# 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

View File

@@ -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)"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)"

View File

@@ -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 <password>" -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 <password>" -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 | |

View File

@@ -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.

View File

@@ -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