From 8c60e154a7ef20346b2ea8c2d25ae7f667f4efe5 Mon Sep 17 00:00:00 2001 From: mpuckett Date: Thu, 25 Jun 2026 12:30:55 -0500 Subject: [PATCH] Initial --- .gitignore | 4 +- package.json | 2 +- scripts/ensure-build.mjs | 5 +- scripts/smoke-api-import.mjs | 2 + scripts/start-api.mjs | 8 +- scripts/start-ui-prod.mjs | 3 +- server/data/graphHosts.js | 38 +++ server/data/helpCatalog.js | 297 ++++++++++++++++++++++ server/data/installerCatalog.js | 107 ++++++++ server/data/intuneCatalog.js | 70 ++++++ server/data/psadtCatalog.js | 365 ++++++++++++++++++++++++++++ server/data/psadtValidationRules.js | 191 +++++++++++++++ server/data/variableCatalog.js | 84 +++++++ 13 files changed, 1163 insertions(+), 13 deletions(-) create mode 100644 scripts/smoke-api-import.mjs create mode 100644 server/data/graphHosts.js create mode 100644 server/data/helpCatalog.js create mode 100644 server/data/installerCatalog.js create mode 100644 server/data/intuneCatalog.js create mode 100644 server/data/psadtCatalog.js create mode 100644 server/data/psadtValidationRules.js create mode 100644 server/data/variableCatalog.js diff --git a/.gitignore b/.gitignore index 17ace3f..9cc1f76 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ node_modules/ dist/ -data/ +/data/ .env .env.* !.env.example @@ -16,4 +16,4 @@ tools/*.exe /output PLAN*.MD PLAN-INTUNE.md -project.txt \ No newline at end of file +project.txt diff --git a/package.json b/package.json index 95b393b..7256334 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "concurrently -n API,UI -c cyan,magenta \"npm run api:dev\" \"npm run ui:dev\"", - "prod": "node scripts/ensure-build.mjs && concurrently -n API,UI -c cyan,magenta \"npm run api:prod\" \"npm run ui:prod\"", + "prod": "node scripts/ensure-build.mjs && node scripts/smoke-api-import.mjs && concurrently -n API,UI -c cyan,magenta \"npm run api:prod\" \"npm run ui:prod\"", "api:dev": "node scripts/start-api.mjs development --watch", "api:prod": "node scripts/start-api.mjs production", "ui:dev": "vite --host 0.0.0.0 --port 3000 --strictPort", diff --git a/scripts/ensure-build.mjs b/scripts/ensure-build.mjs index 1608292..04827bd 100644 --- a/scripts/ensure-build.mjs +++ b/scripts/ensure-build.mjs @@ -5,9 +5,6 @@ if (fs.existsSync('dist/index.html')) { process.exit(0); } -const result = spawnSync('npm', ['run', 'build'], { - stdio: 'inherit', - shell: process.platform === 'win32' -}); +const result = spawnSync(process.execPath, ['node_modules/vite/bin/vite.js', 'build'], { stdio: 'inherit' }); process.exit(result.status ?? 1); diff --git a/scripts/smoke-api-import.mjs b/scripts/smoke-api-import.mjs new file mode 100644 index 0000000..8102060 --- /dev/null +++ b/scripts/smoke-api-import.mjs @@ -0,0 +1,2 @@ +await import('../server/forms/schemas.js'); +await import('../server/services/graphService.js'); diff --git a/scripts/start-api.mjs b/scripts/start-api.mjs index 76e5905..3c933ca 100644 --- a/scripts/start-api.mjs +++ b/scripts/start-api.mjs @@ -2,12 +2,12 @@ import { spawn } from 'node:child_process'; const mode = process.argv[2] || 'development'; const watch = process.argv.includes('--watch'); -const command = watch ? 'nodemon' : 'node'; -const args = watch ? ['server/index.js'] : ['server/index.js']; +const args = watch + ? ['node_modules/nodemon/bin/nodemon.js', 'server/index.js'] + : ['server/index.js']; -const child = spawn(command, args, { +const child = spawn(process.execPath, args, { stdio: 'inherit', - shell: process.platform === 'win32', env: { ...process.env, NODE_ENV: mode diff --git a/scripts/start-ui-prod.mjs b/scripts/start-ui-prod.mjs index 21b87b0..8f2baf8 100644 --- a/scripts/start-ui-prod.mjs +++ b/scripts/start-ui-prod.mjs @@ -1,8 +1,7 @@ import { spawn } from 'node:child_process'; -const child = spawn('vite', ['preview', '--host', '0.0.0.0', '--port', '3000', '--strictPort'], { +const child = spawn(process.execPath, ['node_modules/vite/bin/vite.js', 'preview', '--host', '0.0.0.0', '--port', '3000', '--strictPort'], { stdio: 'inherit', - shell: process.platform === 'win32', env: { ...process.env, NODE_ENV: 'production' diff --git a/server/data/graphHosts.js b/server/data/graphHosts.js new file mode 100644 index 0000000..adb90be --- /dev/null +++ b/server/data/graphHosts.js @@ -0,0 +1,38 @@ +// Allow-list of Microsoft Graph and Entra (Azure AD) login hosts across the +// public, US Government, DoD, and China sovereign clouds. User-supplied Graph +// connection URLs are validated against this list so the server cannot be +// pointed at arbitrary internal endpoints (SSRF). + +export const ALLOWED_GRAPH_HOSTS = new Set([ + 'graph.microsoft.com', + 'graph.microsoft.us', + 'dod-graph.microsoft.us', + 'microsoftgraph.chinacloudapi.cn' +]); + +export const ALLOWED_AUTHORITY_HOSTS = new Set([ + 'login.microsoftonline.com', + 'login.microsoftonline.us', + 'login.partner.microsoftonline.cn', + 'login.chinacloudapi.cn' +]); + +function hostnameOf(url) { + try { + const parsed = new URL(String(url)); + if (parsed.protocol !== 'https:') return null; + return parsed.hostname.toLowerCase(); + } catch { + return null; + } +} + +export function isAllowedGraphHost(url) { + const host = hostnameOf(url); + return Boolean(host && ALLOWED_GRAPH_HOSTS.has(host)); +} + +export function isAllowedAuthorityHost(url) { + const host = hostnameOf(url); + return Boolean(host && ALLOWED_AUTHORITY_HOSTS.has(host)); +} diff --git a/server/data/helpCatalog.js b/server/data/helpCatalog.js new file mode 100644 index 0000000..7e5633f --- /dev/null +++ b/server/data/helpCatalog.js @@ -0,0 +1,297 @@ +import { psadtCatalog } from './psadtCatalog.js'; + +const powershellCoreUrl = 'https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/?view=powershell-5.1'; + +function article(id, category, title, summary, sections, tags = []) { + return { id, category, title, summary, sections, tags }; +} + +function section(heading, body = [], code = '') { + return { heading, body, code }; +} + +function apiExample(method, route, description, body = '') { + const code = [ + '$base = "http://localhost:3000"', + '$token = (Invoke-RestMethod -Method Post -Uri "$base/api/auth/login" -ContentType "application/json" -Body (@{ email = "admin@posh.local"; password = "change-me-now" } | ConvertTo-Json)).token', + '$headers = @{ Authorization = "Bearer $token" }', + body + ? `Invoke-RestMethod -Method ${method} -Uri "$base${route}" -Headers $headers -ContentType "application/json" -Body (${body} | ConvertTo-Json -Depth 10)` + : `Invoke-RestMethod -Method ${method} -Uri "$base${route}" -Headers $headers` + ].join('\n'); + return section(`${method} ${route}`, [description], code); +} + +const operationsArticles = [ + article('ops-overview', 'Operations', 'Application Operations Overview', 'How operators move through POSHManager day to day.', [ + section('Daily workflow', [ + 'Build scripts in the Scripts workspace, assign credentials to hosts in Host Library and Credential Vault, create RunPlans, execute jobs, then review job and system logs.', + 'The UI is intentionally API-first. Every create, update, delete, execute, migration, and publishing action calls the Express API so automation can use the same workflows.' + ]), + section('Navigation map', [ + 'Dashboard shows metrics, charts, recent jobs, and configurable widgets.', + 'Scripts contains the VS Code-style Monaco PowerShell editor, script library, variable studio, and metadata panel.', + 'PSADT contains the deployment factory, verifier, migration wizard, and Intune Publishing Wizard.', + 'Hosts, Credential Vault, RunPlans, Logs, Users & Groups, and Config are separate operational surfaces.' + ]) + ], ['dashboard', 'operations', 'navigation']), + article('ops-scripts', 'Operations', 'Scripts And Variables', 'Create folders, author scripts, use variables, and keep versions.', [ + section('Script storage', [ + 'Use the Script Library rail to create folders and scripts. Right-click folders or scripts for contextual create/delete actions.', + 'Scripts are versioned by the API. Saving an existing script creates a new script_versions row.' + ]), + section('Variables', [ + 'Open Variable Studio from the script toolbar. POSHManager runtime variables can be inserted as PowerShell variables or token syntax.', + 'Custom variables are encrypted at rest and can be personal, shared, or group-scoped.', + 'PSADT reference variables are cataloged for authoring, but POSHManager does not fake PSADT runtime values. Those are available only inside a valid PSADT session.', + 'The editor autocomplete suggests PowerShell cmdlets, PSADT functions, PSADT snippets, POSHManager variables, custom variables, and Asset Library reference tokens.' + ]) + ], ['scripts', 'variables', 'monaco']), + article('ops-assets', 'Operations', 'Asset Library', 'Upload, organize, link, preview, and reference package assets.', [ + section('What belongs here', [ + 'Use Assets for installers, archives, icons, detection scripts, configs, PSADT Files content, SupportFiles content, and reusable files referenced by normal PowerShell scripts.', + 'The library has its own folder tree, separate from script folders, so deployment package content can be organized by app, vendor, platform, or lifecycle stage.', + 'Uploaded files are handled by multer and stored under the API FileLocker tree by asset type: packages, images, scripts, configs, documents, or generic.' + ]), + section('Using assets in scripts and packages', [ + 'Each asset has a reference token such as {{asset:ast_123}}. Insert the token from the asset details panel into a script when you need the API container file path during RunPlan execution.', + 'Each asset also has a package path such as Files/setup.exe or SupportFiles/config.json. PSADT and Intune package-building workflows use that path to stage content into the correct deployment structure.', + 'Assets can be linked to scripts, RunPlans, PSADT profiles, or Intune deployment plans with a role such as installer, support-file, icon, detection-script, or reference.' + ]) + ], ['assets', 'packages', 'psadt', 'intune', 'uploads']), + article('ops-hosts-runplans', 'Operations', 'Hosts, Credential Vault, RunPlans, And Jobs', 'Manage target systems and execute scripts safely.', [ + section('Hosts and credentials', [ + 'Create credentials in Credential Vault first, then attach them to hosts. Secrets are encrypted using AES-256-GCM.', + 'Hosts support WinRM, SSH, local, and API transport metadata. PowerShell remoting and firewall prerequisites must be configured outside POSHManager.' + ]), + section('RunPlans', [ + 'A RunPlan joins one script with one or more hosts. The backend creates a job and host-level log rows during execution.', + 'RunPlans can be personal, shared, or group-scoped. Use the execute action to queue the plan.' + ]), + section('Logs', [ + 'Job logs are aggregated by job and host. System Logs show Winston request/application logs for API request and response troubleshooting.' + ]) + ], ['hosts', 'credentials', 'runplans', 'jobs', 'logs']), + article('ops-admin-config', 'Operations', 'Administration And Configuration', 'Manage users, groups, settings, themes, and deployment variables.', [ + section('Users and groups', [ + 'Users & Groups is a dedicated administration menu item. Add users and groups through modal create flows.', + 'Scripts, RunPlans, variables, PSADT profiles, and Intune deployment plans can use personal, shared, or group visibility.' + ]), + section('Configuration', [ + 'Config is grouped into collapsible sections for Deployment, Authentication, Execution, Runtime, and Other settings, with Microsoft Graph Intune environments managed in the Config / Intune panel.', + 'Settings can be controlled through the UI and environment variables for Docker deployment. Trusted origins, server FQDN, Entra fields, PowerShell binary, and script execution switch are key operational settings.' + ]) + ], ['admin', 'groups', 'settings', 'docker']) +]; + +const apiArticles = [ + article('api-auth-bootstrap', 'API', 'Authentication And Bootstrap API', 'Login, profile, health, and bootstrap examples with PowerShell.', [ + apiExample('Post', '/api/auth/login', 'Authenticate with local credentials and receive a JWT token.', '@{ email = "admin@posh.local"; password = "change-me-now" }'), + apiExample('Get', '/api/auth/me', 'Return the current authenticated user.'), + apiExample('Put', '/api/auth/profile', 'Update display name, profile fields, and avatar URL.', '@{ displayName = "POSH Admin"; email = "admin@posh.local"; jobTitle = "Platform Administrator"; phone = ""; timezone = "America/Chicago"; avatarUrl = "" }'), + apiExample('Get', '/api/bootstrap', 'Return dashboard summary, settings payload, and Entra status.'), + section('Notes', [ + 'Use the Bearer token returned by /api/auth/login for every protected request.', + 'The default development login is seeded only when the users table is empty. Change defaults for real deployments.' + ]) + ], ['api', 'auth', 'bootstrap', 'powershell']), + article('api-library', 'API', 'Script Library API', 'Folder and script API usage with PowerShell examples.', [ + apiExample('Get', '/api/folders', 'List visible folders.'), + apiExample('Post', '/api/folders', 'Create a script folder.', '@{ name = "Deployments"; parentId = $null; visibility = "shared" }'), + apiExample('Get', '/api/scripts', 'List visible scripts.'), + apiExample('Post', '/api/scripts', 'Create a script and initial version.', '@{ name = "Health Check.ps1"; folderId = $null; description = "Simple host health check"; content = "Get-Date"; visibility = "personal" }'), + apiExample('Put', '/api/scripts/scr_123', 'Update a script. Replace scr_123 with the script id.', '@{ name = "Health Check.ps1"; folderId = $null; description = "Updated"; content = "Get-ComputerInfo"; visibility = "personal" }') + ], ['api', 'scripts', 'folders']), + article('api-assets', 'API', 'Asset Library API', 'Upload and manage package assets with PowerShell examples.', [ + apiExample('Get', '/api/assets/folders', 'List visible asset folders.'), + apiExample('Post', '/api/assets/folders', 'Create an asset folder.', '@{ name = "Contoso VPN"; parentId = $null; description = "VPN package assets"; visibility = "shared" }'), + apiExample('Get', '/api/assets', 'List visible assets with referenceToken, packagePath, checksum, viewUrl, and downloadUrl metadata.'), + section('POST /api/assets', [ + 'Upload an asset with multipart form-data. The file field must be named file; metadata fields such as folderId, name, description, packagePath, visibility, and groupId travel alongside it.', + 'The API uses multer to stage uploads into FileLocker/_incoming, then promotes them into FileLocker/assets///.' + ], '$file = "/packages/install-notes.txt"\n$form = @{ file = Get-Item $file; folderId = ""; name = "install-notes.txt"; description = "Package note"; packagePath = "SupportFiles/install-notes.txt"; visibility = "shared" }\nInvoke-RestMethod -Method Post -Uri "$base/api/assets" -Headers $headers -Form $form'), + apiExample('Get', '/api/assets/ast_123/download', 'Download an asset. Replace ast_123 with the asset id.'), + apiExample('Post', '/api/assets/ast_123/links', 'Link an asset to a script, RunPlan, PSADT profile, or Intune deployment.', '@{ targetType = "script"; targetId = "scr_123"; linkRole = "reference"; packagePath = "SupportFiles/install-notes.txt" }') + ], ['api', 'assets', 'uploads', 'packages']), + article('api-hosts-credentials-runplans', 'API', 'Hosts, Credentials, RunPlans, Jobs, And Logs API', 'Manage execution targets and inspect results through the API.', [ + apiExample('Post', '/api/credentials', 'Create an encrypted username/password credential.', '@{ name = "WinRM Admin"; kind = "username_password"; username = "CONTOSO\\svc-posh"; secret = "change-me"; visibility = "personal" }'), + apiExample('Post', '/api/hosts', 'Create a managed host.', '@{ name = "APP01"; address = "app01.contoso.local"; fqdn = "app01.contoso.local"; osFamily = "windows"; transport = "winrm"; port = 5986; credentialId = $null; tags = @("prod","web"); notes = "" }'), + apiExample('Post', '/api/runplans', 'Create a RunPlan. hostIds should contain existing host ids.', '@{ name = "Patch Validation"; description = "Run validation script"; scriptId = "scr_123"; visibility = "shared"; parallel = $true; hostIds = @("hst_123") }'), + apiExample('Post', '/api/runplans/rp_123/execute', 'Execute a RunPlan and create a job. Replace rp_123 with the RunPlan id.'), + apiExample('Get', '/api/jobs', 'List job history.'), + apiExample('Get', '/api/jobs/job_123', 'Read one job with host rows and logs.'), + apiExample('Get', '/api/logs/system', 'Read system/request logs from the Winston log stream.') + ], ['api', 'credentials', 'hosts', 'runplans', 'jobs']), + article('api-psadt', 'API', 'PSADT And Intune API', 'Use the PSADT catalog, verifier, migration wizard, profiles, and Intune publishing plans through API calls.', [ + apiExample('Get', '/api/psadt/catalog', 'Return PSADT source links, module/function/ADMX catalogs, snippets, and Intune publishing guidance.'), + apiExample('Post', '/api/psadt/validate', 'Validate raw PSADT script content.', '@{ platform = "intune"; content = "Invoke-AppDeployToolkit.exe -DeploymentType Install" }'), + apiExample('Post', '/api/psadt/migration/plan', 'Plan a legacy script migration to PSADT 4.2.0.', `@{ targetVersion = "4.2.0"; content = "Deploy-Application.ps1\`nExecute-Process -Path setup.exe\`n$appName = 'Legacy App'" }`), + apiExample('Post', '/api/psadt/profiles', 'Create a reusable PSADT deployment profile.', '@{ name = "Edge Enterprise"; appVendor = "Microsoft"; appName = "Edge"; appVersion = "126.0"; appArch = "x64"; appLang = "EN"; appRevision = "01"; templateVersion = "v4"; deploymentType = "Install"; deployMode = "Auto"; requireAdmin = $true; zeroConfig = $false; suppressReboot = $false; closeProcesses = @(); installTasks = @(@{ type = "exe"; phase = "Install"; filePath = "setup.exe"; arguments = "/S"; secureArguments = $false }); uiPlan = @{ welcome = $true }; configPlan = @{}; admxPlan = @{}; visibility = "shared" }'), + apiExample('Post', '/api/psadt/intune/deployments', 'Create an Intune Publishing Wizard plan.', '@{ name = "Edge Pilot"; sourceFolder = "C:\\Packages\\Edge\\PSADT"; intunewinFile = "Edge.intunewin"; commandStyle = "v4"; installBehavior = "system"; restartBehavior = "return-code"; uiMode = "native-v4"; requirements = @{ architecture = "x64"; minOs = "Windows 10 22H2"; diskSpaceMb = 512; runAs32Bit = $false }; installCommand = "Invoke-AppDeployToolkit.exe -DeploymentType Install"; uninstallCommand = "Invoke-AppDeployToolkit.exe -DeploymentType Uninstall"; detectionType = "custom-script"; detectionRule = "Write-Output detected"; returnCodes = @(@{ code = 0; type = "success"; meaning = "OK" }, @{ code = 1602; type = "retry"; meaning = "Deferred" }, @{ code = 3010; type = "softReboot"; meaning = "Reboot" }); assignments = @(@{ ring = "Pilot"; intent = "available"; groupName = "IT Pilot"; notes = "Validate" }); status = "ready"; visibility = "shared" }') + ], ['api', 'psadt', 'intune', 'migration']) + , + article('api-graph-intune', 'API', 'Microsoft Graph Intune Tracking API', 'Configure Graph environments in Config / Intune and sync Intune deployment status for PSADT publishing plans.', [ + apiExample('Post', '/api/graph/connections', 'Create a Microsoft Graph environment. In the UI this belongs under Config / Intune. The client secret is encrypted and never returned.', '@{ name = "Production Intune"; tenantId = "contoso.onmicrosoft.com"; clientId = "00000000-0000-0000-0000-000000000000"; clientSecret = "secret"; cloud = "global"; graphBaseUrl = "https://graph.microsoft.com"; authorityHost = "https://login.microsoftonline.com"; defaultApiVersion = "v1.0"; enabled = $true; visibility = "shared" }'), + apiExample('Post', '/api/graph/connections/graph_123/test', 'Test token acquisition and Intune app read access. Replace graph_123 with the connection id.'), + apiExample('Get', '/api/graph/connections/graph_123/mobile-apps?q=Edge', 'List Intune mobile apps visible to the Graph app registration.'), + apiExample('Post', '/api/psadt/intune/deployments/intune_123/graph/link', 'Link a POSHManager Intune deployment plan to an Intune mobileApp id.', '@{ connectionId = "graph_123"; graphAppId = "intune-mobile-app-id" }'), + apiExample('Post', '/api/psadt/intune/deployments/intune_123/graph/sync', 'Sync device/user install status and failure details from Microsoft Graph Intune reports.', '@{ connectionId = "graph_123"; graphAppId = "intune-mobile-app-id"; mode = "reports" }'), + apiExample('Get', '/api/psadt/intune/deployments/intune_123/graph/status', 'Read stored status rows, failure rows, and sync run history for a deployment.') + ], ['api', 'graph', 'intune', 'failures']) +]; + +const psadtArticles = [ + article('psadt-integration-overview', 'PSADT', 'PSADT Integration Overview', 'How POSHManager supports PSAppDeployToolkit authoring, verification, migration, and publishing.', [ + section('What is modeled', [ + 'POSHManager catalogs PSADT variables, functions, ADMX policy surface, deployment structure, launch commands, snippets, and upstream v4 template shape.', + 'Profiles generate Invoke-AppDeployToolkit scaffolds and Intune-ready commands. The verifier checks known best practices and gotchas. The migration wizard upgrades common legacy patterns.' + ]), + section('What is not faked', [ + 'POSHManager does not emulate PSADT runtime session variables. They exist when PSADT opens an ADTSession during deployment.', + 'POSHManager does not redistribute PSADT modules/templates. Operators bring the actual PSADT deployment package content.' + ]) + ], ['psadt', 'integration']), + article('psadt-authoring', 'PSADT', 'Authoring PSADT Scripts', 'Use profiles, variables, snippets, and generated scaffolds to create package scripts.', [ + section('Recommended authoring path', [ + 'Create a PSADT profile with application metadata, deployment type, install tasks, close-process list, and UI plan.', + 'Render or create the script from the profile, then continue editing in the Scripts workspace.', + 'Use Variable Studio for PSADT variables and POSHManager custom variables. Use snippets for common UI, installer, reboot, and logging patterns.' + ]), + section('Session guidance', [ + 'Current v4 scaffolds use ADTSession metadata, package-local module import, Remove-ADTHashtableNullOrEmptyValues, Open-ADTSession, phase invocation, and Close-ADTSession.', + 'DeployMode should remain Auto unless the package intentionally requires Interactive, NonInteractive, or Silent.' + ]) + ], ['psadt', 'authoring', 'profiles']), + article('psadt-verifier-migration', 'PSADT', 'Best Practice Verifier And Migration Wizard', 'Validate and migrate PSADT scripts before publishing.', [ + section('Verifier targets', [ + 'Validate a saved script, a rendered PSADT profile, or pasted raw content. Choose platform context such as Intune so platform-specific rules apply.', + 'Findings include severity, rule id, category, line/excerpt, and remediation.' + ]), + section('Migration wizard', [ + 'The migration wizard plans automatic replacements and manual review steps for saved scripts or raw legacy content.', + 'Automatic replacements include common legacy function names, Deploy-Application naming, and $appName-style variables.', + 'Manual review flags include ServiceUI wrappers, raw msiexec, missing Open-ADTSession, legacy phase shape, and missing DeferRunInterval for Intune deferrals.' + ]) + ], ['psadt', 'verifier', 'migration']), + article('psadt-intune-deploy', 'PSADT', 'Deploying PSADT Applications With Intune', 'End-to-end PSADT Win32 app publishing guidance.', [ + section('Core workflow', [ + 'Place installers in Files and supporting files in SupportFiles. Keep icons/assets, config, and strings in the correct PSADT folders.', + 'Edit Invoke-AppDeployToolkit.ps1 for variables, pre/main/post install, uninstall, and repair logic.', + 'Wrap the entire PSADT folder with the Microsoft Win32 Content Prep Tool into a .intunewin package.', + 'Create a Windows app (Win32) in Intune, upload the package, configure commands, requirements, detection, return codes, and assignments.' + ]), + section('Commands and UI context', [ + 'For PSADT v4 packages, prefer Invoke-AppDeployToolkit.exe -DeploymentType Install and Invoke-AppDeployToolkit.exe -DeploymentType Uninstall.', + 'Older v3 examples often use Deploy-Application.exe -DeploymentType "Install" -DeployMode "Interactive". Keep this only for legacy compatibility packages.', + 'Install behavior should normally be System. PSADT v4.1+ supports native user-session UI for Intune SYSTEM installs without ServiceUI. ServiceUI should be legacy-only.' + ]), + section('Return codes and detection', [ + 'Map 0 as success, 1602 as retry/user defer, 3010 as soft reboot, and keep custom PSADT app codes out of 60000-68999.', + 'Some historical guidance uses 1703 as a soft reboot mapping; POSHManager includes it for compatibility.', + 'Use MSI product code, file/version, registry, or custom PowerShell detection. Custom scripts should be fast, side-effect free, and return success only when the app is installed.' + ]) + ], ['psadt', 'intune', 'deployment', 'serviceui']) + , + article('psadt-graph-tracking', 'PSADT', 'Tracking Intune Deployments With Microsoft Graph', 'Use Graph environments to view deployment reach, failures, devices, and users.', [ + section('Graph environment setup', [ + 'Create a Microsoft Entra app registration with application permissions such as DeviceManagementApps.Read.All for read-only tracking. Use DeviceManagementApps.ReadWrite.All only when you intend to manage Intune app objects or assignments.', + 'Store tenant id, client id, and client secret in Config / Intune Graph environments. Client secrets are encrypted and hidden after save.', + 'Use Test to validate token acquisition and read access to deviceAppManagement.' + ]), + section('Deployment tracking workflow', [ + 'Create or open an Intune Publishing Wizard plan, then link it to a Graph environment and Intune mobileApp id.', + 'Sync status to read Intune report data into POSHManager. Stored rows include device name, user principal name, install state, error code, detail, and raw Graph/report data.', + 'Use the failures count and status table to quickly identify who and which devices failed, then correlate with Intune Management Extension logs and PSADT logs.' + ]), + section('Graph reporting notes', [ + 'POSHManager prefers Intune reporting endpoints for install status tracking because older mobileApp deviceStatuses/userStatuses relationships have changed over time.', + 'Microsoft Graph returns request ids and status codes that should be kept for troubleshooting tenant permission, throttling, and report availability issues.', + 'This brings POSHManager closer to full Intune publishing and package lifecycle management. Package content/module redistribution is still a separate future capability unless explicitly implemented.' + ]) + ], ['psadt', 'graph', 'intune', 'tracking', 'failures']) +]; + +const cmdletArticles = [ + article('cmdlets-core-reference', 'PowerShell', 'Microsoft.PowerShell.Core Cmdlet Reference', 'Common core cmdlets and links to the Microsoft reference.', [ + section('Reference source', [ + `Microsoft documents the Microsoft.PowerShell.Core snap-in and common cmdlets at ${powershellCoreUrl}. The snap-in is loaded automatically by Windows PowerShell and cannot be imported or removed like a normal module.` + ]), + section('Common cmdlets', [ + 'Get-Help: display help for commands and concepts.', + 'Get-Command: discover cmdlets, functions, aliases, and applications.', + 'Get-Module and Import-Module: inspect and import modules.', + 'Invoke-Command: run commands locally or against remote sessions.', + 'New-PSSession, Enter-PSSession, Get-PSSession, Remove-PSSession: manage PowerShell remoting sessions.', + 'Start-Job, Get-Job, Receive-Job, Stop-Job, Remove-Job, Wait-Job: manage background jobs.', + 'ForEach-Object and Where-Object: pipeline iteration and filtering.', + 'Set-StrictMode: enforce safer script practices.', + 'Update-Help and Save-Help: maintain local help content.' + ]), + section('Discovery examples', [], 'Get-Help Invoke-Command -Detailed\nGet-Command -Module Microsoft.PowerShell.Core\nGet-Command *PSSession*\nUpdate-Help -Force') + ], ['powershell', 'cmdlets', 'reference']), + article('cmdlets-remoting', 'PowerShell', 'PowerShell Remoting Cmdlets', 'Useful cmdlets for WinRM/PSSession workflows used around POSHManager.', [ + section('Session workflow', [ + 'Enable-PSRemoting configures a computer to receive remote commands. This must be done on targets outside POSHManager.', + 'New-PSSession creates reusable remote sessions. Invoke-Command can run commands through a session or directly against computers.', + 'Get-PSSession shows current sessions. Remove-PSSession closes them.' + ], '$session = New-PSSession -ComputerName "APP01"\nInvoke-Command -Session $session -ScriptBlock { Get-Service }\nGet-PSSession\nRemove-PSSession $session'), + section('Interactive troubleshooting', [ + 'Enter-PSSession starts an interactive remote shell. Exit-PSSession returns to the local shell.', + 'Use Test-WSMan outside the Core module to verify WinRM reachability before adding a host.' + ], 'Enter-PSSession -ComputerName "APP01"\nExit-PSSession') + ], ['powershell', 'remoting', 'winrm']), + article('cmdlets-jobs-pipeline', 'PowerShell', 'Jobs And Pipeline Cmdlets', 'Common cmdlets for async work and object processing.', [ + section('Jobs', [ + 'Start-Job runs a command in the background. Get-Job inspects state, Receive-Job collects output, Stop-Job stops work, Wait-Job blocks until completion, and Remove-Job cleans up.', + 'Use jobs for local async work. POSHManager jobs are application records and not the same thing as PowerShell Start-Job jobs.' + ], '$job = Start-Job -ScriptBlock { Get-Process }\nWait-Job $job\nReceive-Job $job\nRemove-Job $job'), + section('Pipeline processing', [ + 'Where-Object filters pipeline objects. ForEach-Object performs an action for each item.', + 'Prefer object properties over text parsing whenever possible.' + ], 'Get-Service | Where-Object Status -eq "Running" | ForEach-Object { $_.Name }') + ], ['powershell', 'jobs', 'pipeline']) +]; + +function snippetArticles() { + const catalog = psadtCatalog(); + const snippetItems = (catalog.snippets || []).map((snippet) => section(snippet.title, [ + `${snippet.category} snippet for ${snippet.functionName}. Use Send to editor from the old snippet flow or copy the snippet from Help.` + ], snippet.body)); + return [ + article('snippets-psadt', 'Snippets', 'PSADT Snippet Library', 'Common PSADT examples moved into searchable help.', snippetItems, ['snippets', 'psadt', 'examples']), + article('snippets-api-powershell', 'Snippets', 'PowerShell API Snippets', 'Reusable PowerShell snippets for POSHManager API automation.', [ + section('Login helper', [], '$base = "http://localhost:3000"\n$token = (Invoke-RestMethod -Method Post -Uri "$base/api/auth/login" -ContentType "application/json" -Body (@{ email = "admin@posh.local"; password = "change-me-now" } | ConvertTo-Json)).token\n$headers = @{ Authorization = "Bearer $token" }'), + section('Create script and RunPlan', [], '$script = Invoke-RestMethod -Method Post -Uri "$base/api/scripts" -Headers $headers -ContentType "application/json" -Body (@{ name = "Inventory.ps1"; content = "Get-ComputerInfo"; visibility = "personal" } | ConvertTo-Json)\n$plan = Invoke-RestMethod -Method Post -Uri "$base/api/runplans" -Headers $headers -ContentType "application/json" -Body (@{ name = "Inventory"; scriptId = $script.id; parallel = $true; hostIds = @() } | ConvertTo-Json -Depth 5)') + ], ['snippets', 'api', 'powershell']) + ]; +} + +export function helpCatalog() { + return [ + ...operationsArticles, + ...apiArticles, + ...psadtArticles, + ...cmdletArticles, + ...snippetArticles() + ]; +} + +export function helpCategories() { + return [...new Set(helpCatalog().map((item) => item.category))].sort(); +} + +export function helpSearch(query = '', category = '') { + const needle = query.trim().toLowerCase(); + return helpCatalog().filter((item) => { + const categoryMatch = !category || item.category === category; + if (!needle) return categoryMatch; + const haystack = [ + item.title, + item.summary, + item.category, + ...(item.tags || []), + ...item.sections.flatMap((part) => [part.heading, ...(part.body || []), part.code || '']) + ].join(' ').toLowerCase(); + return categoryMatch && haystack.includes(needle); + }); +} diff --git a/server/data/installerCatalog.js b/server/data/installerCatalog.js new file mode 100644 index 0000000..77d9a25 --- /dev/null +++ b/server/data/installerCatalog.js @@ -0,0 +1,107 @@ +// Knowledge base of common Windows installer technologies and their silent +// install/uninstall command lines. Command templates use placeholders: +// {file} the installer file name/path +// {productCode} the MSI product code (MSI/MSP) +// {app} the installed app folder (operator fills in) +// {packageName} the MSIX/AppX package name +// Confidence reflects how reliably the technology can be inferred from a file +// name alone; binary signature sniffing (Phase 5) raises it. + +export const INSTALLER_TECHNOLOGIES = [ + { + id: 'msi', + label: 'Windows Installer (MSI)', + extensions: ['.msi'], + install: 'msiexec.exe /i "{file}" /qn /norestart', + uninstall: 'msiexec.exe /x "{productCode}" /qn /norestart', + psadtInstall: "Start-ADTMsiProcess -Action Install -FilePath '{file}'", + psadtUninstall: "Start-ADTMsiProcess -Action Uninstall -ProductCode '{productCode}'", + detection: 'msi', + notes: 'MSI is silent with /qn. Use the product code for uninstall and detection.' + }, + { + id: 'msp', + label: 'Windows Installer Patch (MSP)', + extensions: ['.msp'], + install: 'msiexec.exe /p "{file}" /qn /norestart', + uninstall: '', + psadtInstall: "Start-ADTMsiProcess -Action Patch -FilePath '{file}'", + detection: 'msi', + notes: 'MSP applies a patch to an installed MSI.' + }, + { + id: 'inno', + label: 'Inno Setup', + extensions: ['.exe'], + hints: ['inno'], + install: '"{file}" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-', + uninstall: '"%ProgramFiles%\\{app}\\unins000.exe" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART', + psadtInstall: "Start-ADTProcess -FilePath '{file}' -ArgumentList '/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-'", + detection: 'registry-uninstall', + notes: 'Inno Setup uses /VERYSILENT; the uninstaller is unins000.exe in the install folder.' + }, + { + id: 'nsis', + label: 'NSIS (Nullsoft)', + extensions: ['.exe'], + hints: ['nsis'], + install: '"{file}" /S', + uninstall: '"%ProgramFiles%\\{app}\\uninstall.exe" /S', + psadtInstall: "Start-ADTProcess -FilePath '{file}' -ArgumentList '/S'", + detection: 'registry-uninstall', + notes: 'NSIS uses an uppercase /S for silent install.' + }, + { + id: 'installshield', + label: 'InstallShield', + extensions: ['.exe'], + hints: ['installshield'], + install: '"{file}" /s /v"/qn"', + uninstall: '"{file}" /s /x /v"/qn"', + psadtInstall: "Start-ADTProcess -FilePath '{file}' -ArgumentList '/s /v\"/qn\"'", + detection: 'registry-uninstall', + notes: 'InstallShield; older versions may require a recorded response file (.iss).' + }, + { + id: 'wix-burn', + label: 'WiX Burn bundle', + extensions: ['.exe'], + hints: ['bundle', 'burn'], + install: '"{file}" /quiet /norestart', + uninstall: '"{file}" /uninstall /quiet /norestart', + psadtInstall: "Start-ADTProcess -FilePath '{file}' -ArgumentList '/quiet /norestart'", + detection: 'registry-uninstall', + notes: 'WiX Burn bootstrapper uses /quiet /norestart.' + }, + { + id: 'squirrel', + label: 'Squirrel (Electron)', + extensions: ['.exe'], + hints: ['squirrel'], + install: '"{file}" --silent', + uninstall: '"{file}" --uninstall --silent', + psadtInstall: "Start-ADTProcess -FilePath '{file}' -ArgumentList '--silent'", + detection: 'registry-uninstall', + notes: 'Squirrel installers (many Electron apps) use --silent.' + }, + { + id: 'msix', + label: 'MSIX / AppX', + extensions: ['.msix', '.msixbundle', '.appx', '.appxbundle'], + install: 'Add-AppxProvisionedPackage -Online -PackagePath "{file}" -SkipLicense', + uninstall: 'Remove-AppxProvisionedPackage -Online -PackageName "{packageName}"', + psadtInstall: "Add-AppxProvisionedPackage -Online -PackagePath '{file}' -SkipLicense", + detection: 'appx', + notes: 'MSIX/AppX provisioned package; detection via Get-AppxPackage.' + }, + { + id: 'exe', + label: 'Generic EXE', + extensions: ['.exe'], + install: '"{file}" /S', + uninstall: '', + psadtInstall: "Start-ADTProcess -FilePath '{file}' -ArgumentList '/S'", + detection: 'registry-uninstall', + notes: 'Unknown EXE installer; confirm the silent switch with the vendor. Common switches: /S, /silent, /quiet, /VERYSILENT.' + } +]; diff --git a/server/data/intuneCatalog.js b/server/data/intuneCatalog.js new file mode 100644 index 0000000..4eb217d --- /dev/null +++ b/server/data/intuneCatalog.js @@ -0,0 +1,70 @@ +export const intuneCoverage = { + appType: 'Windows app (Win32)', + publishingSteps: [ + { id: 'source', title: 'Prepare source', summary: 'Place installers in Files and supporting content in SupportFiles, Assets, Config, or Strings as appropriate.' }, + { id: 'commands', title: 'Program commands', summary: 'Use Invoke-AppDeployToolkit.exe for PSADT v4 packages; keep legacy Deploy-Application.exe only for v3 compatibility packages.' }, + { id: 'requirements', title: 'Requirements', summary: 'Model architecture, minimum Windows version, disk needs, and 32-bit execution flags before upload.' }, + { id: 'detection', title: 'Detection', summary: 'Choose MSI product code, file/version, registry, or custom PowerShell detection with clear installed/not-installed behavior.' }, + { id: 'returns', title: 'Return codes', summary: 'Map PSADT and installer exit codes so Intune retries, succeeds, or requests reboot correctly.' }, + { id: 'assignments', title: 'Assignments', summary: 'Publish to pilot, broad, and exclusion rings with intent and group notes.' }, + { id: 'review', title: 'Review', summary: 'Confirm UI context, ServiceUI compatibility, Graph app id, logs, and package readiness.' } + ], + packaging: [ + { area: 'Package source', guidance: 'Package the entire PSADT deployment folder with Microsoft Win32 Content Prep Tool so Invoke-AppDeployToolkit.exe sits at the content root for v4 packages.' }, + { area: 'Files folder', guidance: 'Place application installers in Files; PSADT installer helpers resolve files relative to that folder.' }, + { area: 'SupportFiles folder', guidance: 'Place helper scripts, transforms, configuration files, or content copied during post-install tasks in SupportFiles.' }, + { area: 'Assets / Config / Strings', guidance: 'Use Assets for icons, Config for toolkit settings such as paths/UI behavior, and Strings for localized prompt text.' }, + { area: 'Install command', guidance: 'PSADT v4: Invoke-AppDeployToolkit.exe -DeploymentType Install. Legacy v3 compatibility: Deploy-Application.exe -DeploymentType "Install" -DeployMode "Interactive".' }, + { area: 'Uninstall command', guidance: 'PSADT v4: Invoke-AppDeployToolkit.exe -DeploymentType Uninstall. Legacy v3 compatibility: Deploy-Application.exe -DeploymentType "Uninstall" -DeployMode "Interactive".' }, + { area: 'Install behavior', guidance: 'System is recommended for enterprise PSADT deployments; PSADT v4.1+ handles user-session UI without ServiceUI.' }, + { area: 'Device restart behavior', guidance: 'Use return-code driven reboot behavior so PSADT can pass 3010/1703 soft reboot signals, or suppress reboot passthrough when Intune should see success.' } + ], + returnCodes: [ + { code: 0, type: 'success', meaning: 'Success' }, + { code: 1602, type: 'retry', meaning: 'User deferred/cancelled; Intune recognizes this natively.' }, + { code: 1703, type: 'softReboot', meaning: 'Legacy PSADT soft reboot mapping used in some Intune deployment examples.' }, + { code: 1641, type: 'softReboot', meaning: 'MSI initiated reboot.' }, + { code: 3010, type: 'softReboot', meaning: 'Soft reboot required.' }, + { code: 60008, type: 'failed', meaning: 'PSADT initialization failure.' }, + { code: '60000-68999', type: 'reserved', meaning: 'Reserved PSADT built-in range; do not use for custom app codes.' }, + { code: '69000-69999', type: 'customScript', meaning: 'Recommended custom Invoke-AppDeployToolkit.ps1 range.' }, + { code: '70000-79999', type: 'extension', meaning: 'Recommended extension module range.' } + ], + detectionRules: [ + { type: 'MSI product code', bestFor: 'Single MSI packages', notes: 'Prefer when zero-config MSI packaging is used and the product code is stable.' }, + { type: 'File/version', bestFor: 'EXE installers and vendor apps', notes: 'Check a stable executable path and ProductVersion/FileVersion.' }, + { type: 'Registry', bestFor: 'Apps with Add/Remove Programs entries or custom install markers', notes: 'Use HKLM uninstall keys or a package-specific marker value.' }, + { type: 'Custom PowerShell', bestFor: 'Complex detection', notes: 'Return exit 0 and write output when installed; keep script fast and side-effect free.' } + ], + requirements: [ + { key: 'Architecture', values: ['x64', 'x86', 'both'], guidance: 'Match the package architecture and the Intune requirement rule.' }, + { key: 'Minimum OS', values: ['Windows 10 22H2', 'Windows 11 23H2', 'Windows 11 24H2'], guidance: 'Keep this aligned with your enterprise baseline and vendor support.' }, + { key: 'Run as 32-bit', values: ['false', 'true'], guidance: 'Use only for installers or detection scripts that require 32-bit registry/file redirection.' } + ], + uiCompatibility: [ + { mode: 'native-v4', label: 'PSADT v4 native UI', guidance: 'Preferred. PSADT v4.1+ can display user prompts from Intune SYSTEM installs without ServiceUI.' }, + { mode: 'serviceui-legacy', label: 'Legacy ServiceUI bridge', guidance: 'Use only for older PSADT/v3 compatibility packages that intentionally bundle ServiceUI/Invoke-ServiceUI.' }, + { mode: 'silent', label: 'Silent / non-interactive', guidance: 'Use for required background installs that should not prompt or defer.' } + ], + assignmentRings: [ + { name: 'Pilot', intent: 'available or required', guidance: 'Small operator or IT validation group; collect PSADT logs and Intune device status.' }, + { name: 'Broad', intent: 'required', guidance: 'Main production group after pilot success.' }, + { name: 'Exclusion', intent: 'exclude', guidance: 'Break-glass group for blocked apps, special users, or conflicting devices.' } + ], + managementActions: [ + { action: 'Upload package', apiStatus: 'planned', guidance: 'Future Graph integration should create/update Win32 app content versions.' }, + { action: 'Create app metadata', apiStatus: 'planned', guidance: 'Map POSHManager PSADT profile metadata to Intune Win32 app display information.' }, + { action: 'Configure install/uninstall commands', apiStatus: 'supported-template', guidance: 'Commands are generated today and should be used when creating Intune apps.' }, + { action: 'Configure return codes', apiStatus: 'supported-template', guidance: 'Return-code table is modeled today for operator copy/automation.' }, + { action: 'Configure detection rules', apiStatus: 'supported-template', guidance: 'Detection patterns are modeled today; future Graph integration can write them directly.' }, + { action: 'Assign rings', apiStatus: 'planned', guidance: 'Future Graph integration should manage required/available assignments and exclusions.' }, + { action: 'Monitor deployment', apiStatus: 'planned', guidance: 'Future Graph integration should pull device/user install status and correlate PSADT logs.' } + ], + gotchas: [ + 'Do not wrap PSADT with ServiceUI.exe for Intune; PSADT v4.1+ has native user-session UI handling.', + 'Use -DeferRunInterval when deferrals are enabled so Intune retry timing does not repeatedly prompt users.', + 'Avoid hard-coding -DeployMode unless required; Auto is the upstream default and respects session/process/OOBE detection.', + 'Map 1602 as retry/user defer, 3010 as soft reboot, and keep custom PSADT app codes out of 60000-68999.', + 'Test packages as SYSTEM with the launch helper patterns before broad Intune assignment.' + ] +}; diff --git a/server/data/psadtCatalog.js b/server/data/psadtCatalog.js new file mode 100644 index 0000000..91412e7 --- /dev/null +++ b/server/data/psadtCatalog.js @@ -0,0 +1,365 @@ +import { intuneCoverage } from './intuneCatalog.js'; + +export const psadtSources = [ + { id: 'github', title: 'PSAppDeployToolkit GitHub repository', url: 'https://github.com/psappdeploytoolkit/psappdeploytoolkit' }, + { id: 'new-deployment', title: 'Creating a new deployment', url: 'https://psappdeploytoolkit.com/docs/getting-started/creating-a-new-deployment' }, + { id: 'requirements', title: 'Requirements', url: 'https://psappdeploytoolkit.com/docs/getting-started/requirements' }, + { id: 'release-notes', title: 'Release Notes', url: 'https://psappdeploytoolkit.com/docs/getting-started/release-notes' }, + { id: 'upgrade-v41', title: 'Upgrade Guidance: v4 to v4.1', url: 'https://psappdeploytoolkit.com/docs/getting-started/upgrade-guidance-4x-to-v41' }, + { id: 'faq', title: 'FAQ', url: 'https://psappdeploytoolkit.com/docs/getting-started/faq' }, + { id: 'structure', title: 'Deployment Structure', url: 'https://psappdeploytoolkit.com/docs/deployment-concepts/deployment-structure' }, + { id: 'invoke', title: 'Invoke-AppDeployToolkit', url: 'https://psappdeploytoolkit.com/docs/deployment-concepts/invoke-appdeploytoolkit' }, + { id: 'zero-config', title: 'Zero-Config Deployment', url: 'https://psappdeploytoolkit.com/docs/deployment-concepts/zero-config-deployment' }, + { id: 'how-to-deploy', title: 'How to Deploy', url: 'https://psappdeploytoolkit.com/docs/usage/how-to-deploy' }, + { id: 'ui', title: 'Adding UI Elements', url: 'https://psappdeploytoolkit.com/docs/usage/adding-ui-elements' }, + { id: 'customize', title: 'Customizing Deployments', url: 'https://psappdeploytoolkit.com/docs/usage/customizing-deployments' }, + { id: 'installing', title: 'Installing Applications', url: 'https://psappdeploytoolkit.com/docs/usage/installing-applications' }, + { id: 'admx', title: 'ADMX Templates', url: 'https://psappdeploytoolkit.com/docs/usage/admx/admx-templates' }, + { id: 'exit-codes', title: 'Exit Codes', url: 'https://psappdeploytoolkit.com/docs/4.1.x/reference/exit-codes' } +]; + +export const psadtModuleMetadata = { + upstreamRepo: 'https://github.com/psappdeploytoolkit/psappdeploytoolkit', + moduleVersion: '4.2.0', + moduleGuid: '8c3c366b-8606-4576-9f2d-4051144f7ca2', + frontendTemplate: 'src/PSAppDeployToolkit/opt/Frontend/v4/Invoke-AppDeployToolkit.ps1', + publicFunctionCount: 142, + admxPolicyCount: 40 +}; + +const publicFunctionNames = `Add-ADTEdgeExtension +Add-ADTFont +Add-ADTModuleCallback +Block-ADTAppExecution +Clear-ADTModuleCallback +Close-ADTInstallationProgress +Close-ADTNotifyIcon +Close-ADTSession +Complete-ADTFunction +Convert-ADTRegistryPath +Convert-ADTValueType +Convert-ADTValuesFromRemainingArguments +ConvertTo-ADTNTAccountOrSID +Copy-ADTContentToCache +Copy-ADTFile +Copy-ADTFileToUserProfiles +Disable-ADTTerminalServerInstallMode +Dismount-ADTWimFile +Enable-ADTTerminalServerInstallMode +Export-ADTEnvironmentTableToSessionState +Get-ADTApplication +Get-ADTBoundParametersAndDefaultValues +Get-ADTCommandTable +Get-ADTConfig +Get-ADTDeferHistory +Get-ADTEnvironmentTable +Get-ADTEnvironmentVariable +Get-ADTExecutableInfo +Get-ADTFileVersion +Get-ADTFreeDiskSpace +Get-ADTIniSection +Get-ADTIniValue +Get-ADTLoggedOnUser +Get-ADTModuleCallback +Get-ADTMsiExitCodeMessage +Get-ADTMsiTableProperty +Get-ADTObjectProperty +Get-ADTOperatingSystemInfo +Get-ADTPEFileArchitecture +Get-ADTPendingReboot +Get-ADTPowerShellProcessPath +Get-ADTPresentationSettingsEnabledUsers +Get-ADTRegistryKey +Get-ADTRunningProcesses +Get-ADTServiceStartMode +Get-ADTSession +Get-ADTShortcut +Get-ADTStringTable +Get-ADTUserNotificationState +Get-ADTUserProfiles +Get-ADTUserToastNotificationMode +Get-ADTWindowTitle +Initialize-ADTFunction +Initialize-ADTModule +Initialize-ADTModuleIfUninitialized +Install-ADTMSUpdates +Install-ADTSCCMSoftwareUpdates +Invoke-ADTAllUsersRegistryAction +Invoke-ADTCommandWithRetries +Invoke-ADTFunctionErrorHandler +Invoke-ADTObjectMethod +Invoke-ADTRegSvr32 +Invoke-ADTSCCMTask +Mount-ADTWimFile +New-ADTErrorRecord +New-ADTFolder +New-ADTLogFileName +New-ADTMsiTransform +New-ADTShortcut +New-ADTTemplate +New-ADTValidateScriptErrorRecord +New-ADTZipFile +Open-ADTSession +Out-ADTPowerShellEncodedCommand +Register-ADTDll +Remove-ADTContentFromCache +Remove-ADTDesktopShortcut +Remove-ADTEdgeExtension +Remove-ADTEnvironmentVariable +Remove-ADTFile +Remove-ADTFileFromUserProfiles +Remove-ADTFolder +Remove-ADTFont +Remove-ADTHashtableNullOrEmptyValues +Remove-ADTIniSection +Remove-ADTIniValue +Remove-ADTInvalidFileNameChars +Remove-ADTModuleCallback +Remove-ADTRegistryKey +Reset-ADTDeferHistory +Resolve-ADTErrorRecord +Select-ADTUniqueObject +Send-ADTKeys +Set-ADTActiveSetup +Set-ADTDeferHistory +Set-ADTEnvironmentVariable +Set-ADTIniSection +Set-ADTIniValue +Set-ADTItemPermission +Set-ADTMsiProperty +Set-ADTPowerShellCulture +Set-ADTRegistryKey +Set-ADTServiceStartMode +Set-ADTShortcut +Show-ADTBalloonTip +Show-ADTDialogBox +Show-ADTHelpConsole +Show-ADTInstallationProgress +Show-ADTInstallationPrompt +Show-ADTInstallationRestartPrompt +Show-ADTInstallationWelcome +Show-ADTNotifyIcon +Start-ADTMsiProcess +Start-ADTMsiProcessAsUser +Start-ADTMspProcess +Start-ADTMspProcessAsUser +Start-ADTProcess +Start-ADTProcessAsUser +Start-ADTServiceAndDependencies +Stop-ADTServiceAndDependencies +Test-ADTBattery +Test-ADTCallerIsAdmin +Test-ADTEspActive +Test-ADTMSUpdates +Test-ADTMicrophoneInUse +Test-ADTModuleInitialized +Test-ADTMutexAvailability +Test-ADTNetworkConnection +Test-ADTOobeCompleted +Test-ADTPowerPoint +Test-ADTRegistryValue +Test-ADTServiceExists +Test-ADTSessionActive +Test-ADTUserInFocusMode +Test-ADTUserIsBusy +Unblock-ADTAppExecution +Uninstall-ADTApplication +Unregister-ADTDll +Update-ADTDesktop +Update-ADTEnvironmentPsProvider +Update-ADTGroupPolicy +Write-ADTLogEntry`; + +function functionCategory(name) { + if (/^(Show|Close).*Installation|^Show-ADT|NotifyIcon|Dialog|Balloon|HelpConsole/.test(name)) return 'User Interface'; + if (/^(Start|Install|Uninstall).*Msi|Msp|Process|Application|Updates|SCCM|RegSvr32|Dll/.test(name)) return 'Installer and process'; + if (/Registry|ActiveSetup|Ini|Shortcut|File|Folder|Font|Content|Zip|Wim|EnvironmentVariable/.test(name)) return 'Filesystem registry config'; + if (/Session|Module|Function|Callback|Config|StringTable|CommandTable/.test(name)) return 'Toolkit session'; + if (/User|LoggedOn|PowerPoint|Battery|Network|Oobe|Esp|PendingReboot|OperatingSystem|Service|Mutex|Caller|Microphone|Window|Desktop|GroupPolicy|TerminalServer/.test(name)) return 'Device user state'; + return 'Utilities'; +} + +export const psadtFunctionCatalog = publicFunctionNames.split('\n').map((name) => ({ + name, + category: functionCategory(name), + snippet: `${name} ` +})); + +export const psadtAdmxPolicies = [ + ['Logo', 'Assets', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Assets'], + ['LogoDark', 'Assets', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Assets'], + ['Banner', 'Assets', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Assets'], + ['TaskbarIcon', 'Assets', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Assets'], + ['InstallParams', 'MSI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\MSI'], + ['LoggingOptions', 'MSI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\MSI'], + ['MutexWaitTime', 'MSI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\MSI'], + ['SilentParams', 'MSI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\MSI'], + ['UninstallParams', 'MSI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\MSI'], + ['CachePath', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['CompanyName', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['CompressLogs', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['FileCopyMode', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['LogAppend', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['LogDebugMessage', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['LogMaxHierarchy', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['LogMaxHistory', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['LogMaxSize', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['LogPath', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['LogPathNoAdminRights', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['LogToHierarchy', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['LogToSubfolder', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['LogStyle', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['LogWriteToHost', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['LogHostOutputToStdStreams', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['RegPath', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['RegPathNoAdminRights', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['TempPath', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['TempPathNoAdminRights', 'Toolkit', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\Toolkit'], + ['BalloonNotifications', 'UI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\UI'], + ['FluentAccentColor', 'UI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\UI'], + ['FluentAccentColorDark', 'UI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\UI'], + ['DialogStyle', 'UI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\UI'], + ['DefaultExitCode', 'UI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\UI'], + ['DefaultPromptPersistInterval', 'UI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\UI'], + ['DefaultTimeout', 'UI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\UI'], + ['DeferExitCode', 'UI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\UI'], + ['LanguageOverride', 'UI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\UI'], + ['PromptToSaveTimeout', 'UI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\UI'], + ['RestartPromptPersistInterval', 'UI', 'SOFTWARE\\Policies\\PSAppDeployToolkit\\Config\\UI'] +].map(([name, category, key]) => ({ name, category, key })); + +export const psadtLaunchCommands = [ + { name: 'Install interactive', command: 'Invoke-AppDeployToolkit.exe -DeploymentType Install -DeployMode Interactive' }, + { name: 'Install silent', command: 'Invoke-AppDeployToolkit.exe -DeploymentType Install -DeployMode Silent' }, + { name: 'Uninstall interactive', command: 'Invoke-AppDeployToolkit.exe -DeploymentType Uninstall -DeployMode Interactive' }, + { name: 'Uninstall silent', command: 'Invoke-AppDeployToolkit.exe -DeploymentType Uninstall -DeployMode Silent' }, + { name: 'Repair interactive', command: 'Invoke-AppDeployToolkit.exe -DeploymentType Repair -DeployMode Interactive' }, + { name: 'Repair silent', command: 'Invoke-AppDeployToolkit.exe -DeploymentType Repair -DeployMode Silent' }, + { name: 'Install interactive as SYSTEM for testing', command: 'PsExec64.exe /accepteula /s /i Invoke-AppDeployToolkit.exe -DeploymentType Install -DeployMode Interactive' }, + { name: 'Install silent as SYSTEM for testing', command: 'PsExec64.exe /accepteula /s Invoke-AppDeployToolkit.exe -DeploymentType Install -DeployMode Silent' } +]; + +export const psadtSupportMatrix = [ + { area: 'Deployment creation', sourceId: 'new-deployment', support: 'profile', status: 'supported', implementation: 'PSADT profile wizard can create v4/v3-compatible script skeletons and script library entries.' }, + { area: 'Requirements', sourceId: 'requirements', support: 'verifier', status: 'supported', implementation: 'Verifier flags scripts that declare unsupported PowerShell versions and records PSADT platform requirements.' }, + { area: 'Release-note gotchas', sourceId: 'release-notes', support: 'verifier', status: 'supported', implementation: 'Verifier checks known gotchas such as ServiceUI usage, Fluent UI prompt icon usage, raw MSI handling, and old deployment names.' }, + { area: 'v4 to v4.1 upgrade guidance', sourceId: 'upgrade-v41', support: 'verifier', status: 'supported', implementation: 'Verifier checks RequireAdmin/AppProcessesToClose placement, removed config keys, session cleanup, and deprecated patterns.' }, + { area: 'FAQ guidance', sourceId: 'faq', support: 'verifier', status: 'supported', implementation: 'Verifier catches missing ADTSession assumptions, global module imports, Intune defer retry concerns, and user-context RequireAdmin guidance.' }, + { area: 'Upstream module coverage', sourceId: 'github', support: 'catalog', status: 'supported', implementation: 'Catalog tracks the upstream 4.2.0 module metadata, all 142 public functions, launch helpers, and 40 ADMX policies.' }, + { area: 'Template structure', sourceId: 'structure', support: 'reference', status: 'supported', implementation: 'Structure table tracks Invoke-AppDeployToolkit.ps1, PSAppDeployToolkit, Files, SupportFiles, Assets, Config, and Strings.' }, + { area: 'ADTSession properties', sourceId: 'invoke', support: 'profile', status: 'supported', implementation: 'Profiles store app metadata, success/reboot codes, RequireAdmin, and close-process definitions.' }, + { area: 'Install/Uninstall/Repair phases', sourceId: 'invoke', support: 'generator', status: 'supported', implementation: 'Renderer emits Install-ADTDeployment, Uninstall-ADTDeployment, and Repair-ADTDeployment phase blocks.' }, + { area: 'Zero-config MSI', sourceId: 'zero-config', support: 'profile', status: 'supported', implementation: 'Zero-config toggle emits an empty AppName and a Files-folder checklist for single MSI/MST/MSP/WIM packages.' }, + { area: 'Deployment commands', sourceId: 'how-to-deploy', support: 'generator', status: 'supported', implementation: 'Renderer produces Invoke-AppDeployToolkit.exe and PowerShell command lines for Intune/package documentation.' }, + { area: 'UI elements', sourceId: 'ui', support: 'snippets', status: 'supported', implementation: 'Snippet library covers welcome, progress, prompts, restart prompts, dialog boxes, and help console.' }, + { area: 'Customization', sourceId: 'customize', support: 'checklist', status: 'supported', implementation: 'Profiles carry config, strings, assets, log, and CompressLogs guidance for package readiness.' }, + { area: 'Application installers', sourceId: 'installing', support: 'snippets', status: 'supported', implementation: 'Snippet library covers Start-ADTProcess, Start-ADTProcessAsUser, and Start-ADTMsiProcess.' }, + { area: 'ADMX / Intune policy', sourceId: 'admx', support: 'checklist', status: 'supported', implementation: 'ADMX checklist tracks policy files, Intune import, Central Store, and HKLM policy path.' } + ,{ area: 'Exit codes', sourceId: 'exit-codes', support: 'verifier', status: 'supported', implementation: 'Verifier flags reserved built-in exit code ranges and recommends custom script/extension ranges.' } +]; + +export const psadtStructureRows = [ + { path: 'Invoke-AppDeployToolkit.ps1', purpose: 'Main deployment script and phase logic.', required: true }, + { path: 'Invoke-AppDeployToolkit.exe', purpose: 'Wrapper executable for hidden PowerShell launches and command-line options.', required: false }, + { path: 'PSAppDeployToolkit/', purpose: 'Core module files; should not be modified in deployments.', required: true }, + { path: 'PSAppDeployToolkit.Extensions/', purpose: 'Optional custom functions/extensions.', required: false }, + { path: 'Files/', purpose: 'Installer media such as EXE, MSI, MST, MSP, WIM, archives, and configs.', required: true }, + { path: 'SupportFiles/', purpose: 'Registry files, configuration templates, shortcuts, docs, licenses, and support payloads.', required: false }, + { path: 'Assets/', purpose: 'AppIcon.png, classic banners, logos, and UI branding images.', required: false }, + { path: 'Config/config.psd1', purpose: 'Toolkit behavior, MSI parameters, logging, UI, security, and performance settings.', required: true }, + { path: 'Strings/strings.psd1', purpose: 'Localized UI strings and deployment messaging.', required: true }, + { path: 'PSAppDeployToolkit/ADMX/', purpose: 'PSADT ADMX/ADML policy templates for Group Policy or Intune.', required: false } +]; + +export const psadtInvokeParameters = [ + { name: '-DeploymentType', values: ['Install', 'Uninstall', 'Repair'], description: 'Selects install, uninstall, or repair deployment flow.' }, + { name: '-DeployMode', values: ['Interactive', 'Silent', 'NonInteractive'], description: 'Controls UI behavior and whether interactive prompts are allowed.' }, + { name: '-SuppressRebootPassThru', values: ['switch'], description: 'Suppresses 3010 reboot-required passthrough exit codes.' }, + { name: '-TerminalServerMode', values: ['switch'], description: 'Changes to user install mode for RDS/Citrix systems.' }, + { name: '-DisableLogging', values: ['switch'], description: 'Disables file logging.' }, + { name: '/Debug', values: ['exe only'], description: 'Opens a visible debug window with live logging.' }, + { name: '/32', values: ['exe only'], description: 'Launches with x86 PowerShell.' }, + { name: '/Core', values: ['exe only'], description: 'Launches with PowerShell Core.' }, + { name: '-File', values: ['*.ps1'], description: 'Runs a custom script file instead of Invoke-AppDeployToolkit.ps1.' } +]; + +export const psadtSnippets = [ + { + id: 'welcome-close-processes', + title: 'Welcome prompt with close processes', + category: 'UI', + functionName: 'Show-ADTInstallationWelcome', + body: "Show-ADTInstallationWelcome -CloseProcesses $adtSession.AppProcessesToClose -AllowDefer -DeferTimes 3 -CheckDiskSpace" + }, + { + id: 'progress', + title: 'Installation progress', + category: 'UI', + functionName: 'Show-ADTInstallationProgress', + body: "Show-ADTInstallationProgress -StatusMessage 'Installing application. Please wait...'" + }, + { + id: 'custom-prompt', + title: 'Custom installation prompt', + category: 'UI', + functionName: 'Show-ADTInstallationPrompt', + body: "Show-ADTInstallationPrompt -Title 'Deployment notice' -Message 'The application will now be installed.' -ButtonRightText 'OK'" + }, + { + id: 'restart-prompt', + title: 'Restart prompt', + category: 'UI', + functionName: 'Show-ADTInstallationRestartPrompt', + body: 'Show-ADTInstallationRestartPrompt -CountdownSeconds 600 -CountdownNoHideSeconds 60' + }, + { + id: 'dialog', + title: 'Generic dialog box', + category: 'UI', + functionName: 'Show-ADTDialogBox', + body: "Show-ADTDialogBox -Text 'Deployment completed.' -Icon Information -Buttons OK" + }, + { + id: 'help-console', + title: 'Toolkit help console', + category: 'UI', + functionName: 'Show-ADTHelpConsole', + body: 'Show-ADTHelpConsole' + }, + { + id: 'start-process', + title: 'Install EXE', + category: 'Install', + functionName: 'Start-ADTProcess', + body: "Start-ADTProcess -FilePath 'setup.exe' -ArgumentList '/S' -WaitForChildProcesses -SuccessExitCodes @(0, 3010)" + }, + { + id: 'start-process-user', + title: 'Run process as active user', + category: 'Install', + functionName: 'Start-ADTProcessAsUser', + body: "Start-ADTProcessAsUser -FilePath '%LOCALAPPDATA%\\Programs\\SomeApp\\Uninstall.exe' -ArgumentList '/S' -ExpandEnvironmentVariables" + }, + { + id: 'start-msi', + title: 'Install MSI with transform', + category: 'Install', + functionName: 'Start-ADTMsiProcess', + body: "Start-ADTMsiProcess -Action 'Install' -FilePath 'SomeApp.msi' -Transforms 'SomeApp.mst' -AdditionalArgumentList 'SERIAL=12345' -SecureArgumentList" + } +]; + +export function psadtCatalog() { + return { + module: psadtModuleMetadata, + sources: psadtSources, + supportMatrix: psadtSupportMatrix, + structure: psadtStructureRows, + invokeParameters: psadtInvokeParameters, + functions: psadtFunctionCatalog, + admxPolicies: psadtAdmxPolicies, + launchCommands: psadtLaunchCommands, + intune: intuneCoverage, + snippets: psadtSnippets + }; +} diff --git a/server/data/psadtValidationRules.js b/server/data/psadtValidationRules.js new file mode 100644 index 0000000..e6fe0e1 --- /dev/null +++ b/server/data/psadtValidationRules.js @@ -0,0 +1,191 @@ +export const psadtValidationRules = [ + { + id: 'REQ-PSVERSION-MINIMUM', + severity: 'warning', + category: 'Requirements', + sourceId: 'requirements', + title: 'PowerShell version requirement is below PSADT v4 support floor', + description: 'PSADT v4 supports Windows PowerShell 5.1 and PowerShell 7.4+. Lower #requires values can hide unsupported runtime assumptions.', + remediation: 'Use Windows PowerShell 5.1 or PowerShell 7.4+ for PSADT v4 deployments.' + }, + { + id: 'UPG-REQUIREADMIN-CONFIG', + severity: 'error', + category: 'v4.1 upgrade', + sourceId: 'upgrade-v41', + title: 'RequireAdmin appears to be configured outside ADTSession', + description: 'PSADT v4.1 moved RequireAdmin out of Config.psd1 and into the deployment session properties.', + remediation: 'Set RequireAdmin in $adtSession, e.g. RequireAdmin = $true or RequireAdmin = $false.' + }, + { + id: 'UPG-REMOVED-CONFIG-DETECTION', + severity: 'error', + category: 'v4.1 upgrade', + sourceId: 'upgrade-v41', + title: 'Removed OOBE/session detection config option found', + description: 'Toolkit.OobeDetection and Toolkit.SessionDetection were removed from Config.psd1 in v4.1.', + remediation: 'Move this decision to deployment/session options such as NoOobeDetection or NoSessionDetection when needed.' + }, + { + id: 'UPG-APP-PROCESSES-SESSION', + severity: 'warning', + category: 'v4.1 upgrade', + sourceId: 'upgrade-v41', + title: 'Close-processes are not centralized in AppProcessesToClose', + description: 'PSADT v4.1 recommends defining AppProcessesToClose in the ADTSession object and reusing it across phases.', + remediation: 'Move repeated process definitions into $adtSession.AppProcessesToClose and pass that to Show-ADTInstallationWelcome.' + }, + { + id: 'UPG-MISSING-SESSION-CLEANUP', + severity: 'warning', + category: 'v4.1 upgrade', + sourceId: 'upgrade-v41', + title: 'Open-ADTSession is used without removing null or empty session values', + description: 'The v4.1 template strips null/empty values from the ADTSession hashtable before Open-ADTSession.', + remediation: 'Call Remove-ADTHashtableNullOrEmptyValues before Open-ADTSession when you build the session hashtable manually.' + }, + { + id: 'REPO-MODULE-VERSION-DRIFT', + severity: 'warning', + category: 'Upstream parity', + sourceId: 'github', + title: 'Generated/imported module version is behind upstream 4.2.0', + description: 'The upstream repo template currently imports PSAppDeployToolkit module version 4.2.0.', + remediation: 'Update ModuleVersion pins and generated templates to 4.2.0 unless you intentionally target an older package.' + }, + { + id: 'REPO-DEPLOYMODE-NO-AUTO', + severity: 'info', + category: 'Upstream parity', + sourceId: 'github', + title: 'DeployMode is hard-coded instead of allowing Auto', + description: 'The upstream v4 frontend template defaults DeployMode to Auto and says not to hard-code it unless required.', + remediation: 'Prefer omitting -DeployMode or using Auto unless the deployment specifically requires Interactive, NonInteractive, or Silent.' + }, + { + id: 'LEGACY-V3-FUNCTION', + severity: 'error', + category: 'Legacy v3 pattern', + sourceId: 'release-notes', + title: 'Legacy v3 function name found', + description: 'PSADT v4 renamed many common v3 functions to ADT-prefixed equivalents.', + remediation: 'Use the v4 function mapping and replace legacy function names such as Execute-Process, Execute-MSI, Write-Log, and Show-InstallationWelcome.' + }, + { + id: 'LEGACY-DEPLOY-APPLICATION', + severity: 'warning', + category: 'Legacy v3 pattern', + sourceId: 'release-notes', + title: 'Legacy Deploy-Application naming found', + description: 'PSADT v4 renamed Deploy-Application.ps1/exe to Invoke-AppDeployToolkit.ps1/exe.', + remediation: 'Use Invoke-AppDeployToolkit.ps1 and Invoke-AppDeployToolkit.exe in v4 packages.' + }, + { + id: 'LEGACY-DEPLOYMENT-VARIABLE', + severity: 'warning', + category: 'ADTSession', + sourceId: 'faq', + title: 'Legacy deployment variable found', + description: 'Deployment variables such as $appName and $appVersion are stored on $adtSession in PSADT v4.', + remediation: 'Use $adtSession.AppName, $adtSession.AppVersion, and related ADTSession properties.' + }, + { + id: 'FAQ-ENV-VAR-BEFORE-SESSION', + severity: 'info', + category: 'ADTSession', + sourceId: 'faq', + title: 'Toolkit environment variables require an ADTSession', + description: 'PSADT environment variables are available after opening an ADTSession or exporting the environment table.', + remediation: 'Ensure Open-ADTSession or Export-ADTEnvironmentTableToSessionState runs before relying on PSADT environment variables.' + }, + { + id: 'INTUNE-SERVICEUI', + severity: 'critical', + category: 'Intune/security', + sourceId: 'release-notes', + title: 'ServiceUI usage found', + description: 'PSADT v4.1 no longer requires ServiceUI for Intune UI display and the release notes call out security risk in token manipulation approaches.', + remediation: 'Remove ServiceUI workarounds and rely on PSADT v4.1 native user-session UI handling.' + }, + { + id: 'INTUNE-DEFER-RUN-INTERVAL', + severity: 'warning', + category: 'Intune/retry', + sourceId: 'faq', + title: 'Deferral prompt lacks DeferRunInterval', + description: 'Intune retries quickly after a 1602 defer result. PSADT FAQ recommends DeferRunInterval to prevent repeated close-app prompts.', + remediation: 'Add -DeferRunInterval when using AllowDefer for Intune-targeted deployments.' + }, + { + id: 'UI-PROMPT-ICON-FLUENT', + severity: 'warning', + category: 'UI', + sourceId: 'release-notes', + title: 'Show-ADTInstallationPrompt uses -Icon', + description: 'The v4.1.8 template removed -Icon from Show-ADTInstallationPrompt because it is not supported in Fluent UI.', + remediation: 'Remove -Icon from Show-ADTInstallationPrompt or switch to a function that supports the desired icon behavior.' + }, + { + id: 'INSTALL-RAW-MSIEXEC', + severity: 'warning', + category: 'Installer', + sourceId: 'installing', + title: 'Raw msiexec usage found', + description: 'PSADT provides Start-ADTMsiProcess for MSI install/uninstall/repair/patch handling and logging.', + remediation: 'Use Start-ADTMsiProcess instead of calling msiexec.exe directly where possible.' + }, + { + id: 'INSTALL-PROCESS-NO-WAIT-CHILD', + severity: 'info', + category: 'Installer', + sourceId: 'release-notes', + title: 'Start-ADTProcess may exit before child installers finish', + description: 'PSADT v4.1 added -WaitForChildProcesses for installers that hand off to child processes.', + remediation: 'Consider -WaitForChildProcesses for setup.exe installers that spawn child processes and exit early.' + }, + { + id: 'EXIT-RESERVED-BUILTIN', + severity: 'error', + category: 'Exit codes', + sourceId: 'exit-codes', + title: 'Reserved PSADT built-in exit code used as custom code', + description: 'Exit codes 60000-68999 are reserved for built-in PSADT exit codes.', + remediation: 'Use 69000-69999 for custom Invoke-AppDeployToolkit.ps1 exit codes or 70000-79999 for extension module codes.' + }, + { + id: 'EXIT-CUSTOM-RANGE', + severity: 'info', + category: 'Exit codes', + sourceId: 'exit-codes', + title: 'Custom exit code range detected', + description: 'Exit codes 69000-69999 are recommended for user customized Invoke-AppDeployToolkit.ps1 exit codes.', + remediation: 'Confirm the code is documented in the package and deployment platform return-code mapping.' + }, + { + id: 'FAQ-GLOBAL-MODULE-IMPORT', + severity: 'warning', + category: 'Packaging', + sourceId: 'faq', + title: 'Global PSADT module import found', + description: 'The FAQ notes packages can include the toolkit module, while central module installs risk running tested packages against newer module versions.', + remediation: 'Prefer package-local PSAppDeployToolkit module files unless you intentionally govern a central module lifecycle.' + }, + { + id: 'DEPRECATED-GET-ADTOBJECTPROPERTY', + severity: 'warning', + category: 'Deprecation', + sourceId: 'github', + title: 'Deprecated Get-ADTObjectProperty usage found', + description: 'The upstream function help marks Get-ADTObjectProperty as deprecated and scheduled for removal in PSAppDeployToolkit 4.3.0.', + remediation: 'Replace Get-ADTObjectProperty usage with direct object/property access or a supported helper before upgrading to 4.3.0.' + }, + { + id: 'USERCONTEXT-REQUIREADMIN', + severity: 'warning', + category: 'User context', + sourceId: 'faq', + title: 'User-context process with RequireAdmin enabled', + description: 'The FAQ recommends RequireAdmin = $false for user-context application installs.', + remediation: 'Set $adtSession.RequireAdmin to $false when the deployment is intended to run in user context.' + } +]; diff --git a/server/data/variableCatalog.js b/server/data/variableCatalog.js new file mode 100644 index 0000000..eddb395 --- /dev/null +++ b/server/data/variableCatalog.js @@ -0,0 +1,84 @@ +function variable(name, category, description, source = 'psadt') { + return { + id: `${source}:${name}`, + name, + syntax: `$${name}`, + token: `{{${name}}}`, + category, + description, + source, + readOnly: true + }; +} + +export const poshManagerVariables = [ + variable('POSHM_JOB_ID', 'POSHManager Runtime', 'Current POSHManager job id.', 'poshmanager'), + variable('POSHM_RUNPLAN_ID', 'POSHManager Runtime', 'RunPlan id that launched the execution.', 'poshmanager'), + variable('POSHM_SCRIPT_ID', 'POSHManager Runtime', 'Script id being executed.', 'poshmanager'), + variable('POSHM_SCRIPT_NAME', 'POSHManager Runtime', 'Script display name being executed.', 'poshmanager'), + variable('POSHM_HOST_ID', 'POSHManager Runtime', 'Target host id for the current execution.', 'poshmanager'), + variable('POSHM_HOST_NAME', 'POSHManager Runtime', 'Target host display name.', 'poshmanager'), + variable('POSHM_HOST_ADDRESS', 'POSHManager Runtime', 'Target host address used by remoting.', 'poshmanager'), + variable('POSHM_HOST_TRANSPORT', 'POSHManager Runtime', 'Transport selected for the target host.', 'poshmanager'), + variable('POSHM_TRIGGERED_BY', 'POSHManager Runtime', 'User id that launched the RunPlan.', 'poshmanager') +]; + +export const psadtVariables = [ + variable('appDeployMainScriptFriendlyName', 'PSADT Toolkit Name', 'Friendly name for the main deployment script.'), + variable('appDeployToolkitName', 'PSADT Toolkit Name', 'PSAppDeployToolkit name.'), + variable('appDeployMainScriptVersion', 'PSADT Script Info', 'Version of the main deployment script.'), + variable('culture', 'PSADT Culture', 'Current culture information.'), + variable('currentLanguage', 'PSADT Culture', 'Current language code.'), + variable('currentUILanguage', 'PSADT Culture', 'Current UI language code.'), + ...[ + 'envAllUsersProfile', 'envAppData', 'envArchitecture', 'envCommonDesktop', 'envCommonDocuments', + 'envCommonProgramFiles', 'envCommonProgramFilesX86', 'envCommonStartMenu', 'envCommonStartMenuPrograms', + 'envCommonStartUp', 'envCommonTemplates', 'envComputerName', 'envComputerNameFQDN', 'envHomeDrive', + 'envHomePath', 'envHomeShare', 'envLocalAppData', 'envLogicalDrives', 'envProgramData', 'envProgramFiles', + 'envProgramFilesX86', 'envPublic', 'envSystem32Directory', 'envSystemDrive', 'envSystemRAM', 'envSystemRoot', + 'envTemp', 'envUserCookies', 'envUserDesktop', 'envUserFavorites', 'envUserInternetCache', + 'envUserInternetHistory', 'envUserMyDocuments', 'envUserName', 'envUserProfile', 'envUserSendTo', + 'envUserStartMenu', 'envUserStartMenuPrograms', 'envUserStartUp', 'envWinDir' + ].map((name) => variable(name, 'PSADT Environment', 'Environment/session variable populated by PSADT.')), + ...[ + 'envLogonServer', 'envMachineADDomain', 'envMachineDNSDomain', 'envMachineWorkgroup', 'envUserDNSDomain', + 'envUserDomain', 'IsMachinePartOfDomain', 'MachineDomainController' + ].map((name) => variable(name, 'PSADT Domain', 'Domain and directory context populated by PSADT.')), + ...[ + 'envOS', 'envOSArchitecture', 'envOSName', 'envOSProductType', 'envOSProductTypeName', 'envOSServicePack', + 'envOSVersion', 'envOSVersionBuild', 'envOSVersionMajor', 'envOSVersionMinor', 'envOSVersionRevision', + 'Is64Bit', 'IsDomainControllerOS', 'IsServerOS', 'IsWorkStationOS' + ].map((name) => variable(name, 'PSADT Operating System', 'Operating system detail populated by PSADT.')), + variable('Is64BitProcess', 'PSADT Process', 'Whether the active PowerShell process is 64-bit.'), + variable('psArchitecture', 'PSADT Process', 'PowerShell process architecture.'), + ...[ + 'envCLRVersion', 'envCLRVersionBuild', 'envCLRVersionMajor', 'envCLRVersionMinor', 'envCLRVersionRevision', + 'envHost', 'envPSVersion', 'envPSVersionBuild', 'envPSVersionMajor', 'envPSVersionMinor', + 'envPSVersionRevision', 'envPSVersionTable' + ].map((name) => variable(name, 'PSADT PowerShell', '.NET CLR, host, or PowerShell version detail populated by PSADT.')), + ...[ + 'CurrentProcessSID', 'CurrentProcessToken', 'IsAdmin', 'IsLocalServiceAccount', 'IsLocalSystemAccount', + 'IsNetworkServiceAccount', 'IsProcessUserInteractive', 'IsServiceAccount', 'LocalSystemNTAccount', + 'ProcessNTAccount', 'ProcessNTAccountSID', 'SessionZero' + ].map((name) => variable(name, 'PSADT Permissions', 'Process identity, token, and permission context populated by PSADT.')), + variable('MSIProductCodeRegExPattern', 'PSADT Regex', 'Regular expression pattern for MSI product codes.'), + variable('Shell', 'PSADT COM', 'Shell COM object created by PSADT.'), + variable('ShellApp', 'PSADT COM', 'Shell application COM object created by PSADT.'), + ...[ + 'CurrentConsoleUserSession', 'CurrentLoggedOnUserSession', 'LoggedOnUserSessions', 'RunAsActiveUser', 'UsersLoggedOn' + ].map((name) => variable(name, 'PSADT Logged On Users', 'Logged-on user session context populated by PSADT.')), + ...[ + 'envOfficeBitness', 'envOfficeChannel', 'envOfficeVars', 'envOfficeVersion' + ].map((name) => variable(name, 'PSADT Office', 'Microsoft Office discovery variable populated by PSADT.')), + variable('invalidFileNameChars', 'PSADT Miscellaneous', 'Invalid file name character list.'), + variable('LocalAdministratorsGroup', 'PSADT Miscellaneous', 'Localized local administrators group.'), + variable('LocalUsersGroup', 'PSADT Miscellaneous', 'Localized local users group.'), + variable('RunningTaskSequence', 'PSADT Miscellaneous', 'Whether execution is running inside a task sequence.') +]; + +export function fullVariableCatalog() { + return { + builtIns: poshManagerVariables, + psadt: psadtVariables + }; +}