; ============================================================================ ; FQDNUpdate - Drive Remapping Tool ; Scans for drives mapped to legacy server names, backs them up, ; clears stale credentials, and remaps using canonical FQDNs with SSO. ; Pure Pascal Script — no PowerShell dependency. ; ============================================================================ [Setup] AppId={{B1A2C3D4-E5F6-7890-ABCD-EF1234567890}} AppName=FQDNUpdate AppVersion=1.0 AppPublisher=WJDT DefaultDirName={tmp}\FQDNUpdate CreateAppDir=no PrivilegesRequired=lowest OutputDir=Output OutputBaseFilename=FQDNUpdate SolidCompression=yes WizardStyle=modern SetupIconFile=gea-logo.ico WizardImageFile=patrick.bmp WizardSmallImageFile=patrick-sm.bmp DisableWelcomePage=no DisableDirPage=yes DisableProgramGroupPage=yes DisableReadyPage=yes Uninstallable=no [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Messages] WelcomeLabel1=FQDNUpdate - Drive Remapping Tool WelcomeLabel2=This tool will scan your mapped network drives and remap any that point to legacy server names so they use Windows SSO (no password prompt).%n%nWhat it does:%n 1. Scans for drives mapped to tsgwp00525 or avwesj-gwy01%n 2. Backs up your current mappings%n 3. Disconnects and clears stale credentials%n 4. Remaps drives using the canonical server name%n%nNo password will be required — your Windows login is used automatically.%n%nClick Next to begin. FinishedHeadingLabel=Remapping Complete [Code] // ============================================================================ // CONSTANTS AND CONFIGURATION // ============================================================================ const NL = #13#10; // Server group 1 OLD_SERVER_1 = 'tsgwp00525.rd.ds.ge.com'; NEW_SERVER_1 = 'tsgwp00525.wjs.geaerospace.net'; SHORT_NAME_1 = 'tsgwp00525'; // Server group 2 OLD_SERVER_2 = 'avwesj-gwy01.av.ge.com'; NEW_SERVER_2 = 'avwesj-gwy01.wjs.geaerospace.net'; SHORT_NAME_2 = 'avwesj-gwy01'; SERVER_VARIANT_COUNT = 6; // ============================================================================ // GLOBAL VARIABLES // ============================================================================ var ProgressPage: TOutputProgressWizardPage; LogContent: String; FailureCount: Integer; BackupPath: String; LogPath: String; // Parallel arrays for matched drives DriveLetters: TArrayOfString; RemotePaths: TArrayOfString; ShareNames: TArrayOfString; CanonicalFQDNs: TArrayOfString; DriveCount: Integer; // ============================================================================ // UTILITY FUNCTIONS // ============================================================================ function ExecWithOutput(const Cmd, Params: String; var Output: String): Integer; var TempFile: String; ResultCode: Integer; OutputAnsi: AnsiString; begin TempFile := ExpandConstant('{tmp}\cmd_output_') + IntToStr(Random(99999)) + '.txt'; Exec('cmd.exe', '/c ' + Cmd + ' ' + Params + ' > "' + TempFile + '" 2>&1', ExpandConstant('{sys}'), SW_HIDE, ewWaitUntilTerminated, ResultCode); if FileExists(TempFile) then begin if LoadStringFromFile(TempFile, OutputAnsi) then Output := String(OutputAnsi) else Output := ''; DeleteFile(TempFile); end else Output := ''; Result := ResultCode; end; procedure Log(const Msg: String); begin LogContent := LogContent + Msg + NL; end; function GetServerVariants: TArrayOfString; begin SetArrayLength(Result, SERVER_VARIANT_COUNT); Result[0] := OLD_SERVER_1; Result[1] := NEW_SERVER_1; Result[2] := SHORT_NAME_1; Result[3] := OLD_SERVER_2; Result[4] := NEW_SERVER_2; Result[5] := SHORT_NAME_2; end; function GetCanonicalFQDN(const ServerName: String): String; var Lower: String; begin Lower := Lowercase(ServerName); if (Lower = Lowercase(OLD_SERVER_1)) or (Lower = Lowercase(NEW_SERVER_1)) or (Lower = Lowercase(SHORT_NAME_1)) then Result := NEW_SERVER_1 else if (Lower = Lowercase(OLD_SERVER_2)) or (Lower = Lowercase(NEW_SERVER_2)) or (Lower = Lowercase(SHORT_NAME_2)) then Result := NEW_SERVER_2 else Result := ''; end; function ContainsServerVariant(const S: String): Boolean; var Lower: String; begin Lower := Lowercase(S); Result := (Pos(Lowercase(OLD_SERVER_1), Lower) > 0) or (Pos(Lowercase(NEW_SERVER_1), Lower) > 0) or (Pos(Lowercase(SHORT_NAME_1), Lower) > 0) or (Pos(Lowercase(OLD_SERVER_2), Lower) > 0) or (Pos(Lowercase(NEW_SERVER_2), Lower) > 0) or (Pos(Lowercase(SHORT_NAME_2), Lower) > 0); end; function ExtractServerFromUNC(const UNCPath: String): String; var S: String; SlashPos: Integer; begin Result := ''; if (Length(UNCPath) > 2) and (UNCPath[1] = '\') and (UNCPath[2] = '\') then begin S := Copy(UNCPath, 3, Length(UNCPath) - 2); SlashPos := Pos('\', S); if SlashPos > 0 then Result := Copy(S, 1, SlashPos - 1) else Result := S; end; end; function ExtractShareFromUNC(const UNCPath: String): String; var S: String; SlashPos: Integer; begin Result := ''; if (Length(UNCPath) > 2) and (UNCPath[1] = '\') and (UNCPath[2] = '\') then begin S := Copy(UNCPath, 3, Length(UNCPath) - 2); SlashPos := Pos('\', S); if SlashPos > 0 then Result := Copy(S, SlashPos + 1, Length(S) - SlashPos); end; end; function GetTimestamp: String; begin Result := GetDateTimeString('yyyymmdd_hhnnss', '-', '-'); end; function GetLogDir: String; begin Result := ExpandConstant('{userdocs}\wjdt\logs'); ForceDirectories(Result); end; // ============================================================================ // PHASE 1: SCAN & BACKUP // ============================================================================ procedure ScanAndBackup; var NetOutput, Line, DriveLetter, RemotePath, ServerName, ShareName, Canonical: String; I, SpacePos, SecondSpace: Integer; BackupContent: String; begin Log('=== Phase 1: Scan & Backup ==='); Log(''); DriveCount := 0; SetArrayLength(DriveLetters, 26); SetArrayLength(RemotePaths, 26); SetArrayLength(ShareNames, 26); SetArrayLength(CanonicalFQDNs, 26); ExecWithOutput('net', 'use', NetOutput); NetOutput := NetOutput + NL; while Length(NetOutput) > 0 do begin I := Pos(#13#10, NetOutput); if I = 0 then I := Pos(#10, NetOutput); if I = 0 then begin Line := NetOutput; NetOutput := ''; end else begin Line := Copy(NetOutput, 1, I - 1); if (I < Length(NetOutput)) and (NetOutput[I] = #13) then NetOutput := Copy(NetOutput, I + 2, Length(NetOutput)) else NetOutput := Copy(NetOutput, I + 1, Length(NetOutput)); end; if not ContainsServerVariant(Line) then Continue; DriveLetter := ''; RemotePath := ''; for I := 1 to Length(Line) - 1 do begin if (Line[I] >= 'A') and (Line[I] <= 'Z') and (Line[I+1] = ':') then begin DriveLetter := Line[I] + ':'; SpacePos := I + 2; while (SpacePos <= Length(Line)) and (Line[SpacePos] = ' ') do SpacePos := SpacePos + 1; if (SpacePos < Length(Line)) and (Line[SpacePos] = '\') and (Line[SpacePos+1] = '\') then begin SecondSpace := SpacePos; while (SecondSpace <= Length(Line)) and (Line[SecondSpace] <> ' ') do SecondSpace := SecondSpace + 1; RemotePath := Copy(Line, SpacePos, SecondSpace - SpacePos); end; Break; end; end; if (DriveLetter <> '') and (RemotePath <> '') then begin ServerName := ExtractServerFromUNC(RemotePath); ShareName := ExtractShareFromUNC(RemotePath); Canonical := GetCanonicalFQDN(ServerName); if Canonical <> '' then begin DriveLetters[DriveCount] := DriveLetter; RemotePaths[DriveCount] := RemotePath; ShareNames[DriveCount] := ShareName; CanonicalFQDNs[DriveCount] := Canonical; DriveCount := DriveCount + 1; end; end; end; if DriveCount = 0 then begin Log('No drives found mapped to target servers.'); Exit; end; Log('Found ' + IntToStr(DriveCount) + ' drive(s):'); for I := 0 to DriveCount - 1 do Log(' ' + DriveLetters[I] + ' -> ' + RemotePaths[I]); BackupContent := ''; for I := 0 to DriveCount - 1 do BackupContent := BackupContent + DriveLetters[I] + '|' + RemotePaths[I] + NL; BackupPath := GetLogDir + '\fqdnupdate_' + GetTimestamp + '.txt'; SaveStringToFile(BackupPath, BackupContent, False); Log(''); Log('Backup saved to ' + BackupPath); end; // ============================================================================ // PHASE 2: DISCONNECT & CLEAR // ============================================================================ procedure DisconnectAndClear; var Output: String; Variants: TArrayOfString; I: Integer; begin Log(''); Log('=== Phase 2: Disconnect & Clear ==='); Log(''); Variants := GetServerVariants; for I := 0 to DriveCount - 1 do begin ExecWithOutput('net', 'use ' + DriveLetters[I] + ' /delete /y', Output); Log(' Disconnected ' + DriveLetters[I]); end; for I := 0 to GetArrayLength(Variants) - 1 do begin ExecWithOutput('net', 'use "\\' + Variants[I] + '\IPC$" /delete /y', Output); ExecWithOutput('net', 'use "\\' + Variants[I] + '" /delete /y', Output); end; Log(' Cleared IPC$/server connections'); for I := 0 to GetArrayLength(Variants) - 1 do begin ExecWithOutput('cmdkey', '/delete:' + Variants[I], Output); ExecWithOutput('cmdkey', '/delete:"Domain:target=' + Variants[I] + '"', Output); end; Log(' Cleared cached credentials'); ExecWithOutput('klist', 'purge', Output); Log(' Purged Kerberos ticket cache'); Sleep(2000); Log(' Waited 2 seconds for connections to release'); end; // ============================================================================ // PHASE 3: REMAP WITH SSO // ============================================================================ procedure RemapDrives; var Output, NewPath: String; ExitCode, I: Integer; begin Log(''); Log('=== Phase 3: Remap with SSO ==='); Log(''); FailureCount := 0; for I := 0 to DriveCount - 1 do begin NewPath := '\\' + CanonicalFQDNs[I] + '\' + ShareNames[I]; ExitCode := ExecWithOutput('net', 'use ' + DriveLetters[I] + ' "' + NewPath + '" /persistent:yes', Output); if ExitCode = 0 then Log(' OK: ' + DriveLetters[I] + ' -> ' + NewPath) else begin Log(' FAILED: ' + DriveLetters[I] + ' -> ' + NewPath + ' (Error ' + IntToStr(ExitCode) + ')'); FailureCount := FailureCount + 1; end; end; Log(''); Log('=== Complete ==='); end; // ============================================================================ // WIZARD PAGE CREATION // ============================================================================ procedure InitializeWizard; begin ProgressPage := CreateOutputProgressPage('Remapping Drives', 'Please wait while your drives are remapped...'); end; // ============================================================================ // PAGE NAVIGATION // ============================================================================ function NextButtonClick(CurPageID: Integer): Boolean; begin Result := True; if CurPageID = wpWelcome then begin LogContent := ''; FailureCount := 0; Log('FQDNUpdate Log - ' + GetDateTimeString('yyyy-mm-dd hh:nn:ss', '-', ':')); Log('User: ' + GetUserNameString + ' Computer: ' + ExpandConstant('{computername}')); Log(''); ProgressPage.Show; try // Phase 1: Scan & Backup ProgressPage.SetText('Scanning for target drives...', ''); ProgressPage.SetProgress(10, 100); ScanAndBackup; if DriveCount > 0 then begin // Phase 2: Disconnect & Clear ProgressPage.SetText('Disconnecting drives and clearing credentials...', ''); ProgressPage.SetProgress(30, 100); DisconnectAndClear; // Phase 3: Remap ProgressPage.SetText('Remapping drives with SSO...', ''); ProgressPage.SetProgress(70, 100); RemapDrives; end; ProgressPage.SetProgress(100, 100); finally ProgressPage.Hide; end; // Save log LogPath := GetLogDir + '\fqdnupdate_' + GetTimestamp + '.log'; SaveStringToFile(LogPath, LogContent, False); // Update finished page text dynamically if DriveCount = 0 then WizardForm.FinishedLabel.Caption := 'No drives were found mapped to target servers.' + NL + NL + 'Nothing was changed.' + NL + NL + 'Log saved to:' + NL + LogPath else if FailureCount = 0 then WizardForm.FinishedLabel.Caption := 'All ' + IntToStr(DriveCount) + ' drive(s) were successfully remapped to use Windows SSO.' + NL + NL + 'Backup: ' + BackupPath + NL + 'Log: ' + LogPath else WizardForm.FinishedLabel.Caption := IntToStr(DriveCount - FailureCount) + ' of ' + IntToStr(DriveCount) + ' drive(s) remapped successfully.' + NL + IntToStr(FailureCount) + ' drive(s) failed — check the log for details.' + NL + NL + 'Backup: ' + BackupPath + NL + 'Log: ' + LogPath; end; end; function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String; begin Result := ''; end;