const replacements = [ { id: 'MIG-FN-EXECUTE-PROCESS', label: 'Execute-Process to Start-ADTProcess', pattern: /\bExecute-Process\b/g, replacement: 'Start-ADTProcess', automatic: true }, { id: 'MIG-FN-EXECUTE-MSI', label: 'Execute-MSI to Start-ADTMsiProcess', pattern: /\bExecute-MSI\b/g, replacement: 'Start-ADTMsiProcess', automatic: true }, { id: 'MIG-FN-EXECUTE-MSP', label: 'Execute-MSP to Start-ADTMspProcess', pattern: /\bExecute-MSP\b/g, replacement: 'Start-ADTMspProcess', automatic: true }, { id: 'MIG-FN-WELCOME', label: 'Show-InstallationWelcome to Show-ADTInstallationWelcome', pattern: /\bShow-InstallationWelcome\b/g, replacement: 'Show-ADTInstallationWelcome', automatic: true }, { id: 'MIG-FN-PROMPT', label: 'Show-InstallationPrompt to Show-ADTInstallationPrompt', pattern: /\bShow-InstallationPrompt\b/g, replacement: 'Show-ADTInstallationPrompt', automatic: true }, { id: 'MIG-FN-PROGRESS', label: 'Show-InstallationProgress to Show-ADTInstallationProgress', pattern: /\bShow-InstallationProgress\b/g, replacement: 'Show-ADTInstallationProgress', automatic: true }, { id: 'MIG-FN-RESTART', label: 'Show-InstallationRestartPrompt to Show-ADTInstallationRestartPrompt', pattern: /\bShow-InstallationRestartPrompt\b/g, replacement: 'Show-ADTInstallationRestartPrompt', automatic: true }, { id: 'MIG-FN-LOG', label: 'Write-Log to Write-ADTLogEntry', pattern: /\bWrite-Log\b/g, replacement: 'Write-ADTLogEntry', automatic: true }, { id: 'MIG-FN-REMOVE-MSI', label: 'Remove-MSIApplications to Uninstall-ADTApplication', pattern: /\bRemove-MSIApplications\b/g, replacement: 'Uninstall-ADTApplication', automatic: true }, { id: 'MIG-FN-HKCU', label: 'Invoke-HKCURegistrySettingsForAllUsers to Invoke-ADTAllUsersRegistryAction', pattern: /\bInvoke-HKCURegistrySettingsForAllUsers\b/g, replacement: 'Invoke-ADTAllUsersRegistryAction', automatic: true }, { id: 'MIG-NAME-DEPLOY-PS1', label: 'Deploy-Application.ps1 to Invoke-AppDeployToolkit.ps1', pattern: /\bDeploy-Application\.ps1\b/g, replacement: 'Invoke-AppDeployToolkit.ps1', automatic: true }, { id: 'MIG-NAME-DEPLOY-EXE', label: 'Deploy-Application.exe to Invoke-AppDeployToolkit.exe', pattern: /\bDeploy-Application\.exe\b/g, replacement: 'Invoke-AppDeployToolkit.exe', automatic: true }, { id: 'MIG-VAR-APPNAME', label: '$appName to $adtSession.AppName', pattern: /\$appName\b/g, replacement: '$adtSession.AppName', automatic: true }, { id: 'MIG-VAR-APPVERSION', label: '$appVersion to $adtSession.AppVersion', pattern: /\$appVersion\b/g, replacement: '$adtSession.AppVersion', automatic: true }, { id: 'MIG-VAR-APPVENDOR', label: '$appVendor to $adtSession.AppVendor', pattern: /\$appVendor\b/g, replacement: '$adtSession.AppVendor', automatic: true }, { id: 'MIG-VAR-APPARCH', label: '$appArch to $adtSession.AppArch', pattern: /\$appArch\b/g, replacement: '$adtSession.AppArch', automatic: true }, { id: 'MIG-VAR-APPLANG', label: '$appLang to $adtSession.AppLang', pattern: /\$appLang\b/g, replacement: '$adtSession.AppLang', automatic: true }, { id: 'MIG-VAR-APPREVISION', label: '$appRevision to $adtSession.AppRevision', pattern: /\$appRevision\b/g, replacement: '$adtSession.AppRevision', automatic: true }, { id: 'MIG-MODULE-420', label: 'Pin PSAppDeployToolkit ModuleVersion to 4.2.0', pattern: /ModuleVersion\s*=\s*['"][0-9.]+['"]/g, replacement: "ModuleVersion = '4.2.0'", automatic: true } ]; const manualChecks = [ { id: 'MANUAL-SERVICEUI', severity: 'critical', pattern: /\b(ServiceUI\.exe|Invoke-ServiceUI\.ps1)\b/i, title: 'Remove ServiceUI launch wrappers', guidance: 'PSADT v4.1+ provides native user-session UI for Intune. Remove ServiceUI and test with SYSTEM context launch helpers.' }, { id: 'MANUAL-TEMPLATE-SHAPE', severity: 'warning', pattern: /function\s+(Install|Uninstall|Repair)-ADTDeployment/i, title: 'Older function-based deployment phase shape detected', guidance: 'Review the migrated script against the upstream v4 template that uses New-Variable phase scriptblocks and Open-ADTSession.' }, { id: 'MANUAL-RAW-MSIEXEC', severity: 'warning', pattern: /\bmsiexec(?:\.exe)?\b/i, title: 'Raw msiexec call needs installer review', guidance: 'Prefer Start-ADTMsiProcess or Start-ADTMspProcess so logging, success codes, and reboot handling stay consistent.' }, { id: 'MANUAL-INTUNE-DEFER', severity: 'warning', pattern: /Show-ADTInstallationWelcome[^\n\r]*-AllowDefer\b(?![^\n\r]*-DeferRunInterval)/i, title: 'Intune deferral flow should include DeferRunInterval', guidance: 'Add -DeferRunInterval to avoid repeated Intune retry prompts after a 1602 defer result.' } ]; export function planPsadtMigration(content = '', options = {}) { const source = String(content || ''); const steps = []; let automaticCount = 0; for (const rule of replacements) { const matches = [...source.matchAll(rule.pattern)].filter((match) => !isCommentLine(source, match.index || 0)); if (!matches.length) continue; automaticCount += matches.length; steps.push({ id: rule.id, type: 'replace', automatic: rule.automatic, status: 'ready', title: rule.label, count: matches.length, lines: matches.slice(0, 8).map((match) => lineNumber(source, match.index || 0)), replacement: rule.replacement }); } for (const rule of manualChecks) { const match = rule.pattern.exec(source); if (!match || isCommentLine(source, match.index || 0)) continue; steps.push({ id: rule.id, type: 'manual-review', automatic: false, status: 'review', severity: rule.severity, title: rule.title, guidance: rule.guidance, lines: [lineNumber(source, match.index || 0)] }); } if (!/Open-ADTSession/i.test(source)) { steps.push({ id: 'MANUAL-OPEN-ADTSESSION', type: 'manual-review', automatic: false, status: 'review', severity: 'warning', title: 'Open-ADTSession not detected', guidance: 'Latest PSADT scripts should initialize the module and open an ADTSession before invoking phase logic.', lines: [] }); } return { targetVersion: options.targetVersion || '4.2.0', summary: { steps: steps.length, automatic: steps.filter((step) => step.automatic).length, manual: steps.filter((step) => !step.automatic).length, replacements: automaticCount }, steps, migratedContent: applyPsadtMigration(source) }; } export function applyPsadtMigration(content = '') { let migrated = String(content || ''); for (const rule of replacements) { migrated = replaceOutsideComments(migrated, rule.pattern, rule.replacement); } migrated = migrated.replace(/(Show-ADTInstallationPrompt[^\n\r]*)\s-Icon\s+\S+/gi, '$1'); if (!/POSHManager PSADT migration notes/i.test(migrated)) { migrated = [ '# POSHManager PSADT migration notes:', '# - Automatic replacements were applied for known v3/v4 legacy names and variables.', '# - Review manual migration findings before deploying through Intune or any production channel.', '', migrated ].join('\n'); } return migrated; } function replaceOutsideComments(content, pattern, replacement) { return content.split(/\r?\n/).map((line) => { if (line.trimStart().startsWith('#')) return line; return line.replace(pattern, replacement); }).join('\n'); } function isCommentLine(content, index) { const line = content.slice(0, index).split(/\r?\n/).length; return content.split(/\r?\n/)[line - 1]?.trimStart().startsWith('#'); } function lineNumber(content, index) { return content.slice(0, index).split(/\r?\n/).length; }