diff --git a/Scripts/Drive-reconnection/JFC/Invoke-DriveMapping.ps1 b/Scripts/Drive-reconnection/JFC/Invoke-DriveMapping.ps1 new file mode 100644 index 0000000..f8f47fd --- /dev/null +++ b/Scripts/Drive-reconnection/JFC/Invoke-DriveMapping.ps1 @@ -0,0 +1,309 @@ +<# +.SYNOPSIS + Maps persistent network drives for JFC locations based on machine site code. + +.DESCRIPTION + Detects the machine's site code from the computer name, looks up the corresponding + UserServer and GroupServer values, and maps persistent network drives (U:, W:, S:). + Supports all 30 JFC locations with special handling for IP-based servers and custom + share configurations. + +.PARAMETER ComputerName + The computer name to extract site code from. Defaults to the local machine name. + Used primarily for testing different location scenarios. + +.PARAMETER Verbose + Enable detailed output for debugging drive mapping operations. + +.EXAMPLE + .\Invoke-DriveMapping.ps1 + Maps drives for the local machine based on its computer name. + +.EXAMPLE + .\Invoke-DriveMapping.ps1 -ComputerName "JFCS6ACT01" -Verbose + Maps drives for Kansas City location (JFC) with verbose output. + +.EXAMPLE + .\Invoke-DriveMapping.ps1 -ComputerName "JOHS8ACT01" -Verbose + Maps drives for Ohio location (JOH) with verbose output. + +.NOTES + Author: Copilot + Version: 1.0 + Date: 2026-01-30 + + Special Cases: + - Terminal Servers (TRM, ACT, ACP suffix): Skipped + - JPMS2SAG (PMAI Sage): Skipped + - IP-based servers (JOH: 10.70.1.1, JPH: 10.30.1.1): Use IP instead of DNS + - Duplicate handlers: JFH/JHI, JPN/JPS/JPM, JOR/JMI, JDL/JHT, JWM/JHO + +.LINK + https://github.com/J-MaFf/PowerShellScripts/issues/[ISSUE_NUMBER] +#> + +[CmdletBinding()] +param( + [Parameter(ValueFromPipeline = $false)] + [string]$ComputerName = $env:COMPUTERNAME +) + +# ============================================================================ +# CONFIGURATION: JFC Site Mappings +# ============================================================================ +$SiteConfig = @{ + 'IDC' = @{ UserServer = 'IDCS6BDC01'; GroupServer = 'IDCS6BDC01'; GroupShare = 'Groups' } + 'JAT' = @{ UserServer = 'JATS2BDC02'; GroupServer = 'JATS2BDC02'; GroupShare = 'Groups' } + 'JBO' = @{ UserServer = 'JBOS2BDC01'; GroupServer = 'JBOS2BDC01'; GroupShare = 'Groups'; SShare = '\\10.80.1.1\Groups' } + 'JBR' = @{ UserServer = 'JBRS2BDC01'; GroupServer = 'JBRS2BDC01'; GroupShare = 'Groups' } + 'JBT' = @{ UserServer = 'JBTS2BDC02'; GroupServer = 'JBTS2BDC02'; GroupShare = 'Groups' } + 'JCH' = @{ UserServer = 'JCHS9BDC02'; GroupServer = 'JCHS9BDC02'; GroupShare = 'Groups' } + 'JDE' = @{ UserServer = 'JDES2BDC02'; GroupServer = 'JDES2BDC02'; GroupShare = 'Groups' } + 'JDL' = @{ UserServer = 'JHTS9BDC01'; GroupServer = 'JHTS9BDC01'; GroupShare = 'JDL_groups'; SShare = '\\10.50.1.1\Groups' } + 'JFC' = @{ UserServer = 'JFCS8BDC01'; GroupServer = 'JFCS8BDC01'; GroupShare = 'Groups' } + 'JFH' = @{ UserServer = 'JHIS2BDC02'; GroupServer = 'JHIS2BDC02'; GroupShare = 'Groups' } + 'JHI' = @{ UserServer = 'JHIS2BDC02'; GroupServer = 'JHIS2BDC02'; GroupShare = 'Groups' } + 'JHO' = @{ UserServer = 'JHOS2BDC01'; GroupServer = 'JHOS2BDC01'; GroupShare = 'Groups' } + 'JHP' = @{ UserServer = '10.30.1.1'; GroupServer = '10.30.1.1'; GroupShare = 'Groups_JPH' } + 'JHT' = @{ UserServer = 'JHTS9BDC01'; GroupServer = 'JHTS9BDC01'; GroupShare = 'Groups' } + 'JLA' = @{ UserServer = 'JLAS2BDC01'; GroupServer = 'JLAS2BDC01'; GroupShare = 'Groups' } + 'JLV' = @{ UserServer = 'JLVS2BDC01'; GroupServer = 'JLVS2BDC01'; GroupShare = 'Groups' } + 'JMI' = @{ UserServer = 'JMIS2BDC01'; GroupServer = 'JMIS2BDC01'; GroupShare = 'Groups' } + 'JMX' = @{ UserServer = 'JMXS6BDC01'; GroupServer = 'JMXS6BDC01'; GroupShare = 'Groups' } + 'JNY' = @{ UserServer = 'JNYS6BDC01'; GroupServer = 'JNYS6BDC01'; GroupShare = 'Groups' } + 'JOH' = @{ UserServer = '10.70.1.1'; GroupServer = '10.70.1.1'; GroupShare = 'JOH_groups'; SShare = '\\10.70.1.1\Groups' } + 'JOR' = @{ UserServer = 'JMIS2BDC01'; GroupServer = 'JMIS2BDC01'; GroupShare = 'Groups' } + 'JPH' = @{ UserServer = '10.30.1.1'; GroupServer = '10.30.1.1'; GroupShare = 'Groups_JPH' } + 'JPM' = @{ UserServer = 'JPMS6BDC01'; GroupServer = 'JPMS6BDC01'; GroupShare = 'Groups' } + 'JPN' = @{ UserServer = 'JPMS6BDC01'; GroupServer = 'JPMS6BDC01'; GroupShare = 'Groups' } + 'JPS' = @{ UserServer = 'JPMS6BDC01'; GroupServer = 'JPMS6BDC01'; GroupShare = 'Groups' } + 'JPV' = @{ UserServer = 'JPVS2BDC01'; GroupServer = 'JPVS2BDC01'; GroupShare = 'Groups' } + 'JSA' = @{ UserServer = 'JSFS9BDC01'; GroupServer = 'JSFS9BDC01'; GroupShare = 'Groups' } + 'JSD' = @{ UserServer = 'JSDS9BDC01'; GroupServer = 'JSDS9BDC01'; GroupShare = 'Groups' } + 'JSE' = @{ UserServer = 'JSES9BDC01'; GroupServer = 'JSES9BDC01'; GroupShare = 'Groups' } + 'JSF' = @{ UserServer = 'JSFS9BDC01'; GroupServer = 'JSFS9BDC01'; GroupShare = 'Groups' } +} + +# ============================================================================ +# FUNCTION: Extract Site Code from Computer Name +# ============================================================================ +function Get-SiteCode { + param([string]$ComputerName) + + Write-Verbose "[Get-SiteCode] Extracting site code from computer name: $ComputerName" + + # Special case: JWM computers map to JHO + if ($ComputerName -match '^JWM') { + Write-Verbose "[Get-SiteCode] JWM computer detected; mapping to JHO" + return 'JHO' + } + + # Extract first 3 characters as site code + if ($ComputerName.Length -ge 3) { + $siteCode = $ComputerName.Substring(0, 3) + Write-Verbose "[Get-SiteCode] Site code extracted: $siteCode" + return $siteCode + } + else { + Write-Error "[Get-SiteCode] Computer name too short to extract site code: $ComputerName" + return $null + } +} + +# ============================================================================ +# FUNCTION: Check for Terminal Server +# ============================================================================ +function Test-TerminalServer { + param([string]$ComputerName) + + Write-Verbose "[Test-TerminalServer] Checking if computer is terminal server: $ComputerName" + + # Check terminal server suffixes from original batch logic: %computername:~5,3% + # This extracts characters at position 5-7 (0-indexed) + if ($ComputerName.Length -ge 8) { + $suffix = $ComputerName.Substring(5, 3) + Write-Verbose "[Test-TerminalServer] Checking position 5-7: $suffix" + + if ($suffix -match '^(TRM|ACT|ACP)') { + Write-Verbose "[Test-TerminalServer] Terminal server detected (suffix: TRM/ACT/ACP)" + return $true + } + } + + # Check for PMAI Sage + if ($ComputerName -match '^JPMS2SAG') { + Write-Verbose "[Test-TerminalServer] PMAI Sage server detected" + return $true + } + + Write-Verbose "[Test-TerminalServer] Not a terminal server" + return $false +} + +# ============================================================================ +# FUNCTION: Disconnect Drive +# ============================================================================ +function Disconnect-MappedDrive { + param( + [string]$DriveLetter, + [bool]$Force = $true + ) + + Write-Verbose "[Disconnect-MappedDrive] Checking drive: $DriveLetter" + + try { + $drive = Get-PSDrive -Name $DriveLetter -ErrorAction SilentlyContinue + + if ($drive) { + Write-Verbose "[Disconnect-MappedDrive] Drive $DriveLetter is currently mapped; disconnecting..." + + if ($Force) { + net use $DriveLetter /delete /yes 2>&1 | ForEach-Object { + Write-Verbose "[Disconnect-MappedDrive] $_ " + } + } + else { + net use $DriveLetter /delete 2>&1 | ForEach-Object { + Write-Verbose "[Disconnect-MappedDrive] $_ " + } + } + + Write-Verbose "[Disconnect-MappedDrive] Successfully disconnected $DriveLetter" + return $true + } + else { + Write-Verbose "[Disconnect-MappedDrive] Drive $DriveLetter is not mapped" + return $true + } + } + catch { + Write-Verbose "[Disconnect-MappedDrive] Error disconnecting $DriveLetter : $_" + return $false + } +} + +# ============================================================================ +# FUNCTION: Connect Network Drive +# ============================================================================ +function Connect-NetworkDrive { + param( + [string]$DriveLetter, + [string]$UNCPath, + [bool]$Persist = $true + ) + + Write-Verbose "[Connect-NetworkDrive] Mapping $DriveLetter to $UNCPath (Persist: $Persist)" + + try { + $persistFlag = if ($Persist) { '/persistent:yes' } else { '/persistent:no' } + $output = net use $DriveLetter $UNCPath $persistFlag 2>&1 + + if ($LASTEXITCODE -eq 0) { + Write-Verbose "[Connect-NetworkDrive] Successfully mapped $DriveLetter to $UNCPath" + return $true + } + else { + Write-Error "[Connect-NetworkDrive] Failed to map $DriveLetter to $UNCPath. Error: $($output -join ' ')" + return $false + } + } + catch { + Write-Error "[Connect-NetworkDrive] Exception mapping $DriveLetter to $UNCPath : $_" + return $false + } +} + +# ============================================================================ +# MAIN SCRIPT +# ============================================================================ + +# Only run main logic if script is invoked directly (not dot-sourced for testing) +if ($MyInvocation.InvocationName -eq '.') { + # Dot-sourced - don't run main logic + Write-Verbose "[Main] Script sourced; functions available for testing" +} +else { + # Script invoked directly - run main logic + + Write-Verbose "[Main] Starting drive mapping script for computer: $ComputerName" + + # Check if terminal server + if (Test-TerminalServer -ComputerName $ComputerName) { + Write-Verbose "[Main] Terminal server detected; exiting script" + Write-Output "Terminal server detected; drive mapping skipped." + exit 0 + } + + # Extract site code + $siteCode = Get-SiteCode -ComputerName $ComputerName + if (-not $siteCode) { + Write-Error "[Main] Failed to extract site code from computer name: $ComputerName" + exit 1 + } + + Write-Verbose "[Main] Site code: $siteCode" + + # Lookup site configuration + if (-not $SiteConfig.ContainsKey($siteCode)) { + Write-Error "[Main] Site code '$siteCode' not found in configuration. Available sites: $($SiteConfig.Keys -join ', ')" + exit 1 + } + + $config = $SiteConfig[$siteCode] + Write-Verbose "[Main] Configuration found for site: $siteCode" + Write-Verbose "[Main] UserServer: $($config.UserServer), GroupServer: $($config.GroupServer)" + + # Build UNC paths + $userUNC = "\\$($config.UserServer)\$env:USERNAME`$" + $groupUNC = "\\$($config.GroupServer)\$($config.GroupShare)" + + Write-Verbose "[Main] User UNC path: $userUNC" + Write-Verbose "[Main] Group UNC path: $groupUNC" + + # Disconnect existing drives + Write-Verbose "[Main] Disconnecting existing drives" + Disconnect-MappedDrive -DriveLetter 'u' -Force $true | Out-Null + Disconnect-MappedDrive -DriveLetter 'w' -Force $true | Out-Null + if ($config.SShare) { + Disconnect-MappedDrive -DriveLetter 's' -Force $true | Out-Null + } + + # Map U: drive (user directory) + Write-Output "Connecting to User Directory on $($config.UserServer)..." + Write-Verbose "[Main] Mapping U: drive" + $uDriveSuccess = Connect-NetworkDrive -DriveLetter 'u' -UNCPath $userUNC -Persist $true + + if (-not $uDriveSuccess) { + Write-Error "[Main] Failed to map U: drive; aborting script" + exit 1 + } + + # Map W: drive (group directory) + Write-Output "Connecting to Workgroup Directory on $($config.GroupServer)..." + Write-Verbose "[Main] Mapping W: drive" + $wDriveSuccess = Connect-NetworkDrive -DriveLetter 'w' -UNCPath $groupUNC -Persist $true + + if (-not $wDriveSuccess) { + Write-Error "[Main] Failed to map W: drive; U: drive may still be mapped" + exit 1 + } + + # Map S: drive if configured + if ($config.SShare) { + Write-Output "Connecting to Branch Groups Directory..." + Write-Verbose "[Main] Mapping S: drive to $($config.SShare)" + $sDriveSuccess = Connect-NetworkDrive -DriveLetter 's' -UNCPath $config.SShare -Persist $true + + if (-not $sDriveSuccess) { + Write-Warning "[Main] Failed to map S: drive, but U: and W: are successfully mapped" + } + } + + Write-Verbose "[Main] Drive mapping completed successfully" + Write-Output "Drive mapping completed successfully for site: $siteCode" + + exit 0 + +} + + diff --git a/Tests/Drive-reconnection/Invoke-DriveMapping.Tests.ps1 b/Tests/Drive-reconnection/Invoke-DriveMapping.Tests.ps1 new file mode 100644 index 0000000..fd2ec97 --- /dev/null +++ b/Tests/Drive-reconnection/Invoke-DriveMapping.Tests.ps1 @@ -0,0 +1,176 @@ +BeforeAll { + # The script is designed to run directly, not be imported as functions + # So we'll test it by invoking it with different parameters + $scriptPath = Join-Path $PSScriptRoot '..\..' 'Scripts' 'Drive-reconnection' 'JFC' 'Invoke-DriveMapping.ps1' +} + +Describe 'Invoke-DriveMapping.ps1 - JFC Drive Mapping Script' { + + Context 'Basic Execution' { + It 'should execute for terminal server TRM without error' { + { & $scriptPath -ComputerName 'JFCS6TRM01' } | Should -Not -Throw + } + + It 'should execute for terminal server ACT without error' { + { & $scriptPath -ComputerName 'JFCS6ACT01' } | Should -Not -Throw + } + + It 'should execute for PMAI Sage without error' { + { & $scriptPath -ComputerName 'JPMS2SAG' } | Should -Not -Throw + } + + It 'should handle verbose output properly' { + $output = & $scriptPath -ComputerName 'JFCS6USA01' -Verbose 4>&1 + $output | Should -Not -BeNullOrEmpty + } + } + + Context 'Site Code Detection' { + It 'should detect JFC site code' { + $output = & $scriptPath -ComputerName 'JFCS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'JFC' + } + + It 'should detect JOH site code' { + $output = & $scriptPath -ComputerName 'JOHS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'JOH' + } + + It 'should detect JPH site code' { + $output = & $scriptPath -ComputerName 'JPHS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'JPH' + } + + It 'should detect JWM and map to JHO' { + $output = & $scriptPath -ComputerName 'JWMS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'JHO' + } + } + + Context 'Terminal Server Detection' { + It 'should skip for TRM suffix' { + $output = & $scriptPath -ComputerName 'JFCS6TRM01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'Terminal server.*skipped' + } + + It 'should skip for ACT suffix' { + $output = & $scriptPath -ComputerName 'JFCS6ACT01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'Terminal server.*skipped' + } + + It 'should skip for ACP suffix' { + $output = & $scriptPath -ComputerName 'JFCS6ACP01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'Terminal server.*skipped' + } + + It 'should not skip for USA suffix' { + $output = & $scriptPath -ComputerName 'JFCS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Not -Match 'Terminal server.*skipped' + } + } + + Context 'Server Configuration' { + It 'should use JFCS8BDC01 for JFC' { + $output = & $scriptPath -ComputerName 'JFCS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'JFCS8BDC01' + } + + It 'should use IP 10.70.1.1 for JOH' { + $output = & $scriptPath -ComputerName 'JOHS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match '10\.70\.1\.1' + } + + It 'should use IP 10.30.1.1 for JPH' { + $output = & $scriptPath -ComputerName 'JPHS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match '10\.30\.1\.1' + } + + It 'should use JHTS9BDC01 for JDL' { + $output = & $scriptPath -ComputerName 'JDLS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'JHTS9BDC01' + } + + It 'should use JBOS2BDC01 for JBO' { + $output = & $scriptPath -ComputerName 'JBOS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'JBOS2BDC01' + } + } + + Context 'Group Share Configuration' { + It 'should use Groups for JFC' { + $output = & $scriptPath -ComputerName 'JFCS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match '\\Groups[^_]' + } + + It 'should use JOH_groups for JOH' { + $output = & $scriptPath -ComputerName 'JOHS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'JOH_groups' + } + + It 'should use Groups_JPH for JPH' { + $output = & $scriptPath -ComputerName 'JPHS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'Groups_JPH' + } + + It 'should use JDL_groups for JDL' { + $output = & $scriptPath -ComputerName 'JDLS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + $outputStr | Should -Match 'JDL_groups' + } + } + + Context 'S Drive Mapping' { + It 'should attempt S: drive mapping for JDL' { + $output = & $scriptPath -ComputerName 'JDLS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + # The script should attempt to map S: drive for JDL even if it fails due to network issues + $outputStr | Should -Match 'JDL' + } + + It 'should attempt S: drive mapping for JBO' { + $output = & $scriptPath -ComputerName 'JBOS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + # The script should attempt to map S: drive for JBO even if it fails due to network issues + $outputStr | Should -Match 'JBO' + } + + It 'should attempt S: drive mapping for JOH' { + $output = & $scriptPath -ComputerName 'JOHS6USA01' -Verbose 4>&1 + [string]$outputStr = $output -join ' ' + # The script should attempt to map S: drive for JOH even if it fails due to network issues + $outputStr | Should -Match 'JOH' + } + } + + Context 'All 30 JFC Locations' { + $allSites = @( + 'IDC', 'JAT', 'JBO', 'JBR', 'JBT', 'JCH', 'JDE', 'JDL', + 'JFC', 'JFH', 'JHI', 'JHO', 'JHP', 'JHT', 'JLA', 'JLV', + 'JMI', 'JMX', 'JNY', 'JOH', 'JPM', 'JPN', 'JPH', 'JPV', + 'JPS', 'JSA', 'JSD', 'JSE', 'JSF', 'JOR' + ) + + foreach ($site in $allSites) { + It "should execute successfully for site $site" { + $computerName = "{0}S6USA01" -f $site + { & $scriptPath -ComputerName $computerName -Verbose 4>&1 } | Should -Not -Throw + } + } + } +}