<# .SYNOPSIS Network Drive Manager - Backup, validate credentials, and reconnect SMB shares .DESCRIPTION This script helps users manage network drives connected to legacy domain servers. It backs up current mappings, validates credentials, and reconnects drives after password changes. .NOTES Author: Cameron Proudlock / GE Aerospace Version: 1.0 Requires: PowerShell 5.1+ #> #Requires -Version 5.1 # ============================================================================ # CONFIGURATION - Modify these variables as needed # ============================================================================ # Legacy domain servers to manage (add/remove as needed) $LegacyServers = @( "tsgwp00525.rd.ds.ge.com" # Add additional servers here: # "server2.rd.ds.ge.com" # "server3.rd.ds.ge.com" ) # Legacy domain suffix $LegacyDomain = "logon.ds.ge.com" # Password reset URL $PasswordResetURL = "https://mypassword.ge.com" # Backup file location (OneDrive Documents) $OneDrivePath = [Environment]::GetFolderPath('MyDocuments') # Falls back if OneDrive not configured $BackupFileName = "NetworkDriveMappings.json" $BackupFilePath = Join-Path -Path $OneDrivePath -ChildPath $BackupFileName # ============================================================================ # FUNCTIONS # ============================================================================ function Get-LegacyUsername { <# .SYNOPSIS Derives the legacy username from the current Windows profile #> $currentUser = $env:USERNAME return "$currentUser@$LegacyDomain" } function Invoke-PasswordResetFlow { <# .SYNOPSIS Guides user through password reset and re-prompts for new credentials .OUTPUTS Returns new SecureString password if successful, $null if cancelled #> Write-Host "" Write-Host "============================================================" -ForegroundColor Yellow Write-Host " Password Reset Required" -ForegroundColor Yellow Write-Host "============================================================" -ForegroundColor Yellow Write-Host "" Write-Host " Your legacy domain password needs to be reset." -ForegroundColor White Write-Host "" Write-Host " Steps to complete:" -ForegroundColor Cyan Write-Host " 1. Click 'Y' below to open the password reset portal" Write-Host " 2. Reset your password at $PasswordResetURL" Write-Host " 3. Return here and type 'DONE'" Write-Host " 4. Wait for the 10-minute sync timer" Write-Host " 5. Enter your NEW password to continue" Write-Host "" Write-Host " NOTE: Password changes can take up to 10 minutes to sync" -ForegroundColor Gray Write-Host " from Azure AD to the legacy domain. The script will wait" -ForegroundColor Gray Write-Host " automatically after you confirm the reset." -ForegroundColor Gray Write-Host "" $openPortal = Read-Host "Open password reset portal now? (Y/n)" if ($openPortal.ToLower() -ne 'n') { Start-Process $PasswordResetURL Write-Host "" Write-Host " Browser opened to password reset portal..." -ForegroundColor Gray } Write-Host "" Write-Host "============================================================" -ForegroundColor Yellow Write-Host "" Write-Host " [ ] I have reset my password at $PasswordResetURL" -ForegroundColor White Write-Host "" $confirmed = $false while (-not $confirmed) { $confirmation = Read-Host "Type 'DONE' when you have reset your password (or 'CANCEL' to abort)" switch ($confirmation.ToUpper()) { "DONE" { $confirmed = $true } "CANCEL" { Write-Host "" Write-Host " Password reset cancelled." -ForegroundColor Yellow return $null } default { Write-Host " Please type 'DONE' or 'CANCEL'" -ForegroundColor Yellow } } } Write-Host "" Write-Host " [X] Password reset confirmed!" -ForegroundColor Green Write-Host "" Write-Host "============================================================" -ForegroundColor Cyan Write-Host " Waiting for AD Sync (~10 minutes)" -ForegroundColor Cyan Write-Host "============================================================" -ForegroundColor Cyan Write-Host "" Write-Host " Your new password needs time to sync to the legacy domain." -ForegroundColor White Write-Host " Please wait for the timer to complete, or press 'S' to skip" -ForegroundColor White Write-Host " if you've already waited." -ForegroundColor White Write-Host "" # 10 minute countdown timer $totalSeconds = 600 # 10 minutes $startTime = Get-Date $endTime = $startTime.AddSeconds($totalSeconds) $skipped = $false # Check if a key is available to read (non-blocking) $host.UI.RawUI.FlushInputBuffer() while ((Get-Date) -lt $endTime -and -not $skipped) { $remaining = $endTime - (Get-Date) $minutes = [math]::Floor($remaining.TotalMinutes) $seconds = $remaining.Seconds # Create progress bar $elapsed = ((Get-Date) - $startTime).TotalSeconds $percentComplete = [math]::Min(100, [math]::Floor(($elapsed / $totalSeconds) * 100)) $barLength = 40 $filledLength = [math]::Floor($barLength * $percentComplete / 100) $bar = ("█" * $filledLength) + ("░" * ($barLength - $filledLength)) # Write progress on same line Write-Host "`r [$bar] $($minutes.ToString('00')):$($seconds.ToString('00')) remaining (Press 'S' to skip) " -NoNewline -ForegroundColor Cyan # Check for keypress (non-blocking) if ([Console]::KeyAvailable) { $key = [Console]::ReadKey($true) if ($key.Key -eq 'S') { $skipped = $true Write-Host "" Write-Host "" Write-Host " Timer skipped by user." -ForegroundColor Yellow } } Start-Sleep -Milliseconds 500 } if (-not $skipped) { Write-Host "" Write-Host "" Write-Host " [OK] Sync wait complete!" -ForegroundColor Green } Write-Host "" Write-Host "============================================================" -ForegroundColor Cyan Write-Host "" Write-Host " Please enter your NEW password below." -ForegroundColor White Write-Host "" $newPassword = Read-Host " Enter your NEW legacy password" -AsSecureString if ($newPassword.Length -eq 0) { Write-Host "" Write-Host " [ERROR] Password cannot be empty." -ForegroundColor Red return $null } # Confirm password Write-Host "" $confirmPassword = Read-Host " Confirm your NEW legacy password" -AsSecureString # Compare the two passwords $BSTR1 = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($newPassword) $BSTR2 = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($confirmPassword) $plain1 = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR1) $plain2 = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR2) [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR1) [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR2) if ($plain1 -ne $plain2) { Write-Host "" Write-Host " [ERROR] Passwords do not match. Please try again." -ForegroundColor Red $plain1 = $null $plain2 = $null return $null } $plain1 = $null $plain2 = $null Write-Host "" Write-Host " [OK] Password confirmed!" -ForegroundColor Green return $newPassword } function Invoke-ZscalerReauthPrompt { <# .SYNOPSIS Prompts user to re-authenticate to Zscaler and retry .OUTPUTS Returns $true if user wants to retry, $false to cancel #> Write-Host "" Write-Host "============================================================" -ForegroundColor Yellow Write-Host " Zscaler Re-Authentication Required" -ForegroundColor Yellow Write-Host "============================================================" -ForegroundColor Yellow Write-Host "" Write-Host " The network path could not be found. This typically means" -ForegroundColor White Write-Host " your Zscaler connection needs to be re-authenticated." -ForegroundColor White Write-Host "" Write-Host " Steps to fix:" -ForegroundColor Cyan Write-Host " 1. Look for the Zscaler icon in your system tray" Write-Host " 2. Right-click and select 'Sign In' or 'Authenticate'" Write-Host " 3. Complete the authentication process" Write-Host " 4. Return here and select 'Retry'" Write-Host "" Write-Host "============================================================" -ForegroundColor Yellow Write-Host "" $retry = Read-Host "Type 'RETRY' after re-authenticating to Zscaler (or 'CANCEL' to abort)" return ($retry.ToUpper() -eq "RETRY") } function Invoke-ErrorActionHandler { <# .SYNOPSIS Handles specific error actions and returns whether to retry .OUTPUTS Hashtable with: - Retry: $true if operation should be retried - NewPassword: SecureString if password was reset, $null otherwise - Cancel: $true if user wants to cancel entirely #> param( [Parameter(Mandatory)] [string]$Action, [Parameter(Mandatory)] [int]$ErrorCode, [string]$AdditionalInfo = "" ) $result = @{ Retry = $false NewPassword = $null Cancel = $false } switch ($Action) { "contact_manager" { Write-Host "" Write-Host "============================================================" -ForegroundColor Red Write-Host " Access Denied (Error 5)" -ForegroundColor Red Write-Host "============================================================" -ForegroundColor Red Write-Host "" Write-Host " You do not have permission to access this share." -ForegroundColor White Write-Host "" Write-Host " ACTION REQUIRED:" -ForegroundColor Yellow Write-Host " Contact your manager to request access to this resource." Write-Host "" Write-Host "============================================================" -ForegroundColor Red Write-Host "" Read-Host "Press Enter to continue..." $result.Cancel = $true } "zscaler_reauth" { $retry = Invoke-ZscalerReauthPrompt $result.Retry = $retry $result.Cancel = (-not $retry) } "retry_or_zscaler" { Write-Host "" Write-Host "============================================================" -ForegroundColor Yellow Write-Host " Connection Dropped (Error 64)" -ForegroundColor Yellow Write-Host "============================================================" -ForegroundColor Yellow Write-Host "" Write-Host " The network connection was unexpectedly terminated." -ForegroundColor White Write-Host "" Write-Host " This could be caused by:" -ForegroundColor Cyan Write-Host " - Zscaler connection timeout (most common)" Write-Host " - Network instability" Write-Host " - Server-side connection reset" Write-Host "" Write-Host " Try re-authenticating to Zscaler first." -ForegroundColor Yellow Write-Host "" Write-Host "============================================================" -ForegroundColor Yellow Write-Host "" $retry = Invoke-ZscalerReauthPrompt $result.Retry = $retry $result.Cancel = (-not $retry) } "verify_path" { Write-Host "" Write-Host "============================================================" -ForegroundColor Yellow Write-Host " Invalid Share Path (Error 67)" -ForegroundColor Yellow Write-Host "============================================================" -ForegroundColor Yellow Write-Host "" Write-Host " The share path appears to be invalid or malformed." -ForegroundColor White if ($AdditionalInfo) { Write-Host " Path: $AdditionalInfo" -ForegroundColor Gray } Write-Host "" Write-Host " Possible causes:" -ForegroundColor Cyan Write-Host " - The share may have been renamed or removed" Write-Host " - The path may be incorrectly formatted" Write-Host " - Typo in the server or share name" Write-Host "" Write-Host " ACTION REQUIRED:" -ForegroundColor Yellow Write-Host " Verify the share path is correct, or contact IT support" Write-Host " if you believe this share should exist." Write-Host "" Write-Host "============================================================" -ForegroundColor Yellow Write-Host "" Read-Host "Press Enter to continue..." # Don't retry automatically - path needs manual verification $result.Cancel = $true } "reassign_drive_letter" { # This is handled automatically - we'll disconnect and reconnect Write-Host "" Write-Host " [INFO] Drive letter conflict detected - will reassign automatically" -ForegroundColor Cyan $result.Retry = $true } "reset_password_flow" { $newPassword = Invoke-PasswordResetFlow if ($null -ne $newPassword) { $result.Retry = $true $result.NewPassword = $newPassword } else { $result.Cancel = $true } } "clear_connections" { Write-Host "" Write-Host "============================================================" -ForegroundColor Yellow Write-Host " Multiple Connections Detected (Error 1219)" -ForegroundColor Yellow Write-Host "============================================================" -ForegroundColor Yellow Write-Host "" Write-Host " You have existing connections to this server with different" -ForegroundColor White Write-Host " credentials. This often happens when:" -ForegroundColor White Write-Host " - You have mapped drives from a previous session" -ForegroundColor Gray Write-Host " - Windows cached old credentials" -ForegroundColor Gray Write-Host " - Another application connected to the server" -ForegroundColor Gray Write-Host "" Write-Host " Clearing all connections to legacy servers..." -ForegroundColor Cyan # Clear all connections to legacy servers foreach ($server in $LegacyServers) { # Clear IPC$ connection $null = net use "\\$server\IPC$" /delete /y 2>&1 # Clear server root $null = net use "\\$server" /delete /y 2>&1 } # Clear any mapped drives to legacy servers $netUseList = net use 2>&1 foreach ($server in $LegacyServers) { $serverConnections = $netUseList | Select-String -Pattern $server foreach ($conn in $serverConnections) { $connString = $conn.ToString() if ($connString -match '^(OK|Disconnected|Unavailable)?\s*([A-Z]:)') { $driveLetter = $Matches[2] Write-Host " Disconnecting $driveLetter..." -NoNewline $null = net use $driveLetter /delete /y 2>&1 Write-Host " Done" -ForegroundColor Green } } } # Also clear cached credentials foreach ($server in $LegacyServers) { $null = cmdkey /delete:$server 2>&1 $null = cmdkey /delete:"Domain:target=$server" 2>&1 } Write-Host "" Write-Host " [OK] Connections cleared. Will retry..." -ForegroundColor Green Write-Host "" Start-Sleep -Seconds 2 $result.Retry = $true } "contact_helpdesk" { Write-Host "" Write-Host "============================================================" -ForegroundColor Red Write-Host " Account Issue Detected" -ForegroundColor Red Write-Host "============================================================" -ForegroundColor Red Write-Host "" Write-Host " Your account has a restriction that prevents connection." -ForegroundColor White Write-Host " Error Code: $ErrorCode" -ForegroundColor Gray Write-Host "" Write-Host " ACTION REQUIRED:" -ForegroundColor Yellow Write-Host " Please contact the IT Helpdesk for assistance." Write-Host "" Write-Host "============================================================" -ForegroundColor Red Write-Host "" Read-Host "Press Enter to continue..." $result.Cancel = $true } "unknown_error" { Write-Host "" Write-Host "============================================================" -ForegroundColor Red Write-Host " Unknown Error (Code: $ErrorCode)" -ForegroundColor Red Write-Host "============================================================" -ForegroundColor Red Write-Host "" Write-Host " An unexpected error occurred." -ForegroundColor White if ($AdditionalInfo) { Write-Host " Details: $AdditionalInfo" -ForegroundColor Gray } Write-Host "" Write-Host " Options:" -ForegroundColor Cyan Write-Host " - Try re-authenticating to Zscaler" Write-Host " - Check your network connection" Write-Host " - Contact IT Helpdesk if the issue persists" Write-Host "" Write-Host "============================================================" -ForegroundColor Red Write-Host "" $choice = Read-Host "Type 'RETRY' to try again, or press Enter to cancel" $result.Retry = ($choice.ToUpper() -eq "RETRY") $result.Cancel = (-not $result.Retry) } default { $result.Cancel = $true } } return $result } function Get-CurrentDriveMappings { <# .SYNOPSIS Gets all current network drive mappings and filters for legacy servers #> param( [switch]$AllDrives, [string[]]$ServerFilter = $LegacyServers ) $mappings = @() # Get all network drives $netDrives = Get-WmiObject -Class Win32_MappedLogicalDisk -ErrorAction SilentlyContinue foreach ($drive in $netDrives) { $mapping = [PSCustomObject]@{ DriveLetter = $drive.DeviceID RemotePath = $drive.ProviderName ServerName = if ($drive.ProviderName -match '\\\\([^\\]+)\\') { $Matches[1] } else { $null } ShareName = if ($drive.ProviderName -match '\\\\[^\\]+\\(.+)$') { $Matches[1] } else { $null } IsLegacy = $false } # Check if this is a legacy server drive foreach ($server in $ServerFilter) { if ($mapping.ServerName -like "*$server*" -or $mapping.RemotePath -like "*$server*") { $mapping.IsLegacy = $true break } } if ($AllDrives -or $mapping.IsLegacy) { $mappings += $mapping } } return $mappings } function Backup-DriveMappings { <# .SYNOPSIS Saves current drive mappings to a JSON file in OneDrive #> param( [string]$FilePath = $BackupFilePath ) $mappings = Get-CurrentDriveMappings -AllDrives $backupData = [PSCustomObject]@{ BackupDate = (Get-Date -Format "yyyy-MM-dd HH:mm:ss") ComputerName = $env:COMPUTERNAME Username = $env:USERNAME Mappings = $mappings } try { $backupData | ConvertTo-Json -Depth 3 | Out-File -FilePath $FilePath -Encoding UTF8 -Force Write-Host "[OK] " -ForegroundColor Green -NoNewline Write-Host "Backup saved to: $FilePath" return $true } catch { Write-Host "[ERROR] " -ForegroundColor Red -NoNewline Write-Host "Failed to save backup: $_" return $false } } function Get-SavedMappings { <# .SYNOPSIS Loads previously saved drive mappings from backup file #> param( [string]$FilePath = $BackupFilePath ) if (-not (Test-Path $FilePath)) { Write-Host "[WARN] " -ForegroundColor Yellow -NoNewline Write-Host "No backup file found at: $FilePath" return $null } try { $backupData = Get-Content -Path $FilePath -Raw | ConvertFrom-Json Write-Host "[OK] " -ForegroundColor Green -NoNewline Write-Host "Loaded backup from: $($backupData.BackupDate)" return $backupData } catch { Write-Host "[ERROR] " -ForegroundColor Red -NoNewline Write-Host "Failed to read backup: $_" return $null } } function Test-SMBCredentials { <# .SYNOPSIS Tests credentials against a legacy SMB server .OUTPUTS Returns a hashtable with Success, ErrorCode, and Message #> param( [Parameter(Mandatory)] [string]$Server, [Parameter(Mandatory)] [string]$Username, [Parameter(Mandatory)] [SecureString]$Password ) $result = @{ Success = $false ErrorCode = 0 Message = "" Action = "" } # Convert SecureString to plain text for net use (unfortunately required) $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password) $PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) # Try to connect to the server's IPC$ share (doesn't require a specific share to exist) $testPath = "\\$Server\IPC$" # First, remove ALL existing connections to this server (not just IPC$) $null = net use $testPath /delete /y 2>&1 $null = net use "\\$Server" /delete /y 2>&1 # Also try to clear any other connections to this server $existingConnections = net use 2>&1 | Select-String -Pattern $Server foreach ($conn in $existingConnections) { if ($conn -match '([A-Z]:)\s+\\\\') { $null = net use $Matches[1] /delete /y 2>&1 } } # Attempt connection $netUseOutput = net use $testPath /user:$Username $PlainPassword 2>&1 $exitCode = $LASTEXITCODE # Parse the actual system error code from the output message # net use often returns exit code 2 but the real error is in the message $parsedErrorCode = $exitCode $outputString = $netUseOutput | Out-String if ($outputString -match 'System error (\d+)') { $parsedErrorCode = [int]$Matches[1] } elseif ($outputString -match 'error (\d+)') { $parsedErrorCode = [int]$Matches[1] } # Clean up the test connection $null = net use $testPath /delete /y 2>&1 # Clear password from memory $PlainPassword = $null # Interpret the result using parsed error code switch ($parsedErrorCode) { 0 { $result.Success = $true $result.Message = "Credentials validated successfully" $result.Action = "none" } 5 { $result.ErrorCode = 5 $result.Message = "Access denied - you may not have permission to this share" $result.Action = "contact_manager" } 53 { $result.ErrorCode = 53 $result.Message = "Network path not found - Zscaler may need re-authentication" $result.Action = "zscaler_reauth" } 64 { $result.ErrorCode = 64 $result.Message = "Network connection was dropped unexpectedly" $result.Action = "retry_or_zscaler" } 67 { $result.ErrorCode = 67 $result.Message = "Network name cannot be found - invalid share path" $result.Action = "verify_path" } 85 { $result.ErrorCode = 85 $result.Message = "Drive letter is already in use" $result.Action = "reassign_drive_letter" } 86 { $result.ErrorCode = 86 $result.Message = "Invalid password" $result.Action = "reset_password_flow" } 1219 { $result.ErrorCode = 1219 $result.Message = "Multiple connections to server exist with different credentials" $result.Action = "clear_connections" } 1326 { $result.ErrorCode = 1326 $result.Message = "Logon failure - unknown username or bad password" $result.Action = "reset_password_flow" } 1327 { $result.ErrorCode = 1327 $result.Message = "Account restriction - policy is preventing logon" $result.Action = "contact_helpdesk" } 1330 { $result.ErrorCode = 1330 $result.Message = "Password has expired" $result.Action = "reset_password_flow" } 1331 { $result.ErrorCode = 1331 $result.Message = "Account is disabled" $result.Action = "contact_helpdesk" } 1909 { $result.ErrorCode = 1909 $result.Message = "Account is locked out" $result.Action = "reset_password_flow" } default { $result.ErrorCode = $parsedErrorCode $result.Message = "Unknown error (code: $parsedErrorCode) - $outputString" $result.Action = "unknown_error" } } return $result } function Remove-LegacyDriveMappings { <# .SYNOPSIS Removes all network drive mappings for legacy servers #> param( [string[]]$Servers = $LegacyServers ) $currentMappings = Get-CurrentDriveMappings $removed = @() foreach ($mapping in $currentMappings) { if ($mapping.IsLegacy) { Write-Host " Removing $($mapping.DriveLetter) ($($mapping.RemotePath))... " -NoNewline $result = net use $mapping.DriveLetter /delete /y 2>&1 if ($LASTEXITCODE -eq 0) { Write-Host "OK" -ForegroundColor Green $removed += $mapping } else { Write-Host "Failed" -ForegroundColor Red } } } # Also clear any cached connections to these servers foreach ($server in $Servers) { $null = net use "\\$server\IPC$" /delete /y 2>&1 } return $removed } function Remove-WindowsCredentials { <# .SYNOPSIS Removes stored Windows credentials for legacy servers #> param( [string[]]$Servers = $LegacyServers ) foreach ($server in $Servers) { Write-Host " Clearing credentials for $server... " -NoNewline # Try different credential target formats $targets = @( $server, "Domain:target=$server", "LegacyGeneric:target=$server", "*$server*" ) $cleared = $false foreach ($target in $targets) { $result = cmdkey /delete:$target 2>&1 if ($LASTEXITCODE -eq 0) { $cleared = $true } } if ($cleared) { Write-Host "OK" -ForegroundColor Green } else { Write-Host "Not found/Already cleared" -ForegroundColor Yellow } } } function Add-DriveMappings { <# .SYNOPSIS Reconnects drive mappings with new credentials Handles error 85 (drive letter in use) automatically #> param( [Parameter(Mandatory)] [PSCustomObject[]]$Mappings, [Parameter(Mandatory)] [string]$Username, [Parameter(Mandatory)] [SecureString]$Password ) # Convert SecureString to plain text $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password) $PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) $results = @() foreach ($mapping in $Mappings) { if (-not $mapping.IsLegacy) { continue } Write-Host " Mapping $($mapping.DriveLetter) to $($mapping.RemotePath)... " -NoNewline # First attempt $netUseOutput = net use $mapping.DriveLetter $mapping.RemotePath /user:$Username $PlainPassword /persistent:yes 2>&1 $exitCode = $LASTEXITCODE # Handle error 85 - drive letter already in use if ($exitCode -eq 85) { Write-Host "in use, reassigning... " -NoNewline -ForegroundColor Yellow # Try to disconnect the existing mapping $null = net use $mapping.DriveLetter /delete /y 2>&1 Start-Sleep -Milliseconds 500 # Retry the mapping $netUseOutput = net use $mapping.DriveLetter $mapping.RemotePath /user:$Username $PlainPassword /persistent:yes 2>&1 $exitCode = $LASTEXITCODE } $mapResult = [PSCustomObject]@{ DriveLetter = $mapping.DriveLetter RemotePath = $mapping.RemotePath Success = ($exitCode -eq 0) ErrorCode = $exitCode Message = $netUseOutput } if ($exitCode -eq 0) { Write-Host "OK" -ForegroundColor Green } else { Write-Host "Failed (Error: $exitCode)" -ForegroundColor Red } $results += $mapResult } # Clear password from memory $PlainPassword = $null return $results } function Show-Menu { <# .SYNOPSIS Displays the main menu #> Clear-Host Write-Host "============================================================" -ForegroundColor Cyan Write-Host " GE Aerospace - Network Drive Manager" -ForegroundColor Cyan Write-Host "============================================================" -ForegroundColor Cyan Write-Host "" Write-Host " Current User: $env:USERNAME" -ForegroundColor Gray Write-Host " Legacy User: $(Get-LegacyUsername)" -ForegroundColor Gray Write-Host " Backup File: $BackupFilePath" -ForegroundColor Gray Write-Host "" Write-Host "============================================================" -ForegroundColor Cyan Write-Host "" Write-Host " 1. " -NoNewline -ForegroundColor Yellow Write-Host "View current network drive mappings" Write-Host " 2. " -NoNewline -ForegroundColor Yellow Write-Host "Backup current mappings to OneDrive" Write-Host " 3. " -NoNewline -ForegroundColor Yellow Write-Host "Test legacy credentials" Write-Host " 4. " -NoNewline -ForegroundColor Yellow Write-Host "Full reset - Disconnect, clear creds, and reconnect drives" Write-Host " 5. " -NoNewline -ForegroundColor Yellow Write-Host "Restore drives from backup" Write-Host " 6. " -NoNewline -ForegroundColor Yellow Write-Host "Open password reset portal" Write-Host "" Write-Host " Q. " -NoNewline -ForegroundColor Red Write-Host "Quit" Write-Host "" Write-Host "============================================================" -ForegroundColor Cyan } function Invoke-FullReset { <# .SYNOPSIS Performs full credential reset and drive reconnection With intelligent error handling and retry logic #> Write-Host "" Write-Host "============================================================" -ForegroundColor Cyan Write-Host " Full Credential Reset & Drive Reconnection" -ForegroundColor Cyan Write-Host "============================================================" -ForegroundColor Cyan Write-Host "" # Step 1: Backup current mappings Write-Host "[Step 1/5] Backing up current drive mappings..." -ForegroundColor Yellow $backupSuccess = Backup-DriveMappings if (-not $backupSuccess) { Write-Host "" $continue = Read-Host "Backup failed. Continue anyway? (y/N)" if ($continue -ne 'y') { return } } # Load the backup we just made (or existing one) $savedMappings = Get-SavedMappings $legacyMappings = $savedMappings.Mappings | Where-Object { $_.IsLegacy -eq $true } if (-not $legacyMappings -or $legacyMappings.Count -eq 0) { Write-Host "" Write-Host "[WARN] " -ForegroundColor Yellow -NoNewline Write-Host "No legacy drive mappings found to restore." Write-Host " Press Enter to return to menu..." Read-Host return } Write-Host "" Write-Host "Found $($legacyMappings.Count) legacy drive(s) to manage:" -ForegroundColor Cyan foreach ($map in $legacyMappings) { Write-Host " $($map.DriveLetter) -> $($map.RemotePath)" -ForegroundColor Gray } Write-Host "" # Step 2: Get credentials from user Write-Host "[Step 2/5] Enter your legacy domain credentials" -ForegroundColor Yellow $legacyUser = Get-LegacyUsername Write-Host " Username: $legacyUser" -ForegroundColor Gray Write-Host "" $password = Read-Host " Enter your legacy password" -AsSecureString if ($password.Length -eq 0) { Write-Host "[ERROR] " -ForegroundColor Red -NoNewline Write-Host "Password cannot be empty." Read-Host "Press Enter to return to menu..." return } # Step 3: Test credentials first (with retry loop for errors) $credentialsValid = $false $maxRetries = 3 $retryCount = 0 while (-not $credentialsValid -and $retryCount -lt $maxRetries) { Write-Host "" Write-Host "[Step 3/5] Testing credentials against $($LegacyServers[0])..." -ForegroundColor Yellow $testResult = Test-SMBCredentials -Server $LegacyServers[0] -Username $legacyUser -Password $password if ($testResult.Success) { $credentialsValid = $true Write-Host "[OK] " -ForegroundColor Green -NoNewline Write-Host "Credentials validated successfully!" } else { Write-Host "" Write-Host "[FAILED] " -ForegroundColor Red -NoNewline Write-Host $testResult.Message # Handle the specific error $errorHandler = Invoke-ErrorActionHandler -Action $testResult.Action -ErrorCode $testResult.ErrorCode if ($errorHandler.Cancel) { Write-Host "" Write-Host "Operation cancelled." -ForegroundColor Yellow Read-Host "Press Enter to return to menu..." return } if ($errorHandler.NewPassword) { # User reset their password - use the new one $password = $errorHandler.NewPassword Write-Host "" Write-Host " Retrying with new password..." -ForegroundColor Cyan } elseif ($errorHandler.Retry) { # Retry with same credentials (e.g., after Zscaler reauth) Write-Host "" Write-Host " Retrying..." -ForegroundColor Cyan } $retryCount++ } } if (-not $credentialsValid) { Write-Host "" Write-Host "[ERROR] " -ForegroundColor Red -NoNewline Write-Host "Maximum retry attempts reached. Please try again later." Read-Host "Press Enter to return to menu..." return } Write-Host "" # Step 4: Remove existing mappings and credentials Write-Host "[Step 4/5] Removing existing mappings and credentials..." -ForegroundColor Yellow Remove-LegacyDriveMappings Remove-WindowsCredentials Write-Host "" # Step 5: Reconnect drives (with individual error handling) Write-Host "[Step 5/5] Reconnecting drives with new credentials..." -ForegroundColor Yellow $mapResults = Add-DriveMappings -Mappings $legacyMappings -Username $legacyUser -Password $password # Check for failures that need individual handling $failedMappings = $mapResults | Where-Object { -not $_.Success } foreach ($failed in $failedMappings) { Write-Host "" Write-Host " Handling failed mapping: $($failed.DriveLetter)" -ForegroundColor Yellow # Determine the action based on error code $action = switch ($failed.ErrorCode) { 5 { "contact_manager" } 53 { "zscaler_reauth" } 64 { "retry_or_zscaler" } 67 { "verify_path" } 86 { "reset_password_flow" } 1326 { "reset_password_flow" } 1330 { "reset_password_flow" } 1909 { "reset_password_flow" } default { "unknown_error" } } $errorHandler = Invoke-ErrorActionHandler -Action $action -ErrorCode $failed.ErrorCode -AdditionalInfo $failed.RemotePath if ($errorHandler.Retry -and -not $errorHandler.Cancel) { if ($errorHandler.NewPassword) { $password = $errorHandler.NewPassword } # Retry just this one mapping Write-Host " Retrying $($failed.DriveLetter)... " -NoNewline $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password) $PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) $retryOutput = net use $failed.DriveLetter $failed.RemotePath /user:$legacyUser $PlainPassword /persistent:yes 2>&1 $PlainPassword = $null if ($LASTEXITCODE -eq 0) { Write-Host "OK" -ForegroundColor Green # Update the result $failed.Success = $true $failed.ErrorCode = 0 } else { Write-Host "Still failed (Error: $LASTEXITCODE)" -ForegroundColor Red } } } Write-Host "" Write-Host "============================================================" -ForegroundColor Cyan Write-Host " Summary" -ForegroundColor Cyan Write-Host "============================================================" -ForegroundColor Cyan $successCount = ($mapResults | Where-Object { $_.Success }).Count $failCount = ($mapResults | Where-Object { -not $_.Success }).Count Write-Host "" Write-Host " Successful: " -NoNewline Write-Host $successCount -ForegroundColor Green Write-Host " Failed: " -NoNewline Write-Host $failCount -ForegroundColor $(if ($failCount -gt 0) { "Red" } else { "Green" }) Write-Host "" if ($failCount -gt 0) { Write-Host "Failed mappings:" -ForegroundColor Red foreach ($result in ($mapResults | Where-Object { -not $_.Success })) { Write-Host " $($result.DriveLetter) - Error $($result.ErrorCode)" -ForegroundColor Red } Write-Host "" Write-Host " You may need to map these drives manually or contact IT support." -ForegroundColor Yellow } Write-Host "" Read-Host "Press Enter to return to menu..." } # ============================================================================ # MAIN SCRIPT # ============================================================================ # Check if running interactively or with parameters param( [switch]$Silent, [switch]$BackupOnly, [switch]$Reset ) if ($BackupOnly) { Backup-DriveMappings exit 0 } if ($Reset) { # Non-interactive reset - would need credentials passed in or prompted Write-Host "Non-interactive reset not yet implemented. Please run without -Reset for interactive mode." exit 1 } # ============================================================================ # AUTO-BACKUP ON STARTUP # ============================================================================ Write-Host "" Write-Host "============================================================" -ForegroundColor Cyan Write-Host " GE Aerospace - Network Drive Manager" -ForegroundColor Cyan Write-Host "============================================================" -ForegroundColor Cyan Write-Host "" # Check for existing backup and auto-backup $doBackup = $true if (Test-Path $BackupFilePath) { # Backup exists - ask if user wants to overwrite try { $existingBackup = Get-Content -Path $BackupFilePath -Raw | ConvertFrom-Json $backupDate = $existingBackup.BackupDate Write-Host " Existing backup found from: $backupDate" -ForegroundColor Yellow Write-Host "" $overwrite = Read-Host " Overwrite existing backup with current mappings? (y/N)" $doBackup = ($overwrite.ToLower() -eq 'y') } catch { Write-Host " Existing backup file is corrupted. Creating new backup..." -ForegroundColor Yellow $doBackup = $true } } else { Write-Host " No existing backup found. Creating automatic backup..." -ForegroundColor Cyan } if ($doBackup) { $currentMappings = Get-CurrentDriveMappings -AllDrives $legacyCount = ($currentMappings | Where-Object { $_.IsLegacy }).Count if ($currentMappings.Count -gt 0) { Backup-DriveMappings | Out-Null Write-Host " [OK] Backed up $($currentMappings.Count) drive(s) ($legacyCount legacy)" -ForegroundColor Green } else { Write-Host " [INFO] No network drives to backup" -ForegroundColor Yellow } } else { Write-Host " [SKIP] Keeping existing backup" -ForegroundColor Gray } Write-Host "" Start-Sleep -Seconds 1 # Interactive menu loop do { Show-Menu $choice = Read-Host "Select an option" switch ($choice.ToLower()) { "1" { # View current mappings Write-Host "" Write-Host "Current Network Drive Mappings:" -ForegroundColor Cyan Write-Host "================================" -ForegroundColor Cyan $mappings = Get-CurrentDriveMappings -AllDrives if ($mappings.Count -eq 0) { Write-Host " No network drives mapped." -ForegroundColor Yellow } else { foreach ($map in $mappings) { $legacyTag = if ($map.IsLegacy) { " [LEGACY]" } else { "" } $color = if ($map.IsLegacy) { "Yellow" } else { "Gray" } Write-Host " $($map.DriveLetter) -> $($map.RemotePath)$legacyTag" -ForegroundColor $color } } Write-Host "" Read-Host "Press Enter to continue..." } "2" { # Backup mappings Write-Host "" Backup-DriveMappings Write-Host "" Read-Host "Press Enter to continue..." } "3" { # Test credentials Write-Host "" Write-Host "Testing Legacy Credentials" -ForegroundColor Cyan Write-Host "==========================" -ForegroundColor Cyan $legacyUser = Get-LegacyUsername Write-Host "Username: $legacyUser" -ForegroundColor Gray Write-Host "" $password = Read-Host "Enter your legacy password" -AsSecureString Write-Host "" Write-Host "Testing against servers..." -ForegroundColor Yellow foreach ($server in $LegacyServers) { Write-Host " $server... " -NoNewline $result = Test-SMBCredentials -Server $server -Username $legacyUser -Password $password if ($result.Success) { Write-Host "OK" -ForegroundColor Green } else { Write-Host "FAILED - $($result.Message)" -ForegroundColor Red } } Write-Host "" Read-Host "Press Enter to continue..." } "4" { # Full reset Invoke-FullReset } "5" { # Restore from backup Write-Host "" $savedMappings = Get-SavedMappings if ($savedMappings) { Write-Host "" Write-Host "Saved mappings from $($savedMappings.BackupDate):" -ForegroundColor Cyan foreach ($map in $savedMappings.Mappings) { $legacyTag = if ($map.IsLegacy) { " [LEGACY]" } else { "" } Write-Host " $($map.DriveLetter) -> $($map.RemotePath)$legacyTag" } Write-Host "" $restore = Read-Host "Restore legacy drives from this backup? (y/N)" if ($restore -eq 'y') { $legacyUser = Get-LegacyUsername Write-Host "" Write-Host "Username: $legacyUser" -ForegroundColor Gray $password = Read-Host "Enter your legacy password" -AsSecureString $legacyMappings = $savedMappings.Mappings | Where-Object { $_.IsLegacy } Add-DriveMappings -Mappings $legacyMappings -Username $legacyUser -Password $password } } Write-Host "" Read-Host "Press Enter to continue..." } "6" { # Open password portal Write-Host "" Write-Host "Opening $PasswordResetURL ..." -ForegroundColor Cyan Start-Process $PasswordResetURL Start-Sleep -Seconds 2 } "q" { Write-Host "" Write-Host "Goodbye!" -ForegroundColor Cyan } default { Write-Host "" Write-Host "Invalid option. Please try again." -ForegroundColor Red Start-Sleep -Seconds 1 } } } while ($choice.ToLower() -ne 'q')