Files
claudetools/clients/cascades-tucson/docs/migration/scripts/phase2-ad-setup.ps1
Howard Enos 8d975c1b44 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>
2026-04-16 19:43:58 -07:00

317 lines
12 KiB
PowerShell

#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