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:
@@ -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
|
||||
Reference in New Issue
Block a user