diff --git a/export-ODBC.ps1 b/export-ODBC.ps1 new file mode 100644 index 0000000..2ad0a32 --- /dev/null +++ b/export-ODBC.ps1 @@ -0,0 +1,273 @@ +<# +.SYNOPSIS + Export ODBC DSNs, registry keys, and driver inventory. + Generate migration package + report with vendor download links. + +.DESCRIPTION + This script: + - Exports ODBC System/User DSNs + - Exports registry keys for ODBC.INI + - Lists all installed ODBC drivers and Add/Remove Program entries + - Matches known ODBC drivers to official vendor download URLs + - Searches for local EXE/MSI installers + - Generates CSV + Markdown reports + - Compresses all output into a single ZIP archive + + Run as Administrator on the source PC. +#> + +# =========================== +# CONFIGURATION +# =========================== +$BaseDir = "C:\ODBC_Migration" +$ExportDir = Join-Path $BaseDir "export" +$ZipFile = Join-Path $BaseDir "ODBC_Backup.zip" +$LogFile = Join-Path $BaseDir "export_log.txt" + +# Optional: Add local folders to search for installers +$InstallerHuntPaths = @( + "$env:USERPROFILE\Downloads", + "C:\Installers", + "C:\Drivers", + "$env:PUBLIC\Downloads" +) | Where-Object { Test-Path $_ } + +# =========================== +# PREP +# =========================== +New-Item -ItemType Directory -Path $ExportDir -Force | Out-Null +Start-Transcript -Path $LogFile -Append +Write-Host "Starting ODBC migration export..." -ForegroundColor Cyan + +# =========================== +# STEP 1: EXPORT BASELINE DATA +# =========================== +Write-Host "Exporting ODBC driver list..." +$drivers = Get-OdbcDriver | Select-Object Name, Platform, Version, Attribute +$driversCsv = Join-Path $ExportDir "odbc_drivers.csv" +$drivers | Export-Csv -Path $driversCsv -NoTypeInformation -Encoding UTF8 + +Write-Host "Exporting DSNs..." +$dsnsXml = Join-Path $ExportDir "odbc_dsns.xml" +Get-OdbcDsn | Export-Clixml $dsnsXml + +Write-Host "Exporting registry keys..." +reg export "HKLM\SOFTWARE\ODBC\ODBC.INI" (Join-Path $ExportDir "ODBC_System.reg") /y | Out-Null +reg export "HKCU\Software\ODBC\ODBC.INI" (Join-Path $ExportDir "ODBC_User.reg") /y | Out-Null +$WowKey = "HKLM\SOFTWARE\WOW6432Node\ODBC\ODBC.INI" +if (Test-Path "Registry::$WowKey") { + reg export $WowKey (Join-Path $ExportDir "ODBC_System32.reg") /y | Out-Null + Write-Host "Exported 32-bit ODBC keys." +} + +# =========================== +# STEP 2: ENUMERATE INSTALLED PRODUCTS +# =========================== +Write-Host "Enumerating Add/Remove Programs entries..." +function Get-UninstallEntries { + $roots = @( + "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", + "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", + "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" + ) + foreach ($r in $roots) { + if (Test-Path $r) { + Get-ChildItem $r | ForEach-Object { Get-ItemProperty $_.PsPath } + } + } +} +$uninst = Get-UninstallEntries | Select-Object ` + DisplayName, DisplayVersion, Publisher, InstallLocation, UninstallString, ModifyPath, QuietUninstallString + +# Tokenizer helper +function Normalize([string]$s) { + if (-not $s) { return "" } + return (($s -replace '[^a-zA-Z0-9]+', ' ') -as [string]).Trim().ToLowerInvariant() +} + +# Map ODBC drivers -> installed programs +Write-Host "Mapping ODBC drivers to installed programs..." +$map = foreach ($d in $drivers) { + $nameNorm = Normalize $d.Name + $candidates = $uninst | Where-Object { + $_.DisplayName -and (Normalize $_.DisplayName) -match ($nameNorm -replace '\s+','.*') + } + + if (-not $candidates -and $d.Name -match 'oracle|mysql|postgres|sql\s*server|mariadb|sqlite|snowflake|teradata|ibm|db2|progress|sybase|firebird|hana|vertica|redshift') { + $kw = ($matches[0]) + $candidates = $uninst | Where-Object { $_.DisplayName -match $kw } + } + + [PSCustomObject]@{ + OdbcDriverName = $d.Name + Platform = $d.Platform + DriverVersion = $d.Version + ProductName = ($candidates | Select-Object -First 1 -ExpandProperty DisplayName) + ProductVersion = ($candidates | Select-Object -First 1 -ExpandProperty DisplayVersion) + Publisher = ($candidates | Select-Object -First 1 -ExpandProperty Publisher) + InstallLocation = ($candidates | Select-Object -First 1 -ExpandProperty InstallLocation) + UninstallString = ($candidates | Select-Object -First 1 -ExpandProperty UninstallString) + } +} + +# =========================== +# STEP 3: ADD KNOWN VENDOR LINKS +# =========================== +Write-Host "Attaching known vendor download links..." + +$vendorLinks = @{ + # --- Microsoft --- + 'odbc driver 18 for sql server' = 'https://learn.microsoft.com/sql/connect/odbc/download-odbc-driver-for-sql-server' + 'odbc driver 17 for sql server' = 'https://learn.microsoft.com/sql/connect/odbc/download-odbc-driver-for-sql-server' + 'sql server native client' = 'https://learn.microsoft.com/sql/relational-databases/native-client/applications/installing-sql-server-native-client' + 'microsoft access driver' = 'https://www.microsoft.com/download/details.aspx?id=54920' + 'microsoft excel driver' = 'https://www.microsoft.com/download/details.aspx?id=54920' + 'microsoft text driver' = 'https://www.microsoft.com/download/details.aspx?id=54920' + 'microsoft dbase driver' = 'https://www.microsoft.com/download/details.aspx?id=54920' + 'microsoft odbc for oracle' = 'https://learn.microsoft.com/sql/odbc/microsoft/odbc-driver-for-oracle' + + # --- Oracle --- + 'oracle odbc driver' = 'https://www.oracle.com/database/technologies/releasenote-odbc-ic.html' + 'oracle in oraclient' = 'https://www.oracle.com/database/technologies/releasenote-odbc-ic.html' + + # --- MySQL / MariaDB --- + 'mysql odbc' = 'https://dev.mysql.com/downloads/connector/odbc/' + 'mariadb odbc' = 'https://mariadb.com/products/connectors/odbc/' + + # --- PostgreSQL --- + 'postgresql odbc' = 'https://odbc.postgresql.org/' + 'psqlodbc' = 'https://odbc.postgresql.org/' + + # --- SQLite --- + 'sqlite odbc' = 'https://www.ch-werner.de/sqliteodbc/' + + # --- IBM / Informix --- + 'ibm db2 odbc' = 'https://www.ibm.com/support/pages/db2-clients' + 'informix odbc' = 'https://www.ibm.com/support/pages/informix-odbc-driver' + + # --- Snowflake --- + 'snowflake odbc' = 'https://developers.snowflake.com/odbc/' + + # --- Teradata --- + 'teradata odbc' = 'https://downloads.teradata.com/connectivity/odbc-driver' + + # --- Progress / OpenEdge --- + 'progress openedge odbc' = 'https://www.progress.com/odbc/openedge' + 'openedge odbc' = 'https://www.progress.com/odbc/openedge' + + # --- SAP HANA --- + 'sap hana odbc' = 'https://tools.hana.ondemand.com/#hanatools' + + # --- Amazon Redshift --- + 'redshift odbc' = 'https://docs.aws.amazon.com/redshift/latest/mgmt/configure-odbc-connection.html' + + # --- Vertica --- + 'vertica odbc' = 'https://www.vertica.com/download/vertica/client-drivers/' + + # --- Firebird --- + 'firebird odbc' = 'https://firebirdsql.org/en/odbc-driver/' + + # --- Generic fallback --- + 'generic odbc driver' = 'https://www.microsoft.com/en-us/sql/connect/odbc/' +} + +foreach ($item in $map) { + $norm = Normalize $item.OdbcDriverName + $found = $false + foreach ($key in $vendorLinks.Keys) { + if ($norm -like "*$key*") { + $item | Add-Member -NotePropertyName "DownloadURL" -NotePropertyValue $vendorLinks[$key] + $found = $true + break + } + } + if (-not $found) { + $item | Add-Member -NotePropertyName "DownloadURL" -NotePropertyValue "Search manually" + } +} + +# =========================== +# STEP 4: FIND LOCAL INSTALLER FILES +# =========================== +Write-Host "Scanning for local installers..." +$searchTerms = ($map.OdbcDriverName | Sort-Object -Unique) +$installerHits = @() +foreach ($p in $InstallerHuntPaths) { + Get-ChildItem -Path $p -Recurse -ErrorAction SilentlyContinue -Include *.msi,*.exe | + ForEach-Object { + $fileNorm = Normalize $_.Name + foreach ($term in $searchTerms) { + $t = Normalize $term + if ($fileNorm -match ($t -replace '\s+','.*')) { + $installerHits += [PSCustomObject]@{ + Term = $term + FileName = $_.Name + FullPath = $_.FullName + SizeMB = [math]::Round($_.Length/1MB,2) + LastWrite = $_.LastWriteTime + SearchRoot = $p + } + break + } + } + } +} + +# =========================== +# STEP 5: REPORT OUTPUT +# =========================== +$mapCsv = Join-Path $ExportDir "odbc_driver_product_map.csv" +$map | Export-Csv -Path $mapCsv -NoTypeInformation -Encoding UTF8 +$hitsCsv = Join-Path $ExportDir "odbc_installer_hits.csv" +$installerHits | Export-Csv -Path $hitsCsv -NoTypeInformation -Encoding UTF8 + +$reportMd = Join-Path $ExportDir "ODBC_Migration_Report.md" +@" +# ODBC Migration Report + +**Generated:** $(Get-Date -Format "yyyy-MM-dd HH:mm:ss") +**Machine:** $env:COMPUTERNAME + +## Summary +- Drivers detected: $($drivers.Count) +- DSNs exported: $((Get-OdbcDsn).Count) +- Installer hits: $($installerHits.Count) + +## Drivers & Product Mapping +| ODBC Driver | Platform | Version | Product | Publisher | Download Link | +|---|---:|---|---|---|---| +$( + ($map | ForEach-Object { + $dn = if ($_.OdbcDriverName) { $_.OdbcDriverName.Replace('|','-') } else { '' } + $pf = $_.Platform + $dv = $_.DriverVersion + $pn = ($_.ProductName -replace '\|','-') + $pu = ($_.Publisher -replace '\|','-') + $dl = if ($_.DownloadURL -match '^https?') { "[Download]($($_.DownloadURL))" } else { $_.DownloadURL } + "| $dn | $pf | $dv | $pn | $pu | $dl |" + }) -join "`r`n" +) + +## Likely Local Installer Files +| Matches Term | File | Size (MB) | Modified | Path | +|---|---|---:|---|---| +$( + ($installerHits | Sort-Object Term,FileName | ForEach-Object { + "| $($_.Term) | $($_.FileName) | $($_.SizeMB) | $($_.LastWrite.ToString('yyyy-MM-dd')) | $($_.FullPath) |" + }) -join "`r`n" +) + +## Next Steps +1. On the **target PC**, install matching ODBC driver packages using the links above or your local installers. +2. Then run **Restore-ODBC.ps1** to import DSNs and registry settings. +3. Verify DSNs in *ODBC Data Source Administrator* (both 32-bit and 64-bit). +"@ | Out-File -FilePath $reportMd -Encoding UTF8 + +# =========================== +# STEP 6: ZIP PACKAGE +# =========================== +if (Test-Path $ZipFile) { Remove-Item $ZipFile -Force } +Compress-Archive -Path "$ExportDir\*" -DestinationPath $ZipFile -Force + +Write-Host "Export complete. ZIP created at: $ZipFile" -ForegroundColor Green +Stop-Transcript diff --git a/restore-ODBC.ps1 b/restore-ODBC.ps1 new file mode 100644 index 0000000..1b71c36 --- /dev/null +++ b/restore-ODBC.ps1 @@ -0,0 +1,115 @@ +<# +.SYNOPSIS + Restore ODBC DSNs and registry keys, and identify missing drivers. + +.DESCRIPTION + - Extracts ODBC_Backup.zip into C:\ODBC_Migration\restore + - Imports all .reg and .xml files + - Recreates DSNs + - Compares exported vs installed ODBC drivers + - Displays missing drivers with official vendor download links + - Logs all operations +#> + +# =========================== +# CONFIGURATION +# =========================== +$BaseDir = "C:\ODBC_Migration" +$ZipFile = Join-Path $BaseDir "ODBC_Backup.zip" +$ExtractDir = Join-Path $BaseDir "restore" +$LogFile = Join-Path $BaseDir ("restore_log_" + (Get-Date -Format "yyyyMMdd_HHmm") + ".txt") + +# =========================== +# PREP +# =========================== +New-Item -ItemType Directory -Path $ExtractDir -Force | Out-Null +Start-Transcript -Path $LogFile -Append +Write-Host "`n=== ODBC RESTORE STARTED ===`n" -ForegroundColor Cyan + +# =========================== +# STEP 1: EXTRACT ZIP PACKAGE +# =========================== +if (-not (Test-Path $ZipFile)) { + Write-Error "Cannot find ODBC_Backup.zip in $BaseDir" + Stop-Transcript + exit 1 +} +Write-Host "Extracting archive..." +Expand-Archive -Path $ZipFile -DestinationPath $ExtractDir -Force + +# =========================== +# STEP 2: IMPORT REGISTRY KEYS +# =========================== +Write-Host "Importing registry keys..." +$regFiles = Get-ChildItem $ExtractDir -Filter *.reg +foreach ($reg in $regFiles) { + Write-Host " -> $($reg.Name)" + reg import $reg.FullName | Out-Null +} + +# =========================== +# STEP 3: RESTORE DSNs +# =========================== +$xmlPath = Join-Path $ExtractDir "odbc_dsns.xml" +if (Test-Path $xmlPath) { + Write-Host "Recreating DSNs from XML..." + $dsns = Import-Clixml $xmlPath + foreach ($dsn in $dsns) { + try { + Add-OdbcDsn @dsn -ErrorAction Stop + Write-Host " + Added DSN: $($dsn.Name)" -ForegroundColor Green + } catch { + Write-Warning " ! Failed to add DSN: $($dsn.Name) - $_" + } + } +} else { + Write-Warning "No odbc_dsns.xml found in package." +} + +# =========================== +# STEP 4: VERIFY DRIVER PRESENCE +# =========================== +Write-Host "`nChecking for missing ODBC drivers..." -ForegroundColor Yellow +$exportCsv = Join-Path $ExtractDir "odbc_driver_product_map.csv" +if (-not (Test-Path $exportCsv)) { + Write-Warning "No driver map CSV found — skipping driver comparison." +} else { + $needed = Import-Csv $exportCsv + $installed = Get-OdbcDriver | Select-Object -ExpandProperty Name + + $missing = @() + foreach ($n in $needed) { + if ($n.OdbcDriverName -and ($installed -notcontains $n.OdbcDriverName)) { + $missing += $n + } + } + + if ($missing.Count -gt 0) { + Write-Host "`n=== Missing ODBC Drivers Detected ===`n" -ForegroundColor Red + foreach ($m in $missing) { + $dl = $m.DownloadURL + if ($dl -match '^https?') { + Write-Host (" - {0} ({1})`n Download: {2}" -f $m.OdbcDriverName, $m.Platform, $dl) -ForegroundColor Yellow + } else { + Write-Host (" - {0} ({1})`n Download: [Manual search required]" -f $m.OdbcDriverName, $m.Platform) -ForegroundColor DarkYellow + } + } + Write-Host "`nSee the detailed Markdown report for clickable links:" -ForegroundColor Cyan + Write-Host " $($ExtractDir)\ODBC_Migration_Report.md`n" -ForegroundColor White + } else { + Write-Host "All previously detected drivers are installed." -ForegroundColor Green + } +} + +# =========================== +# STEP 5: SUMMARY +# =========================== +Write-Host "`n=== RESTORE COMPLETE ===`n" -ForegroundColor Cyan +Write-Host "✔ Registry and DSNs restored" +Write-Host "✔ Logs saved to: $LogFile" +Write-Host "✔ Extracted files in: $ExtractDir" +Write-Host "`nNext Steps:" +Write-Host "1️⃣ Verify DSNs in ODBC Administrator (32-bit and 64-bit)." +Write-Host "2️⃣ Install any missing drivers listed above." +Write-Host "3️⃣ Re-run this script if needed after driver installation.`n" +Stop-Transcript